?
Solved

Pass string from C# dll to C expecting LPStr

Posted on 2008-10-17
4
Medium Priority
?
3,524 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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

Enroll in August's Course of the Month

August's CompTIA IT Fundamentals course includes 19 hours of basic computer principle modules and prepares you for the certification exam. It's free for Premium Members, Team Accounts, and Qualified Experts!

Question has a verified solution.

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

Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
Real-time is more about the business, not the technology. In day-to-day life, to make real-time decisions like buying or investing, business needs the latest information(e.g. Gold Rate/Stock Rate). Unlike traditional days, you need not wait for a fe…
The goal of this video is to provide viewers with basic examples to understand recursion in the C programming language.
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
Suggested Courses
Course of the Month11 days, 5 hours left to enroll

770 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