在Windows编程中,跨进程通信是一个常见的需求。当需要与另一个进程中的控件交互时,例如发送消息或获取数据,通常需要使用到SendMessage或PostMessage函数。然而,这些函数要求手动管理内存分配和释放,以及进程句柄的关闭,这无疑增加了编程的复杂性。为了简化这一过程,可以使用CProcessData类,这是一个模板类,它封装了跨进程通信中的数据操作。
CProcessData类提供了一种便捷的方式来在不同进程之间传递数据。它通过封装OpenProcess和VirtualAllocEx等Windows API函数,使得在不同进程间读写数据变得更加简单。
CProcessData类的构造函数允许指定目标进程的ID、访问权限、内存分配类型和内存保护属性。如果进程ID设置为0,则默认使用当前进程的ID。
CProcessData(DWORD dwProcessId = 0, DWORD dwDesiredAccess = PROCESS_ALL_ACCESS, DWORD flAllocationType = MEM_COMMIT, DWORD flProtect = PAGE_READWRITE);
    
WriteData方法用于将数据复制到目标进程的内存中。它接受一个引用参数,并将其内容写入到目标进程的内存地址。
BOOL WriteData(const T& data);
    
ReadData方法用于从目标进程的内存中读取数据。它接受一个指针参数,并将目标进程内存中的数据读取到该指针指向的变量中。
BOOL ReadData(T* data);
    
CProcessData类还提供了一个模板化的ReadData方法,允许从目标进程的特定内存地址读取特定类型的数据。
template BOOL ReadData(TSUBTYPE* data, LPCVOID lpData);
     
GetData方法用于获取在目标进程中分配的内存地址。
const T* GetData();
    
假设需要向另一个进程中的日期/时间选择控件发送DTM_SETSYSTEMTIME消息。使用CProcessData类,可以简化这一过程。
SYSTEMTIME systim;
// 填充systim
CProcessData data(pid);
data.WriteData(systim);
DWORD dwResult = (DWORD)::SendMessage(hwnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)data.GetData());
if (dwResult == 0) {
    DWORD err = GetLastError();
    // ...
}
     
通过使用CProcessData类,不仅减少了代码量,还避免了忘记释放内存或关闭进程句柄的风险。
再比如,需要从另一个进程的Toolbar控件中获取按钮信息。使用CProcessData类,同样可以简化这一过程。
CProcessData data(pid);
::SendMessage(hwnd, TB_GETBUTTON, index, (LPARAM)data.GetData());
TBBUTTON tb;
data.ReadData(&tb);
CUSTOMDATA cus;
data.ReadData(&cus, (LPCVOID)tb.dwData);
      
CProcessData类使得跨进程通信变得更加简洁和安全。
CProcessData类是一个模板类,它提供了跨进程通信中数据操作的封装。以下是类的源代码参考:
/*
Author  :    Nishant Sivakumar
Date    :    June 9, 2005
Info    :    Template class that makes it easy to use data allocated
            in a different process. Useful when making inter-process
            SendMessage/PostMessage calls.
Contact :    nish#voidnish.com
*/
//
ProcessData.h
#pragma once
template
class CProcessData
{
public:
    CProcessData(DWORD dwProcessId = 0, DWORD dwDesiredAccess = PROCESS_ALL_ACCESS, DWORD flAllocationType = MEM_COMMIT, DWORD flProtect = PAGE_READWRITE)
    {
        m_hProcess = OpenProcess(dwDesiredAccess, FALSE, dwProcessId ? dwProcessId : GetCurrentProcessId());
        ASSERT(m_hProcess);
        if (m_hProcess)
        {
            m_lpData = VirtualAllocEx(m_hProcess, NULL, sizeof(T), flAllocationType, flProtect);
            ASSERT(m_lpData);
        }
    }
    ~CProcessData()
    {
        if (m_hProcess)
        {
            if (m_lpData)
            {
                VirtualFreeEx(m_hProcess, m_lpData, NULL, MEM_RELEASE);
            }
            CloseHandle(m_hProcess);
        }
    }
    BOOL WriteData(const T& data)
    {
        return (m_hProcess && m_lpData) ? WriteProcessMemory(m_hProcess, m_lpData, (LPCVOID)&data, sizeof(T), NULL) : FALSE;
    }
    BOOL ReadData(T* data)
    {
        return (m_hProcess && m_lpData) ? ReadProcessMemory(m_hProcess, m_lpData, (LPVOID)data, sizeof(T), NULL) : FALSE;
    }
    template
    BOOL ReadData(TSUBTYPE* data, LPCVOID lpData)
    {
        return m_hProcess ? ReadProcessMemory(m_hProcess, lpData, (LPVOID)data, sizeof(TSUBTYPE), NULL) : FALSE;
    }
    const T* GetData()
    {
        return (m_hProcess && m_lpData) ? (T*)m_lpData : NULL;
    }
private:
    T m_Data;
    HANDLE m_hProcess;
    LPVOID m_lpData;
};