Chciałbym prosty przykład eksportowania funkcji z biblioteki DLL C ++ systemu Windows.
Chciałbym zobaczyć nagłówek, .cpp
plik i .def
plik (jeśli jest to absolutnie wymagane).
Chciałbym, aby wyeksportowana nazwa była bez ozdób . Chciałbym użyć najbardziej standardowej konwencji wywoływania ( __stdcall
?). Chciałbym użyć __declspec(dllexport)
i nie muszę używać .def
pliku.
Na przykład:
//header
extern "C"
{
__declspec(dllexport) int __stdcall foo(long bar);
}
//cpp
int __stdcall foo(long bar)
{
return 0;
}
Próbuję uniknąć dodawania przez konsolidator podkreśleń i / lub liczb (liczba bajtów?) Do nazwy.
Nie mam nic przeciwko temu, że nie obsługuję dllimport
i dllexport
używam tego samego nagłówka. Nie chcę żadnych informacji o eksportowaniu metod klasy C ++, tylko funkcje globalne w stylu C.
AKTUALIZACJA
Bez konwencji wywoływania (i używania extern "C"
) daje mi nazwy eksportu tak, jak lubię, ale co to oznacza? Czy jest jakakolwiek domyślna konwencja wywoływania, którą otrzymuję pinvoke (.NET), deklaruję (vb6) i GetProcAddress
czego się spodziewa? (Myślę GetProcAddress
, że zależy to od wskaźnika funkcji utworzonego przez wywołującego).
Chcę, aby ta biblioteka DLL była używana bez pliku nagłówkowego, więc naprawdę nie potrzebuję dużo fantazji, #defines
aby nagłówek był używany przez dzwoniącego.
Nic mi nie jest z odpowiedzią, że muszę użyć *.def
pliku.
źródło
extern C
usunie dekorację opisującą typy parametrów funkcji, ale nie dekorację opisującą konwencję wywoływania funkcji; b) aby usunąć całą dekorację, należy określić (niezdekorowaną) nazwę w pliku DEF.Odpowiedzi:
Jeśli chcesz eksportować zwykłe C, użyj projektu C, a nie C ++. Biblioteki DLL C ++ polegają na manipulowaniu nazwami dla wszystkich isms C ++ (przestrzeni nazw itp.). Możesz skompilować swój kod jako C, przechodząc do ustawień projektu w C / C ++ -> Advanced, jest opcja „Kompiluj jako”, która odpowiada przełącznikom kompilatora / TP i / TC.
Jeśli nadal chcesz używać C ++ do pisania wewnętrznych elementów swojej biblioteki, ale eksportujesz niektóre niezarządzane funkcje do użytku poza C ++, zobacz drugą sekcję poniżej.
Eksportowanie / importowanie bibliotek DLL w VC ++
To, co naprawdę chcesz zrobić, to zdefiniować warunkowe makro w nagłówku, które zostanie uwzględnione we wszystkich plikach źródłowych w projekcie DLL:
Następnie na funkcji, którą chcesz wyeksportować, używasz
LIBRARY_API
:W projekcie kompilacji biblioteki utwórz definicję,
LIBRARY_EXPORTS
która spowoduje wyeksportowanie funkcji do kompilacji biblioteki DLL.Ponieważ
LIBRARY_EXPORTS
nie zostanie zdefiniowany w projekcie korzystającym z biblioteki DLL, gdy ten projekt zawiera plik nagłówkowy biblioteki, zamiast tego zostaną zaimportowane wszystkie funkcje.Jeśli twoja biblioteka ma być wieloplatformowa, możesz zdefiniować LIBRARY_API jako nic, gdy nie jest w systemie Windows:
Podczas korzystania z dllexport / dllimport nie musisz używać plików DEF, jeśli używasz plików DEF, nie musisz używać dllexport / dllimport. Te dwie metody wykonują to samo zadanie na różne sposoby, uważam, że dllexport / dllimport jest zalecaną metodą spośród dwóch.
Eksportowanie niezarządzanych funkcji z biblioteki C ++ DLL dla LoadLibrary / PInvoke
Jeśli potrzebujesz tego do korzystania z LoadLibrary i GetProcAddress, lub może importując z innego języka (np. PInvoke z .NET lub FFI w Python / R itp.), Możesz użyć
extern "C"
wbudowanego portu dllexport, aby poinformować kompilator C ++, aby nie zmieniał nazw. A ponieważ używamy GetProcAddress zamiast dllimport, nie musimy wykonywać tańca ifdef z góry, wystarczy prosty dllexport:Kod:
A oto jak wygląda eksport z Dumpbin / export:
Więc ten kod działa dobrze:
źródło
Dla C ++:
Właśnie spotkałem się z tym samym problemem i myślę, że warto wspomnieć o problemie, który pojawia się, gdy używa się obu
__stdcall
(lubWINAPI
) iextern "C"
:Jak wiadomo
extern "C"
usuwa dekorację, dzięki czemu zamiast:otrzymujesz nieozdobioną nazwę symbolu:
Jednak
_stdcall
(= makro WINAPI, które zmienia konwencję wywoływania) również ozdabia nazwy, więc jeśli użyjemy obu, otrzymamy:a korzyści z
extern "C"
są tracone, ponieważ symbol jest ozdobiony (z _ @ bajtów)Jest to szczególnie trudne, jeśli kierujesz reklamy na platformy x86 i x64.
Dwa rozwiązania
Użyj pliku definicji. Ale to zmusza cię do utrzymania stanu pliku def.
najprostszy sposób: zdefiniuj makro (patrz msdn ):
a następnie dołącz następującą pragmę do treści funkcji:
Pełny przykład:
Spowoduje to wyeksportowanie funkcji bez dekoracji zarówno dla obiektów docelowych x86, jak i x64, zachowując
__stdcall
konwencję dla x86. W tym przypadku__declspec(dllexport)
nie jest to wymagane.źródło
__FUNCTION__
makro działa tylko w funkcjach.Miałem dokładnie ten sam problem, moim rozwiązaniem było użycie pliku definicji modułu (.def) zamiast
__declspec(dllexport)
definiowania eksportu ( http://msdn.microsoft.com/en-us/library/d91k01sh.aspx ). Nie mam pojęcia, dlaczego to działa, ale działaźródło
.def
eksportu modułu plik robi pracę, ale kosztem jest w stanie dostarczyćextern
definicji w pliku nagłówka dla np DANYCH takim przypadku masz globalne dostarczyćextern
ręcznie definicję w wewnętrznych zastosowań te dane. (Tak, są chwile, kiedy tego potrzebujesz.) Lepiej jest zarówno ogólnie, jak i szczególnie w przypadku kodu wieloplatformowego, po prostu używać go__declspec()
z makrem, aby można było normalnie przekazywać dane.__stdcall
, następnie__declspec(dllexport)
będzie nie usunąć dekoracje..def
Jednak dodanie funkcji do testamentu.Myślę, że _naked może uzyskać to, czego chcesz, ale uniemożliwia również kompilatorowi wygenerowanie kodu zarządzania stosem dla funkcji. extern "C" powoduje dekorację nazwy w stylu C. Usuń to i to powinno pozbyć się twoich _. Konsolidator nie dodaje podkreślenia, kompilator to robi. stdcall powoduje dołączenie rozmiaru stosu argumentów.
Więcej można znaleźć na stronie : http://en.wikipedia.org/wiki/X86_calling_conventions http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx
Większe pytanie brzmi: dlaczego chcesz to zrobić? Co jest nie tak z zniekształconymi nazwami?
źródło