How to Pass User-Defined Structure Containing Strings to DLL

J

Joe

I am working on a VB project that has to use a C++ dll that provides some
look up function. The original VB6 code followed the example at
http://support.microsoft.com/kb/107750, but this is no longer applicable for
a number of reasons under VB 2008.

The issues that I am having are related to getting the structure memory to
match inthe VB calling routine. Instead of using:
[
Type MYSTRINGSTRUCT
str1 As String * 8
str2 As String * 8
End Type
]
I am using:
[
Dim c As Char = " "
Structure MYSTRINGSTRUCT
Public str1() As Char
Public str2() As Char

Public Sub new( c As Char )
str1 = new String( c, 8 ).ToCharArray()
str2 = new String( c, 8 ).ToCharArray()
End Sub
End Structure
]

When I look at the structure in the debugger the size looks right. When I
fire off the SizeOf operation I get a smaler value than expected. The size
matches my expectation for the size of a pair of string pointers.

How can I be sure that I am passing the correct reference to the structure
that will be viewed on the C++ side? Do I need to worry about instantiating
a copy of my structure in unmanaged memory space? How can I insure that the
memory for my string is properly part of the passed in structure?

Thanks in advance!
 
H

Herfried K. Wagner [MVP]

Joe said:
I am working on a VB project that has to use a C++ dll that provides some
look up function. The original VB6 code followed the example at
http://support.microsoft.com/kb/107750, but this is no longer applicable
for
a number of reasons under VB 2008.

The issues that I am having are related to getting the structure memory to
match inthe VB calling routine. Instead of using:
[
Type MYSTRINGSTRUCT
str1 As String * 8
str2 As String * 8
End Type
]
I am using:
[
Dim c As Char = " "
Structure MYSTRINGSTRUCT
Public str1() As Char
Public str2() As Char

Public Sub new( c As Char )
str1 = new String( c, 8 ).ToCharArray()
str2 = new String( c, 8 ).ToCharArray()
End Sub
End Structure
]

When I look at the structure in the debugger the size looks right. When I
fire off the SizeOf operation I get a smaler value than expected. The
size
matches my expectation for the size of a pair of string pointers.

Take a look at the 'MarshalAs' attribute. Sample:

\\\
Public Structure FOO
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
Public s As String
End Structure
///
 
J

Joe

Herfried K. Wagner said:
Joe said:
I am working on a VB project that has to use a C++ dll that provides some
look up function. The original VB6 code followed the example at
http://support.microsoft.com/kb/107750, but this is no longer applicable
for
a number of reasons under VB 2008.

The issues that I am having are related to getting the structure memory to
match inthe VB calling routine. Instead of using:
[
Type MYSTRINGSTRUCT
str1 As String * 8
str2 As String * 8
End Type
]
I am using:
[
Dim c As Char = " "
Structure MYSTRINGSTRUCT
Public str1() As Char
Public str2() As Char

Public Sub new( c As Char )
str1 = new String( c, 8 ).ToCharArray()
str2 = new String( c, 8 ).ToCharArray()
End Sub
End Structure
]

When I look at the structure in the debugger the size looks right. When I
fire off the SizeOf operation I get a smaler value than expected. The
size
matches my expectation for the size of a pair of string pointers.

Take a look at the 'MarshalAs' attribute. Sample:

\\\
Public Structure FOO
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
Public s As String
End Structure
///

I applied this logic and found some benefit. I am able to pass strings as
structure elements to the C++ dll. I am able to see the string in the dll
just fine (verified by enableing a trace log echoing this string). I am not
able to write to this string in the dll and have the results get stuffed into
the passed in string. The C+ dll uses a bcopy to rip the contents of the
stored string onto the structure's string, and I can echo out the value in
the C++ side. When I observe the value once the process gets back to VB2008
it is the value from my initialization.
 
J

Joe

Joe said:
Herfried K. Wagner said:
Joe said:
I am working on a VB project that has to use a C++ dll that provides some
look up function. The original VB6 code followed the example at
http://support.microsoft.com/kb/107750, but this is no longer applicable
for
a number of reasons under VB 2008.

The issues that I am having are related to getting the structure memory to
match inthe VB calling routine. Instead of using:
[
Type MYSTRINGSTRUCT
str1 As String * 8
str2 As String * 8
End Type
]
I am using:
[
Dim c As Char = " "
Structure MYSTRINGSTRUCT
Public str1() As Char
Public str2() As Char

Public Sub new( c As Char )
str1 = new String( c, 8 ).ToCharArray()
str2 = new String( c, 8 ).ToCharArray()
End Sub
End Structure
]

When I look at the structure in the debugger the size looks right. When I
fire off the SizeOf operation I get a smaler value than expected. The
size
matches my expectation for the size of a pair of string pointers.

