vb6->.NET 2.0 conversion 'ByVal sName As Any' argument for c++ 'char szName[128]' param

what would be the appropriate .NET 2005 equivalent parameter be for this vb6 pinvoke declaration?

Public Declare Function foo Lib "foo.dll" (ByVal sName As Any)

where the c++ argument for the parameter sName is is'char szName[128]'

Related question (I hope)

I am confused as to when I should use

1.<MarshalAs(UnmanagedType.LPStr)> ByValsVar as String

rather than simply

2.ByValsVar as String

for cases where the vc++ parameter ischar *sVar ?

Thank you,

-Greg

[1285 byte] By [hazz] at [2007-12-22]
# 1

The answer again is: it depends on the particular situation and how you delcare the PInvoke function.

  • If you use ByVal sVar as String in a Declare statement, the default will be for the string to be marshalled as a VB6 String (BSTR).
  • If you use ByVal sVar as String in a function declared with the "DllImportAttribute()", which is functionally equivalent to the Declare statement, then the string will be marshalled as an LPTStr.

While most of the time the BSTR and TChar * Strings are compatible for simple consumption, there are semantic differences into how the strings are implemented and marshalled that might cause your application to not work as expected. Do note that this differences do show up when running your application on a different OS or platform (32bits vs 64bits).

The reason for specifying the right attributes is so that the marshaller knows the following information:

  • Whether the string is length prefixed.
  • Whether the string is ASCII or UNICODE.
  • On which directions the string needs to be copied when crossing the managed/unmanaged interfaces.
  • Whether the string is zero terminated, or it is just a fixed size buffer.

Here are some questions that you need to ask yourself to know how to marshal the string correctly:

  • Is the string going to be modified inside the function?
  • Is the function going to use memory beyond the null-termination mark?

If your answer is no to both questions then you can use <MarshalAs(UnmanagedType.LPStr)> ByVal sVar as String without any problems.

If that's not the case, please state how the C++ functions treats the string so that I can tell you how to marshal the String.

Hope this helps,

MSFTAbelValadez at 2007-8-30 > top of Msdn Tech,Visual Basic,Visual Basic General...
# 2

Thank you for your patience Abel. And this application is going to be running on a 64 bit machine rather than the 32 bit which it has been running on as a vb6 app. Thanks for pointing that out. This is a graphics intensive app.

Question # 1. In what cases does a function use memory beyond the null-termination mark? What is a real world example of that?

Question # 2. What other cases might there be other than the two you mentioned? (string modification/memory usage beyond null-termination mark?)

I'll have to look at the 50-60 c++ functions in which these strings are consumed/modified on a case by case basis.

thanks,

-greg

This was helpful for me to read.... http://msdn.microsoft.com/msdnmag/issues/03/07/NET/

hazz at 2007-8-30 > top of Msdn Tech,Visual Basic,Visual Basic General...
# 3

Assume that you have the following C++ function:

void foo(char szName[128])
{

for (int i = 0; i < 128; i++)
{
char temp = szName[ i ];
}
}

If you call the function like this:

foo("ASimpleString")

You are only passing a string with 13 characters in it (14 with the null-terminator). However, the function assumes that the provided buffer is indeed 128 bytes long, so it happily goes and accesses random memory after position 14. If the memory past the null termination character is accessible, nothing will happen, but if it isn't you will get an access violation.

For those scenarios you will need to tell the marshaller to actually send 128 bytes, regardless of the size of the string.

Hope this helps,

MSFTAbelValadez at 2007-8-30 > top of Msdn Tech,Visual Basic,Visual Basic General...
# 4

How do I tell the marshaller to actually send 128 bytes.

<MarshalAs(UnmanagedType.ByValTStr,SizeConst:=128)> ByVal var as String ?

Thanks,

-g

hazz at 2007-8-30 > top of Msdn Tech,Visual Basic,Visual Basic General...
# 5

No, ByValTStr is only used within structures when you want to declare a fixed size char array.

If you want to send the full 128 bytes as a "char *" parameter, you can either marshal it as:

  • <MarshalAs(UnmanagedType.LPStr)> ByVal var as StringBuilder
    • Note that the size of the StringBuilderneeds to be set to 128 characters before calling the function:
      • Dim Buf As New StringBuilder(128)
      • Foo(Buf)

or

  • <MarshalAs(UnmanagedType.LPArray, SizeConst=128)> ByVal var() As Byte

Hope this helps,

MSFTAbelValadez at 2007-8-30 > top of Msdn Tech,Visual Basic,Visual Basic General...
# 6

Thank you Abel. I'm studying.. -greg

LPStr

A single byte, null-terminated ANSI character string. You can use this member on the System.String or System.Text.StringBuilder data types

or

LPArray

A pointer to the first element of a C-style array. When marshaling from managed to unmanaged, the length of the array is determined by the length of the managed array. When marshaling from unmanaged to managed, the length of the array is determined from the MarshalAsAttribute.SizeConst and the MarshalAsAttribute.SizeParamIndex fields, optionally followed by the unmanaged type of the elements within the array when it is necessary to differentiate among string types.

hazz at 2007-8-30 > top of Msdn Tech,Visual Basic,Visual Basic General...