c++ 生成dll文件並調用-轉


.h(頭文件) .lib(庫文件) .dll(動態鏈接庫文件) 之間的關系和作用的區分

 

.h頭文件是編譯時必須的,lib是鏈接時需要的,dll是運行時需要的。

附加依賴項的是.lib不是.dll,若生成了DLL,則肯定也生成 LIB文件。如果要完成源代碼的編譯和鏈接,有頭文件和lib就夠了。如果也使動態連接的程序運行起來,有dll就夠了(放在Debug文件夾里)。在開發和調試階段,當然最好都有。

.h .lib .dll三者的關系是:

H文件作用是:聲明函數接口

DLL文件作用是: 函數可執行代碼

當我們在自己的程序中引用了一個H文件里的函數,編鏈器怎么知道該調用哪個DLL文件呢?這就是LIB文件的作用: 告訴鏈接器 調用的函數在哪個DLL中,函數執行代碼在DLL中的什么位置,這也就是為什么需要附加依賴項 .LIB文件,它起到橋梁的作用。如果生成靜態庫文件,則沒有DLL ,只有lib,這時函數可執行代碼部分也在lib文件中

目前以lib后綴的庫有兩種,一種為靜態鏈接庫(Static Libary,以下簡稱“靜態庫”),另一種為動態連接庫(DLL,以下簡稱“動態庫”)的導入庫(Import Libary,以下簡稱“導入庫”)。靜態庫是一個或者多個obj文件的打包,所以有人干脆把從obj文件生成lib的過程稱為Archive,即合並到一起。比如你鏈接一個靜態庫,如果其中有錯,它會准確的找到是哪個obj有錯,即靜態lib只是殼子。動態庫一般會有對應的導入庫,方便程序靜態載入動態鏈接庫,否則你可能就需要自己LoadLibary調入DLL文件,然后再手工GetProcAddress獲得對應函數了。有了導入庫,你只需要鏈接導入庫后按照頭文件函數接口的聲明調用函數就可以了。導入庫和靜態庫的區別很大,他們實質是不一樣的東西。靜態庫本身就包含了實際執行代碼、符號表等等,而對於導入庫而言,其實際的執行代碼位於動態庫中,導入庫只包含了地址符號表等,確保程序找到對應函數的一些基本地址信息。

一般的動態庫程序有lib文件和dll文件。lib文件是必須在編譯期就連接到應用程序中的,而dll文件是運行期才會被調用的。如果有dll文件,那么對應的lib文件一般是一些索引信息,具體的實現在dll文件中。如果只有lib文件,那么這個lib文件是靜態編譯出來的,索引和實現都在其中。靜態編譯的lib文件有好處:給用戶安裝時就不需要再掛動態庫了。但也有缺點,就是導致應用程序比較大,而且失去了動態庫的靈活性,在版本升級時,同時要發布新的應用程序才行。在動態庫的情況下,有兩個文件,而一個是引入庫(.LIB)文件,一個是DLL文件,引入庫文件包含被DLL導出的函數的名稱和位置,DLL包含實際的函數和數據,應用程序使用LIB文件鏈接到所需要使用的DLL文件,庫中的函數和數據並不復制到可執行文件中,因此在應用程序的可執行文件中,存放的不是被調用的函數代碼,而是DLL中所要調用的函數的內存地址,這樣當一個或多個應用程序運行是再把程序代碼和被調用的函數代碼鏈接起來,從而節省了內存資源。從上面的說明可以看出,DLL和.LIB文件必須隨應用程序一起發行,否則應用程序將會產生錯誤。

-------------------------------------------------------------------------------------
靜態鏈接庫(Lib)與動態鏈接庫(DLL)的區別 

     靜態連接庫就是把(lib)文件中用到的函數代碼直接鏈接進目標程序,程序運行的時候不再需要其它的庫文件;動態鏈接就是把調用的函數所在文件模塊(DLL)和調用函數在文件中的位置等信息鏈接進目標程序,程序運行的時候再從DLL中尋找相應函數代碼,因此需要相應DLL文件的支持。

靜態鏈接庫與動態鏈接庫都是共享代碼的方式,如果采用靜態鏈接庫,則無論你願不願意,lib 中的指令都全部被直接包含在最終生成的 EXE 文件中了。但是若使用 DLL,該 DLL 不必被包含在最終 EXE 文件中,EXE 文件執行時可以“動態”地引用和卸載這個與 EXE 獨立的 DLL 文件。靜態鏈接庫和動態鏈接庫的另外一個區別在於靜態鏈接庫中不能再包含其他的動態鏈接庫或者靜態庫,而在動態鏈接庫中還可以再包含其他的動態或靜態鏈接庫。


