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.
Related
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.
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
);
I have a Unity5 program that is using a common structure (smMsg) to send data from a C++ DLL into C#.
The structure contains an array:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public float[] mtx;
This array is to be considered as a 4x4 Matrix, hence the constant size of 16.
The program works, receiving data into the C# corresponding structure; however, it seems that from my array I am missing the first element (mtx[0]) each time I run the program. It seems every other element has shifted to the left with an extra 0 for the last element, maintaining the sequential order they are to be in.
I considered it to be because of the UnmanagedType I was using, however other sources tell me that UnmanagedType.ByValArray is the correct type.
Would anyone have a direction or lead I can follow to help solve this problem?
Process of Copying Data
C#:
// DLL Import
[DllImport(DLL_NAME, EntryPoint = "smCopy")]
private static extern void smCopyData(IntPtr dest, IntPtr len); //const VOID *dest, SIZE_T len);
// Copying data logic
{
// allocate intptr to buffer
var len = Marshal.SizeOf(typeof(smMsg));
IntPtr msg_intptr = Marshal.AllocHGlobal(len);
try
{
// Copy data
smCopyData(msg_intptr, (IntPtr)(len));
// Set POINTER data to struct
return_msg = (smMsg)Marshal.PtrToStructure(msg_intptr, typeof(smMsg));
}
finally
{
// free unmanaged memory!
Marshal.FreeHGlobal(msg_intptr);
}
}
C++
// Function called via DLL
void DLL_API smCopy(const VOID *dest, SIZE_T len)
{
CopyMemory((PVOID)(dest), (PVOID)(mapBuffer), len);
}
Struct Definition
The main piece of data I am interested in is float[] mtx
[StructLayout(LayoutKind.Sequential)]
public struct smMsg
{
public smHeader header;
public smData data;
}
[StructLayout(LayoutKind.Sequential)]
public struct smData
{
// Event ID.
public int evtId;
public int status;
// Floating point values. Actual data to be transmitted.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public float[] mtx;
}
[StructLayout(LayoutKind.Sequential)]
public struct smHeader
{
public ushort chkSum;
public char numWords;
public char msgType;
}
UPDATE 2/29
Thanks to #Zastai, I ended up able to recover the missing element. As it turns out, I wanted to be using "byte" as the data type instead of char, as char is a C# unicode type (short).
What I ended up doing is changing my smHeader as:
[StructLayout(LayoutKind.Sequential)]
public struct smHeader
{
public ushort chkSum;
public byte numWords;
public byte msgType;
}
.. which in turn lowered the smHeader size from 6 to 4, setting the smMsg struct size equal in terms of both C# & C++.
Thanks to #Zastai for helping out a ton.
Turns out that when using char in C++ for a struct, it's C# counterpart is a byte. This resolved the difference in structure size between C# & C++. Because of this error, the smMsg struct in C# allocated more memory than it needed.
The question is updated with the new smHeader definition.
I'm trying to P/invoke this in such a way I should be able to access the const char* members of this struct from C# (system, game, song, copyright, etc).
This is the struct as it is defined in the C++ header here: gme.h starting at line 79
struct gme_info_t
{
/* times in milliseconds; -1 if unknown */
int length; /* total length, if file specifies it */
int intro_length; /* length of song up to looping section */
int loop_length; /* length of looping section */
/* Length if available, otherwise intro_length+loop_length*2 if available,
otherwise a default of 150000 (2.5 minutes). */
int play_length;
int i4,i5,i6,i7,i8,i9,i10,i11,i12,i13,i14,i15; /* reserved */
/* empty string ("") if not available */
const char* system;
const char* game;
const char* song;
const char* author;
const char* copyright;
const char* comment;
const char* dumper;
const char *s7,*s8,*s9,*s10,*s11,*s12,*s13,*s14,*s15; /* reserved */
};
In my C# code, I have modeled this struct as follows:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct gme_info_t
{
public int length;
public int introLength;
public int loopLength;
public int playLength;
public int i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15;
public string system;
public string game;
public string song;
public string author;
public string copyright;
public string comment;
public string dumper;
public string s7, s8, s9, s10, s11, s12, s13, s14, s15;
}
Now, the function I'm calling is one that I have P/Invoked which has the C++ prototype as follows:
gme_err_t gme_track_info( Music_Emu const* me, gme_info_t** out, int track )
where gme_err_t is a const char*
(see gme.h, line 74, if you want a direct look at it)
(see gme.cpp, line 252 for its definition)
So, the function without all of its typedefs is as follows:
const char* gme_track_info( Music_Emu const* me, gme_info_t** out, int track )
The way this function works is that when called with a valid Music_Emu and a valid track,
the result is info about the music track which is assigned to the parameter 'out'.
The const char* being returned is basically for when errors occur, so it's not the main focus. The 'out' parameter is.
I have defined the P/Invoke for this function in C# as follows:
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern string gme_track_info(IntPtr emuHandle, out gme_info_t trackInfo, int track);
Here is my code currently for attempting to read the copyright string in that struct.
static void Main()
{
// Initialize the MusicEmu reference first (this works fine).
IntPtr emuRef;
string initEmuRef = NativeMethods.gme_open_file("Adventure Island 4.nsf", out emuRef, 48000);
Console.WriteLine("Error Message (if any): " + initEmuRef);
// Now get the track info.
gme_info_t trackInfo;
NativeMethods.gme_track_info(emuRef, out trackInfo, 0);
Console.WriteLine("Copyright: " + trackInfo.copyright); // I get an empty string. When checked with a different NSF reader it prints "1994 Hudson Soft."
// Keep console window up.
Console.ReadLine();
}
Any help would be appreciated. I've tried for roughly 4 hours to make this work. I've looked all around StackOverflow (and the net in general) for possible solutions, but I haven't found any that were close to this sort of question. Most other problems were about a pointer to a pointer to a struct of arrays and such, which isn't very helpful at all for this case.
If any other information is needed, just ask, I'd be happy to provide it.
Your trackInfo parameter should be an out IntPtr. Then you can marshal it to a C# struct with the Marshal.PtrToStructure method.
IntPtr trackInfoPtr;
NativeMethods.gme_track_info(emuRef, out trackInfoPtr);
gme_info_t trackInfo = (gme_info_t)Marshal.PtrToStructure(trackInfoPtr, typeof(gme_info_t));
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.