VC++ __stdcall callbacks in C# - c#

I am working on a C# gui application that needs to call into a C++ dll. It appears that all calls are working except one in particular. From the C++ header file the function and callback signature are such:
typedef void (__stdcall *LPFNDLL_RECEIVE_CALLBACK)(CANMsg*);
USBCANPLUS_API
CAN_STATUS __stdcall canplus_setReceiveCallBack(
CANHANDLE handle,
LPFNDLL_RECEIVE_CALLBACK cbfn
);
Based on readings I have setup and inner class wrapping the call as such:
[DllImport("USBCanPlusDllF.dll")]
public static extern int canplus_setReceiveCallBack(int handle, CallbackDelegate callback);
[UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
public delegate void CallbackDelegate(ref CANMsg msg);
And used as:
private static void callback(ref EASYSYNC.CANMsg msg)
{
Debug.Print(msg.id + ":" + msg.timestamp + ":" + msg.flags + ":" + msg.len + ":" + msg.data);
}
static EASYSYNC.CallbackDelegate del = new EASYSYNC.CallbackDelegate(callback);
private void button1_Click(object sender, EventArgs e)
{
int handle = EASYSYNC.canplus_Open(IntPtr.Zero, "1000", IntPtr.Zero, IntPtr.Zero, 0);
if(handle > 0)
{
int result = EASYSYNC.canplus_setReceiveCallBack(handle, del);
}
}
I know dealing with C++ callbacks in C# are tricky and have read up a bit on the subject, hence I have the code above. My problem is one my code hits the int result = .... line the whole program goes out to lunch indicating that I am probably still doing something wrong with handling the callback. Can anyone advise?

I am a little unsure about this but you have specified the calling convention on the delegate to be StdCall (which it must be because the C/C++ code says so) but you did not do it for the canplus_setReceiveCallBack() dllimport function declaration, does it work when you do the following:
[DllImport("USBCanPlusDllF.dll", CallingConvention=CallingConvention.StdCall)]
public static extern int canplus_setReceiveCallBack(int handle, CallbackDelegate callback);
As I said, I am unsure but this might be the problem.

Related

Memory allocated doesn't get freed when C# application ends

I have a C++ wrapper around a Python module, which works perfectly in C++. That is there is no memory leak whatsoever. I exposed the C++ functionalities in C and used it in C# application. However, I noticed Whenever I end the C# application, the memory allocated does not get freed!
The irony is, everything is done in Python, and in C++ there is no memory allocation, it is just a series of methods which call their Python counterparts, and in Python all memory are managed by the interpreter.
The Python module basically runs an infinite loop in which a webcam feed is used and the images are processed and sent back to the clients (C#, C++). There is a Stop method in Python which simply sets a value which ends this loop in python so when that loop ends, effectively everything is ended and thus freed.
Side note:
The memory you see, is related to the model that gets loaded into the memory and that model is managed by Pytorch (a deeplearning framework in Python) so on the Python part we just have calls to other managed codes.
The C++, just calls these methods and I don't have any issues in Python or C++, but when it comes to C#, the allocated memory does not get freed !
In case it matters, here are my DLLImport statements on C# :
[DllImport(#"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Initialize(bool showFeed);
[DllImport(#"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Start(bool async);
[DllImport(#"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Stop();
[DllImport(#"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetCpuAffinity(int mask);
[DllImport(#"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool GetVideoFeedDbgStatus();
[DllImport(#"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetVideoFeedDbgStatus(bool status);
public delegate void CallbackDelegate(bool status, string message, IntPtr img, int rows, int cols);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
[DllImport(#"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void AddCallback(IntPtr fn);
[DllImport(#"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void RemoveCallback(IntPtr fn);
[DllImport(#"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetCallbacks_str();
And this is the C# callback:
int op=0;
public void callback01(bool status, string id, IntPtr img_ptr, int rows, int cols)
{
this.status = status;
this.id = id;
int[] sizes = { rows, cols };
img = new Mat(sizes, MatType.CV_8UC3, img_ptr);
// this is to retain the image, as the callback is so fast
// the time c# will draw it, it will be invalidated and thus you'd get
// distorted image. sowe make a deep copy here so we can work with images
// at our own pace!
img2 = new Mat();
img.CopyTo(img2);
//this allows to execute all interop calls under one thread
switch (op)
{
case 1:
Stop();
//MessageBox.Show("done.");
t.Abort();
break;
case 2:
MessageBox.Show(GetVideoFeedDbgStatus().ToString());
break;
default:
break;
}
//resetting the flag
op = 0;
}
How it is started:
Thread t;
private void btnRunService_Click(object sender, EventArgs e)
{
tmrFetchStatus.Start();
t = new Thread(new ThreadStart(() =>
{
RunService();
}));
t.IsBackground = true;
t.Start();
}
void RunService()
{
btnInit_Click(null, null);
Start(chkboxAsync.Checked);
}
private void btnInit_Click(object sender, EventArgs e)
{
Initialize(chkbxIShowFeed.Checked);
SetUpCallback();
tmrFetchStatus.Enabled = true;
}
and this is how it ends :
private void btnStop_Click(object sender, EventArgs e)
{
op = 1;
}
and the C exposed methods are like this :
extern "C"
{
Core* core = nullptr;
CORE_API int Initialize(bool showFeed)
{
return CreateHandle(showFeed) != nullptr ? 0 : 1;
}
CORE_API void Start(bool async)
{
core = reinterpret_cast<Core*>(GetHandle());
core->Start(async);
}
CORE_API void Stop(void)
{
core = reinterpret_cast<Core*>(GetHandle());
core->Stop();
}
CORE_API void AddCallback(CCallbackFn callback)
{
core->AddCallback_C_tmp(callback);
}
CORE_API void RemoveCallback(CCallbackFn callback)
{
core->RemoveCallback_C_tmp(callback);
}
std::string callbackLst;
CORE_API const char* GetCallbacks_str(void)
{
core = reinterpret_cast<Core*>(GetHandle());
auto results = core->GetCallbacks_C_tmp();
// this is shared, so clear it each time.
callbackLst = "";
for (auto pair : results)
{
callbackLst += pair.first + "\r\n";
}
//size_t size = callbackLst.size() * sizeof(char) + 1;
//char* output = new char[size];
//strcpy_s(output, size, callbackLst.c_str());
return callbackLst.c_str();
}
CORE_API CCallbackFn* GetCallbacks()
{
core = reinterpret_cast<Core*>(GetHandle());
auto results = core->GetCallbacks_C_tmp();
size_t size = results.size() * sizeof(CCallbackFn) + 1;
CCallbackFn* callback_list = new CCallbackFn[size];
int i = 0;
for (auto pair : results)
{
callback_list[i] = pair.second;
}
return callback_list;
}
}
And in the C++ part which uses/calls the Python module:
this->serviceUtilsModule = py::module::import("SomeModule");
this->cls = this->serviceUtilsModule.attr("SomeClass");
//...
this->startFunc = this->obj.attr("start");
this->startFuncAsync = this->obj.attr("start_async");
this->stopFunc = this->obj.attr("stop");
...
}
//...
CORE_API void Core::Start(bool async)
{
try
{
if (async)
{
this->startFuncAsync();
}
else
{
this->startFunc();
}
}
catch (std::exception ex)
{
std::cout << ex.what() << std::endl;
}
}
CORE_API void Core::Stop(void)
{
this->stopFunc();
}
What could be the cause here ? What should I be doing that I am not ? And here is a small demo showcasing the difference between the C++ application and the C# (The C++ doesn't have any memory issues, while in C#, I have to manually free the memory!) : https://imgur.com/a/eaW9AYT
And here is a second clip, showing the C# app inside vs (more debugging info): https://imgur.com/a/wL6UUOV
The C++ app simply starts and then after 10 seconds calls stops and when exists all memory is freed. In C#, however, this doesn't occur as you can see.
I found the issue. The issue seems to have been caused by terminating the thread inside itself that is inside callback01 which itself was run under let's say thread 2, we were trying to terminate this very thread:
...
switch (op)
{
case 1:
Stop();
//MessageBox.Show("done.");
t.Abort();
break;
case 2:
MessageBox.Show(GetVideoFeedDbgStatus().ToString());
break;
default:
break;
}
The Stop does stop the Python loop it seems, but it doesn't free the memory asap! as I believe is because the Python module is not unloaded, the Python object still exists and thus retains its memory. However, closing the C# app does free the memory.
Since the thread is marked as a Background, closing the app should terminate it as well.
Side note: I wonder why I didn't get any exception regarding this in C#?

Loading a native unmanaged C++ DLL into a managed C# app causes the DLL to output garbage

I have a native, unmanaged C++ DLL (symulator.dll) which I have to load in and call from a managed C# application.
The DLL utilizes C++ classes and dynamic memory allocation (via the new operator).
It exports a function called Init and its definition is as follows:
extern "C" __declspec( dllexport ) int Init( void )
{
sym = new CSymulator();
sym->Init();
return 0;
}
The CSymulator class contained within the DLL has a rather simple constructor:
CSymulator::CSymulator( void )
{
memset( mem, 0, sizeof( mem ) );
memset( &rmr, 0, sizeof( rmr ) );
md = 0;
ma = 0;
tacts = 0;
}
The CSymulator::Init() method, called by the Init() function exported by the DLL, is defined as follows:
int CSymulator::Init( void )
{
int *a = new int;
*a = 1;
FILE *f = fopen( "tmp.log", "wb" );
fprintf( f, "%i", *a );
fclose( f );
delete a;
return 0;
}
I am loading the native C++ DLL into the managed C# application using this code:
public partial class Form1 : Form
{
public IntPtr SimHandle;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string libname);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
delegate int SimInit();
SimInit DLL_Init;
public void InicjujDLL()
{
IntPtr adres;
adres = GetProcAddress(SimHandle, "Init");
DLL_Init = (SimInit)Marshal.GetDelegateForFunctionPointer(adres, typeof(SimInit));
int rc = DLL_Init();
}
private void WczytajDLL()
{
String fileName = "D:\\prg\\kompilator\\Debug DLL\\symulator.dll";
SimHandle = LoadLibrary(fileName);
if (SimHandle == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Exception(string.Format("Blad przy wczytywaniu biblioteki ({0})", errorCode));
}
else
{
InicjujDLL();
}
}
private void Form1_Load(object sender, EventArgs e)
{
WczytajDLL();
}
}
This code should produce a file named tmp.log with the content of 1 in it. But for some reason, tmp.log contains garbage data (a random 32-bit integer value instead of 1; for example, 2550276).
It's not the only function that produces garbage output. Any DLL function that tries to allocate memory dynamically is unable to use it after doing so.
It's as if the native C++ DLL is somehow getting its memory purged by the C# garbage collector.
How to prevent this behavior?
Wait a sec: Look at the reference below:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int MultiplyByTen(int numberToMultiply);
I didn't notice that your delegate doesn't have the same attribute.
For reference, I don't see anything unusual in how you are doing the LoadLibrary:
Dynamically Loading a Native Library
I have done this myself using the exact reference without a problem. I would suggest removing ALL code temporarily that executes inside the DLL and just do a simple pass-through value. Right now Init() is always returning 0. Try something else since you have zeroed out memory before, getting a zero back may just be a side-effect of the mem-zero op. Return 1974 or something.
Ensure you have allow unsafe code enabled and use the memory viewer to look at the stack (you are getting a pointer back so you have a starting point). If you do this beside the IL, you might spot where your memory is getting trashed.
HTH

Need help resolving AccessViolationException with C#/C interop

I need some help fixing a persistent AccessViolationException.
Given a c signature like this
struct message {
char *topic;
void *payload;
int payloadlen;
};
__declspec(dllexport) void callback_set(struct data *dat, void (*on_publish)(struct data *, void *, const struct message *));
I have this C#
public struct message
{
public string topic;
public IntPtr payload;
public int payloadlen;
};
/* INTEROP ACCESS */
public delegate void on_publish(IntPtr dat, IntPtr usrData, IntPtr messageData);
[DllImport(dllPath, CallingConvention = CallingConvention.Cdecl)]
public extern static void callback_set(IntPtr dat, IntPtr callback);
/* IMPLEMENTATION OF OUR on_publish*/
public static void MessageHandler(IntPtr dat, IntPtr usrData, IntPtr messageData)
{
var instance = (message)Marshal.PtrToStructure(messageData, typeof(message));
string test = instance.topic; // <-- this is successfully received
Console.WriteLine("message rec " + test);
} //<-- as soon as I exit, the dll blows up with access violation
/* REGISTERING MESSAGEHANDLER AS ON_PUBLISH */
public static void RegisterMessageHandler(IntPtr dat) //<-- I promise, the pointer I send here is valid and not modified
{
messageHandler = new on_publish(MessageHandler);
messageHandlerPtr = Marshal.GetFunctionPointerForDelegate(messageHandler);
callback_set(dat, messageHandlerPtr); //<-- if I do not call this, everything works, no ADE
Console.WriteLine("message handler registered");
}
//just tried to move to scope in order to retain their managed memory loc
private static IntPtr messageHandlerPtr;
private static on_publish messageHandler;
When running, and making sure a message should be received - I get the correct string for the topic but as soon as MessageHandler returns, I get the dreaded exception.
Things I've tried:
Change CallingConvention
Use on_publish instead of IntPtr in managed callback_set definition
Probably more things in desperation that should not have an impact
Any help much appreciated!
I can share a zip of the project if anyone can help - it will be BSD licensed just like Mosquitto which I'm trying to interop with.
I ended up creating a C++/CLI project to wrap the C project to .NET.
I found it much easier to manage the unmanaged code using C++, and I ended up with a nice interop to C# as my class became .NET accessible.
I would recommend this path, and will do it myself - the next time I need to integrate C# with a C lib.

C# pInvoke to a C function

I would be grateful for help with a problem I have been stuck on for a couple of days.
I have a native C++ function type declared so:
typedef STATUS (T_TED_AcppBoxDYN_RegisterEventCallback) (
PEventCallback function, // pointer to the customer callback
PVOID param // custom data (returned in callback)
);
where PEventCallback and PEVENT are declared like so:
typedef int (*PEventCallback) (PEVENT event, PVOID param);
typedef struct
{
int nEventId;
void* pParam;
} EVENT,*PEVENT;
The C++ code provides a pointer to a function of that type as a global variable:
T_TED_AcppBoxDYN_RegisterEventCallback* TED_AcppBoxDYN_RegisterEventCallback
= NULL;
which is initialized later, via this code:
#ifdef LOAD_PROC_ADDRESS
#undef LOAD_PROC_ADDRESS
#endif
#define LOAD_PROC_ADDRESS(handle,func) \
if((func=(T_##func*)GetProcAddress(handle,#func))==NULL) \
{\
sMsg.Format( "Error occurs while loading entry point\n'%s'\n"\
"from detector DLL '%s'\n", GetName (), #func );\
MessageBox(NULL, sMsg.GetBuffer(), "Load Proc Error", MB_OK | MB_ICONSTOP);\
return (false);\
}
bool P5100EDllManager::LoadProc ()
{
CString sMsg;
HMODULE hDllHandle = GetHandle();
if (hDllHandle == NULL)
{
return false; // cannot load the proc if the dll has not been loaded
}
LOAD_PROC_ADDRESS(hDllHandle, TED_AcppBoxDYN_RegisterEventCallback);
return true;
}
I want to call the pointed-to function from C#. For that purpose, I have defined a C# wrapper:
public delegate void TDICallBack(IntPtr callbackEvent, IntPtr pParam);
[DllImport(DLL, EntryPoint = "TED_AcppBoxDYN_RegisterEventCallback", CallingConvention = CallingConvention.Cdecl)]
private static extern int TED_AcppBoxDYN_RegisterEventCallback(TDICallBack callBack, IntPtr param);
public void RegisterEventCallback(TDICallBack callBack, IntPtr param)
{
TED_AcppBoxDYN_RegisterEventCallback(callBack, param);
}
I am using it like this:
TdiapiFacade.RegisterEventCallback(OnTdiCallBack, IntPtr.Zero);
public void OnTdiCallBack(IntPtr ptr, IntPtr param)
{
}
The RegisterEventCallback() seems to work successfully, but at the point where the callback function is supposed to be invoked, the app crashes. As you can see, at this stage I am not even unwrapping the parameters provided to the callback function.
What do I need to do to make this work?
P/invoke doesn't allow access to exported data (variables) such as your function pointer. You'll need a helper inside the DLL, either an exported function which wraps the function pointer, or one that returns it (the return type will be seen as a delegate inside C#).
Thanks for your input
I've resolved the issue, by modifying the registration of my callback from:
void RegisterCB()
{
var ret = TdiapiFacade.RegisterEventCallback(OnTdiCallBack, new IntPtr());
HandleError(ret);
}
to this:
private TDIAPI.TDICallBack callback;
void RegisterCB()
{
callback = new TDIAPI.TDICallBack(OnTdiCallBack);
var ret = TdiapiFacade.RegisterEventCallback(callback , new IntPtr());
HandleError(ret);
}
This fixes the issue, now my callback is properly invoked.

C# P/Invoke: Varargs delegate callback

I was just trying to do some managed/unmanaged interop. To get extended error information I decided to register a log callback offered by the dll:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate void LogCallback(void* arg1,int level,byte* fmt);
This definition works, but i get strings like "Format %s probed with size=%d and score=%d".
I tryed to add the __arglist keyword, but it is not allowed for delegates.
Well, it is not so dramatic for me, but I am just curious wether one could get the varargs parameters in C#.
I know that I could use c++ for interop.
So: Is there a way to do this purely in C#, with reasonable efford?
EDIT: For those who still did not get it: I am NOT IMPORTING a varargs function BUT EXPORTING it as a callback, which is then called my native code. I can specify only one at a time -> only one overload possible and __arglist does NOT work.
No there is no possible way to do it. The reason it is impossible is because of the way variable argument lists work in C.
In C variable arguments are just pushed as extra parameters on to the stack (the unmanaged stack in our case). C does not anywhere record the number of parameters on the stack, the called function takes its last formal parameter (the last argument before the varargs) gets its location and starts popping arguments off the stack.
The way that it knows how many variables to pop off the stack is completely convention based - some other parameter indicates how many variable arguments are sitting on the stack. For printf it does that by parsing the format string and popping off the stack every time it sees a format code. It seems like your callback is similar.
For the CLR to handle that, it would have to be able to know the correct convention to determine how many arguments it needed to pickup. You can't write your own handler, because it would require access to the unmanaged stack which you don't have access to. So there is no way you can do this from C#.
For more information on this you need to read up on C calling conventions.
Here is the way to deal with it. It may or may not be applicable to your case, depending on whether your callback arguments are meant to be used with printf family of functions.
First, import vsprintf and _vscprintf from msvcrt:
[DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern int vsprintf(
StringBuilder buffer,
string format,
IntPtr args);
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int _vscprintf(
string format,
IntPtr ptr);
Next, declare your delegate with IntPtr args pointer:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate void LogCallback(
void* arg1,
int level,
[In][MarshalAs(UnmanagedType.LPStr)] string fmt,
IntPtr args);
Now when your delegate is invoked via native code, simply use vsprintf to format the message correctly:
private void LogCallback(void* data, int level, string fmt, IntPtr args)
{
var sb = new StringBuilder(_vscprintf(fmt, args) + 1);
vsprintf(sb, fmt, args);
//here formattedMessage has the value your are looking for
var formattedMessage = sb.ToString();
...
}
Actually it is possible in CIL:
.class public auto ansi sealed MSIL.TestDelegate
extends [mscorlib]System.MulticastDelegate
{
.method public hidebysig specialname rtspecialname
instance void .ctor(object 'object',
native int 'method') runtime managed
{
}
.method public hidebysig newslot virtual
instance vararg void Invoke() runtime managed
{
}
}
I disagree with #shf301, It's possible.
You can use __arglist in case of PInvoke, like this:
[DllImport("msvcrt", CallingConvention = CallingConvention.Cdecl, EntryPoint = "printf")]
public static extern int PrintFormat([MarshalAs(UnmanagedType.LPStr)] string format, __arglist);
Calling: PrintFormat("Hello %d", __arglist(2019));
In the case of delegates and callbacks:
Define the following struct:
public unsafe struct VariableArgumentBuffer
{
public const int BufferLength = 64; // you can increase it if needed
public fixed byte Buffer[BufferLength];
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static VariableArgumentBuffer Create(params object[] args)
{
VariableArgumentBuffer buffer = new VariableArgumentBuffer();
Write(ref buffer, args);
return buffer;
}
public static void Write(ref VariableArgumentBuffer buffer, params object[] args)
{
if (args == null)
return;
fixed (byte* ptr = buffer.Buffer)
{
int offset = 0;
for (int i = 0; i < args.Length; i++)
{
var arg = args[i];
if (offset + Marshal.SizeOf(arg) > BufferLength)
throw new ArgumentOutOfRangeException();
switch (arg)
{
case byte value:
*(ptr + offset++) = value;
break;
case short value:
*(short*)(ptr + offset) = value;
offset += sizeof(short);
break;
case int value:
*(int*)(ptr + offset) = value;
offset += sizeof(int);
break;
case long value:
*(long*)(ptr + offset) = value;
offset += sizeof(long);
break;
case IntPtr value:
*(IntPtr*)(ptr + offset) = value;
offset += IntPtr.Size;
break;
default: // TODO: Add more types
throw new NotImplementedException();
}
}
}
}
}
Define your delegate
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int PrintFormatDelegate([MarshalAs(UnmanagedType.LPStr)] string format, VariableArgumentBuffer arglist);
For calling
callback("Hello %d %s", VariableArgumentBuffer.Create(2019, Marshal.StringToHGlobalAnsi("Merry christmas")));
For implementing
public static int MyPrintFormat(string format, VariableArgumentBuffer arglist)
{
var stream = new UnmanagedMemoryStream(arglist.Buffer, VariableArgumentBuffer.BufferLength);
var binary = new BinaryReader(stream);
....
}
You have to parse format to know what is pushed into the stack, and then read arguments using binary. For example, if you know an int32 is pushed, you can read it using binary.ReadInt32(). If you don't understand this part, please tell me in comments so I can provide you more info.
The following article covers a slightly different scenario and may be helpful:
How to P/Invoke VarArgs (variable arguments) in C#
You'd need support from the P/invoke marshaller for this to be possible. The marshaller does not provide such support. Thus it cannot be done.

Categories