2008-08-25 16 views
8

Piszę C/C++ DLL i chcesz eksportować do niektórych funkcji, które robiłem przed użyciem pliku .def jak tenPrzeciążone funkcje w C++ DLL plik DEF

LIBRARY "MyLib" 
EXPORTS 
    Foo 
    Bar 

z kodem określonym jako to, na przykład:

int Foo(int a); 
void Bar(int foo); 

jednak co, jeśli chcę zadeklarować przeciążony metodę Foo(), takie jak:

int Foo(int a, int b); 

jako D Plik EF ma tylko nazwę funkcji, a nie pełny prototyp. Nie widzę sposobu, w jaki poradziłby on z przeciążonymi funkcjami. Czy używasz tylko jednego wpisu, a następnie określasz, która wersja jest przeładowana, kiedy przechodzisz we właściwie prototypowanym wskaźniku funkcji do LoadLibrary()?

Edit: Żeby było jasne, to na Windows przy użyciu programu Visual Studio 2005

Edit: Oznaczono metodą non-def (__declspec) jako odpowiedź ... Wiem, że to nie faktycznie rozwiązać problemu, korzystając def plików, jak chciałem, ale wygląda na to, że prawdopodobnie nie ma (oficjalnego) rozwiązania przy użyciu plików def. Zostawi pytanie otwarte, jednak na wypadek, gdyby ktoś wiedział, że nie mamy przeciążonych funkcji i plików def.

Odpowiedz

9

W samym kodzie zaznacz funkcje, które chcesz wyeksportować za pomocą __declspec (dllexport). Na przykład:

#define DllExport __declspec(dllexport) 

int DllExport Foo(int a) { 
    // implementation 
} 
int DllExport Foo(int a, int b) { 
    // implementation 
} 

Jeśli to zrobisz, nie musisz wyświetlać funkcji w pliku .def.

Alternatywnie, może być w stanie korzystać z domyślnej wartości parametru, jak:

int Foo(int a, int b = -1) 

ta zakłada, że ​​istnieje wartość dla B, które można wykorzystać, aby wskazać, że jest nieużywany. Jeśli -1 jest prawną wartością b, lub jeśli nie ma lub nie powinno być wartością domyślną, nie zadziała.

Edytuj (Adam Haile): Poprawiono użycie __declspec jako __dllspec nie było poprawne, więc mogłem oznaczyć to jako oficjalną odpowiedź ... było wystarczająco blisko.

Edytuj (Graeme): Ups - dzięki za poprawienie mojej literówki!

+0

co jeśli używamy GetProcAddress() z dynamiczną biblioteką DLL? – null

+2

Następnie musisz użyć zniekształconych nazw lub zmienić nazwę jednej z funkcji i uczynić je zarówno "zewnętrznymi" C "', zakładając, że żaden z nich nie przyjmuje lub nie zwraca obiektów C++. –

11

Przeciążanie funkcji jest funkcją C++, która polega na manglingu nazwy (tajemnicze nazwy funkcji w komunikatach o błędach linkera).

pisząc zniekształcone nazwy do pliku def, mogę dostać mój projekt testowy, aby połączyć i uruchomić:

LIBRARY "TestDLL" 
EXPORTS 
    [email protected]@[email protected] 
    [email protected]@[email protected] 

wydaje się działać dla

void Foo(int x); 
void Foo(int x, int y); 

Więc skopiuj C nazwy funkcji ++ z komunikat o błędzie i zapisz go w swoim pliku def. Jednak prawdziwe pytanie brzmi: Dlaczego chcesz użyć pliku def, a nie iść z __declspec (dllexport)?

Zmanipulowane nazwy są nieprzenośne, testowałem z VC++ 2008.

+0

Interesujące podejście. Przez nieprzenośne masz na myśli różne wersje Visual Studio? Co może sugerować, że zmieniają swoje schematy wymieniające nazwy między wersjami? – jxramos

+0

@jxramos Nie jestem pewien, czy w rzeczywistości mogą zmienić schemat wymazywania nazwy. Ale wątpię, aby to działało w taki sam sposób, kiedy przełączasz się na inny kompilator, chyba że ten kompilator próbuje emulować zachowanie VC. – Timbo

