Connect with Expertise | Find Experts, Get Answers, Share Insights

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

 
Join Date: Feb 2010
Posts: 7
#1: Feb 18 '10
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 :).

✓ answered by MarDude

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.
Expand|Select|Wrap|Line Numbers
  1.         Dim rc As twRC
  2.         Dim pOneValue As IntPtr
  3.         Dim pPosition As Int32 = 0
  4.         Dim strValue As String
  5.         Dim twFix32 As twsFix32
  6.         Dim twOneValue As TW_ONEVALUE
  7.  
  8.  
  9.         rc = DS_Capability(mtwApp, mtwSource, twDG.Control, twDAT.Capability, twMSG.Get, twCapability)
  10.         If rc = twRC.Success Then
  11.  
  12.             twOneValue = New TW_ONEVALUE
  13.             pOneValue = GlobalLock(twCapability.Handle)
  14.             twOneValue.ItemType = CType(Runtime.InteropServices.Marshal.ReadInt16(pOneValue, pPosition), ItemType)
  15.             pPosition += 2
  16.  
  17.             Select Case twOneValue.ItemType
  18.  
  19.                 Case ItemType.Fix32
  20.                     Dim origFrac As Int32
  21.                     twFix32 = New twsFix32
  22.  
  23.                     twOneValue.Item = Marshal.ReadInt16(pOneValue, pPosition)
  24.                     twFix32.Whole = twOneValue.Item
  25.                     pPosition += 2
  26.  
  27.  
  28.                     origFrac = Marshal.ReadInt16(pOneValue, pPosition)
  29.                     If origFrac < 0 Then
  30.                         origFrac += 65536
  31.                     End If
  32.                     pPosition += 2
  33.  
  34.                     'twFix32.Frac = CUShort(origFrac)
  35.                     strValue = (twFix32.Whole + (origFrac / 65536)).ToString
  36.  
  37.                 Case Else
  38.                     MsgBox("Unexpected One Value.  Ignored")
  39.  
  40.             End Select
  41.  
  42.             GlobalUnlock(twCapability.Handle)
  43.  
  44.             Return strValue
  45.  
  46.         End If

tlhintoq's Avatar
E
C
 
Join Date: Mar 2008
Location: Arizona, USA
Posts: 3,476
#2: Feb 18 '10

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


Another post here on Bytes for nearly the same thing.
http://bytes.com/topic/net/answers/8...-through-dll-c

Hint: There is a search box in the upper right of the Bytes webpage.
 
Join Date: Feb 2010
Posts: 7
#3: Feb 19 '10

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


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:
Expand|Select|Wrap|Line Numbers
  1. typedef unsigned short TW_UINT16, FAR *pTW_UINT16;
  2. typedef unsigned long  TW_UINT32, FAR *pTW_UINT32;
  3. typedef HANDLE         TW_HANDLE;
  4.  
  5. typedef struct {
  6.    TW_UINT16  Cap; /* id of capability to set or get, e.g. CAP_BRIGHTNESS */
  7.    TW_UINT16  ConType; /* TWON_ONEVALUE, _RANGE, _ENUMERATION or _ARRAY   */
  8.    TW_HANDLE  hContainer; /* Handle to container of type Dat              */
  9. } TW_CAPABILITY, FAR * pTW_CAPABILITY;
  10.  
  11. typedef struct {
  12.    TW_UINT16  ItemType;
  13.    TW_UINT32  Item;
  14. } TW_ONEVALUE, FAR * pTW_ONEVALUE;
  15.  
  16.  
Which have been converted to VB.NET structure/class:

Expand|Select|Wrap|Line Numbers
  1.     <StructLayout(LayoutKind.Sequential, Pack:=4)> Private Structure twsCapability
  2.         Public Cap As twCap         'short
  3.         Public ConType As twContainerTyoe   'short
  4.         Public Handle As IntPtr     'Pointer to a container
  5.     End Structure
  6.  
  7.     <StructLayout(LayoutKind.Sequential, Pack:=4)> Friend Class TW_ONEVALUE
  8.         Public ItemType As Short
  9.         Public Item As Integer
  10.     End Class
  11.  
