日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

win32 DLL 学习总结

發(fā)布時間:2025/4/14 编程问答 65 豆豆
生活随笔 收集整理的這篇文章主要介紹了 win32 DLL 学习总结 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

DLL的開發(fā)與調(diào)用(一)——創(chuàng)建導出函數(shù)的Win32 DLL

http://www.cnblogs.com/Pickuper/articles/2053745.html

Visual C++6.0 中可實現(xiàn)的DLL

? ? ? ? ?Visual C++6.0 支持自動生成Win32 DLL和MFC AppWizard DLL兩種,其中Win32 DLL不使用MFC類庫,其導出的函數(shù)是標準的C接口,能夠被非MFC和MFC的應用程序調(diào)用,應用范圍更廣泛。所以下面就介紹Win32 DLL的開發(fā)。

創(chuàng)建導出函數(shù)的Win32 DLL
1、啟動Visual C++6.0,利用AppWizard創(chuàng)建一個“Win32 Dynamic-Link Library”類型的工程,工程名為SayHello。采用默認設置,即創(chuàng)建一個Win32 DLL的空項目。
2、為DLL工程添加頭文件SayHello.h和源文件SayHello.cpp。在頭文件SayHello.h中,聲明DLL的導出函數(shù)Say和Sum,分別用來顯示"Hello,World!"和求和。聲明代碼如下:

//SayHello.h
//
/*
extern "C"修飾詞的作用是使C++編譯器以C語言的方式對這個函數(shù)進行處理,以便供其他語言所用。
*/
extern "C" void _declspec(dllexport)Say(char* szWords,int nLen); ? ? ? ? ? ? ? ? ? ? ? ? ? //聲明Say導出函數(shù)
extern "C" float _declspec(dllexport)Sum(float fNum1,float fNum2); ? //聲明Sum導出函數(shù)
在源文件SayHello.cpp中添加函數(shù)Say和Sum的實現(xiàn)代碼,代碼如下:

//SayHello.cpp
//
#include <string.h>
#include "SayHello.h"

void Say(char* szWords,int nLen)
{
? ? strcpy(szWords,"Hello,World!");
? ? strcat(szWords,"\0");
}

float Sum(float fNum1,float fNum2)
{
? ? return fNum1+fNum2;
}
3、【F7】鍵編譯生成DLL。此時在工程的Debug文件夾下生成實際代碼文件SayHello.dll和導入庫文件SayHello.lib。
4、從DLL中導出函數(shù)有兩種方法,一種是使用_declspec(dllexport)關(guān)鍵字,如SayHello.h中所示;一種是添加.def文件(值得注意的是,添加的文件類型是文本文件,且名稱應輸入SayHello.def),代碼如下:

;SayHello.def
;

LIBRARY "SayHello"
DESCRIPTION "導出DLL中的函數(shù)"
EXPORTS
? ? Say ?@1
? ? Sum ?@2
5、加載DLL分為靜態(tài)加載和動態(tài)加載。動態(tài)加載(運行時動態(tài)鏈接,也叫顯示鏈接)DLL是通過LoadLibrary、GetProcAddress和FreeLibrary這3個API函數(shù)進行的。調(diào)用如下:

typedef void(*SAY)(char*,int);
SAY Say;
typedef float(*SUM)(float,float);
SUM Sum;
HINSTANCE hdll;
hdll=LoadLibrary("..\\..\\SayHello\\Debug\\SayHello.dll");
if(hdll!=NULL)
{
? ? //GetProcAddress函數(shù)獲得獲得獲得DLL導出函數(shù)地址
? ? Say=(SAY)GetProcAddress(hdll,"Say");
? ? Sum=(SUM)GetProcAddress(hdll,"Sum");
}
else
{
? ? AfxMessageBox("無法加載DLL!");
? ? return;
}
UpdateData(TRUE);
const int Len=20;
char p[Len];
Say(p,Len);
m_strDispHello.Format("%s",p);
m_fResult=Sum(m_fNum1,m_fNum2);
UpdateData(FALSE);
FreeLibrary(hdll);
靜態(tài)加載(加載時動態(tài)鏈接,也叫隱式鏈接)DLL是由編譯系統(tǒng)完成對DLL的加載和應用程序結(jié)束時對DLL的卸載,需要將DLL的引用庫文件(.lib)與應用程序進行靜態(tài)鏈接。調(diào)用如下:
#pragma comment(lib,"SayHello.lib")
extern "C" _declspec(dllimport) void Say(char* szWords,int nLen);?
extern "C" _declspec(dllimport) float Sum(float fNum1,float fNum2);
此時就使用Say和Sum函數(shù)了。
========

Win32 動態(tài)鏈接(dll)簡單示例