“每一個lib文件就是若干函數(假設只有函數)的定義” 
lib庫有兩種,一種是包含了函數所在DLL文件和文件中函數位置的信息,稱為導出庫;一種是包含函數代碼本身,一般現有的DLL,用的是前一種庫;以前在DOS下的TC/BC等,是后一種庫。包含函數原型聲明的,是頭文件(.h)。 

“通過#include包含這些函數聲明的頭文件后,我們的應用程序就可以使用lib文件中的函數”

還要指定編譯器鏈接相應的庫文件。在IDE環境下,一般是一次指定所有用到的庫文件,編譯器自己尋找每個模塊需要的庫;在命令行編譯環境下,需要指定每個模塊調用的庫。 

“那他和直接給出那個函數定義的文件,比如.cpp文件,和頭文件有什么區別,靜態鏈接庫有什么用” 
cpp文件是源代碼,庫文件是編譯后的二進制代碼,比如你可以調用Windows的API,但是不能看到其源代碼一樣。 

“還有不明白的是,靜態鏈接庫中的lib文件只要用到,則整個lib文件的內容都放進了exe文件中,那它是被編譯進去還是鏈接的時候連接進去的呢?” 
是在鏈接的時候將lib鏈接到目標代碼中。

靜態鏈接庫(Lib)
在VC++6.0中new一個名稱為libTest的static library工程,

並新建lib.h和lib.cpp兩個文件,lib.h和lib.cpp的源代碼如下:

//文件:lib.h
#ifndef LIB_H
#define LIB_H
extern "C" int add(int x,int y);   //聲明為C編譯、連接方式的外部函數
#endif

//文件:lib.cpp
#include "lib.h"
int add(int x,int y)
{
return x + y;
}


  編譯這個工程就得到了一個.lib文件,這個文件就是一個函數庫,它提供了add的功能。將頭文件和.lib文件提交給用戶后,用戶就可以直接使用其中的add函數了。

  標准Turbo C2.0中的C庫函數(我們用來的scanf、printf、memcpy、strcpy等)就來自這種靜態庫。

下面來看看怎么使用這個庫,在libTest工程所在的工作區內new一個libCall工程。libCall工程僅包含一個main.cpp文件,它演示了靜態鏈接庫的調用方法,其源代碼如下:

#include <stdio.h>
#include "..\lib.h"//不可丟失
#pragma comment( lib, "..\\debug\\libTest.lib" )  //指定與靜態庫一起連接
int main(int argc, char* argv[])
{
     printf( "2 + 3 = %d", add( 2, 3 ) );
}
  靜態鏈接庫的調用就是這么簡單,或許我們每天都在用,可是我們沒有明白這個概念。代碼中#pragma comment( lib , "..\\debug\\libTest.lib" )的意思是指本文件生成的.obj文件應與libTest.lib一起連接

-------------------------------------------------------------------------------------------
用VC++生成靜態庫文件
今天閑着沒事做,自己寫了一點小筆記,不知道對於新手有沒用,高手就不用看了,作為新手的我斗膽來發表一個筆記,就是靜態庫文件的封裝過程,使用VC++6.0編寫,下面是正文,也許我的用語並不專業

以前我們寫C/C++源文件的時候,都是先將各個寫好的源文件編譯,編譯生成的是目標文件機器碼,即.obj文件.(目標文件的擴展名不一定是.obj文件).

我們調用的標准C/C++函數機器碼實際被封裝於標准C/C++靜態庫文件中的.即那些擴展名為.lib的文件中.

最后鏈接器將我們編譯的各個目標文件里的機器碼和靜態庫(標准C/C++庫)中的函數機器碼鏈接到一起形成一個擴展名為.exe的可執行文件模塊.

在這里我們敘述將C/C++源文件編譯鏈接成一個靜態庫文件,但它不是可執行模塊,它體內含有可執行機器碼

靜態庫文件就像一個倉庫或者容器,里面封裝了一些可執行機器碼.這些機器碼是我們用程序設計語言,比如C/C++源文件編譯后生成的機器碼.

一.下面將討論將C/C++源文件編譯並鏈接成一個靜態庫文件的過程,

在VC++6.0中選擇File-New-Win32 Static Library,寫好工程名創建好工作空間后再選擇菜單中New-File來為工程添加C或者C++ 源文件.

假如我們為該工程添加了一個名為lib_c.c和一個名為lib_cpp.cpp的源文件

//lib_c.c中的內容

extern int Add(int x,int y) //該函數是一個外部函數,任何文件都可以訪問它

{
    return x+y;

}

