What are the implications or unperceived consequences of throwing an exception inside of a delegate that is used during an unmanaged callback? Here is my situation:
Unmanaged C:
int return_callback_val(int (*callback)(void))
{
return callback();
}
Managed C#:
[DllImport("MyDll.dll")]
static extern int return_callback_val(IntPtr callback);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int CallbackDelegate();
int Callback()
{
throw new Exception();
}
void Main()
{
CallbackDelegate delegate = new CallbackDelegate(Callback);
IntPtr callback = Marshal.GetFunctionPointerForDelegate(delegate);
int returnedVal = return_callback_val(callback);
}
The native code will bomb on the unhandled exception and the program terminates.
If you actually want to handle that exception then you need to use the custom __try/__catch keywords in the native code. Which is pretty useless, all the details of the managed exception are lost. The only distinguishing characteristic is the exception code, 0xe0434f4d. Since you cannot know exactly what went wrong, you cannot reliably restore the program state either. Better not catch it. Or better not throw it.
I think the correct way to say to a COM Object you have an exception is simply to return HRESULT.E_FAIL.
Could not test it right now, but I think that if the COM Object is in another process, all you will do is to kill your process and the COM Object may become unresponsive, waiting for your code to return from the callback function (since your process is dead already).
Related
Not sure why but at random when calling the function InternetQueryDataAvailable a null ref exception occurs for no apparent reason sins non of the arguments which it accepts can be null:
[DllImport(Dlls.Wininet, SetLastError = true)]
public static extern bool InternetQueryDataAvailable([In] IntPtr hFile, [Out] out int numberOfBytesAvailable, [Optional, In] int reserved0, [Optional, In] IntPtr reserved1);
Here is the exception:
And no, CheckHandle() isn't the culprit as all it does is check if _handle is zero or not, aka invalid.
Also, if not this then after downloading all the data and attempting to close the application, sins I set it up so all the handles close before the app closes, the call to InternetCloseHandle throws the null ref exception even tho, just like with InternetQueryDataAvailable, non of the arguments are nullable sins all it accepts is a single IntPtr:
[DllImport(Dlls.Wininet, SetLastError = true)]
public static extern bool InternetCloseHandle([In] IntPtr hInternet);
Am not sure what's going on because, on rare occasions everything works fine and I am able to download all the data and close the handle without a random exception being thrown.
For those wondering what the function with InternetCloseHandle looks like, it's just:
public void Dispose()
{
if (_handle != IntPtr.Zero)
{
if (!WinINet.InternetCloseHandle(_handle))
{
throw new Win32Exception();
}
_handle = IntPtr.Zero;
}
}
Note that the exception which is thrown after calling InternetQueryDataAvailable only occurs after the first call, so the first is fine but all the ones after have a chance of triggering the exception.
A thing I didn't point out, which was the cause of this null ref exception, was that when InternetOpen is performed, I instantly assign the handle to a status callback via InternetSetStatusCallback by just doing InternetSetStatusCallback(handle, CallbackMethod);, apparently what was happening was that the GC collected the delegate because it supposedly wasn't being used, note that when InternetQueryDataAvailable is called the statuses INTERNET_STATUS_RECEIVING_RESPONSE and INTERNET_STATUS_RESPONSE_RECEIVED do get passed to the callback, so am not sure why the GC collected the delegate but, that was the cause of the null ref. It wasn't that the InternetQueryDataAvailable was throwing the exception, as some suspected.
The solution for this was to create a field to which the delegate is assigned, like that the GC wont collect it due to the fact that the class housing it is always alive and is never out of scope.
Note that the exception which is thrown after calling InternetQueryDataAvailable only occurs after the first call, so the first is fine but all the ones after have a chance of triggering the exception.
This is to be expected. It is a data retreival. Those can always run into Exogenous Exceptions. It does not mater if that function wraps around a file access or a network operation going over half the planet - both of those scenarios got exogenous exception potential in spades.
Of course the specific exception is still odd.
I found out about the error because I saw it in the windows built-in event viewer:
Description: The process was terminated due to an unhandled exception.
Exception Info: System.MissingMethodException
Stack:
at Injection.Main.DrawText_Hooked(...)
I have a c# application using easyhook. My dll critical code:
public void Run(RemoteHooking.IContext InContext, String InChannelName)
{
// Install system hook to detect calls to DrawTextExW that is made by the client and call the function DrawText_Hooked when ever this happens
try
{
DrawTextExHook = LocalHook.Create(LocalHook.GetProcAddress("user32.dll", "DrawTextExW"), new DDrawTextEx(DrawText_Hooked), this);
DrawTextExHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
}....
And my delegate to handle the hooked function is:
int DrawText_Hooked(...)
{
Interface.Read(hdc, lpString, cchText, dwDTFormat);
return DrawTextExW(hdc, lpString, cchText, ref lprc, dwDTFormat, ref dparams);
}
When I shut down my main application everything works fine unless I use Interface.Read(...): in this case, the hooked application crashes. I've read it's probably because Interface.Read(...) doesn't exist anymore once I exit my app but I don't know how to tell my dll to stop doing that or simply unload so that it doesn't try to do Interface.Read(...) and finds out it doesn't actually exist anymore. How shall I do it?
Two days looking for the answer and after posting it I discover it myself after 10':
What I did was to declare the hook static:
static LocalHook DrawTextExHook;
So from my main code, on exit, I could call a static method that points it to null, therefore stopping calling my Interface.Read(...).
public static void stopIt()
{
DrawTextExHook = null;
}
I am leveraging a C++ library via C# interop which has a simple API to perform some tasks in a background thread. The catch is that the C++ function which performs the background task takes a null pointer, which is then 'filled in' while the background task is performed. Also, the threading is done on the C# side, so I am having to use the 'ParameterizedThreadStart' delegate type, which only takes an object as a parameter. I then need to access that object from my C# code. Hopefully the following snippets can illustrate what I mean.
C++:
extern "C" void __declspec(dllexport) performLongTask(MyClass** myObject)
{
myObject = new MyClass;
// Time-consuming operation...
}
C#:
[DllImport("myDllName")]
public static extern void performLongTask(ref IntPtr myObject);
// Ugly, but necessary for ParameterizedThreadStart delegate?
unsafe class PtrWrapper
{
public IntPtr Pointer = new IntPtr();
}
public static unsafe performLongTask_paramThreadStartWrapper(object arg)
{
PtrWrapper ptrWrapper = (PtrWrapper)arg;
performLongTask(ref ptrWrapper.Pointer);
}
public static unsafe void DoInteropStuff()
{
PtrWrapper ptrWrapper = new PtrWrapper();
Thread bgThread = new Thread(new ParameterizedThreadStart(performLongTask_paramThreadStartWrapper));
bgThread.Start(ptrWrapper);
// Wait a little while, let background thread processing go....
// Use pointer to retrieve some stuff from other unmanaged code
if(ptrWrapper.Pointer != IntPtr.Zero)
object currentData = getData(ptrWrapper.Pointer);
}
The result of this is that the IntPtr I create on the managed side never gets changed, despite the fact that the unmanaged code seems to execute fine (debug print statements show that things are going as expected, and that the pointer has certainly been set on the unmanaged side).
I know it's not the prettiest code, and there are probably several ways to do this whole thing cleaner. I'm interested in several things:
1.) How to accomplish this the best way using the C++ library as is (no modifications to unmanaged code).
2.) Why doesn't this code work? As ugly as it is, it seems to me that it should work as I think that pointers are being passed around correctly...
Thanks for your help!
I've a delegate to an unmanaged function on a DLL (which I loaded using GetProcAddress). I can call this delegate with no trouble. When I call the delegates with BeginInvoke, however, I get the following exception:
Managed Debugging Assistant 'FatalExecutionEngineError' has detected a problem in '...'.
Additional Information: The runtime has encountered a fatal error. The address of the
error was at 0x63fd8687, on thread 0xb4c. The error code is 0xc0000005. This error may
be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common
sources of this bug include user marshaling errors for COM-interop or PInvoke, which
may corrupt the stack.
This is the code:
private void OnConnectFinished(IAsyncResult a_oResult)
{
ConnectDelegate l_oDelegate = (ConnectDelegate)a_oResult.AsyncState;
if (ConnectFinished != null)
{
ConnectFinished(l_oDelegate.EndInvoke(a_oResult));
}
}
public bool Connect()
{
AsyncCallback l_oCallback = new AsyncCallback(OnConnectFinished);
IAsyncResult l_oResult = DLLConnect.BeginInvoke(l_oCallback, DLLConnect);
//This Works!:
//bool l_bResult = DLLConnect(m_oConnectFinishedDelegate);
//return l_bResult;
return true;
}
Any ideas on why is this happening?
Passing the delegate as userState (the second argument in BeginInvoke) seems suspicious. Will it work if you pass null there? (Yes, I know that your callback won't work, but this issue can be handled another way. Let's check if the reported error disappears.)
i am trying to programming an interface for Walther Mfs100 Check scanner but after the scan i get "CallbackOnCollectedDelegate was detected" error. How can i fix this. I use .net 2.0 with c#
[DllImport("mflib.dll.stdcall")]
public static extern int mfScanFeeder(int mode, int font, int timeout);
retval = modMFS100.mfScanFeeder(0, 2,5000);
It isn't this particular API call that's the source of the problem. The API is too obscure and too poorly documented to give a straight answer, but look for an initialization style function that let's you set a callback. That callback is the cause of the exception. You must create a delegate object and store it in a field of your class. That way the garbage collector sees a reference to it and won't garbage collect it.
Thus, instead of:
void SetupScanner() {
mfInitialize(something, myCallback);
}
Do it like this:
SomeDelegateType callback;
void SetupScanner() {
callback = new SomeDelegateType(myCallback);
mfInitialize(something, callback);
}
Contact the vendor for support if this doesn't help.
Here is some info about this error:
http://social.msdn.microsoft.com/forums/en-US/Vsexpressvb/thread/665b876d-9070-41de-9a3a-d2093895d0c2
You can try to use static variable for your delegate, this will prevent the delegate from being garbage collected.