2013-04-10 12 views
5

Jestem pisarz C++ (MSVC), początkujący VB próbuje pomóc ekspertowi pisarz VB.net, który właśnie nie zrobił tego zadania wcześniej.przekazywanie wskaźników do extern funkcji C w DLL z VB

Chcemy rozwijać aplikacje C/C++ i VB, aby korzystać z biblioteki DLL napisanej w C++ z zewnętrznymi funkcjami API C. Program w C++ działa dobrze. To VB, gdzie mamy trudności.

DLL zapewnia extern C funkcję:

RegisterCallback(void* cbFuncPtr, void* dataPtr); 

UWAGA 1: Patrz mój komentarz poniżej o zmianie projektu, a powodów udało się.

UWAGA 2: Dodatkowa aktualizacja została dodana jako odpowiedź poniżej.

gdzie funkcja zwrotna Choroba wibracyjna to C typedef:

typedef (void)(* CALL_NACK)(void*); 

cbFuncPtr ma być wskaźnik funkcji do niektórych funkcji VB, że dostanie zwanej jako CALL_BACK. dataPtr jest wskaźnikiem do struktury danych, która ma tę definicję C:

typedef struct 
{ 
    int  retCode; 
    void* a_C_ptr; 
    char  message[500]; 
} cbResponse_t; 

