2016-04-13 46 views
0

雖然我沒有問題通過Visual Studio設置運行時DLL加載,但在通過Visual Studio CLI工具手動執行時遇到了一些麻煩。如何通過命令行設置VC++動態鏈接?

假設我們有以下2個簡單的C++源代碼文件,我們想編譯一個二進制可執行文件,一個用於DLL:

的main.cpp

void say_hello(); 

int main() 
{ 
    say_hello(); 
    return 0; 
} 

say_hello的.cpp

#include <stdio.h> 

void say_hello() 
{ 
    printf("Hello DLL World!"); 
} 

什麼是編譯步驟3210文件作爲DLL然後動態鏈接它與main.cpp的呼叫?

基礎上,通過MSDN文檔我讀,我能成功編譯say_hello.dll和應用程序,然後運行它,用下面的命令:

cl say_hello.cpp /LD 
lib say_hello.obj 
cl say_hello.lib main.cpp 

不幸的是,這似乎只允許靜態鏈接該應用程序通過say_hello.lib文件(可以通過刪除.lib和.dll文件來確認,該文件仍然允許二進制文件成功運行)。

我必須將哪些命令/參數傳遞給編譯/鏈接階段才能讓main.exe使用DLL而不是靜態庫?

+0

'cl say_hello.cpp/LD'應該在.lib文件中產生DLL和一個導入庫。隨後的'lib'命令會用靜態庫覆蓋前面提到的導入庫。只是不要那樣做。另一個問題是名稱需要通過DEF文件或'__declspec(dllexport)'從DLL中顯式導出。正如所寫的,你不會輸出任何東西,所以沒有什麼可以實際鏈接到的。 –

+0

Ahhh yes ...第一步'cl say_hello.cpp/LD'的控制檯輸出似乎表明「/implib:say_hello.lib」確實被創建,但我只能看到'say_hello.obj'和在目錄中生成'say_hello.dll'。是否有其他國旗通過告訴它不要清理進口圖書館? – depthfirstdesigner

+0

Ahh我看到'__declspec(dllexport)'宏作爲要導出函數的前綴是非常重要的,因爲沒有它,cl.exe會在完成後立即刪除導入庫文件'say_hello.lib'。 – depthfirstdesigner

回答

1

下面是一個例子。並不是每一個事情是完全必要的(如的DLLMain),但我認爲這些事情你應該看看了;-)

SayHello.cpp

#define WIN32_LEAN_AND_MEAN 
#include <windows.h> 
#include <stdio.h> 

// see https://msdn.microsoft.com/en-us/library/56h2zst2.aspx : Decorated Names 
extern "C" { // somehow making it superfluous to put the code in SayHello.cPP ...but anyway ;-) 
    // see https://msdn.microsoft.com/en-us/library/3y1sfaz2.aspx : dllexport, dllimport 
    __declspec(dllexport) void say_hello() 
    { 
     printf("Hello DLL World!"); 
    } 

    // see https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx : (optional) DllMain entry point 
    BOOL WINAPI DllMain(HMODULE hModule, 
     DWORD ul_reason_for_call, 
     LPVOID lpReserved 
     ) 
    { 
     switch (ul_reason_for_call) 
     { 
     case DLL_PROCESS_ATTACH: 
     case DLL_THREAD_ATTACH: 
     case DLL_THREAD_DETACH: 
     case DLL_PROCESS_DETACH: 
      break; 
     } 
     return TRUE; 
    } 
} 

的main.cpp

extern "C" { __declspec(dllimport) void say_hello(); } // we did this in SayHello.cpp, so we have to do it here too. 
// otherwise the name wouldn't match 

int main() { 
    say_hello(); 
    return 0; 
} 

,然後編譯/聯

cl /D_USRDLL /D_WINDLL SayHello.cpp /LD /link /OUT:SayHello.dll 

/LD告訴鏈接器建立一個DLL 使用/ MT,請參見/MD, /MT, /LD (Use Run-Time Library)。 (通過OUT:參數可以更改.dll的名稱;這裏它是默認設置,僅用於演示目的。如果不顯示,您也可以跳過/ link參數,因爲不再有鏈接器參數了。)

cl.exe /MT main.cpp /link /SUBSYSTEM:CONSOLE "SayHello.lib" 

匹配DLL的運行時的lib設置,創建console application(main.cpp中具有int main())和鏈接的SayHello(的存根LIB,而不是使用LoadLibrary("SayHello.dll")/GetProcAddress(...)

+0

Doh,混亂了第二個cl.exe命令的編譯器/鏈接器參數。注意自我:首先測試,然後發佈。 – VolkerK

0

我將VolkerK的答案標記爲正確,因爲它包含了一些其他重要的Windows DLL API細節,這些細節絕對值得一個閱讀,但我想總結最低更改可以使其獲得動態鏈接工作在最簡單的例子基於伊戈爾Tandetnik和VolkerK的評論。

  • 至少一個功能之前添加該宏__declspec(dllexport)在DLL導出是必不可少的,否則該庫編譯命令將不會創建導入庫以指示哪些函數可用於動態鏈接。

  • 第二個「lib say_hello.obj」是完全錯誤的,因爲DLL編譯命令會正確地生成say_hello.lib(而該lib命令只會生成一個靜態庫並最終覆蓋第一個命令)。

下面是完整的最基本的工作例如:

的main.cpp

void say_hello(); 

int main() 
{ 
    say_hello(); 
    return 0; 
} 

say_hello.cpp

#include <stdio.h> 

__declspec(dllexport) void say_hello() 
{ 
    printf("Hello DLL World!"); 
} 

編譯命令:

cl say_hello.cpp /LD 
cl main.cpp say_hello.lib