Solved

Write specifically encoded string to unmanaged memory pointer from C#

Posted on 2008-10-22
10
1,352 Views
Last Modified: 2012-05-05
I'm working on managed and unmanaged code interop and implementing a Win32 dll using C# as described here http://blong.com/Conferences/BorConUK2002/Interop1/Win32AndDotNetInterop.htm#ExportingMethods

I have a C (or possibly C++) application which can make calls to Win32 dlls registered with it as integration dlls. It expects these dlls to have implemented particular entry points. I have already gotten 3 of these entry points to work, 2 return ints and 1 returns an int and copies a byte[] to a pointer passed in from the C application.
The fourth one (the problem) returns an int, and should also copy a string to a pointer. I've tried implementing it in the same way that worked for the previous method (see attached code snippet) but it's not working for me this time.

The documentation for the C application states that the integration dll must implement the interface as:
long GetField(long key, LPCSTR fieldName, long typeID, LPSTR data, long maxLen)

The LPSTR data parameter it details as:
A buffer to contain the field value, as an 8-bit string with Windows-1252 encoding. Pass the string <^UNANSWERED^> to indicate that the field should be set to unanswered. If you pass an empty string, the application will not change the current answer for that variable.

Since char in C# is 16 bits and char in C/C++ is 8 bits I have tried writing a byte[] to the unmanaged memory as shown in the code snippet. I have also attempted to encode these bytes as described above.

I don't have access to the code of the C/C++ application so I can't tell what it's receiving but it doesn't seem to be reading anything from the pointer.

This is an experiment I'm doing and writing it in C or C++ would defeat the purpose.

Any ideas?

Thanks,
Colette
public static int GetField( int key, string fieldName, int typeID, IntPtr data, int maxLen)
{
   string retData = "Hello";
   Encoding enc = Encoding.GetEncoding("Windows-1252");
   int len = maxLen;
 
   try{
      byte[] ba = enc.GetBytes(retData);
      if (ba.Length > maxLen){
         len = maxLen;
      }
      else{
         len = ba.Length;
      }
      Marshal.Copy(ba, 0, data, len);
   }
   catch (Exception ex){
      LogMessage(ex.Message + Environment.NewLine + ex.StackTrace, EventLogEntryType.Error);
   }
   return len;
}

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
  • 5
  • 3
  • 2
10 Comments
 
LVL 5

Expert Comment

by:jose_juan
ID: 22774866
Hi,

to simplify the problem, you can make a wrapper throught COM objects. Make a COM object to access your shadowed dll and use this COM from C#.

You can access easily to COM from C#. e.g. see:

http://blogs.msdn.com/sanpil/archive/2004/07/07/175855.aspx

Good luck!

0
 

Author Comment

by:capsoftuk
ID: 22775352
Unfortunately that is not what my problem is.
What I'm trying to do it the reverse of what you've suggested, I'm trying to call C# from C.

Usually what we do in this situation is to create a C dll which calls the C# dll. The C dll can then be called by the application that it's integrating with and run our C# code that way.

However since I found a way to expose a C# dll as a Win32 dll I want to see if I can ditch the C dll intermediate between the C application and it calling the C# dll.
0
 
LVL 39

Accepted Solution

by:
itsmeandnobodyelse earned 500 total points
ID: 22776022
>>>> I can't tell what it's receiving but it doesn't seem to be reading anything from the pointer.
When passing UNICODE char strings to a C/C++ which expects ANSI (single-byte) strings, the receiving function would get a length of 0 or 1 when calling strlen for the buffer, cause the 16bit-char-stream contains binary zero characters for any second byte. So, you would need to marshal your string from wide-string (UNICODE) to multi-byte (ANSI)(actually it is single-byte beside of some single language-specific exceptions) in order to remove these terminating zero chars.

Encoding is the wrong function (or may do worse) cause it doesn't change from wide chars to single chars but only the charset. You need to use one of the marshalAs functions (unfortunately I never used C# myself).
0
Salesforce Has Never Been Easier

Improve and reinforce salesforce training & adoption using WalkMe's digital adoption platform. Start saving on costly employee training by creating fast intuitive Walk-Thrus for Salesforce. Claim your Free Account Now

 
LVL 5

Expert Comment

by:jose_juan
ID: 22776662
Ops!

