C# string marshalling and LocalAlloc - c#

I have a COM callback from an unmanaged DLL that I need to use in C#.
The unmanaged DLL expects the callee to allocate memory using LocalAlloc (which the caller will LocalFree), populate it with WSTR and set value and chars to the WSTR pointer and string length respectively.
Code snippet I'm trying to convert to C#:
STDMETHODIMP CMyImpl::GetString(LPCSTR field, LPWSTR* value, int* chars) {
CStringW ret;
if (!strcmp(field, "matrix")) {
ret = L"None";
if (...)
ret.Append(L"001");
else if (...)
ret.Append(L"002");
else
ret.Append(L"003");
}
if (!ret.IsEmpty()) {
int len = ret.GetLength();
size_t sz = (len + 1) * sizeof(WCHAR);
LPWSTR buf = (LPWSTR)LocalAlloc(LPTR, sz);
if (!buf) {
return E_OUTOFMEMORY;
}
wcscpy_s(buf, len + 1, ret);
*chars = len;
*value = buf;
return S_OK;
}
return E_INVALIDARG;
}
What would the equivalent C# code be?
EDIT: COM Interface:
[id(2)] HRESULT GetString([in] LPCSTR field, [out] LPWSTR* value, [out] int* chars);

Straightforward way would be to import LocalAlloc function, convert the string to bytes using UnicodeEncoding.GetBytes and copy them to allocated memory with Marshall.Copy.

Related

C# Marshalling unsigned char* array from C++ DLL