http://blog.csdn.net/weiwenhp/article/details/8710811

dll(dynamic link library)動態(tài)鏈接庫相當于是把一些函數(shù)或者類啊編譯成源碼.不過它不可執(zhí)行.只是當被其他exe或dll調(diào)用到時才被加載到內(nèi)存中.像windows那些API都是放到一些dll文件中.比如kernel32.dll,它包含管理內(nèi)存,進程,線程的一些函數(shù).User32.dll包含用于執(zhí)行用戶界面任務的函數(shù).

而當我們寫代碼要用到dll中的函數(shù)時,在編譯階段一般只要個lib文件,里面有dll中的函數(shù)和類的描述信息,但沒有實現(xiàn)代碼信息.

DLL的創(chuàng)建
下面來看一個創(chuàng)建dll的簡單示例
創(chuàng)建 Win32 Project-->application type選DLL.
project名字就取DllTest.創(chuàng)建好項目后我們會看到自動生成了.dllmain.cpp和DllTest.cpp,前一個文件不用去動它.
我們就在DllTest.cpp文件中添加如下內(nèi)容

_declspec(dllexport) int multiply(int one , int two) //返回兩數(shù)相乘的積 { return one*two; }
編譯下這個項目.你會在目錄下面看到DllTest.dll 和 DllTest.lib 這兩文件.等會其他項目中要用它倆.

DLL的使用
新建一個簡單的Win32 console application 項目.把上面的DllTest.dll和DllTest.lib兩文件拷到項目目錄下.再添加如下代碼

#include <iostream> using namespace std; #pragma comment(lib, "./DllTest.lib") int multiply( int one , int two) ; //函數(shù)聲明,函數(shù)定義最終是去調(diào)用DllTest.dll中的代碼了.//另外最好是寫成這樣_declspec(dllimport) int multiply( int one , int two) int main() {int ret = multiply( 4,5);cout<<ret; //20return 0; }
當然了,如果你嫌#pragma comment(lib, "./DllTest.lib")這樣寫麻煩,也不不寫,而是在項目的property page -->Linker -->Input -->Additional Dependencies里面敲入DllTest.lib

Dll創(chuàng)建示例2(帶類的dll)
上面是比較簡單的再來看個復雜點的.

跟前面一樣還是一樣先創(chuàng)建一個win32 dll項目名為DllTest.然后添加class Arwen.

/Arwen.h中內(nèi)容/
#pragma once
#include <iostream>
#define DLL_API _declspec(dllimport)
class DLL_API Arwen{
public:
int age;
void Fun();
};
?
//Arwen.cpp中內(nèi)容/
#include "StdAfx.h"
#include "Arwen.h"
#define DLL_API _declspec(dllexport)
void Arwen::Fun()
{
std::cout<<"my age is "<<age;
}?

使用DLL

新建一個win32 console application ,把DllTest.dll和Dll.lib拷貝過去.另外把頭文件Arwen.h也拷過去.

#include "Arwen.h"
#pragma comment(lib, "./DllTest.lib")
int main()
{
Arwen an;
an.age = 25;
an.Fun();
?return 0;
}

?動態(tài)加載DLL
前面講的是靜態(tài)加載DLL,現(xiàn)在瞧下怎么動態(tài)加載.

#include <windows.h>

typedef int( *pFun) (int a, int b); ?//定義一個函數(shù)指針類型

void main()

{

? HINSTANCE hInt = LoalLibrary( _T( "../debug/DllTest.dll") ); ?//動態(tài)加載

? pFun mulitplyFun = (pFun) GetProcAddress( hInt , (LPCSTR) MAKEINTRESOURCE(2)); //函數(shù)序列號是通過工具dumpbin查到的

}?

工具dumpbin的使用.

1.先找到vsvar32.bat文件,目錄是在: 安裝目錄\VC\bin\vcvars32.bat.然后在cmd里面執(zhí)行它

2.先切換到dll文件所在目錄,假如這里是DllTest.dll,然后執(zhí)行命令dumpbin - exports DllTest.dll

3.得到所以導出函數(shù)信息,其中ordianl那一列指函數(shù)序列號 , name那一列則是編譯之后函數(shù)的名字,比之前的函數(shù)名多了些前綴后綴.
========

win32 dll簡單例子


http://blog.csdn.net/rem2002/article/details/1744978
一。顯示鏈接dll

編寫dll

FILE->Visal C++項目: Win32項目->應用程序設置: 選擇 DLL(D) 選項 并勾選 導出符號,將 h,cpp文件修改如下:

MyDll.h

? ?//Mydll.h
? ?#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif

