2013-04-10 4 views
5

Ich bin ein C++ (MSVC) Schreiber, VB-Neuling versucht, einen Experten VB.net Schriftsteller zu unterstützen, der diese Aufgabe zuvor nicht getan hat.Weitergabe von Zeigern an externe C-Funktion in einer DLL von VB

Wir möchten sowohl C/C++ - als auch VB-Anwendungen entwickeln, um eine in C++ geschriebene DLL mit C extern API-Funktionen zu verwenden. Das C++ - Programm funktioniert einwandfrei. Es ist VB, wo wir Schwierigkeiten haben.

Die DLL stellt eine extern C Funktion:

RegisterCallback(void* cbFuncPtr, void* dataPtr); 

Anmerkung 1: Siehe meine Anmerkung unten für eine Designänderung und die Gründe, warum wir es geschafft.

HINWEIS 2: Zusätzliche Aktualisierung als Antwort unten hinzugefügt.

wo die Callback-Funktion havs dieses C typedef:

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

Die cbFuncPtr wird erwartet, dass ein Funktionszeiger auf einige VB-Funktion sein, die als CALL_BACK genannt wird erhalten. Die dataPtr ist ein Zeiger auf eine Datenstruktur, die diese C Definition hat:

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

wo a_C_ptr is an internal pointer in the DLL that the VB can cast to long`. Es identifiziert eindeutig, wo in der DLL der Rückruf durchgeführt wurde, und ermöglicht es der VB-Funktion, Anrufe von denselben/verschiedenen Standorten zu erkennen.

Wir sind in der Lage, die RegisterCallback()-Funktion von VB zugreifen und ausführen, einfach gut. Protokollierung zeigt, dass wir dorthin gelangen und dass Daten weitergegeben werden. Es sind die tatsächlichen Daten, die das Problem zu sein scheinen.

Beim Lesen von ungefähr einer Million Foreneinträgen haben wir gelernt, dass VB nicht weiß, welche Zeiger sind und dass eine VB-Struktur mehr als nur organisierter Speicher ist. Wir sind uns ziemlich sicher, dass die "Adresse" einer VB-Struktur nicht das ist, was C für eine Adresse hält. Wir haben wiederholt Hinweise auf "Marshalling" und "Managed Data" gesehen, aber wir haben nicht genug Verständnis, um zu wissen, was uns das sagt.

Wie sollen wir VB codieren, um der DLL die Ausführungsadresse ihrer Callback-Funktion zu geben und wie wir ein VB-Konstrukt codieren, das die DLL genauso ausfüllen kann wie für C++?

Benötigen wir eine DLL-Funktion, in der die aufrufende App "C" oder "VB" sagen kann und die DLL die Strukturzeiger anders verarbeitet? Wenn ja, wie würde man C kodieren, um die VB-Struktur auszufüllen?

+1

Diese [MS Artikel darüber, wie zu marshall Daten in einem DLL] (http://msdn.microsoft.com/en-us/library/fzhhdwae.aspx) zumindest sieht aus wie es angewandt werden könnte .Es wurde von diesem [Artikel über das Aufrufen von C++ - DLLs aus VB] (http://social.msdn.microsoft.com/forums/en-US/Vsexpressvb/thread/4c486d10-fe8b-49da-a5f1-8054b82251ff/) verknüpft. Ich bin kein VB- oder .NET-Programmierer, also bin ich vielleicht daneben. –

+0

@DaveNewman Der letzte Beitrag zu dieser Diskussion ist bereits eine große Hilfe. Als C-Programmierer ist die Idee, dass Daten sich nur aufheben und bewegen können und dass Zeiger darauf nur so lange vertrauenswürdig sind, wie die Anweisung, die sie abgefragt hat, Häresie ist. Ich denke, ich muss die Struktur auf der DLL-Seite erstellen und der App mitteilen, wo ich die Daten ablege. Das vereitelt die Idee, die App die Daten so besitzen zu lassen, dass sie trotz der DLL vorhanden wären. –

+1

Soweit ich weiß, ist es in vb.net sehr nützlich, die IntPtr-Struktur für Zeiger zu bevorzugen. Aber wenn Sie bereits eine Zeigerdefinition haben, nicht sicher, ob sie wirklich benötigt wird. Zeiger sind auch in vb.net, nur ein bisschen schwieriger zu finden – Amegon

Antwort

1

Das ist ein bisschen zu groß und tief ist eine Bearbeitung auf das Original-Posting nur sein ...

Aus den von @DaveNewman geschrieben link, extrahierte ich dieses Juwel:


Hier ist ein wenig über das kompakte Framework, aber es ist das gleiche in der Erwachsene Framework:

Das .NET Compact Framework unterstützt das automatische Marshalling von Strukturen und Klassen, die einfache Typen enthalten. Alle Felder sind sequentiell im Speicher in der gleichen Reihenfolge angeordnet, wie sie in der Struktur oder Klassendefinition erscheinen.Beide Klassen und Strukturen erscheinen in systemeigenem Code als Zeiger auf C/C++ - Strukturen.

Objekte im verwalteten Heap können jederzeit im Speicher vom Garbage Collector verschoben werden, so dass sich ihre physischen Adressen ohne vorherige Ankündigung ändern können . P/Invoke pinnt automatisch verwaltete Objekte , die für die Dauer jedes Methodenaufrufs als Referenz übergeben wurden. Das bedeutet, dass Zeiger, die an nicht verwalteten Code übergeben werden, für diesen einen Aufruf gültig sind. Denken Sie daran, dass nicht garantiert werden kann, dass das Objekt bei nachfolgenden Aufrufen nicht an eine andere Speicheradresse verschoben wird .

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


Dieses große Hürde für eine RegisterCallback(fcnPtr, dataPtr) Funktion. Der zur Registrierungszeit übergebene Zeiger könnte sich jederzeit ändern, RegisterCallback() ist nicht die aktuelle Anweisung. Der Entsendeautor summierte es auf diese Weise

Sie müssen nichts tun, da die Strukturen für die Dauer des Anrufs automatisch fixiert werden.

impliziert, natürlich nicht außerhalb des Anrufs festgesteckt.

Aus diesem Grund haben wir uns für eine Designänderung entschieden, damit die Antwortstruktur sozusagen in die C/C++ Welt der DLL eingebaut wird, nicht in VB's Raum. So bleibt es stehen. Die Signatur der eigentlichen Callback-Funktion bleibt unverändert, so dass das VB-Programm wissen kann, wo die DLL die Antwort abgelegt hat. Dies ermöglicht auch den Respondern in der DLL, separate Antwortstrukturen für separate Bedürfnisse zuzuweisen.

+0

Ein Update. Basierend auf der Veröffentlichung von @DaveNewman und der großartigen Arbeit meines Kollegen fügen wir die verwalteten zu nicht verwalteten Klassen hinzu, die in [Anrufen von verwaltetem Code aus nicht verwaltetem Code und umgekehrt] vorgeschlagen werden (http://www.codeproject.com/Articles/ 9903/Calling-Managed-Code-aus-Unmanaged-Code-und-Vice # xx3493475xx). –

0

Wieder einmal ist mein Update zu groß für einen Kommentar!

18-Update April 2013:

Nun, der Versuch, den Code von Calling Managed Code from Unmanaged Code oben genannten zu verwenden war eine Büste. Wir mussten schließlich/clr zur DLL hinzufügen, um die DLL in verwalteten Code zu verwandeln, was sie aus einer C-Anwendung unbrauchbar machte.

Wir testen jetzt das Beispiel unter Callback Sample, die ich zeigen konnte, machte eine DLL, die mit VB und C++ arbeitete. Sie müssten die PinvokeLib.dll Source haben, damit dies funktioniert.

Hier ist der Code für den C++ (C wirklich) Tester. Zusammengestellt als ein MSVC-Projekt.


HINWEIS: Beachten Sie die __cdecl in dieser Zeile:

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

Es ist das Geheimnis war ich finden musste. Die DLL und diese App sind mit __cdecl-Verknüpfung, nicht __stdcall kompiliert. Sie sind die Standardeinstellung in VC++ und ich habe nur die Standardeinstellungen verwendet. Ich habe versucht, alles auf __stdcall zu ändern, aber das hat nicht funktioniert. Muss __cdecl sein.


// 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; 
} 
Verwandte Themen