使用内联汇编动态调用DLL

使用内联汇编动态调用DLL
1.首先我们必须知道C语言的调用约定为 __cdecl(即参数从右向左依次进栈,由调用者还原堆栈).
2.一条push指令最多压入4个字节,当不足4个字节时应补齐4个字节,超过4个字节时应该由低位到高位依次压栈.
3.pop指令也和push一样一次只能弹出4个字节.
4.我们需要一个CallStruct类型来储存一个参数.
 1  class  CallStruct
 2      {
 3       public :
 4          INT            Integer;
 5          BOOL        Bool;
 6          DOUBLE        Real;
 7          WCHAR        String[MAX_STRING];
 8          
 9           enum  TYPE
10          {
11              ctInteger,
12              ctString,
13              ctBool,
14              ctReal,
15              ctVoid,
16          }Type;
17          
18          CallStruct() : Integer( 0 ),Bool(FALSE),Real( 0 )
19          {
20              String[ 0 ]     =   0 ;
21          }
22 
23          CallStruct( const  NAutoPtr < VirtualMachine::VarClass >&  Var) : Integer(Var -> Integer),Bool(Var -> Bool),Real(Var -> Real),Type((TYPE)Var -> Type)
24          {
25               if (Type  ==  ctString) wcscpy(String,Var -> String);
26          }
27      };
5.我们需要一个列表来存放参数和一个返回值对象来存放返回值.
1      List < NAutoPtr < CallStruct >>  VarList;
2      NAutoPtr < CallStruct >  Return;
6.最后我们需要一个HMODULE和一个FARPROC分别存放dll的句柄和函数地址.
1      HMODULE hModule;
2      FARPROC FunctionPtr;
7.然后我们添加几个功能函数.
1      BOOL AddVar(NAutoPtr < VirtualMachine::VarClass >&  Var);
2      BOOL SetReturnType(CallStruct::TYPE Type);
3      BOOL SetLibName(LPTSTR Name);
4      BOOL SetFunctionName(LPTSTR Name);
注意:GetProcAddress第二个参数只接受LPCSTR类型的字符串,应此如果传入的是Unicode编码的字符必须将其转换成ANSI的.
8.我们添加一个函数Run用于调用函数.
 1  BOOL CallMacro::Run()
 2  {
 3       if (FunctionPtr  ==   0   ||  Return.Buffer()  ==   0 return  FALSE;
 4      union RealStruct
 5      {
 6           double  Real;
 7           struct
 8          {
 9               int  Head,Tail;
10          };
11      };
12      NAutoPtr < CallStruct >  cs;
13       int  Integer;
14      BOOL Bool;
15      RealStruct Real;  //  Push指令一次只能压入4字节
16      LPTSTR String;
17 
18       int  iEsp;
19      __asm mov  int  ptr[iEsp],esp;  //  保存esp
20       for ( int  i = 0 ;i < VarList.Size();i ++ )
21      {
22          cs  =  VarList[i];
23          Integer  =  cs -> Integer;
24          Bool  =  cs -> Bool;
25          Real.Real  =  cs -> Real;
26          String  =  cs -> String;
27           switch (cs -> Type)
28          {
29           case  CallStruct::ctInteger:
30              __asm push Integer;
31               break ;
32           case  CallStruct::ctString:
33              __asm push String;
34               break ;
35           case  CallStruct::ctBool:
36              __asm push Bool;
37               break ;
38           case  CallStruct::ctReal:
39              __asm push Real.Tail;
40              __asm push Real.Head;
41               break ;
42          }
43      }
44      FARPROC proc  =  FunctionPtr;
45       int  Head,Tail;
46      __asm
47      {
48          call proc
49          mov  int  ptr[Head],edx
50          mov  int  ptr[Tail],eax
51      }
52       switch (Return -> Type)
53      {
54       case  CallStruct::ctInteger:
55          Return -> Integer  =  Tail;
56           break ;
57       case  CallStruct::ctString:
58          wcscpy(Return -> String,(LPCTSTR)Tail);
59           break ;
60       case  CallStruct::ctBool:
61          Return -> Bool  =  Tail;
62           break ;
63       case  CallStruct::ctReal:
64          __asm fstp [Real.Real];
65          Return -> Real  =  Real.Real;
66           break ;
67      }
68       //  __declspec调用约定,需要手工还原堆栈
69      __asm mov esp, int  ptr[iEsp];
70       return  TRUE;
71  }
Run函数首先检查是否已经装载了DLL并获得了函数地址,以及返回值类型是否已经定义.
然后根据类型依次将函数压栈.
然后调用call指令并保存返回值.(这里需要注意的是 当返回值类型为double或float类型时必须使用fstp指令从FPU寄存器栈的栈顶的值取出来)
最后还原堆栈.

然后我们来测试一下.
创建一个名为TestDLL的DLL工程并添加函数Test.
1  TESTDLL_API  double  Test( double  d, double *  d1,WCHAR *  lpBuffer)
2  {
3       if (d  ==   123.456789 ) MessageBox( 0 ,lpBuffer,L "" , 0 );
4       * d1  =   789.654 ;
5       return   77777 ;
6  }

然后创建一个测试工程,添加相关文件.
在_tmain中添加以下代码.
 1  int  _tmain( int  argc, _TCHAR *  argv[])
 2  {
 3       double  d;
 4      CallMacro cm;
 5      NAutoPtr < VirtualMachine::VarClass >  Var;
 6 
 7      Var  =   new  VirtualMachine::VarClass;
 8      Var -> Type  =  VirtualMachine::VarClass::vtString;
 9      wcscpy(Var -> String,L " aaaaa " );
10      cm.AddVar(Var);
11 
12      Var  =   new  VirtualMachine::VarClass;
13      Var -> Type  =  VirtualMachine::VarClass::vtInteger;
14      Var -> Integer  =  (INT) & d;
15      cm.AddVar(Var);
16 
17      Var  =   new  VirtualMachine::VarClass;
18      Var -> Type  =  VirtualMachine::VarClass::vtReal;
19      Var -> Real  =   123.456789 ;
20      cm.AddVar(Var);
21 
22      cm.SetLibName(L " TestDll.dll " );
23      cm.SetFunctionName(L " Test " );
24      cm.SetReturnType(CallMacro::CallStruct::ctReal);
25      cm.Run();
26 
27      wprintf(L " %f %f\n " ,d,cm.Return -> Real);
28      system( " pause " );
29       return   0 ;
30  }

编译并运行,可以看到Test函数调用成功,并成功输出d的值和返回值.

最后给出 完整代码.

你可能感兴趣的:(使用内联汇编动态调用DLL)