C# DllImport - Call a function that expects a struct - c#

I am working in C# and I need to call a function in a C++ dll library. This function expects a struct as parameter but I can´t get anything.
This is the function I need to call and the struct that expects in C++ library:
ATHENA_API int __stdcall GetEEGChannelsInfo( _EEGCHANNELS_INFO & stEEGChannelsInfo);
typedef struct TD_EEGCHANNELS_INFO{
char cChName[24][20];
BOOL bChUsed[24];
BOOL bChUsedInQualityData[24];
BOOL bChCanBeDecontaminated[24];
BOOL bIsChEEG[24];
BOOL bIsFlex;
int nChannelMap;
}_EEGCHANNELS_INFO;
This is my C# code trying to call the function:
[DllImport(#"\BAlertSDK\ABM_Athena.dll")]
public static extern int GetEEGChannelsInfo(_EEGCHANNELS_INFO e);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
class _EEGCHANNELS_INFO
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)]
public string[] cChName = new string[20];
public bool[] bChUsed = new bool[24];
public bool[] bChUsedInQualityData = new bool[24];
public bool[] bChCanBeDecontaminated = new bool[24];
public bool[] bIsChEEG = new bool[24];
public bool bIsFlex;
public int nChannelMap;
}
void Test_Click()
{
_EEGCHANNELS_INFO d = new _EEGCHANNELS_INFO();
int result = GetEEGChannelsInfo(d);
}
I can't get anything. And the function throws an unspecified error. I think my problem is that I am not defining correctly the struct.
I have never worked with dll´s to this level. Can you help me?
Thanks in advance.

Related

C# call C Function pass struct array: data scrambled

