巧用Marshal.GetDelegateForFunctionPointer--C#如何调用按键精灵插件dll

原来是为了在游戏外挂中发送键盘鼠标消息,自己写个sendmessage或者是postmessage又比较麻烦。于是google了一下,发现现在很多脚本工具都有这个功能,其中按键精灵的一个叫361度的插件已经有这个的实现,还验证过了。为什么不拿来己用呢?
首先分析一下按键精灵插件的接口,发现:

插件的功能函数没有直接暴露出来,而是通过一个GetCommand的函数返回一个函数描述结构。
接下来看看这个结构:
巧用Marshal.GetDelegateForFunctionPointer--C#如何调用按键精灵插件dll_第1张图片
上面这个结构我已经是转换成C#的对应结构了,原结构可以查看按键精灵提供的插件C++接口源代码。
这个结构里面的 handlerFunction 实际上是指向函数的入口点,也就是一个函数指针,每个函数都一样是2个参数:

typedef  int  ( * QMPLUGIN_HANDLER)( char   * lpszParamList,  char   * lpszRetVal);

转换为C#中相应的委托为:

delegate   void  Invoker( string  parameters, StringBuilder returnValue);

大家注意到,有两个参数,c++原型中都是char*类型,转换为C#的delegate后第一个为string,第二个为StringBuilder。这是因为parameters是in的,dll中不会对这个参数做修改,而returnValue是out的,dll返回时候要把返回值写入这个StringBuilder的缓冲区。

原本的想法是用C++写一个桥来调用dll,不过在.net 2.0 中,框架直接提供了 Marshal.GetDelegateForFunctionPointer 来转换一个函数指针为一个委托,这就方便多拉。请看下面代码,注意看 BGKM_ExecuteCommand 这个函数里面的东西。
using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Runtime.InteropServices;

namespace  WJsHome.Game.Utility
{
    
public   class  QMacro
    {
        [DllImport(
" BGKM5.dll " , EntryPoint  =   " GetCommand " )]
        
static   extern  IntPtr BGKM_GetCommand( int  commandNum);

        [StructLayout(LayoutKind.Sequential)]
        
class  QMPLUGIN_CMD_INFO
        {
            
public   string  commandName;
            
public   string  commandDescription;
            
public  IntPtr handlerFunction;
            
public   uint  paramNumber;
        }

        
delegate   void  Invoker( string  parameters, StringBuilder returnValue);

        
static   string  BuildParameters( params   object [] parameters)
        {
            StringBuilder sb 
=   new  StringBuilder();
            
for  ( int  i  =   0 ; i  <  parameters.Length; i ++ )
            {
                sb.Append(parameters[i].ToString());
                
if  (i  !=  parameters.Length  -   1 )
                {
                    sb.Append(
' , ' );
                }
            }
            
return  sb.ToString();
        }

        
static   void  BGKM_ExecuteCommand( int  cmdNum,  string  parameters, StringBuilder retVal)
        {
            IntPtr pCmdInfo 
=  BGKM_GetCommand(cmdNum);
            QMPLUGIN_CMD_INFO cmdInfo 
=   new  QMPLUGIN_CMD_INFO();
            Marshal.PtrToStructure(pCmdInfo, cmdInfo);
            Invoker invoker 
=  Marshal.GetDelegateForFunctionPointer(cmdInfo.handlerFunction,  typeof (Invoker))  as  Invoker;
            invoker(parameters, retVal);
        }

        
public   static   void  BGKM_KeyClick(IntPtr hWnd,  int  key)
        {
            BGKM_ExecuteCommand(
0 , BuildParameters(hWnd, key),  null );
        }

        
public   static   void  BGKM_KeyDown(IntPtr hWnd,  int  key)
        {
            BGKM_ExecuteCommand(
1 , BuildParameters(hWnd, key),  null );
        }

        

        
public   static   void  BGKM_Mouse(IntPtr hWnd,  int  code,  int  x,  int  y)
        {
            BGKM_ExecuteCommand(
15 , BuildParameters(hWnd, code, x, y),  null );
        }

        
public   static  WinApi.POINT BGKM_ScrToCli(IntPtr hWnd,  int  x,  int  y)
        {
            StringBuilder retVal 
=   new  StringBuilder();
            BGKM_ExecuteCommand(
16 , BuildParameters(hWnd, x, y), retVal);
            
string [] tmp  =  retVal.ToString().Split( ' | ' );
            
return   new  WinApi.POINT( int .Parse(tmp[ 0 ]),  int .Parse(tmp[ 1 ]));
        }
    }
}


好了,方便哇?这样一来,我们可以在.net上面实现动态加载和卸载Win32 dll. 具体思路就是:(还是代码来得方便)
public   delegate   int  MsgBox( int  hwnd, string  msg, string  cpp, int  ok);

[DllImport(
" Kernel32 " )]
public   static   extern   int  GetProcAddress( int  handle, String funcname);
[DllImport(
" Kernel32 " )]
public   static   extern   int  LoadLibrary(String funcname);
[DllImport(
" Kernel32 " )]
public   static   extern   int  FreeLibrary( int  handle);

private   static  Delegate GetAddress( int  dllModule,  string  functionname, Type t)
{
 
int  addr  =  GetProcAddress(dllModule, functionname);
 
if  (addr  ==   0
  
return   null
 
else  
  
return  Marshal.GetDelegateForFunctionPointer( new  IntPtr(addr), t);
}

private   void  button1_Click( object  sender, EventArgs e)
{
 
int  huser32  =   0 ;
 huser32 
=  LoadLibrary( " user32.dll " ); 
 MsgBox mymsg 
=  (MsgBox)GetAddress(huser32,  " MessageBoxA " typeof (MsgBox));
 mymsg(
this .Handle.ToInt32(), txtmsg.Text, txttitle.Text ,  64 );
 FreeLibrary(huser32);
}
上面代码是从internet上copy下来的,anyway, enjoy ~

你可能感兴趣的:(巧用Marshal.GetDelegateForFunctionPointer--C#如何调用按键精灵插件dll)