So I have a native 3rd party C++ code base I am working with (.lib and .hpp files) that I used to build a wrapper in C++/CLI for eventual use in C#.
I've run into a particular problem when switching from Debug to Release mode, in that I get an Access Violation Exception when a callback's code returns.
The code from the original hpp files for callback function format:
typedef int (*CallbackFunction) (void *inst, const void *data);
Code from the C++/CLI Wrapper for callback function format:
(I'll explain why I declared two in a moment)
public delegate int ManagedCallbackFunction (IntPtr oInst, const IntPtr oData);
public delegate int UnManagedCallbackFunction (void* inst, const void* data);
--Quickly, the reason I declared a second "UnManagedCallbackFunction" is that I tried to create an "intermediary" callback in the wrapper, so the chain changed from Native C++ > C# to a version of Native C++ > C++/CLI Wrapper > C#...Full disclosure, the problem still lives, it's just been pushed to the C++/CLI Wrapper now on the same line (the return).
And finally, the crashing code from C#:
public static int hReceiveLogEvent(IntPtr pInstance, IntPtr pData)
{
Console.WriteLine("in hReceiveLogEvent...");
Console.WriteLine("pInstance: {0}", pInstance);
Console.WriteLine("pData: {0}", pData);
// provide object context for static member function
helloworld hw = (helloworld)GCHandle.FromIntPtr(pInstance).Target;
if (hw == null || pData == null)
{
Console.WriteLine("hReceiveLogEvent: received null instance pointer or null data\n");
return 0;
}
// typecast data to DataLogger object ptr
IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData)));
DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target;
//Do Logging Stuff
Console.WriteLine("exiting hReceiveLogEvent...");
Console.WriteLine("pInstance: {0}", pInstance);
Console.WriteLine("pData: {0}", pData);
Console.WriteLine("Setting pData to zero...");
pData = IntPtr.Zero;
pInstance = IntPtr.Zero;
Console.WriteLine("pData: {0}", pData);
Console.WriteLine("pInstance: {0}", pInstance);
return 1;
}
All writes to the console are done and then we see the dreaded crash on the return:
Unhandled exception at 0x04d1004c in
helloworld.exe: 0xC0000005: Access
violation reading location 0x04d1004c.
If I step into the debugger from here, all I see is that the last entry on the call stack is: > "04d1004c()" which evaluates to a decimal value of: 80805964
Which is only interesting if you look at the console which shows:
entering registerDataLogger
pointer to callback handle: 790848
fp for callback: 2631370
pointer to inst: 790844
in hReceiveLogEvent...
pInstance: 790844
pData: 80805964
exiting hReceiveLogEvent...
pInstance: 790844
pData: 80805964
Setting pData to zero...
pData: 0
pInstance: 0
Now, I know that between debug and release some things are quite different in the Microsoft world. I am, of course worried about byte padding and initialization of variables, so if there is something I am not providing here, just let me know and I'll add to the (already long) post. I also think the managed code may NOT be releasing all ownership and then the native C++ stuff (which I don't have the code for) may be trying to delete or kill off the pData object, thus crashing the app.
More full disclosure, it all works fine (seemingly) in Debug mode!
A real head scratch issue that would appreciate any help!
I think the stack got crushed because of mismatching calling conventions:
try out to put the attribute
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
on the callback delegate declaration.
This doesn't directly answer your question, but it may lead you in the right direction as far as debug mode okay vs. release mode not okay:
Since the debugger adds a lot of record-keeping information to the stack, generally padding out the size and layout of my program in memory, I was “getting lucky” in debug mode by scribbling over 912 bytes of memory that weren’t very important. Without the debugger, though, I was scribbling on top of rather important things, eventually walking outside of my own memory space, causing Interop to delete memory it didn’t own.
What is the definition of DataLoggerWrap? A char field may be too small for the data you are receiving.
I'm not sure what your are trying to achieve.
A few points:
1) The garbage collector is more aggressive in release mode so with bad ownership the behaviour you describe is not uncommon.
2) I don't understands what the below code is trying to do?
IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData)));
DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target;
You use GCHandle.Alloc to lock an instance of DataLoggerWrap in memory, but then you never pass it out to unmanaged - so why do you lock it?
You also never free it?
The second line then grabs back a reference - why the circular path? why the reference - you never use it?
3) You set the IntPtrs to null - why? - this will have no effect outside of the function scope.
4) You need to know what the contract of the callback is. Who owns pData the callback or the calling function?
I'm with #jdehaan, except CallingConvetion.StdCall could be the answer, especially when the 3rd party lib is written in BC++, for example.
Related
I have a C++ dll which is reading video frames from a camera. These frames get allocated in the DLL returned via pointer to the caller (a C# program).
When C# is done with a particular frame of video, it needs to clean it up. The DLL interface and memory management is wrapped in a disposable class in C# so its easier to control things. However, it seems like the memory doesn't get freed/released. The memory footprint of my process grows and grows and in less than a minute, I get allocation errors in the C++ DLL as there isn't any memory left.
The video frames are a bit over 9 MB each. There is a lot of code, so I'll simply provide the allocation/deallocations/types/etc.
First : Allocation in C++ of raw buffer for the camera bytes.
dst = new unsigned char[mFrameLengthInBytes];
Second : transfer from the raw pointer back to across the DLL boundary as an unsigned char * and into an IntPtr in C#
IntPtr pFrame = VideoSource_GetFrame(mCamera, ImageFormat.BAYER);
return new VideoFrame(pFrame, .... );
So now the IntPtr is passed into the CTOR of the VideoFrame class. Inside the CTOR the IntPtr is copied to an internal member of the class as follows :
IntPtr dataPtr;
public VideoFrame(IntPtr pDataToCopy, ...)
{
...
this.dataPtr = pDataToCopy;
}
My understanding is that is a shallow copy and the class now references the original data buffer. The frame is used/processed/etc. Later, the VideoFrame class is disposed and the following is used to clean up the memory.
Marshal.FreeHGlobal(this.dataPtr);
I suspect the problem is that... dataPtr is an IntPtr and C# has no way to know that the underlying buffer is actually 9 MB, right? Is there a way to tell it how much memory to release at that point? Am I using the wrong C# free method? Is there one specifically for this sort of situation?
You need to call the corresponding "free" method in the library you're using.
Memory allocated via new is part of the C++ runtime, and calling FreeHGlobal won't work. You need to call (one way or the other) delete[] against the memory.
If this is your own library then create a function (eg VideoSource_FreeFrame) that deletes the memory. Eg:
void VideoSource_FreeFrame(unsigned char *buffer)
{
delete[] buffer;
}
And then call this from C#, passing in the IntPtr you got back.
You need to (in c++) delete dst;. That means you need to provide an API that the C# code can call, like FreeFrame(...), which does exactly that.
I agree with the first answer. Do NOT free it in C# code, using any magical, liturgical incantations. Write a method in C++ that free's the memory, and call it from your C# code. Do NOT get into the habit of allocationg memory in one heap (native) and freeing it another heap (managed), that's just bad news.
Remember one of the rules from the book effective C++: Allocate memory in the constructor, and deallocate in the destructor. And if you can't do it in the destructor, do it in an in-class method, not some global (or even worse) friend function.
I have code in C++, which I will call from C# via interop mechanism.
In my C++ code, this is my class structure
extern "C" struct __declspec(dllexport) LegList
{
SimpleList simple_List;
int* inputList;
};
This is my C# code:
[StructLayout(LayoutKind.Sequential)]
public struct LegList
{
SimpleList simple_List;
public IntPtr inputList;
}
The inputList is not used anywhere in C#.
My questions are:
In C++ code, should I set inputList to NULL?
If I set it to NULL, how should I best set it to avoid bugs? Should I use inputList=0, or inputList=NULL, or inputList=(int*)malloc(0)?
If I don't set it, or if I set it wrongly, what are the consequences? Will some very hard to diagnose bug appears? What I am afraid is that if I don't set it correctly, some machines might run the code fine, others might not. And when the program stops working, it will stop at a point much later, resulting in a very hard to diagnose problem for us, such as AccessViolationException.
All in all, if I set it to inputList=(int*)malloc(0), or if I don't set it at all, will the program crash at a different place at a later time, in a very unpredictable and inconsistent manner, depending on which machine the code is running on?
Will assigning a pointer to (int*)malloc(0) causes unpredictable crash? How should I properly set an unused pointer?
Simply set it to NULL or, if your compiler supports it, to nullptr. This makes it clear that it doesn't point to anything useful.
So either inputList=NULL or inputList=nullptr.
Obviously, not setting it at all, is the worst thing to do. If there is any code that uses it, it will behave unpredictably.
I'm not sure, why one would want to do inputList=(int*)malloc(0).
C#, VS 2010
I need to determine if a float value is NaN.
Testing a float for NaN using
float.IsNaN(aFloatNumber)
crashes with a stack overflow.
So does
aFloatNumber.CompareTo(float.NaN).
The following does not crash, but it's not useful as it returns NaN regardless:
aFloatNumber - float.NaN
A search for "stack overflow" returns results about this website instead of results about an actual stack overflow, so I can't find relevant answers.
Why is my application going into a stack overflow when testing for NaN?
Edit: the call stack:
Edit: it's clearly something in my code: this statement:
bool aaa = float.IsNaN(float.NaN);
works OK in the constructor of the application, right after InitializeComponent();
works OK in the constructor of theclass for a custom control, right after InitializeComponent();
but crashes in an event handler inside the class for a custom control.
So, this is what I am doing:
Abstract Custom control: public abstract partial class ConfigNumberBaseBox : TextBox
has a Validating event handler ValidateTextBoxEntry
ValidateTextBoxEntry is defined inside the ConfigNumberBaseBox class
Custom control that inherits from ConfigNumberBaseBox : public partial class ConfigTemperBox : ConfigNumberBaseBox
Run the app
When I finish editing a ConfigTemperBox control, ValidateTextBoxEntry is called
ValidateTextBoxEntry runs fine until it encounters float.IsNaN
stack overflow
Edit:
Debug.WriteLine() shows that the code is executed only once: no recursion.
Edit:
This works:
float fff = 0F;
int iii = fff.CompareTo(float.PositiveInfinity);
This crashes:
float fff = 0F;
int iii = fff.CompareTo(float.NaN);
works OK in the constructor of theclass for a custom control
This is the only real hint towards the underlying problem. Code that runs on a thread can manipulate two stacks inside the processor. One is the normal one that everybody knows about and gave this web site its name. There is however another one, well hidden inside the FPU (Floating Point Unit). It stores intermediate operand values while making floating point calculations. It is 8 levels deep.
Any kind of mishap inside the FPU is not supposed to generate runtime exceptions. The CLR assumes that the FPU is configured with its defaults for the FPU control word, the hardware exceptions it can generate are supposed to be disabled.
That does have a knack for going wrong when your program uses code that came from the 1990s, back when enabling FPU exceptions still sounded like a good idea. Code generated by Borland tooling are notorious for doing this for example. Its C runtime module reprograms the FPU control word and unmasks the hardware exceptions. The kind of exceptions you can get for that can be very mysterious, using NaN in your code is a good way to trigger such an exception.
This should be at least partially visible with the debugger. Set a breakpoint on the "still good" code and use the Debug + Windows + Registers debugger window. Right-click it and select "Floating point". You'll see all of the registers that are involved with floating point calculations, ST0 through ST7 are the stack registers for example. The important one here is marked CTRL, its normal value in a .NET process is 027F. The last 6 bits in that value are the exception masking bits (0x3F), all turned on to prevent hardware exceptions.
Single step through the code and the expectation is that you see the CTRL value change. As soon as it does then you'll have found the evil code. If you enable unmanaged debugging then you should also see the load notification in the Output window and see it appear in the Debug + Windows + Module window.
Undoing the damage that the DLL did is fairly awkward. You'd have to pinvoke _control87() in msvcrt.dll for example to restore the CTRL word. Or a simple trick that you can use, you can intentionally throw an exception. The exception handling logic inside the CLR resets the FPU control word. So with some luck, this kind of code is going to solve your problem:
InitializeComponent();
try { throw new Exception("Please ignore, resetting FPU"); }
catch {}
You may have to move it, next best guess is the Load event. The debugger should tell you where.
I just wrote an example to reproduce the error:
1. Create a native C/C++ DLL which exports this function:
extern "C" __declspec(dllexport) int SetfloatingControlWord(void)
{
//unmask all the floating excetpions
int err = _controlfp_s(NULL, 0, _MCW_EM);
return err;
}
2. Create a C# console program, which call the function SetfloatingControlWord, after that, do some floating operation such as NaN compare, then it leads to stack overflow.
[DllImport("floatcontrol.dll")]
public static extern Int32 SetfloatingControlWord();
static void Main(string[] args)
{
int n = SetfloatingControlWord();
float fff = 0F;
int iii = fff.CompareTo(float.NaN);
}
I encountered the same problem years ago, also, I noticed that after an .NET exception throws, everything works fine, it took me a while to figure out why and trace the code which changed the FPU.
As the doc of function _controlfp_s says: By default, the run-time libraries mask all floating-point exceptions. The common language runtime (CLR) only supports the default floating-point precision, so CLR doesn't handle these kind exceptions.
As MSDN says:By default, the system has all FP exceptions turned off. Therefore, computations result in NAN or INFINITY, rather than an exception.
After NaN was introduced in IEEE 754 1985, it suppose that application software no longer need to handle the floating point exceptions.
The solution:
First of all, thank you to #Matt for pointing me in the right direction, and #Hans Passant for providing the workaround.
The application talks to a CAN-USB adapter from Chinese manufacturer QM_CAN.
The problem is in their driver.
The DLL statements and Driver import:
// DLL Statement
IntPtr QM_DLL;
TYPE_Init_can Init_can;
TYPE_Quit_can Quit_can;
TYPE_Can_send Can_send;
TYPE_Can_receive Can_receive;
delegate int TYPE_Init_can(byte com_NUM, byte Model, int CanBaudRate, byte SET_ID_TYPE, byte FILTER_MODE, byte[] RXF, byte[] RXM);
delegate int TYPE_Quit_can();
delegate int TYPE_Can_send(byte[] IDbuff, byte[] Databuff, byte FreamType, byte Bytes);
delegate int TYPE_Can_receive(byte[] IDbuff, byte[] Databuff, byte[] FreamType, byte[] Bytes);
// Driver
[DllImport("kernel32.dll")]
static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll")]
static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
The call to the offending code, including Hans' workaround:
private void InitCanUsbDLL() // Initiate the driver for the CAN-USB dongle
{
// Here is an example of dynamically loaded DLL functions
QM_DLL = LoadLibrary("QM_USB.dll");
if (QM_DLL != IntPtr.Zero)
{
IntPtr P_Init_can = GetProcAddress(QM_DLL, "Init_can");
IntPtr P_Quit_can = GetProcAddress(QM_DLL, "Quit_can");
IntPtr P_Can_send = GetProcAddress(QM_DLL, "Can_send");
IntPtr P_Can_receive = GetProcAddress(QM_DLL, "Can_receive");
// The next line results in a FPU stack overflow if float.NaN is called by a handler
Init_can = (TYPE_Init_can)Marshal.GetDelegateForFunctionPointer(P_Init_can, typeof(TYPE_Init_can));
// Reset the FPU, otherwise we get a stack overflow when we work with float.NaN within a event handler
// Thanks to Matt for pointing me in the right direction and to Hans Passant for this workaround:
// http://stackoverflow.com/questions/25205112/testing-for-a-float-nan-results-in-a-stack-overflow/25206025
try { throw new Exception("Please ignore, resetting FPU"); }
catch { }
Quit_can = (TYPE_Quit_can)Marshal.GetDelegateForFunctionPointer(P_Quit_can, typeof(TYPE_Quit_can));
Can_send = (TYPE_Can_send)Marshal.GetDelegateForFunctionPointer(P_Can_send, typeof(TYPE_Can_send));
Can_receive = (TYPE_Can_receive)Marshal.GetDelegateForFunctionPointer(P_Can_receive, typeof(TYPE_Can_receive));
}
}
The reason that the application crashed when a reference was made to float.NaN in the event handler and not in the constructor was a simple matter of timing: the constructor is called before InitCanUsbDLL(), but the event handler was called long after InitCanUsbDLL() corrupted the FPU registers.
I would like to calculate how many bytes my function fills so that I can inject it into another process using CreateRemoteThread(). Once I know the number of bytes, I can write them into the remote process using the function's pointer. I have found an article online (see http://www.codeproject.com/KB/threads/winspy.aspx#section_3, chapter III) where they do the following in C++ :
// ThreadFunc
// Notice: - the code being injected;
//Return value: password length
static DWORD WINAPI ThreadFunc (INJDATA *pData)
{
//Code to be executed remotely
}
// This function marks the memory address after ThreadFunc.
static void AfterThreadFunc (void) {
}
Then they calculate the number of bytes ThreadFunc fills using :
const int cbCodeSize = ((LPBYTE) AfterThreadFunc - (LPBYTE) ThreadFunc);
Using cbCodeSize they allocate memory in the remote process for the injected ThreadFunc and write a copy of ThreadFunc to the allocated memory:
pCodeRemote = (PDWORD) VirtualAllocEx( hProcess, 0, cbCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if (pCodeRemote == NULL)
__leave;
WriteProcessMemory( hProcess, pCodeRemote, &ThreadFunc, cbCodeSize, &dwNumBytesXferred );
I would like to do this in C#. :)
I have tried creating delegates, getting their pointers, and subtracting them like this:
// Thread proc, to be used with Create*Thread
public delegate int ThreadProc(InjectionData param);
//Function pointer
ThreadFuncDeleg = new ThreadProc(ThreadFunc);
ThreadFuncPtr = Marshal.GetFunctionPointerForDelegate(ThreadFuncDeleg);
//FunctionPointer
AfterThreadFuncDeleg = new ThreadProc(AfterThreadFunc);
IntPtr AfterThreadFuncDelegPtr= Marshal.GetFunctionPointerForDelegate(AfterThreadFuncDeleg);
//Number of bytes
int cbCodeSize = (AfterThreadFuncDelegPtr.ToInt32() - ThreadFuncPtr.ToInt32())*4 ;
It just does not seem right, as I get a static number no matter what I do with the code.
My question is, if possible, how does one calculate the number of bytes a function's code fills in C#?
Thank you in advance.
I don't think it is possible due dynamic optimization and code generation in .NET. You can try to measure IL-code length but when you try to measure machine-depended code length in general case it will fail.
By 'fail' I mean you can't get correct size that provide any meaning by using this technique dynamically.
Of course you can go with finding how NGEN, JIT compile works, pdb structure and try to measure. You can determine size of your code by exploring generated machine code in VS for example.
How to see the Assembly code generated by the JIT using Visual Studio
If you really need to determine size, start with NET Internals and Code Injection / NET Internals and Native Compiling but I can't imagine why you ever want it.
Be aware all internals about how JIT works exactly is subject to change so depending solution can be broken by any future version of .NET.
If you want to stick with IL: check Profiling Interfaces (CLR Profiling API), and a bit old articles: Rewrite MSIL Code on the Fly with the .NET Framework Profiling API and No Code Can Hide from the Profiling API in the .NET Framework 2.0. There are also some topics about CLR Profiling API here on SO.
But simplest way to explore assembly is Reflection API, you want MethodBody there. So you can check Length of MethodBody.GetILAsByteArray and you'll find method length in IL-commands.
I'm trying to use an unmanaged C dll for loading image data into a C# application. The library has a fairly simple interface where you pass in a struct that contains three callbacks, one to receive the size of the image, one that receives each row of the pixels and finally one called when the load is completed. Like this (C# managed definition):
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct st_ImageProtocol
{
public st_ImageProtocol_done Done;
public st_ImageProtocol_setSize SetSize;
public st_ImageProtocol_sendLine SendLine;
}
The types starting st_ImageProtocol are delgates:
public delegate int st_ImageProtocol_sendLine(System.IntPtr localData, int rowNumber, System.IntPtr pixelData);
With the test file that I'm using the SetSize should get called once, then the SendLine will get called 200 times (once for each row of pixels in the image), finally the Done callback gets triggered. What actually happens is that the SendLine is called 19 times and then a AccessViolationException is thrown claiming that the library tried to access protected memory.
I have access to the code of the C library (though I can't change the functionality) and during the loop where it calls the SendLine method it does not allocate or free any new memory, so my assumption is that the delegate itself is the issue and I need to pin it before I pass it in (I have no code inside the delegate itself currently, besides a counter to see how often it gets called, so I doubt I'm breaking anything on the managed side). The problem is that I don't know how to do this; the method I've been using to declare the structs in unmanaged space doesn't work with delegates (Marshal.AllocHGlobal()) and I can't find any other suitable method. The delegates themselves are static fields in the Program class so they shouldn't be being garbage collected, but I guess the runtime could be moving them.
This blog entry by Chris Brumme says that delegates don't need to be pinned before being passed into unmanaged code:
Clearly the unmanaged function pointer must refer to a fixed address. It would be a disaster if the GC were relocating that! This leads many applications to create a pinning handle for the delegate. This is completely unnecessary. The unmanaged function pointer actually refers to a native code stub that we dynamically generate to perform the transition & marshaling. This stub exists in fixed memory outside of the GC heap.
But I don't know if this holds true when the delegate is part of a struct. It does imply that it is possible to manually pin them though, and I'm interested in how to do this or any better suggestions as to why a loop would run 19 times then suddenly fail.
Thanks.
Edited to answer Johan's questions...
The code that allocates the struct is as follows:
_sendLineFunc = new st_ImageProtocol_sendLine(protocolSendLineStub);
_imageProtocol = new st_ImageProtocol()
{
//Set some other properties...
SendLine = _sendLineFunc
};
int protocolSize = Marshal.SizeOf(_imageProtocol);
_imageProtocolPtr = Marshal.AllocHGlobal(protocolSize);
Marshal.StructureToPtr(_imageProtocol, _imageProtocolPtr, true);
Where the _sendLineFunc and the _imageProtocol variables are both static fields of the Program class. If I understand the internals of this correctly, that means that I'm passing an unmanaged pointer to a copy of the _imageProtocol variable into the C library, but that copy contains a reference to the static _sendLineFunc. This should mean that the copy isn't touched by the GC - since it is unmanaged - and the delegate won't be collected since it is still in scope (static).
The struct actually gets passed to the library as a return value from another callback, but as a pointer:
private static IntPtr beginCallback(IntPtr localData, en_ImageType imageType)
{
return _imageProtocolPtr;
}
Basically there is another struct type that holds the image filename and the function pointer to this callback, the library figures out what type of image is stored in the file and uses this callback to request the correct protocol struct for the given type. My filename struct is declared and managed in the same way as the protocol one above, so probably contains the same mistakes, but since this delegate is only called once and called quickly I haven't had any problems with it yet.
Edited to update
Thanks to everybody for their responses, but after spending another couple of days on the problem and making no progress I decided to shelve it. In case anyone is interested I was attempting write a tool for users of the Lightwave 3D rendering application and a nice feature would have been the ability to view all the different image formats that Lightwave supports (some of which are fairly exotic). I thought that the best way to do this would be to write a C# wrapper for the plugin architecture that Lightwave uses for image manipulation so I could use their code to actually load the files. Unfortunately after trying a number of the plugins against my solution I had a variety of errors that I couldn't understand or fix and my guess is that Lightwave doesn't call the methods on the plugins in a standard way, probably to improve the security of running external code (wild stab in the dark, I admit). For the time being I'm going to drop the image feature and if I do decide to reinstate it I'll approach it in a different way.
Thanks again, I learnt a lot through this process even though I didn't get the result I wanted.
I had a similar problem when registering a callback delegate (it would be called, then poof!). My problem was that the object with the method being delegated was getting GC'ed. I created the object in a more global place so as to keep it from being GC'ed.
If something like that doesn't work, here are some other things to look at:
As additional info, take a look at GetFunctionPointerForDelegate from the Marshal class. That is another way you could do this. Just make sure that the delegates are not GC'ed. Then, instead of delegates in your struct, declare them as IntPtr.
That may not solve the pinning, but take a look at fixed keyword, even though that may not work for you since you are dealing with a longer lifetime than for what that is typically used.
Finally, look at stackalloc for creating non-GC memory. These methods will require the use of unsafe, and might therefore put some other constraints on your Assemblies.
It would be interesting to know a little more:
How do you create the ImageProtocol struct? Is it a local variable or a class member or do you allocate it in unmanaged memory with Marshal.AllocHGlobal?
How is it sent to the C function? Directly as stack variable or as a pointer?
A really tricky problem! It feels like the delegate data is moved around by the GC which causes the access violation. The interesting thing is that the delegate data type is a reference data type, which stores its data on the GC heap. This data contains things like the address of the function to call (function pointer) but also a reference to the object that contains the function. This should mean that even though the actual function code is stored outside of the GC heap, the data that holds the function pointer is stored in the GC heap and can hence be moved by the GC. I thought about the problem a lot last night but haven't come up with a solution....
You don't say exactly how the callback is declared in the C library. Unless it is explictly declared __stdcall you'll slowly corrupt your stack. You'll see your method get called (probably with the parameters reversed) but at some point in the future the program will crash.
So far as I know there is no way around that, other than writing another callback function in C that sits between the C# code and the library that wants a __cdecl callback.
If the c function is a __cdecl function then you have to use the Attribut
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
before the delegate declaration.