Take a look at the 'MarshalAs' attribute. Sample:

\\\
Public Structure FOO
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
Public s As String
End Structure
///

I applied this logic and found some benefit. I am able to pass strings as
structure elements to the C++ dll. I am able to see the string in the dll
just fine (verified by enableing a trace log echoing this string). I am not
able to write to this string in the dll and have the results get stuffed into
the passed in string. The C+ dll uses a bcopy to rip the contents of the
stored string onto the structure's string, and I can echo out the value in
the C++ side. When I observe the value once the process gets back to VB2008
it is the value from my initialization.

Where bcopy is defined as:
/*** bcopy:
*** Copy a sequence of bytes.
*** Args: dstp = Ptr to destination memory.
*** srcp = Ptr to source memory.
*** nbytes = Number of bytes.
*** Ret: None.
***/
static void bcopy(void *dstp,void *srcp,short nbytes)
{
int i;
UCHAR *dp, *sp;

for (i=0, dp=(UCHAR *)dstp, sp=(UCHAR *)srcp; i<nbytes; i++)
*dp++ = *sp++;
}
 
L

Lloyd Sheen

Joe said:
Joe said:
Herfried K. Wagner said:
I am working on a VB project that has to use a C++ dll that provides
some
look up function. The original VB6 code followed the example at
http://support.microsoft.com/kb/107750, but this is no longer
applicable
for
a number of reasons under VB 2008.

The issues that I am having are related to getting the structure
memory to
match inthe VB calling routine. Instead of using:
[
Type MYSTRINGSTRUCT
str1 As String * 8
str2 As String * 8
End Type
]
I am using:
[
Dim c As Char = " "
Structure MYSTRINGSTRUCT
Public str1() As Char
Public str2() As Char

Public Sub new( c As Char )
str1 = new String( c, 8 ).ToCharArray()
str2 = new String( c, 8 ).ToCharArray()
End Sub
End Structure
]

When I look at the structure in the debugger the size looks right.
When I
fire off the SizeOf operation I get a smaler value than expected.
The
size
matches my expectation for the size of a pair of string pointers.

Take a look at the 'MarshalAs' attribute. Sample:

\\\
Public Structure FOO
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
Public s As String
End Structure
///

I applied this logic and found some benefit. I am able to pass strings
as
structure elements to the C++ dll. I am able to see the string in the
dll
just fine (verified by enableing a trace log echoing this string). I am
not
able to write to this string in the dll and have the results get stuffed
into
the passed in string. The C+ dll uses a bcopy to rip the contents of the
stored string onto the structure's string, and I can echo out the value
in
the C++ side. When I observe the value once the process gets back to
VB2008
it is the value from my initialization.

Where bcopy is defined as:
/*** bcopy:
*** Copy a sequence of bytes.
*** Args: dstp = Ptr to destination memory.
*** srcp = Ptr to source memory.
*** nbytes = Number of bytes.
*** Ret: None.
***/
static void bcopy(void *dstp,void *srcp,short nbytes)
{
int i;
UCHAR *dp, *sp;

for (i=0, dp=(UCHAR *)dstp, sp=(UCHAR *)srcp; i<nbytes; i++)
*dp++ = *sp++;
}

Using the new MS tool P/Invoke Interop Assistant I get the following for the
bcopy function



Partial Public Class NativeMethods

'''Return Type: void
'''dstp: void*
'''srcp: void*
'''nbytes: short
<System.Runtime.InteropServices.DllImportAttribute("<Unknown>",
EntryPoint:="bcopy")> _
Public Shared Sub bcopy(ByVal dstp As System.IntPtr, ByVal srcp As
System.IntPtr, ByVal nbytes As Short)
End Sub
End Class


Hope this helps.

For all those who did not see post first time
http://msdn2.microsoft.com/en-us/magazine/cc164193.aspx is link to this tool
which hopefully will reduce the number of P/Invoke questions.

LS
 
J

Joe

Lloyd Sheen said:
Joe said:
Joe said:
:

I am working on a VB project that has to use a C++ dll that provides
some
look up function. The original VB6 code followed the example at
http://support.microsoft.com/kb/107750, but this is no longer
applicable
for
a number of reasons under VB 2008.

The issues that I am having are related to getting the structure
memory to
match inthe VB calling routine. Instead of using:
[
Type MYSTRINGSTRUCT
str1 As String * 8
str2 As String * 8
End Type
]
I am using:
[
Dim c As Char = " "
Structure MYSTRINGSTRUCT
Public str1() As Char
Public str2() As Char

Public Sub new( c As Char )
str1 = new String( c, 8 ).ToCharArray()
str2 = new String( c, 8 ).ToCharArray()
End Sub
End Structure
]