I am trying to marshal data from a C++ dll to C# that has an unsigned char* parameter.
I know this question has been asked before but I have tried most of the answers and still not getting far....the best I go was an array of bytes but that contained all 0000's
So here's the C++ function
int Hid_Read(int iIndex, unsigned char* pucaBuffer, int iSize);
Here's the C# DLLImport
[DllImport(#"The.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int hid_Read(int iIndex, [Out] byte[] pucaBuffer, int iSize);
And the c# call:-
byte[] HID_ReceiveBuffer = new byte[128];
int intResult = hid_Read(HIDdeviceIndex, HID_ReceiveBuffer, 128);
for (int inteach = 0; inteach < 128; inteach++)
{
Debug.WriteLine("Data = " + HID_ReceiveBuffer[inteach].ToString());
}
This exits successfully but returns an array of 000's in the hid HID_ReceiveBuffer
Any help would be gratefully appreciated

IntPtr C# to C++ void*

I am a beginner in C# and I have been asked to create a C# code which uses a C++ DLL.
Functions from C++ DLL need a void* parameter so I send a IntPtr parameter from the C# code.
But at the end of this function my pointer seems to be NULL. Am I missing something?
Here my C# code:
int somme = 0;
IntPtr deviceHandle = new IntPtr();
uint[] SpiConf = new uint[7];
somme = Opening(deviceHandle, SpiConf);
if (deviceHandle == IntPtr.Zero)
{
Console.WriteLine("deviceHandle is NULL"); //
}
And here is my function from the C++ DLL:
int Opening(void* deviceHandle, unsigned char SpiConf[])
{
wchar_t devPath;
unsigned long devPathsize = 0;
unsigned short VID = 0x4d8;
unsigned short PID = 0xde;
int res;
deviceHandle = Mcp2210_OpenByIndex(VID, PID, 0, &devPath, &devPathsize);
res = Mcp2210_GetLastError();
if (res != E_SUCCESS)
{
//Console.WriteLine("Failed to open connection");
return -1;
}
if (deviceHandle == NULL)
{
return -2;
}
return 0; // This function returns 0
}
Any help would be very appreciate.

Calling C++ function with LPStr return value from C#

I have a C++ dll in 64-Bit, which contains a function that returns an LPStr. I would like to call this function in C#. The function declaration looks like this:
__declspec(dllexport) LPSTR __stdcall function(int16_t error_code);
In my C# code I have tried the following:
[DllImport(#"<PathToInterface.dll>", EntryPoint = "function")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string function(Int16 error_code);
And then in the program:
string ErrorMessage = "";
ErrorMessage = function(-10210);
I know that the function itself is good, as I can call it from another program (written in LabVIEW FWIW). But when I execute the C# Program, it just exits with error code 0x80000003, I can't even try, catch the exeption.
How do I call this function properly?
As a side node: I do have other functions in this dll, that use LPStr as parameters, which I can call without a problem. It is only two functions that return LPStr that make problems
How do I call this function properly?
As interop? you can't ... it is also error prone in plain C++
you should rather do it like
extern "C" __declspec(dllexport) int __stdcall function(int16_t error_code,
LPSTR buffer, size_t size)
{
LPCSTR format = "error: %i";
size_t req = _scprintf(format, error_code); // check for require size
if (req > size) //buffer size is too small
{
return req; //return required size
}
sprintf_s(buffer, size, format, error_code); //fill buffer
return 0;
}
And usage
class Program
{
static void Main(string[] args)
{
short error_code = -10210;
var ret = function(error_code, null, 0); // check for required size of the buffer
var sb = new StringBuilder(ret); // create large enough buffer
ret = function(error_code, sb, (uint)sb.Capacity + 1); //call again
var error_desc = sb.ToString(); //get results
Console.WriteLine(error_desc);
Console.ReadKey();
}
[DllImport("TestDll.dll", EntryPoint = "function", CharSet = CharSet.Ansi)]
public static extern int function(short error_code, StringBuilder sb, int size);
}
usage in C++
typedef int (__stdcall *function)(int16_t error_code, LPSTR buffer, size_t size);
int main()
{
auto handle = LoadLibrary(L"TestDll.dll");
auto proc = (function)GetProcAddress(handle, "_function#12");
// of course it can be done via linking
int16_t error_code = 333;
const int ret = proc(error_code, NULL, 0); // check for size
CHAR* buffer = new CHAR[ret + 1];
//CHAR buffer[200]; //eventually allocate on stack
//but size have to be constant value and may be too small
proc(error_code, buffer, ret+1); // call again
MessageBoxA(0, buffer, "Return", 0); //show result
delete[] buffer; //free buffer!
FreeLibrary(handle);
return 0;
}

C++ method declaration in C#

What will be the C# declaration of the following C++ methods of an unmanaged dll:
long WINAPI MT123_GetVersion(HANDLE hComHandle, BYTE *Version, int &len);
long __stdcall MT123_GetStatus(HANDLE hComHandle, BYTE *Saddr, BYTE &status)
long __stdcall MT123_GetTagData(HANDLE hComHandle, BYTE *Saddr,BYTE &status,
BYTE *Data, int &len);
with DllImport attribute?
Generally this:
long WINAPI MT123_GetVersion(HANDLE hComHandle, BYTE *Version, int &len);
Becomes:
int MT123_GetVersion(IntPtr hComHandle, IntPtr pVersionBuffer, ref int len)
The pVersionBuffer parameter must be initialised using Marshal.AllocCoTaskMem. You should use Marshal.Copy to copy the data to a managed array, then free the buffer using Marshal.FreeCoTaskMem.
I suggest you write a wrapper function to manage the nitty-gritty. For example
how much memory do you need to allocate: Is this one where you call the function twice, first time passing null to retrieve the length? Or does it have a reasonable maximum size you can hard-code?
What's the meaning of the return value: Is it an error code you should turn into an exception?
E.g:
byte[] GetVersionWrapper(IntPtr hComHandle)
{
IntPtr pVersionBuffer = IntPtr.Zero;
try
{
int len = 0;
// First retrieve the length
int status = MT123_GetVersion(hComHandle, IntPtr.Zero, ref len);
if(status != 0) throw new Exception("message here");
if(len < 0 || len > 1024) throw new Exception("message here");
// Now allocate a buffer of the given length.
pVersionBuffer = Marshal.AllocCoTaskMem(len);
// Now retrieve the version information into the buffer
int status = MT123_GetVersion(hComHandle, pVersionBuffer, ref len);
if(status != 0) throw new Exception("message here");
// Now copy the version information to a managed array.
byte[] retVal = new byte[len];
Marshal.Copy(pVersionBuffer, retVal, 0, len);
// Return the managed array.
return retVal;
}
finally
{
// done in Finally in case anything above throws an exception.
Marshal.FreeCoTaskMem(pVersionBuffer);
}
}

How to use extern "C" dll function taking char** as an argument in C# application?

I have dll with function:
extern "C"
int
doJob(char** buffer);
Its usage with C++ looks like this:
char* buf;
int status = doJob(&buf);
What definition should I have for this function in C#?
How can I use this function in C#?
One of the possible patterns is:
[DllImport("containsdojob.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 doJob(out IntPtr buffer);
[DllImport("containsdojob.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void freeMemory(IntPtr buffer);
and
IntPtr buffer = IntPtr.Zero;
string str = null;
try
{
doJob(out buffer);
if (buffer != IntPtr.Zero)
{
str = Marshal.PtrToStringAnsi(buffer);
}
}
finally
{
if (buffer != IntPtr.Zero)
{
freeMemory(buffer);
}
}
Note that you'll need a freeMemory method to free the memory allocated by doJob.
There are other possible patterns, for example based on BSTR and SysAllocString that are easier to implement C#-side (but more difficult to implement C-side)
The "pattern" for using BSTR:
C-side:
char *str = "Foo"; // your string
int len = strlen(str);
int wslen = MultiByteToWideChar(CP_ACP, 0, str, len, 0, 0);
BSTR bstr = SysAllocStringLen(NULL, wslen);
MultiByteToWideChar(CP_ACP, 0, str, len, bstr, wslen);
// bstr is the returned string
C#-side:
[DllImport("containsdojob.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 doJob([MarshalAs(UnmanagedType.BStr)] out string buffer);
string str;
doJob(out str);
The memory is automatically handled (freed) by the CLR.
If you are using Visual C++ you can even
char *str = "Foo"; // your string
_bstr_t bstrt(str);
BSTR bstr = bstrt.Detach();
// bstr is the returned string
Or C-side you could use one of the two allocators that can be freed C#-side: LocalAlloc or CoTaskMemAlloc:
char *str = "Foo"; // your string
char *buf = (char*)LocalAlloc(LMEM_FIXED, strlen(str) + 1);
// or char *buf = (char*)CoTaskMemAlloc(strlen(str) + 1);
strcpy(buf, str);
// buf is the returned string
Then you use the first example, but instead of calling
freeMemory(buffer);
you call:
Marshal.FreeHGlobal(buffer); // for LocalAlloc
or
Marshal.FreeCoTaskMem(buffer); // for CoTaskMemAlloc

Categories