Solved

Write specifically encoded string to unmanaged memory pointer from C#

Posted on 2008-10-22
10
1,334 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
  • 5
  • 3
  • 2
10 Comments
 
LVL 5

Expert Comment

by:jose_juan
Comment Utility
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
Comment Utility
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
Comment Utility
>>>> 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
 
LVL 5

Expert Comment

by:jose_juan
Comment Utility
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
Comment Utility
0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 

Author Comment

by:capsoftuk
Comment Utility
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
Comment Utility
>>>> 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
Comment Utility
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
Comment Utility
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
Comment Utility
Thank you for having a crack at this, your advice was good.
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Log4Net custom Appender not being fired. 5 41
XML to SQL Table using c# 5 44
asp.net bundle 8 32
Hide Tab Page 3 18
Summary: This tutorial covers some basics of pointer, pointer arithmetic and function pointer. What is a pointer: A pointer is a variable which holds an address. This address might be address of another variable/address of devices/address of fu…
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.
The goal of this video is to provide viewers with basic examples to understand how to use strings and some functions related to them 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.

763 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

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now