开发者

How to pass address of objects created in a C# List to C++ dll?

开发者 https://www.devze.com 2023-03-12 13:23 出处:网络
I have been looking all over google to find some answers to my questions but do not quite understand what I have found. I have some objects which are created and stored in C# List after using System.I

I have been looking all over google to find some answers to my questions but do not quite understand what I have found. I have some objects which are created and stored in C# List after using System.IO to read some text files. After that, I want to send references (using const pointers) to each of these objects to the internal classes in C++ dll so that it can use them for computation of some algorithms.

Here are some simple example (not actual code) of what I am doing:

The C# class:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class SimpleClass
{
    [MarshalAs(UnmanagedType.LPStr)]
    public string           Name;
    public float            Length;
}

with corresponding C struct:

struct SimpleClass
{
    const char* Name;
    float Length;
};

stored in

List<SimpleClass> ItemList;

after parsing some text files.

Then calling the following dll function:

C#:

[DllImport("SimpleDLL")]
public static extern void AddSimpleReference(SimpleClass inSimple);

C:

void AddSimpleReference(const SimpleClass* inSimple)
{
   g_Vector.push_back(inSimple); // g_Vector is a std::vector<const SimpleClass* > type
}

What I have tried is:

for(int i=0; i<ItemList.Count;++i)
{
    SimpleClass theSimpleItem = ItemList[i];
    AddSimpleReference(theSimpleItem);
}

Initially, I thought it would be easy to get a actual reference/address just by using the assignment operator since classes in C# are passed-by-reference but it turns out that the C++ class is always pushing the same address value (the address value of the temp reference) into the container instead of the actual address. How do I get the actual object addresses and send it to C++ DLL so that it can have read-only access to the C# objects?

UPDATE: Sorry to those who posted answers with unsafe codes. I forgot to mention that the C# code is开发者_JAVA百科 actually used as a script in Unity game engine which does not allow the use of unsafe codes.


First you need to change your interop signature to take a pointer (and thus making it unsafe).

[DllImport("SimpleDLL")]
public unsafe static extern void AddSimpleReference(SimpleClass* inSimple);

Then, because the GC is free to move objects around in memory as it pleases, you will need to pin the object in memory for the entire time you will need its address on the unmanaged side. For that you need the fixed statement:

SimpleClass theSimpleItem = ItemList[i];
unsafe
{
    fixed(SimpleClass* ptr = &theSimpleItem)
    {
        AddSimpleReference(ptr);
    }
}

This would work if AddSimpleReference used the pointer and then discarded it. But you're storing the pointer in a std::vector for later. That won't work, because the pointer will probably become invalid due to the GC moving the original item somewhere else once execution leaves the fixed block.

To solve this, you need to pin the items until you are done with them. To do this you may need to resort to the GCHandle type.

// Change the interop signature, use IntPtr instead (no "unsafe" modifier)
[DllImport("SimpleDLL")]
public static extern void AddSimpleReference(IntPtr inSimple);

// ----
for(int i=0; i<ItemList.Count;++i)
{
    SimpleClass theSimpleItem = ItemList[i];
    // take a pinned handle before passing the item down.
    GCHandle handle = GCHandle.Alloc(theSimpleItem, GCHandleType.Pinned);
    AddSimpleReference(GCHandle.ToIntPtr(handle));
    // probably a good idea save this handle somewhere for later release
}

// ----
// when you're done, don't forget to ensure the handle is freed
// probably in a Dispose method, or a finally block somewhere appropriate
GCHandle.Free(handle);

When doing something like this, keep in mind that pinning objects in memory for a long time is a bad idea, because it prevents the garbage collector from doing its job efficiently.


Even though I think this is not a good idea, have a look at unsafe code and memory pinning. Here is a good start on MSDN.

fixed and unsafe keywords are likely what you should be looking for.


You cannot. The C# GC will move objects for fun. Your addresses will go out of scope.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号