Ⅰ C++編譯後生成.obj、.pch、idb、pdb等文件,我要怎麼做才能將這些文件編譯成dll文件呢
做成DLL 可以使用特定的dll 工程,包括主函數的寫法什麼都有約定,我記得是這樣,而不是把obj等文件轉換變異成dll,動態鏈接的文件是通過編譯輸出一個函數介面,使用時候要動態import
Ⅱ c#使用命令行編譯生成dll文件
C#使用csc.exe編譯程序,csc使用/target:library(縮寫: /t:library)參數生成Dll文件。
其它參數如下:
Visual C# 編譯器選項
- 輸出文件 -
/out:<文件> 指定輸出文件名(默認值: 包含主類的文件或第一個文件的基名稱)
/target:exe 生成控制台可執行文件(默認) (縮寫: /t:exe)
/target:winexe 生成 Windows 可執行文件 (縮寫: /t:winexe)
/target:library 生成庫 (縮寫: /t:library)
/target:mole 生成能添加到其他程序集的模塊 (縮寫: /t:mole)
/target:appcontainerexe 生成 Appcontainer 可執行文件 (縮寫: /t:appcontainerexe)
/target:winmdobj 生成 WinMDExp 使用的 Windows 運行時中間文件 (縮寫: /t:winmdobj)
/doc:<文件> 要生成的 XML 文檔文件
/platform:<字元串> 限制可以在其上運行此代碼的平台: x86、Itanium、x64、arm、anycpu32bitpreferred 或 anycpu。默認值為 anycpu。
- 輸入文件 -
/recurse:<通配符> 根據通配符規范,包括當前目錄和子目錄下的所有文件
/reference:<別名>=<文件> 使用給定的別名從指定的程序集文件引用元數據 (縮寫: /r)
/reference:<文件列表> 從指定的程序集文件引用元數據 (縮寫: /r)
/addmole:<文件列表> 將指定的模塊鏈接到此程序集中
/link:<文件列表> 嵌入指定的互操作程序集文件中的元數據 (縮寫: /l)
- 資源 -
/win32res:<文件> 指定 Win32 資源文件(.res)
/win32icon:<文件> 對輸出使用此圖標
/win32manifest:<文件> 指定 Win32 清單文件(.xml)
/nowin32manifest 不包括默認 Win32 清單
/resource:<資源信息> 嵌入指定的資源 (縮寫: /res)
/linkresource:<資源信息> 將指定的資源鏈接到此程序集 (縮寫: /linkres)
其中 resinfo 的格式是 <file>[,<string name>[,public|private]]
- 代碼生成 -
/debug[+|-] 發出調試信息
/debug:{full|pdbonly} 指定調試類型(「full」是默認類型,可以將調試程序附加到正在運行的程序)
/optimize[+|-] 啟用優化 (縮寫: /o)
- 錯誤和警告 -
/warnaserror[+|-] 將所有警告報告為錯誤
/warnaserror[+|-]:<警告列表> 將特定警告報告為錯誤
/warn:<n> 設置警告等級(0-4) (縮寫: /w)
/nowarn:<警告列表> 禁用特定的警告消息
- 語言 -
/checked[+|-] 生成溢出檢查
/unsafe[+|-] 允許「不安全」代碼
/define:<符號列表> 定義條件編譯符號 (縮寫: /d)
/langversion:<字元串> 指定語言版本模式: ISO-1、ISO-2、3、4、5 或 Default
- 安全性 -
/delaysign[+|-] 僅使用強名稱密鑰的公共部分對程序集進行延遲簽名
/keyfile:<文件> 指定強名稱密鑰文件
/keycontainer:<字元串> 指定強名稱密鑰容器
/highentropyva[+|-] 啟用高平均信息量的 ASLR
- 雜項 -
@<文件> 有關更多選項,請閱讀響應文件
/help 顯示此用法信息 (縮寫: /?)
/nologo 取消編譯器版權信息
/noconfig 不要自動包含 CSC.RSP 文件
- 高級 -
/baseaddress:<地址> 要生成的庫的基址
/bugreport:<文件> 創建「Bug 報告」文件
/codepage:<n> 指定打開源文件時要使用的代碼頁
/utf8output 以 UTF-8 編碼格式輸出編譯器消息
/main:<類型> 指定包含入口點的類型(忽略所有其他可能的入口點) (縮寫: /m)
/fullpaths 編譯器生成完全限定路徑
/filealign:<n> 指定用於輸出文件節的對齊方式
/pdb:<文件> 指定調試信息文件名(默認值: 擴展名為 .pdb 的輸出文件名)
/errorendlocation 輸出每個錯誤的結束位置的行和列
/preferreilang 指定首選輸出語言名稱。
/nostdlib[+|-] 不引用標准庫(mscorlib.dll)
/subsystemversion:<字元串> 指定此程序集的子系統版本
/lib:<文件列表> 指定要在其中搜索引用的附加目錄
/errorreport:<字元串> 指定如何處理內部編譯器錯誤: prompt、send、queue 或 none。默認值為 queue。
/appconfig:<文件> 指定一個包含程序集綁定設置的應用程序配置文件
/moleassemblyname:<字元串> 此模塊所屬程序集的名稱
Ⅲ 如何編譯生成dll
使用VC下的cl和link手工創建dll並實現函數導入
1、創建dll頭文件:
/*
* dllmain.h
*/
#ifndef _DLLMAIN_H
#define _DLLMAIN_H
int getNumber();
#endif
2、創建dll源文件:
/*
* dllmain.c
*/
#include "dllmain.h"
int getNumber()
{
return 10;
}
3、 創建def文件:
; export.def
LIBRARY MY_DLLMAIN ; MY_DLLMAIN 將成為生成的dll的名稱
EXPORTS
getNumber @1 ; 這個名稱即為函數的實際導出名稱 @1為函數的導出編號
4、生成dll文件:
cl dllmain.c /c
link /def:export.def /dll dllmain.obj
這時,工程中已經包含了 dllmain.h dllmain.c export.def dllmain.obj dllmain.lib dllmain.exp MY_DLLMAIN.dll 其中,後4個文件是編譯鏈接過程中生成的文件
5、創建dlltest.c:
/*
* dlltest.c
*/
#include <stdio.h>
#include "dllmain.h" //dll庫的頭文件
#pragma comment(lib,"dllmain.lib") //dllmain.lib即是上一步生成的文件
int main()
{
printf("%dn",getNumber());
}
6、編譯、鏈接dlltest.c
cl dlltest.c /c
link dlltest.obj
注意:這里dllmain.lib和dllmain.h應該和dlltest.c在同一個目錄中。此步的結果將生成 dlltest.exe
7、運行:
dlltest
這時,系統將載入my_dllmain.dll這個動態鏈接庫,將調用其中的getNubmer函數。
Ⅳ 如何編譯預置obj 文件
1. 如何在preloader 中預置obj 文件?
Branch: GB, GB2, ICS, ICS2, JB, JB2
Step1. 首先獲取obj 文件,如果是preloader 已有的Source code 需要預置為obj 文
件,那麼可到如下路徑獲取obj 文件:mediatek/[source]/preloader/out
Step2. 在mediatek/[source]/preloader 目錄下創建文件夾:myobjs,將Step1 中獲取
的obj 文件放到該文件夾中
Step3. 修改mediatek/[source]/preloader/Makefile 文件,在該文件如下Code:
$(D_BIN)/$(PL_IMAGE_NAME).elf:
之前添加:
MYOBJS := $(D_ROOT)/myobj
Step4. 修改mediatek/[source]/preloader/Makefile 中$(D_BIN)/$(PL_IMAGE_NAME).elf
生成命令,將MYOBJS 加入:如下:
$(D_BIN)/$(PL_IMAGE_NAME).elf:
$(LD) --gc-sections -Bstatic -T$(MTK_PATH_PLATFORM)/link_descriptor.ld \
$(wildcard $(D_OBJ)/*) $(wildcard $(MYOBJS)/*) $(SECURITY_LIB) -Map
system.map -o $(D_BIN)/$(PL_IMAGE_NAME).elf
Step5. 如果添加的obj 文件在preloader 中有對應的Source code,還需要修改
Source code 對應的make File 文件,將該Source code 從make File 文件中刪除,以
bmt.c 為例,需要修改medaitek/platform/$platform/preloader/src/drivers/makefile,
將bmt.c 從該文件的MOD_SRC 中刪除
說明:preloader 的Source code 位於如下路徑:
– mediatek/platform/$platform/preloader/
– mediatek/custom/$platform/preloader/
– mediatek/custom/common/preloader/
– mediatek/custom/$porject/preloader/
2. 如何在uboot 中預置obj 文件?
Branch: GB, GB2, ICS, ICS2
Case1. 該obj 文件是從第三方處獲取,在codebase 原本不存在.c 文件,不需要編
譯進某個lib 文件
Step1. 首先獲取obj 文件
Step2. 在bootable/bootloader/uboot 目錄下添加myobjs 文件夾,將Step1 中獲取的
obj 文件放到該文件夾中
Step3. 修改bootable/bootloader/uboot/Makefile,在該文件如下Code:
GEN_UBOOT = \
之前添加:
MYCUSTOMOBJS := $(wildcard myobjs/*)
MEDIATEK CONFIDENTIAL
FOR zhanghui@ vanzotec.com USE ONLY
[email protected],time=2013-10-08 19:27:59,ip=180.166.121.198,doctitle=如何在preloader、uboot、lk、kernel中預置obj文件.docx,company=Vanzo_WCX
Step4. 修改bootable/bootloader/uboot/Makefile,將MYOBJSDIR 中的obj 文件添加
到u-boot 的生成命令中,如下:
GEN_UBOOT = \
UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
sed -n -e 's/.*$(SYM_PREFIX)__u_boot_cmd_.*/-u\1/p'|sort|uniq`;\
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(MYCUSTOMOBJS)
$(__OBJS) \
Case2. 該obj 文件在Codebase 中存在對應的.c 文件,現在需要將.c 文件刪除,替
換為obj 文件
Step1. 編譯生成obj 文件,uboot 編譯生成的obj 文件與其.c 文件位於同級目錄下
Step2.在原先obj 文件目錄下創建一個文件夾prebuilt,並將obj 文件放入其中,同
時將.c 文件刪除
Step3. 修改包含該.c 文件的Makefile 文件(一般與.c 文件位於同級目錄下),將該obj
文件的名稱從OBJS 變數中刪除,同時在該Makefile 中添加一個變數,MYOBJS,將
該obj 文件添加到MYOBJS 中,以
bootable/bootloader/uboot/drivers/video/cfb_console.c 為例,修改該目錄下的
Makefile 文件,將cfb_console.o 從變數OBJS 中刪除,同時添加一行:
MYOBJS := cfb_console.o
Step4. 繼續修改Step3 中的Makefile,將MYOBJS 添加到生成LIB 的命令中,如下:
$(LIB): $(OBJS) $(SOBJS)
$(AR) $(ARFLAGS) $@ $(OBJS) $(MYOBJS) $(SOBJS)
Case3. 某些模塊,Uboot 和kernel 共用同一份Source code,比如LCM,這種情況
需要做特殊處理,以LCM 為例,如下:
Step1. 首先編譯出obj 文件,路徑位於:mediatek/custom/out/$project/uboot/lcm,
同時將要替換的Source code 刪除(mediate/custom/common/kernel/lcm)
Step2. 在mediatek/custom/$project/uboot 下面添加lcm 文件夾,同時將Step1 中獲
取到的obj 文件添加到該文件夾下,同時將原目錄下的obj 文件刪除(這里獲取的
obj 文件一定要是Uboot 目錄下的,不要到kernel 目錄下獲取這些obj 文件)
Step3. 修改mediatek/custom/common/uboot/Makefile 文件,將要替換的obj 文件
名稱從COBJS 刪除:COBJS += $(patsubst %.c, %.o, $(wildcard lcm/*.c))
Step4. 繼續修改Step3 中的Makefile,添加如下Code:MYOBJS := $(addprefix $(obj),
$(wildcard lcm/*.o)),同時將MYOBJS 添加到$(LIB)的編譯命令中,如下:
$(LIB): $(OBJS) $(SOBJS)
$(AR) $(ARFLAGS) $@ $(OBJS) $(MYOBJS) $(SOBJS)
說明:Uboot 的Source Code 主要位於如下路徑:
– bootable/bootloader/uboot/
– mediatek/platform/$platform/uboot/
MEDIATEK CONFIDENTIAL
FOR zhanghui@ vanzotec.com USE ONLY
[email protected],time=2013-10-08 19:27:59,ip=180.166.121.198,doctitle=如何在preloader、uboot、lk、kernel中預置obj文件.docx,company=Vanzo_WCX
– mediatek/custom/$platform/uboot/
– mediatek/custom/common/uboot/
– mediatek/custom/$porject/uboot/
3. 如何在kernel 中預置obj 文件?
Branch:GB, GB2, ICS
– 比如要將xxx.c 用xxx.o 替代編譯
– 先正常編譯出xxx.o
– 然後在xxx.c 所在目錄下創建prebuilt 目錄,將xxx.o 放入
• 注意prebuilt 目錄的目錄名不能修改,否則在clean 階段會被清除
– 修改xxx.c 所在目錄下的Makefile 文件,原本已經有obj-y := xxx.o,在其後
面添加xxx-y := prebuilt/xxx.o 即可
– mediatek/custom/[XXX]/kernel 目錄下對應的Source Code 沒有Makefile 文件,
自己在Source code 對應的同級目錄下添加一個Makefile 即可
Branch:ICS2, JB, JB2
– 比如要將debug.c 用debug.o 替代編譯
– 先正常編譯出debug.o (針對kernel 和lk, uboot 共用Source Code 的情況,
如LCM,這里獲取 到的obj 文件一定要是kernel/out 目錄下的)
– 然後將debug.o 復制到原先debug.c 所在目錄下,並重命名為
debug.o_shipped
– 原先debug.c 同級目錄下的Makefile 不變,依然保持為
obj-y:=debug.o;mediatek/custom/[XXX]/kernel 目錄下對應的Source Code 沒有
Makefile 文件,自己在Source code 對應的同級目錄下添加一個Makefile 即可
– 重新編譯kernel 即可
說明:kernel 的Source code 主要位於如下路徑:
– kernel/
– mediatek/platform/$platform/kernel/
– mediatek/[source]/kernel/
– mediatek/custom/$platform/kernel/
– mediatek/custom/common/kernel/
– mediatek/custom/$porject/kernel/
4. 如何在lk 中預置obj 文件
Branch:JB,JB2
Step1. 在bootable/bootloader/lk/makefile 中添加:MYOBJ :=
Step2. 獲取obj 文件,Codebase 編譯生成的obj 文件位於
bootable/bootloader/lk/build-$project/custom/$project/lk 目錄下
Step3. 將獲取的obj 文件放到與.c 文件相同目錄下;同時可將.c 文件刪除
MEDIATEK CONFIDENTIAL
FOR zhanghui@ vanzotec.com USE ONLY
[email protected],time=2013-10-08 19:27:59,ip=180.166.121.198,doctitle=如何在preloader、uboot、lk、kernel中預置obj文件.docx,company=Vanzo_WCX
Step4. 將相應的.c 文件從包含該.c 文件的rules.mk(一般與.c 文件位於同級目錄)中刪
除
Step5. 將Step3 中添加的.o 文件在rules.mk 中添加到MYOBJ,比如MYOBJ += test.o
Step6. 打開bootable/bootloader/lk/make/build.mk,將MYOBJ 添加到OUTELF 的生
成命令中,如下:
else
$(OUTELF): $(ALLOBJS) $(LINKER_SCRIPT)
@echo linking $@
$(NOECHO)$(LD) $(LDFLAGS) -T $(LINKER_SCRIPT) $(ALLOBJS)
$(MYOBJ) $(LIBGCC) -o $@ endif
Step7. 如果要替換的.c 文件是lk 與kernel 共用的,比如lcm 模塊,那麼Step2 需要
做一下修改:將獲取的obj 文件放到mediatek/custom/$project/lk/lcm 中,同時要
確保這里獲取的obj 文件是bootable/bootloader/lk/build-
$project/custom/$project/lk 目錄下的,不要到kernel/out 目錄下獲取這些obj 文件
說明:lk 的Source Code 主要位於如下路徑:
– mediatek/platform/$platform/lk/
– mediatek/custom/$platform/lk/
– mediatek/custom/common/lk/
– mediatek/custom/$porject/lk/
– bootable/bootloader/lk/
MEDIATEK CONFIDENTIAL
Ⅳ c語言編譯生成的.obj文件解釋下
c語言編譯生成的.obj文件是一種文本文件,程序編譯時生成的中間代碼文件目標文件,一般是程序編譯後的二進制文件,再通過鏈接器和資源文件鏈接就成可執行文件了。OBJ只給出了程序的相對地址,而可執行文件是絕對地址。
obj文件基本結構:OBJ文件不需要任何種文件頭(File Header),盡管經常使用幾行文件信息的注釋作為文件的開頭。
OBJ文件由一行行文本組成,注釋行以符號「#」為開頭,空格和空行可以隨意加到文件中以增加文件的可讀性。有字的行都由一兩個標記字母也就是關鍵字(Keyword)開頭,關鍵字可以說明這一行是什麼樣的數據。
(5)編譯dllobj擴展閱讀:
C編譯的整個過程很復雜,大致可以分為以下階段:
1、預處理階段
在該階段主要完成對源代碼的預處理工作,主要包括對宏定義指令,頭文件包含指令,預定義指令和特殊字元的處理,如對宏定義的替換以及文件頭中所包含的文件中預定義代碼的替換等,總之這步主要完成一些替換工作,輸出是同源文件含義相同但內容不同的文件。
2、編譯、優化階段
編譯就是將第一階段處理得到的文件通過詞法語法分析等轉換為匯編語言。優化包括對中間代碼的優化,如刪除公共表達式,循環優化等;和對目標代碼的生成進行的優化,如如何充分利用機器的寄存器存放有關變數的值,以減少內存訪問次數。
3、匯編階段
將匯編語言翻譯成機器指令。
Ⅵ 怎麼在編譯dll文件時將自己寫的類的構造函數作為介面
這里,我們為了簡要說明DLL的原理,我們決定使用最簡單的編譯環境VC6.0,如下圖,我們先建立一個新的Win32 Dynamic-Link Library工程,名稱為「MyDLL」,在Visual Studio中,你也可以通過建立Win32控制台程序,然後在「應用程序類型」中選擇「DLL」選項,
點擊確定,選擇「一個空的DLL工程」,確定,完成即可。
一個簡單的dll
在第一步我們建立的工程中建立一個源碼文件」dllmain.cpp「,在「dllmain.cpp」中,鍵入如下代碼
#include <Windows.h>#include <stdio.h>BOOL APIENTRY DllMain(HMODULE hMole, DWORD ul_reason_for_call, LPVOID lpReserved){ switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: printf("DLL_PROCESS_ATTACH
"); break; case DLL_THREAD_ATTACH: printf("DLL_THREAD_ATTACH
"); break; case DLL_THREAD_DETACH: printf("DLL_THREAD_DETACH
"); break; case DLL_PROCESS_DETACH: printf("DLL_PROCESS_DETACH
"); break; } return TRUE;}
之後,我們直接編譯,即可以在Debug文件夾下,找到我們生成的dll文件,「MyDLL.dll」,注意,代碼裡面的printf語句,並不是必須的,只是我們用於測試程序時使用。而DllMain函數,是dll的進入/退出函數。
實際上,讓線程調用DLL的方式有兩種,分別是隱式鏈接和顯式鏈接,其目的均是將DLL的文件映像映射進線程的進程的地址空間。我們這里只大概提一下,不做深入研究,如果感興趣,可以去看《Window高級編程指南》的第12章內容。
隱式鏈接調用
隱士地鏈接是將DLL的文件影響映射到進程的地址空間中最常用的方法。當鏈接一個應用程序時,必須制定要鏈接的一組LIB文件。每個LIB文件中包含了DLL文件允許應用程序(或另一個DLL)調用的函數的列表。當鏈接器看到應用程序調用了某個DLL的LIB文件中給出的函數時,它就在生成的EXE文件映像中加入了信息,指出了包含函數的DLL文件的名稱。當操作系統載入EXE文件時,系統查看EXE文件映像的內容來看要裝入哪些DLL,而後試圖將需要的DLL文件映像映射到進程的地址空間中。當尋找DLL時,系統在系列位置查找文件映像。
1.包含EXE映像文件的目錄
2.進程的當前目錄
3.Windows系統的目錄
4.Windows目錄
5.列在PATH環境變數中的目錄
這種方法,一般都是在程序鏈接時控制,反映在鏈接器的配置上,網上大多數講的各種庫的配置,比如OPENGL或者OPENCV等,都是用的這種方法
顯式鏈接調用
這里我們只提到兩種函數,一種是載入函數
返回值HINSTANCE值指出了文件映像映射的虛擬內存地址。如果DLL不能被映進程的地址空間,函數就返回NULL。你可以使用類似於
或者
的方式進行調用,不帶後綴和帶後綴在搜索策略上有區別,這里不再詳解。
顯式釋放DLL
在顯式載入DLL後,在任意時刻可以調用FreeLibrary函數來顯式地從進程的地址空間中解除該文件的映像。
這里,在同一個進程中調用同一個DLL時,實際上還牽涉到一個計數的問題。這里也不在詳解。
線程可以調用GetMoleHandle函數:
來判斷一個DLL是否被映射進進程的地址空間。例如,下面的代碼判斷MyDLL.dll是否已被映射到進程的地址空間,如果沒有,則裝入它:
實際上,還有一些函數,比如 GetMoleFileName用來獲取DLL的全路徑名稱,FreeLibraryAndExitThread來減少DLL的使用計數並退出線程。具體內容還是參見《Window高級編程指南》的第12章內容,此文中不適合講太多的內容以至於讀者不能一下子接受。
DLL的進入與退出函數
說到這里,實際上只是講了幾個常用的函數,這一個小節才是重點。
在上面,我們看到的MyDLL的例子中,有一個DllMain函數,這就是所謂的進入/退出函數。系統在不同的時候調用此函數。這些調用主要提供信息,常常被DLL用來執行進程級或線程級的初始化和清理工作。如果你的DLL不需要這些通知,就不必再你的DLL源代碼中實現此函數,例如,如果你創建的DLL只含有資源,就不必實現該函數。但如果有,則必須像我們上面的格式。
DllMain函數中的ul_reason_for_call參數指出了為什麼調用該函數。該參數有4個可能值: DLL_PROCESS_ATTACH、DLL_THREAD_ATTACH、DLL_THREAD_DETACH、DLL_PROCESS_DETACH。
其中,DLL_PROCESS_ATTACH是在一個DLL首次被映射到進程的地址空間時,系統調用它的DllMain函數,傳遞的ul_reason_for_call參數為DLL_PROCESS_ATTACH。這只有在首次映射時發生。如果一個線程後來為已經映射進來的DLL調用LoadLibrary或LoadLibraryEx,操作系統只會增加DLL的計數,它不會再用DLL_PROCESS_ATTACH調用DLL的DllMain函數。
而DLL_PROCESS_DETACH是在DLL被從進程的地址空間解除映射時,系統調用它的DllMain函數,傳遞的ul_reason_for_call值為DLL_PROCESS_DETACH。我們需要注意的是,當用DLL_PROCESS_ATTACH調用DLL的DllMain函數時,如果返回FALSE,說明初始化不成功,系統仍會用DLL_PROCESS_DETACH調用DLL的DllMain。因此,必須確保沒有清理那些沒有成功初始化的東西。
DLL_THREAD_ATTACH:當進程中創建一個線程時,系統察看當前映射到進程的地址空間中的所有DLL文件映像,並用值DLL_THREAD_ATTACH調用所有的這些DLL的DllMain函數。該通知告訴所有的DLL去執行線程級的初始化。注意,當映射一個新的DLL時,進程中已有的幾個線程在運行,系統不會為已經運行的線程用值DLL_THREAD_ATTACH調用DLL的DllMain函數。
而DLL_THREAD_DETACH,如果線程調用ExitThread來終結(如果讓線程函數返回而不是調用ExitThread,系統會自動調用ExitThread),系統察看當前映射到進程空間的所有DLL文件映像,並用值DLL_THREAD_DETACH來調用所有的DLL的DllMain函數。該通知告訴所有的DLL去執行線程級的清理工作。
這里,我們需要注意的是,如果線程的終結是因為系統中的一個線程調用了TerminateThread,系統就不會再使用DLL_THREAD_DETACH來調用DLL和DllMain函數。這與TerminateProcess一樣,不再萬不得已時,不要使用。
下面,我們貼出《Window高級編程指南》中的兩個圖來說明上述四種參數的調用情況。
好的,介紹了以上的情況,下面,我們來繼續實踐,這次,建立一個新的空的win32控制台工程TestDLL,不再多說,代碼如下:
代碼很好理解,但是前提是,你必須對線程有一定的概念。另外,注意,我們上面編譯的獲得的「MyDLL.dll"必須拷貝到能夠讓我們這個工程找到的地方,也就是上面我們提到的搜索路徑中的一個地方。
這里,我們先貼結果,當然,這只是在我機器上其中某次運行結果。
有了上面我們介紹的知識,這個就不是很難理解,主進程在調用LoadLibrary時,用DLL_PROCESS_ATTACH調用了DllMain函數,而線程創建時,用DLL_THREAD_ATTACH調用了DllMain函數,而由於主線程和子線程並行的原因,可能輸出的時候會有打斷。但是,這樣反而能讓我們更清楚的理解程序。
DllMain與C運行庫
」在前面對DllMain函數的討論中,我假設讀者使用Microsoft的VisualC++編譯器來建立自己的動態鏈接庫。當編寫DLL時,可能會需要一些C運行庫的啟動幫助。比方說,你建立的DLL中包含一個全局變數,它是一個C++類的實例。在DLL能使用該全局變數之前,必須調用了它的構造函數——這就是C運行時庫的DLL啟動代碼的工作。「
上面一段話也就是告訴我們,實際上,當DLL文件被映射到進程的地址空間中時,系統實際上調用的並不直接是DllMain函數,而是另外一個函數,需要先完成一些初始化工作,實際上,這個函數便是_DllMainCRTStartup函數。該函數初始化了C運行時庫,並確保當它接收到DLL_PROCESS_ATTACH通知時,所有的全局或靜態C++對象都被創建了。為了解釋這點,我們准備對以上的MyDLL.dll代碼進行一些修改,如下,其中增加了一個類A,以及定義了一個全局變數a。
編譯為DLL後,替換掉原來的的MyDLL.dll,可以直接運行TestDLL.exe,可以看到,在DLL_PROCESS_ATTACH調用了類A的構造函數,而在DLL_PROCESS_DETACH之後,調用了類A的析構函數。
在經過以上的證實之後,實際上,我們也可以理解為什麼我們之前說不必在DLL的源代碼中實現一個DllMain函數,因為如果你沒有DllMain函數,C運行庫有它自己的一個DllMain函數。鏈接器鏈接DLL時,如果它在DLL的OBJ文件中找不到一個DllMain函數,就會鏈接C運行時的DllMain函數的實現。
從DLL中輸出函數和變數
當創建一個DLL時,實際上創建了一組能讓EXE或其他DLL調用的一組函數。當一個DLL函數能被EXE或另一個DLL文件使用時,它被稱為輸出了(exported)。
這里,我們只說一種方法,即用_declspec(dllexport) 的方法。當然,也可以用def文件,但是,我們最常用的還是 _declspec(dllexport)的方法。什麼是輸出函數與輸出變數。簡單的來說,你開發一個dll之後,一般都是想讓別的程序員開發的應用程序或dll調用,而輸出變數就是為了完成這件事情的。想一想你在開發windows應用程序時調用的各種api,實際上,大部分也都是在dll中封起來的函數。廢話不多少,上代碼,我們還用以上的兩個工程,MyDLL工程和TestDLL工程,但是這次,大幅度修改了代碼,我們刪去了DllMain函數,增加了一個函數,和一個全局整形變數,同時也修改了類A,這次,我們准備先來點正常。
其中MyDLL工程中的代碼(注意,這里代碼沒有按照頭文件與源代碼的分離,僅僅為了更好理解知識,在工程項目中請勿模仿)
這里需要注意的是 _declspec(dllexport) ,代表了dll導出的意思,編譯組建一下,你會發現,這次,我們得到的不在單單是一個dll文件,還有MyDLL.lib和MyDLL.exp文件,其中,這些文件的意思,請參見此文.dll,.lib,.def 和 .exp文件
之後,我們這里決定先用上面提到的隱式鏈接的方法進行調用。我們需要先配置一下我們的TestDLL工程,配置方法如下,選擇「工程」->「設置」彈出一下窗口,選擇「鏈接」標簽頁,然後按照我們下面圈紅的部分,添加上「MyDLL.lib」文件以及相應的附加庫路徑(及lib所在的位置),這里我們為了方便起見,把MyDLL工程的Debug文件夾下生成的dll與lib均拷貝到了TestDLL的Debug文件夾下。
之後,修改TestDLL工程的源代碼如下,(這里,我們再次聲明,我們沒有用頭文件的方式,非常不建議這樣用。)
這里,注意的是,在導出的位置,我們用的是_declspec(dllexport),而在這里導入的時候,我們聲明的時候,用的是_declspec(dllimport),這個例子當中,我們分別導出了變數,函數,類。讀者僅僅需要注意的是導入和導出關鍵字的使用。運行結果如下:
另外,大家可能對為什麼要用 extern "C"括起來表示好奇,這里可以先推後考慮,我們在說到如何顯式載入該文件時會提到。
這里,建議大家一下,如果將類A聲明部分的構造函數刪除,即改為
想想會發生什麼,不妨動手試一下,這又是為什麼?如果還理解,說明你可能對動態鏈接庫的lib文件理解不夠透徹,可以再讀一讀我們上面說的那篇文章.dll,.lib,.def 和 .exp文件。
到這里,實際上我們已經大致說完動態鏈接庫的相關內容,但是,既然我們上面提到了顯式調用,那麼,想過沒有如果才能顯式調用我們現在的這個dll文件,還有,那個extern 「C」到底是什麼,這里,我們還是先推薦一篇文章,extern "C"的用法解析
看了這么多,快瘋了吧? 有點兒接受不了,告訴你,筆者也寫的快瘋了,come on ! 動手幹活,先找一個工具,mpbin,一般在你VC或VS的安裝目錄的某個bin文件夾下,搜一下就出來了(筆者的VC下的mpbin不能用,所以用VS2013下的mpbin了,但是,應該變化不大,如果不同,還請見諒),然後再cmd中運行,如筆者一樣以下的截圖一樣,加上-EXPORTS參數,如下,
之後,去掉extern 「C」,如下,
再次編譯成DLL,同樣使用mpbin工具,再次運行,如下,
兩幅圖對比著看,主要看我們用紅框圈出來的部分,這樣,特別是一會兒我們准備調用的Add方法,很容易發現,有extern "C"的直接為「Add」,而去掉extern "C"之後,變成了「?Add@@YAHHH@Z」,後面那一場串東西,實際上就是編譯器為了解決重載問題加入的東西。
這里,我們為了調用方便,我們使用帶有 extern "C"的版本,TestDLL代碼如下:
之後,最後一幅圖,運行效果,
代碼中主要用到的就是 GetProcAddress函數,來獲取函數指針,之後通過函數指針調用Add函數,如果感興趣的話,可以將pAdd的值輸出出來,看一下,是否和我們用mpbin看到的相互一致。而我們用extern "C"的原因在於,如果不使用的話,我們在調用GetProcAddress函數時,填第二個參數,將會令人頭疼。
好了,結束。順便一提,此文實際上還說的比較簡略,如果想深入研究,還是找本書,細細研究幾遍的好。
閱讀更多登錄後自動展開
Ⅶ c怎麼生成dll文件
用vc 6.0 下的cl.exe 和 link.exe工具,請讀下文: 聲明:下面這篇文章不是我寫的,源自:一個叫,有容乃大 的博客 如何手工編寫動態鏈接庫(windows dll) 1.本例介紹在命令行(Console)環境下製作dll的方法 2.讀者動手前,請確保在windows中安裝有編譯、鏈接工具和必要的函數庫文件。 3.本例使用C語言實現. 4.本例中使用路徑均為我機器上的絕對路徑,讀者需根據實際情況調整。 工具要求: Microsoft的編譯器cl.exe MIcrosoft鏈接器link.exe dll製作步驟: 1.編寫dll函數實現源代碼hello.c #include int say_hello(char* name) { printf( "hello %s\n ", name); return 1; } 2.編寫dll函數輸出定義文件hello.def. LIBRARY hello EXPORTS say_hello @1 3.編譯dll源碼,生成dll,lib文件. 3.1 新建命令行窗口 3.2 設置PATH INCLUDE LIB 3個環境變數. SET PATH=K:\vcnet\vc7\bin;%PATH% SET INCLUDE=K:\vcnet\vc7\include;%INCLUDE% SET LIB=K:\vsnet\Vc7\lib;%LIB% 3.3 編譯hello.c cd K:\Source\dllsample (hello.c和hello.def所在目錄) cl /c hello.c 3.4 鏈接hello.obj,生成hello.dll,hello.lib兩個文件. link /def:hello.def /dll hello.obj 4.測試dll函數. 4.1 編寫測試代碼 test.c extern int say_hello(char* name); int main(int argc,char** argv) { say_hello( "robbie "); return 0; } 4.2 編譯測試代碼test.c cl /c test.c 4.3 鏈接test.obj和 hello.lib,生成可執行文件test.exe link test.obj hello.lib 4.4 運行test.exe,屏幕輸出: hello robbie 至此,一個dll構造完畢. 下面是我自己的一點補充: 如果要在c++下,或者win32 mfc下使用標准c寫的dll,必須把上面的聲明 extern int say_hello(char* name);改成:extern "C " int say_hello(char* name);
Ⅷ 為什麼我的易語言靜態編譯時編出個obj類型的文件
任何
編譯器
都會生成obj文件,稱之為目標代碼文件。該文件通過連接程序(link)生成最後可執行文件(exe)或動態鏈接庫文件(dll)。你可能只進行了
編譯
,還要執行連接才能得到可執行文件或動態鏈接庫文件。
Ⅸ C語言中main主函數怎樣調用一個.obj文件和.dll文件,有幾種情況
obj 是編譯是產生的中間文件,是不可調用的。
dll 僅限於 window 平台,有平台相關性, 標准 C 是無法調用 dll 的。如果是用 VC ,那就很簡單了,調一個載入函數就可以了。
Ⅹ obj 和 dll哪個保密更好
obj里還保留著大量鏈接器鏈接的時候要使用的符號,而這些很大部分是人類可讀的
dll如果沒有調試信息那麼就剩導出符號了