extern "C" MYDLL_API int fun(int mode); //自己寫的 extern "C" 不可少
extern "C" MYDLL_API int fun2(int a,int b);?

MyDll.cpp

#include "stdafx.h"
#include "MyDll.h"
BOOL APIENTRY DllMain( HANDLE 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;
}

MYDLL_API int fun(int mode) //自己寫的
{
? ??
? ? return mode*mode;
}

MYDLL_API int fun2(int a,int b) //自己寫的
{
? ? int d = (a>b?(a-b):(b-a));
? ? return d;
}
編寫測試程序:testDll

采用win32控制臺生成的執(zhí)行程序進行測試 (注: 屬性->C/C++:預處理器->預處理器定義 ?加宏:MYDLL_EXPORTS)
因為MyDll.h中定義了宏 #define MYDLL_API __declspec(dllexport)

#include <iostream>
#include <Windows.h>

typedef int (*PFNMYDLL)(int);//聲明函數(shù)原型
typedef int (*HHH)(int,int);?

using namespace std;

void main()
{
? ? HMODULE hModule = ::LoadLibrary("MyDll.dll");//加載DLL庫

? ? PFNMYDLL newfun = (PFNMYDLL)::GetProcAddress(hModule,"fun");//取得fun函數(shù)的地址

? ? int i = newfun(4);
? ? printf("The result is %d ",i);

? ? HHH newfun2 = (HHH)::GetProcAddress(hModule,"fun2");//取得fun函數(shù)的地址

? ? int d = newfun2(6,4);
? ? printf("the 6,4 is: %d ",d);


? ? int c = newfun2(7,19);
? ? printf("the 7,19 is:%d ",c);


? ? ::FreeLibrary(hModule);
}
?
二.隱式鏈接


[cpp] view plain copy
#ifdef MYDLL_EXPORTS ?
#define MYDLL_API __declspec(dllexport) ?
#else ?
#define MYDLL_API __declspec(dllimport) ?
#endif ?
??
class MYDLL_API MyDll ?
{ ?
public: ?
? ? MyDll(void); ?
? ? ~MyDll(void); ?
? ? void setValue(int value); ?
? ? int getValue(); ?
??
private: ?
? ? int m_nValue; ?
}; ?
?
?使用 dll 代碼


[cpp] view plain copy
#include <stdlib.h> ?
#include <stdio.h> ?
#include <windows.h> ?
#include "MyDll.h" ?
#pragma comment(lib,"MyDll.lib") ?
??
void main() ?
{ ?
??
?MyDll myDll; ?
?myDll.setValue(20); ?
?int i = myDll.getValue(); ?
??
?printf("%d",i); ?
} ?
?
?以下為轉(zhuǎn)貼


三。導出并顯式鏈接一組C++成員函數(shù)


這里有兩個問題。第一是C++成員函數(shù)名是經(jīng)過修飾的(即使指定extern "C"標記也是這樣);第二是C++不允許將指向成員函數(shù)的指針轉(zhuǎn)換成其它類型。這兩個問題限制了C++類的顯式鏈接。下面介紹兩種方法來解決這個問題:①用虛函數(shù)表的方法,這也是COM使用的方法;②用GetProcAddress直接調(diào)用。


1.虛函數(shù)表方法:


使用到的 dll 頭文件 MyDll.h


[cpp] view plain copy
#ifdef MYDLL_EXPORTS ?
#define MYDLL_API __declspec(dllexport) ?
#else ?
#define MYDLL_API __declspec(dllimport) ?
#endif ?
??
class MYDLL_API MyDll ?
{ ?
public: ?
? ? MyDll(void); ?
? ? MyDll(int i); ?
? ? virtual ~MyDll(void); ?
? ? virtual void setValue(int value); ?
? ? virtual int getValue(); ?
??
private: ?
? ? int m_nValue; ?
}; ?
?
使用 dll 的代碼


[cpp] view plain copy
#include <stdlib.h> ?
#include <stdio.h> ?
#include <string> ?
#include <windows.h> ?
??
#include "MyDll.h" ?
??
typedef MyDll* (*pCreateA)(); ?
typedef MyDll* (*pCreateA1)(int); ?
??
void main() ?
{ ?
? ? HMODULE hModule; ?
? ? ??
? ? hModule = ::LoadLibrary("MyDll");//加載DLL庫 ?
??
? ? pCreateA pCreate = (pCreateA)GetProcAddress(hModule, TEXT("CreateMyDll")); ?
??
? ? MyDll* a = (pCreate)(); ?
? ? a->setValue(20); ?
? ? printf("one:%d/n",a->getValue()); ? ?
??
? ? pCreateA1 pCreate1 = (pCreateA1)GetProcAddress(hModule, TEXT("CreateMyDll1")); ?
??
? ? MyDll* b = (pCreate1)(50); ?
? ? printf("two:%d/n",b->getValue()); ?
??
? ? ::FreeLibrary(hModule); ?
??
? ? getchar(); ?
? ? return; ?
} ??