then I think you need this

http://www.experts-exchange.com/Programming/Misc/Q_21840589.html

Regards.
0
 
LVL 5

Expert Comment

by:jose_juan
ID: 22776684
0
 

Author Comment

by:capsoftuk
ID: 22923527
Hi itsmeandnobodyelse,
I've been fiddling about with your idea that the string needs to be converted from wide characters but without success so far.
I've imported WideCharToMultiByte from kernel32.dll but although it's return value says it's copied the correct number of bytes or characters the location it's copying to seems to remain empty and I'm getting error 997 as the last win32 error.

I'm revisiting the idea that the Encoding class can help me and I've found a IsSingleByte property of Encoding which seems to indicate which encodings have multiple bytes and which have only 1 and the encoding I was using originally seems to only have 1. http://msdn.microsoft.com/en-us/library/system.text.encoding.issinglebyte(VS.80).aspx
The Encoding class in c# does seem to be about more than just the character set http://msdn.microsoft.com/en-us/library/system.text.encoding%28VS.80%29.aspx.

I'm going to do some more experimentation with this since I've hit a bit of a dead end with WideCharToMultiByte but I'd appreciate it if perhaps you could point me in the direction of any further information on this or any alternatives to or examples on WideCharToMultiByte conversions.

Thanks,
Colette
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 22926914
>>>> I'm getting error 997 as the last win32 error.

997 is a dderror

// Overlapped I/O operation is in progress.
//
#define ERROR_IO_PENDING                 997L    // dderror

It hardly has somewhat to do with conversion of wide-char to multi-byte.

>>>> it's return value says it's copied the correct number of bytes or characters the location it's copying to seems to remain empty

As told I have not much knowledge of C# but the idea to use a kernel function WideCharToMultiByte - which of course is unmanaged code - to convert a C# string to a byte array seems to be rather strange.  Why didn't you use any of the marshalAs functions which really are supposed to turn from managed (.NET) code to unmanaged (C) code. You also could pass the wide string (plus a length argument) and do the WideCharToMultiByte at the C++ side. That also would make sense, imo.
0
 

Author Comment

by:capsoftuk
ID: 22932480
Yeah I didn't think that the 997 error was necessarily a problem with the conversion I've no idea what could be causing it.

I've tried Marshal.StringToHGlobalAnsi for copying the text and while I can read the text copied to the target location using Marshal.PtrToStringAnsi it's still not coming out in my application, although it's progress at least since I can confirm that it's there. Ultimately I don't think these are suitable because the C++ application is expecting the string in such a specific encoding (ISO-8859-1).
I don't have access to the C++ code to do the WideCharToMultiByte on that side unfortunately.
I'll keep plugging away, I'm bound to either get it or come to the conclusion that it's not possible fairly soon.
0
 

Author Comment

by:capsoftuk
ID: 22938888
So funny thing, turns out that it was probably working fine all along.
The API documentation I was using was wrong and once I corrected the return value hey presto everything worked.
So what I've got in place at the moment that's working is as follows, the byte array and Marshal.Copy lines being the crucial points.

Thank you for your suggestions.
public static int GetField( int key, string fieldName, int typeID, IntPtr data, int maxLen)
{
   HDIsAvailable result = HDIsAvailable.HD_SUCCESS;
 
   string retData = "Hello";
   Encoding ISO_8859_1 = Encoding.GetEncoding(28591);
               
   int len = maxLen;
   try{
      byte[] ba = ISO_8859_1.GetBytes(retData);
                
      Marshal.Copy(ba, 0, data, ba.Length);
 
   }
   catch (Exception ex){
      result = HDIsAvailable.HD_FAILURE;
   }            
   return (int)result;
}

Open in new window

0
 

Author Closing Comment

by:capsoftuk
ID: 31508671
Thank you for having a crack at this, your advice was good.
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

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

This is a short and sweet, but (hopefully) to the point article. There seems to be some fundamental misunderstanding about the function prototype for the "main" function in C and C++, more specifically what type this function should return. I see so…
This article aims to explain the working of CircularLogArchiver. This tool was designed to solve the buildup of log file in cases where systems do not support circular logging or where circular logging is not enabled
The goal of this video is to provide viewers with basic examples to understand recursion in the C programming language.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

733 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