P/Invoke throw System.ExecutionEngineException - c#

I have a closed source unmanaged DLL coded in C++ that I wanted to use in a C# solution so I created a wrapper managed DLL that use P/Invoke to call the closed source DLL function. That works pretty well for no param function and int variables. However I get a System.ExecutionEngineException when running a more complex function that take an array of struct as parameter which contains array of char for strings. Here is what I had:
[StructLayout(LayoutKind.Sequential)]
public struct Target
{
public int targetID;
public string Label;
}
[DllImport("tyrfde.dll", EntryPoint = "tyrfdeGetTarget")]
public static extern int GetTarget(ref Target[] targets);
Below is the information I have from the header file of the DLL:
#define TARGET_LBL_SIZE (256l)
typedef struct _tyrfdeTarget
{
TInt32 TargetID; // integer signed 32bits
TCharA Label[TARGET_LBL_SIZE]; // caracter
} tyrfdeTarget;
TInt32 __stdcall tyrfdeGetTargets(tyrfdeTarget* pTargets);
Not quite sure why the array size is specified as long but anyway SizeConst only take int. After some search here is what I tried to fix.
[StructLayout(LayoutKind.Sequential, Size = 260), Serializable]
public struct Target
{
public int targetID;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 256)]
public string Label;
}
[DllImport("tyrfde.dll", EntryPoint = "tyrfdeGetTargets")]
public static extern int GetTarget(ref Target[] targets);
But I still have the problem. I have read that this exception can throw if the functions clear part of the memory used by CLR. Unfortunately I cannot verify that. Is there something in my code that is obviously wrong and could cause the problem?

Hm, I think your problem is with the ref Target[] targets parameter. AFAIR this is a reference to a reference, which is probably not what you actually want.
I'd try this:
[DllImport("tyrfde.dll", EntryPoint = "tyrfdeGetTargets")]
public static extern int GetTarget([Out, MarshalAs(UnmanagedType.LPArray)] Target[] targets);
Maybe this article helps you to find the correct declaration.
Note that the size of the array is not clear here, usually in such cases there is also a ref int length parameter, which can then be referenced in the MarshalAs attribute via the SizeParameterIndex property.

1) Are you sure that TCharA is 16-bit? Otherwise I think you should also specify whar CharSet to use.
2) Writing this kind of wrappers is way way simpler in C++/CLI.

Related

How to fix 'Method's type signature is not PInvoke compatible' error in C#