dll 項目


MyDll.h 即使用到的 dll 頭文件


MyDll.cpp


[cpp] view plain copy
#include "MyDll.h" ?
??
MyDll::MyDll(void) ?
:m_nValue(0) ?
{ ?
} ?
??
MyDll::MyDll(int i) ?
{ ?
? ? m_nValue = i; ?
} ?
??
MyDll::~MyDll(void) ?
{ ?
? ? m_nValue = 0; ?
} ?
??
void MyDll::setValue(int value) ?
{ ?
? ? m_nValue = value; ?
} ?
??
int MyDll::getValue() ?
{ ?
? ? return m_nValue; ?
} ?
Inst.cpp


[c-sharp] view plain copy
#include "MyDll.h" ?
??
extern "C" __declspec(dllexport) MyDll* CreateMyDll() ?
{ ?
? ? return new MyDll(); ?
} ?
extern "C" __declspec(dllexport) MyDll* CreateMyDll1(int i) ?
{ ?
? ? return new MyDll(i); ?
} ?
?
這個方法的使用得用戶可以很容易地為你的程序制作插件。它的缺點是創(chuàng)建對象的內(nèi)存必須在dll中分配.


直接使用GetProcAddress進行顯式鏈接


這個方法的關(guān)鍵在于將GetProcAddress函數(shù)返回的FARPROC類型轉(zhuǎn)化為C++中指向成員函數(shù)的指針。幸運的是,通過C++的unio和模板機制,這個目標可以很容易地實現(xiàn)。我們要做的只是定義如下的函數(shù):


template<class Src , class Dest>


Dest force_cast(Src src){


?union{


? Dest d;


? Src s;


?} convertor;


convertor.s = Src;


?return convertor.d;


}


上面的函數(shù)允許我們在任何類型間進行轉(zhuǎn)換,比reinterpret_cast更加有效。例如,我們定義一種指針類型:


typedef void (A::*PSetNum)(int);


我們可以將FARPROC類型的指針fp轉(zhuǎn)化成PSetNum:


PSetNum psn = force_cast<PSetNum>(fp);


找到了將FARPROC轉(zhuǎn)化成成員函數(shù)指針的方法以后,我們要考慮如何將C++成員函數(shù)以更加友好的名字導出。這可以通過一個.def文件來實現(xiàn)。


第一步是找到待導出函數(shù)經(jīng)過修飾的函數(shù)名,這可以通過查看map file或者匯編代碼來實現(xiàn)。然后在.def文件中指定導出函數(shù)的新的函數(shù)名:


EXPORTS


?ConstructorOfA1 = ??0A@@QAE@XZ ? ? ? ?PRIVATE


?ConstructorOfA2 = ??0A@@QAE@H@Z ? ? ? PRIVATE


?SetValueOfA ? ? ? = ?SetNum@A@@UAEXH@Z ?PRIVATE


?GetValueOfA ? ? ? = ?GetNum@A@@UAEHXZ ? PRIVATE ?


下面是調(diào)用這些成員函數(shù)的方法:


typedef void (A::*PfnConstructorOfA1)();


typedef void (A::*PfnConstructorOfA2)(int);


typedef void (A::*PfnDestructorOfA)();


typedef void (A::*PfnSetNumOfA)(int);


typedef int ?(A::*PfnGetNumOfA)();


A* a1 = (A*)_alloca(sizeof(A));


PfnConstructorOfA1 pfnConsA =


? ? ?force_cast<PfnConstructorOfA1>(GetProcAddress(hMod, TEXT("ConstructorOfA1")));


(a1->*pfnConsA)();


PfnSetNumOfA pfnSetNumA =


? ? ? ? ? force_cast<PfnSetNumOfA>(GetProcAddress(hMod, TEXT("SetNumOfA")));


(a1->*pfnSetNumA)(1); ? ??


PfnGetNumOfA pfnGetNumA =


? ? ? ? ? force_cast<PfnGetNumOfA>(GetProcAddress(hMod, TEXT("GetNumOfA")));


_tprintf(TEXT("Value of m_nNum in a is %d/n"),(a1->*pfnGetNumA)());


?注意這里使用了alloca從棧中分配內(nèi)存,你也可以使用malloc從堆中分配內(nèi)存。但是不能使用C++的new操作符,因為能過new來分配內(nèi)存編譯器會自動插入對constructor的調(diào)用。但我們要的是顯式鏈接,所以必須避免這種情況。隨之產(chǎn)生的結(jié)果是我們只能顯式地去調(diào)用構(gòu)造函數(shù)和析構(gòu)函數(shù)。
========