extern int data_c
//這是一個外部全局變量,任何文件可以訪問它

//lib_cpp.cpp中的內容

extern “C” int
        reduce(int x,int y)//這里加了個”C”表示允許C源文件訪問這個C++函數代碼

{
    return x-y;

}

extern “C” int data_cpp=2;

注意以下幾點

(1)當“extern”關鍵字修飾在函數或全局變量的定義中時,表示該函數或全局變量任何文件可以訪問,“extern”關鍵字可以省略不寫,缺省下就是”extern”

  當“extern”關鍵字修飾在函數聲明或全局變量聲明中時,表示限定當前文件只能引用用“extern”關鍵字修飾定義的函數或全局變量.

(2)當”static”關鍵字修飾在函數或全局變量的定義中時,表示該函數或全局變量只能由本文件中加了”static”關鍵字修飾的函數聲明或全局變量聲明來引用.

  當”static”關鍵字修飾在函數聲明或全局變量聲明中時,表示限定當前文件只能引用用“static”關鍵字修飾定義的函數或全局變量.

(3)在CPP源文件的函數和全局變量定義中加了個”C”表示允許C源文件訪問該函數和全局變量.如果是C++源文件訪它們的話則可加可不加.注意這”C”要大寫.

接下來就要將寫好的C/C++源文件進行編譯和鏈接,最后會生成一個擴展名為.lib的文件.該文件就是靜態庫文件了,該靜態庫文件是不能直接運行的,我們所編譯的C/C++源文件的機器碼就已經被封裝進這個用VC++6.0創建的靜態庫文件里面去了.

二.如何將編寫好的靜態庫文件像使用C/C++標准庫那樣使用,下面將繼續討論

1.用VC++6.0新建一個工程名為TEST,添加一個名為TEST.c的源文件到該工程,因為我們將測試一下,將我們編寫的庫文件里的函數或者全局變量的機器碼鏈接到我們這個TEST.c源文件中去,假設我們生成的庫文件名為TEST.lib,先拷貝如下范例代碼到TEST.c中

//TEST.c

#include <stdio.h>

extern int 
Add(int x,int y); //當前文件只能訪問“extern”關鍵字修飾定義的Add函數

extern int
reduce(int x,int y);// //當前文件只能訪問“extern”關鍵字修飾定義的reduce函數

#pragma comment(lib,"TEST.lib") //指示鏈接器到字符串所表示的文件路徑中去找庫文件

int main()

{
    printf("%d\n",Add(2,3));
    printf("%d\n",reduce(3,2));
    return 0;

}

這里我們要聲明靜態庫中已知的函數或全局變量的聲明

#pragma comment(lib,"TEST.lib")這條指令告訴鏈接器到字符串所表示的路徑下去找庫文件,這里我將庫文件放到了當前工程目錄下.也可以不寫這句.

還有一種方法,可以直接在VC++6.0中設置依次選擇tools、options、directories、library files菜單或選項,填入庫文件路徑(只鍵入庫文件所在目錄路徑而不能輸入庫文件名),這只是告訴鏈接器庫文件所在目錄的路徑,還沒告訴鏈接器庫文件名,方法是VC++6.0中設置依次選擇project-settings-link 在object/library modules: 這欄輸入庫文件名字然后就OK了

2.當用C++源文件的目標文件和庫文件的代碼鏈接時有一點小改變,這里就不浪費口舌了,假設我們新建了一個工程並添加了一個名為TEST.CPP的源文件,拷貝如下范例代碼到TEST.CPP中

//TEST.cpp

#include <stdio.h>

extern “C” int 
       Add(int x,int y); //表示引用的是C函數代碼

extern int
      reduce(int x,int y);

#pragma comment(lib,"TEST.lib")

int main()

{
    printf("%d\n",Add(2,3));
    printf("%d\n",reduce(3,2));
    return 0;

}

在這個C++源文件里引用C函數代碼同樣要加個”C”,但是在C源文件引用C++函數代碼不能加”C++”,編譯會報錯,只能在C++文件函數定義中加”C”.

只有C++才支持這種引用方式,也許因為只有C++兼容C而沒有C兼容C++這一原則.

 

 

.h用於編譯階段的審核,如在math.h中有函數聲明:
int abs(int);
但是在使用中寫為
#include <math.h>
...abs(3,5);
編譯器階段就會檢測出錯誤。

.dll用於運行階段,如調用SetWindowText()函數等,需要在user32.dll中找到該函數。DLL可以簡單認為是一種包含供別人調用的函數和資源的可執行文件。

