轉 命令行下玩VC

說明:(1)轉載請註明出處:http://www.cnblogs.com/opangle/p/4298155.html

(2)以下以VS2013爲例,並假設VC安裝路徑爲%VC_INSTALL_PATH%(本人的安裝目錄爲D:\Program Files (x86)\Microsoft Visual Studio 12.0)。

 

一、環境配置

 

■ 方法一:使用MS提供的Developer Command Prompt快捷方式

 

“開始” => “Visual Studio 2013” => “Visual Studio Tools” => “Developer Command Prompt for VS2013”。

 

■  方法二:使用MS提供的vcvarsall.bat腳本

 

在命令行窗口中進入%VC_INSTALL_PATH%目錄,執行“vcvarsall.bat”腳本。

 

■  方法三:手動配置環境變量

 

手動配置環境變量,至少要設置一下三個環境變量:

※  PATH:默認情況下cl命令(微軟編譯器)是不可以使用的,需要將cl.exe文件所在的路徑(即%VC_INSTALL_PATH%\bin目錄)添加到PATH環境變量中。

※  INCLUDE:默認情況下cl命令不知道從何處查找系統頭文件的,該環境變量告訴cl命令從何處查找系統頭文件。

※  LIB:與INCLUDE環境變量類似,LIB環境變量用來告訴鏈接器:從何處查找庫文件、目標文件等。

 

本人試過的最小配置如下:

環境變量

配置說明(多個路徑之間用分號分隔)

PATH

將以下路徑加入PATH環境變量中:

%VC_INSTALL_PATH%\bin

INCLUDE

將以下路徑加入INCLUDE環境變量中:

%VC_INSTALL_PATH%\include

C:\Program Files (x86)\Windows Kits\8.1\include\shared

C:\Program Files (x86)\Windows Kits\8.1\include\um

LIB

將以下路徑加入到LIB環境變量中:

%VC_INSTALL_PATH%\lib

C:\Program Files (x86)\Windows Kits\8.1\lib\winv6.3\um\x86

注:最完整的配置方式可以參考“方法一”、“方法二”中的環境變量配置方式。

 

■  測試環境配置

 

寫一個簡單的“Hello World”程序(假設爲hello.cpp),在命令行下執行:cl hello.cpp,如果能成功生成hello.exe可執行文件,即說明配置成功!

 

二、常用的命令行選項

 

下表列出了一些常用的命令行選項,並同時列出了gcc中相對應的選項,熟悉gcc的朋友可以不用看“說明”應該也能明白各選項的含義和作用。

MSVC

gcc

說明

/E

-E

輸出預處理結果

/Dname

/Dname=value

-Dname

-Dname=value

定義一個宏

/Idirecotry

-Idirecotry

指定頭文件搜索路徑

/c

-c

編譯、彙編生成目標文件

/libpath:direcotry

-Ldirecotry

指定庫文件搜索路徑(MSVC的/libpath屬於鏈接選項,第一個鏈接選項之前要指定/link選項,用來告訴編譯器驅動,後續選項傳給鏈接器使用)

 

另外,如果覺得每次編譯都要使用同樣的選項,敲太長的命令實在是一件累人的事,微軟同樣爲大家提供了省事的方式:仍然是設置環境變量。詳細說明如下:

※  CL環境變量:其中可以指定多個常用的選項,cl命令會自動將該環境變量的內容加入編譯命令。

※  LINK環境變量:鏈接器會自動將該環境變量中的內容加入到鏈接命令中。

 

三、靜態庫的創建與使用

 

■  創建靜態庫

 

linux下創建靜態庫(*.a)通常需要藉助ar命令,例如:“ar -r mylib.a foo.o bar.o”。微軟也提供了類似的命令——lib命令,該命令的詳細使用方法可參考其幫助文檔“lib /?”,下面僅給出一個簡單的示例:

cl /c foo.cpp
cl /c bar.cpp
lib foo.obj bar.obj /out:mylib.lib

注:其中“/out選項”用於指定生成的靜態庫文件名。

 

■ 使用靜態庫

 

