What is wrong with my INTEROP snippet? - c#

What's wrong with this? I can't seem to figure out how to change it. Please help....!!!!
Here's the error message:
A call to PInvoke function 'MyClassName::Process' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
#include "stdafx.h"
#include "TestDll.h"
extern "C" __declspec(dllexport) void Process(lpUnmagedStruct lpStruct, int size)
{
lpStruct[0].a = 0;
lpStruct[0].b = 0;
lpStruct[1].a = 1;
lpStruct[1].b = 1;
}
typedef struct
{
double a;
double b;
}UnmanagedStruct, far *lpUnmagedStruct;
extern "C" __declspec(dllexport) void Process(lpUnmagedStruct lpStruct, int size);
And here is my .NET codes:
[DllImport("TestDLL.dll", EntryPoint = "Process", CharSet = CharSet.Ansi)]
internal static extern void Process([In, Out] ManagedStruct[] aStruct, int size );
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class ManagedStruct
{
public double a;
public double b;
}
const int size = 3;
ManagedStruct[] aStruct = new ManagedStruct[size];
Process(aStruct, size);

I suspect you need to add the calling convention:
[DllImport("TestDLL.dll",
EntryPoint = "Process",
CharSet = CharSet.Ansi,
CallingConvention=CallingConvention.Cdecl)]

Related

Net5 calls the external DLL method and the return value is const char*, C# will directly crash

