深入浅出Dll(介绍函数导出、类导出、钓子dll、不同语言混合编程方法、插件 等的实现方法) 所有代码均经过测试,如有问题可留言 一。简单的dll函数调用有两种方式: 1。显式调用2。隐式调用.如下例子 //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // dlltest.cpp : Dll 撰写 //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx #include <windows.h> #include <stdio.h> extern "C" __declspec(dllexport) int Add(int n1, int n2); BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { if(ul_reason_for_call==DLL_PROCESS_ATTACH) { //TRACE0("DLL Initializing!\n"); } else if(ul_reason_for_call=DLL_PROCESS_DETACH) { //TRACE0("DLL Terminating!\n"); } return TRUE; } __declspec(dllexport) Add(int n1, int n2) { char szTxt[1024]; sprintf(szTxt,"%d + %d =%d",n1,n2,n1+n2); MessageBox(0,szTxt,"Dll Respond here:",0); return 1; } //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // usedll.cpp : Dll 调用测试 //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx #include <windows.h> //#define USESTATELIB // 隐式链接方式: #ifdef USESTATELIB #pragma comment(lib,"lib\\dlltest.lib") extern "C" __declspec(dllimport) int Add(int n1, int n2); #endif int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { #ifndef USESTATELIB // 显式调用方式: HINSTANCE hIns=LoadLibrary("dlltest.dll"); if(!hIns) { MessageBox(0,"couldn't find dlltest.dll","error:",0); return 0; } typedef int (DLL_Add)(int , int ); DLL_Add *testit=(DLL_Add*) GetProcAddress(hIns,"Add"); if(testit) { (*testit)(1,2); }else { MessageBox(0,"couldn't find Add from dlltest.dll","error:",0); return 0; } FreeLibrary(GetModuleHandle("dlltest.dll")); #else // 隐式链接方式: int nres= Add(1, 2); #endif return 0; } //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // 二。 导出类 // 注意CTest类应该定义在文件头,dllexport最好用宏作判断定义, // 而这里仅作测试。 //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 1。 dll生成: // tdll.cpp : Defines the entry point for the DLL application. // #include "stdafx.h" class __declspec(dllexport) CTest { private: int m_nvalue; public: CTest() : m_nvalue(0) {} int Getvalue() const; }; int CTest::Getvalue() const { return m_nvalue; } BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { return TRUE; } 2。调用(记得把上述生成的tdll.dll及tdll.lib拷过来): #pragma comment(lib, "tdll.lib") class __declspec(dllimport) CTest { private: int m_nvalue; public: CTest() : m_nvalue(1) {} int Getvalue() const; }; int main(int argc, char* argv[]) { CTest a; int kk = a.Getvalue(); return 0; } 运行正确。 有趣的现象是: 当我把应用端的dllimport错误的改成dllexport,程序依然可以build通过 (不过注释了#pragma comment(lib, "tdll.lib")就不行),运行时结果 是1(为区别于dll中初始值0,我 把应用端的初始为1)。 嗯,这是因为编译器在处理应用端的CTest时,编译确实看得见类结构,而链接时 在 tdll.lib中也可找Getvalue()相应符号,所以build成功,运行时既然应用端自己 的CTest是dllexport,当然就是直接运行它了。 //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // 三。使用钓子的dll // 1.参见我的QQ本马程序: // http://blog./thread.asp?BlogID=386&;threadid=13701 ;threadid=13701 // 2.另一个用于按F3弹出dll框供输出debug信息的dll,代码如下: // 用vc创建一个win32dll,并在菜单中选择insert下的resource,插入一个dialog按ctrl+s存盘,并编写内容如下: // logdll.h内容: #ifdef LOGDLL_EXPORTS #define LOGDLL_API __declspec(dllexport) #else #define LOGDLL_API __declspec(dllimport) #endif extern "C" LOGDLL_API void LG( const char* szTxt ); // logdll.cpp 内容: // #include <windows.h> #include "logdll.h" #include "resource.h" // global data: #pragma comment( linker, "section:Shared,rws" ) #pragma data_seg( "Shared" ) HHOOK g_hhook = NULL; HINSTANCE g_hInstance = NULL; HWND g_pLogDlg = NULL; BOOL g_bShowDlg = FALSE; #pragma data_seg() //------------------------------------------------------------------------ void Init(); void Finish(); BOOL CreateLogDlg(); BOOL ShowLogDlg(); void InstallHook(); void UninstallHook(); LRESULT CALLBACK KeyboardProc( int, WPARAM, LPARAM ); INT_PTR CALLBACK LgDlgProc( HWND, UINT, WPARAM, LPARAM ); //------------------------------------------------------------------------ /* */ BOOL CreateLogDlg() { if( g_pLogDlg ) return TRUE; if( FALSE == g_hInstance ) return FALSE; g_pLogDlg = CreateDialog( g_hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)LgDlgProc ); return TRUE; } //------------------------------------------------------------------------ /* */ BOOL ShowLogDlg() { if( NULL == g_pLogDlg ) { if( FALSE == CreateLogDlg() ) return FALSE; } return ShowWindow( g_pLogDlg, (g_bShowDlg = !g_bShowDlg) ? SW_SHOW : SW_HIDE ); } //------------------------------------------------------------------------ /* */ void InstallHook() { if( g_hhook == NULL ) { g_hhook = SetWindowsHookEx( WH_KEYBOARD, (HOOKPROC)KeyboardProc, g_hInstance, 0 ); } } //------------------------------------------------------------------------ /* */ void UninstallHook() { if( g_hhook ) { UnhookWindowsHookEx(g_hhook); g_hhook = NULL; } } //------------------------------------------------------------------------ /* */ LOGDLL_API void LG( const char* szTxt ) { // here set the text info to your dialog's text ctrl } void Init() { InstallHook(); } void Finish() { UninstallHook(); if( g_pLogDlg ) { DestroyWindow( g_pLogDlg ); g_pLogDlg = NULL; } } //------------------------------------------------------------------------ /* */ BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { g_hInstance = (HINSTANCE) hModule; switch( ul_reason_for_call ) { case DLL_PROCESS_ATTACH: Init(); break; case DLL_THREAD_ATTACH: Init(); break; case DLL_THREAD_DETACH: Finish(); break; case DLL_PROCESS_DETACH: Finish(); break; } return TRUE; } //------------------------------------------------------------------------ /* */ LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { LRESULT res = CallNextHookEx( g_hhook, nCode, wParam, lParam); if( (lParam & (1 << 31)) && (wParam == VK_F3) && (nCode == HC_ACTION) ) { ShowLogDlg(); } return res; } //------------------------------------------------------------------------ /* */ INT_PTR CALLBACK LgDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam ) { switch ( msg ) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: switch( LOWORD(wParam) ) { case IDCANCEL: case IDOK: EndDialog( hDlg, TRUE ); g_bShowDlg = FALSE; return TRUE; } break; case WM_DESTROY: break; } return FALSE; } //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // 四。不同语言混合编程方法 // 标题太夸张了点,这里只是通过一种语言编写dll给另一种语言使用而已。例如:在VC中写dll而在vb中调用。这个在我转载的文章中已有祥细例子,我就 不多说了,参见:(引用别人的“库”,又少写了很多“代码”,呵呵) http://blog./thread.asp?BlogID=386& ;threadid=13945 //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // 五。插件dll相关撰写 // (我写了以下例子来说明3dmax的导出插件是如何编写与实现, // 源码简单就不说明了) //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 这 里我们的程序我用app.h和app.cpp实现,而别人写的插件我用plugtest.h与plugtest.cpp实现. 1.先写我们的程序 (用建立普通win32程序方法创建) // app.h头文件内容: #include <windows.h> // extern "C" // Define USING_IMPORT by app's project setting that this head file local at #ifdef USING_IMPORT #define THIS_CLASS_EXPORT __declspec(dllimport) #else #define THIS_CLASS_EXPORT __declspec(dllexport) #endif class THIS_CLASS_EXPORT TestExport { public: TestExport() {}; virtual ~TestExport() {}; virtual int DoExport() = 0; }; class THIS_CLASS_EXPORT ClassDesc { public: virtual HINSTANCE HInstance() = 0; virtual void* Create() = 0; // { return new TestExport's Derive class } }; // app.cpp 文件内容: // #include "app.h" #include <stdio.h> #include <IO.H> typedef ClassDesc* (*PFN_DESC)(); void TestCall( const char* szName ) { HINSTANCE hDll = LoadLibrary( szName ); PFN_DESC pPlugDesc = (PFN_DESC)GetProcAddress( hDll, "Get_PlugDesc" ); ClassDesc* pDesc = (*pPlugDesc)(); if( pDesc ) { TestExport* pExp = (TestExport*)pDesc->Create(); if( pExp ) { pExp->DoExport(); } } } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // search all dll: struct _finddata_t c_file; long hFile; /* Find first .c file in current directory */ if( (hFile = _findfirst( "*.dll", &c_file )) == -1L ) printf( "No *.dll files in current directory!\n" ); else { TestCall( c_file.name ); /* Find the rest of the .c files */ while( _findnext( hFile, &c_file ) == 0 ) { TestCall( c_file.name ); } _findclose( hFile ); } return 0; } 下面写一个插件来测试一下: 可在vc中用普通win32 dll的创建方法来创建以下一个dll内容: // plugtest.cpp内容 // #include <windows.h> #include "../app/app.h" //#pragma comment(lib, "app.lib") HINSTANCE g_hInstance; // example: // notic: must not define USING_IMPORT(because this example using for client) class Dll_Plug : public TestExport { int DoExport() { MessageBox(0, "test ok", "ok", 0); return 1; } }; class Test_plusClassDesc : public ClassDesc { HINSTANCE HInstance() { return g_hInstance; } void* Create() { return new Dll_Plug; } }; static Test_plusClassDesc test_plusDesc; extern "C" THIS_CLASS_EXPORT ClassDesc* Get_PlugDesc() { return &test_plusDesc; } BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { g_hInstance = (HINSTANCE)hModule; return TRUE; } [注:] 1. 函数调用有不同的约定方式,其中带extern "C"的是c约定与不带 的是不同的,具体 体现在输出的符号上,一般c约定的符号与函数名相同, c++的则带xx@xxxx之类(这可用dll查看工具查看),主要是区别参数的不同(为了 处理类似重载等),相关文章可参见我转载的文章: http://blog./thread.asp?BlogID=386& ;threadid=13945 2. dll查看工具推荐: process explorer.exe、 dependency walker.exe
这些工具非常有用,如果你的项目经常与一堆dll打交道的话就知道 怎么用它们来查出不同dll工程之间关系,查出找不到符号的原因 |
|
来自: scholes_goal > 《技术》