P/Invoke sometimes cause Win32 1008 Error with StringBuilder parameters - c#

I have a DLL from which I need to P/Invoke the following C method:
int DAStart(
HANDLE hOpen,
char* IPAddress,
int IPPort,
int threadPriority,
char* name,
char* password,
char* userName)
Using the P/Invoke Assistant and my own research, I've come up with the following C# signature:
[DllImportAttribute("<libName>", EntryPoint="DAStart")]
static extern int DAStart(
IntPtr hOpen,
[MarshalAs(Unmanaged.LPStr)] StringBuilder IPAddress,
int IPPort,
int threadPriority,
[MarshalAs(Unmanaged.LPStr)] StringBuilder name,
[MarshalAs(Unmanaged.LPStr)] StringBuilder password,
[MarshalAs(Unmanaged.LPStr)] StringBuilder userName);
Now, I'm doing the call in the following way:
int port = 3000;
int threadPriority = 20;
DAStart(
this.nativeDllHandle, // an IntPtr class field
new StringBuilder("10.0.10.1"),
int port,
int threadPriority,
new StringBuilder("admin"),
new StringBuilder("admin"),
new StringBuilder("admin"));
Now, sometimes this works just fine, but something I get a Win32 Error 1008 - An attempt was made to reference a token that does not exist on following calls to this library.
Could it be that my StringBuilder objects get garbage collected so that the reference no longer exist if the native code tries to use it? Should I keep a reference for each one of them?
Would an IntPtr be better solution for passing my strings in that case?
** UPDATE **
This is all the API documentation I have for DAStart:
Inputs
HANDLE hInfo The handle returned by the DAOpen
char *IPAdress_in IP Address of the TMEE Server
int IPPort Console Port of the TMEE Server (default is port 3000)
int threadPriority The thread priority for the send file thread.
char *name Not used in Hardware DLL
char *password Not used in Hardware DLL
char *username Not used in Hardware DLL
Returns
ERROR_SUCCESS 0
ERROR_BAD_HANDLE -1
ERROR_BIND_FAILED -10
Comments
The DAStart API connects the client dll to the active TMEE Server service. The client thread is started with a priority set to the threadPriority parameter. The IP address parameter must be set to the address of the TMEE Server. The port parameter must be set to the port the TMEE Server listens on for Console connections (the default port the Consoles use is 3010). The Console thread is started with a priority set to the threadPriority parameter.

In the comments you indicate that the DLL takes a copy of the char* pointers (and not the contents of the strings) and then modifies the contents of the strings after DAStart returns.
In the face of this very unconventional interface design your only option is to take charge of the marshaling of the string parameters. You cannot use StringBuilder since the char* passed to the native code is valid only during the pinvoke call. It cannot be relied upon to be valid after the pinvoke call returns.
So your only solution is to pass the string parameters as IntPtr. Allocate the memory with Marshal.StringToHGlobalAnsi. Pass the resulting IntPtr to DAStart. When you are sure the DLL is done with the pointer, deallocate with Marshal.FreeHGlobal.

References outside the managed environment are unknown to the Garbage collector.
Thus you need to keep an additional reference somewhere as long as you need to the object - easiest is via a static property in the calling class.
An IntPtr does not make a difference - it will get collected as well.

Related

C# COM server interop - System.InvalidCastException

