Casting a CLR type to void* and back

2019-07-01 14:23发布

问题:

How to properly convert a CLR type, say Foo^, to void* and back some time later?


The scenario is, I have some unmanaged code in a DLL that can be summarized as

class Handler {
  void* _obj;
  void (*_call)(void* obj, int detail);

  void handle_event() { _call(_obj, 1234); }
public:
  void set_object(void* obj) { _obj = obj; }
  void set_callback(void(*callback)(void*,int)) { _call = callback; }
};

I want to store a CLR object in the Handler's _obj field. How to implement it, taking into account that the GC may move the CLR object? (pin_ptr? gcroot?)

static void event_callback(void* obj, int detail) {
   Foo^ clr_obj = undo_magic(obj);
//                ^^^^^^^^^^ how?
   clr_obj->DoStuff(detail);
}

public ref class Foo {
   Handle* h;
public:
   void Start() {
     h = new Handler;
     void* obj = do_magic(this);
//               ^^^^^^^^ how?
     h->set_object(obj);
     h->set_callback(event_callback);
   }
   ...
}

回答1:

Pinning would be required. However, you are storing this 'reference', requiring the object to stay pinned. That's quite unhealthy, the garbage collector would constantly have to work around it. Another problem is that just pinning isn't enough, there has to be a recognizable reference to the object so that the GC won't collect the object. The stored void* isn't good enough. You'd normally solve this with gcroot<> but that can't work here either.

A better approach is to simply pass a 'handle'. Use a Dictionary<int, Foo^> to convert the void* back to the object. Or a List<Foo^>, now the index could be the handle.