Passing struct to unmanaged DLL with PInvoke - c#

due to several restrictions related to the project and not this problem in particular, I'm trying to pass through PInvoke a struct containing the data relevant to the Mat constructor of a Bitmap on C#.
In C# I receive a Bitmap, called frame, and in order to access the pixel data I'm doing:
BitmapData data = frame.LockBits(new Rectangle(0,0,frame.Width,frame.Height), ImageLockMode.ReadWrite, frame.PixelFormat);
using data.Scan0 as the IntPtr I create the struct for my unmanaged dll. Obviously, after calling my external function, I call frame.UnlockBits(data);
Here is the code
C# Invoke
[DllImport("ImageProcessingExtern", CallingConvention = CallingConvention.Cdecl)]
public static extern void drawLine(IntPtr objPtr, ref ExportBitmap bitmap);
C# Struct declaration
[StructLayout(LayoutKind.Sequential, Pack=8)]
public struct ExportBitmap
{
public int height;
public int width;
public IntPtr data;
public int step;
public int channels;
}
C++ function declaration
CVAPI(void) drawLine(ObjectsFinder* objPtr, ExportBitmap* bitmap) {
cv::Mat testmat(cv::Size(bitmap->width, bitmap->height), CV_MAKETYPE(CV_8U, bitmap->channels), bitmap->data, bitmap->step);
}
C++ Struct
#pragma pack(push, 8)
typedef struct ExportBitmap
{
int height;
int width;
void* data;
int step;
int channels;
} ExportBitmap;
#pragma pack(pop)
So far my code seems to work as long as I don't create that mat. If I modify ANY parameter other than the data inside the IntPtr everything works fine. When using the Mat constructor line I get an error saying the DLL is not a valid Win32 application(error code 193 from kernel32 LoadLibrary())
I've been stuck for about two weeks searching for a solution to this and I'm still not sure what is going on, I can only think there is a problem with how I'm obtaining the editable bitmap data, but I'm not sure what the problem with it might be.

Related

Unable to get pointer of structure C#