Then I run this routine and get back 64356640 for the physical height and width:
Expand|Select|Wrap|Line Numbers
  1. 'Call into the C library
  2.     <DllImport("twain_32.dll", EntryPoint:="#1")> Private Shared Function DS_Capability( _
  3.         <[In](), Out()> ByVal pOrigin As twsIdentity, _
  4.         <[In]()> ByVal pSource As twsIdentity, _
  5.         ByVal dg As twDG, _
  6.         ByVal dat As twDAT, _
  7.         ByVal msg As twMSG, _
  8.         <[In](), Out()> ByRef pData As twsCapability) As twRC
  9.     End Function
  10.  
  11. 'Start here:
  12.             ...previous code
  13.             twCapability = New twsCapability
  14.             With twCapability
  15.                 .Cap = twCap.IPhysicalWidth
  16.                 .ConType = twContainerTyoe.One   'TWON_DONTCARE16
  17.                 '.Handle = Nothing
  18.             End With
  19.             strWidth = GetOneValue(twCapability)
  20.  
  21.             With twCapability
  22.                 .Cap = twCap.IPhysicalHeight
  23.                 '.ConType = 7 'TWON_DONTCARE16
  24.                 '.Handle = Nothing
  25.             End With
  26.             strHeight = GetOneValue(twCapability)
  27.             ...more code
  28.  
  29. Private Function GetOneValue(ByVal twCapability As twsCapability) As String
  30.  
  31.         Dim rc As twRC
  32.         Dim strTemp As String
  33.         Dim twOneValue As TW_ONEVALUE
  34.  
  35.  
  36.         rc = DS_Capability(mtwApp, mtwSource, twDG.Control, twDAT.Capability, twMSG.Get, twCapability)
  37.         If rc = twRC.Success Then
  38.             strTemp = vbNullString
  39.             Select Case twCapability.ConType
  40.                 Case twContainerTyoe.One
  41.                     twOneValue = New TW_ONEVALUE
  42.                     Marshal.PtrToStructure(twCapability.Handle, twOneValue)
  43.                     strTemp = CType(twOneValue.Item, String)
  44.                 Case Else
  45.                     MsgBox("Error, one value expected; " & twCapability.ConType & " returned.")
  46.             End Select
  47.             Marshal.FreeHGlobal(twCapability.Handle)
  48.             Return strTemp
  49.         Else
  50.             ErrorStatus()
  51.             Return vbNullString
  52.         End If
  53.  
  54.     End Function
  55.  
  56. Private Function GetOneValueNew(ByVal twCapability As twsCapability) As String
  57.  
  58.         Dim rc As twRC
  59.         Dim pOneValue As Integer
  60.         Dim pTemp As Integer
  61.         Dim strTemp As String
  62.         Dim twOneValue As TW_ONEVALUE
  63.  
  64.  
  65.         rc = DS_Capability(mtwApp, mtwSource, twDG.Control, twDAT.Capability, twMSG.Get, twCapability)
  66.         If rc = twRC.Success Then
  67.             strTemp = vbNullString
  68.             Select Case twCapability.ConType
  69.                 Case twContainerTyoe.One
  70.                     twOneValue = New TW_ONEVALUE
  71.                     pOneValue = GlobalLock(twCapability.Handle)
  72.                     pTemp = VarPtr(twOneValue)
  73.                     CopyMemory(pTemp, pOneValue, Marshal.SizeOf(twOneValue))
  74.                     GlobalUnlock(twCapability.Handle)
  75.                     strTemp = CType(twOneValue.Item, String)
  76.                 Case Else
  77.                     MsgBox("Error, one value expected; " & twCapability.ConType & " returned.")
  78.             End Select
  79.             GlobalFree(twCapability.Handle)
  80.             Return strTemp
  81.         Else
  82.             ErrorStatus()
  83.             Return vbNullString
  84.         End If
  85.  
  86.     End Function
  87.  
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!
 
Join Date: Feb 2010
Posts: 7
#4: Feb 23 '10

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


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.
Expand|Select|Wrap|Line Numbers
  1.         Dim rc As twRC
  2.         Dim pOneValue As IntPtr
  3.         Dim pPosition As Int32 = 0
  4.         Dim strValue As String
  5.         Dim twFix32 As twsFix32
  6.         Dim twOneValue As TW_ONEVALUE
  7.  
  8.  
  9.         rc = DS_Capability(mtwApp, mtwSource, twDG.Control, twDAT.Capability, twMSG.Get, twCapability)
  10.         If rc = twRC.Success Then
  11.  
  12.             twOneValue = New TW_ONEVALUE
  13.             pOneValue = GlobalLock(twCapability.Handle)
  14.             twOneValue.ItemType = CType(Runtime.InteropServices.Marshal.ReadInt16(pOneValue, pPosition), ItemType)
  15.             pPosition += 2
  16.  
  17.             Select Case twOneValue.ItemType
  18.  
  19.                 Case ItemType.Fix32
  20.                     Dim origFrac As Int32
  21.                     twFix32 = New twsFix32
  22.  
  23.                     twOneValue.Item = Marshal.ReadInt16(pOneValue, pPosition)
  24.                     twFix32.Whole = twOneValue.Item
  25.                     pPosition += 2
  26.  
  27.  
  28.                     origFrac = Marshal.ReadInt16(pOneValue, pPosition)
  29.                     If origFrac < 0 Then
  30.                         origFrac += 65536
  31.                     End If
  32.                     pPosition += 2
  33.  
  34.                     'twFix32.Frac = CUShort(origFrac)
  35.                     strValue = (twFix32.Whole + (origFrac / 65536)).ToString
  36.  
  37.                 Case Else
  38.                     MsgBox("Unexpected One Value.  Ignored")
  39.  
  40.             End Select
  41.  
  42.             GlobalUnlock(twCapability.Handle)
  43.  
  44.             Return strValue
  45.  
  46.         End If
Reply