Gå til innhold

utforming av C++ grensesnitt for DLL


Anbefalte innlegg

Jeg holder på med en DLL som skal brukes til å styre et lyd system. Jeg har laget klasser for de forskjellige typer objekter. Problemer oppstår når jeg skal lage en grensesnitt. Det beste jeg har kommet oppmed er en funksjon som ser noe slik ut

int sett_data_for_lyd_klasse(Handle,hvilke_data_som_skal_endres,data);

med dette er jo ikke c++!, men ettersom jeg vet så hvis jeg skal sende pointere til klassene å la programmet kalle på klasse medlemmene selv så må jeg ha koden i programmet eller lage et c++ lag oppå c laget.

 

Så hvis det er noen som har noen ider om hvordan dette burde gjøres så mottaes det med takk.

Lenke til kommentar
Videoannonse
Annonse

Erm, jeg skjønner ikke helt hva du er ute etter.

 

Du har laget en DLL, der du har deklarert (og definert?) en del typer. I klassene har du en del medlemsfunksjoner, som du ønsker å nå "utenfra". Har du statisk data "i klassen" du ønsker å nå? Eller er det instantierte klasser (objekter) du ønsker å hente data fra?

 

Den "vanlige" måten å lage interface på når det gjelder å nå beskyttede / private data i en klasse er jo å lage endel tilgangsfunksjoner som returnerer de data du ønsker, f.eks. du har en

 

protected: long lFoo;

 

Da lager du jo bare en

public: long returnFoo() { return lFoo; }

 

 

Jeg er desverre ikke sikker på om dette svarer på spørsmålet ditt, prøv gjerne å reformulere dersom jeg nettopp dro en skivebom og forklarte noe som du synest er innlysende.

Lenke til kommentar

Det jeg har laget er et slags utvidet c++ grensesnitt mot OpenAL, klassene har en et par variabler og et par funsjoner. Det jeg vil er å kalle på funsjonene utenfra DLL fila. Men det får jeg ikke til, jeg får bare til å lage et C grensesnitt og det liker jeg ikke.

 

Håper det ble litt klarere :)

Lenke til kommentar
Det jeg har laget er et slags utvidet c++ grensesnitt mot OpenAL, klassene har en et par variabler og et par funsjoner. Det jeg vil er å kalle på funsjonene utenfra DLL fila. Men det får jeg ikke til, jeg får bare til å lage et C grensesnitt og det liker jeg ikke.

 

Håper det ble litt klarere :)

5381794[/snapback]

Synes du forklarte det bra i den første posten.. :)

 

Det hele kommer litt an på hvordan du linker dll-fila. Hvis du linker implisitt kan du eksportere og importere klassen din. Men husk da at du kan være avhengig av å bruke samme kompilator på dll og applikasjon. Grunnen til det er at det ikke finnes noe standard binærrepresentasjon og navndekorasjon mellom kompilatorene. Dette var en av grunnene til at Microsoft en gang lagde COM. Derfor kan du strengt tatt lage COM-dll, men det er ikke nødvendigvis den beste løsningen for deg.

 

Hvis det er en dll-fil som er ment til gjenbruk i mange forskjellige applikasjoner foretrekker jeg selv å lage et C-grensesnitt og eksportere det fra dll-fila. Noen ganger lager jeg også en C++ adapter på topp av C-grensesnittet igjen. Dette er ikke det mest effektive, men er ikke noe problem med mindre du gjør svært mange kall til dll-fila veldig ofte.

 

Eksemplet du skriver med handle er en god og vanlig måte å gjøre det på. Handle er da gjerne pekeren til et instantiert objekt internt i dll-fila.

Lenke til kommentar

Hmmm det virker som om jeg må lese om implisitt linker :hmm:

Jeg liker ikke tanken på å lage en c++ ramme siden det kan bli en del kall, men det kan være mulig å redusere :)

Er det mulig å implisitt linke i runtime?

 

takk for gode tips kjetil7

Lenke til kommentar
Hmmm det virker som om jeg må lese om implisitt linker  :hmm:

Jeg liker ikke tanken på å lage en c++ ramme siden det kan bli en del kall, men det kan være mulig å redusere  :)

Er det mulig å implisitt linke i runtime?

 

takk for gode tips kjetil7

5383262[/snapback]

Nei, du kan ikke linke implisitt i runtime. Eksplisitt linking vil si at du bruker LoadLibrary() og implisitt at du bruker *.lib fila.

 

Du importerer og eksporterer klassen ved å bruke __declspec(dllimport) og __declspec(dllexport). Sjekk MSDN for mer info.

Endret av kjetil7
Lenke til kommentar

Sånn her har jeg gort det:

 

class ExportedClass
{
 virtual void Dispose() // ptr er alltid this
 {
   delete ptr;
 }
};

class ExportModule
{
 virtual int GetNumberOfClasses() { return 1; }
 virtual ExportedClass *CreateObject(int ClassNum)  { return new OpenALDevice(); }
 virtual string GetClassName(int num) { return "OpenALDevice"; }
 virtual void DeleteObject(ExportedClass *obj)
 {
   delete obj;
 }
};