動態(tài)鏈接庫-Win32 DLL的創(chuàng)建和使用

http://www.cnblogs.com/because/archive/2012/02/18/2357109.html
摘要
? ? ? ?利用Visual C++6.0創(chuàng)建和使用DLL(Dynamic-Link Library).


概述
   在實際編程時,我們可以把完成某種功能的函數(shù)放在一個動態(tài)鏈接庫中,然后給其他程序調(diào)用。


   WinAPI中所有的函數(shù)都包含在3個最重要的DLL中。


Kernel32.dll
        它包含那些用于管理內(nèi)存、進程和線程的函數(shù),例如CreateThread函數(shù);


User32.dll
        它包含那些用于執(zhí)行用戶界面任務的函數(shù),例如CreateWindow函數(shù);


GDI32.dll   
        它包含那些用于畫圖和顯示文本的函數(shù)。


用法 
?新建一個Win32 Console Application工程:


以MathLib為工程名稱新建Win32 Dynamic-Link Library的空工程,


添加C++ Source File 源文件到工程中,命名為MathLib.c


添加以下代碼:


復制代碼
?1 #define MATH_API _declspec(dllexport)
?2 #include "MathLib.h"
?3 int add(int a,int b)
?4 {
?5 ? ? return a+b;
?6 }
?7 int subtract(int a,int b)
?8 {
?9 ? ? return a-b;
10 }
復制代碼
添加C/C++ Header File 頭文件到工程中,命名為MathLib.h


復制代碼
1 #ifdef MATH_API
2 #else
3 ? ? #define ? MATH_API _declspec(dllimport)
4 #endif
5 MATH_API int add(int a,int b);
6 MATH_API int subtract(int a,int b);
復制代碼
編譯后生成MathLib.dll和MathLib.lib兩個動態(tài)鏈接庫文件。


測試
隱式調(diào)用
?
新建MFC AppWizard[exe]可執(zhí)行工程DllTest,用于測試剛才新建動態(tài)鏈接庫MathLib的功能。


復制MathLib.dll,MathLib.lib,MathLib.h到當前工程,


在DllTestDlg.cpp中添加頭文件引用:


#include "MathLib.h"
?
添加MathLib.h頭文件至工程,


在Project->Setting->Link->object/library modules:添加MathLib.lib


添加一個按鈕Add到Dialogue中,在Add按鈕的響應函數(shù)中添加以下代碼:


1 void CDllTestDlg::OnBtnMath()?
2 {
3 ? ? // TODO: Add your control notification handler code here
4 ? ? CString res;
5 ? ? res.Format("10+2=%d",add(10,2));
6 ? ? MessageBox(res);
7 }
復制代碼
編譯運行程序,


成功運行MathLib中的加法功能。


工程文件:
========

windows程序設計之調(diào)用動態(tài)鏈接庫DLL DLL的調(diào)用約定

http://www.cnblogs.com/llz5023/archive/2012/12/30/2839682.html
1、動態(tài)鏈接庫英文為DLL,是Dynamic Link Library 的縮寫形式,DLL是一個包含可由多個程序同時使用的代碼和數(shù)據(jù)的庫,DLL不是可執(zhí)行文件。動態(tài)鏈接提供了一種方法,使進程可以調(diào)用不屬于其可執(zhí)行代碼的函數(shù)。函數(shù)的可執(zhí)行代碼位于一個 DLL 中,該 DLL 包含一個或多個已被編譯、鏈接并與使用它們的進程分開存儲的函數(shù)。DLL 還有助于共享數(shù)據(jù)和資源。多個應用程序可同時訪問內(nèi)存中單個DLL 副本的內(nèi)容。DLL 是一個包含可由多個程序同時使用的代碼和數(shù)據(jù)的庫。


2、操作實例,C語言咧調(diào)用系統(tǒng)的kernel32.dll中的GlobalMemoryStatusEx函數(shù)