I have created a COM server in C# which serves a specific device and is consumed by a C++ COM client (customer application, no source available).
Customer application among other things requires periodic updates, and for that purpose function called Notify() is used. That function is implemented in unmanaged COM client code.
All is well when Notify() is called from within same thread where this notification listener object was created. But this can happen only once since C++ COM client initially asks for data just once.
And even though it asks for data just once, it doesn't seem to tolerate responses which take over a second or so. And my device is kind of slow to react, especially over network, well over a second to get a reading of some sort. Ok, I get that, it wants swift response.
So not only to achieve periodic updates, but also to relieve main thread of waiting for data, I have created a separate thread which probes a device every minute and then reports results back to unmanaged code, by calling Notify().
When this Notify() is called from a different thread, I get a very nasty System.InvalidCastException. The exception was thrown from deep within CLR.
This was on a production system. On my own development system where I have my own C++ COM client mock up, this exception is masked by ContextSwitchDeadlock during debugging session. I can switch this one off, but doesn't change the fact that Notify() doesn't behave well cross thread. In fact, according to logs, function never returns.
What to do next?
From what I could tell from the registry, COM threading model is set to 'Both'.
I have tried to remedy the problem by creating a small C++ dll, which would act as a wrapper for Notify(). So, instead of calling directly into unmanaged COM client code using this:
[MethodImpl(MethodImplOptions.InternalCall)]
void Notify([In] uint dwCount, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.BStr, SizeParamIndex = 0)] string[] psAddresses, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct, SizeParamIndex = 0)] object[] pvarValues, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.R8, SizeParamIndex = 0)] double[] pdtTimestamps);
I have created small dll with just this function:
extern "C" __declspec(dllexport)
void CppNotify(INotificationListener *pNotifier, unsigned long dwCount, BSTR *psAddresses, VARIANT *pvarValues, DATE *pdtTimestamps)
{
pNotifier->Notify(dwCount, psAddresses, pvarValues, pdtTimestamps);
}
Which gets called using this difinition:
[DllImport("CppNotifier.dll", CallingConvention = CallingConvention.Cdecl)]
extern public static void NotifyCpp([In] [MarshalAs(UnmanagedType.Interface)] INotificationListener pNotifier, uint dwCount, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.BStr, SizeParamIndex = 0)] string[] psAddresses, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct, SizeParamIndex = 0)] object[] pvarValues, [In] [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.R8, SizeParamIndex = 0)] double[] pdtTimestamps);
I was hoping that this would avoid System.InvalidCastException, but no luck yet.
Our C# COM server targets .NET 4.0, while .NET version installed on system is 4.7.1. Could this trigger such a problem?
I have also tried to re-register C# dll using RegAsm.exe, but this didn't seem to have any effect.
You need to marshall a interface to your object from the main thread to the worker thread, and call the Notify method using on this interface.
Define the following two member variables in your class:
IStream* m_StreamThis ;
<your interface>* m_pMarshalledThis ;
Before creating your thread, call the function CoMarshalInterThreadInterfaceInStream().
CoMarshalInterThreadInterfaceInStream ( <your interface>, this, &m_StreamThis ) ;
The second parameter is a pointer to IUnknown. Inside your class, which must implement IUnknown, I think that it is OK to specify "this".
The thread entry point cannot be a class member, so a common practice is to pass the object pointer as the parameter to the thread and then to use it to call a member function. I assume that you are doing this already.
In the thread, after calling a member function, call the function CoGetInterfaceAndReleaseStream().
CoGetInterfaceAndReleaseStream ( m_StreamThis, <your interface>, (void**)&m_pMarshalThis ) ;
When you need to call the Notify function, call it using the marshalled pointer:
m_pMarshalThis->Notify() ;
This will result in the function being called on the main thread.
When your thread terminates, you should of course free up the pointer m_pMarshalThis. You might want to attach it to a smart pointer class.
And of course, you should really check the HRESULT returned by both of the function calls, but that is up to you.
You can certainly find other (and better) examples of how to use these functions on Stack Overflow or other sites.

How to safely pass string reference from c# to c++?

