✅ Debugging exceptions not caught by try-catch
Hi, I have a small piece of code I am having trouble debugging. I have this class:
And the line I am getting the exception on is
CreateInstanceInternal = Marshal.GetDelegateForFunctionPointer<PfnVkCreateInstance>(createInstanceFp);
. The problem I am having is that the exception that is thrown is both not caught by the catch block and doesn't provide any information as to why it throws the exception. I've even stepped through the IL generated for Marshal.GetDelegateForFunctionPointer<PfnVkCreateInstance>(createInstanceFp);
and it passes through all the exception checks that it has. So I would have thought that the function would have succeeded. I'm at a loss as to what to try next. Any ideas?29 Replies
there's no such thing as an exception that cannot be caught by a try/catch
the exception is not being thrown from
Marshal.GetDelegateForFunctionPointer()
what are you actually observing?The behaviour I am observing is that execution reaches
CreateInstanceInternal = Marshal.GetDelegateForFunctionPointer<PfnVkCreateInstance>(createInstanceFp);
I step into the function just fine and manage to step over all the lines until the very last line in the IL code: return (TDelegate)(object)GetDelegateForFunctionPointerInternal(ptr, t);
. When I try to step over that line I get an EngineExecutionError
that isn't caught by try-catch block.I have tried catching both
Exception
s and EngineExecutionException
s. In fact when I tried catching an EngineExecutionException
specifically Rider warned me that those exceptions aren't even raised by the runtime anymore.And if you continue, will it get to the catch block?
If I try to continue the app stops entirely and reports a fatal internal CLR error:
since I see Vulkan... maybe @ref<'perksey> has some idea
ExecutionEngineException, AccessViolationException, and on non-Windows any exception that isn’t caught until it crosses a native barrier all can’t be caught.
Is the delegate type you’re using marked with the UnmanagedFunctionPointer attribute?
By the way I recommend using Silk.NET (takes care of everything for you), C# 9 function pointers, or basically anything other than the really slow and clunky delegate-based interop functions.
I do, I have
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
and I have also tried [UnmanagedFunctionPointer(CallingConvention.Cdecl]
. I have seen Silk.NET, and as nice as it looks part of this exercise is me wanting to learn how to do performant native interops. Even as wide spread as the .NET community is I'm not always going to be lucky enough that there is a ready made library that does it for me. It's interesting that you mention that delegates are slow and clunky because I actually asked here in a different thread what the most performance way to do native function calls are and the LibraryLoad
attribute was what was suggested to me. Does Silk use C#9 function pointers?yeah silk uses C# 9 function pointers, you have to use them for Vulkan (or the delegate tricks which are awful these days)
it’s basically
((delegate* unmanaged[Cdecl]<VkInstanceCreateInfo*, VkAllocationCallbacks*, VkInstanceT*>)createInstanceFp)(…);
This is the most efficient way to call a native function that is not a simple static export today
LibraryImport is just DllImport but with marshaling done by a source generator instead of the runtime, so it can only be used with simple static exportsI see, thanks for the advice. And just so I'm understanding this correctly. I would be able to use a C# 9 function pointer for a native function pointer returned to me by using
vkGetInstanceProcAddr
? I would like to follow Khronos' guidelines for reducing driver overheadyeah that’s the idea, you could (and probably should) DllImport vkGetInstanceProcAddr as that’s always guaranteed to be exported by the loader and will ensure that you remain AOT compatible, but for the rest of th use function pointers
Silk.NET generates a bunch of instance device pair specific function pointer tables and store them in an object on which you call the functions
Oh cool, so I was part way there with my LibraryLoad of
vkGetInstanceProcAddr
it was just the latter part that was slow and clunky (and broken)
That sounds very similar to how I wanted to set mine upah cool, I’m glad someone else gets it. Most people actually really dislike the API-as-objects thing but we have to do this for correctness and overhead.
I will admit I am more familiar with the raw C api and prefer it to say the C++ api. But I think in the context of C/C++ it makes sense to use the raw API. C# is a managed lanugage and I feel like having a more managed approach to it, even when AOT'd, makes sense
I do also plan to have a
Raw
namepsace which will be just the C api with a C# front cover. So in the event that I want to use the raw API I can, and internall the managed API will use the Raw
function callsyeah that’s the thing, like yeah the C api is all static but that’s because it’s doing a bunch of state-based trampolines under-the-hood within the loader
oh we still expose the raw API exclusively, we just don’t expose them as static functions given that the function pointers are instance and device dependent
Right, because
vkGetInstanceProcAddr/vkGetDeviceProcAddr
require a valid Instance or Device. C get's away with it because it does the trampolining, but if I wanted to do that in C# every function would have to be LibraryLoad
ed which would be a performance nightmare. Okay maybe I don't do that then 😂any other questions just shout, I’ve spent three years of my life bikeshedding stuff like this 😅
Thanks, I'll keep that in mind. Any particular location to shout you from?
Sorry for the ping, but I just wanted to check my use of C# function pointers. Would this be how you expect them to be used?
Assuming that that's correct, I would also typedef the function pointer for better readability
oh why'd you have to use
IntPtr
😭
but no that's not correct, and this is possibly why your delegate wasn't working either. the signatures need to be fully blittableIs there something wrong with IntPtr? I thought that was how C# represented pointers and IntPtr.Zero is the equivalent of null?
C# represents pointers using pointers
uint*
ExtensionProperties*
this particular function would be
I see, I have been lied to by the internet. Colour me shocked 🤣
the internet is full of a ridiculous heap of misinformation about C# and its native interoperability
and that's because it's had so many forms over the years
e.g. there was a time where making a C++/CLI shim was standard practice
and a time when CLS compliance mattered, that's where the IntPtr to represent pointers came from
I see, that does complicate things. In my own research I find that there's either a lot of research on a topic, usually pre-2012. Or 1 page on MS's docs site that doesn't really give me enough information for me to understand how it works
more reason to rely on a library that has already spent years figuring this all out haha
True, true. But like I said, I'd rather learn this myself so I can apply it to other libraries. My main ulterior motive right now are some internal libraries at work. We're forced to use C/C++ because they are C libraries. But I personally have fallen out of favour with C++ and want to move to .NET now it has AOT support. I picked Vulkan as a practice library because it's a library I have native familiarity with and because it had the added complexity of needing to load function pointers manually to skip trampolining. I figured if I can get some semi-complex demo running using Vulkan I can map our internal libraries to .NET and use a more palletable language (in my opinion)
Got that function working. At least for evalutating how many layers there are. Now to make it return the actual data. Thanks again
np. here's some old generated code from silk in case you get stuck
oh wait that's the crappy .NET Standard version, one sec
this should be the good one
Awesome, thanks.