? ? ? ? ? ?typedef ? void(WINAPI* ? FunctionGlobalMemoryStatusEx)(LPMEMORYSTATUS);//聲明函數(shù)指針模型
? ? ? ? ? ? HMODULE ? hModule;//Dll句柄
? ? ? ? ? ? FunctionGlobalMemoryStatusEx ? GlobalMemoryStatusEx;//函數(shù)指針模型聲明函數(shù)變量
? ? ? ? ? ? MEMORYSTATUS status;
? ? ? ? ? ? status.dwLength = sizeof(status);
? ? ? ? ? ? //GlobalMemoryStatus(&status);
? ? ? ? ? ? hModule ? = ? LoadLibrary("kernel32.dll");//調(diào)試時hModule為0x10000000,載入動態(tài)鏈接庫dll,返回它的句柄
? ? ? ? ? ? if(NULL==hModule)//判斷載入是否成功
? ? ? ? ? ? {
? ? ? ? ? ? ? ? //error.
? ? ? ? ? ? ? ? MessageBox(hwndDlg,TEXT("載入指定的動態(tài)鏈接庫dll失敗"),TEXT("error"),MB_OK);
? ? ? ? ? ? ? ? return 0;
? ? ? ? ? ? }
? ? ? ? ? ? //調(diào)用GetProcAddress API根據(jù)dll句柄,和dll的聲明的函數(shù)名獲取函數(shù)指針
? ? ? ? ? ? GlobalMemoryStatusEx ? =(FunctionGlobalMemoryStatusEx)GetProcAddress(hModule,"GlobalMemoryStatusEx");
? ? ? ? ? ? if(NULL==GlobalMemoryStatusEx)//判斷獲取是否成功
? ? ? ? ? ? {
? ? ? ? ? ? ? ? //error
? ? ? ? ? ? ? ? MessageBox(hwndDlg,TEXT("error2"),TEXT("error2"),MB_OK);
? ? ? ? ? ? ? ? return 0;
? ? ? ? ? ? }
? ? ? ? ? ? //獲取成功,然后可以直接用函數(shù)指針來調(diào)用函數(shù),函數(shù)名就是函數(shù)指針,C語言應該都懂
? ? ? ? ? ? GlobalMemoryStatusEx(&status);//調(diào)用函數(shù)
? ? ? ? ? ? FreeLibrary(hModule);//用完了要釋放dll
3、第二步已經(jīng)說名了怎么動態(tài)調(diào)用DLL,我們還要注意一點,DLL的調(diào)用約定
dll有__cdecl __stdcall WINAPI 等不同的調(diào)用約定,也就是參數(shù)的壓棧順序等,暫時不用關(guān)心,只要保證調(diào)用的時候和dll中的調(diào)用約定一樣就可以。
//否則會報錯:The value of ESP was not properly saved across a function call. ?This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.?
如上面的列子,如果我把typedef ? void(WINAPI* ? FunctionGlobalMemoryStatusEx)(LPMEMORYSTATUS);//聲明函數(shù)指針模型,改成:
typedef ? void(__cdecl* ? FunctionGlobalMemoryStatusEx)(LPMEMORYSTATUS););//聲明函數(shù)指針模型
運行時會報錯誤:
image?
由此,在聲明函數(shù)原型指針時要注意寫對調(diào)用約定,如果不知道,那么換著調(diào)試看那個對。
4、說明一下調(diào)用約定(Calling Convention)相關(guān)的(其他地方拷貝來的)


調(diào)用約定用來處理決定函數(shù)參數(shù)傳送時入棧和出棧的順序(由調(diào)用者還是被調(diào)用者把參數(shù)彈出棧),以及編譯器用來識別函數(shù)名稱的名稱修飾約定等問題。在Microsoft VC++ 6.0中定義了下面幾種調(diào)用約定,我們將結(jié)合匯編語言來一一分析它們:


4.1、__cdecl


__cdecl是C/C++和MFC程序默認使用的調(diào)用約定,也可以在函數(shù)聲明時加上__cdecl關(guān)鍵字來手工指定。采用__cdecl約定時,函數(shù)參數(shù)按照從右到左的順序入棧,并且由調(diào)用函數(shù)者把參數(shù)彈出棧以清理堆棧。因此,實現(xiàn)可變參數(shù)的函數(shù)只能使用該調(diào)用約定。由于每一個使用__cdecl約定的函數(shù)都要包含清理堆棧的代碼,所以產(chǎn)生的可執(zhí)行文件大小會比較大。__cdecl可以寫成_cdecl。


下面將通過一個具體實例來分析__cdecl約定:


在VC++中新建一個Win32 Console工程,命名為cdecl。其代碼如下:


int __cdecl Add(int a, int b); //函數(shù)聲明


void main()


{


Add(1,2); //函數(shù)調(diào)用


}


int __cdecl Add(int a, int b) //函數(shù)實現(xiàn)


{


return (a + b);


}


函數(shù)調(diào)用處反匯編代碼如下:


;Add(1,2);


push 2 ;參數(shù)從右到左入棧,先壓入2


push 1 ;壓入1


call @ILT+0(Add) (00401005) ;調(diào)用函數(shù)實現(xiàn)


add esp,8 ;由函數(shù)調(diào)用清棧


4.2、__stdcall


