深入浅出Dll(介绍函数导出、类导出、钓子dll、不同语言混合编程方法、插件等的实现方法)

导读:
   深入浅出Dll(介绍函数导出、类导出、钓子dll、不同语言混合编程方法、插件等的实现方法)
  所有代码均经过测试,如有问题可留言
  一。简单的dll函数调用有两种方式:
  1。显式调用2。隐式调用.如下例子
  //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  // dlltest.cpp : Dll 撰写
  //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  #include
  #include
  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
  //#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.gameres.com/thread.asp?BlogID=386&;threadid=13701threadid=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
  #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.gameres.com/thread.asp?BlogID=386&threadid=13945
  //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  // 五。插件dll相关撰写
  // (我写了以下例子来说明3dmax的导出插件是如何编写与实现,
  // 源码简单就不说明了)
  //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  这里我们的程序我用app.h和app.cpp实现,而别人写的插件我用plugtest.h与plugtest.cpp实现.
  1.先写我们的程序(用建立普通win32程序方法创建)
  // app.h头文件内容:
  #include
  // 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
  #include
  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
  #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.gameres.com/thread.asp?BlogID=386&threadid=13945
  2. dll查看工具推荐: process explorer.exe、 dependency walker.exe
  这些工具非常有用,如果你的项目经常与一堆dll打交道的话就知道
  怎么用它们来查出不同dll工程之间关系,查出找不到符号的原因。
  相关有关工具参见:
  http://blog.gameres.com/show.asp?BlogID=386&column=469
  Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=125197

本文转自
http://blog.csdn.net/iceezone/archive/2004/10/05/125197.aspx
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值