// 首先得到输入框的句柄。通过spy++这类工具分析,聊天窗体的类名为“#32770”
// 但当前系统里不只一个类名为“#32770”的窗体,这就需要全体遍历一次。
// 类名为“#32770”标题含“聊天”基本能确定。为保险起见还判断一下所在进程是否为“qq.exe”
uses PsAPI, RichEdit;
function Process_ReadRichEditText(AHandle: THandle): WideString;
var
vGetTextEx: GETTEXTEX;
vGetTextLengthEx: GETTEXTLENGTHEX;
L: Integer;
vProcessId: DWORD;
vProcess: THandle;
vPointer: Pointer;
vNumberOfBytesRead: Cardinal;
begin
Result := '';
if not IsWindow(AHandle) then Exit;
GetWindowThreadProcessId(AHandle, @vProcessId);
vProcess := OpenProcess(PROCESS_VM_OPERATION or PROCESS_VM_READ or
PROCESS_VM_WRITE, False, vProcessId);
try
vPointer := VirtualAllocEx(vProcess, nil, 4096, MEM_RESERVE or MEM_COMMIT,
PAGE_READWRITE);
vGetTextLengthEx.flags := GTL_DEFAULT;
vGetTextLengthEx.codepage := 1200; // Unicode
WriteProcessMemory(vProcess, vPointer, @vGetTextLengthEx,
SizeOf(vGetTextLengthEx), vNumberOfBytesRead);
L := SendMessage(AHandle, EM_GETTEXTLENGTHEX, Integer(vPointer), 0);
VirtualFreeEx(vProcess, vPointer, 0, MEM_RELEASE);
if L <= 0 then Exit;
vPointer := VirtualAllocEx(vProcess, nil, SizeOf(vGetTextEx) + L * 2 + 2,
MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE);
SetLength(Result, L);
vGetTextEx.cb := L * 2 + 2;
vGetTextEx.flags := GT_DEFAULT;
vGetTextEx.codepage := 1200; // Unicode
vGetTextEx.lpDefaultChar := nil;
vGetTextEx.lpUsedDefChar := nil;
WriteProcessMemory(vProcess, vPointer, @vGetTextEx,
SizeOf(vGetTextEx), vNumberOfBytesRead);
SendMessage(AHandle, EM_GETTEXTEX, Integer(vPointer),
Integer(vPointer) + SizeOf(vGetTextEx));
ReadProcessMemory(vProcess, Pointer(Integer(vPointer) + SizeOf(vGetTextEx)),
@Result[1], L * 2, vNumberOfBytesRead);
VirtualFreeEx(vProcess, vPointer, 0, MEM_RELEASE);
finally
CloseHandle(vProcess);
end;
end; { Process_ReadRichEditText }
function GetProcessName(AProcessID: THandle): string;
var
vBuffer: array[0..MAX_PATH] of Char;
vProcess: THandle;
begin
vProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False,
AProcessID);
try
if GetModuleBaseName(vProcess, 0, vBuffer, SizeOf(vBuffer)) > 0 then
Result := vBuffer
else Result := '';
finally
CloseHandle(vProcess);
end;
end; { GetProcessName }
function EnumChild(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
var
vBuffer: array[0..255] of Char;
begin
Result := True;
if not IsWindowVisible(hwnd) then Exit; // 不可见
GetClassName(hwnd, vBuffer, SizeOf(vBuffer));
if SameText(vBuffer, 'RichEdit20A') then
begin
if GetWindowLong(hwnd, GWL_STYLE) and ES_READONLY <> ES_READONLY then // 非只读
begin
PInteger(lParam)^ := hwnd;
Result := False;
end;
end;
end; { EnumChild }
function EnumFunc(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
var
vBuffer: array[0..255] of Char;
vProcessId: THandle;
begin
Result := True;
if not IsWindowVisible(hwnd) then Exit; // 不可见
GetClassName(hwnd, vBuffer, SizeOf(vBuffer));
if SameText(vBuffer, '#32770') then
begin
GetWindowThreadProcessId(hwnd, vProcessId);
if SameText(GetProcessName(vProcessId), 'qq.exe') then
begin
GetWindowText(hwnd, vBuffer, SizeOf(vBuffer));
if Pos('聊天中', vBuffer) > 0 then // 标题中含"聊天中"
begin
EnumChildWindows(hwnd, @EnumChild, lParam);
Result := False;
end;
end;
end;
end; { EnumFunc }
procedure TForm1.Button1Click(Sender: TObject);
var
vHandle: THandle;
begin
vHandle := 0;
EnumWindows(@EnumFunc, Integer(@vHandle));
if vHandle = 0 then Exit;
Memo1.Text := Process_ReadRichEditText(vHandle);
end;
using
System.Runtime.InteropServices;
[DllImport(
"
User32.DLL
"
)]
public
static
extern
int
SendMessage(IntPtr hWnd,
uint
Msg,
int
wParam,
int
lParam);
public
delegate
bool
WNDENUMPROC(IntPtr hwnd,
int
lParam);
[DllImport(
"
user32.dll
"
)]
public
static
extern
int
EnumWindows(WNDENUMPROC lpEnumFunc,
int
lParam);
[DllImport(
"
user32.dll
"
)]
public
static
extern
int
EnumChildWindows(IntPtr hWndParent,
WNDENUMPROC lpEnumFunc,
int
lParam);
[DllImport(
"
user32.dll
"
)]
public
static
extern
int
GetWindowText(IntPtr hWnd, StringBuilder lpString,
int
nMaxCount);
[DllImport(
"
user32.dll
"
)]
public
static
extern
int
GetClassName(IntPtr hWnd, StringBuilder lpClassName,
int
nMaxCount);
[DllImport(
"
user32.dll
"
)]
public
static
extern
bool
IsWindow(IntPtr hWnd);
[DllImport(
"
user32.dll
"
)]
public
static
extern
bool
IsWindowVisible(IntPtr hWnd);
[DllImport(
"
user32.DLL
"
)]
public
static
extern
IntPtr FindWindowEx(IntPtr hwndParent,
IntPtr hwndChildAfter,
string
lpszClass,
string
lpszWindow);
[DllImport(
"
user32.dll
"
)]
public
static
extern
uint
GetWindowThreadProcessId(IntPtr hWnd,
out
uint
dwProcessId);
[DllImport(
"
psapi.dll
"
)]
public
static
extern
uint
GetModuleBaseName(IntPtr hProcess, IntPtr hModule,
StringBuilder lpBaseName,
uint
nSize);
public
const
uint
PROCESS_VM_OPERATION
=
0x0008
;
public
const
uint
PROCESS_VM_READ
=
0x0010
;
public
const
uint
PROCESS_VM_WRITE
=
0x0020
;
public
const
uint
PROCESS_QUERY_INFORMATION
=
0x0400
;
[DllImport(
"
kernel32.dll
"
)]
public
static
extern
IntPtr OpenProcess(
uint
dwDesiredAccess,
bool
bInheritHandle,
uint
dwProcessId);
[DllImport(
"
kernel32.dll
"
)]
public
static
extern
bool
CloseHandle(IntPtr handle);
[DllImport(
"
user32.dll
"
)]
public
static
extern
int
GetWindowLong(IntPtr hWnd,
int
nIndex);
public
const
int
GWL_STYLE
=
-
16
;
public
const
int
ES_READONLY
=
0x800
;
public
const
uint
MEM_COMMIT
=
0x1000
;
public
const
uint
MEM_RELEASE
=
0x8000
;
public
const
uint
MEM_RESERVE
=
0x2000
;
public
const
uint
PAGE_READWRITE
=
4
;
[DllImport(
"
kernel32.dll
"
)]
public
static
extern
IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,
uint
dwSize,
uint
flAllocationType,
uint
flProtect);
[DllImport(
"
kernel32.dll
"
)]
public
static
extern
bool
VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress,
uint
dwSize,
uint
dwFreeType);
[DllImport(
"
kernel32.dll
"
)]
public
static
extern
bool
WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
IntPtr lpBuffer,
int
nSize,
ref
uint
vNumberOfBytesRead);
[DllImport(
"
kernel32.dll
"
)]
public
static
extern
bool
ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
IntPtr lpBuffer,
int
nSize,
ref
uint
vNumberOfBytesRead);
private
IntPtr richHandle;
public
string
GetProcessName(
uint
AProcessId)
...
{
StringBuilder vBuffer = new StringBuilder(256);
IntPtr vProcess = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, AProcessId);
try
...{
if (GetModuleBaseName(vProcess, IntPtr.Zero, vBuffer,
(uint)vBuffer.Capacity) > 0)
return vBuffer.ToString();
else return string.Empty;
}
finally
...{
CloseHandle(vProcess);
}
}
public
bool
EnumChild(IntPtr hwnd,
int
lParam)
...
{
if (!IsWindowVisible(hwnd)) return true; // 不可见
StringBuilder vBuffer = new StringBuilder(256);
GetClassName(hwnd, vBuffer, vBuffer.Capacity);
if (vBuffer.ToString().ToLower() == "richedit20a")
...{
if ((GetWindowLong(hwnd, GWL_STYLE) & ES_READONLY) != ES_READONLY) // 非只读
...{
richHandle = hwnd;
return false;
}
}
return true;
}
public
bool
EnumFunc(IntPtr hwnd,
int
lParam)
...
{
if (!IsWindowVisible(hwnd)) return true; // 不可见
StringBuilder vBuffer = new StringBuilder(256);
GetClassName(hwnd, vBuffer, vBuffer.Capacity);
if (vBuffer.ToString() == "#32770")
...{
uint vProcessId;
GetWindowThreadProcessId(hwnd, out vProcessId);
if (GetProcessName(vProcessId).ToLower() == "qq.exe")
...{
GetWindowText(hwnd, vBuffer, vBuffer.Capacity);
if (vBuffer.ToString().IndexOf("聊天中") >= 0) // 标题中含"聊天中"
...{
EnumChildWindows(hwnd, @EnumChild, lParam);
return false;
}
}
}
return true;
}
[StructLayout(LayoutKind.Sequential)]
public
struct
GETTEXTLENGTHEX
...
{
public uint flags;
public uint codepage;
}
[StructLayout(LayoutKind.Sequential)]
public
struct
GETTEXTEX
...
{
public int cb;
public int flags;
public int codepage;
public IntPtr lpDefaultChar;
public IntPtr lpUsedDefChar;
}
;
public
const
int
GTL_DEFAULT
=
0
;
public
const
int
GT_DEFAULT
=
0
;
public
const
int
WM_USER
=
0x0400
;
public
const
int
EM_GETTEXTEX
=
WM_USER
+
94
;
public
const
int
EM_GETTEXTLENGTHEX
=
WM_USER
+
95
;
public
string
Process_ReadRichEditText(IntPtr AHandle)
...
{
if (!IsWindow(AHandle)) return string.Empty;
string vReturn = string.Empty;
uint vProcessId;
GetWindowThreadProcessId(AHandle, out vProcessId);
IntPtr vProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ |
PROCESS_VM_WRITE, false, vProcessId);
try
...{
uint vNumberOfBytesRead = 0;
IntPtr vPointer = VirtualAllocEx(vProcess, IntPtr.Zero, 0x1000,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
GETTEXTLENGTHEX vGetTextLengthEx = new GETTEXTLENGTHEX();
vGetTextLengthEx.flags = GTL_DEFAULT;
vGetTextLengthEx.codepage = 1200; // Unicode
IntPtr vAddress = Marshal.AllocCoTaskMem(Marshal.SizeOf(vGetTextLengthEx));
Marshal.StructureToPtr(vGetTextLengthEx, vAddress, false);
WriteProcessMemory(vProcess, vPointer, vAddress,
Marshal.SizeOf(vGetTextLengthEx), ref vNumberOfBytesRead);
Marshal.FreeCoTaskMem(vAddress);
int L = SendMessage(AHandle, EM_GETTEXTLENGTHEX, (int)vPointer, 0);
VirtualFreeEx(vProcess, vPointer, 0, MEM_RELEASE);
if (L <= 0) return vReturn;
GETTEXTEX vGetTextEx = new GETTEXTEX();
vGetTextEx.cb = L * 2 + 2;
vGetTextEx.flags = GT_DEFAULT;
vGetTextEx.codepage = 1200; // Unicode
vGetTextEx.lpDefaultChar = IntPtr.Zero;
vGetTextEx.lpUsedDefChar = IntPtr.Zero;
vPointer = VirtualAllocEx(vProcess, IntPtr.Zero,
(uint)(Marshal.SizeOf(vGetTextEx) + L * 2 + 2),
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
vAddress = Marshal.AllocCoTaskMem(Marshal.SizeOf(vGetTextEx));
Marshal.StructureToPtr(vGetTextEx, vAddress, false);
WriteProcessMemory(vProcess, vPointer, vAddress,
Marshal.SizeOf(vGetTextEx), ref vNumberOfBytesRead);
Marshal.FreeCoTaskMem(vAddress);
SendMessage(AHandle, EM_GETTEXTEX, (int)vPointer,
(int)vPointer + Marshal.SizeOf(vGetTextEx));
vAddress = Marshal.AllocCoTaskMem(L * 2);
ReadProcessMemory(vProcess,
(IntPtr)((int)vPointer + Marshal.SizeOf(vGetTextEx)),
vAddress, L * 2, ref vNumberOfBytesRead);
vReturn = Marshal.PtrToStringUni(vAddress, L * 2);
Marshal.FreeCoTaskMem(vAddress);
VirtualFreeEx(vProcess, vPointer, 0, MEM_RELEASE);
}
finally
...{
CloseHandle(vProcess);
}
return vReturn;
}
private
void
button1_Click(
object
sender, EventArgs e)
...
{
richHandle = IntPtr.Zero;
EnumWindows(EnumFunc, 0);
if (richHandle == IntPtr.Zero) return;
Console.WriteLine(Process_ReadRichEditText(richHandle));
}