gdzie a_C_ptr is an internal pointer in the DLL that the VB can cast to long`. Jednoznacznie identyfikuje miejsce w bibliotece DLL wywołania zwrotnego i pozwala funkcji VB rozpoznawać połączenia z tych samych/różnych lokalizacji.

Jesteśmy w stanie uzyskać dostęp i uruchomić funkcję RegisterCallback() z VB w porządku. Rejestrowanie pokazuje, że dostaliśmy się tam i dane są przekazywane. To rzeczywiste dane wydają się być problemem.

Czytając o milionie wpisów na forum dowiedzieliśmy się, że VB nie wie, jakie są wskaźniki, a struktura VB to więcej niż tylko uporządkowana pamięć. Jesteśmy prawie pewni, że "adres" struktury VB nie jest tym, co C uważa za adres. Widzieliśmy powtarzające się odniesienia do "marszu" i "zarządzanych danych", ale brakuje im wystarczającej wiedzy, aby wiedzieć, co nam to mówi.

W jaki sposób powinniśmy zakodować VB, aby dać DLLowi adres wykonywania swojej funkcji wywołania zwrotnego i jak możemy zakodować konstrukt VB, który DLL może wypełnić tak samo, jak robi to dla C++?

Czy potrzebujemy funkcji DLL, w której aplikacja wywołująca może powiedzieć "C" lub "VB" i czy biblioteka DLL radzi sobie z wskaźnikami wytrzymałościowymi inaczej? Jeśli tak, to w jaki sposób można zakodować kod C, aby wypełnić strukturę VB?

+1

Ten [MS artykuł o tym, jak dane Marshalla DLL] (http://msdn.microsoft.com/en-us/library/fzhhdwae.aspx) przynajmniej wygląda to może zastosować .To było związane z tym [artykułem o wywołaniu C++ DLLs z VB] (http://social.msdn.microsoft.com/forums/en-US/Vsexpressvb/thread/4c486d10-fe8b-49da-a5f1-8054b82251ff/). Nie jestem programistą VB lub .NET, więc mogę być nie na miejscu. –

+0

@DaveNewman Ostatnia publikacja do tej dyskusji jest już bardzo pomocna. Jako programista kategorii C pomysł, że dane mogą się poruszać w górę i poruszać, a to jest dla nich wskazany, jest zaufany tylko tak długo, jak długo oświadczenie, które ich wypytywało, jest herezją. Chyba będę musiał stworzyć strukturę po stronie DLL i powiedzieć aplikacji, gdzie umieściłem dane. To pokonuje pomysł, aby aplikacja posiadała dane tak, aby były dostępne niezależnie od tego, co może zrobić biblioteka DLL. –

+1

O ile mi wiadomo, w vb.net bardzo przydatne jest preferowanie struktury IntPtr dla wskaźników. Ale jeśli już masz definicję wskaźnika, nie wiesz, czy naprawdę potrzebujesz. Wskaźnik jest również dostępny w vb.net, tylko nieco trudniej znaleźć – Amegon

Odpowiedz

1

Jest to nieco zbyt duże i głębokie, aby być tylko edycja oryginalnego delegowania ...

Z link wysłana przez @DaveNewman, ja ekstrakcji ten klejnot:


Tutaj jest trochę o kompaktowej strukturze, ale jest tak samo w architekturze dla dorosłych:

.NET Compact Framework obsługuje automatyczne tworzenie sieci struktur i klasy zawierające typy proste. Wszystkie pola są ułożone kolejno w pamięci w tej samej kolejności, w jakiej występują w strukturze lub klasie klasy .Obie klasy i struktury pojawiają się w natywnym kodzie jako wskaźniki do struktur C/C++.

Obiekty w zarządzanej stercie można przenosić w pamięci w dowolnym momencie przez odśmiecarkę, więc ich fizyczne adresy mogą ulec zmianie bez powiadomienia. P/Wywoływanie automatycznie przypisuje zarządzane obiekty przekazywane przez referencję na czas trwania każdego wywołania metody. Oznacza to, że wskaźniki przekazane do niezarządzanego kodu będą ważne dla tego jednego połączenia. Należy pamiętać, że nie ma gwarancji, że obiekt nie zostanie przeniesiony na inny adres pamięci podczas kolejnych połączeń.

http://msdn.microsoft.com/en-us/library/aa446538.aspx#netcfmarshallingtypes_topic6


To jest główną przeszkodą dla RegisterCallback(fcnPtr, dataPtr) funkcji. Wskaźnik przekazany w czasie rejestracji może się zmienić w dowolnym momencie RegisterCallback() nie jest bieżącym oświadczeniem. Autor wpisu podsumował to w ten sposób:

Nie musisz nic robić, ponieważ struktury są przypinane automatycznie przez czas trwania połączenia.

sugerowanie, oczywiście, nie przypięte poza rozmową.

Z tego powodu zdecydowaliśmy się na zmianę projektu, aby struktura odpowiedzi została wbudowana, by tak rzec, w świecie C/C++ biblioteki DLL, a nie w przestrzeni VB. W ten sposób pozostanie na miejscu. Faktyczna sygnatura funkcji wywołania zwrotnego pozostanie niezmieniona, aby program VB mógł dowiedzieć się, gdzie DLL umieścił odpowiedź. Pozwala to także respondentom w bibliotece DLL na przydzielanie oddzielnych struktur odpowiedzi dla oddzielnych potrzeb.

+0

Aktualizacja. W oparciu o publikację @DaveNewman i wspaniałą pracę mojego współpracownika, dodajemy zarządzane klasy niezarządzane sugerowane w [Calling Managed Code from Unmanaged Code i vice versa] (http://www.codeproject.com/Articles/ 9903/Calling-Managed-Code-from-Unmanaged-Code-and-vice # xx3493475xx). –

0

Po raz kolejny moja aktualizacja jest zbyt duża, by skomentować!

Aktualizacja 18 kwietnia 2013:

Cóż, próba użycia kodu z Calling Managed Code from Unmanaged Code cytowanego powyżej była niewypałem. Skończyło się na tym, że musieliśmy dodać/clr do biblioteki DLL, przekształcając bibliotekę DLL w kod zarządzany, co uniemożliwiło użycie aplikacji C.

Obecnie testujemy przykład pod adresem Callback Sample, który udało mi się wyświetlić, wykonaną bibliotekę DLL, która działała zarówno z VB, jak i C++. Aby to działało, musisz mieć PinvokeLib.dll Source.

Oto kod testera C++ (C naprawdę). Opracowany jako projekt MSVC.


UWAGA: Zwróć uwagę na __cdecl w tej linii:

typedef bool (__cdecl *FPtr)(BOOL_FP_INT fp, int i); 

Było tajemnicą musiałem znaleźć. Biblioteka DLL i ta aplikacja są kompilowane z łączem __cdecl, a nie __stdcall. Są domyślne w VC++ i właśnie użyłem ustawień domyślnych. Próbowałem zmienić wszystko na __stdcall, ale to nie zadziałało. Musi to być __cdecl.


// PinvokeTester.cpp : Defines the entry point for the console application. 
// 


#include <stdio.h> 
#include <cstdio> 
#include <stdlib.h> 
#include <cstdlib> 
#include <string.h> 
#include <cstring> 
#include <sstream> 
#include <iostream> 
#include <algorithm> 
#include <Windows.h> 

#define PINVOKELIB_API __declspec(dllimport) 

HINSTANCE  hLib;       // Windows DLL handle 

bool  CALLBACK VBCallBack(int value); 
bool  AttachLibrary(void); 
void * GetFuncAddress(HINSTANCE hLib, const char* procname); 

int main(int argc, char* argv[]) 
{ 
    if (!AttachLibrary()) 
    { 
     printf("Lib did not attach.\n"); 
     exit(1); 
    } 

    typedef bool (CALLBACK *BOOL_FP_INT)(int i); 

    typedef bool (__cdecl *FPtr)(BOOL_FP_INT fp, int i); 

    FPtr TestCallBack = (FPtr)GetFuncAddress(hLib, "TestCallBack"); 

    TestCallBack((BOOL_FP_INT)VBCallBack, 255); 

    return 0; 
} 

bool CALLBACK VBCallBack(int value) 
{ 
    printf("\nCallback called with param: %d", value); 
    return true; 
} 


bool  AttachLibrary(void) 
{ 
    // Get a var for the IPC-dll library. 
    std::string dllName; 

    /*--- First, link to the IPC-dll library or report failure to do so. ---*/ 
    dllName = ".\\PinvokeLib"; 
    if (NULL == (hLib = LoadLibraryA(dllName.c_str()))) 
    { 
     printf("\nERROR: Library \"%s\" Not Found or Failed to Load. \n\n", dllName.c_str()); 
     printf("\"%s\"\n", GetLastError()); 
     return false; 
    } 

    return true; 
} 

//===================================================================== 
void * GetFuncAddress(HINSTANCE hLib, const char* procname) 
{ 
    void * procAddr = NULL; 

    procAddr = (void *)GetProcAddress(hLib, procname); 

    // If the symbol wasn't found, handle error --------------------- 
    if (NULL == procAddr) 
    { 
     std::cout << "ERROR: Could not get an address for the \"" 
       << procname << "\" function. : " 
       << GetLastError() << std::endl; 
     exit(7); 
     procAddr = (void*)NULL; 
    } 

    return procAddr; 
} 
Powiązane problemy