Scenario is like there is GUI and Dll, GUI is in C#(WPF) and it is using c++ Dll and dll needs structure pointer of structure created in GUI for processing commands.
In Dll structure having fixed size array so same created in GUI also.
C++ structure
struct _TempData
{
Int32 iOnFly_StepCalib;
Int32 iOnFly_BiDiCalib;
UNIT Unit;
byte uiPrintDirection;
Int16 siStep_Feed[20];
};
so In GUI(C#) structure is like...
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct _TempData
{
public Int32 iOnFly_StepCalib;
public Int32 iOnFly_BiDiCalib;
public UNIT Unit;
public byte uiPrintDirection;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public Int16[] siStep_Feed;
};
and my c# code for getting this structure pointer is here
[DllImport("Dll12.dll")]
public static extern UIntPtr sendMessageToHSM(Int32 MSG, IntPtr CmdStruct);
static void Main(string[] args)
{
_TempData tempData;
tempData.iOnFly_BiDiCalib = 1;
tempData.iOnFly_StepCalib = 2;
tempData.uiPrintDirection = 1;
tempData.Unit = UNIT.INCH;
unsafe
{
void* tempdata1 = &tempData;
sendMessageToHSM((int)HSM_COMMANDS.HSM_GUI_PC_UPDATE_STEP_CALIB, (IntPtr)tempdata1 );
}
}
This error is showing...
Error CS0208 Cannot take the address of, get the size of, or declare a pointer to a managed type ('_TempData')
on commenting this line in structure
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public Int16[] siStep_Feed;
then code is running fine.
I am new to C#.
Any help will be appreciated.
Thanks.
There are many errors in your code, also unsafe keyword is no use here
I believe what you might be looking for is
// get the size
int size = Marshal.SizeOf(tempData);
// Create some unmanaged memory
IntPtr ptr = Marshal.AllocHGlobal(size);
// marshal the structure to pointer
Marshal.StructureToPtr(tempData, ptr, false);
// call the funky api
sendMessageToHSM((int)HSM_COMMANDS.HSM_GUI_PC_UPDATE_STEP_CALIB, ptr);
// get the result if there is any
_TempData tempData2 = (_TempData)Marshal.PtrToStructure(ptr, typeof(_TempData));
// clean up the memory
Marshal.FreeHGlobal(ptr);
Also in C# we don't name types with an underscore

Unity, passing bytes array to a C++ function in a native plugin [duplicate]

I'm trying to pass raw texture data from Texture2D (byte array) to unmanaged C++ code. In C# code array length is about 1,5kk, however in C++ 'sizeof' always returns 8.
C# declaration of native method :
[DllImport("LibName", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr ProcessData(byte[] data);
C++:
extern "C" {
__declspec(dllexport) void ProcessData(uint8_t *data) {
//sizeof(data) is always 8
}
}
What am I doing wrong? Is there is a way to pass array without additional memory allocation in C++ code?
Few things you need to do with your current code:
1.You have to send the size of the array to the C++ plugin in another parameter as UnholySheep mentioned.
2.You also have to pin the array before sending it to the C++ side. This is done with the fixed keyword or GCHandle.Alloc function.
3.If the C++ function has a void return type, your C# function should also have the void return type.
Below is a corrected version of your code. No additional memory allocation is performed.
C#:
[DllImport("LibName", CallingConvention = CallingConvention.Cdecl)]
static extern void ProcessData(IntPtr data, int size);
public unsafe void ProcessData(byte[] data, int size)
{
//Pin Memory
fixed (byte* p = data)
{
ProcessData((IntPtr)p, size);
}
}
C++:
extern "C"
{
__declspec(dllexport) void ProcessData(unsigned char* data, int size)
{
//sizeof(data) is always 8
}
}

PInvoke error marshaling C++ dll into C#, passing struct into method

Hi all I am trying to compile a C++ project into a DLL and then use the Structs and methods from C#. I made an openCV program and I need to pass data back and forth. For now I am trying simple things to get familiar with the language since it is the first time I touch C++ and C# ...
So in C++ I have :
#pragma pack(push, 2)
typedef struct
{
//char *plate;
unsigned int width;
unsigned int height;
//bool hasPlateOn;
//unsigned char *imgData;
} Sframe;
extern "C" Sframe MYCVAPI findPossiblePlates(Sframe& in)
And I try to use it from C# like this :
[StructLayout(LayoutKind.Sequential)]
struct Sframe
{
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
// char[] plate;
[MarshalAs(UnmanagedType.U4)]
int width;
[MarshalAs(UnmanagedType.U4)]
int height;
// bool hasPlateOn;
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
// char[] imgData;
}
[DllImport(#"ALPR_pwv.dll",CallingConvention=CallingConvention.Cdecl,EntryPoint = "findPossiblePlates")]
static extern Sframe findPossiblePlates(Sframe infoOut);
But no matter what I try I get errors that the method's type signature is not PInvoke compatible.
is there anyone that could help me understand this issue?
Is it even possible to pass and get custom structs among C++ and C# ?
because the end stuct will be much more complex in the end since I have to pass images with data like coordinates etc...
As #SaniSinghHuttunen mentioned I was missing the ref keyword and had to use MarshalAs.U4 ... Thanks again :)

P/Invoke C++ DLL for filling a managed buffer

Basically I have a DLL and a header file (C++) for a data acquisition device that I use for my research. The device has 8 sensors and each sensor has x,y,z,etc data points. I'd like to create a UI program in WPF for this device. So far I've successfully been able to communicate with the device in .NET via an unsafe code implementation and read data into an array of structs (where each struct corresponds to one of the sensors). However I'd like to see if I can get it working with a strictly safe code implementation, but I've just been hitting brick walls for a while now. This is my first time working with unmanaged vs. managed code so please bear with me. I've searched countless threads online and the rabbit hole just keeps getting deeper so I'd like some advice from someone with experience. Basically the API header file has a function with the following definition:
int GetSynchronousRecord(USHORT sensorID, void *pRecord, int recordSize);
Essentially we pass the function a buffer by reference, and it fills it up. I have the option of getting either a single sensor's data, or all sensors at once depending on the sensorID argument I pass. What HAS worked for me so far (in the managed world) is if I do the following:
[StructLayout(LayoutKind.Sequential)]
public struct DOUBLE_POSITION_ANGLES_TIME_Q_RECORD
{
public double x;
public double y;
public double z;
public double a;
public double e;
public double r;
public double time;
public ushort quality;
};
...
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(ushort sensorID, ref DOUBLE_POSITION_ANGLES_TIME_Q_RECORD record, int recordSize);
...
DOUBLE_POSITION_ANGLES_TIME_Q_RECORD record = new DOUBLE_POSITION_ANGLES_TIME_Q_RECORD();
// Get the data from SENSOR_1 only
errorCode = GetSynchronousRecord(1, ref record, Marshal.SizeOf(record));
So this implementation works fine, I can get all the coordinate data and at a really good speed. However, I'd like to get ALL the sensors at once. In the C++ API code samples, they pass the GetSynchronousRecord function an ARRAY of STRUCTS, one struct for each sensor. I tried to do the same in C# as follows:
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(ushort sensorID, ref DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] record, int recordSize);
// Define Array of Struct for each sensor
DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] record = new DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[8];
while(recording) {
...
// Get data from ALL sensors (0xffff)
errorCode = GetSynchronousRecord(0xffff, ref record, Marshal.SizeOf(record)*8);
...
}
But this straight up crashes my program with an System.ExecutionEngineException error. I've read that since my function is expecting a void* pointer, that I should use an IntPtr argument, but this approach seemed quite confusing to be honest. Another thing I tried is to actually loop over each sensor and call the function for the sensor, but this dropped the speed INSANELY almost to 1 record/second (instead of 100 records/second). Many other similar threads on stackexchange say to use out parameter, or to use [In, Out] attribute on the function definition, but none of these suggestions worked.
TL;DR: If I understand my situation correctly, I have a MANAGED array of structs that I need to correctly pass to a C++ function as an argument (pass by reference), and then the function will fill my structs with data from a data acquisition device.
I apologize for the wall of text/code, any information for me from someone with experience would be much appreciated.
EDIT: Just to clarify, the GetSynchronousRecord function is INSIDE a while loop where on each iteration I'm getting new data points for each struct.
Your second p/invoke declaration is wrong. You had
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(
ushort sensorID,
ref DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] record,
int recordSize
);
The problem is the array parameter. Because you pass that array by ref that actually makes it a double pointer. Instead you want to simply remove the ref and declare the import like so:
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(
ushort sensorID,
[Out] DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] record,
int recordSize
);
The [Out] attribute tells the marshaler that the data is flowing out of the function. Without it the default assumption is that the data flows in.
When you call the function do so like this:
errorCode = GetSynchronousRecord(0xffff, record, Marshal.SizeOf(record)*record.Length);
The best way is probably using IntPtr rather than ref DOUBLE_POSITION_ANGLES_TIME_Q_RECORD.
I'd change the P/Invoke signature to:
[DllImport("ATC3DG64.DLL", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetSynchronousRecord(ushort sensorID, IntPtr record, int recordSize);
Create an IntPtr that points to the needed memory space:
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf<DOUBLE_POSITION_ANGLES_TIME_Q_RECORD>() * 8);
Call the P/Invoke function (which should fill the memory with the structs):
errorCode = GetSynchronousRecord(0xffff, ptr, Marshal.SizeOf<DOUBLE_POSITION_ANGLES_TIME_Q_RECORD>() * 8);
Get the structures from the memory block:
DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[] records = new DOUBLE_POSITION_ANGLES_TIME_Q_RECORD[8];
for (i = 0; i < 8; i++) {
records[i] = Marshal.PtrToStructure<DOUBLE_POSITION_ANGLES_TIME_Q_RECORD>(IntPtr.Add(ptr, i * Marshal.SizeOf<DOUBLE_POSITION_ANGLES_TIME_Q_RECORD>()));
}
Marshal.FreeHGlobal(ptr);

Porting (unmanaged) C++ to C# vs. using the C++ as a DLL in a C# application

I have a code library written in plain old C++ (no .NET/managed code) and I'm porting the application that uses this code to C#. I'm faced with two options:
Rewrite the C++ code in C# to achieve the same functionality;
Compile the C++ as a DLL and use it as a library in the C# application.
I'm relatively new to C# and am pretty unfamiliar with the implications of using an unmanaged code library in a C# app (or if there even are any). The code itself is moderate in size; it will likely take only a few days to rewrite in C#, but my thought is that leaving the code as a it is would allow me to use it in other applications as well (and to compile it on UNIX, etc).
What sort of things should I be aware of when making this decision? Are there any major drawbacks or gotchas to using the DLL in the C# application?
I would make a wrapper library using C++/CLI to expose the library to C#. This can leave your library unchanged, and just wrap it for use from .NET, providing the best of both options.
One thing I've found useful is to delve into C++/CLI when dealing with unmanaged C++ libraries. Create a managed wrapper using C++/CLI and call it from your C# code. The managed wrapper can include the library (I assume it's statically linked) in its DLL, and a project reference is all you need for your C# code.
It's not necessary to write a wrapper in C++/CLI. You can directly use Platform Invoke from C#:
http://msdn.microsoft.com/en-us/library/aa288468%28VS.71%29.aspx
EDIT: If you do it using C++/CLI, you'll need to be doing LoadLibrary calls and creating function pointers. This is significantly easier in C#. This is from the MSDN tutorial linked above, but with my own added comments:
class PlatformInvokeTest
{
[DllImport("msvcrt.dll")] // Specify the DLL we're importing from
public static extern int puts(string c); // This matches the signature of the DLL function. The CLR automatically marshals C++ types to C# types.
[DllImport("msvcrt.dll")]
internal static extern int _flushall();
public static void Main()
{
puts("Test");
_flushall();
}
}
EDIT: Complex types can also be marshaled, though it's necessary to define the structs. This example taken from my own code that invokes GDI+. I've snipped it a bit.
private static int SRCCOPY = 0x00CC0020;
private static uint BI_RGB = 0;
private static uint DIB_RGB_COLORS = 0;
[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);
[StructLayout(LayoutKind.Sequential)]
private struct BITMAPINFO
{
public uint biSize;
public int biWidth;
public int biHeight;
public short biPlanes;
public short biBitCount;
public uint biCompression;
public uint biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public uint biClrUsed;
public uint biClrImportant;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public uint[] cols;
}
public static Bitmap Downsample(Bitmap input, int bpp)
{
Bitmap retval = null;
// We will call into this GDI functionality from C#. Our plan:
// (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed)
// (2) Create a GDI monochrome hbitmap
// (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above)
// (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed)
IntPtr inputHandle = input.GetHbitmap();
//
// Step (2): create the monochrome bitmap.
//
BITMAPINFO bmi = new BITMAPINFO();
bmi.biSize = 40; // the size of the BITMAPHEADERINFO struct
bmi.biWidth = input.Width;
bmi.biHeight = input.Height;
bmi.biPlanes = 1;
bmi.biBitCount = (short)bpp; // 1bpp or 8bpp
bmi.biCompression = BI_RGB;
bmi.biSizeImage = (uint)(((input.Width + 7) & 0xFFFFFFF8) * input.Height / 8);
bmi.biXPelsPerMeter = 0; // not really important
bmi.biYPelsPerMeter = 0; // not really important
//
// Create the color palette.
//
uint numColors = (uint)1 << bpp; // 2 colors for 1bpp; 256 colors for 8bpp
bmi.biClrUsed = numColors;
bmi.biClrImportant = numColors;
bmi.cols = new uint[256];
if (bpp == 1)
{
bmi.cols[0] = MAKERGB(0, 0, 0);
bmi.cols[1] = MAKERGB(255, 255, 255);
}
else
{
for (int i = 0; i < numColors; i++)
{
bmi.cols[i] = MAKERGB(i, i, i);
}
}
//
// Now create the indexed bitmap
//
IntPtr bits0;
IntPtr indexedBitmapHandle = CreateDIBSection(IntPtr.Zero, ref bmi, DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);
IntPtr sourceDC = GetDC(IntPtr.Zero);
IntPtr hdc = CreateCompatibleDC(sourceDC);
IntPtr hdc0 = CreateCompatibleDC(sourceDC);
SelectObject(hdc, inputHandle);
SelectObject(hdc0, indexedBitmapHandle);
BitBlt(hdc0, 0, 0, input.Width, input.Height, hdc, 0, 0, SRCCOPY);
retval = Bitmap.FromHbitmap(indexedBitmapHandle);
//
// Dispose of the crud
//
DeleteDC(hdc);
DeleteDC(hdc0);
ReleaseDC(IntPtr.Zero, sourceDC);
DeleteObject(inputHandle);
DeleteObject(indexedBitmapHandle);
return retval;
}

Categories

Resources