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

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#?

Related

Unable to get result of std::string function in C++ to C# Interop

What I am trying to achieve: I am trying to access a C++ application's functions through its DLL in a C# (Interop).
Issue which I am facing right now: When i create a std::string return type function in C++ and calls it through its DLL in C# code there is now output at all.
Code which I have written for C++ APP
extern "C"
{
__declspec(dllexport) int SM_Interop_API_Add(int a, int b)
{
return a + b;
}
__declspec(dllexport) std::string SM_Interop_API_getLanguage()
{
return "This is Test String";
//return getLanguage();
}
}
Code which I have written for C# APP
class Program
{
[DllImport(#"CPPSample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi,EntryPoint = "SM_Interop_API_Add")]
public static extern int SM_Interop_API_Add(int a, int b);
[DllImport(#"CPPSample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "SM_Interop_API_getLanguage")]
public static extern string SM_Interop_API_getLanguage();
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
int sum = SM_Interop_API_Add(10, 10);
Console.WriteLine($"Result: {sum}");
string result = SM_Interop_API_getLanguage();
Console.WriteLine($"Sm Language: {result}");
Console.WriteLine("----EOP----");
}
}
In the above code for C++, SM_Interop_API_getLanguage is supposed to return a string, and when I calls it through its DLL in C# code it does not return any result at all, first, I tried by returning the actual output of getLanguage which did not work then I tried to return some hard codded string but that is also coming as output.
Also to mention the function with int return type works perfectly only std::string return type is not working here in my case.
Kindly guide me if I am doing anything wrong or missing anything. Any help is appreciated.
std::string will never work here as the marshaller does not know how to free it.
Instead, you need to pass in a buffer from the C# side, like this
class Program
{
[DllImport(#"CPPSample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi,EntryPoint = "SM_Interop_API_Add")]
public static extern int SM_Interop_API_Add(int a, int b);
[DllImport(#"CPPSample.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "SM_Interop_API_getLanguage")]
public static extern void SM_Interop_API_getLanguage(StringBuilder buffer)
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
int sum = SM_Interop_API_Add(10, 10);
Console.WriteLine($"Result: {sum}");
var result = new StringBuilder(200);
SM_Interop_API_getLanguage(result);
Console.WriteLine($"Sm Language: {result}");
Console.WriteLine("----EOP----");
}
}
Then on the C++ side, you simply copy it in (not great with C++, guessing a bit)
__declspec(dllexport) void SM_Interop_API_getLanguage(char* buffer)
{
strcpy(buffer, "This is Test String");
//return getLanguage();
}
Passing a buffer size and checking that it's large enough would be wise also.

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

Buffer overrun detected

I have an one question when I use C# DllImport C++ dll, I use the visual studio 2010 & checked the "Enable unmanaged code debugging", when it's running, always show the message "Buffer overrun detected! ... A buffer overrun has been detected which has corrupted the program's internal state. The program cannot safely continue execution and must now be terminated."
This dll is Third-party vendors to provide, and they says it's no error to run this dll, how can i fixed it?
My M++ dll function is,
int avc_to_avi_convert(char* chPath_avc, char* chPath_avi, int pPrivate, PROGRESS_CALLBACK progress_callback);
void avc_to_avi_close(int* pFd_avi);
typedef void (*PROGRESS_CALLBACK)(int iPercent, int pPrivate);
And i am use it in my C# dllimport like this :
public delegate void PROGRESS_CALLBACK(int _iPercent, int _pPrivate);
[DllImportAttribute(#"..\xxx.dll", EntryPoint = "avc_to_avi_convert", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern unsafe int avc_to_avi_convert([MarshalAs(UnmanagedType.LPStr)] StringBuilder _inputAVC, [MarshalAs(UnmanagedType.LPStr)] StringBuilder _ouputAVI, int pPrivate, PROGRESS_CALLBACK _pg);
[DllImportAttribute(#"..\xxx.dll", EntryPoint = "avc_to_avi_close", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern unsafe void avc_to_avi_close(int pFd_avi);
private void button1_Click(object sender, EventArgs e)
{
int i = 0;
StringBuilder inputFile = new StringBuilder(Application.StartupPath + #"\avc\abc.avc");//(#"C:\avc\abc.avc");
StringBuilder outputFile = new StringBuilder(Application.StartupPath + #"\avi\abc.avi");//(#"C:\avi\abc.avi");
if (avc_to_avi_convert(
inputFile,
outputFile,
1,
progress_func) > 0) {
}
}
public void progress_func(int iProgress, int pPrivate)
{
if (iProgress == 100)
{
//success
}
else if (iProgress == -1)
{
//convert error
}
else if (iProgress == -2)
{
//Not enough disk space
}
else
{
//update progress
}
}
I changed my code to,
[DllImportAttribute(#"..\xxx.dll", EntryPoint = "avc_to_avi_convert", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public static extern unsafe int avc_to_avi_convert([MarshalAs(UnmanagedType.BStr)] String _inputAVC, [MarshalAs(UnmanagedType.BStr)] String _ouputAVI, int pPrivate, PROGRESS_CALLBACK _pg);
[DllImportAttribute(#"..\xxx.dll", EntryPoint = "avc_to_avi_close", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public static extern void avc_to_avi_close(ref int avi);
And ran it, and I still get the same error.
1.) Are you sure about the calling convention? Try CallingConvention.StdCall. Read this:
http://blogs.msdn.com/b/adam_nathan/archive/2003/05/21/56690.aspx
2.) Try a different values for the CharSet attribute and use String instead of StringBuilder for the path arguments. This is a good reference:
http://msdn.microsoft.com/en-us/library/s9ts558h.aspx
3.) Also, the avc_to_avi_close method should look like this:
[DllImportAttribute("xxx.dll", EntryPoint="avc_to_avi_close")]
public static extern void avc_to_avi_close(ref int avi);
4.) Other things you my want to try:
Use unusual combinations of CharSet and MarshalAs. For example you know from the link above that Unicode should be used with LPWStr and Ansi with LPStr but you can of course try other combinations.
Your extern methods need not to be marked unsafe.
If you have exhausted all other options, why don't you try to write a C++ program that is analogous to your C# code:
If it crashes you proved that the error is in the library.
If it doesn't crash you know the error is in your use of PInovke, and you can go through all the 12+ combinations from my answer one by one.
You are passing a delegate to p/invoke and then letting the garbage collector free it. This causes the crash, because when the delegate object is finalized, the trampoline set up to allow calls from native code is deallocated. Then the native code invokes a dangling function pointer.
The garbage collector is totally unaware of any objects being used from inside native code. You MUST keep a reference to the delegate alive for the duration of the call. Try adding a delegate variable (don't use implicit conversion which creates a temporary) and then use GCHandle to keep the delegate alive for as long as the native code is using it.

VC++ __stdcall callbacks in 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.

How do I marshal a pointer to an array of pointers to structures from managed to unmanaged code?

I'm trying to write plugin dll which has to implement the following function:
int GetFunctionTable(FuncDescStruct **ppFunctionTable);
So my plugin code in C# declares this:
public static unsafe int GetFunctionTable(IntPtr functionTablePtr);
This function will be called and expected to fill functionTablePtr with pointer to array of structs describing set of callback functions.
In plain C/C++ it looks something like:
// declare func table
// VExampleF1, VExampleF2 - are function pointers
FuncDescStruct funcTable[] = {
"ExampleF1", { VExampleF1, 0, 0, 0, 0, NULL }, //filling descriptions.
"ExampleF2", { VExampleF2, 1, 0, 1, 0, NULL }
};
int GetFunctionTable(FuncDescStruct **ppFunctionTable)
{
*ppFunctionTable = funcTable; // how to do this correctly in C#?
// must return the number of functions in the table
return funcTableSize;
}
I'm trying to do the following:
static unsafe FunctionTag[] funcTable;
static List<IntPtr> allocatedMemory;
public static unsafe int GetFunctionTable(IntPtr functionTablePtr)
{
//create just one test callback description
funcTable = new FunctionTag[1];
funcTable[0].Name = "VExampleF1";
funcTable[0].Description.Function = VExampleF1;
funcTable[0].Description.ArrayQty = 0;
funcTable[0].Description.FloatQty = 0;
funcTable[0].Description.StringQty = 0;
funcTable[0].Description.DefaultQty = 0;
funcTable[0].Description.DefaultValues = null;
// saving allocated memory for further cleanup
allocatedMemory = new List<IntPtr>();
int intPtrSize = Marshal.SizeOf(typeof(IntPtr));
IntPtr nativeArray = Marshal.AllocHGlobal(intPtrSize * funcTable.Length);
for (int i = 0; i < funcTable.Length; i++)
{
IntPtr nativeFD = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FunctionTag)));
allocatedMemory.Add(nativeFD);
Marshal.StructureToPtr(funcTable[i], nativeFD, false);
Marshal.WriteIntPtr(nativeArray, i * intPtrSize, nativeFD);
}
Marshal.WriteIntPtr(functionTablePtr, nativeArray);
return funcTable.Length;
}
Such code doesn't work, and the question is how to send a pointer to array of managed structs for use by unmanaged code? In which direction should I go?
We've been doing quite a lot of this sort of thing over the last couple of years.
We use a mixed-mode 'bridge' assembly to manage mapping function calls and marshalling data between managed and unmanaged environments. We often use a 'mixed-mode' class to wrap a managed class, providing a native interface for calling it's functionality.
Let's consider your problem. You could write GetFunctionTable in managed c++. It can call some managed c# code to get the function information in a managed struct, and then 'marshall' it into the native struct.
In (c#) a managed version of GetFunctionTable function:
delegate void FunctionDelegate();
public struct FuncDescStructManager
{
public string Name;
public FunctionDelegate Function;
//...
}
public static List<FuncDescStructManager> GetFunctionTableManaged()
{
List<FuncDescStructManager> list = new List<FuncDescStructManager>();
list.Add(new FuncDescStructManaged () {"ExampleF1", VExampleF1});
return list;
}
In the mixed-mode bridge assembly you can implement the native function GetFunctionTable,
calling the managed function and marshalling the data:
int GetFunctionTable(FuncDescStruct **ppFunctionTable)
{
// Call the managed function
List<FuncDescStructManaged>^ managedList = GetFunctionTableManaged();
nativeArray = malloc(managedList.Length * sizeof(FuncDescStruct));
int i=0;
foreach (FuncDescStructManaged managedFunc in managedList)
{
// Marshall the managed string to native string could look like this:
stringPtr = Marshal::StringToHGlobalUni(managedFunc.Name);
nativeArray[i].Name = ((wchar_t*)stringPtr.ToPointer());
Marshal::FreeHGlobal(stringPtr);
// Marshall a delegate into a native function pointer using a
// wrapper class:
WrapDelegateAsPtr funcPtr = new WrapDelegateAsPtr(managedFunc.Function);
// funcPtr will need to be deleted by caller
nativeArray[i].Function = funcPtr.NativeFunction;
i++;
}
return i;
}
// Mixed mode wrapper class
// Member is a reference to a managed delegate.
// Because we need to reference this from native code, the wrapped
// delegate will be stored as a void*.
class WrapDelegateAsFPtr
{
public:
WrapDelegateAsNativeFunctionPointer(FunctionDelegate _delegate)
{
delegate = _delegate;
// Tell the garbage handler not to remove the delegate object yet
GCHandle gch = GCHandle::Alloc(svgContents);
managedDelegatePtr = GCHandle::ToIntPtr(gch).ToPointer();
}
~WrapDelegateAsNativeFunctionPointer
{
// Tell the garbage collector we are finished with the managed object
IntPtr temp(managedDelegatePtr;);
GCHandle gch = static_cast<GCHandle>(temp);
gch.Free();
}
void NativeFunction()
{
}
private:
void* managedDelegatePtr;
}
Hope this helps - Any questions just ask!
THis is a rather belatedd answer, but I have come up against exactly the same problem. I have implmented a DATA PLUGIN using Kostya's framework, and got it to work perfectly, then trying to implmement an AFL plugin, I ran into the problem as per above. I have managed to get itworking without using a C++ rapper class.
The problem is with the function pointer/reference/address that is passed from withing the FUnctionTable. AS these functions have been dclared as STATIC for EXPORT purposes, they are incompatible with the C++ delegate implmentation in the GETFUNCTIONTABLE method. IF you ddo the following , it should work:-
Add the 2 signatures:-
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool SetDllDirectory(string lpPathName);
the function reference definition in the GetFunctionTable structure must be changed to:
IntPtr Function;
Add the following statements to get the exported function address:-
IntPtr dllHandle = LoadLibrary(fullPath);
IntPtr fptr = GetProcAddress(dllHandle, "VfmExample1");
and lastly initialize the function variable in the GetFunctionTable structure , e.g.
functable[0].Description.function = fptr;
and that should do it
You have to use the fixing construct to fix the buffer in place, because the C# GC reserves the right to move it around if you don't, invalidating the pointer. I don't know how to fix a buffer indefinitely. You're going to have to worry about how that memory is managed, too.

Categories