激烈振动

Visit My MSN Space

导航

进程外内存空间分配(续)

原理讲完了,下面给点代码吧。根据前面讲的步骤,也不算特别复杂,就是一组API的应用,不过写起来还是比较复杂的,为了方便使用,我总结出一组宏来方便代码编写。当时正热衷于ATL,对宏起到的作用非常崇拜,因此模仿了一下。我知道许多人不喜欢宏,但如果合理应用,还是非常管用的,如有异议,见ATL代码。
下面就是完整的宏代码:

//////////////////////////////////////////////////////////////////////////////

///  简化进程外内存分配的宏.

/// 开始在hWnd所在的进程分配内存, size为分配内存的大小

#define  DST_BEGIN_VM_ALLOC(hWnd, size) \

     DST_BEGIN_OS_SPECIAL(VER_PLATFORM_WIN32_NT) \

     DWORD dwProcessId; \

     GetWindowThreadProcessId(hWnd, &dwProcessId); \

     HANDLE hProcess = OpenProcess( \

         PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, \

         FALSE, dwProcessId); \

     if (hProcess == NULL) \

     { \

     ::MessageBox(NULL, _T("Could not communicate with process"), \

              _T("Error"), MB_OK | MB_ICONWARNING); \

     } \

     void*    pRemoteBuffer = VirtualAllocEx(hProcess, \

         NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

 

///////////////////////////////////////////////////////////

/// 结束进程外内存分配

#define DST_END_VM_ALLOC() \

     VirtualFreeEx(hProcess, pRemoteBuffer, 0, MEM_RELEASE); \

     CloseHandle(hProcess); \

     DST_END_OS_SPECIAL()

 

//////////////////////////////////////////////////////////////////   

/// 向已分配的进程地址中写入数据, offset为写入地址的偏移量

/// pBuffer 为要写入的数据缓冲区, size为写入内存的尺寸

#define DST_WRITE_VM(offset, pBuffer, size) \

     ::WriteProcessMemory(hProcess, (LPBYTE)pRemoteBuffer + offset, pBuffer, size, NULL);

 

///////////////////////////////////////////////////////////////

/// 从进程地址中读出数据, offset为读取地址的偏移量

/// pBuffer为读取结果缓冲区, size为读取字节数

#define DST_READ_VM(offset, pBuffer, size) \

     ::ReadProcessMemory(hProcess, (LPBYTE)pRemoteBuffer + offset, pBuffer, size, NULL);

 

///////////////////////////////////////////////////////////////////

/// 得到已分配的进程外内存地址, offset为偏移量

#define DST_GET_VM(offset) ((LPBYTE)pRemoteBuffer + offset)

    

/////////////////////////////////////////////////////////////////////

///  开始执行特定操作系统代码

#define DST_BEGIN_OS_SPECIAL(osid) \

     { \

         OSVERSIONINFO osinfo; \

         memset(&osinfo, 0, sizeof(osinfo)); \

         osinfo.dwOSVersionInfoSize = sizeof(osinfo); \

         GetVersionEx(&osinfo); \

         if (osinfo.dwPlatformId==osid) \

         {

 

////////////////////////////////////////////////////////////////////////

/// 结束执行特定操作系统代码

#define DST_END_OS_SPECIAL() \

         } \

     }

 

有了这些宏,接下来的代码就很方便了。每条宏的作用不多讲了,反正代码都在这里,有兴趣的朋友自己看了。这里还是给出一些实际使用的代码吧,还是以得到ProgressBar的范围为例子。

  
 PBRANGE range;
 DST_BEGIN_VM_ALLOC(hWnd, 256);
 DST_WRITE_VM(0, &range, sizeof(range));
 ::SendMessage(hWnd, PBM_GETRANGE, TRUE, (LPARAM)DST_GET_VM(0));
 DST_READ_VM(0, &range, sizeof(range));
 DST_END_VM_ALLOC()

好了,上面这段代码结束后,变量range就得到了目标进程中进度条的变化范围。稍微解释一下上面的代码吧:

  • hWnd是进度条窗口的句柄
  • DST_BEGIN_VM_ALLOC宏在进度条窗口所在进程分配了256字节的内存;
  • DST_WRITE_VM把本进程range变量的内容写到了目标进程刚分配的内存中;
  • SendMessage调用结束后,进度条窗口把进度条的范围写到了所在进程的内存中;
  • 然后DST_READ_VM宏从目标进程的内存里读取数据,并填充到本进程的range变量中;
  • 最后DST_END_VM_ALLOC宏做清理工作。
上面仅给出了最简单的示例,其实利用这些宏可以做很多非常有意思的工作,上面代码来自本人做的一个项目,叫做UI克隆器,它的作用是读取其他进程的界面,并生成一个完全一样的窗口,包括其所有控件、菜单等。当然,用来做普通的进程间内存共享也是不错的。
顺便说一下,某些API函数只有在Windows2000及更高版本的系统上才支持,因此,Windows98系统就不能使用这些功能了。为了让代码在98系统下编译运行不出现错误,所以定义了DST_BEGIN_OS_SPECIAL和DST_END_OS_SPECIAL宏,这两个宏已经在其他宏里面嵌套使用了,但也可以单独使用,不多说了,反正对于宏这个东西,我还是比较喜欢的,当然要合理使用

      posted on 2004-04-21 10:56  vibration  阅读(1207)  评论(2编辑  收藏  举报