Click here to Skip to main content
15,885,309 members
Please Sign up or sign in to vote.
5.00/5 (2 votes)
I need to keep references in unmanaged code to objects on managed side, like handles. There are going to be approx. 10,000,000 objects.

I'm not familiar with interop, initially I thought it was as simple as declaring ref SomeClass on C# side and void* in C++.

Then I realized SomeClass would maybe need to have a representation in C++, that it wouldn't work with void*.
Am I forced to find a "common denominator" between the type representations in C# and C++? The common denominator would be the type System.Object or the specific specific, per object type, or hopefully with void* no type relationship would matter.

Another issue: after some research I found out that I should probably pin the object in memory on managed side. Maybe something akin to pin_ptr<T> would exist for C#? Or no pinning is necessary?

Or maybe using IntPtr would save the day? And I'd convert back and forth between IntPtr and void*.

I haven't tried anything as I don't know how to approach this. Especially as I've read that it could all seem to work out well until GC relocates variables and it messes all up.
And at the same time I'd like to take the path of the most performant solution, like avoiding ops any time the object is referenced, like unforseen checkings.

What I have tried:

The answer at https://stackoverflow.com/questions/19696459/pass-by-reference-from-c-through-c-l-i-to-unmanaged-c is inconclusive as it doesn't say anything about pinning.

If pinning is necessary, I could use the answer at https://stackoverflow.com/questions/35603788/is-it-possible-to-create-pinned-pointer-in-c

Ultimately, hopefully pinning is not necessary and neither any type conversion. I would get away with using IntPtr and void* converting as specified at https://docs.microsoft.com/en-us/dotnet/api/system.intptr.op_explicit?redirectedfrom=MSDN&view=net-5.0#System_IntPtr_op_Explicit_System_IntPtr__System_Void_

Looking at https://docs.microsoft.com/en-us/dotnet/api/system.intptr?view=net-5.0 IntPtr is a struct that potentially adds complexity as compared to a simple ref - void* option. But it probably there's no way around what IntPtr accomplishes
Posted
Updated 15-Feb-21 2:13am
Comments
Shao Voon Wong 15-Feb-21 21:30pm    
Very hard to evaluate whether pinning is needed or not when the class details are not revealed. If you are using C++/CLI (the managed C++), you can pass a reference to the array to it to keep, only pin one element at a time when you need to access or assign it to the native C++ object/variable. Pinning is a very expensive operation that slows down the GC cycle considerably.
amirea 23-Feb-21 8:29am    
Thanks for the comment. I'm not using CLI. just C#/.NET and C++. Also I think I'm missing something on the utility of memory pinning.
My impression is that pinning is necessary in order to refer to the very same variable instance from both runtimes and work with it directly. And that without pinning, a variable would be marshaled (copied or "translated").
Is my knowledge correct? I could be wrong in 2 ways:
1. Maybe you can directly reference an unpinned memory and view it as the variable.
2. Either pinned or not, variable would be marshaled and there wouldn't be any direct interaction with the variable but only through "mediation".

1 solution

You shouldnt do that this way, but really think hard about using so many objects at once.
If you really need that heavy load than you better code the program in one language to get best performance with that many objects.

Else you can use the IntPtr as unique identifier between the runtimes, but be aware that each communication is real workload which costs performance. Any typed pointer has the advantage of helping you debug - with the cost of some performance. Maybe you use an own typedef of pointer and try with IntPtr type.

Before you move on should really should do performance tests to verify that any future solution works with an acceptable performance. I doubt that!

Tip: make some concept of data flow and computation and ask some expert.
 
Share this answer
 
Comments
amirea 15-Feb-21 12:03pm    
Thank you for the insight! I won't be updating all of those very often, just some of them at a given moment.
"each communication is real workload which costs performance"
I'll send all of them in a single call, in an array from C++ to C#.
"Any typed pointer has the advantage of helping you debug - with the cost of some performance"
In this particular case I prefer performance over the debugging options.

There are so many options apparent to me -- somebody that knows next to nothing about the matter but I'm sure there are really just a couple of valid solutions, out of which there's one that fits the bill.

Ideally pinning the object in memory isn't needed (?) and I may get away with ref in C# and & or void* on C++ side.
amirea 25-Feb-21 11:42am    
I am sorry, as I was saying -- I'm just starting with interop and I probably didn't properly articulate my question.
It was recently that I found out:
1. Primary types are not copied but directly accessed (shared) between runtimes.
2. Arrays are shared as well.
3. structs are passed by value but one can avoid this by passing as <code>ref and pointers.
4. classes are copied. But one can avoid this by manually pinning through custom marshaling the type.
5. In all automatic cases (non-custom marshaling) P/Invoke internally takes care of pinning as appropriate from case to case.
6. IntPtr is automatically translated back and forth to void*.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900