a simple Codesnippet should demonstrate the problem:
C:
typedef struct {
//unsigned short tno ;
unsigned long avf ;
}
MY_MAZ_TD;
__declspec(dllexport) void CMazGetAllToolData(MY_MAZ_TD* maz_td)
{
maz_td[0].avf = 4;
maz_td[1].avf = 5;
}
C#:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MY_MAZ_TD
{
//public ushort tno; /* Tool number */
public ulong avf;
}
[DllImport(TEST_DLL, CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern int CMazGetAllToolData([In, Out] MY_MAZ_TD[] maz_td);
MY_MAZ_TD[] my_maz_td = new MY_MAZ_TD[num];
CMazGetAllToolData(my_maz_td);
After calling CMazGetAllToolData:
my_maz_td[0].avf = 21474836484;
my_maz_td[1].avf = 0;
Actually I have much more values in the structure (like the commented tno). But even with only one variable in the struct, the values get scrambled. What is wrong here?

AccessViolationException using unmanaged C++ DLL

I'm trying, for the first time, to use an unmanaged C++ DLL ("res_lib") in a C# application. I've used cppsharp to generate the PInvoke code: for example, one of the functions/methods I'm trying to call is get_system_snapshot. From the .h file, this is defined as
SYS_INT SYS_ERR get_system_snapshot(SNAPSHOT_PARMS* snapshotp);
SYS_INT and SYS_ERR equate to a int32_t. SNAPSHOT_PARMS is
typedef struct SNAPSHOT_PARMS
{
SYS_ULONG size;
SYS_UINT count;
SYS_CHAR serial_no[600];
} SYS_PACK_DIRECTIVE SYS_SNAPSHOT_PARMS;
cppsharp has turned this into the following code snippets:
DllImport
[SuppressUnmanagedCodeSecurity]
[DllImport("res_lib", CallingConvention = CallingConvention.StdCall,
EntryPoint="get_system_snapshot")]
internal static extern int GetSystemSnapshot(IntPtr snapshotp);
Object
public unsafe partial class SNAPSHOT_PARMS : IDisposable
{
[StructLayout(LayoutKind.Explicit, Size = 608)]
public partial struct __Internal
{
[FieldOffset(0)]
internal uint size;
[FieldOffset(4)]
internal uint count;
[FieldOffset(8)]
internal fixed sbyte serial_no[600];
[SuppressUnmanagedCodeSecurity]
[DllImport("res_lib", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.ThisCall,
EntryPoint="??0SNAPSHOT_PARMS##QAE#ABU0##Z")]
internal static extern global::System.IntPtr cctor(global::System.IntPtr instance, global::System.IntPtr _0);
}
}
public SNAPSHOT_PARMS()
{
__Instance = Marshal.AllocHGlobal(sizeof(global::res_lib.SNAPSHOT_PARMS.__Internal));
__ownsNativeInstance = true;
NativeToManagedMap[__Instance] = this;
}
Main code
static void Main(string[] args)
{
SNAPSHOT_PARMS p = new SNAPSHOT_PARMS();
var result = res_lib.res_lib.GetSystemSnapshot(p);
}
public static unsafe int GetSystemSnapshot(global::res_lib.SNAPSHOT_PARMS snapshotp)
{
var __arg0 = ReferenceEquals(snapshotp, null) ? global::System.IntPtr.Zero : snapshotp.__Instance;
var __ret = __Internal.GetSystemSnapshot(out __arg0);
return __ret;
}
When calling the function, I get the infamous:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I've tried changing the CallingConvention from StdCall to Cdecl, introducing [In] and [Out] to the DllImport etc, but all to no avail. Can anyone see anything obviously wrong with the code - as may be apparent, this is all new to me, and perhaps I'm asking a bit much for cppsharp to generate code that won't need tweaked.
EDIT The original C++ documentation has an example, where the struct is initialised by
#define INIT_STRUCT(struct_p) { memset(struct_p, 0, sizeof(*(struct_p))); (struct_p)->size = sizeof(*(struct_p)); }
and is used
SNAPSHOT_PARMS snapshot_parms;
SYS_ERR result;
INIT_STRUCT(&snapshot_parms);
result = get_system_snapshot(&snapshot_parms);
From the C++ declarations, this should suffice:
[StructLayout(LayoutKind.Sequential)]
unsafe struct SNAPSHOT_PARMS {
public int size;
public int count;
public fixed byte serial_no[600];
}
[DllImport("res_lib", EntryPoint = "get_system_snapshot")]
static extern int GetSystemSnapshot(ref SNAPSHOT_PARMS snapshot);
Use as
var s = new SNAPSHOT_PARMS { size = Marshal.SizeOf<SNAPSHOT_PARMS>() };
int result = GetSystemSnapshot(ref s);
// check result, use s

How to return two different variable in c++?

dll C++
extern "C"
{
__declspec(dllexport) int mainfun()
{
return x;
}
}
In C#
[DllImport("example.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mainfun();
I only know how to return and call one variable from C++ to C#. I am writing a program where i need to call two different varables in C# from c++ dll
(like return x,y;). Please i need help.
EDIT1:
In C++
struct Point
{
int count_err;
int statement;
} obj;
extern "C"
{
__declspec(dllexport) Point mainfun()
{
return obj;
}
}
In C#
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public int errsize;
public int statmnt;
}
[DllImport("example.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Point mainfun();
errsize = mainfun();
statmnt = mainfun();
Here errsize is giving an error-"the name 'errsize' does not exist in the current context".. What to do?
EDIT2:
In C#
total_errors.Text = p.errsize.ToString();
giving same error-"the name 'p' does not exist in the current." context"
Define new struct or array of data. Something like this:
C++:
struct Point
{
int count_err;
int statement;
} obj;
extern "C"
{
__declspec(dllexport) Point mainfun()
{
return obj;
}
}
C#:
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public int errsize;
public int statmnt;
}
[DllImport("example.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Point mainfun();
Point p = mainfun();
var errsize = p.errsize;
var statmnt = p.statmnt;
First understand that if you want to return more than one value from any function, then you will need an object which can hold multiple values like struct, class object, list etc. But in your case you cannot use LIST or KeyValuePairList of C# because you have direct dependency with C++ code.
So use structure which is same in both the platforms. Now first you need to create a suitable data structure and change the return type of mainfun() when you are calling it as follows..
public struct abc {
public int a;
public int b;
}
[DllImport("example.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern abc mainfun();
Now in your C++ library, add the data structure and change the function definition.
typedef struct {
int a;
int b;
} abc;
extern "C"
{
__declspec(dllexport) abc mainfun()
{
abc obj;
obj.x = 1;
obj.y = 2;
return obj;
}
}

Method's type signature is not PInvoke compatible

I am trying to use a function from a c dll, in a c# application, I am facing this error each time i try to run the application and call the function in question.
At first i thought maybe this is because of the wrong signature i am using, but i tried to make it as simple as possible yet no luck.!
To cut a long story short:
This is my actual source code for c dll :
#include <stdio.h>
#include <string.h>
extern "C"
{
struct teststruct
{
char acharacter;
int anumber;
char* astring;
char anarray[10];
const char* conststring;
};
__declspec(dllexport) teststruct TestDLL()
{
teststruct stc;
stc.acharacter = 'A';
stc.anumber = 10;
strcpy(stc.anarray, "Test");
stc.astring = "astring!";
stc.conststring = "Crash?";
return stc;
}
}
And this is the c# counter part:
[StructLayout(LayoutKind.Sequential)]
public struct teststruct
{
public char acharacter;
public int anumber;
[MarshalAs(UnmanagedType.LPStr)]
public string astring;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public char[] anarray;
[MarshalAs(UnmanagedType.LPStr)]
public string conststring;
}
namespace Tcp_connections_list
{
public partial class Form1 : Form
{
[DllImport("simple c dll.dll",CallingConvention= CallingConvention.Cdecl)]
public static extern teststruct TestDLL();
public Form1()
{
InitializeComponent();
}
private void btnTestDll_Click(object sender, EventArgs e)
{
teststruct test = TestDLL(); //Crash at the very begining!
textBox1.Text = test.acharacter.ToString();
textBox1.Text = test.anarray.ToString();
textBox1.Text = test.anumber.ToString();
textBox1.Text = test.astring.ToString();
textBox1.Text = test.conststring.ToString();
}
}
}
The following code snippet gives me the same exact error, I changed the structure to
struct teststruct
{
char acharacter;
int anumber;
};
and its C# equivalent to
[StructLayout(LayoutKind.Sequential)]
public struct teststruct
{
public char acharacter;
public int anumber;
}
to make it as simple as possible, Yet again i get the same exact error!
What am i missing here?
The problem is the marshalling of the null-terminated C strings. You cannot expect the p/invoke marshaller to deal with those in a function return value. The struct needs to be declared like this:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct teststruct
{
public byte acharacter; // don't use C# char which is 2 bytes wide
public int anumber;
public IntPtr astring;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public char[] anarray;
public IntPtr conststring;
}
You'll need to use Marshal.PtrToStringAnsi to extract the contents of the C strings.
As a more general piece of advice, passing large structs as function return values is a bad idea. There is no single generally accepted ABI for doing that. Different compilers handle this in different ways. Better is to allocate the struct in the calling code, and pass its address. Like this:
__declspec(dllexport) void TestDLL(teststruct *ts)
{
ts->... = ...;
...
}
And on the C# side:
[DllImport(...)]
public static extern void TestDLL(out teststruct ts);
In fact, it would not surprise me if the struct that you are trying to work with cannot be marshalled as a return value. You really should pass it as an out parameter.

Pinvoking not copying entire string

I have a C++ native dll where a structure is defined as follows:
typedef struct
{
int x;
int y;
unsigned short* orange;
}SET_PROFILE;
And a function in the C++ dll as:
extern "C" _declspec(dllexport) void modifyClass(SET_PROFILE** input)
{
*input = &globPro;
}
where globPro is a global object of type SET_PROFILE and its member orange is "ABC".
I have redefined this structure on the C# side as a class:
[StructLayout(LayoutKind.Sequential)]
public class SET_PROFILE
{
public int x;
public int y;
public String orange;
}
and pinvoking the function:
[DllImport("Project2.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void modifyClass(out IntPtr input);
When I call this function and then marhal it back to the structure:
IntPtr classPtr = IntPtr.Zero;
classPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SET_PROFILE)));
modifyClass(out classPtr);
profile1 = (SET_PROFILE)Marshal.PtrToStructure(classPtr, typeof(SET_PROFILE));
Now, profile1's orange meber just has "A" instead of "ABC". It has something to do with how it's copied using pointers. I can't modify the C++ function however.
Is it because I used a string as the member of the C# class instead of unsigned short[]. I tried that too but failed.
Try using the following:
[StructLayout(LayoutKind.Sequential)]
public class SET_PROFILE
{
public int x;
public int y;
public System.IntPtr orange;
public string OrangeString { get { return Marshal.PtrToStringAnsi(orange); } }
}

Categories