ExportModule mod;

__declspec(dllexport) ExportModule &GetExportModule() { return mod; }

 

Og bruker LoadLibrary og GetProcAddress for å finne disse.

 

hvis dette var det du lurte på da....

Lenke til kommentar

Jeg er litt usikker på hvordan systemet ditt (GeirGrusom) fungerer , men det jeg er ute etter er muligheten til å kalle på funsjoner i OpenALDevice utenfra DLL filen, men selve funsjonen kjøres i DLL filen.

 

Slik jeg ser det så returnerer GetExportModule() bare en peker til klassen OpenALDevice, men du må selv ha funsjonen i programmet.

Lenke til kommentar

Prinsippet bak GeirGrusom sitt eksempel er det samme som COM bruker og vil fungere.

 

Det han gjør er å instantiere objektet i dll-fila slik at vtable blir satt til å peke på metodene i dll-fila. Alle metoder i klassen du ønsker å "eksportere" må med andre ord være virtual.

 

Siden en C++ konstruktor ikke kan være virtual må du også eksportere en egen funksjon som lager selve objektet. GeirGrusom har gjort dette i GetExportModule() funksjonen. COM bruker CoCreateInstance() for å gjøre det samme.

 

En grunnregel er at dll-fila skal frigjøre alt minne den allokerer. Derfor må du også tilby dette til brukeren av dll-fila. GeirGrusom gjør dette i Dispose() (merk: du kan strengt tatt gjøre destruktoren virtual og slette objektet med delete fra klienten. Men i følge MSDN skal dll-fila selv frigjøre minne den selv allokerer og en virtual destruktor er derfor unødvendig).

 

En god praksis er å gjøre alle konstruktorene og destruktoren privat slik at klienter ikke kan lage egne instanser uten å gå veien rundt GetExportedModule(). Bruker av dll-fila vil da heller ikke ha mulighet til å slette objektet uten å bruke Dispose().

 

Jeg har laget et lite eksempel som er noe "forenklet":

 

explicit_dll_export.h

#ifndef __explicit_dll_export_header
#define __explicit_dll_export_header


#if defined (__cplusplus)

class exported_class
{
 exported_class();
 exported_class(const exported_class &);
 exported_class & operator=(const exported_class &);
 virtual ~exported_class();
 friend int create_instance_impl(int, void**);

public:
 virtual void dispose();

 virtual const char* get_name() const;
};

#endif



#if defined(__cplusplus)
extern "C" {
#endif


typedef int (proc_create_instance_t) (int, void**);

int create_instance(int guid, void** ptr);


#if defined(__cplusplus)
} /* extern "C" */
#endif



/* guids */
#define GUID_EXPORTED_CLASS 0x01


#endif /* __explicit_dll_export_header */

 

 

 

explicit_dll_export.cpp

#include "explicit_dll_export.h"


exported_class::exported_class()
{
}

exported_class::exported_class(const exported_class &)
{
}

exported_class & exported_class::operator=(const exported_class &)
{
 return *this;
}

exported_class::~exported_class()
{
}

void exported_class::dispose()
{
 delete this;
}

const char* exported_class::get_name() const
{
 return "exported_class";
}


// ----------------------------


// a naive implementation of create_instance_impl ...
int create_instance_impl(int guid, void** ptr)
{
 switch(guid)
 {
 case GUID_EXPORTED_CLASS:
   *ptr = new exported_class();
   break;

 default:
   *ptr = 0;
 }

 return (*ptr ? 0 : -1);
}



// exported functions

extern "C"
{
 
 int create_instance(int guid, void** ptr)
 {
   return create_instance_impl(guid, ptr);
 }
}

 

 

explicit_dll_export.def:

LIBRARY	explicit_dll_export

EXPORTS

create_instance                @1

 

 

klienten:

#include <windows.h>
#include <tchar.h>
#include <iostream>
#include "explicit_dll_export.h"


int main()
{
 HINSTANCE dllModule = LoadLibrary(_T("explicit_dll_export.dll"));
 proc_create_instance_t* fnCreateInstance;

 // --- import library and functions

 if(!dllModule)
 {
   std::cout << "failed to load dll" << std::endl;
   exit(-1);
 }

 fnCreateInstance = (proc_create_instance_t*) GetProcAddress(dllModule, _T("create_instance"));
 
 if(!fnCreateInstance)
 {
   std::cout << "failed to get function address from dll" << std::endl;
   exit(-2);
 }

   
 // --- create instance of exported_class
 exported_class* obj;

 // not legal... compiler will generate an error:
 // obj = new exported_class();

 fnCreateInstance(GUID_EXPORTED_CLASS, (void**)&obj);


 // --- use object
 std::cout << "class name: " << obj->get_name() << std::endl;


 // --- delete object

 // not legal... compiler will generate an error:
 // delete obj;

 obj->dispose();


 fnCreateInstance = 0;
 FreeLibrary(dllModule);
}

Lenke til kommentar
  • 1 måned senere...

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
  • Hvem er aktive   0 medlemmer

    • Ingen innloggede medlemmer aktive
×
×
  • Opprett ny...