+0

To z pewnością tylko kwestia VC++, ponieważ nie sądzę, aby inne kompilatory używały plików def. Także jeśli pamiętam, co kiedyś mi powiedziałem, to że Microsoft ma pomysł interfejsu dll, w którym wybrane przez użytkownika elementy są publicznie ujawniane, przez plik def lub __declspec, podczas gdy w Uniksie z jego plikami * .so ujawnia się wszystko z publicznym interfejsem API. Nie mają rozróżnienia między logicznym publicznym interfejsem API a publicznym interfejsem biblioteki. – jxramos

2

Nie istnieje agnostyczny sposób eksportowania przeciążonej funkcji w języku lub wersji, ponieważ konwencja manewrowania może się zmieniać przy każdym wydaniu kompilatora.

Jest to jeden z powodów, dla których większość funkcji WinXX ma zabawne nazwy, takie jak * Ex lub * 2.

+0

ciekawe tło z komentarzem WinXX! – jxramos

3

Nie ma oficjalnego sposobu robienia tego, co chcesz, ponieważ interfejs dll to api C.

Sam kompilator używa nazw zmanowanych jako obejścia, więc powinieneś używać nazw, aby nie zmieniać zbyt wiele w swoim kodzie.

8

Miałem podobny problem, więc chciałem napisać o tym również.

  1. Zazwyczaj używając

    extern "C" __declspec(dllexport) void Foo(); 
    

    eksportować nazwy funkcji jest w porządku. To będzie zwykle wyeksportować nazwę niezmieniona bez potrzeby użycia pliku .def o numerze . Istnieje jednak kilka wyjątków, takich jak __stdcall function i przeciążonych nazw funkcji.

  2. Jeśli zadeklarujesz funkcję użyć konwencję __stdcall (jak to się dzieje w wielu funkcjach API), a następnie

    extern "C" __declspec(dllexport) void __stdcall Foo(); 
    

    będzie eksportować zniekształcone nazwy jak _Foo @ 4. W takim przypadku może być konieczne jawne odwzorowanie wyeksportowanej nazwy na wewnętrzną, zniekształconą nazwę.

A. Jak wyeksportować niezmienioną nazwę. W pliku .def dodaj:

Spróbuje znaleźć "najlepsze dopasowanie" dla wewnętrznej funkcji Foo i wyeksportować ją. W powyższym przypadku, gdy istnieje tylko jeden foo to stworzy mapowanie

Foo = _Foo @ 4

jak można zobaczyć poprzez DUMPBIN/EKSPORTU

Jeśli przeciążone nazwy funkcji potem może potrzebować jawnie powiedzieć, którą funkcję chcesz w pliku .def , określając zniekształconą nazwę, używając składni entryname [= internalname]. na przykład

---- 
EXPORTS 
    ; Explicit exports can go here 

    [email protected] 
----- 

B. Alternatywą dla plików .def jest możliwość eksportu nazw "na miejscu" za pomocą #pragma.

#pragma comment(linker, "/export:[email protected]") 

C. Trzeci wariant może być podawanie jedynie jedna wersja Foo jako zewnętrzny „C”, które mają być eksportowane unmangled. Aby uzyskać szczegółowe informacje, patrz here.

2

Systax dla wywozu definicja brzmi:

entryname[=internalname] [@ordinal [NONAME]] [PRIVATE] [DATA] 

entryname jest funkcja lub nazwa zmiennej, którą chcesz wyeksportować. Jest to wymagane. Jeśli wyeksportowana nazwa różni się od nazwy w bibliotece DLL, określ nazwę eksportu w bibliotece DLL z nazwą wewnętrzną.

Na przykład, jeśli DLL eksportuje funkcję, func1() i chcesz go stosować jako func2(), to byłoby określić:

EXPORTS 
func2=func1 

Wystarczy zobaczyć zniekształcone nazwy (przy użyciu Dependency Walker) i podaj własną nazwę funkcji.

Źródło: http://msdn.microsoft.com/en-us/library/hyx1zcd3(v=vs.71).aspx

Edit: Działa to dla dynamicznych bibliotek DLL, gdzie musimy użyć GetProcAddress(), aby wyraźnie sprowadzić funkcji w DLL.