All I need to know is how to return a struct from a PInvoke in C++ that has the following struct. For the moment I can deal with it being blank and I just want to know how to return the struct under the conditions set in the code.
I've tried with with the entire struct that I need to return and isolated each part of struct to know which part is giving me the issue (which will be made apparent in the code provided).
I've tried the same method by wanting to return a few integers within the struct which works fine. (Tried to make this bold using ***, ___)
//.header file
typedef struct { //Defintion of my struct in C++
TCHAR msg[256];
}testTCHAR;
//.cpp file
extern "C" {
__declspec(dllexport) testTCHAR* (_stdcall TestChar(testTCHAR* AR))
{
AR->msg;
return AR;
}
}
In my C# I Call the .dll as:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void testChar(testTCHAR AR);
[DllImport("C:\\Users\\jch\\source\\repos\\FlatPanelSensor\\x64\\Debug\\VADAV_AcqS.dll", EntryPoint = "TestCallBackChar", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern testTCHAR TestCallBackChar([MarshalAs(UnmanagedType.FunctionPtr)] testChar call);
//Struct
public struct testTCHAR
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string rMsg; //I assume the error should be fixed here
but to what exactly I don't know.
}
//Defining the callback
testChar tChar =
(test) =>
{
//As far as I'm aware this part can be left blank
as I followed a tutorial online
};
testTCHAR returned = TestCallBackChar(tChar); //This is where the error
happens
I just need to return the struct, preferably with a value attached to it.
The error I get is 'Method's type signature is not PInvoke compatible.' Which is in the title, but I'm covering all basis.
If you need anymore information about this please ask away and I should be able to provide.
Since the testTCHAR type contains managed references, you cannot use a pointer as a part of signature (which you didn't), but also it does not make sense to pass it by value (this is why the runtime produces the error you see).
You need to change your signatures so that it's apparent that you want to pass a pointer into the native method:
public delegate void testChar(IntPtr data);
public unsafe static extern IntPtr TestCallBackChar([MarshalAs(UnmanagedType.FunctionPtr)] testChar call);
When calling you need to explicitly marshal structures to the managed equivalent (and vice versa):
testChar tChar =
(inputPtr) =>
{
testTCHAR input = (testTCHAR)Marshal.PtrToStructure(inputPtr, typeof(testTCHAR));
};
IntPtr returnedPtr = TestCallBackChar(tChar);
testTCHAR returned = (testTCHAR)Marshal.PtrToStructure(returnedPtr, typeof(testTCHAR));
Additionally, I guess there is a mismatch between C++ and C# signature and method name.

PInvoking AddSecurityPackageA

I am working on a project where I need to PInvoke the secur32!AddSecurityPackageA function, but I am still learning the ins and outs of how to do this by hand and could use some help.
Here are a the references I am working with:
https://learn.microsoft.com/en-us/windows/desktop/api/sspi/nf-sspi-addsecuritypackagea
https://learn.microsoft.com/en-us/windows/desktop/api/sspi/ns-sspi-_security_package_options
And here's a sample of my code where I am trying to define the struct and call the function:
[DllImport("secur32.dll", EntryPoint = "AddSecurityPackageA")]
public static extern void AddSecurityPackageA(
ref string pszPackageName,
ref SECURITY_PACKAGE_OPTIONS[] Options
);
[StructLayout(LayoutKind.Sequential, CharSet =CharSet.Ansi)]
public class SECURITY_PACKAGE_OPTIONS
{
public ulong Size;
public ulong Type;
public ulong Flags;
public ulong SignatureSize;
public IntPtr Signature;
}
string dll = #"c:\temp\test.dll";
SECURITY_PACKAGE_OPTIONS[] pkgOpts = new SECURITY_PACKAGE_OPTIONS();
AddSecurityPackageA(ref dll, ref pkgOpts);
My questions are:
At lines 3 and 4, is this an appropriate use of ref and is this generally correct according to the MSDN docs?
At line 14, the C++ struct on MSDN has this as a void pointer, but while researching I found that the C# equivalent is an IntPtr. Is that correct or do I need to use unsafe?
In general, has anyone found any really good PInvoke tutorials outside of reading other people's code? I'm moving over from Python so it is quite a bit different and much of what I've found is either "draw a circle, draw the rest of the owl" or insanely lengthy MSDN documentation that makes a lot of assumptions.
Thank you!
Some comments:
Use the W function rather than the A function. You don't want to limit yourself to ANSI. This is a Unicode world.
The function has a return value. You must declare the function with a matching return value type. Presumably it is uint or int but you should check in the C++ header file.
ref string is wrong. It should be string.
ref SECURITY_PACKAGE_OPTIONS[] is wrong. It is not an array. It is a pointer to a struct. Since you declared SECURITY_PACKAGE_OPTIONS as a class, a reference type, you can replace ref SECURITY_PACKAGE_OPTIONS[] with SECURITY_PACKAGE_OPTIONS.
C++ unsigned long is 32 bits, so it should be uint in C#.
IntPtr is correct, but that leaves unresolved the question of how to declare the digital signature and obtain a pointer to it. I think it's outside the remit of this question for us to track down an example of how to do that.
This one works for me:
[DllImport("secur32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern uint AddSecurityPackage(
string pszPackageName,
SECURITY_PACKAGE_OPTIONS Options
);
[DllImport("secur32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern uint DeleteSecurityPackage(
string pszPackageName
);

Issue with native C++ dll in C#

I have native C++ dll with function that finds the number of cameras connected to the computer and returns their serial number. I am trying to use native C++ dll in C# application but I keep getting the Access Violation error(Attempted to read or write protected memory).
The function in question is
uint32_t GetSerialNumList(char** theBufList, int theBufSize, int theListLength);
The way I am using PInvoke is as follows:
[DllImport(CameraDll, EntryPoint = "GetSerialNumList", CallingConvention = CallingConvention.Cdecl)]
private static extern uint GetSerialNumList(out byte[] pBuf, int BufSize, int ListLength);
If I create native C++ application to use the dll and use the function as follows:
char* theSerialNumb;
theSerialNumb = (char *) malloc(sizeof(char)* 8);
status = TRI_GetSerialNumList(&theSerialNumb, 8, 1);
It works fine however, if I use as follows in C# it give me above mentioned error:
byte[] BufList;
BufList = new byte[8];
rv = GetSerialNumList(out BufList, 8, 1);
The parameter you're passing in c# is a pointer to a byte array. What you're passing in c++ is a pointer to a pointer to a byte array. Also, in the C++ example, you're passing data to the function, but in the C# example, you're passing it as an out instead of a ref.
Although I'm not sure this would work, I would try to create a struct containing a byte array and pass the struct to the external function.
To answer some of the above comments, these functions typically modify memory passed to it rather than try to allocate additional memory due to the different ways programs create heaps.
The first thing I'd check is the C# import signature being used. There's the P/Invoke Interop Assistant tool available for free here.
Loading your function signature into the tool, translates it to:
public partial class NativeMethods {
/// Return Type: unsigned int
///theBufList: char**
///theBufSize: int
///theListLength: int
[System.Runtime.InteropServices.DllImportAttribute("<Unknown>", EntryPoint="GetSerialNumList")]
public static extern uint GetSerialNumList(ref System.IntPtr theBufList, int theBufSize, int theListLength) ;
}
The second thing, is that since you are allocating memory for the buffer in the C++/native version; perhaps you need to pass a pre-allocated buffer as well, when using C#.
Hope this helps.
Okay, I took pointers from Russell and kvr and did some digging around and following is the scheme that I came up with.
Original native function call:
uint32_t GetSerialNumList(char** theBufList, int theBufSize, int theListLength);
The way I am using PInvoke is as follows:
[DllImport(CameraDll, EntryPoint = "GetSerialNumList", CallingConvention = CallingConvention.Cdecl)]
private static extern int GetSerialNumList(ref IntPtr pBuf, int BufSize, int ListLength);
byte[] BufIn;
BufIn = new byte[8 * ListLength];
IntPtr pBuf = IntPtr.Zero;
pBuf = Marshal.AllocHGlobal(8 * ListLength);
Console.WriteLine("Calling GetSerialNumList");
rv = GetSerialNumList(ref pBuf, 8, ListLength);
Marshal.Copy(pBuf, BufIn, 0, 8*ListLength);
I feel this is somewhat long, but it gives me the desired result.

Trouble marshaling array of struct data from C++ to C#

I have searched for days and have tried everything I could find, but still cannot get this to work.
Details:
I have a 3rd party stock trading app that is calling into my unmanaged dll. It is supplying data that the dll processes/filters and then saves into a global ring buffer. The ring buffer is an array of structures, 100 long. All of this runs in the stock trading apps process.
I also have a managed C# app calling into the same dll in a different process that needs to get the info in the global ring buffer as quickly and efficiently as possible. Everything works except that I can only get data for the first structure in the array. Also after the call to the dll from C# the C# code no longer knows that arrayMD is an array of structs, it shows up in the debugger as a simple structure. Could it be the memcpy in the dll causing the problem? I’ve tried all kinds of combinations with [In, Out], IntPtr, and Marchal.PtrToStructure combinations. I am greatly fubar. Any help would be greatly appreciated.
Thanks
Here is what I am attempting.
On the dll side:
struct stMD
{
float Price;
unsigned int PriceDir;
unsigned int PriceDirCnt;
};
// Global memory
#pragma data_seg (".IPC")
bool NewPoint = false; // Flag used to signal a new point.
static stMD aryMD [100] = {{0}};
#pragma data_seg()
void __stdcall RetrieveMD (stMD *LatestMD [])
{
memcpy(*LatestMD, aryMD, sizeof(aryMD));
}
On the C# side:
[StructLayout(LayoutKind.Sequential)]
public struct stMD
{
public float Price;
public uint PriceDir;
public uint PriceDirCnt;
};
public static stMD[] arrayMD = new stMD[100];
[DllImport(#"Market.dll")]
public static extern void RetrieveMD(ref stMD[] arrayMD);
RetrieveMD(ref arrayMD);
The problem is the definition of your DLL entry point:
void __stdcall RetrieveMD (stMDP *LatestMD [])
You don't specify the size of the array, so how is C# supposed to know how many elements were copied into it? This is a problem in other languages too. Your implementation simply assumes that the provided memory is large enough to contain aryMD. But what if it's not? You've just created a buffer overrun.
If you want the caller to allocate the array, then the caller must also pass in the number of elements that the array contains.
Edit
The C++ declaration should look something like this:
// On input, length should be the number of elements in the LatestMD array.
// On output, length should be the number of valid records copied into the array.
void __stdcall RetrieveMD( stMDP * LatestMD, int * length );
The C# declaration would then look something like this:
[DllImport(#"Market.dll")]
public static extern void RetrieveMD(
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] ref stMD[] arrayMD,
[In, Out] ref int length);
Your problem, I think, is that you are trying to pass an array by reference from C# into C, and you almost never need to do that. In your case, all you want to happen is for C# to allocate memory for 100 of your structures, then pass that memory to C to be filled in. In your case, you're passing a pointer to an array of structures, which is an extra level of indirection that you don't really need.
You rarely see C functions that take a fixed sized array; instead, your function would typically be defined in C to accept a pointer and a length, e.g. something like:
void __stdcall RetrieveMD (stMDP *LatestMD, int size)
{
int count = 0;
if (size < sizeof(aryMD))
count = size;
else
count = sizeof(aryMD);
memcpy(LatestMD, aryMD, count);
}
To call this method from C#, you need to do two things. First, you need to allocate an array of the appropriate size to pass in. Second, you need to tell the marshaling code (that does the C# -> C copying) how to figure out how much data to be marshaled, via the [MarshalAs] attribute:
[DllImport(#"Market.dll")]
public static extern void RetrieveMD (
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] stMDP[] arrayMD,
int length
);
var x = new stMDP[100];
RetrieveMD(x, 100);
I got it working. Was I ever tring to make this harder than it is.
I re-read chapter 2 of ".NET 2.0 Interoperability Recipes: A Problem-Solution Approach".
Here's what works.
On C++ side I removed the pointers
struct stMD
{
float Price;
unsigned int PriceDir;
unsigned int PriceDirCnt;
};
// Global memory
#pragma data_seg (".IPC")
bool NewPoint = false; // Flag used to signal a new point.
static stMD aryMD [100] = {{0}};
#pragma data_seg()
void __stdcall RetrieveMD (stMD LatestMD [100])
{
memcpy(LatestMD, aryMD, sizeof(aryMD));
}
On the C# side I removed both (ref)s and added [In, Out]
[StructLayout(LayoutKind.Sequential)]
public struct stMD
{
public float Price;
public uint PriceDir;
public uint PriceDirCnt;
};
public static stMD[] arrayMD = new stMD[100];
[DllImport(#"Market.dll")]
public static extern void RetrieveMD([In, Out] stMD[] arrayMD);
RetrieveMD(arrayMD);
Thanks to everyone who offered help.

Interop C# to C++ struct

I am trying to call some legacy C code using interop in C#.
I am not too famliar with how interop works on C# yet but has to work with some confusing structs.
I got part of it working but the address messes up when I try to get the struct into the C layer.
I am trying to pass a struct to C code, it will do something to it, and I need to get a result back
I have these structs in C#
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RETURNBUFFER
public IntPtr records; //this is the struct RECORD
public IntPtr infoA; // this is the struct INFO
public IntPtr infoB;
public int number;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct INFO
{
public IntPtr doc; //this is a handle in C code
public int cpFirst;
public int cpLim;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RECORD
{
public int size;
}
Records is actually a pointer to another Struct STATS defined in C# like this,
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STATS
{
public int size;
public int a;
public int b;
public int c;
public int d;
public int e;
public int f;
public int g;
}
in the C# layer, i create the struct like the following
RETURNBUFFER returnBuffer = new RETURNBUFFER();
returnBuffer.infoA = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(INFO)));
returnBuffer.infoB = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(INFO)));
returnBuffer.records = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(STATS)));
When I run my code, I was only able to retrieve the first item in returnBuffer which is returnBuffer.records,
all the other item including the int value in returnBuffer is messed up.
I try to debug through it and look into the address value,
I found that when the code codes from C# -> C the address is shifted
I am not sure why the address is off,
Here is an example of what happened under a 64bit environment
C#
&ReturnBuffer
0x00000000046f05f8
&ReturnBuffer.records
0x00000000046f05f8
&ReturnBuffer.infoA
0x00000000046f0600
&ReturnBuffer.infoB
0x00000000046f0608
&ReturnBuffer.number
0x00000000046f0610
in C, let say the function I am calling is taking parameter RETURNBUFFER *pReturnBuffer,
i get these address,
pReturnBuffer
0x00000000046F05F8
&pReturnBuffer->records
0x00000000046F05F8
&pReturnBuffer->infoA
0x00000000046F0600
&pReturnBuffer->infoB
0x00000000046F0610 **This address is off by 8**
&pReturnBuffer->number
0x00000000046F0620 **this is off by 16**
So as a result,
when the code moves back to C# function,
I can construct the returnBuffer.records correctly,
but weren't able to construct neither infoA nor infoB nor get the correct value for returnBuffer.number
not sure what I am missing out here.
===============================================
I edited my code with help of, Fun Mun Pieng
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct CRB
{
[FieldOffset(0)]
public IntPtr pcstat;//CSTAT
[FieldOffset(8)]
public IntPtr caProc;//NLCA
[FieldOffset(24)]
public IntPtr caSent;//NLCA
[FieldOffset(40)]
public int cnlch;
}
now, the address matches up when it goes to C# -> C++ -> C#
However I am still getting some garbage data back.
I did some investigation and here is the erratic behaviour I found.
in C# code i make the call like this
IntPtr text = Marshal.StringToCoTaskMemUni("I am here");
legacyFunction(text, ref returnBuffer)
in here, when I call the GetHashCode function, i get the following values
returnBuffer.records.GetHashCode() 473881344
returnBuffer.infoA.GetHashCode() 473898944
returnBuffer.infoB.GetHashCode() 473898784
text.GetHashCode() 468770816
upon returning from the function, these hash value changes,
returnBuffer.records.GetHashCode() 543431240
returnBuffer.infoA.GetHashCode() 473799988
returnBuffer.infoB.GetHashCode() 473799988
text.GetHashCode() 473799988
Now, I can actually do,
Marshal.PtrToStringUni(checkReturnBuffer.infoA) and I get "I am here"
C# now thinks, both infoA and infoB are the same as text .
====================================================
2nd edit
The c++ structure is in fact
typedef struct RETURNBUFFER
{
RECORD *precord;
INFO infoA;
INFO infoB;
UINT number;
} CRB;
Thanks for the reply, this was indeed my problem.
I was somehow under the impression,
for every struct/class/object in C++
I have to make an equivalent IntPtr in C#
One last question, while I am here so I dont have to re define all structs in a new question,
for the IntPtr in struct INFO.
in C++, it is of type HANDLE
Am i correct here to define it as IntPtr? It is only a handle, but it is not a *handle thought, or should i just let it be a uint value?
Here's what I read from a msdn site "Remember, any API function that returns or accepts a handle is really working with an opaque pointer. Your code should marshal handles in Windows as System.IntPtr values"
If i defined it as IntPtr,
How should I alloc memory for it? Will the below be correct?
returnBuffer.infoA.doc = Marshal.AllocCoTaskMem(System.IntPtr.Size);
to unmarshal
Marshal.PtrToStructure(returnBuffer.infoA.doc, typeof(IntPtr));
is this the right approach?
Thank you so much
It might be because any of the following:
your C++ is compiled to 16 bytes alignment
your type for infoA is different between C++ and C#, or the size of the types are different
possible solutions include:
[FieldOffset(24)] public IntPtr infoB;
OR
comparing IntPtr.Size against sizeof(infoB) on C++
Edit: It seems infoA is 16 bytes, but your INFO declaration is not 16 bytes. It's very likely that your C# and C++ declarations are different. It would be good if you can include your C++ declarations in the question.
In the meantime, I can only guess the best match to be:
public struct RETURNBUFFER
{
public RECORD records; //this is the struct RECORD
public INFO infoA; // this is the struct INFO
public INFO infoB;
public int number;
}
Your assumption that RETURNBUFFER contains structure pointers has to be wrong. That's the only way that infoA and infoB can take up 16 bytes. The INFO structure certainly is 16 bytes long, I can't see the type of infoB. Thus:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RETURNBUFFER
public IntPtr records;
public INFO infoA;
public DUNNO infoB;
public int number;
}
Update your question with the C declarations if you still have trouble. It should be easy to see from them.
Try to make sure the win32 struct and c# struct is bit(bit size) mapping. This can be achieved by using exact c# type for win32 type.

Categories