Solved

Pass string from C# dll to C expecting LPStr

Posted on 2008-10-17
4
3,442 Views
Last Modified: 2012-06-22
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

0
Comment
Question by:capsoftuk
  • 2
4 Comments
 
LVL 86

Expert Comment

by:jkr
ID: 22741836
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.
0
 

Author Comment

by:capsoftuk
ID: 22742072
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.
0
 

Accepted Solution

by:
capsoftuk earned 0 total points
ID: 22742414
I've sorted it.
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.
I've included my updated code.
Basically I've set the expected parameter to be an IntPtr and then copied an array of bytes to it using Marshal.Copy.
Works like a charm and now I've only got another 2 even more complicated such scenarios to work through.
public unsafe static int GetFieldName(IntPtr fieldName, int fieldNum)
{
   int retVal = 0;
   string fName = "";
   try{
      if (fieldNum == 0){
         fName = "Hello";
         retVal = 1;
      }
      else if (fieldNum == 1){
         fName = "Goodbye";
         retVal = 1;
      }
      else{
         retVal = 0;
      }
      byte[] ba = Encoding.ASCII.GetBytes(fName);
      Marshal.Copy(ba, 0, fieldName, ba.Length);
      
   }
   catch (Exception ex){
      LogMessage(ex.Message + Environment.NewLine + ex.StackTrace, EventLogEntryType.Error);
   }
   return retVal;
}

Open in new window

0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 22748109
>>>> 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.



0

Featured Post

NAS Cloud Backup Strategies

This article explains backup scenarios when using network storage. We review the so-called “3-2-1 strategy” and summarize the methods you can use to send NAS data to the cloud

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Windows programmers of the C/C++ variety, how many of you realise that since Window 9x Microsoft has been lying to you about what constitutes Unicode (http://en.wikipedia.org/wiki/Unicode)? They will have you believe that Unicode requires you to use…
Calculating holidays and working days is a function that is often needed yet it is not one found within the Framework. This article presents one approach to building a working-day calculator for use in .NET.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

810 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question