// 首先得到输入框的句柄。通过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;
usingSystem.Runtime.InteropServices;

[DllImport("User32.DLL")]
publicstaticexternintSendMessage(IntPtrhWnd,uintMsg,intwParam,intlParam);

publicdelegateboolWNDENUMPROC(IntPtrhwnd,intlParam);

[DllImport("user32.dll")]
publicstaticexternintEnumWindows(WNDENUMPROClpEnumFunc,intlParam);
[DllImport("user32.dll")]
publicstaticexternintEnumChildWindows(IntPtrhWndParent,
WNDENUMPROClpEnumFunc,intlParam);
[DllImport("user32.dll")]
publicstaticexternintGetWindowText(IntPtrhWnd,StringBuilderlpString,
intnMaxCount);
[DllImport("user32.dll")]
publicstaticexternintGetClassName(IntPtrhWnd,StringBuilderlpClassName,
intnMaxCount);
[DllImport("user32.dll")]
publicstaticexternboolIsWindow(IntPtrhWnd);
[DllImport("user32.dll")]
publicstaticexternboolIsWindowVisible(IntPtrhWnd);
[DllImport("user32.DLL")]
publicstaticexternIntPtrFindWindowEx(IntPtrhwndParent,
IntPtrhwndChildAfter,stringlpszClass,stringlpszWindow);
[DllImport("user32.dll")]
publicstaticexternuintGetWindowThreadProcessId(IntPtrhWnd,
outuintdwProcessId);

[DllImport("psapi.dll")]
publicstaticexternuintGetModuleBaseName(IntPtrhProcess,IntPtrhModule,
StringBuilderlpBaseName,uintnSize);

publicconstuintPROCESS_VM_OPERATION=0x0008;
publicconstuintPROCESS_VM_READ=0x0010;
publicconstuintPROCESS_VM_WRITE=0x0020;
publicconstuintPROCESS_QUERY_INFORMATION=0x0400;

[DllImport("kernel32.dll")]
publicstaticexternIntPtrOpenProcess(uintdwDesiredAccess,
boolbInheritHandle,uintdwProcessId);

[DllImport("kernel32.dll")]
publicstaticexternboolCloseHandle(IntPtrhandle);

[DllImport("user32.dll")]
publicstaticexternintGetWindowLong(IntPtrhWnd,intnIndex);
publicconstintGWL_STYLE=-16;
publicconstintES_READONLY=0x800;

publicconstuintMEM_COMMIT=0x1000;
publicconstuintMEM_RELEASE=0x8000;

publicconstuintMEM_RESERVE=0x2000;
publicconstuintPAGE_READWRITE=4;

[DllImport("kernel32.dll")]
publicstaticexternIntPtrVirtualAllocEx(IntPtrhProcess,IntPtrlpAddress,
uintdwSize,uintflAllocationType,uintflProtect);

[DllImport("kernel32.dll")]
publicstaticexternboolVirtualFreeEx(IntPtrhProcess,IntPtrlpAddress,
uintdwSize,uintdwFreeType);

[DllImport("kernel32.dll")]
publicstaticexternboolWriteProcessMemory(IntPtrhProcess,IntPtrlpBaseAddress,
IntPtrlpBuffer,intnSize,refuintvNumberOfBytesRead);

[DllImport("kernel32.dll")]
publicstaticexternboolReadProcessMemory(IntPtrhProcess,IntPtrlpBaseAddress,
IntPtrlpBuffer,intnSize,refuintvNumberOfBytesRead);

privateIntPtrrichHandle;

publicstringGetProcessName(uintAProcessId)

...{
StringBuildervBuffer=newStringBuilder(256);
IntPtrvProcess=OpenProcess(
PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,false,AProcessId);
try

...{
if(GetModuleBaseName(vProcess,IntPtr.Zero,vBuffer,
(uint)vBuffer.Capacity)>0)
returnvBuffer.ToString();
elsereturnstring.Empty;
}
finally

...{
CloseHandle(vProcess);
}
}

publicboolEnumChild(IntPtrhwnd,intlParam)

