This may be a stupid beginners question but I do not get it. I have a DLL which declares a function
int get_state(const unsigned char n,unsigned int *state)
What is the related C# import statement? Is
public static extern int get_card(byte n,ref uint state);
[DllImport(#"my.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
correct?
And when using this function, how do I have to call get_card() in order to get the data out of the parameter returned in state?
Thanks!
Well, DllImportAttribute must be put before the method it describes:
public static class MyClass {
...
// Since you don't use String, StringBuilder etc.
// CharSet = CharSet.Ansi is redundant and can be omitted
[DllImport(#"my.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int get_card(byte n, ref uint state);
...
}
Having get_card method declared, you can use it as usual, as any other method (and .Net will marshall the arguments):
...
byte n = 123;
uint state = 456;
int result = MyClass.get_card(n, ref state);
...
Related
I am working in C# and I need to call a function in a C++ dll library. This function returns a struct but I can´t get anything.
This is the function I need to call and the struct that returns in C++ library:
ATHENA_API _DEVICE_INFO* __stdcall GetDeviceInfoKeepConnection(_DEVICE_INFO* pDeviceInfo);
typedef struct TD_DEVICE_INFO{
TCHAR chDeviceName[256];
int nCommPort;
int nECGPos;
int nNumberOfChannel;
int nESUType;
int nTymestampType;
int nDeviceHandle;
TCHAR chDeviceID[260];
}_DEVICE_INFO;
This is my C# code trying to call the function:
[DllImport(#"\BAlertSDK\ABM_Athena.dll")]
static extern _DEVICE_INFO GetDeviceInfoKeepConnection(_DEVICE_INFO deviceInfo);
[StructLayout(LayoutKind.Sequential)]
struct _DEVICE_INFO
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string chDeviceName;
public int nCommPort;
public int nECGPos;
public int nNumberOfChannel;
public int nESUType;
public int nTymestampType;
public int nDeviceHandle;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string chDeviceID;
}
void Test_Click()
{
_DEVICE_INFO d = new _DEVICE_INFO();
_DEVICE_INFO deviceInfo = GetDeviceInfoKeepConnection(d);
}
The only I can get is an empty _DEVICE_INFO object. I think my problem is that I am not defining correctly the DEVICE INFO struct.
I have never worked with dll´s to this level. Can you help me?
Thanks in advance.
Thanks to all!! The problem has solved with this:
Parameter pass by reference and struct charset Unicode.
It seems that the function GetDeviceInfoKeepConnection returns a pointer to _DEVICE_INFO struct. So, in your C# code, you need to change the definition of the function to:
[DllImport(#"\BAlertSDK\ABM_Athena.dll")]
static extern IntPtr GetDeviceInfoKeepConnection(IntPtr deviceInfo);
And then you can access the struct data like this:
void Test_Click()
{
_DEVICE_INFO d = new _DEVICE_INFO();
IntPtr pDeviceInfo = Marshal.AllocHGlobal(Marshal.SizeOf(d));
Marshal.StructureToPtr(d, pDeviceInfo, false);
IntPtr deviceInfo = GetDeviceInfoKeepConnection(pDeviceInfo);
_DEVICE_INFO result = (_DEVICE_INFO)Marshal.PtrToStructure(deviceInfo, typeof(_DEVICE_INFO));
Marshal.FreeHGlobal(pDeviceInfo);
}
Note that, to ensure that the memory is cleaned up after use, you should use the Marshal.FreeHGlobal method to free the memory that was allocated by Marshal.AllocHGlobal.
I have hooked QtGui4.dll's unmanaged function QPainter::drawTextItem(const QPointF &p, const QTextItem &ti); using EasyHook in C#. I have wrapped the QPointF and QTextItem classes into managed structs and can get the data from these parameters by marshaling them. However, I would like to call member functions of the unmanaged QPainter class using managed code, so I am trying to wrap the QPainter class into a managed class. I have access to the unmanaged code for testing purposes, but will not have access to it in production. I have looked at the memory layout of the unmanaged classes QPainter and QPaintDevice. The QPainter class is just a single pointer and the QPaintDevice class is just two pointers (one for the virtual function table). I tried following the techniques in this blog, but when I call the wrapped QPainter::device() method just to see if I can get a pointer, my target (unmanaged, hooked) application crashes with either an access violation error (Access violation reading location 0x00000078) or an argument exception error (EEArgumentException at memory location 0x003786f4), depending on whether my wrapped QPainter class uses a sequential layout or not, respectively. I don’t expect someone to correct all the source code, but I was hoping if someone could point out some of the mistakes I am making in my approach. By the way, this is the first time I've ever posted a question on Stack Overflow because I usually find the answers to my problems from one or more existing questions. I apologize for the long question, but I tried to simplify the problem as much as possible without leaving out potentially relevant information. Thanks in advance for your help.
Memory Dump:
class QPainter (size = 4):
(0) d_ptr
class QPaintDevice (size = 8):
(0) {vfptr}
(4) painters
Unmanaged Code (qpainter.h):
class QPainter
{
QPainter();
explicit QPainter(QPaintDevice *);
~QPainter();
QPaintDevice *device() const; // trying to call this function
void drawTextItem(const QPointF &p, const QTextItem &ti); // hooked function
// class has additional functions, but left out for brevity
}
Managed Hook (hook.cs):
[DllImport("QtGui4.dll", CharSet = CharSet.Unicode, SetLastError = true,
CallingConvention = CallingConvention.ThisCall,
EntryPoint = "?drawTextItem#QPainter##QAEXABVQPointF##ABVQTextItem###Z")]
public static extern void QPainter_drawTextItem(IntPtr obj, IntPtr pt, IntPtr ti);
[UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Unicode,
SetLastError = true)]
delegate void D_QPainter_drawTextItem(IntPtr obj, IntPtr pt, IntPtr ti);
static unsafe void QPainter_drawTextItem_Hooked(IntPtr obj, IntPtr pt, IntPtr ti)
{
QTextItem qti = (QTextItem)Marshal.PtrToStructure(ti, typeof(QTextItem));
QPointF qpt = (QPointF)Marshal.PtrToStructure(pt, typeof(QPointF));
QPainter painter = (QPainter)Marshal.PtrToStructure(obj, typeof(QPainter));
__QPaintDevice* pd = painter.device(); // ERROR
QPainter_drawTextItem(obj, pt, ti);
}
Managed Wrapper (wrap.cs):
[StructLayout(LayoutKind.Sequential, Size=8)]
public unsafe struct __QPaintDevice
{
public IntPtr *vfptr;
public IntPtr painters;
};
[StructLayout(LayoutKind.Sequential, Size=4)]
public unsafe struct __QPainter
{
public IntPtr d_ptr;
};
// When the following line is included, I get this error:
// Access violation reading location 0x00000078.
// When the following line is excluded, I get this error:
// EEArgumentException at memory location 0x003786f4.
[StructLayout(LayoutKind.Sequential)] // is this useful?
public unsafe class QPainter : IDisposable
{
private __QPainter* _this;
private __QPaintDevice* _pd;
[DllImport("QtGui4.dll", EntryPoint = "??0QPainter##QAE#XZ",
CallingConvention = CallingConvention.ThisCall)]
private static extern int _QPainter_Constructor(__QPainter* ths);
[DllImport("QtGui4.dll",
EntryPoint = "??0QPainter##QAE#PAVQPaintDevice###Z",
CallingConvention = CallingConvention.ThisCall)]
private static extern int _QPainter_Constructor(__QPainter* ths,
__QPaintDevice* pd);
[DllImport("QtGui4.dll", EntryPoint = "??1QPainter##QAE#XZ",
CallingConvention = CallingConvention.ThisCall)]
private static extern int _QPainter_Destructor(__QPainter* ths);
[DllImport("QtGui4.dll",
EntryPoint = "?device#QPainter##QBEPAVQPaintDevice##XZ",
CallingConvention = CallingConvention.ThisCall)]
private static extern __QPaintDevice* _device(__QPainter* ths);
public QPainter()
{
_this = (__QPainter*)Marshal.AllocHGlobal(sizeof(__QPainter));
_QPainter_Constructor(_this);
}
public QPainter(__QPaintDevice* pd)
{
_this = (__QPainter*)Marshal.AllocHGlobal(sizeof(__QPainter));
_pd = (__QPaintDevice*)Marshal.AllocHGlobal(sizeof(__QPaintDevice));
_QPainter_Constructor(_this, pd);
}
public void Dispose()
{
_QPainter_Destructor(_this);
Marshal.FreeHGlobal((IntPtr)_this);
Marshal.FreeHGlobal((IntPtr)_pd);
_this = null;
_pd = null;
}
public __QPaintDevice* device()
{
return _device(_this);
}
};
I have a struct like this in C#:
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct MyStruct
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]
public string StringValue;
public uint IUintValue;
}
And a corresponding struct in native code
struct MyStruct
{
char StringValue[17];
ulong UintValue;
}
My goal is to pass an array of this structs from c# side to c++(native side) using pinvoke.
Here is how I use it in c#
[DllImport(#"MyLibrary.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern int SendArray([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]ref MyStruct[] arr, int recordsCount);
and a calling code is:
var array = new MyStruct[10];
//initialize
SendArray(ref array, array.Length);
On native side I have the following function signature:
extern "C" __declspec(dllexport) int SendArray(MyStruct** Arr, int recordsCount);
And it appears o work only for the first element in array. On c++ side I get this array, but only first element is correctly marshaled. The rest of them appears to be trash.
Where is my mistake?
Your C++ code does not receive an array of structs. It receives an array of pointers to struct. Change the C++ code to be like so:
int SendArray(MyStruct* Arr, int recordsCount);
or perhaps
int SendArray(MyStruct Arr[], int recordsCount);
And then your p/invoke should be
[DllImport(...)]
public static extern int SendArray([In] MyStruct[] arr, int recordsCount);
I am also suspicious of Pack=8. Are you quite sure?
I am trying to learn enough C# so that I can pass a strcture by reference to a C DLL; but it never gets to the "cFunction". As you can see in the cFunction, I am explicitly setting the streamSubset value to 44; but back in the c# portion it does not return "44".
Here is the C code:
typedef struct s_mPlot
{
double price[100];
int streamSubset;
} mPlot;
extern "C" __declspec( dllexport )
void cFunction(mPlot *Mplot){
Mplot->streamSubset = 44;}
// and here is the c# code
using System;
using Vibe.Function;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public class MPLOT
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public double [] price;
public int streamSubset;
}
namespace Vibe.Indicator{
public class myIdx : IndicatorObject {
[DllImport("C:\\Users\\joe\\mcDll.dll", CharSet = CharSet.Auto)]
public static extern void cFunction(
[In, MarshalAs(UnmanagedType.LPStruct)] MPLOT mPlot );
public myIdx(object _ctx):base(_ctx){}
private IPlotObject plot1;
protected override void Create()
{
MPLOT mPlot = new MPLOT();
mPlot.streamSubset = 2;
cFunction(mPlot);
if (mPlot.streamSubset == 44)
go();
}
}
}
I can see the following:
You almost certainly need to specify the cdecl calling convention in your DllImport attribute. Add CallingConvention=CallingConvention.Cdecl.
I believe that UnmanagedType.LPStruct adds an extra level of indirection. But you are passing a C# class which is a reference type. That means you are passing a pointer to a pointer. That's one level of indirection too many. First of all remove [In, MarshalAs(UnmanagedType.LPStruct)] altogether. Then your code should work. If you switched to a struct rather than a class for MPLOT then you'd need to pass by ref to get the indirection.
I think I would have the code like this:
[StructLayout(LayoutKind.Sequential)]
public struct MPLOT
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public double [] price;
public int streamSubset;
}
[DllImport("dllname.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern void cFunction(
ref MPLOT mPlot
);
Try specifying the calling convention explicitly:
[DllImport("C:\\Users\\joe\\mcDll.dll", CallingConvention=CallingConvention.Cdecl CharSet = CharSet.Auto)]
VC exports with calling convention cdecl by default, but DllImport uses stdcall by default. So you have to specify at least one of them explicitly, or better, both.
Replace [In, MarshalAs(UnmanagedType.LPStruct)] with ref.
When I want get total value of memory in C# I found a kernel32 function in MSDN to invoke data from system. MSDN declare function this way:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);
but this don't work correctly. I change "ref" to "[In, Out]" then it work correctly.
How can tell me what is [In, Out] parameters in C#?
In: http://msdn.microsoft.com/de-de/library/system.runtime.interopservices.inattribute.aspx
Out: http://msdn.microsoft.com/de-de/library/system.runtime.interopservices.outattribute.aspx
Short: They control the way data is marshalled. In this case, where you specify both of them, it means that data is marshalled to both sides (caller and callee).
The out and the ref parameters are used to return values in the same variables, ref is enough if you don't know you will use it in or out.
Out if you just want to use the variable to receive data from the function, In if you just want to send data to the function.
ref if you want to send and receive data from a function, if you put nothing so it will be In by default
Note: ref and out parameters are very useful when your method needs to return more than one values.
The following definition works (define the MEMORYSTATUSEX as a class):
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GlobalMemoryStatusEx(MEMORYSTATUSEX lpBuffer);
[StructLayout(LayoutKind.Sequential)]
public sealed class MEMORYSTATUSEX {
public uint dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
public uint dwMemoryLoad;
public ulong ullTotalPhys;
public ulong ullAvailPhys;
public ulong ullTotalPageFile;
public ulong ullAvailPageFile;
public ulong ullTotalVirtual;
public ulong ullAvailVirtual;
public ulong ullAvailExtendedVirtual;
}
Usage
var status = new MEMORYSTATUSEX();
GlobalMemoryStatusEx(status);
If you look at the function definition on MSDN it will tell you whether the parameters are In/Out:
BOOL WINAPI GlobalMemoryStatusEx(
__inout LPMEMORYSTATUSEX lpBuffer
);
In general if it says out, you should use a ref parameter, it makes is easier on any future developers trying to figure out how the code is working. When looking at the function call, you know the developer meant for the argument to be affected.