c#, passing any array to C++ dll with a single function - c#

For now I'm using below definition in the C# side
[DllImport("CL", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr bufferWrite(IntPtr handle,float[] obj);
to send a float array to C space in a c++ dll as in below definition
__declspec(dllexport)
void bufferWrite(OpenClBuffer * handle, void * ptr)
{
...
}
and it works(or seems like working). Now I need to support double, long and byte arrays too. Also I dont want to rewrite additional functions in c++ dll because it is already using a void pointer and size of buffer is handled by some other parameters inside.
Is it legal to use "ref object obj" instead of "float[] obj" or "long [] obj" in the C# space for all platforms(32 bit, 64 bit, xp,7,8.1,10) as in below
[DllImport("CL", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr bufferWrite(IntPtr handle,ref object obj);
or do I need to add extra functions to c++ such as
[DllImport("CL", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr bufferWriteFloat(...);
[DllImport("CL", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr bufferWriteInt(...);
[DllImport("CL", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr bufferWriteLong(...);
and make them call same old function with a casting to (void *)? Maybe just overloading the function in C# is enough and workds 100% of time?
[DllImport("CL", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr bufferWrit(..., float[] obj);
[DllImport("CL", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr bufferWrite(..., long[] obj);
[DllImport("CL", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr bufferWrite(..., double[] obj);
I'm assuming a void pointer is always same size as an array pointer when C#-C++ interop is used.

Related

C# Getting System.DIINotFoundException in An Integration Test But Not When I'm Actually Running The App?

The weirdest thing here is that my app works perfectly. It's just that my integration test can't find the .dll or dylib file. I am using liboqs and my projects structure looks like this:
amaranth/
Models, Controllers, Views, etc...
oqs.dll
liboqs.dylib
liboqs.so
amaranth.Tests/
IntegrationTests/
AdminControllerTests.cs
The file that references the dll/dylib files is at `amaranth/Helpers/liboqsRelatedHelpers/Sig.cs and the part of that file that references the dll/dylib files looks like this:
#region OQS native DLL functions
[DllImport("oqs", CallingConvention = CallingConvention.Cdecl)]
extern private static IntPtr OQS_SIG_new(string method_name);
[DllImport("oqs", CallingConvention = CallingConvention.Cdecl)]
extern private static int OQS_SIG_keypair(IntPtr sig, byte[] public_key, byte[] secret_key);
[DllImport("oqs", CallingConvention = CallingConvention.Cdecl)]
extern private static int OQS_SIG_sign(IntPtr sig, byte[] signature, ref UIntPtr sig_len, byte[] message, int message_len, byte[] secret_key);
[DllImport("oqs", CallingConvention = CallingConvention.Cdecl)]
extern private static int OQS_SIG_verify(IntPtr sig, byte[] message, int message_len, byte[] signature, int signature_len, byte[] public_key);
[DllImport("oqs", CallingConvention = CallingConvention.Cdecl)]
extern private static void OQS_SIG_free(IntPtr sig);
[DllImport("oqs", CallingConvention = CallingConvention.Cdecl)]
extern private static IntPtr OQS_SIG_alg_identifier(int index);
[DllImport("oqs", CallingConvention = CallingConvention.Cdecl)]
extern private static int OQS_SIG_alg_count();
[DllImport("oqs", CallingConvention = CallingConvention.Cdecl)]
extern private static int OQS_SIG_alg_is_enabled(string method_name);
#endregion
This is what it looked like when I found the error in Visual Studio for Mac:
What could be causing these file's not to be referenced in the Integration Tests but allowing the files to be referenced in running the actual app?
I had to copy the liboqs builds into bin/Debug/net6.0/ of the amaranth.Tests directory. So the new project structure is:
amaranth/
Models, Controllers, Views, etc...
oqs.dll
liboqs.dylib
liboqs.so
amaranth.Tests/
IntegrationTests/
AdminControllerTests.cs
bin/
Debug/
net6.0/
oqs.dll
liboqs.dylib
liboqs.so

c# unmanaged PInvoke AccessViolation

I have C++ DLL.
When call method from this DLL in C# AccessViolation was been throwing.
What in my code is wrong? Can someone help me?
C++ Header part:
typedef PVOID X_HANDLE;
XREADER_API BOOL ReaderOpen(X_HANDLE *pxHandle);
XREADER_API BOOL ReaderReceiveW26(X_HANDLE xHandle, LPVOID pBuffer, DWORD nBufferSize);
Working Example part C++:
X_HANDLE hReader;
unsigned char xKeyBuffer[3];
ReaderOpen(&hReader);
ReaderReceiveW26(hReader,xKeyBuffer,sizeof(xKeyBuffer));
My C# Code:
[DllImport("reader.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern bool ReaderOpen(IntPtr reference);
[DllImport("reader.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern bool ReaderReceiveW26(IntPtr hReader, IntPtr pBuffer, uint xKeyBuffer);
static void Main(string[] args)
{
byte[] received = new byte[3];
IntPtr unmanagedPointer = Marshal.AllocHGlobal(received.Length);
Marshal.Copy(received, 0, unmanagedPointer, received.Length);
IntPtr hReader = Marshal.AllocHGlobal(sizeof(uint));
var qqq = uint.Parse((Marshal.SizeOf(typeof(byte)) * received.Length).ToString());
ReaderOpen(hReader);
while (true)
{
if (ReaderReceiveW26(hReader, unmanagedPointer, qqq))
{
Console.WriteLine("!");
}
}
}
AccessViolation throwing at ReaderReceiveW26(hReader, unmanagedPointer, qqq)
Thanks for your patience!
Thanks to Hast Passant comment!
If we want use IntPtr or smth which edited by unmanaged code we need use
out paramName
For PVOID we can pass real c# type.
Working example:
[DllImport("reader.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern bool ReaderOpen(out IntPtr reference);
[DllImport("reader.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern bool ReaderReceiveW26(IntPtr hReader, byte[] pBuffer, uint xKeyBuffer);

how to display in messagebox a C function from C# with a WCHAR*

I'm building a C# application that loads a C++ library.
I call functions from that C++ DLL. I use below function to display input string.
c++ dll:
wchar_t* Test_EchoString( wchar_t *InputStr )
{
String HWStr = String( InputStr );
return HWStr.c_str();
}
c# code:
[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int _Test_EchoString([MarshalAs(UnmanagedType.LPWStr)] string s);
private void echo_string_Click(object sender, RoutedEventArgs e)
{
string upn = "aaaaa";
_Test_EchoString(upn);
MessageBox.Show(_Test_EchoString(upn).ToString());
}
I get in messagebox number 18666252 but i want to get a string from _Test_EchoString().
You have a pair of problems in your code:
in your C# you defined _Test_EchoString as public static extern int _Test_EchoString, so when you execute it, the returned value will be the address of the first character of the string HWStr.c_str().
And here it shows another problem, as anderas said, you are returning an invalid pointer, because HWStr.c_str() returns the pointer to the current value of the std::wstring object, so it is valid as long that the wstring is valid, so when the method Test_EchoString ends its execution it is no more valid(because HWStr is destroyed).
There are different ways to fix this problems, I'm going to show you two of these:
1) The first is to allocate the memory you want to return in the Heap and free it later with another call:
static wchar_t *Test_EchoStringResult;
extern "C" __declspec(dllexport) const wchar_t * Test_EchoStringNew(const wchar_t *InputStr)
{
std::wstring HWStr(InputStr);
HWStr += L" something";
Test_EchoStringResult = new wchar_t[HWStr.length() + sizeof(wchar_t)];
HWStr.copy(Test_EchoStringResult, HWStr.length());
Test_EchoStringResult[HWStr.length()] = L'\0';
return Test_EchoStringResult;
}
extern "C" __declspec(dllexport) void Test_EchoStringDelete()
{
delete[] Test_EchoStringResult;
}
And this is the usage in C#:
[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern IntPtr Test_EchoStringNew(string foo);
[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void Test_EchoStringDelete();
public void foo()
{
string result = Marshal.PtrToStringAuto(Test_EchoStringNew("test"));
MessageBox.Show(result.ToString());
Test_EchoStringDelete();
}
To me, this looks pretty ugly, so I'd prefer to use another pattern
2) Passing a callback to the C method and pass to this method HWStr.c_str() when HWStr is still valid:
extern "C" __declspec(dllexport) void Test_EchoString(const wchar_t *InputStr, void (*callback)(const wchar_t*))
{
std::wstring HWStr(InputStr);
HWStr += L" something";
callback(HWStr.c_str());
}
And here is the C# usage:
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public delegate void myCallback(string toShow);
[DllImport("testDll.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern void Test_EchoString(string foo, myCallback callback);
public void foo()
{
Test_EchoString("test", callback);
}
void callback(string toShow)
{
MessageBox.Show(toShow);
}

C++ function to C#

I am totally new to C++ programming. I need to call a C++ function from C#.
C++ function is:
BOOL Usb_Init(HWND hwnd);
I've tried:
[DllImport("UsbComm.dll", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool Usb_Init( out IntPtr hwnd);
I got the Error message:
PInvoke signature does not match the unmanaged target signature.
How to call the above C++ method?
I see the following mistakes in the code in the question:
The C++ code uses cdecl and the C# code uses stdcall. That does not match.
The C++ code is passed an HWND by value. The C# code has an IntPtr passed as an out parameter. That does not match.
There are multiple spurious arguments to the DllImport attribute.
The correct C# declaration is:
[DllImport("UsbComm.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool Usb_Init(IntPtr hwnd);
You could set ExactSpelling to true but I see no compelling reason to do so. Feel free to add that if you prefer. There's no point in specifying CharSet since there is no text involved. And SetLastError = true is probably a mistake. It's unlikely in my judgement that the unmanaged function calls SetLastError. My expectation is that you added SetLastError = true whilst trying to get rid of the error.
BOOL is defined as int in <windef.h>
You'll need to use int in the export declaration in C#. Reminder: a value of 0 equals false; anything else is true.
public static extern int Usb_Init(out IntPtr hwnd);
But also, your calling convention could also be wrong. Try each enum of CallingConvention
EDIT: The working signature is
[DllImport("UsbComm.dll", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
public static extern int Usb_Init(out IntPtr hwnd);
C++ code: Make sure definition of Usb_Init should look as shown below:
extern "C" __declspec(dllexport) BOOL __stdcall Usb_Init(HWND hwnd)
{
return TRUE;
}
C# code:
using System;
using System.Runtime.InteropServices;
namespace Win32DllClient
{
class Program
{
[DllImport("UsbComm.dll", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
public static extern bool Usb_Init(out IntPtr hwnd);
static void Main(string[] args)
{
IntPtr hwnd = new IntPtr(0);
var ret = Usb_Init(out hwnd);
}
}
}

P/Invoke function call problem

I am working on a system that requires interaction with a native C API using P/Invoke. Now I've (yet again) stumbled upon a problem which I cannot seem to solve in any way. The original function is designed to return 2 kinds of structures, based on a parameter that specifies which structure to use.
The C header file defines the structures and function as follows:
#pragma pack(1)
typedef struct {
DWORD JobId;
DWORD CardNum;
HANDLE hPrinter;
} CARDIDTYPE, FAR *LPCARDIDTYPE;
#pragma pack()
typedef struct {
BOOL bActive;
BOOL bSuccess;
} CARD_INFO_1, *PCARD_INFO_1, FAR *LPCARD_INFO_1;
typedef struct {
DWORD dwCopiesPrinted;
DWORD dwRemakeAttempts;
SYSTEMTIME TimeCompleted;
} CARD_INFO_2, *PCARD_INFO_2, FAR *LPCARD_INFO_2;
BOOL ICEAPI GetCardId(HDC hdc, LPCARDIDTYPE pCardId);
BOOL ICEAPI GetCardStatus(CARDIDTYPE CardId, DWORD level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded );
I have attempted to implement P/Invoke wrappers like this:
[StructLayout(LayoutKind.Sequential, Pack=1)]
public class CARDIDTYPE {
public UInt32 JobId;
public UInt32 CardNum;
public IntPtr hPrinter;
}
[StructLayout(LayoutKind.Sequential)]
public class CARD_INFO_1 {
public bool bActive;
public bool bSuccess;
}
[StructLayout(LayoutKind.Sequential)]
public class CARD_INFO_2 {
public UInt32 dwCopiesPrinted;
public UInt32 dwRemakeAttempts;
public Win32Util.SYSTEMTIME TimeCompleted;
}
[DllImport("ICE_API.DLL", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool GetCardId(HandleRef hDC, [Out]CARDIDTYPE pCardId);
[DllImport("ICE_API.DLL", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool GetCardStatus(CARDIDTYPE CardId, UInt32 level, [Out] byte[] pData, UInt32 cbBuf, out UInt32 pcbNeeded);
Calling the "GetCardId" seems to work fine. I get plausible data in CARDIDTYPE instance after calling it. However when I call "GetCardStatus" the problems start. The type of structure that should be returned is defined by the "level" param, and a value of 1 should result in a CARD_INFO_1 structure to be returnes in "pData".
The documentation contains the following C example:
CARD_INFO_1 ci1;
DWORD cbNeeded;
ci1.bActive = TRUE;
if (GetCardStatus(*lpCardID, 1, (LPBYTE)&ci1, sizeof(ci1), &cbNeeded )) { /* success */ }
My equivalent C# implementation is like this:
uint needed;
byte[] byteArray = new byte[Marshal.SizeOf(typeof(CARD_INFO_1))];
if (GetCardStatus(cardId, 1, byteArray, (uint)byteArray.Length, out needed)) { /* success */ }
When I execute this C# code, the method returns false and Marshal.GetLastWin32Error() return -1073741737 (which does not make much sense to me). I see no reason why this call should fail, and definitely not with this error code. So I suspect I have got something wrong in my P/Invoke wrapper.
I know that using "byte[]" as the type of pData is probably not correct, but according to some googling a "LPBYTE" translates to "[Out] byte[]". I guess the correct way to do this is to have pData as an IntPtr, and create the structure using Marshal.PtrToStructure(...). I have tried this, but the result is the same. Here is the code for this scenario:
[DllImport(#"ICE_API.DLL", CharSet = CharSet.Auto, EntryPoint = "_GetCardStatus#28", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool GetCardStatus(CARDIDTYPE CardId, UInt32 level, IntPtr pData, UInt32 cbBuf, out UInt32 pcbNeeded);
uint needed;
int memSize = Marshal.SizeOf(typeof(CARD_INFO_1));
IntPtr memPtr = Marshal.AllocHGlobal(memSize);
if (!GetCardStatus(cardId, 1, memPtr, (uint)memSize, out needed)) {
int lastError = Marshal.GetLastWin32Error();
// error code is -1073741737
}
CARD_INFO_1 info = (CARD_INFO_1)Marshal.PtrToStructure(memPtr, typeof(CARD_INFO_1));
Marshal.FreeHGlobal(memPtr);
Edit:
One thing I forgot to mention is that for some reason the GetCardStatus call fails with an unknown entry point exception if I do not specify EntryPoint = "_GetCardStatus#28". This has not happened to any other function I have wrapped, so it got me wondering a bit.
_GetCardStatus#28 gave me an idea. Unless you are running on 64-bit Windows, you've got the number of arguments wrong. Your P/Invoke for GetCardStatus would be _GetCardStatus#20, because it has 5 32-bit arguments. Your C declaration of GetCardStatus seems to accept the cardId by value rather than by reference. Since CARDIDTYPE is 12 bytes long, this would give the correct length of the argument list (28). Moreover, this would explain both your receiving an error code of -1073741737 (C0000057, STATUS_INVALID_PARAMETER) — since you're not passing a valid cardId — and the access violation — GetCardStatus tries to write to pcbNeeded, which is garbage because the marshaler hasn't even pushed it!
Ergo:
[DllImport("ICE_API.DLL", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool GetCardStatus (
IntPtr hPrinter, UInt32 cardNum, UInt32 jobId, UInt32 level,
[In, Out] CARD_INFO_1 data, UInt32 cbBuf, out UInt32 pcbNeeded) ;
[DllImport("ICE_API.DLL", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool GetCardStatus (
IntPtr hPrinter, UInt32 cardNum, UInt32 jobId, UInt32 level,
[In, Out] CARD_INFO_2 data, UInt32 cbBuf, out UInt32 pcbNeeded) ;
Note reverse order of the three CARDIDTYPE members: stdcall pushes the parameters left-to-right (i.e. towards lower addresses), and my guess is that a struct is "pushed" as a unit.
Also, if you later close the printer handle with CloseHandle, I'd suggest receiving the handle in CARDIDTYPE into an appropriate SafeHandle, not into a bare IntPtr, and declaring GetCardStatus to receive the safe handle.
As Anton suggests the problem lies in the parameters passed to the function. I did not notice this yesterday but the CARDIDTYPE structure is passed by pointer in the GetCardID function, and by value in the GetCardStatus function. In my calls I passed the CARDIDTYPE by pointer to the GetCardStatus also, forcing the P/Invoke framework to locate the correct function by specifying the exact function name as found in Dependecy Walker.
I solved this by defining the CARDIDTYPE as a struct instead of a class, and pass it by reference to the GetCardId function. Further the CARDIDTYPE is marshaled as a Struct when passed to the GetCardStatus function. This in addition to Antons technique of using two function definitions with different pData types (CARD_INFO_1 and CARD_INFO_2) now works correctly. Here is the final definitions:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CARDIDTYPE {
public UInt32 JobId;
public UInt32 CardNum;
public IntPtr hPrinter;
}
[StructLayout(LayoutKind.Sequential)]
public class CARD_INFO_1 {
public bool bActive;
public bool bSuccess;
}
[StructLayout(LayoutKind.Sequential)]
public class CARD_INFO_2 {
public UInt32 dwCopiesPrinted;
public UInt32 dwRemakeAttempts;
public Win32Util.SYSTEMTIME TimeCompleted;
}
[DllImport("ICE_API.DLL", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool GetCardId(HandleRef hDC, ref CARDIDTYPE pCardId);
[DllImport(#"ICE_API.DLL", EntryPoint = "GetCardStatus", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool GetCardStatus([MarshalAs(UnmanagedType.Struct)]CARDIDTYPE CardId, UInt32 level,
[In, Out] CARD_INFO_1 pData, UInt32 cbBuf, out UInt32 pcbNeeded);
[DllImport(#"ICE_API.DLL", EntryPoint = "GetCardStatus", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool GetCardStatus([MarshalAs(UnmanagedType.Struct)]CARDIDTYPE CardId, UInt32 level,
[In, Out] CARD_INFO_2 pData, UInt32 cbBuf, out UInt32 pcbNeeded);
Thank you both for your contribution to solving this problem :-)
The problem is you are using [Out] where you should be using nothing
[DllImport("ICE_API.DLL", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool GetCardStatus(CARDIDTYPE CardId, UInt32 level, byte[] pData, UInt32 cbBuf, out UInt32 pcbNeeded);
The Out/In attributes tell the CLR Marshaller in which direction the immediate variable will be marshalled. In the case of the byte[], the parameter is really doing nothing. One of it's sub elements is being moved.
Marshalling arrays is a tricky business though, especially when used directly in a signature vs. a structure. It may be better for you to use an IntPtr, allocate memory there and manually marshal the array out of the IntPtr.
[DllImport("ICE_API.DLL", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
public static extern bool GetCardStatus(CARDIDTYPE CardId, UInt32 level, IntPtr pData, UInt32 cbBuf, out UInt32 pcbNeeded);
public void Example(uint size) {
// Get other params
var ptr = Marshal.AllocHGlobal(size);
GetCardStatus(cardId, level, ptr, size, out needed);
// Marshal back the byte array here
Marshal.FreeHGlobal(ptr);
}

Categories