...{
if(!IsWindowVisible(hwnd))returntrue;//不可见
StringBuildervBuffer=newStringBuilder(256);
GetClassName(hwnd,vBuffer,vBuffer.Capacity);
if(vBuffer.ToString().ToLower()=="richedit20a")

...{
if((GetWindowLong(hwnd,GWL_STYLE)&ES_READONLY)!=ES_READONLY)//非只读

...{
richHandle=hwnd;
returnfalse;
}
}
returntrue;
}

publicboolEnumFunc(IntPtrhwnd,intlParam)

...{
if(!IsWindowVisible(hwnd))returntrue;//不可见
StringBuildervBuffer=newStringBuilder(256);
GetClassName(hwnd,vBuffer,vBuffer.Capacity);
if(vBuffer.ToString()=="#32770")

...{
uintvProcessId;
GetWindowThreadProcessId(hwnd,outvProcessId);
if(GetProcessName(vProcessId).ToLower()=="qq.exe")

...{
GetWindowText(hwnd,vBuffer,vBuffer.Capacity);
if(vBuffer.ToString().IndexOf("聊天中")>=0)//标题中含"聊天中"

...{
EnumChildWindows(hwnd,@EnumChild,lParam);
returnfalse;
}
}
}
returntrue;
}

[StructLayout(LayoutKind.Sequential)]
publicstructGETTEXTLENGTHEX

...{
publicuintflags;
publicuintcodepage;
}

[StructLayout(LayoutKind.Sequential)]
publicstructGETTEXTEX

...{
publicintcb;
publicintflags;
publicintcodepage;
publicIntPtrlpDefaultChar;
publicIntPtrlpUsedDefChar;
};

publicconstintGTL_DEFAULT=0;
publicconstintGT_DEFAULT=0;
publicconstintWM_USER=0x0400;
publicconstintEM_GETTEXTEX=WM_USER+94;
publicconstintEM_GETTEXTLENGTHEX=WM_USER+95;

publicstringProcess_ReadRichEditText(IntPtrAHandle)

...{
if(!IsWindow(AHandle))returnstring.Empty;
stringvReturn=string.Empty;
uintvProcessId;
GetWindowThreadProcessId(AHandle,outvProcessId);
IntPtrvProcess=OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|
PROCESS_VM_WRITE,false,vProcessId);
try

...{
uintvNumberOfBytesRead=0;
IntPtrvPointer=VirtualAllocEx(vProcess,IntPtr.Zero,0x1000,
MEM_RESERVE|MEM_COMMIT,PAGE_READWRITE);
GETTEXTLENGTHEXvGetTextLengthEx=newGETTEXTLENGTHEX();
vGetTextLengthEx.flags=GTL_DEFAULT;
vGetTextLengthEx.codepage=1200;//Unicode
IntPtrvAddress=Marshal.AllocCoTaskMem(Marshal.SizeOf(vGetTextLengthEx));
Marshal.StructureToPtr(vGetTextLengthEx,vAddress,false);
WriteProcessMemory(vProcess,vPointer,vAddress,
Marshal.SizeOf(vGetTextLengthEx),refvNumberOfBytesRead);
Marshal.FreeCoTaskMem(vAddress);
intL=SendMessage(AHandle,EM_GETTEXTLENGTHEX,(int)vPointer,0);
VirtualFreeEx(vProcess,vPointer,0,MEM_RELEASE);
if(L<=0)returnvReturn;
GETTEXTEXvGetTextEx=newGETTEXTEX();
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),refvNumberOfBytesRead);
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,refvNumberOfBytesRead);
vReturn=Marshal.PtrToStringUni(vAddress,L*2);
Marshal.FreeCoTaskMem(vAddress);
VirtualFreeEx(vProcess,vPointer,0,MEM_RELEASE);
}
finally

...{
CloseHandle(vProcess);
}
returnvReturn;
}

privatevoidbutton1_Click(objectsender,EventArgse)

...{
richHandle=IntPtr.Zero;
EnumWindows(EnumFunc,0);
if(richHandle==IntPtr.Zero)return;
Console.WriteLine(Process_ReadRichEditText(richHandle));
}
