I have an unmanaged C++ function residing inside a Dll that I call from my C# application. Here is the signature of the function:
GetCrashMeasurement(LPCTSTR channelName, LPCTSTR properties, LPCTSTR * Values, HANDLE error)
where channelName and properties are input parameter [in]; and Values is an output parameter[out].
I'm also using the Platform invoke from my C# application as follows:
[DllImport("DrvCrashHAL.dll", EntryPoint = "coCRAL_GetCrashMeasurements")]
public static unsafe extern CoStatus GetCrashMeasurements(string sChannel, string sMeasurements, ref string sValues, IntPtr hError);
From my C# application I call the function in the following way:
string Text = "";
intptr herror = intptr.zero;
GetCrashMeasurements("channelname","",ref Text,herror);
But then my program stops exactly at this line without throwing any exception, and all I see in the output window is the following message:
Critical error detected c0000374
Critical error detected c0000374
The program '[4964] ProjectX.exe: Managed' has exited with code 0 (0x0).
The program '[4964] ProjectX.exe: Native' has exited with code 0 (0x0).
My guess is, the problem lies in the marshalling type of LPCTSTR.
Could anyone please tell what I`m doing wrong or point me to the right direction?
Thanks in advance.
As far as I can tell from what you've provided, parameters 1, 2 and 4 should marshall just fine. The problem is likely to be parameter 3.
Error c0000374 is heap corruption. That would fit.
The issue in trying to solve your problem is to know what that 3rd parameter does. It's declared as a kind of char**, and as an [out] parameter I would expect it to be output as a char*. In other words, I would expect the function to actually output a pointer, not a string, with the expectation that the caller will receive that pointer and copy a (null-terminated) string from it.
You can investigate calls like this by redefining the entry point as an int* or int** and passing in an intptr. That way you should (a) fix the crash and (b) get to see the output value (as an int or pointer). You may have to do your own marshalling for this one. Given a pointer to a COM BSTR you can easily retrieve the value you need.
However, according to my reading of the Interop spec, this should work correctly with a declaration of out string sValues and not ref. The heap corruption would be happening when the function is called, not when it returns.
This is helpful, if rather technical: http://msdn.microsoft.com/en-us/magazine/cc164193.aspx.
I suggest you try that first. If not, maybe try the debugging route to see what pointer you are getting.
As #jester noticed LPCTSTR* Values sounds more like an array of strings. Can you try to change your third parameter to be an array of strings?
public static unsafe extern CoStatus GetCrashMeasurements(string sChannel, string sMeasurements, string[] sValues, IntPtr hError);
and call it as follows
GetCrashMeasurements("channelname","",new[] { Text },herror);
Also you can specify MarshalAs on sValues.
public static unsafe extern CoStatus GetCrashMeasurements(string sChannel,
string sMeasurements,
[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPTStr)]
string[] sValues,
IntPtr hError);
Related
I am using DLL runtime which is made with C language into C#.
I came across below statement.
typedef void *JCCP_PROPERTY_HANDLE;
In function it is being used as:
JCCP_RESULT __JCCP_FUNCTION__ jccpGetProperty(
JCCP_HANDLE hjccp,
const char *name,
JCCP_PROPERTY_HANDLE *phproperty);
Now I want to call jccpGetProperty() method in my C# code.
Can anybody tell how can I pass third parameter(JCCP_PROPERTY_HANDLE *phproperty) to function from C#.
I tried with below code but not working.
Extern Method:
[DllImport(DLL_NAME, EntryPoint = "_jccpGetProperty", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr jccpGetProperty(IntPtr hjccp, string name, ref IntPtr JCCP_PROPERTY_HANDLE);
Usage
IntPtr handle = IntPtr.Zero;
string tag = "server.version";
var result = jccpGetProperty(hjccp, tag, ref handle);
Can anybody help me in this?
IntPtr is the correct type mapping for void*. The native type void* is generally used for an opaque pointer, and that is mapped to IntPtr in C#.
Those parts of the p/invoke declaration that we can verify are correct. The unverifible parts are:
The calling convention. You believe that it is cdecl, but we can't check.
The return type. You believe it to be pointer sized. Again we cannot check. My guess is that a 32 bit integer, int or uint is more likely. That would make a difference in a 64 bit process.
The values passed to the function. It's perfectly possible that the function is declared correctly, but you are passing invalid values.
Because you only showed partial code and details, it's hard to say much more. You will have to verify all the parts of the program that we cannot.
I suggest that you start with working C or C++ code and translate that, looking for the first point of deviation in behaviour between that code and your C# translation.
I have dll imported. All the other parts work, but the string return value of imported method gives this:
Unhandled exception at 0x7748EA5F (ntdll.dll) in ***.exe: 0xC0000374: A heap has been corrupted (parameters: 0x774C4270).
It still returns the string, but I'm worried that this causes some other errors later on, that are hard to debug. From what I have tested, it feels like it can be anything, that is causing this.
This is my importing code:
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
private delegate String GetStringDelegate(int handle, int index);
private static GetStringDelegate getString { get; set; }
var addressOfGetString = NativeMethods.GetProcAddress(_handle, "GetString");
getString = (GetStringDelegate)Marshal.GetDelegateForFunctionPointer(addressOfGetString, typeof(GetStringDelegate));
usage
getString(Handle, 1);
This works, but it causes the error. While debugging, just pressing "continue" will allow it to process it and show the results. Result is correct.
This is how it is done in delphi dll
function GetString(Hnd,Index : Integer) : PChar; stdcall;
begin
Result:=TControl(Hnd).Stack.GetString(Index);
end;
I have same kind of code for integers, doubles, bools and everything else in the dll works, without errors. So I think it creates somekind of overflow or wrong size of memory allocation.
Note: If I create console application, it just fails, without breaking on error, if I run console without debugger ( ctrl+f5 ), it works, still without error. Heap error is generated when I call this from forms application.
TL;DR; This code works, but it shows the heap error, while returning ints, bools etc works perfectly.
When you return a string as a function return value of a p/invoke function the marshaller takes responsibility for freeing that memory. It assumes the memory was allocated on the COM heap, e.g. with CoTaskMemAlloc. Your string does not meet that requirement.
You could change the Delphi code to allocate the memory that way.
You could return IntPtr and use Marshal.PtrToStringAnsi to manually marshal. The question then remains as to whether the memory needs to be freed, and if so how.
You could return a COM BSTR, but that only works with Delphi as an out parameter and not a function return value. See Why can a WideString not be used as a function return value for interop?
You could ask the caller to allocate memory and have the callee populate it.
I cannot see all the way into your code to be sure, but I would not be surprised if you were returning PChar(s) where s was a local variable. That would mean that you would be returning the address of freed memory.
The bottom line here is that passing strings (or indeed arrays or other dynamic structures) from callee to caller is much more complex than passing simple value types. You are going to need to re-consider how you do this.
I am using PInvoke to call a C++ function from my C# program. The code looks like this:
IntPtr data = Poll(this.vhtHand);
double[] arr = new double[NR_FINGERS /* = 5 */ * NR_JOINTS /* = 3*/];
Marshal.Copy(data, arr, 0, arr.Length);
With Poll()'s signature looking like this:
[DllImport("VirtualHandBridge.dll")]
static public extern IntPtr Poll(IntPtr hand);
The C-function Poll's signature:
extern "C" __declspec(dllexport) double* Poll(CyberHand::Hand* hand)
Unless I'm having a huge brain failure (admittedly, fairly common for me), this looks to me like it should be working.
However, the double values I am getting are completely incorrect, and I think this is because of incorrect memory usage. I have looked it up, and I think doubles in C# and C++ are identical in size, but maybe there is some other issue playing here. One thing that rubs me the wrong way is that Marshal.Copy is never told what type of data it should expect, but I read that it is supposed to be used this way.
Any clues, anyone? If needed, I can post the correct results and the returned results.
You are missing the CallingConvention property, it is Cdecl.
You really want to favor a better function signature, the one you have is extremely brittle due to the memory management problem, the required manual marshaling, the uncertainty of getting the right size array and the requirement to copy the data. Always favor the caller passing a buffer that your native code fills in:
extern "C" __declspec(dllexport)
int __stdcall Poll(CyberHand::Hand* hand, double* buffer, size_t bufferSize)
[DllImport("foo.dll")]
private static extern int Poll(IntPtr hand, double[] buffer, int bufferSize)
Use the int return value to report a status code. Like a negative value to report an error code, a positive value to return the number of elements actually copied into the buffer.
You shouldn't even need to marshal the data like that, as long as you declare the P/Invoke correctly.
If your CyberHand::Hand* is in reality a pointer to a double, then you should declare your P/Invoke as
[DllImport("VirtualHandBridge.dll")]
static public extern IntPtr Poll(double[] data);
And then just call it with your array of doubles.
If it isn't really an array of doubles, then you certainly can't do what you're doing.
Also, how does your 'C' function know how big the array will be? Is it a fixed size?
The IntPtr return value will be a problem. What is the double* pointing to? An array or a single item?
You could find that it's easier (if you can) to write a simpler more friendly 'C' wrapper for the function you're calling, and call the wrapper function itself. You can of course only do that if you can change the source code of the 'C' DLL. But without knowing exactly what your function does, I can't give you specific advice.
[EDIT]
Ok, your code should theoretically work if the memory being passed back isn't being messed around with (e.g. freed up). If it's not working, then I suspect something like that is happening. You'd definitely be better writing a wrapper 'C' function that fills in an array allocated by the C# and passed to the function, rather than passing back a pointer to some internal memory.
BTW: I don't like code which passes around pointers to blocks of memory without also passing the size of that block. Seems a bit prone to nasty things.
I have a c++ plus method that generates a value. i am calling this method from a c# application.
The C++ method is like this:
extern "C" REGISTRATION_API char * generate(char dIn[],char dOut[])
The generate method returns an array of chars (sOut[]=returnvalue; return sOut;)
Now I'm calling this method from my c# app:
[DllImport("mydll.dll")]
static extern string generate(string sIn, string sOut);
As you can see, the return type in c# is string. What is happening is that the returned value in c# is not correct and it is corrupted. (The value is correct inside the generate method, but whenever i call it from c# to extract it, i get some erroneous value.)
Is it OK that my method in C# has a string return value while in c++ it's a char*?
Please share your comments, it is urgent, thanks.
Unfortunately, char* can be somewhat ambiguous. (Literally, it's a pointer to a single character, where you may or may not be able to get at other characters by dereferencing the pointer like an array.) Usually, though, it's a pointer to a null-terminated string of single bytes in ANSI encoding (what Microsoft calls an LPStr). By default, P/Invoke marshals instances of System.String as the BStr type, which is like a Unicode Pascal string (with a length at the beginning). You'll need to use the MarshalAs attribute to explicitly specify how you want the strings marshaled between managed and unmanaged code.
Most likely, you'll want to declare it like so:
[DllImport("mydll.dll")]
[return: MarshalAs(UnmanagedType.LPStr)]
static extern string generate(
[MarshalAs(UnmanagedType.LPStr)] string sIn,
[MarshalAs(UnmanagedType.LPStr)] out string sOut);
However, your function signature is confusing: is it returning the string, or is it setting an output variable? You may have to adjust the P/Invoke declaration to fit your needs.
Is it OK that my method in C# has a
string return value while in c++ it's
a char*?
It depends really.
If that C++ method is allocating the memory for the string and expecting the caller to deallocate it then you have problems...big problems. You could try calling Marshal.FreeHGlobal on it, but there is no guarentee that the C++ method actually allocated the memory on the global heap. Most APIs have you feed the pointer back into one of their methods to free the memory.
It is also possible that the char * is pointing to memory which does not need to be freed. In that case using a string should be fine.
You could have the C# declaration return an IntPtr and then call one of the Marhsal.PtrToStringXXX methods.
This method cannot be called safely from a native C++ application either. It returns a pointer to an array on the stack frame. Any call to another function will overwrite that stack frame and corrupt the array.
This probably works by accident in a C++ program right now because there is no function call after obtaining the return value of this function. This kind of luck is no longer available when the P/Invoke marshaller sits in between.
You will have to redesign the C++ function. You should give it arguments that allows the caller to pass its own buffer to be filled with the result. For example:
extern "C" void __stdcall generate(const char* input, char* output, int outputSize)
Call this in C# code by passing a StringBuilder for the output argument, properly initialized with a sufficient Capacity to receive the string. The outputSize argument ensures that the C++ function can avoid writing past the end of the buffer and corrupt the garbage collected heap. Don't ignore it.
In a function in my C++ DLL, I'm returning a std::string to my c# application. It pretty much looks like this:
std::string g_DllName = "MyDLL";
extern "C" THUNDER_API const char* __stdcall GetDLLName()
{
return g_DllName.c_str();
}
But when my C# code calls this function, I get this message in my output window:
Invalid Address specified to RtlFreeHeap( 00150000, 0012D8D8 )
The function declaration in c# looks like this:
[DllImport("MyDll", EntryPoint = "GetDLLName")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string GetDLLName();
From what I've been able to find online, sometimes this message appears when there's an inconsistency between which version of new(debug or release, etc) is being used with delete. But I'm not sure if that is what is going on in my case. So I'm not sure exactly what's causing it. Maybe the MashallAs might have something to do with it?
Any ideas?
Thanks!
I managed to find the issue. It was the way the C# definition was done. From what I can understand, using the MarshallAs(UnmanagedType.LPStr) in combination with the string return type makes it so that it'll attempt to free the string when its done. But because the string comes from the C++ DLL, and most likely a totally different memory manager, it fails. And even if it didn't fail, I don't want it to be freed anyway.
The solution I found was to change the C# declaration to this (the C++ code is unchanged):
[DllImport("MyDll", EntryPoint = "GetDLLName")]
public static extern IntPtr GetDLLName();
So this makes it so that it just returns a pointer to the string data. And then to change it to a string, pass it to Marshal.PtrToStringAnsi()
return Marshal.PtrToStringAnsi(GetDLLName());
And that gets wrapped into another function for cleanliness.
I found the solution from this page:
http://discuss.fogcreek.com/dotnetquestions/default.asp?cmd=show&ixPost=1108