測試目錄結構如下:

.
+-- lib
|    +-- mylib.lib
|    +-- mylib.h
+-- test
     +-- test.cpp

測試代碼如下:

複製代碼
// mylib.h
extern "C" void foo();

// test.cpp
#include <mylib.h>
int main() {
    foo();
    return 0;
}
複製代碼

在test目錄下編譯test.cpp文件:

        cl /I..\lib test.cpp /link /libpath:..\lib mylib.lib

其中/link選項用於告訴cl命令:後續選項爲鏈接選項,請從..\lib目錄查找mylib.lib庫文件!

 

除了使用命令行選項告訴鏈接器應該鏈接的庫、從何處查找庫,還可以使用“#pragma comment”指令。

例1(編譯命令cl /I..\lib test.cpp /link /libpath:..\lib):

#include <mylib.h>
#pragma comment(lib, "mylib.lib") // 此處包含庫名信息,因此命令行選項中不需指定庫名
int main() {
    foo();
    return 0;
}

例2(編譯命令cl /I..\lib test.cpp):

#include <mylib.h>
#pragma comment(lib, "..\\lib\\mylib.lib") // 此處包含庫名及路徑信息,注意轉義符用“\\”
int main() {
    foo();
    return 0;
}

 

四、動態庫的創建與使用

 

與靜態庫的創建相比,動態庫的創建相對複雜。爲了更容易理解,這裏先介紹一些關於windows動態鏈接庫(DLL)基本概念,然後再介紹動態庫的創建和使用方法。

 

■ 基本概念一:動態庫的分類

 

微軟將動態庫分爲四類:

※ 非MFC動態鏈接庫(Non-MFC DLL)

※ 靜態鏈接MFC的正規DLL(Regular DLLs statically linked to MFC)

※ 動態鏈接MFC的正規DLL(Regular DLLs dynamically linked to MFC)

※ 擴展DLL(Extension DLLs)

其中只有第一種動態庫不需要鏈接微軟的MFC,後三種都需要鏈接MFC(不管你是否使用MFC),個人感覺微軟這樣做,似乎有點將自己的產品強加於人的感覺,當然,這純屬本人的個人想法,也許有人真的需要MFC吧。這裏需要特別說明的是:下文中所提到的“動態庫”,特指上述第一種動態庫,而不再贅述成“非MFC動態鏈接庫(Non-MFC DLL)”了。

 

■ 基本概念二:符號的導出與導入

 

符號的導出與導入的分兩個方向:

※ 從動態庫導出符號:生成導出符號表;

※ 嚮應用程序導入符號:告知鏈接器——我需要的符號來自某個動態庫。

下面首先介紹符號的導出,再介紹符號的導入。

 

windows下的動態庫文件(.dll)與可執行文件(.exe)在文件的組織結構上最大的區別在於:動態庫文件中包含一個導出符號表。只有存在於該導出符號表中的符號(名字)纔可以被其它程序直接訪問,我們可以使用dumpbin命令來查看一個動態庫的導出符號表,例如:

        dumpbin /exports mylib.dll

 

在動態庫中導出符號有兩種方式:

(1) 創建模塊定義文件(.def)(Exporting a symbol from DLL by ordinal):

     優點:可以減小導出符號表的大小;

     缺點:當導出C++函數時,需要使用名字修飾後的符號名。

(2) 使用__declspec(dllexport)關鍵字(Exporting a symbol from DLL by name)。

     優點:不用考慮名字修飾的問題;

     缺點:將符號名存儲在導出符號表中,當導出內容較多時,會導致符號表變得非常龐大。

 

■ 創建動態庫

 

示例1:創建模塊定義文件來導出函數

複製代碼
// foo.cpp
#include <stdio.h>
extern "C" void foo() { printf("foo()\n"); }

// bar.cpp
#include <stdio.h>
extern "C" void bar() { printf("bar()\n"); }

// mylib.def
LIBRARY   mylib
EXPORTS
   foo   @1
   bar   @2
複製代碼

