Overview
CProcessData is a template class that makes it easy to use data
allocated in a different process, and is useful when making inter-process
SendMessage/PostMessage calls.
Example Scenario - 1
Imagine that you are sending a DTM_SETSYSTEMTIME message to a
Date/Time picker control in a different process.
Without CProcessData
SYSTEMTIME systim;
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
LPVOID lpData = VirtualAllocEx(hProc, NULL,
sizeof SYSTEMTIME, MEM_COMMIT, PAGE_READWRITE);
WriteProcessMemory(hProc, lpData, (LPCVOID)&systim,
sizeof SYSTEMTIME, NULL);
DWORD dwResult = (DWORD)::SendMessage(hwnd,
DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&lpData);
if(dwResult == 0)
{
DWORD err = GetLastError();
}
VirtualFreeEx(hProc, lpData, NULL, MEM_RELEASE);
CloseHandle(hProc);
Using CProcessData
SYSTEMTIME systim;
CProcessData<SYSTEMTIME> data(pid);
data.WriteData(systim);
DWORD dwResult = (DWORD)::SendMessage(hwnd,
DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)data.GetData());
if(dwResult == 0)
{
DWORD err = GetLastError();
}
Note only have you saved on lines of code, but you don't run the risk of
forgetting to free the allocated memory or closing the process handle.
Example Scenario - 2
Imagine that you are retrieving toolbar info from a toolbar control in a
different process.
Without CProcessData
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
LPVOID lpData = VirtualAllocEx(hProc, NULL,
sizeof TBBUTTON, MEM_COMMIT, PAGE_READWRITE);
::SendMessage(hwnd, TB_GETBUTTON, index, (LPARAM)lpData);
TBBUTTON tb;
ReadProcessMemory(hProc, lpData,(LPVOID)&tb,
sizeof TBBUTTON, NULL)
CUSTOMDATA cus;
ReadProcessMemory(hProc, (LPCVOID)tb.dwData, (LPVOID)&cus,
sizeof CUSTOMDATA, NULL)
VirtualFreeEx(hProc,lpData,NULL,MEM_RELEASE);
CloseHandle(hProc);
Using CProcessData
CProcessData<TBBUTTON> data(pid);
::SendMessage(hwnd, TB_GETBUTTON, index,
(LPARAM)data.GetData());
TBBUTTON tb;
data.ReadData(&tb);
CUSTOMDATA cus;
data.ReadData<CUSTOMDATA>(&cus,(LPCVOID)tb.dwData);
Pretty neat, huh?
Class Reference
Constructor
CProcessData(DWORD dwProcessId = 0, DWORD dwDesiredAccess =
PROCESS_ALL_ACCESS, DWORD flAllocationType = MEM_COMMIT, DWORD flProtect =
PAGE_READWRITE)
If you pass in a dwProcessId of 0, the current process Id
is used. For the other arguments, see MSDN documentation for OpenProcess
and VirtualAllocEx.
WriteData
BOOL WriteData(const T& data)
WriteData copies data to memory in the foreign
process
ReadData
BOOL ReadData(T* data)
ReadData reads back from the memory in the foreign process into
data.
ReadData (template version)
template<typename TSUBTYPE> BOOL ReadData(TSUBTYPE* data, LPCVOID
lpData)
Templated ReadData that's used to read a specific data type from
a memory address located in the foreign process.
Full source listing
[Listing has been formatted to fit within 600 pixels. Actual source code
(included as a download) is formatted for wider screens]
#pragma once
template<typename T> 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<typename TSUBTYPE> 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;
};
History
- June 16, 2005 : Small bug fix in destructor. (
VirtualFreeEx
and CloseHandle were in the wrong order)
- June 10, 2005 : Article first published.
- June 9, 2005 : Class written.
Nish is a real nice guy who has been writing code since 1990 when he first got his hands on an 8088 with 640 KB RAM. Originally from sunny Trivandrum in India, he has been living in various places over the past few years and often thinks it’s time he settled down somewhere.
Nish has been a Microsoft Visual C++ MVP since October, 2002 - awfully nice of Microsoft, he thinks. He maintains an MVP tips and tricks web site -
www.voidnish.com where you can find a consolidated list of his articles, writings and ideas on VC++, MFC, .NET and C++/CLI. Oh, and you might want to check out his blog on C++/CLI, MFC, .NET and a lot of other stuff -
blog.voidnish.com.
Nish loves reading Science Fiction, P G Wodehouse and Agatha Christie, and also fancies himself to be a decent writer of sorts. He has authored a romantic comedy
Summer Love and Some more Cricket as well as a programming book –
Extending MFC applications with the .NET Framework.
Nish's latest book
C++/CLI in Action published by Manning Publications is now available for purchase. You can read more about the book on his blog.
Despite his wife's attempts to get him into cooking, his best effort so far has been a badly done omelette. Some day, he hopes to be a good cook, and to cook a tasty dinner for his wife.