C++ code:
__declspec(dllexport) const char* Get() {
return "hello word!";
}
C# code:
[DllImport("TestLink.dll")]
public static extern string Get();
The program crashes directly after calling
In any case, when you allocate something from C/C++/Native side, you must use the COM allocator which .NET understands. So there are many ways to return a string, for example:
C++:
extern "C" __declspec(dllexport) void* __stdcall GetBSTR() {
return SysAllocString(L"hello world"); // uses CoTaskMemAlloc underneath
}
extern "C" __declspec(dllexport) void* __stdcall GetLPSTR() {
const char* p = "hello world";
int size = lstrlenA(p) + 1;
void* lp = CoTaskMemAlloc(size);
if (lp)
{
CopyMemory(lp, p, size);
}
return lp;
}
extern "C" __declspec(dllexport) void* __stdcall GetLPWSTR() {
const wchar_t* p = L"hello world";
int size = (lstrlenW(p) + 1) * sizeof(wchar_t);
void* lp = CoTaskMemAlloc(size);
if (lp)
{
CopyMemory(lp, p, size);
}
return lp;
}
And C#
[DllImport("MyDll")]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string GetBSTR();
[DllImport("MyDll")]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string GetLPWSTR();
[DllImport("MyDll")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string GetLPSTR();

Marshal callback in a struct from C++ to C#

I need to return the necessary information about an object as a struct with callbacks and other data.
This is what it looks like on the C# side:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ChartAddDataCallback(IntPtr data, int size);
[StructLayout(LayoutKind.Sequential)]
public struct ChartAccessors
{
public IntPtr HWnd;
public ChartAddDataCallback addDataCallback;
}
[DllImport("CppPart.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetAccessors")]
public static extern ChartAccessors GetAccessors();
The C++ "mirrored" version looks like this:
typedef void(__cdecl *AddDataCallback) (int * data, int size);
struct ChartAccessors
{
HWND m_chartHWnd;
AddDataCallback m_addDataCallback;
};
extern "C" __declspec(dllexport) ChartAccessors GetAccessors();
Usage:
static void Main(string[] args)
{
ChartAccessors accessors = GetAccessors();
}
However, when i start up the program i get the exception "Method's type signature is not PInvoke compatible."
It works if i use any other return type (like int or float) instead of the struct.
Marshal.PtrToStructure was actually solving this issue, as Pavel pointed out.
void RegisterCallbacks(IntPtr callbackPtr)
{
ChartAccessors accessors = (ChartAccessors)Marshal.PtrToStructure(callbackPtr, typeof(ChartAccessors));
// do stuff with the struct
}

Dllimport passing string from C# to C++

I'm trying to pass a string from C# to C++, using platform invoke.
C++ code:
#include<string>
using namespace std;
extern "C"
{
double __declspec(dllexport) Add(double a, double b)
{
return a + b;
}
string __declspec(dllexport) ToUpper(string s)
{
string tmp = s;
for(string::iterator it = tmp.begin();it != tmp.end();it++)
(*it)-=32;
return tmp;
}
}
C# code:
[DllImport("TestDll.dll", CharSet = CharSet.Ansi, CallingConvention =CallingConvention.Cdecl)]
public static extern string ToUpper(string s);
static void Main(string[] args)
{
string s = "hello";
Console.WriteLine(Add(a,b));
Console.WriteLine(ToUpper(s));
}
I receive a SEHException. Is it impossible to use std::string like this? Should I use char* instead ?
Сorrect decision
C# side:
[DllImport("CppDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetString(string s);
public string GetString_(string s)
{
var ptr = GetString(s);
var answerStr = Marshal.PtrToStringAnsi(ptr);
return answerStr;
}
C++ side:
extern "C" __declspec(dllexport) const char* GetString(char* s)
{
string workStr(s);
int lenStr = workStr.length() + 1;
char* answer = new char[lenStr];
const char * constAnswer = new char[lenStr];
strcpy(answer, workStr.c_str());
constAnswer = answer;
return constAnswer;
}
And disable /sdl- in the settings of the cpp project.
One way of doing it without causing a memory leak is using a callback.
C# side:
private delegate bool DLLCallback(IntPtr message);
[DllImport(#"YourLibrary.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
private static extern void Receive(DLLCallback callback);
private static bool callback(IntPtr ptr)
{
string result = Marshal.PtrToStringAnsi(ptr);
Console.WriteLine(result);
// If the Heap is used
// Marshal.FreeHGlobal(ptr);
return true;
}
private static void Main(string[] args) {
Receive(callback);
}
C++ side:
extern "C" {
typedef BOOL(__stdcall* OutputCallback)(const char* str);
__declspec(dllexport) void Receive(OutputCallback callback)
{
char buffer[BUFFER_SIZE];
ZeroMemory(buffer, BUFFER_SIZE);
BOOL callbackResult = callback(buffer);
}
}
There are other options. This is a good article about passing strings between managed and unmanaged code: article
I suggest to use char*. Here a possible solution.
If you create another C# function ToUpper_2 as follows
C# side:
[DllImport("TestDll.dll"), CallingConvention = CallingConvention.Cdecl]
private static extern IntPtr ToUpper(string s);
public static string ToUpper_2(string s)
{
return Marshal.PtrToStringAnsi(ToUpper(string s));
}
C++ side:
#include <algorithm>
#include <string>
extern "C" __declspec(dllexport) const char* ToUpper(char* s)
{
string tmp(s);
// your code for a string applied to tmp
return tmp.c_str();
}
you are done!

Invalid Managed/Unmanaged Type Combination With Embedded, Dynamically-Allocated Array

I have a common construct in an unmanaged Win32 C++ DLL:
// FirstElemPtrContainer.h
#include "stdafx.h"
typedef unsigned char elem_type; // a byte
typedef struct FirstElemPtrContainer {
unsigned char num_elems;
void *allocd_ary;
} FirstElemPtrContainer;
The void* in the struct is meant to contain a pointer to the first element of an allocated byte array.
The DLL that uses this definition then exports functions to allocate, populate, and deallocate the struct:
// The exported allocator function.
extern "C" _declspec(dllexport)
FirstElemPtrContainer *BuildStruct(int elem_count)
{
FirstElemPtrContainer *fepc_ptr = new FirstElemPtrContainer;
fepc_ptr->num_elems = elem_count;
elem_type *ary = new elem_type[fepc_ptr->num_elems];
for (int i = 0; i < fepc_ptr->num_elems; i++)
{
ary[i] = ((i + 1) * 5); // multiples of 5
}
fepc_ptr->allocd_ary = ary;
return fepc_ptr;
}
// The exported deallocator function.
extern "C" _declspec(dllexport) void
DestroyStruct(FirstElemPtrContainer *fepc_ptr)
{
delete[] fepc_ptr->allocd_ary;
delete fepc_ptr;
}
These work just fine for a native caller.
In C#, I try to describe this same structure via PInvoke:
[StructLayout(LayoutKind.Sequential)]
public struct FirstElemPtrContainer
{
public byte num_elems;
[MarshalAs(UnmanagedType.LPArray,
ArraySubType = UnmanagedType.U1, SizeConst = 4)]
public IntPtr allocd_ary;
}
... and describe the call interface like so:
public static class Imports
{
[DllImport("MyLib", CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr BuildStruct(int elem_count);
[DllImport("MyLib", CallingConvention = CallingConvention.Winapi)]
public static extern void DestroyStruct(IntPtr fepc_ptr);
}
Now I attempt to call my interface:
class Program
{
const int NUM_ELEMS = 4;
static void Main(string[] args)
{
IntPtr fepc_ptr = Imports.BuildStruct(NUM_ELEMS);
if ( fepc_ptr == IntPtr.Zero )
{
Console.WriteLine("Error getting struct from PInvoke.");
return;
}
FirstElemPtrContainer fepc =
(FirstElemPtrContainer)Marshal.PtrToStructure(fepc_ptr,
typeof(FirstElemPtrContainer));
//...
}
}
The PtrToStructure() call gives the error "Cannot marshal field 'allocd_ary' of type 'MyLibInvoke.FirstElemPtrContainer': Invalid managed/unmanaged type combination (Int/UInt must be paired with SysInt or SysUInt)."
You can see that I've hard-coded a particular number of elements, which we'll assume the caller adheres to. I've also added an ArraySubType clause, though it seems not to make a difference. Why the type mismatch complaint?
Your struct should be declared like this:
[StructLayout(LayoutKind.Sequential)]
public struct FirstElemPtrContainer
{
public byte num_elems;
public IntPtr allocd_ary;
}
it has to be done this way because allocd_ary is a pointer to unmanaged memory and cannot be marshalled by the p/invoke marshaller.
In order to read the contents of allocd_ary you can use Marshal.Copy.
FirstElemPtrContainer fepc = (FirstElemPtrContainer)Marshal.
PtrToStructure(fepc_ptr, typeof(FirstElemPtrContainer));
byte[] ary = new byte[fepc.num_elems];
Marshal.Copy(fepc.allocd_ary, ary, 0, ary.Length);
I suspect that CallingConvention.Winapi is wrong and that you should be using CallingConvention.Cdecl.

NUnit does not capture output of std::cerr

I have an nunit Test in C#, that calls a C# wrapper of a function in a C++ DLL.
The C++ code uses std::cerr to output various messages.
These messages cannot be redirected using nunit-console /out /err or /xml switch.
In nunit (the GUI version) the output does not appear anywhere.
I would like to be able to see this output in nunit (GUI version).
Ideally I would like to be able to access this output in the Test.
Thanks for any help.
Redirecting std::cerr is a matter of replacing the stream buffer with your own.
It is important to restore in original buffer before we exit. I don't know what your wrapper looks like, but you can probably figure out how to make it read output.str().
#include <iostream>
#include <sstream>
#include <cassert>
using namespace std;
int main()
{
streambuf* buf(cerr.rdbuf());
stringstream output;
cerr.rdbuf(output.rdbuf());
cerr << "Hello, world!" << endl;
assert(output.str() == "Hello, world!\n");
cerr.rdbuf(buf);
return 0;
}
Thanks for the hint.
This is what I ended up doing:
.CPP file ------------------------
#include <iostream>
#include <sstream>
static std::stringstream buffer;
static std::streambuf * savedBuffer = NULL;
extern "C" __declspec(dllexport) bool Redirect()
{
if (savedBuffer)
{
return false;
}
std::streambuf * buf(std::cerr.rdbuf());
std::cerr.rdbuf(buffer.rdbuf());
// This two lines are for illustration purposes only!
std::cerr << "Hello world" << std::endl;
return true;
}
extern "C" __declspec(dllexport) void Revert()
{
if (savedBuffer)
{
std::cerr.rdbuf(savedBuffer);
}
savedBuffer = NULL;
}
extern "C" __declspec(dllexport) const char * getCerr()
{
return _strdup(buffer.str().c_str());
}
extern "C" __declspec(dllexport) void freeCharPtr(char *ptr)
{
free(ptr);
}
.CS file ------------------------------------------
public static class Redirector
{
// PRIVATE ------------------------------------------------------------
private const String LibraryName = "MyCpp.dll";
[DllImport(LibraryName, CharSet = CharSet.Ansi)]
private static extern IntPtr getCerr();
// PUBLIC -------------------------------------------------------------
[DllImport(LibraryName, CharSet = CharSet.Ansi)]
public static extern bool Redirect();
[DllImport(LibraryName, CharSet = CharSet.Ansi)]
public static extern void Revert();
[DllImport(LibraryName, CharSet = CharSet.Ansi)]
internal static extern void freeCharPtr(IntPtr ptr);
public static string GetCerr()
{
IntPtr temp = getCerr();
string result = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(temp);
freeCharPtr(temp);
return result;
}
}
NUnit Test -----------------------
[Test]
// [Ignore]
public void TestRedirect()
{
Redirector.Redirect();
// Call more functions that output to std::cerr here.
Redirector.Revert();
System.Console.WriteLine(Redirector.GetCerr());
}
The freeCharPtr() stuff is necessary to free the allocated memory from _strdup(), since I could not work out (if it's even possible) how to marshal an std::string.
Note: This is not thread safe!

Categories

Resources