I am developing a C# application that uses a dll written in C++. I am able to call most of the functions in .dll from C# application, but only one function cause problem. I think it is about marshalling types.
This is the C++ prototype of the function:
int GetProgressInfo(
int *state,
double *progress,
char* stateInfo,
int infoStringMaxLen
);
This is my C# delegate:
delegate int GET_PROGRESS_INFO(
ref int state,
ref double progress,
[MarshalAs(UnmanagedType.LPStr)]
string stateInfo,
int infoStringMaxLen
);
Can you please help me? I can not find what is wrong
One very important lesson that you must learn is that the signature of a function is not enough information to fully specify the semantics for that function. In this case, a C++ int* could be the address of a single int, or an array. Likewise for double*. And char* could be a string being passed to the function, or a pointer to a buffer in which the function returns a string to the caller.
I'm going to take my best guess, but you do need to check the actual semantics.
My best guess is that the function returns three things to the caller: an int with the state int, a double with the progress value, and a string giving info on the state. The latter information is encoded in the final two parameters. The caller allocates a buffer and passes the pointer to the beginning, together with the length. So the C# should look like this:
delegate int GET_PROGRESS_INFO(
out int state,
out double progress,
StringBuilder stateInfo,
int infoStringMaxLen
);
I'm assuming that you are using the default charset of CharSet.Ansi, and so the third parameter does not need a MarshalAs attribute.
Call the function like this:
int state;
double progress;
StringBuilder stateInfoSb = new StringBuilder(256);
int retval = GetProgressInfo(
out state,
out progress,
stateInfoSb,
stateInfoSb.Capacity
);
if (retval == 0) // guessing that 0 means OK
{
string stateInfo = stateInfoSb.ToString();
....
}
Another possible cause for mismatch is the calling convention. As written, there are no calling conventions specified. The default for C++ code is cdecl, the default for C# is stdcall. You need to check what calling convention is used by the C++ and make sure the C# code matches.
And once again, you do need to check with the documentation that my guesswork above is accurate.
Related
I am trying to call a function that converts sql queries between dialects. I am using an open-source project's DLL to use the functions for conversion in a C# university project. The problem i am facing is that i am getting a access violation reading location.
I've read some posts here on stack overflow which suggest that there might be a bad pointer somewhere, but i cannot find where. My pointers are not corrupt
The function for the conversion is this :
int ConvertSql(void *parser, const char *input, int64_t size, const char
**output, int64_t *out_size, int64_t *lines)
{
if(parser == NULL)
return -1;
SqlParser *sql_parser = (SqlParser*)parser;
// Run conversion
sql_parser->Convert(input, size, output, out_size, lines);
return 0;
}
I am calling the function in C#
char *parentInput;
fixed(char *input = &inputStr.ToCharArray()[0])
{
parentInput = input;
}
char** output = null;
Int64 out_size = 0;
Int64 lines = 0;
Int64 size = inputStr.Length;
Console.WriteLine(new IntPtr(&out_size)+" "+ new IntPtr(&lines)+" "+new IntPtr(&parserObj)+" "+new IntPtr(output));
int result = ConvertSql(&parserObj, intputStr, size, output, &out_size, &lines);
i get my parser object from this code which works without errors:
IntPtr parserObj = CreateParserObject();
the dllimport for the funtions are using this code:
[DllImport(dllName: "PATHTODLLFOLDER\\sqlparser.dll", EntryPoint = "CreateParserObject", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr CreateParserObject();
[DllImport(dllName: "PATHTODLLFOLDER\\sqlparser.dll", EntryPoint = "ConvertSql", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern int ConvertSql(void *parser, String input, Int64 size, char **output, Int64 *out_size, Int64 *lines);
In .NET, calling an unmanaged method through P/invoke (which is what happens when you call an extern method) involves various type conversions on the parameters, which is known as "marshalling" and is done automatically by a part of the runtime known as the "marshaller".
In general, it's a terrible idea to marshal pointers. Instead, use the CLR marshaller's ability to convert certain types to pointers for you by changing the signature of your P/invoked method:
// split on multiple lines for readability
[DllImport("PATHTODLLFOLDER\\sqlparser.dll", EntryPoint = "ConvertSql", CallingConvention = CallingConvention.Cdecl)]
public static extern int ConvertSql(
IntPtr parser,
[MarshalAs(UnmanagedType.LPStr)] string input,
long size,
out IntPtr output, // see explanation below
out long out_size,
out long lines);
A few things about the above. First, I took the liberty of using the C# type aliases (string, long), because that's more idiomatic C#, but it doesn't change the behavior. Also, since there are no pointers anymore, no need for unsafe.
First, I declared parser as an IntPtr because those get converted to void* automatically if needed and that's what's already returned by CreateParserObject() anyway.
Second, out parameters can be converted to pointers to uninitialized objects, so by marking both out_size and lines as out, you fix that other problem.
The input is a string, which has a specific format in .NET. Since your function takes a const char*, you need to tell the marshaller how to convert the characters. That's where the MarshalAs attribute comes in: it's when the default conversion doesn't work for you. UnmanagedType.LPStr means char* in this case, so the string gets converted. The runtime will manage the memory for you.
But here we hit a big snag in the road: the output. When marshalling things, there's always questions about lifetime, or, specifically: who releases the memory? The fact that output is a char** implies that the parser allocates a block of memory and then returns it through that, which means the caller has to release it. From the get go, that reeks of bad C++ design because the caller doesn't know how the memory was allocated. Was it with malloc? new[]? Platform specific APIs like LocalAlloc? Is it a pointer to a block of static memory? Usually these APIs come with documentation telling precisely what to do with the pointer once you're done with it. Good C++ APIs return smart pointers, or ask that the caller pass a block of previously allocated memory that they can then play with.
But, this is the thing you're playing with, so here's how to make it work. First, you'd think that you could declare output as [MarshalAs(UnmanagedType.LPStr)] out string: the marshaller will copy the characters into a managed string and return it... but then the native string's (on the C++ side) memory will leak because the runtime doesn't know how the string was allocated, so it prefers to not do anything about it. Also, this assumes the string is null-terminated, which might not always be the case.
So, another option would be to instead declare output as out IntPtr. Then you can use Marshal.PtrToStringAnsi to convert your pointer into a string, and then release it... but you'll need to know how it was allocated first.
Putting it all together:
var parserObj = CreateParserObject();
var output = IntPtr.Zero;
try
{
long lines;
long out_size;
int result = ConvertSql(parserObj, inputStr, inputStr.Length, out output, out out_size, out lines);
var outputStr = Marshal.PtrToStringAnsi(output, (int)out_size);
// do what you want with outputStr here
}
finally
{
if (output != IntPtr.Zero)
{
// release output here
}
}
Oh, also, one final thought: whatever is returned by CreateParserObject() will probably have to be freed at one point. You're probably going to need another function for it, probably like:
[DllImport(/* ... */)]
public static extern void DestroyParserObject(IntPtr parserObject);
It might even already exist in your DLL.
Good luck!
I have a dll that I cannot import in my vs2012 c# project. I have used dllImport before but I have never had to use Marshal or pointers before. Lucky me I guess.
This is the code that I currently have.
The function being called is fnLDA_GetDevInfo(DEVID *ActiveDevices)
DEVID is a normal unsigned integer (#define DEVID unsigned integer)
//Allocate an array big enough to hold the device ids for the number of devices present.
//Call fnLDA_GetDevInfo(DEVID *ActiveDevices), which will fill in the array with the device ids for each connected attenuator
//The function returns an integer, which is the number of devices present on the machine.
[DllImport(DLLLOCATION,CallingConvention = CallingConvention.Cdecl)]
private static extern int fnLDA_GetDevInfo([MarshalAs(UnmanagedType.LPArray)] ref uint[] ActiveDevices);
I call the function in my code this way
uint[] MyDevices;
fnLDA_GetDevInfo(ref MyDevices);
At this point I get an error:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Now I'm pretty sure the error occurs because I don't call the pointer right or something.
Any help would be appreciated.
You have an extra level of indirection. An array is marshalled as a pointer to the array. When you declare the parameter as ref, again a pointer is passed. Thus your C# code matches uint**. Even so, you cannot use ref with an array type because you cannot expect the unmanaged code to produce a managed array.
Your p/invoke should be:
[DllImport(DLLLOCATION,CallingConvention = CallingConvention.Cdecl)]
private static extern int fnLDA_GetDevInfo([Out] uint[] ActiveDevices);
Note that this function is pretty hard to call. Since the function is not passed the length of the array, it is impossible for the function to avoid running off the end of the array if the array is not long enough. I really hope that you have some way to work out how large the array needs to be ahead of calling this function.
So perhaps you are expected to call it like this:
uint[] MyDevices = new uint[SomeLargeNumberThatYouPresumablyCanProvide];
int len = fnLDA_GetDevInfo(MyDevices);
Or perhaps like this:
int len = fnLDA_GetDevInfo(null);
uint[] MyDevices = new uint[len];
fnLDA_GetDevInfo(MyDevices);
I trust that you'll be able to work the rest out from the documentation for the DLL and/or the example C++ programs that call the DLL.
I have written a C# application that injects a DLL into a third-party executable (which happens to have been built using the Qt framework). This DLL uses EasyHook to intercept calls to a number of specific functions. When my injected code is called, I then try to inspect some of the objects that are parameters to these functions.
For example, I have intercepted a call made to parse some XML:
virtual bool __thiscall QXmlSimpleReader::parse(class QXmlInputSource const &)
In my C# code, I have a PInvoke signature to match this:
static extern bool XmlParse(IntPtr Reader, IntPtr Source)
I would like to call the "data()" function which is a member of the "Source" class. That is, the QXmlSimpleReader parses the raw XML from the QXmlInputSource, but before it does so, I am trying to inspect the raw XML via this "data()" function.
On the advice of one of the experts here, I have tried to use C++/CLI to access the object natively (see Calling methods in third-party DLLs ). I have constructed a wrapper object in C++ that accepts the IntPtr from the C# code:
Header:
public ref class QXmlInputSource
{
public:
// Constructor must be called with C# IntPtr
QXmlInputSource(IntPtr Ptr);
bool LoadQt(void);
bool UnloadQt(void);
String^ Data();
private:
// Pointer to the native Qt object
void * Native;
HINSTANCE DllHandle;
// SIGNATURE: virtual QString QXmlInputSource::data() const
typedef void * (__thiscall *QXmlInputSource_Data)(void *);
QXmlInputSource_Data fpData;
};
CPP file:
QXmlInputSource::QXmlInputSource(IntPtr Ptr)
{
LoadQt();
Native = Ptr.ToPointer();
}
bool QXmlInputSource::LoadQt(void)
{
FARPROC Addr;
/* get handle to dll */
std::wstring LibName = QtPath + QtXml;
DllHandle = LoadLibrary(LibName.c_str());
/* get pointer to the function in the dll*/
Addr = GetProcAddress(HMODULE (DllHandle), "?data#QXmlInputSource##UBE?AVQString##XZ");
fpData = QXmlInputSource_Data(Addr);
return true;
}
bool QXmlInputSource::UnloadQt()
{
/* Release the Dll */
FreeLibrary(DllHandle);
return true;
}
String^ QXmlInputSource::Data()
{
void* Ptr = fpData(Native);
return "EPIC FAIL";
}
The Qt-based application crashes when I try to call the fpData() function pointer. Help :P
Some additional information which may or may not help:
I have successfully called functions on "simpler" objects, such as QString.count() and QString.data() using the same methodology. (QString seems to be just a lightweight wrapper for a standard unicode string).
In the QtXml4.dll file that contains the XML functions I am interested in, there are actually TWO parse() methods; one where the Source is a const &, and in the other, Source is a const *. I have no idea if I should be using one or the other. I don't think my signatures will change in any event.
While I was trying to play around, I tried dereferencing the IntPtr in the C# code and passing it to C++:
IntPtr DerefSrc = (IntPtr)Marshal.PtrToStructure(Source, typeof(IntPtr));
If I print out the values of these two IntPtrs, Source has a value of around 3.5Mb, while the DerefSrc has a value of 1.6Gb - which roughly matches the address of the QtXml4.dll in memory. My guess is that the 3.5Mb is a relative offset, while the DerefSrc is clearly an absolute reference. Is it worth a shot converting the DerefSrc to a relative address and passing that to C++ instead ... ?
I see several problems:
1.: You don't check the return value of LoadLibrary and GetProcAddress. Is QXmlInputSource::data even a DLL-exported method? If not then GetProcAddress will obviously fail.
2.: How do you instantiate the QXmlInputSource class? I ask because it seems you are trying to do it in the C# code, which is hard to do right (you need to know the size required for the class, allocate a properly aligned chunk of memory of that size, and call the constructor on it).
3.: The way you're invoking the function pointer is wrong. You need to declare a method pointer of the appropriate type:
FARPROC fp = ::GetProcAddress(...);
typedef QString (QXmlInputSource::*DataMethod)();
DataMethod mpData = reinterpret_cast<DataMethod>(fp);
QXmlInputSource source;
QString data = (source.*mpData)();
4.: Looking at the documentation for QXmlInputSource::data, I see that it returns a QString, which is woefully different from a pointer (as you're currently treating it). To convert it to a System.String, you need code like this:
QString s1 = (QChar*)L"example string";
String^ s2 = gcnew String((wchar_t*)s1.data()); // calls the String::String(wchar_t*) constructor overload
I am working on a C# application which uses an unmanaged dll which as a method that takes in a function pointer as one of it's parameters. As I do not have access to the actual C++ source code for the unmanaged dll I can only rely on a snippet I have from a C++ sample which uses that the same dll. The problem I am having is that the delegate I have made to pass into the unmanaged method is not working.
Below is the DllImport of the method I am trying to use which takes the delegate I've created:
[DllImport("pos_tk.dll")]
static internal extern ErrorCode AddSIGHandle([MarshalAs(UnmanagedType.FunctionPtr)] SIG_Callback func);
Here is the delegate signature I currently have:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void SIG_Callback ([MarshalAs(UnmanagedType.LPArray)] ref int[] buf, int rev, object sender);
And this is the function the C++ sample snippet uses in which I based my delegate off of:
void __stdcall point_handle (int *buf, int rev, LPVOID pParam)
Now, before you tell me to change the ref int[] buf parameter of my delegate to ref int buf the program will throw an InvalidVariant error once the callback is attempted. The code snippet uses int *buf as an array of size rev.
Using the code I have above, the application throws an AccessViolationException when the callback is attempted. My hunch is that I should be using a different type for the ref int[] buf parameter, but I've tried everything I can think of. I've also been searching Google, SO, and MSDN all day but with no luck.
Anyone have any ideas?
As a final note, is this any way similar to how you can use a StringBuilder as a parameter for a unmanaged method that takes a char* parameter to retrieve a variable length string?
Passing arrays back and forth is always a PITA...try this signature, it seems applicable:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void point_handle(
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]
int[] buf,
int rev,
IntPtr pParam);
EDIT: just to elaborate, when you've got a "C style" array (that is a T*) as well as the length both specified in the arguments, SizeParamIndex is your salvation; basically it says "Hey, there's an unknown-length array coming in, but the parameter at this index will tell you how many of them there are. Use both that and the type to help marshal"
I want to call the following function from my managed code:
short LS_LoadConfig(LS_ID SensorID,LPVARIANT varConfigPathFile,BOOL bInit)
Here is how I declare the extern function in my C# class:
[DllImport("LineSensor.dll", EntryPoint = "#16")]
private static extern Int16 LS_LoadConfig(
Int16 deviceId,
IntPtr variantFilePath,
int init);
And here is how I create the VARIANT instance and I obtain a pointer to it. Then I call the C# function:
string filepath = #"C:\Windows\ ...";
IntPtr variantFilePath = Marshal.AllocCoTaskMem(200);
Marshal.GetNativeVariantForObject(filepath, variantFilePath);
LS_LoadConfig(device.Id, variantFilePath, initLineSensor);
The problem is that I keep receiving error messages such as "calling the LS_LoadConfig function has unbalanced the stack, check that parameters match the unmanaged signature".
It seems that the problem is caused by the second argument "variantFilePath", like it is not properly marshaled and its size on the unmanaged heap doesn't correspond to the one of an address (32-bit in my case). I tried to change the type in the C# function signature from IntPtr to int as follows:
[DllImport("LineSensor.dll", EntryPoint = "#16")]
private static extern Int16 LS_LoadConfig(
Int16 deviceId,
int variantFilePath,
int init);
I tried to call the function passing a random number and it got slightly better, I have just received an error "memory access violation". Obviously because the random number wasn't a valid address.
Does anybody knows the solution to this problem?
Thank you for any helpful information.
The access violation you created is not better. It also prevents the MDA warning from being generated. Short from the argument types being wrong, the int16 looks pretty weird, the most likely trouble is caused by the CallingConvention. Try StdCall.
And declare the 2nd argument as "object", its default marshaling is to a VARIANT. Declare it with the "ref" keyword to get an LPVARIANT.
"calling the LS_LoadConfig function has unbalanced the stack, check that parameters match the unmanaged signature".
This usually means that you're using conflicting calling conventions between your native and managed code. C# by default uses stdcall, and c/c++ uses cdecl. Try specifying CallingConvention = CallingConvention.Cdecl.