When I look at the structure in the debugger the size looks right.
When I
fire off the SizeOf operation I get a smaler value than expected.
The
size
matches my expectation for the size of a pair of string pointers.

Take a look at the 'MarshalAs' attribute. Sample:

\\\
Public Structure FOO
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
Public s As String
End Structure
///

--
M S Herfried K. Wagner
M V P <URL:http://dotnet.mvps.org/>
V B <URL:http://dotnet.mvps.org/dotnet/faqs/>



I applied this logic and found some benefit. I am able to pass strings
as
structure elements to the C++ dll. I am able to see the string in the
dll
just fine (verified by enableing a trace log echoing this string). I am
not
able to write to this string in the dll and have the results get stuffed
into
the passed in string. The C+ dll uses a bcopy to rip the contents of the
stored string onto the structure's string, and I can echo out the value
in
the C++ side. When I observe the value once the process gets back to
VB2008
it is the value from my initialization.

Where bcopy is defined as:
/*** bcopy:
*** Copy a sequence of bytes.
*** Args: dstp = Ptr to destination memory.
*** srcp = Ptr to source memory.
*** nbytes = Number of bytes.
*** Ret: None.
***/
static void bcopy(void *dstp,void *srcp,short nbytes)
{
int i;
UCHAR *dp, *sp;

for (i=0, dp=(UCHAR *)dstp, sp=(UCHAR *)srcp; i<nbytes; i++)
*dp++ = *sp++;
}

Using the new MS tool P/Invoke Interop Assistant I get the following for the
bcopy function



Partial Public Class NativeMethods

'''Return Type: void
'''dstp: void*
'''srcp: void*
'''nbytes: short
<System.Runtime.InteropServices.DllImportAttribute("<Unknown>",
EntryPoint:="bcopy")> _
Public Shared Sub bcopy(ByVal dstp As System.IntPtr, ByVal srcp As
System.IntPtr, ByVal nbytes As Short)
End Sub
End Class


Hope this helps.

For all those who did not see post first time
http://msdn2.microsoft.com/en-us/magazine/cc164193.aspx is link to this tool
which hopefully will reduce the number of P/Invoke questions.

LS

I tested the basic principal with success. I wrote a simple C++ dll:

---- header file ----
#include <windows.h> /* Windows headers */

#define ERR_NONE 0 /* No error */

struct MixedString
{
int m_int;
double m_double;
char m_string[ 40 ];
};

short WINAPI getMixedStringItem( MixedString *result );
----- end header ----

---- cpp file ----
#include "StringDllTest.h"
#include <WinDef.h>

/*** bcopy:
*** Copy a sequence of bytes.
*** Args: dstp = Ptr to destination memory.
*** srcp = Ptr to source memory.
*** nbytes = Number of bytes.
*** Ret: None.
***/
static void bcopy(void *dstp,void *srcp,short nbytes)
{
int i;
UCHAR *dp, *sp;

for (i=0, dp=(UCHAR *)dstp, sp=(UCHAR *)srcp; i<nbytes; i++)
*dp++ = *sp++;
}

short WINAPI getMixedStringItem( MixedString *result )
{
result->m_int = 1;
result->m_double = 1.1;
bcopy( result->m_string, "This is my test string.", 23 );
return ERR_NONE;
}
---- end cpp file ----

---- def file ----
LIBRARY "StringDllTest"

EXPORTS
getMixedStringItem @1
---- end def file ----


---- basic file test ----
Module Module1

Structure MixedString
Public m_int As Short
Public m_double as double

<System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst:=260)> Public m_string As String
End Structure
Declare Function getMixedStringItem Lib "StringDllTest.dll" (ByRef
testStruct As MixedString ) As Int16