__stdcall調(diào)用約定用于調(diào)用Win32 API函數(shù)。采用__stdcal約定時,函數(shù)參數(shù)按照從右到左的順序入棧,被調(diào)用的函數(shù)在返回前清理傳送參數(shù)的棧,函數(shù)參數(shù)個數(shù)固定。由于函數(shù)體本身知道傳進來的參數(shù)個數(shù),因此被調(diào)用的函數(shù)可以在返回前用一條ret n指令直接清理傳遞參數(shù)的堆棧。__stdcall可以寫成_stdcall。


還是那個例子,將__cdecl約定換成__stdcall:


int __stdcall Add(int a, int b)


{


return (a + b);


}


函數(shù)調(diào)用處反匯編代碼:


; Add(1,2);


push 2 ;參數(shù)從右到左入棧,先壓入2


push 1 ;壓入1


call @ILT+10(Add) (0040100f) ;調(diào)用函數(shù)實現(xiàn)


函數(shù)實現(xiàn)部分的反匯編代碼:


;int __stdcall Add(int a, int b)


push ebp


mov ebp,esp


sub esp,40h


push ebx


push esi


push edi


lea edi,[ebp-40h]


mov ecx,10h


mov eax,0CCCCCCCCh


rep stos dword ptr [edi]


;return (a + b);


mov eax,dword ptr [ebp+8]


add eax,dword ptr [ebp+0Ch]


pop edi


pop esi


pop ebx


mov esp,ebp


pop ebp


ret 8 ;清棧


4.3、__fastcall


__fastcall約定用于對性能要求非常高的場合。__fastcall約定將函數(shù)的從左邊開始的兩個大小不大于4個字節(jié)(DWORD)的參數(shù)分別放在ECX和EDX寄存器,其余的參數(shù)仍舊自右向左壓棧傳送,被調(diào)用的函數(shù)在返回前清理傳送參數(shù)的堆棧。__fastcall可以寫成_fastcall。


依舊是相類似的例子,此時函數(shù)調(diào)用約定為__fastcall,函數(shù)參數(shù)個數(shù)增加2個:


int __fastcall Add(int a, double b, int c, int d)


{


return (a + b + c + d);


}


函數(shù)調(diào)用部分的匯編代碼:


;Add(1, 2, 3, 4);


push 4 ;后兩個參數(shù)從右到左入棧,先壓入4


mov edx,3 ;將int類型的3放入edx


push 40000000h ;壓入double類型的2


push 0


mov ecx,1 ;將int類型的1放入ecx


call @ILT+0(Add) (00401005) ;調(diào)用函數(shù)實現(xiàn)


函數(shù)實現(xiàn)部分的反匯編代碼:


; int __fastcall Add(int a, double b, int c, int d)


push ebp


mov ebp,esp


sub esp,48h


push ebx


push esi


push edi


push ecx


lea edi,[ebp-48h]


mov ecx,12h


mov eax,0CCCCCCCCh


rep stos dword ptr [edi]


pop ecx


mov dword ptr [ebp-8],edx


mov dword ptr [ebp-4],ecx


;return (a + b + c + d);


fild dword ptr [ebp-4]


fadd qword ptr [ebp+8]


fiadd dword ptr [ebp-8]


fiadd dword ptr [ebp+10h]


call __ftol (004011b8)


pop edi


pop esi


pop ebx


mov esp,ebp


pop ebp


ret 0Ch ;清棧


關(guān)鍵字__cdecl、__stdcall和__fastcall可以直接加在要輸出的函數(shù)前,也可以在編譯環(huán)境的Setting...->C/C++->Code Generation項選擇。它們對應的命令行參數(shù)分別為/Gd、/Gz和/Gr。缺省狀態(tài)為/Gd,即__cdecl。當加在輸出函數(shù)前的關(guān)鍵字與編譯環(huán)境中的選擇不同時,直接加在輸出函數(shù)前的關(guān)鍵字有效。
========

DLL中導出函數(shù)(函數(shù)名及其調(diào)用約定)

http://www.cnblogs.com/leijiangtao/p/4797585.html
最近簡單研究了一下dll的導出函數(shù),整理了一下


1.導出函數(shù)名的問題


dll導出函數(shù)最簡單的語法是


void__declspec(dllexport) fun();


由于它默認的是c++的調(diào)用約定cdecl,因此導出的函數(shù)就變成了


?fun@@YAXXZ


如果直接取函數(shù)名fun,就會找不到函數(shù),有兩種方法可以解決這個問題:用C的編譯方式和def文件


① ? ? ?用C的編譯方式


在導出函數(shù)前聲明extern “C”,即:


extern “C” void__declspec(dllexport) fun();


