strange problem with pinvoke RRS feed

  • General discussion

  • We have some legacy c++ dll libraries and I am using one of the library functions,

    the signature of that function is

    char* function( char* pszInputString, char* pszInOutString, ... );

    I translated the function to

    [DllImport("legacy.dll", CharSet = CharSet.Ansi, EntryPoint = "function", PreserveSig = true, ThrowOnUnmappableChar = true)]
    internal extern static IntPtr function([In]IntPtr srcString, StringBuilder rstString, ref ToSomeStruct ccb, ref ToSomeAnotherStruct crb);

    now I get correct results when I launch with visual studio in debug and release build, but when I launch the application using I get access violation exception surrounding the pinvoke function.

    anybody has any idea regarding this problem

    I even turned of all optimizations :-(

    Monday, March 23, 2009 1:56 PM

All replies

  • Any time you pass raw pointers you have a chance of getting access violations if you aren't careful about the contents of that pointer.

    Since you specified CharSet = CharSet.Ansi, all strings will be copied as ANSI strings for the native call. So there's no reason to use IntPtr for srcString. Just pass the string without ref/out modifiers.

    If the contents will be modified, then you should pass a StringBuilder, which is what you are doing with pszInOutString (good). Just make absolutely sure that the stringbuilder is set appropriately before you make the call (for example, add as many spaces or null characters as necessary before calling the method). Since the C++ sig isn't char**, the native function can't reallocate that parameter, it can only modify the contents of the string passed in.

    Other problems can come from struct parameters, especially since they are being passed by ref, but since you didn't provide the original signatures or the struct definitions themselves, it'll be difficult to tell.
    -Rob Teixeira
    Monday, March 23, 2009 7:41 PM
  • the sturcture members are all unsigned int type.. but one of the structure is of type [in,out].. do I have to pin it first before passing it as a ref variable ? since ref is sort of in/out, I didn't do it.

    the first string is not modified i.e. a const char* type, but the second one is a buffer to  hold an ansi string and it will not exceed 4096, so I made a stringbuilder with 4096 capacity.
    for the first string I use the Marshal.StringToHGlobalAnsi() to get an IntPtr. I have earlier tried this parameter as just a managed string type. but the result is the same when I lauch it from the explorer window ( double clicking the exe ). The debug build of the exe works this way perfectly.

     first I find it strange because the program run with visual studio ( both debug and release ) , when I launch from explorer I get this access violation.

    Monday, March 23, 2009 8:47 PM
  • It is certainly a very troublesome function signature.  There are lots of ways to blow your foot off with a function that returns a char*.  The only safe way for that to work is when the function allocates the string on the heap.  In which case you'll leak memory badly, there's no way for you to release the string.  If the function actually returns a char* that points to a buffer on the stack (classic unmanaged C/C++ coding mistake), you get the problem that it works in some cases but not others.
    Hans Passant.
    Tuesday, March 24, 2009 1:13 AM
  • Well, I am allocating memory on the heap for the first string using Marshal.StringToGlobalAnsi() and for the buffer I use StringBuilder... now my only doubt is, whether the CLR changes the memory location of the StringBuilder passed in, since it is a in/out parameter, I am not sure whether the buffer pointed by the StringBuilder is pinned... because the function is returning the same memory buffer. Old C style way of doing things. I think I had tried the StringBuilder parameter with IntPtr and allocated memory using Marshal.AllocHGlobal... Same problem

    I was wondering why this problem was not detected by visual studio !!! I am thinking to write the pinvoke in managed C++ (have to learn it quictk ) will it solve the problem ? because I wrote a C++ client and it works fine. :-(
    Tuesday, March 24, 2009 8:52 AM
  • Since the function you're calling is a vararg function, you should almost certainly specify the cdecl calling convention in the DllImport attribute (the default is stdcall).

    Since the StringBuilder uses unicode internally, its internal buffer will not be passed directly to the function. Instead the CLR will allocate a separate ANSI buffer and do the necessary copying back and forth. If the function returns a pointer to a location inside the rstString buffer, you can't expect that pointer to still be valid when the function returns.

    Mattias, C# MVP
    Tuesday, March 24, 2009 1:30 PM
  • Hi Mattias,   thanks for your buffer allocation points with StringBuilder. first of all sorry about the confusion with the vararg function, it is not so.. I just put like that to same the typing sorry about that.

    char* function( char* pszInputString, char* pszInOutString, ToSomeStruct* pSomeStruct, ToSomeAnotherStruct* pSomeAnotherStruct);

    I have a couple of questions about the points you made about StringBuilder,

    if I have applied the Charset as ansi, then should the StringBuilder be allocated  according to the needs of ansi ?

    if the CLR does not pin the StringBuilder's underlying storage, how does it keep track of it when the native function copies the results back and forth ?  or the CLR allocate two different buffers to do this function call (one for StringBuilder and the other Ansi buffer you talked about ) ?

    can you explain a bit ?


    Tuesday, March 24, 2009 2:02 PM
  • said:

    or the CLR allocate two different buffers to do this function call (one for StringBuilder and the other Ansi buffer you talked about ) ?

    Yes, you'll have two buffers.
    Mattias, C# MVP
    Wednesday, March 25, 2009 8:35 AM