Sub Main()
Dim test As MixedString
test.m_int = 0
test.m_double = 0
test.m_string = "blah blah blah"
Console.WriteLine( "Before: " & test.m_int & " " & test.m_double & "
" & test.m_string )
getMixedStringItem( test )
Console.WriteLine( "After: " & test.m_int & " " & test.m_double & "
" & test.m_string )
Console.ReadKey()
End Sub

End Module
---- end basic file test ----


It worked. The output of the test is:
Before: 0 0 blah blah blah
After: 1 1.1 This is my test string.

My original test is still failing however... the result string is never
overwritten. Something else must be amiss.
 
J

Joe

Joe said:
Lloyd Sheen said:
Joe said:
:



:

I am working on a VB project that has to use a C++ dll that provides
some
look up function. The original VB6 code followed the example at
http://support.microsoft.com/kb/107750, but this is no longer
applicable
for
a number of reasons under VB 2008.

The issues that I am having are related to getting the structure
memory to
match inthe VB calling routine. Instead of using:
[
Type MYSTRINGSTRUCT
str1 As String * 8
str2 As String * 8
End Type
]
I am using:
[
Dim c As Char = " "
Structure MYSTRINGSTRUCT
Public str1() As Char
Public str2() As Char

Public Sub new( c As Char )
str1 = new String( c, 8 ).ToCharArray()
str2 = new String( c, 8 ).ToCharArray()
End Sub
End Structure
]

When I look at the structure in the debugger the size looks right.
When I
fire off the SizeOf operation I get a smaler value than expected.
The
size
matches my expectation for the size of a pair of string pointers.

Take a look at the 'MarshalAs' attribute. Sample:

\\\
Public Structure FOO
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
Public s As String
End Structure
///

--
M S Herfried K. Wagner
M V P <URL:http://dotnet.mvps.org/>
V B <URL:http://dotnet.mvps.org/dotnet/faqs/>



I applied this logic and found some benefit. I am able to pass strings
as
structure elements to the C++ dll. I am able to see the string in the
dll
just fine (verified by enableing a trace log echoing this string). I am
not
able to write to this string in the dll and have the results get stuffed
into
the passed in string. The C+ dll uses a bcopy to rip the contents of the
stored string onto the structure's string, and I can echo out the value
in
the C++ side. When I observe the value once the process gets back to
VB2008
it is the value from my initialization.

Where bcopy is defined as:
/*** bcopy:
*** Copy a sequence of bytes.
*** Args: dstp = Ptr to destination memory.
*** srcp = Ptr to source memory.
*** nbytes = Number of bytes.
*** Ret: None.
***/
static void bcopy(void *dstp,void *srcp,short nbytes)
{
int i;
UCHAR *dp, *sp;

for (i=0, dp=(UCHAR *)dstp, sp=(UCHAR *)srcp; i<nbytes; i++)
*dp++ = *sp++;
}

Using the new MS tool P/Invoke Interop Assistant I get the following for the
bcopy function



Partial Public Class NativeMethods

'''Return Type: void
'''dstp: void*
'''srcp: void*
'''nbytes: short
<System.Runtime.InteropServices.DllImportAttribute("<Unknown>",
EntryPoint:="bcopy")> _
Public Shared Sub bcopy(ByVal dstp As System.IntPtr, ByVal srcp As
System.IntPtr, ByVal nbytes As Short)
End Sub
End Class


Hope this helps.

For all those who did not see post first time
http://msdn2.microsoft.com/en-us/magazine/cc164193.aspx is link to this tool
which hopefully will reduce the number of P/Invoke questions.

LS

I tested the basic principal with success. I wrote a simple C++ dll:

---- header file ----
#include <windows.h> /* Windows headers */

#define ERR_NONE 0 /* No error */

struct MixedString
{
int m_int;
double m_double;
char m_string[ 40 ];
};

short WINAPI getMixedStringItem( MixedString *result );
----- end header ----

---- cpp file ----
#include "StringDllTest.h"
#include <WinDef.h>

/*** bcopy:
*** Copy a sequence of bytes.
*** Args: dstp = Ptr to destination memory.
*** srcp = Ptr to source memory.
*** nbytes = Number of bytes.
*** Ret: None.
***/
static void bcopy(void *dstp,void *srcp,short nbytes)
{
int i;
UCHAR *dp, *sp;

for (i=0, dp=(UCHAR *)dstp, sp=(UCHAR *)srcp; i<nbytes; i++)
*dp++ = *sp++;
}

short WINAPI getMixedStringItem( MixedString *result )
{
result->m_int = 1;
result->m_double = 1.1;
bcopy( result->m_string, "This is my test string.", 23 );
return ERR_NONE;
}
---- end cpp file ----

---- def file ----
LIBRARY "StringDllTest"

EXPORTS
getMixedStringItem @1
---- end def file ----


---- basic file test ----
Module Module1

Structure MixedString
Public m_int As Short
Public m_double as double

<System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst:=260)> Public m_string As String
End Structure
Declare Function getMixedStringItem Lib "StringDllTest.dll" (ByRef
testStruct As MixedString ) As Int16


Sub Main()
Dim test As MixedString
test.m_int = 0
test.m_double = 0
test.m_string = "blah blah blah"
Console.WriteLine( "Before: " & test.m_int & " " & test.m_double & "
" & test.m_string )
getMixedStringItem( test )
Console.WriteLine( "After: " & test.m_int & " " & test.m_double & "
" & test.m_string )
Console.ReadKey()
End Sub

End Module
---- end basic file test ----


It worked. The output of the test is:
Before: 0 0 blah blah blah
After: 1 1.1 This is my test string.

My original test is still failing however... the result string is never
overwritten. Something else must be amiss.

My c++ dll had #pragma pack(4).
My VB code works now that I added the directive to set the packing.
<StructLayout(LayoutKind.Sequential, Pack:=4)> _
Structure name ...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top