加入extern “C”是告訴編譯器,用C的編譯方式生成文件,不需要加入?yún)?shù)作為修飾


② ? ? ?Def文件


在project中建立一個def文件,寫入


LIBRARY ? "testDLL"// testDLL是project的名字


EXPORTS ? ? ? ? ? ?//輸出


? fun ? ? ? ? ? ? ?//函數(shù)名(也可以帶序號的輸出函數(shù)名fun@1)


extern “C” void__declspec(dllexport) 和在def文件中導出函數(shù)的作用是一樣的,因此沒必要都寫在工程中。


Ps,如果導出的函數(shù)名帶一些修飾,如:?fun@@YAXXZ,用GetProcAddress()函數(shù)直接調(diào)用“?fun@@YAXXZ”也是可以找到函數(shù)的。


2. 修飾函數(shù)的關(guān)鍵字


stdcall cdecl fastcall thiscall naked call


這些調(diào)用約定決定了:


? ? ? ? ? 參數(shù)傳遞次序


? ? ? ? ? 調(diào)用堆棧由誰(調(diào)用函數(shù)或被調(diào)用函數(shù))清理


? ? ? ? ? 導出函數(shù)名


導出函數(shù)的調(diào)用約定和使用這個函數(shù)時聲明的調(diào)用約定必須一致,否則程序會崩潰。


在C和C++中默認的調(diào)用約定是__cdecl,上面函數(shù)完整的修飾就是:


void__declspec(dllexport) __cdeclfun();


但是windows系統(tǒng)用的回調(diào)函數(shù)一般都是_stdcall。


下面是各個調(diào)用約定詳細的解釋:


_stdcall


是Pascal方式清理C方式壓棧,通常用于Win32 Api中,函數(shù)采用從右到左的壓棧方式, 自己在退出時清空堆棧。VC將函數(shù)編譯后會在函數(shù)名前面加上下劃線前綴,在函數(shù)名后加上"@"和參數(shù)的字節(jié)數(shù)。


int f(void *p) -->> _f@4(在外部匯編語言里可以用這個名字引用這個函數(shù))?


__cdecl


C調(diào)用約定(即用__cdecl關(guān)鍵字說明)(The C default calling convention)按從右至左的順序壓參數(shù)入棧,由調(diào)用者把參數(shù)彈出棧。對于傳送參數(shù)的內(nèi)存棧是由調(diào)用者來維護的(正因為如此,實現(xiàn)可變參數(shù)vararg的函數(shù)(如printf)只能使用該調(diào)用約定)。


另外,在函數(shù)名修飾約定方面也有所不同。 _cdecl是C和C++程序的缺省調(diào)用方式。每一個調(diào)用它的函數(shù)都包含清空堆棧的代碼,所以產(chǎn)生的可執(zhí)行文件大小會比調(diào)用_stdcall函數(shù)的大。函數(shù)采用從右到左的壓棧方式。VC將函數(shù)編譯后會在函數(shù)名前面加上下劃線前綴。


_fastcall


調(diào)用的主要特點就是快,因為它是通過寄存器來傳送參數(shù)的(實際上,它用ECX和EDX傳送前兩個雙字(DWORD)或更小的參數(shù),剩下的參數(shù)仍舊自右向左壓棧傳送,被調(diào)用的函數(shù)在返回前清理傳送參數(shù)的內(nèi)存棧),


在函數(shù)名修飾約定方面,它和前兩者均不同。__fastcall方式的函數(shù)采用寄存器傳遞參數(shù),VC將函數(shù)編譯后會在函數(shù)名前面加上"@"前綴,在函數(shù)名后加上"@"和參數(shù)的字節(jié)數(shù)。


thiscall


僅僅應用于“C++”成員函數(shù)。this指針存放于CX/ECX寄存器中,參數(shù)從右到左壓。thiscall不是關(guān)鍵詞,因此不能被程序員指定。


naked call


當采用1-4的調(diào)用約定時,如果必要的話,進入函數(shù)時編譯器會產(chǎn)生代碼來保存ESI,EDI,EBX,EBP寄存器,退出函數(shù)時則產(chǎn)生代碼恢復這些寄存器的內(nèi)容。


(這些代碼稱作 prolog and epilog code,一般,ebp,esp的保存是必須的).?


但是naked call不產(chǎn)生這樣的代碼。naked call不是類型修飾符,故必須和_declspec共同使用。?


另外,關(guān)鍵字 __stdcall、__cdecl和__fastcall可以直接加在要輸出的函數(shù)前。它們對應的命令行參數(shù)分別為/Gz、/Gd和/Gr。缺省狀態(tài)為/Gd,即__cdecl。
========

總結(jié)

以上是生活随笔為你收集整理的win32 DLL 学习总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。