I am developing a C# dll project with C++ dll project.
Let's say that C++ dll logins to a certain web site, and do some query on the web server.
And C++ dll has to return that html code of a web site.
In the same time, C++ dll must save the cookie data from the web site.
So, I passed StringBuilder object to C++ function.
I already know how to get html code from a web site using HttpWebRequest and HttpWebResponse classed in C#, but unfortunately I have to do it in C++ dll project.
So bear in mind, I don't need any C# codes.
I have tried Passing a string variable as a ref between a c# dll and c++ dll.
Passing StringBuilder from C# and get it as LPTSTR.
It works fine, but some strings were missing from the result.
I couldn't find out the reason.
Anyway, here is my code.
C++
extern "C" __declspec(dllexport) BSTR LoginQuery(const char* UserID, const char* UserPW, char Cookies[])
{
std::string html;
try
{
std::map<std::string, std::string> cookies;
MyClass *myclass = new MyClass();
html = myclass->LoginQuery(UserID, UserPW, cookies);
// Response cookies
std::string cookieData;
for (std::map<std::string, std::string>::iterator iterator = cookies.begin(); iterator != cookies.end(); iterator++)
{
cookieData += iterator->first;
cookieData += "=";
cookieData += iterator->second;
cookieData += ";";
}
sprintf(Cookies, cookieData.c_str(), 0);
delete myclass;
}
catch (...)
{
}
return ::SysAllocString(CComBSTR(html.c_str()).Detach());
}
C#
[DllImport(#"MyDll.dll", EntryPoint="LoginQuery", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern void LoginQuery(string UserID, string UserPW, StringBuilder Cookies);
void SomeThing()
{
StringBuilder _cookies = new StringBuilder(1024);
string result = LoginQuery("test", "1234", _cookies);
}
It works fine.
With the StringBuilder as cookie, I can carry on the next url of the web site.
(I am using libcurl in C++ project.)
But the problem is that I have about 100 ids.
When it runs about 3~40, it returns heap error.
Debug Assertion Failed!
Program: ~~~~mics\dbgheap.c
Line: 1424
Expression: _pFirstBlock == pHead
I cannot click the abort, retry or ignore button.
It looks like C# application hangs.
I read so many articles about debug assertion failed with dbgheap.
Mostly like free memory object from another heap.
I am newbie to C++.
I read Edson's question on http://bytes.com/topic/c-sharp/answers/812465-best-way-interop-c-system-string-c-std-string.
But the error does not always comes out in certain time.
So I came across with a guess, that it happens when .NET runs garbage collector.
.NET garbage collector tries to free some memory which created from C++ dll and I get the heap error.
Am I right?
I think he's suffering same problem as mine.
What is the best way to avoid heap error and return correct string from C++ dll to C# dll?
P/S: And the heap error occurs only when I run it debug mode. I don't get the error when I run release mode.
EDIT
According to WhozCraig answer, I changed my code as below.
//return ::SysAllocString(CComBSTR(html.c_str()).Detach());
CComBSTR res(html.c_str());
return res.Detach();
But no luck, I still get the heap error.
Your question is title asks about passing a string reference from c# to c++, but later in the text you ask how to return a string from C++ to C#. Also, you tell you are new to C++. With this in mind, I'll tell how I did this sort of interaction last time I had to do that. I just made C++ side allocate and free all the memory, passing out to C# only IntPtrs to be Marshal.PtrToStringAnsi(p)ed. In my case, storing recerences thread-local in C++ and freeing them on each function call was enough, but you can make a C++ function that frees whatever ref it is given. Not very intellectual and not necessarily the most efficient way, but it works.
upd:
It does just what they say it does. Some quick googling comes up with this article. I think it's pretty good, so you can refer to it instead of my suggestion. Passing raw IntPtrs is good if the pointer is not okay to be freed by itself (like old Delphi/C++Builder style strings, for example) and you prefer to be bothered more on the managed side than on the native side.
As an example, piece of code doing Delphi interaction (good for C++ Builder as well):
// function Get_TI_TC(AuraFileName:PAnsiChar; var TI,TC:PAnsiChar):Boolean; stdcall; external 'AuraToIec104.dll' name 'Get_TI_TC';
[DllImport("AuraToIec104")]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool Get_TI_TC(string AuraFileName, out IntPtr TI, out IntPtr TC);
public static bool Get_TI_TC(string AuraFileName, out string TI, out string TC)
{
IntPtr pTI, pTC;
bool result = Get_TI_TC(AuraFileName, out pTI, out pTC);
TI = Marshal.PtrToStringAnsi(pTI);
TC = Marshal.PtrToStringAnsi(pTC);
return result;
}
It looks like your problem is rather simple. You are creating a StringBuilder that can hold as much as 1024 chars. If your C++ function returns more than that, your application will crash (sooner or later).
So to fix your problem, increase the StringBuilder's size to the maximum possible output length. More details: Passing StringBuilder to PInvoke function which quotes:
The only
caveat is that the StringBuilder must
be allocated enough space for the
return value, or the text will
overflow, causing an exception to be
thrown by P/Invoke.
It might actually be better in your case with dynamic string lengths to use a BSTR parameter in the C++ function. You can then use [MarshalAs(UnmanagedType.AnsiBStr), Out] ref string ... in C# and BSTR* in C++.

Mapping IntPtr into Struct via Marshal.PtrToStructure causing "Access Violation Exception"

I'm trying to use a C++ DLL (3rd party library implementing EMI protocol, having source code available) in .NET. I've succesfully done marshalling, calling the functions and getting everything work fine.
The problem occurs when I want to do marshalling from IntPtr back into the .NET Struct , here's the code (modified as suggested - removed "ref" and changed the AllocHGlobal to allocate just size of emiStruct) :
private EMI emiStruct;
private IntPtr emiIntPtr;
emiIntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(emiStruct));
Marshal.StructureToPtr(emiStruct, emiIntPtr, false);
EMIStruct.Error result = emi_init(emiIntPtr, hostname, portNumber, password, shortNumber, windowSize, throughput);
Marshal.PtrToStructure(emiIntPtr, emiStruct);
The last line (PtrToStructure) causes an exception "Attempted to read or write protected memory. This is often an indication that other memory is corrupt".
Also, I can see a debug output:
A first chance exception of type 'System.AccessViolationException' occurred in mscorlib.dll
First-chance exception at 0x7c970441 in XXXXX.exe: 0xC0000005: Access violation reading location 0xc3fffff8.
First-chance exception at 0x7c970441 in XXXXX.exe: 0xC0000005: Access violation reading location 0x01fffff7.
First-chance exception at 0x7c970441 in XXXXX.exe: 0xC0000005: Access violation reading location 0x00001f1d.
I assume the problem is somewhere in allocation of memory for the pointer emiIntPtr. Althought, when I run the code, and there is a problem with connecting to the server (e.g. server not found), the followed marshalling to the Struct emiStruct is done correctly (no exception). The problem only occurs when the connection is succesfully established and server send a respond.
Also, I wrote a C++ sample app using the same DLL library I'm trying to use in .NET, and this application (when I compile it) runs just fine - it means, the C++ DLL should be ok and not causing crashing.
Furthermore, I've found some hints to check/uncheck several properties for the project compilator (using JIT, compile it for x86 cpu, etc.), unfortunately, none of this helped.
Do you have any suggestion where the problem might be or how to do a correct IntPtr inicialization in .NET and mapping between IntPtr and Struct?
Thanks all for your replies:
Here I'm adding the C++ header of the emi_init func:
FUNC( init)( EMI* emi, /* out */
const char* hostname, /* in */
unsigned short port, /* in */
const char* password, /* in */
const char* origin_addr, /* in */
int window_sz, /* in */
int throughput); /* in */
And here is the C# emi_init declaration (I've removed the "ref" attribute for emiPtr as was suggested):
[System.Runtime.InteropServices.DllImport("emi.dll", EntryPoint = "_emi_init")]
public static extern EMIStruct.Error emi_init(
System.IntPtr emiPtr,
[System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string hostname,
ushort port,
[System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string password,
[System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string origin_addr,
int window_sz, int throughput);
However, still getting the same exception.
You are using Marshal.PtrToStructure incorrectly.
The second argument requires a type, iow typeof(EMI).
The return value contains the resulting struct.
So the solution should be:
var s = (EMI) Marshal.PtrToStructure(emiIntPtr, typeof(EMI));
My guess is that you have declared the first parameter incorrectly in the C#. You have declared it as ref IntPtr which is equivalent to EMI** in C++. But I bet the C++ declaration, which you unfortunately did not include, reads EMI*. So simply remove the ref and all should be well.
I expect that emi_init does not read from the EMI parameter, i.e. it has out semantics. In which case you don't need the StructureToPtr call before you call emi_init.

Creating C# to C++ bridge: Why do I get a AccessViolationException when calling DLL?

I'm exploring the idea of building a bridge between a DLL plugin for a 3rd party app and a C# app. I'm writing both the plugin DLL and the C# application. The plugin will be loaded into the 3rd party app and then I want to use call the plugin from C# to indirectly get data from the 3rd party app.
I am able to successfully call an exported function from the DLL from C#. For example:
C++ DLL:
extern "C" __declspec(dllexport) char * HelloFromDll()
{
char *result;
result = "Hello from my DLL";
return result;
}
C#:
using System.Runtime.InteropServices;
[DllImport(#"MyDll.dll")]
private static extern string HelloFromDll();
I can then call this DLL function from C# and display the string in the UI. However, as soon as I create an export function that calls a function from my 3rd party app, I get an AccessViolationException. For example,
extern "C" __declspec(dllexport) char * GetData()
{
char *result;
result = 3rdPartyLibrary::SomeFunction();
return result;
}
Through some testing, the error seems to occur as soon as I make a call to a 3rd party function. How can I fix this?
This function is very difficult to use in a C program as well. Returning strings from functions is a poorly supported scenario. There's a memory management problem, it isn't clear who owns the string. In most cases the caller is expected to take ownership of the string and free it after using it. That's not going to work out well for your function, the program will crash since you returned a string literal.
The .NET pinvoke marshaller needs to solve this problem as well. With the extra problem that it cannot use the allocator that's used by the C code. It is going to call CoTaskMemFree (the COM allocator). That causes an undiagnosable memory leak on XP, a crash on Vista and Win7.
Just don't write C code like this. Always let the caller pass the buffer for the string. Now there's no guessing who owns the memory. Like this:
extern "C" __declspec(dllexport) void HelloFromDll(char* buffer, int bufferSize)
{
strcpy_s(result, bufferSize, "Hello from my DLL");
}
With your C# code like this:
[DllImport("foo.dll", CharSet = CharSet.Ansi)]
private static extern void HelloFromDll(StringBuilder buffer, int bufferSize);
...
var sb = new StringBuilder(666);
HelloFromDll(sb, sb.Capacity);
string result = sb.ToString();
From your question it seems that this is the scenario:
ProcessA (3rd party App) --> loads X.DLL --> initializes the plugin --> does other stuff.
ProcessB (Your C# App) --> loads X.DLL --> calls GetData();
Does X.DLL loaded in ProcessA have any mechanism to talk to X.DLL loaded in ProcessB?
if not then this approach is flawed. Your code probbably crashes because "3rdPartyLibrary" class hasn't been initialised in your C# app as it is completely different copy of the DLL.
For you to extract this data you need a query interface defined by X.DLL which can talk across processes, maybe sockets?
Then ProcessB talks to this interface and extracts the data. if using sockets, then your X.DLL would implement both server and client code, where your GetData() would use this mechanism (maybe sockets) and query the data and return it.
So : X.DLL in ProcessA should act like a server.
And: X.DLL (or write a Y.DLL) in ProcessB should act like a client and get this information from ProcessA.
btw, if the query is only needed to be done once, just hard code this is in X.DLL and dump to disk, and then explore at your convinience :-)
Generally, a returned char* needs to be returned as an IntPtr:
[DllImport(#"MyDll.dll")]
private static IntPtr HelloFromDll();
Then, you'll need to convert that IntPtr into a string:
string retVal=Marshal.PtrToStringAnsi(HelloFromDll());
Strings are a bit difficult in P/Invoke. My general rule of thumb is:
Input char* parameter = c# string
Return char * = IntPtr (use PtrToStringAnsi)
Output char* parameter = c# StringBuilder - and be sure to pre-allocate it large enough
before (ie = new StringBuilder(size)) calling the function.

Memory AccessViolationException Error Calling DLL From C#

I'm encountering a strange memory read/write error while calling a compiled DLL from C#. I use DllImport to get a handle to the function we need, which writes a return value to a parametric pointer to an int (i.e., int* out). This function is called multiple times within a thread, and runs successfully over the execution life of the first thread. However, if one launches another thread after the first has completed, the call to the external dll throws an AccessViolationException. Since the multiple calls from the first thread execute successfully, I'm thinking this is somehow related to the first thread not releasing the pointers to the relevant integer parameters(?). If so, how can I explicitly release that memory? Or, perhaps someone has a different insight into what might be going on here? Thank you very much.
EDIT: Danny has requested more specifics, and I'm happy to oblige. Here is where the external routine is invoked:
int success = -1;
unsafe
{
int res = 0;
int* res_ptr = &res;
int len = cmd.ToCharArray().Length;
int* len_ptr = &len;
CmdInterpreter(ref cmd, len_ptr, res_ptr);
success = *res_ptr;
}
Where CmdInterpreter is defined as:
[DllImport("brugs.dll", EntryPoint="CmdInterpreter",
ExactSpelling=false, CallingConvention = CallingConvention.StdCall)]
public static unsafe extern void CmdInterpreter(ref string cmd, int *len, int *res);
Please let me know if there is any additional info that would be helpful. Thank you!
Given that the problem only occurs when multiple threads are involved, it may be that the command interpreter DLL is using some sort of thread-local storage and doing it incorrectly. It could also have to do with the COM initialization state of the second thread (the one that generates the error).
It would be interesting to know what happens if you launch your new thread and have it call into the DLL before making any calls into the DLL on your first/main thread. If it works, that might support the thread-local storage theory. If it fails, that would support the COM state theory.
It may be the [DllImport]. If you post the [DllImport] signature, and the DLL's ptototype, maybe we can spot a problem.
I read that the Managed, Native, and COM Interop Team released the PInvoke Interop Assistant on CodePlex. http://www.codeplex.com/clrinterop/Release/ProjectReleases.aspx?ReleaseId=14120

Categories

Resources