I have a Windows Forms application running under .NET 4.0. This application imports a DLL which is available for:
32 bit
64 bit
Here is my code snippet:
[DllImport("my64Bit.dll"), EntryPoint="GetLastErrorText"]
private static extern string GetLastErrorText();
// Do some stuff...
string message = GetLastErrorText();
When calling this function (compiled for x64) the application just crashes. I can't even see any debug message in Visual Studio 2012. The identical code with the 32-bit-DLL (compiled for x86) works fine. The prototype is:
LPCSTR APIENTRY GetLastErrorText()
Unfortunately I don't have any further information about the DLL as it is a third-party product.
The function signature is quite troublesome. Whether your code will crash depends on what operating system you run. Nothing happens on XP, an AccessViolation exception is thrown on Vista and later.
At issue is that C functions returning strings need to typically do so by returning a pointer to a buffer that stores a string. That buffer needs to be allocated from the heap and the caller needs to release that buffer after using the string. The pinvoke marshaller implements that contract, it calls CoTaskMemFree() on the returned string pointer after converting it to a System.String.
That invariably turns out poorly, a C function almost never uses CoTaskMemAlloc() to allocate the buffer. The XP heap manager is very forgiving, it simply ignores bad pointers. Not the later Windows versions, they intentionally generate an exception. A strong enabler for the "Vista sucks" label btw, it took a while for programmers to get their pointer bugs fixed. If you have unmanaged debugging enabled then you'll get a diagnostic from the heap manager which warns that the pointer is invalid. Very nice feature but unmanaged debugging is invariably disabled when you debug managed code.
You can stop the pinvoke marshaller from trying to release the string by declaring the return value as IntPtr. You then have to marshal the string yourself with Marshal.PtrToStringAnsi() or one of its friends.
You still have the problem of having to release the string buffer. There is no way to do this reliably, you cannot call the proper deallocator. The only hope you have is that the C function actually returns a pointer to a string literal, one that's stored in the data segment and should not be released. That might work for a function that returns an error string, provided it doesn't implement anything fancy like localization. The const char* return type is encouraging.
You will need to test this to make sure there is no memory leak from not releasing the string buffer. Easy to do, call this function a billion times in a loop. If you don't get IntPtr.Zero as a return value and the program doesn't otherwise fall over with a out-of-memory exception then you're good. For 64-bit pinvoke you'll need to keep an eye on the test program's memory consumption.
Found it. The native function returns LPCSTR, i.e. the C# function cannot return a string. Instead an IntPtr must be returned like this:
[DllImport("my64Bit.dll"), EntryPoint="GetLastErrorText"]
private static extern IntPtr GetLastErrorText();
// Do some stuff...
IntPtr ptr = GetLastErrorText();
string s = Marshal.PtrToStringAnsi(ptr);
Related
I have a native (unmanaged) .dll written in C++ that is to be called from a managed process (a C# program). When debugging the dll the problem I have has shown to be that when I create an object in the dll with the new keyword I get a System Access Violation Exception. This only shows up when calling the dll from a managed process, not when I am calling it from another native program.
The code is something similar to this:
// Native.dll file
MyClass myInstance; // global variable (and does need to be so)
__declspec(dllexport) uint8_t _stdcall NativeFunction(){
myInstance = new MyClass(); // <-- this causes Access Violation Exception
}
and the C# code:
using System.Runtime.Interopservices;
// Loading the dll
[DllImport("Native.dll",CallingConvention = CallingConvention.StdCall)]
private extern static byte NativeFunction();
class TestClass{
byte returnVal = NativeFunction(); //<-- exception in managed context
}
I know this has something to do with the native process trying to allocate memory outside allowed memory-space. It only happens when memory is allocated with new (at least in this project) which I unfortunately do need to use. My question is: Does anyone know why this is causing the exception and how to avoid it?
new MyClass is most likely going to call ::operator new, the global operator, unless you have provided MyClass::operator new. And if you haven't provided ::operator new yourself, you should be getting the ::operator new from your compiler (likely Visual Studio).
This ::operator new implementation will probably forward to HeapAlloc. And guess what? That's the same Win32 function which .Net will call as well. There's not much magic involved here; that's how Windows assigns pages of memory to your virtual address space. And when you use those pages, Windows will assign RAM.
Now the thing here is that you don't need to do anything special for this. In fact, doing anything special is how you would break operator new. And since you broke it, you''re going to have to figure that out. There is not much magic code going on here. Use a debug build, so you will have a clear stack dump (no inlining). Can you backtrack to HeapAlloc?
Check also the content of the Access Violation Exception. The error code will be C0000005. But what type of exception is it? Read or write? On what type of address? Code or data?
If i have a c++ function which returns a char* like this:
char *cctalk_app_event_name(DG_CCTALK_APP_EVT_CODE);
And the corresponding C# signature:
[System.Runtime.InteropServices.DllImportAttribute("cctalk.dll", EntryPoint = "cctalk_app_event_name", CallingConvention = CallingConvention.Cdecl)]
public static extern System.IntPtr cctalk_app_event_name(DG_CCTALK_APP_EVT_CODE param0);
If the native code returns a char* allocated with the new keyword, i am for sure gonna have a memleak each time i call this function C#? Is there a way i can free that memory?
My hunch from looking at the function name is that this may well be returning a pointer to a string constant within the DLL, in which case you don't need to worry about freeing the pointer anyway.
If the manual for the SDK (link here) wasn't any use, then I'd disassemble the DLL and look at what that function did, but don't worry about how to delete it before you've established that you need to.
If you don't have any documentation or source then you cannot know whether or not the memory was allocated with new. If it was allocated with new then it needs to be deallocated with delete. That can only be done from the native code. If that is needed then the DLL will need to export a deallocator for you.
Other possibilities include allocation from a shared heap, e.g. the COM heap. Not very likely. Or perhaps the string is statically allocated and does not need deallocation. This final option is usually the case when a functions returns a C string as a return value. If you had to guess, that's the percentage option. In any case, if there's no way for you to deallocate the string, what else can you do?
The only way you can be sure is to have documentation, or source code, or support from the author. I appreciate that you want to know the solution, but your only hope are the options listed in the first sentence of this paragraph.
I find it hard to believe that a library this complex has no documentation. How did you come by this library? Are you really sure there are no docs?
there's a WinForms-application written in C# using .NET Framework 3.5. This application uses a C++ Dll which is imported using the following declaration:
[DllImport(DllName)]
public static unsafe extern int LoadDBData(String dsn, String userid, String password);
This method imports data from a given ODBC-DSN using a SQL Server database. The call crashes when there is too much data in database. The provider of this extern dll said this happens because the dll is unable to grab more heap size and my application should provide more heap memory.
How could I solve this problem? As far as I know the only possibility to exclude a component from automatic garbage collection is the unsafe keyword which I already used.
Any idea would be appreciated.
Thanks in advance
Martin
This seems like a problem with the vendor's library, rather than your code.
Managed and unmanaged memory should be considered to be completely separate. Managed memory is typically memory allocated on a garbage-collected
heap, while unmanaged memory is anything else: the ANSI C memory pool
allocated through malloc(3), custom memory pools, and
garbage-allocated heaps outside the control of the CLI implementation...
Note that the above quote is from the Mono documentation, but I believe (if I'm not mistaken) the same is true for .NET in general. If the data is being loaded in the DLL's internal data structures, then it should allocate its own memory. If you're providing a buffer which will get filled up with the data, then it will only get filled up with as much data as you've allocated for the buffer (and pinned before marshalling). So where is the data being loaded?
You can't increase the heap size in .NET.
You could create an EXE in c/c++ that your .NET app calls using Process.Start.
Your c/c++ EXE would just call the DLL function and return the result (or if you have more than one function it could take a command line parameter). If you don't want a separate EXE you could try using RunDll32 instead.
I doubt this is specific to .NET, managed memory, garbage collection etc. It's a native DLL so it uses regular, unmanaged memory. Of course, the .NET runtime will also use it's share of memory but a native application using the DLL would do the same.
If you're running in a 32 bit process, the total heap size for .NET and unmanaged code can be limited to 1.5 GB. It's difficult to tell without additional information, but you might have hit that limit.
So one option would be to ask your vendor, whether they have a 64 bit version of the library and switch to a 64 process. In a 64 bit process, memory is almost unlimited (according to today's standard).
Short version:
Could passing the handle from GlobalAlloc(GMEM_MOVEABLE, Size) to Marshal.PtrToStructure() and Marshal.FreeHGlobal() cause memory corruption?
Long version:
I'm using Windows Global memory allocation to pass a data structure back and forth between a Delphi and C# application (the fact that it's Delphi isn't really significant to this question, because it's just Win32 API calls).
On the Delphi side, I pass in a record, it allocates the space, locks the memory, copies the structure into memory, and the unlocks the memory:
function MarshalRec(SourceRec: TInteropItemRec): THandle;
var
Size: integer;
Buffer: Pointer;
begin
Size := sizeof(SourceRec);
result := GlobalAlloc(GMEM_MOVEABLE and GMEM_ZEROINIT, Size);
Buffer := GlobalLock(result);
try
CopyMemory(Buffer, #SourceRec, Size);
finally
GlobalUnlock(result);
end;
end;
On the C# side, it gets that THandle (which is basically an unsigned int) into an IntPtr and uses Marshal.PtrToStructure to copy the data into the C# structure:
public void FromMemory(IntPtr Source)
{
Marshal.PtrToStructure(Source, this);
Marshal.FreeHGlobal(Source);
}
The problem I'm running into, is very rarely (as in 4 times over 6 months for me), the whole application goes down (This application has encountered an error and must close). If I try to pause execution in Visual Studio, I get "A fatal error has occurred and debugging needs to be terminated. For more details, please see the Microsoft Help and Support web site. HRESULT=0x80131c08."
Anyway, we managed to get a couple logs of it happening, and in both cases, it showed a recent call to that "MarshalRec" function above, a few other function calls, and then some processing of Windows messages on the event loop on the Delphi thread (yeah, it has its own thread and event loop to deal with a time sensitive device driver).
So my suspicion is falling on the GMEM_MOVEABLE flag to GlobalAlloc. I couldn't find anything in the Marshal class that does the GlobalLock and GlobalUnlock stuff, so I had assumed it was handled internally by PtrToStructure().
Does PtrToStructure deal properly with a handle, or does it need an actual pointer obtained from GlobalLock()? Is it possible that in very rare cases, Windows happens to move the memory I allocated, which means I need to call GlobalLock() to get an actual pointer to pass in? That FreeHGlobal is actually freeing something it shouldn't have, which brings down the whole app the next time that resource is accessed?
And if so, should changing the GMEM_MOVABLE to GMEM_FIXED prevent this from happening again? Or do I need to DllImport GlobalLock() and GlobalUnlock()?
I'm tempted to just blindly make those changes, but given the non-reproducibility of this issue, there's no way to tell if it's fixed until it happens again. So I'm looking for feedback on whether this code could lead to the symptoms I'm seeing, or if I need to start coming up with other theories.
Well, you are explicitly violating the contract for GlobalAlloc(). Your hope that Marshal.PtrToStructure() will call GlobalLock is unfounded, it has no way to tell whether the passed IntPtr is a handle or a pointer.
GlobalAlloc is a fairly hopelessly outdated legacy function from the Windows 3.x era. Yes, it is quite likely to return the address as the handle value with GlobalLock() being a no-op. But it is certainly not documented to do this. CoTaskMemAlloc() is the better mouse trap.
I have this problem, which I am dealing with for some time already. At start, I have two dlls with unmanaged code (let's say... dll_1, dll_2) and managed aplication in c#. What i'm supposed to do is to get pointers to unmanaged functions in dll_1 in managed code, pack it into structure, and send this structure as an argument to unmanaged function in dll_2. Have anyone dealt with that kind of problem before maybe?
Since you don't do anything in managed code but the DLLs live in the same process, just use an IntPtr (is automatically 32 or 64 bits depending on the platform) to pass the unmanaged pointer around. You can of course also insert the IntPtr into your struct and use it as argument or return value when using an external call (e.g. [DllImport('YourDll')] static extern IntPtr ImportedFunction();).
However, to give you more information, it would be necessary to know more about the DLL calls and their data structures.