Marshaling data between managed VB.NET and an unmanaged C Dll

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • MarDude
    New Member
    • Feb 2010
    • 7

    Marshaling data between managed VB.NET and an unmanaged C Dll

    Hello,
    I am trying to get some data from a C Dll via a structure and I am getting incorrect values back. I tried a couple of examples I found but they do not work. Since I do not know the proper way. I do not know how to fix it. Can anyone give me any advice, hints, redirection or any assistance what-so-ever. I've been struggling with this for weeks now so I will appreciate any kind of guidance (so will my boss :).
  • tlhintoq
    Recognized Expert Specialist
    • Mar 2008
    • 3532

    #2
    Another post here on Bytes for nearly the same thing.


    Hint: There is a search box in the upper right of the Bytes webpage.

    Comment

    • MarDude
      New Member
      • Feb 2010
      • 7

      #3
      Thanks for the reply. For the record I searched through quite a few pages on this site which took a couple of hours and found NOTHING helpful. Perhaps no one used the same keywords I used, although it did pick up several quotes that contained the keywords which the article had no relation.
      And that link you provided doesn't provide anything useful, besides it discusses C#, NOT VB. since the problem I am having involves passing data from one language to another, including a third unnecessarily complicates things. Perhaps I need to give more detailed information, here goes:

      I am writing a VB.NET application that uses TWAIN (dumb dll). When I call the function and get data back it is incorrect. What I don't know is what I am doing wrong. Here are the C structures:
      Code:
      typedef unsigned short TW_UINT16, FAR *pTW_UINT16;
      typedef unsigned long  TW_UINT32, FAR *pTW_UINT32;
      typedef HANDLE         TW_HANDLE;
      
      typedef struct {
         TW_UINT16  Cap; /* id of capability to set or get, e.g. CAP_BRIGHTNESS */
         TW_UINT16  ConType; /* TWON_ONEVALUE, _RANGE, _ENUMERATION or _ARRAY   */
         TW_HANDLE  hContainer; /* Handle to container of type Dat              */
      } TW_CAPABILITY, FAR * pTW_CAPABILITY;
      
      typedef struct {
         TW_UINT16  ItemType;
         TW_UINT32  Item;
      } TW_ONEVALUE, FAR * pTW_ONEVALUE;
      Which have been converted to VB.NET structure/class:

      Code:
          <StructLayout(LayoutKind.Sequential, Pack:=4)> Private Structure twsCapability
              Public Cap As twCap         'short
              Public ConType As twContainerTyoe   'short
              Public Handle As IntPtr     'Pointer to a container
          End Structure
      
          <StructLayout(LayoutKind.Sequential, Pack:=4)> Friend Class TW_ONEVALUE
              Public ItemType As Short
              Public Item As Integer
          End Class
      Then I run this routine and get back 64356640 for the physical height and width:
      Code:
      'Call into the C library
          <DllImport("twain_32.dll", EntryPoint:="#1")> Private Shared Function DS_Capability( _
              <[In](), Out()> ByVal pOrigin As twsIdentity, _
              <[In]()> ByVal pSource As twsIdentity, _
              ByVal dg As twDG, _
              ByVal dat As twDAT, _
              ByVal msg As twMSG, _
              <[In](), Out()> ByRef pData As twsCapability) As twRC
          End Function
      
      'Start here:
                  ...previous code
                  twCapability = New twsCapability
                  With twCapability
                      .Cap = twCap.IPhysicalWidth
                      .ConType = twContainerTyoe.One   'TWON_DONTCARE16
                      '.Handle = Nothing
                  End With
                  strWidth = GetOneValue(twCapability)
      
                  With twCapability
                      .Cap = twCap.IPhysicalHeight
                      '.ConType = 7 'TWON_DONTCARE16
                      '.Handle = Nothing
                  End With
                  strHeight = GetOneValue(twCapability)
                  ...more code
      
      Private Function GetOneValue(ByVal twCapability As twsCapability) As String
      
              Dim rc As twRC
              Dim strTemp As String
              Dim twOneValue As TW_ONEVALUE
      
      
              rc = DS_Capability(mtwApp, mtwSource, twDG.Control, twDAT.Capability, twMSG.Get, twCapability)
              If rc = twRC.Success Then
                  strTemp = vbNullString
                  Select Case twCapability.ConType
                      Case twContainerTyoe.One
                          twOneValue = New TW_ONEVALUE
                          Marshal.PtrToStructure(twCapability.Handle, twOneValue)
                          strTemp = CType(twOneValue.Item, String)
                      Case Else
                          MsgBox("Error, one value expected; " & twCapability.ConType & " returned.")
                  End Select
                  Marshal.FreeHGlobal(twCapability.Handle)
                  Return strTemp
              Else
                  ErrorStatus()
                  Return vbNullString
              End If
      
          End Function
      
      Private Function GetOneValueNew(ByVal twCapability As twsCapability) As String
      
              Dim rc As twRC
              Dim pOneValue As Integer
              Dim pTemp As Integer
              Dim strTemp As String
              Dim twOneValue As TW_ONEVALUE
      
      
              rc = DS_Capability(mtwApp, mtwSource, twDG.Control, twDAT.Capability, twMSG.Get, twCapability)
              If rc = twRC.Success Then
                  strTemp = vbNullString
                  Select Case twCapability.ConType
                      Case twContainerTyoe.One
                          twOneValue = New TW_ONEVALUE
                          pOneValue = GlobalLock(twCapability.Handle)
                          pTemp = VarPtr(twOneValue)
                          CopyMemory(pTemp, pOneValue, Marshal.SizeOf(twOneValue))
                          GlobalUnlock(twCapability.Handle)
                          strTemp = CType(twOneValue.Item, String)
                      Case Else
                          MsgBox("Error, one value expected; " & twCapability.ConType & " returned.")
                  End Select
                  GlobalFree(twCapability.Handle)
                  Return strTemp
              Else
                  ErrorStatus()
                  Return vbNullString
              End If
      
          End Function
      I included the function GetOneValueNew( because I found that method somewhere on the net, but it was written in VB6. Unfortunately I get 0 back from that.

      I tried searching the net but didn't find my answer. I used "VB.NET marshal structure C dll". I included this incase someone may have a better search string.

      I even tried writing the code to access the dll in C++. But I get an error in the VB routine that calls it "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." I presume that is also a problem form the unmanaged C dll trying to write to memory allocated in managed C++.

      Thanks for looking!

      Comment

      • MarDude
        New Member
        • Feb 2010
        • 7

        #4
        I found out that a pointer to a structure is not the same as a pointer to the memory that the structure occupies. Changed my code to copy the members explicitly.
        Code:
                Dim rc As twRC
                Dim pOneValue As IntPtr
                Dim pPosition As Int32 = 0
                Dim strValue As String
                Dim twFix32 As twsFix32
                Dim twOneValue As TW_ONEVALUE
        
        
                rc = DS_Capability(mtwApp, mtwSource, twDG.Control, twDAT.Capability, twMSG.Get, twCapability)
                If rc = twRC.Success Then
        
                    twOneValue = New TW_ONEVALUE
                    pOneValue = GlobalLock(twCapability.Handle)
                    twOneValue.ItemType = CType(Runtime.InteropServices.Marshal.ReadInt16(pOneValue, pPosition), ItemType)
                    pPosition += 2
        
                    Select Case twOneValue.ItemType
        
                        Case ItemType.Fix32
                            Dim origFrac As Int32
                            twFix32 = New twsFix32
        
                            twOneValue.Item = Marshal.ReadInt16(pOneValue, pPosition)
                            twFix32.Whole = twOneValue.Item
                            pPosition += 2
        
        
                            origFrac = Marshal.ReadInt16(pOneValue, pPosition)
                            If origFrac < 0 Then
                                origFrac += 65536
                            End If
                            pPosition += 2
        
                            'twFix32.Frac = CUShort(origFrac)
                            strValue = (twFix32.Whole + (origFrac / 65536)).ToString
        
                        Case Else
                            MsgBox("Unexpected One Value.  Ignored")
        
                    End Select
        
                    GlobalUnlock(twCapability.Handle)
        
                    Return strValue
        
                End If

        Comment

        Working...