2009-02-20 11 views
5

Tworzę bibliotekę DLL C++/CLI, która zależy od wielu bibliotek statycznych C++. Niektóre wywołania funkcji wymagają podania niezarządzanych wskaźników. Jak przekazać je poprawnie?Przekazywanie niezarządzanych wskaźników w C++/CLI

Również inne funkcje oczekują, że "ten wskaźnik" zostanie przekazany jako pusty *. Jaki jest właściwy sposób przekazania "tego"?

Oto moja definicja klasy ...

public ref class RTPClient 
{ 
    public: 
     RTPClient(); 
     ~RTPClient(); 

     bool Connect(); 
     void Disconnect(); 

    private: 
     CIsmaClient* mClient; 
}; 

Oto mój zwyczaj, gdzie stosowane są wskaźniki w pytaniu ...

RTPClient::RTPClient(): 
    mClient(NULL) 
{ 
    CIsmaClient::Create(&mClient, NULL, &AllocBuffer, &GetDataPointer, this); 
} 

Wykorzystanie & mClient i " powoduje to następujące błędy kompilatora ... 1>. \ VBLoadSimulatorDll.cpp (40): error C2664: 'CIs maClient :: Tworzenie”: nie można konwertować parametru 1 od 'cli :: interior_ptr' do 'CIsmaClient **' 1> z 1> [ 1> Typ = CIsmaClient * 1>]

1> \. VBLoadSimulatorDll.cpp (40): błąd C2664: "CIsmaClient :: Utwórz": nie można przekonwertować parametru 5 z "VBLoadSimulator :: RTPClient^const" na "VOID *"

Odpowiedz

9

Jeśli przekazujesz wskaźnik do zarządzanej klasy, to łatwo jest przekonwertować^odniesienie do wskaźnika, ale musisz przypiąć zarządzany obiekt, aby GC nie przesuwał go w pamięci (tym samym unieważniając wskaźnik)

Jest to proste z pin_ptr

Jednak kod robi dwie rzeczy, które nie będą działać

RTPClient::RTPClient(): 
     mClient(NULL) 
{ 
    CIsmaClient::Create(
     &mClient,   // 1 
     NULL, 
     &AllocBuffer, 
     &GetDataPointer, 
     this);   //2 
} 

1) Próbujesz podjąć adres czegoś na zarządzanej stercie (lokalizacji wskaźnik do wskaźnika mClient znajduje się na sterowanej stercie.

Jako takie może poruszać się w pamięci, a tym samym wewnętrznym wskaźniku dostawcy kompilatora (którego wartość jest utrzymywana nad operacjami GC). Musi to być pinned i będzie działać tylko wtedy, gdy funkcja Create nie będzie używać wskaźnika po zakończeniu zasięgu (jeśli przejdzie gdziekolwiek indziej, aby go przechowywać, spowoduje to błędy).

2) Podajesz handle (symbol śmiesznego kapelusza) zamiast wskaźnika. (Przeczytaj sekcję poświęconą tej wikipedii, która jest dobrym przeglądem) Nie jest to (i nie może być) zrozumiane przez niezarządzany kod.

Jedynym powodem, dla którego mogę myśleć o tym parametrze w tym kontekście, jest jawna zmienna stanu przekazywana do kolejnych wywołań funkcji (popraw mnie, jeśli się mylę). "to" w tym kontekście NIGDY nie będzie działało poprawnie, ponieważ może się poruszać w pamięci, gdy pin_ptr wychodzi poza zakres.

Mając to na uwadze, tutaj jest (częściowo) poprawiona implementacja, która wyjaśnia, co można i nie można naprawić.

RTPClient::RTPClient(): 
     mClient(NULL) 
{ 
    // make it clear you want the address of the instance variable 
    pin_ptr<CIsmaClient*> pinnedClient = &this->mClient; 
    CIsmaClient::Create(
     pinnedClient,   // fixed 
     NULL, 
     &AllocBuffer, 
     &GetDataPointer, 
     x /* pass something else in */);   //2 
} 

Jeśli dostarczyć więcej informacji na temat tego, co ostatni parametr jest używany do mogę zaproponować możliwe rozwiązania

+0

Oto prawidłowe powiązanie: http://msdn.microsoft.com/en-us/library/1dz8byfh.aspx –

+0

wskaźnik jest niezarządzanego klasy mimo ... – cjserio

+0

jeśli jest niekontrolowana wtedy po prostu przekazując zwykły tylko C + + wskaźnik jest w porządku. Nie widzę problemu. – ShuggyCoUk

2

myślę, że będziesz to najprostszy sposób przekazywania zarządzanego poprzez odniesienie void wskaźnik:

void SomeFunction(void* input) 
{ 
    gcroot<ManagedClass^>* pointer = (gcroot<ManagedClass^>*)(input); 
    (*pointer)->ManagedFunction(); 
} 

void Example() 
{ 
    ManagedClass^ handle = gcnew ManagedClass(); 
    gcroot<ManagedClass^>* pointer = new gcroot<ManagedClass^>(handle); 
    SomeFunction((void*)pointer); 
    delete pointer; 
} 
+0

Dlaczego używasz 'nowego' i' delete'? –

+0

, ponieważ nie możesz wziąć adresu gcroot. Jeśli chcesz, powinieneś używać GCHandle bezpośrednio, a także, w wielu przypadkach, nie bój się używać Obect^* jeśli ukryty obiekt^znajduje się na stosie. czyli tutaj: SomeFunction (& handle); i dane wejściowe należy przesłać do ManagedClass^* – reuns