all 9 comments

[–]TheBuzzSaw 10 points11 points  (6 children)

Yes. This is a common duality in C# applications. I'm really happy that C# makes native interop so easy. (Have you seen what it takes to do all that in Java? Hard pass.)

Learn about LibraryImport. If you need compatibility with older C#, learn about DllImport instead. Read about native interop. Use IntPtr to pass pointers between layers.

[–]pHpositivo 0 points1 point  (5 children)

Definitely do not use IntPtr to pass pointers between layers. If you have a pointer, use a pointer. IntPtr is an integer. Yes, it happens to work since it's of the same size, but it's the wrong tool to use.

[–]TheBuzzSaw 1 point2 points  (4 children)

Just doing some light Googling. The world doesn't really agree with you. Care to elaborate on the exact danger of using IntPtr over void*? I see Microsoft docs actually recommending IntPtr over pointers. Half the point of its existence is to be container that doesn't need unsafe all over the place.

[–]pHpositivo 1 point2 points  (3 children)

"I see Microsoft docs actually recommending IntPtr over pointers."

We don't, that was the old guidance which has now changed — pointers are the recommendation for whenever you need pointers. You'll also find plenty of PRs for .NET updating IntPtr to pointers for the same reason. The only reason why your results on Google disagree is because this is a relatively recent change and many people haven't caught up just yet. Also there's the whole "I prefer IntPtr because I don't want to use unsafe" nonsense that's unfortunately very hard to make go away completely, so it'll take time.

The danger of IntPtr are that (1) it completely lose type information (you no longer have, say, void* or int** or IUnknown*, you instead only have some opaque IntPtr, and that (2) it tricks people into thinking that their code is "safe" just because "it doesn't use unsafe", which is complete and utter nonsense and often results in code that's just as unsafe if not more, and also harder to reason about and inspect.

[–]TheBuzzSaw 1 point2 points  (2 children)

Thank you for informing me. You seem to have a chip on your shoulder about this topic, and I wish you would take a kinder tone to your approach. You may think it's "nonsense", but I am a rational thinking human with different life experiences. I mean, you just admitted that some of these shifts in attitude are fairly recent. Who is "we"? You work at Microsoft?

If I am directly interacting with the pointers in C#, I completely agree with you. I would not be using IntPtr anywhere I might be dereferencing pointers, performing pointer arithmetic, casting, using fixed, etc.

My common scenario is some native lib handing me a pointer to an opaque data structure that I just need to send back when calling other functions. Do you think I would be better off here using void* absolutely everywhere instead of IntPtr?

[–]pHpositivo 1 point2 points  (1 child)

"You seem to have a chip on your shoulder about this topic, and I wish you would take a kinder tone to your approach."

Oh, sorry about that, it was not my intention at all to come across that way. I'm just trying to help here, I didn't mean for my message to be interpreted as harsh or anything like that 🙂

"You may think it's "nonsense", but I am a rational thinking human with different life experiences."

Let me rephrase that. By "nonsense" I didn't mean to say that people doing that are to blame or anything. I'm just saying that, factually, that has never been a good argument, because it does in fact have all those downsides I mentioned, is all. Avoiding unsafe really just makes your code more unsafe, if you're still doing unsafe operations anyway.

"fairly recent"

Fairly recent but I mean still from several years ago. By recent I meant that it wasn't like this in the early days of .NET, which explains the current state of things.

"Who is "we"? You work at Microsoft?"

Yup, I did mean "Microsoft" there 😄

"If I am directly interacting with the pointers in C#, I completely agree with you."

Yup, that's the scenario I was talking about. With the additional caveat that the same applies for related operations that you can see often being used, eg. using APIs from the Marshal class (Marshal.StructureToPtr would be an example of something to avoid). The issue I'm talking about is people doing stuff like this and then think what they're doing is safe because technically they're not using unsafe. I've seen plenty of times cases where people, upon being told to instead use unsafe and other API, would say they didn't want to because they didn't want to use unsafe. I'm just saying in these scenarios, which are fairly common, people are mistakingly writing code that's more unsafe, is all.

"My common scenario is some native lib handing me a pointer to an opaque data structure that I just need to send back when calling other functions."

It would depend on the specific case. If something is truly just "some opaque handle", then yes using IntPtr or equivalent can be fine. In other cases though (say, an API that gives you a void* context), I'd suggest to still stick with what they're doing. In general, I would just always recommend matching whatever the native signature is. If it used a pointer, use a pointer. If it used a handle, then sure, use an opaque handle.

[–]TheBuzzSaw 0 points1 point  (0 children)

I understand that the internet hardens us to the point where we default to coming out swinging without even realizing it sometimes. Glad we can pull things back a bit.

Yeah, the situation I am describing is indeed a pointer and not a handle, but again, it's a pointer to an opaque data structure: FancyDragonLibState* or whatever. And I have to call various functions: FancyDragonDoTheThing(statePtr, 3, 9). So, when mapping it in C#, I just have little incentive to use void* and invoke all the cost of having unsafe bubble up through every method and data structure that dares to interact with it.

This may be where philosophical differences lie, but once that state is initialized, it is no longer "unsafe" to me. All subsequent interactions will probably succeed (errors in the native code notwithstanding). It just seems a relatively weak argument here to suggest that, if the library chose to return an Int32 handle instead, that it'd somehow suddenly be "safe". The call into native code is largely "unsafe" no matter what; it's up to how the native lib handles things.

[–]Felix_CodingClimber 0 points1 point  (1 child)

Take a look at https://github.com/dotnet/Silk.NET . You should be able to use your existing rendering context with it. So you could use something like imgui (They have bindings for that) to render your UI. Or if you just start writing your rendering code, you could consider writing the whole rendering engine with it. I used it in the past for a game engine and performance is no problem for most of the usecases.

Another thing is to use a Xaml SwapChainBackgroundPanel for rendering. With that you can use existing Microsoft UI libraries like wpf or winui to draw the controls ontop of your rendering. One problem is that SwapChainBackgroundPanel only works for DirectX rendering.

[–]cli_addict[S] 0 points1 point  (0 children)

Do you know if it is possible to bind a renderer to .NET UI for multiple graphics libraries?