編譯生成動態庫:

        cl foo.cpp bar.cpp /link /dll /def:mylib.def /out:mylib.dll

 

示例2:使用__declspec(dllexport)關鍵字來導出函數

// foo.cpp
__declspec(dllexport) void foo() { printf("foo()\n"); }

// bar.cpp
__declspec(dllexport) void __stdcall bar() { printf("foo()\n"); }

編譯生成動態庫:

        cl foo.cpp bar.cpp /link /dll /out:mylib.dll

 

注:上述兩個示例中,無論使用哪種方式來導出符號,最終都會生成以下三個文件:

※  mylib.dll:動態鏈接庫;

※  mylib.lib:導入庫,後面“使用動態庫”一節中“使用加載時動態鏈接”的示例中會用到。

※  mylib.exp:暫未知。

 

■ 使用動態庫

 

示例1:加載時動態鏈接(Using Load-Time Dynamic Linking)

複製代碼
extern "C" __declspec(dllimport) void foo();
extern "C" void bar(); // __declspec(dllimport)並不是必須的
int main() {
    foo();
    bar();
    return 0;
}
複製代碼

編譯鏈接上述代碼時需要鏈接導入庫mylib.lib,例如:

        cl test.cpp /link /libpath:..\lib mylib.lib

 

示例2:運行時動態鏈接(Using Run-Time Dynamic Linking)

複製代碼
#include <windows.h>
typedef void (FunType)(void);
int main() {
    FunType* pfoo, *pbar;
    HINSTANCE dll = LoadLibrary(TEXT("mylib.dll"));
    pfoo = (FunType*)GetProcAddress(dll, "foo");
    pbar = (FunType*)GetProcAddress(dll, "bar");
    pfoo();
    pbar();
    FreeLibrary(dll);
    return 0;
}
複製代碼

編譯鏈接上述代碼時需要鏈接導入庫mylib.lib,例如:

        cl test.cpp

 

注:在運行上述兩個示例中生成的test.exe可執行文件時,都需要拷貝一份mylib.dll到test目錄下,或者將mylib.dll所在的路徑加入PATH環境變量中,否則操作系統不知道從何處查找mylib.dll。

 

五、關於nmake和Makefile

 

與GNU make類似,VC安裝目錄下還自帶了nmake工具,Makefile的書寫形式也與linux下類似。下面給出一個簡單的示例(示例文件名爲Makefile):

複製代碼
CXX     = cl.exe
LD      = link.exe
default: mylib.dll
mylib.dll: foo.obj bar.obj
    $(LD) foo.obj bar.obj /dll /out:mylib.dll
foo.obj: foo.cpp
    $(CXX) /c foo.cpp
bar.obj: bar.cpp
    $(CXX) /c bar.cpp
複製代碼

執行:  nmake,即可編譯生成mylib.dll動態庫文件。關於nmake的更多說明可參考:“nmake /?”。

 

六、參考文檔

Setting the Path and Environment Variables for Command-Line Builds
    https://msdn.microsoft.com/en-us/library/f2ccy3wt.aspx
    
CL Environment Variables
    https://msdn.microsoft.com/en-us/library/kezkeayy.aspx
    
LINK Environment Variables
    https://msdn.microsoft.com/en-us/library/6y6t9esh.aspx
    
Compiler Options Listed by Category
    https://msdn.microsoft.com/en-us/library/19z1t1wy.aspx
    
Kinds of DLLs
    https://msdn.microsoft.com/en-us/library/9se914de.aspx

Importing and Exporting
    https://msdn.microsoft.com/en-us/library/9h658af8.aspx

Exporting from a DLL
    https://msdn.microsoft.com/en-us/library/z4zxe9k8.aspx
    
Importing into an Application
    https://msdn.microsoft.com/en-us/library/kh1zw7z7.aspx
    
Using Load-Time Dynamic Linking
    https://msdn.microsoft.com/en-us/library/ms686923.aspx
    
Using Run-Time Dynamic Linking
    https://msdn.microsoft.com/en-us/library/ms686944.aspx
    
NMAKE Reference
    https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章