Using Win32 dll in C # application (char * return to C # problem)

I am working on a C # application (using win 32 dll in my application) ... I am trying something like this In a DLL (test.dll):

char* Connect(TCHAR* lpPostData) { char buffer[1000]; ..... return buffer; } 

In a C # application:

 [DllImport("test.dll", EntryPoint = "Connect", CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.LPWStr)] public static extern string Connect(StringBuilder postdata); string returnedData = Connect(postdata); 

But the data return does not happen properly .... Pls can any body tell me where I am going wrong Thanks in advance

+4
source share
5 answers

TCHAR * tells me that it enters unicode (UNICODE is defined by CE), but char * tells me that it is rather a byte array (or ascii string), and the one who created this API should be deleted to mix the two, since this is really a very bad design.

Of course, you cannot marshal the return as a wide string of characters, because this is not one. On the desktop, you should use Tony's suggestion, but MSDN (and practice) clearly shows that it is not available in CF (I donโ€™t know why MS thought we would not need it).

Devices Smart Device Framework has it . Another option is to use a marshal to copy from the returned pointer to an array of bytes, and then use Encoding.ASCII to turn this array into a string. Of course, this points to another obvious flaw in this API, which should not return a string in the first place.

EDIT 1

Since I see other suggestions about what you should do with which I really disagree, I should give you an example:

Your native call should look something like this:

 extern "C" __declspec(dllexport) const BOOL __cdecl Connect(TCHAR* lpPostData, TCHAR *returnBuffer, DWORD *returnSize) { // validate returnSize, returnBuffer, etc // write your data into returnBuffer TCHAR *data = _T("this is my data"); _tcscpy(returnBuffer, data); *returnSize = (_tcslen(data) + 1) * sizeof(TCHAR); return succeeded; } 

Please note that I am simply returning a success code. Text data is passed as a pointer along with the length for it (so the API knows how much space it can use and can return how much it used). It's also not that I agree with my string variable data types, and I use TCHAR macros, which will become wchar_t under CE, which is consistent with the rest of the OS (which has virtually no ASCII API).

Most of the WIn32 API suite works the same way.

Your P / Invoke ad, it's very simple:

 [DllImport("test.dll", SetLastError=true)] private static extern bool Connect(string postData, StringBuilder data, ref int length); 

Using this is also simple:

 void Foo() { int length = 260; StringBuilder sb = new StringBuilder(length); if(Connect("Bar", sb, ref length)) { // do something useful } } 

Note that StringBuilder must get initialized to a certain size, and this size is what you pass in front of the third parameter.

+2
source

You are trying to push a variable onto the stack; it will not work. You can declare a buffer static as a proof of concept to get a marshaling job.

+2
source

There are several drawbacks, as indicated in the various answers that you have. To summarize everything, here is what I suggest.

On the inside, declare something like this (assuming you are coding in C ++):

 extern "C" __declspec(dllexport) const char* __cdecl Connect(const char* lpPostData) { static char buffer[1000]; ... return buffer; } 

extern "C" makes it clear to the compiler that it should not distort the name of the function when exporting it, but should treat it the same way as processing the function C.

__ceclspec(dllexport) makes the function visible in the DLL as an exported entry point. You can then reference it from external projects.

__cdecl ensures that you use the C method to pass arguments on the stack. You could have badly damaged things if the caller does not accept the same argument passing scheme.

Use char sequentially: either sick before char (ANSI text) or wchar_t (16-bit Unicode text). And never, never, return a pointer to a local variable, as it was in your example. As soon as the function returns, this memory location will no longer be valid and may contain garbage. In addition, if the user changes the data stored there, this may lead to a program crash, as this will lead to a failure of the call stack.

On the managed side of C #, here's what I recommend:

 [DllImport("test.dll", EntryPoint="Connect", CharSet=CharSet.Ansi, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl)] private static extern System.IntPtr Connect(string postData); 

which will be bound to your Connect entry point, use the ANSI conditional convention (8 bits per character, as expected by char ), and make sure the caller is expecting __cdecl convection.

You will need to march the IntPtr object to the string object by calling:

 string value = Marshal.PtrToStringAnsi (Connect (postData)); 

Note: in my original post, I recommended declaring Connect as a return string , but as Mattias's comment fixes, this is incorrect. doing so. See this article in the CLR Inside Out for an explanation of how the CLR handles retval type string . Thanks for pointing this out to me (and thanks to romkyns ).

You can also consider copying a string from buffer to a piece of memory allocated through CoTaskMemAlloc in your own code and returning a pointer to it. Then you can simply declare the DllImport function returning a string and not need further sorting in a controlled world.

+1
source

The return type of your function must be IntPtr , and then use Marshal.PtrToStringAnsi to get a string from your IntPtr .

0
source

Here is an example of how you could do what Tony suggested:

 [DllImport("test.dll", EntryPoint="my_test_func", CharSet=CharSet.Ansi, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl)] private static extern IntPtr my_test_func_imported(); public static string my_test_func() { return Marshal.PtrToStringAnsi(my_test_func_imported()); } 

On the unmanaged side:

 const char *my_test_func(void); 

(this imports the C function from "test.dll")

PS As indicated by ctacke, this is probably unsuitable for .NET CF.

0
source

Source: https://habr.com/ru/post/1299939/


All Articles