Link to home
Start Free TrialLog in
Avatar of capsoftuk
capsoftukFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Pass string from C# dll to C expecting LPStr

I'm trying to write a passive integration dll.
The application expects a win32 dll which implements particular methods, these methods should be "declared to use the WINAPI calling convention (__stdcall in Visual C)" apparently.

As an experiment I'm trying to write this dll in C# (I don't know C or C++) and I'm disassembling it with ildasm manually editing the file to expose the methods and then reassembling it with ilasm as per this site http://blong.com/Conferences/BorConUK2002/Interop1/Win32AndDotNetInterop.htm#ExportingMethods. This bit actually seems to be working fine and the application is certainly picking up the dll and can get the correct results from a method which just returns an int (C# int being the equivalent of C long).
Where I'm running into trouble is when I'm trying to implement a method which should have the following interface according to the application documentation
long GetFieldName(LPSTR fieldName, long fieldNum)
Now I can return the correct long value and I'm receiving the expected fieldNum value correctly but the method should then write a value to the fieldName parameter and the application then picks up this value.
What I'm getting out of this at the moment is gibberish.
The documentation says this about the fieldName parameter

LPSTR fieldName [out]: The buffer for the field name. It can hold up to a maximum of 50 characters plus a NULL terminator.


I've attached the current state of the code for this method.
As you can see I'm trying to use MarshalAs(UnmanagedType.LPStr) with a StringBuilder which is passed out. I have also tried this with LPWStr, a string, without the out keyword and without Marshaling.
I haven't tried it as a ref as I didn't think this would be valid, is this the wrong assumption?

I don't have access to change the application code, only the C# dll.
Any suggestions are appreciated, except those to just write it in C or C++.

Thanks,
Colette
public static int GetFieldName([MarshalAs(UnmanagedType.LPStr)]out StringBuilder fieldName, int fieldNum)
{
    LogMessage("In GetFieldName, fieldNum is " + fieldNum.ToString(), EventLogEntryType.Information);
    int retVal = 0;
    fieldName = new StringBuilder("", 49);
 
    try{
       if (fieldNum == 0){
          fieldName.Append("Hello");
          retVal = 1;
       }
       else if (fieldNum == 1){
          fieldName.Append("Goodbye");
          retVal = 1;
       }
       else{
          retVal = 0;
       }
   }
   catch (Exception ex){
       LogMessage(ex.Message + Environment.NewLine + ex.StackTrace, EventLogEntryType.Error);
   }
return retVal;
}

Open in new window

Avatar of jkr
jkr
Flag of Germany image

http://msdn.microsoft.com/msdnmag/issues/02/08/CQA/ pretty much targets this issue. E.g.

C/C++ DLL ("returnstring.dll"):  

#include <windows.h>
#include <tchar.h>

int GetString(LPSTR buf, int nMaxCount)
{
      TCHAR* pszText = _T( "Hi from unmanaged code");
      int nLen = _tcslen(pszText);

      if (nLen < nMaxCount)
      {
         _tcscpy(buf,pszText);

         return nLen;
      }

      return 0;
}

C#

using System.Text; // for StringBuilder
public class ReturnStringDll{
  [DllImport("returnstring.dll")]
  public static extern int GetString(StringBuilder buf, int nMaxCount);
}

StringBuilder cb = new StringBuilder(256);
ReturnStringDll.GetString(sb, sb.Capacity);

That works for just displaying a string on the C/C++ side also.
Avatar of capsoftuk

ASKER

Would that it were that simple jkr :)
I haven't explained myself very well.
I'm not calling the C methods, I don't have access to them at all.
This application that I'm working with lets you write dlls to expand it's functionality.
One of the types it supports requires you to create a dll that implements the method I outlined in my first post GetFieldName and then I just need to register the dll with the application so it can see it.
It's the application that's calling the C# dll, not the other way around.

So the C application is passing the LPSTR parameter into the C# dll method and I don't know how to manipulate the parameter.
ASKER CERTIFIED SOLUTION
Avatar of capsoftuk
capsoftuk
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
>>>> I ditched the ideal the LPSTR was a string and could be handled by a
>>>> StringBuilder or string and went with the idea that it's just a pointer.

A LPSTR is typedef of char* what is a (writeable) pointer to a char or (the first element of) a char array. If the char array has any contents, then it is  a zero-terminated string (chance>99%) which indeed could be handled by any string class of any language.

What could make the "gibbering" is that the char sequence is ANSI and the C# StringBuilder most probably has UNICODE strings. But the main reason why your first approach seem to be unsuccessful is that  were clearing the passed fieldName by

  fieldName = new StringBuilder("", 49);

while in the second approach you were marshaling the given char sequence to a byte array - what succeeds as a 'byte' is a unsigned char what is bit-identically with a (signed) char.