.lib用於鏈接階段,在鏈接各部分目標文件(通常為.obj)到可執行文件(通常為.exe)過程中,需要在.lib文件中查找動態調用函數(一般為DLL中的函數)的地址信息,此時需要在lib文件中查找,如查找SetWindowText()函數的地址偏移就需要查找user32.lib文件。(.lib也可用於靜態鏈接的內嵌代碼)

 

lib和dll文件的區別和聯系    
   
  .dll是在你的程序運行的時候才連接的文件,因此它是一種比較小的可執行文件格式,.dll還有其他的文件格式如.ocx等,所有的.dll文件都是可執行。  
   
  .lib是在你的程序編譯連接的時候就連接的文件,因此你必須告知編譯器連接的lib文件在那里。一般來說,與動態連接文件相對比,lib文件也被稱為是靜態連接庫。當你把代碼編譯成這幾種格式的文件時,在以后他們就不可能再被更改。如果你想使用lib文件,就必須:  
  1?   包含一個對應的頭文件告知編譯器lib文件里面的具體內容  
  2?   設置lib文件允許編譯器去查找已經編譯好的二進制代碼  
   
  如果你想從你的代碼分離一個dll文件出來代替靜態連接庫,仍然需要一個lib文件。這個lib文件將被連接到程序告訴操作系統在運行的時候你想用到什么dll文件,一般情況下,lib文件里有相應的dll文件的名字和一個指明dll輸出函數入口的順序表。如果不想用lib文件或者是沒有lib文件,可以用WIN32   API函數LoadLibrary、GetProcAddress。事實上,我們可以在Visual   C++   IDE中以二進制形式打開lib文件,大多情況下會看到ASCII碼格式的C++函數或一些重載操作的函數名字。  
   
  一般我們最主要的關於lib文件的麻煩就是出現unresolved   symble   這類錯誤,這就是lib文件連接錯誤或者沒有包含.c、.cpp文件到工程里,關鍵是如果在C++工程里用了C語言寫的lib文件,就必需要這樣包含:  
  extern   "C"  
  {  
  #include   "myheader.h"  
  }  
  這是因為C語言寫的lib文件沒有C++所必須的名字破壞,C函數不能被重載,因此連接器會出錯。

 

C語言中有一些函數不需要進行編譯,有一些函數也可以在多個文件中使用。一般來說,這些函數都會執行一些標准任務,如數據庫輸入/輸出操作或屏幕控制等。可以事先對這些函數進行編譯,然后將它們放置在一些特殊的目標代碼文件中,這些目標代碼文件就稱為庫。庫文件中的函數可以通過連接程序與應用程序進行連接。這樣就不必在每次開發程序時都對這些通用的函數進行編譯了。    
   
    不同類型的應用程序將會使用不同的函數庫。例如:libdbm庫中組包含了對數據庫文件進行訪問的dbm函數,需要對數據庫進行操作的程序就會與該庫進行連接。數學應用程序將使用數學庫libm,X-Windows應用程序將使用Xlib庫,libX11。另外,所有的程序都將使用標准的C函數庫。libc,該庫中包含了諸好內存管理或輸入輸出操作的基本函數,這些庫都存放在/usr/lib這些系統公用的目錄中,系統中的任何用戶都可以利用這些庫。當然用戶也可以建立自己專用的庫函數,供自己或其它指定的人員使用。    
   
    庫可以有三種使用的形式:靜態、共享和動態。靜態庫的代碼在編譯時就已連接到開發人員開發的應用程序中,而共享庫只是在程序開始運行時才載入,在編譯時,只是簡單地指定需要使用的庫函數。動態庫則是共享庫的另一種變化形式。動態庫也是在程序運行時載入,但與共享庫不同的是,使用的庫函數不是在程序運行開始,而是在程序中的語句需要使用該函數時才載入。動態庫可以在程序運行期間釋放動態庫所占用的內存,騰出空間供其它程序使用。由於共享庫和動態庫並沒有在程序中包括庫函數的內容,只是包含了對庫函數的引用,因此代碼的規模比較小。

 

lib是靜態庫,dll一般是動態鏈接庫(也有可能是別的)

比如要編譯個exe,lib在編譯的時候就會被編譯到exe里,作為程序的一部分

而dll是不被編譯進去,是運行的時候才調入的(可能是exe剛運行就調入,也可能運行了一半才調入)

用法,lib需要個.lib文件和一個.h文件,程序正常使用.h的函數,在鏈接選項里加入.lib文件就ok

dll用法有2種,一是 .h + .lib + .dll的,用法和前面一樣,中間的lib是個中轉,運行的時候會調用dll
二是:直接用dll,需要知道dll的函數定義,用LoadLibrary和GetProcAddress把函數指針取出來,看msdn的例子吧


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM