我试图使用WinAPI更改另一个应用程序窗口中页面控件中的一个选项卡。
我向页面控件发送了一条TCM_SETCURSEL消息,该消息确实更改了选项卡,但没有更改选项卡内容。例: Pagecontrol在选项卡0上,我向页面控件发送一个TCM_SETCURSEL索引: 1,页面控件现在在表1上,但是继续显示选项卡0的内容而不是标签1的内容。
我试过:
我正在使用delphi 2010,目标应用程序也是delphi应用程序。
这是最后一个代码迭代,它将通知发送到页控件的父控件:
procedure ChangeTab(PageControlHandle: HWND; TabIndex: Integer);
var
Info: TNMHdr;
begin
Info.hwndFrom := PageControlHandle;
Info.idFrom := GetWindowLongPtr(PageControlHandle, GWL_ID);
Info.code := TCN_SELCHANGING;
if SendMessage(GetParent(PageControlHandle), WM_NOTIFY, PageControlHandle, lParam(@Info)) <> 0 then
raise Exception.Create('Page control didn''t allow tab to change.');
if SendMessage(PageControlHandle, TCM_SETCURSEL, TabIndex, 0) = -1 then
raise Exception.Create('Failed to change tab.');
Info.code := TCN_SELCHANGE;
SendMessage(GetParent(PageControlHandle), WM_NOTIFY, PageControlHandle, lParam(@Info))
end;当我单击选项卡1时,WinSpy显示它收到了以下消息:
<000001> 001D0774 S WM_WINDOWPOSCHANGING lpwp:0018F308
<000002> 001D0774 R WM_WINDOWPOSCHANGING
<000003> 001D0774 S WM_CHILDACTIVATE
<000004> 001D0774 R WM_CHILDACTIVATE
<000005> 001D0774 S WM_WINDOWPOSCHANGED lpwp:0018F308
<000006> 001D0774 R WM_WINDOWPOSCHANGED
<000007> 001D0774 S WM_WINDOWPOSCHANGING lpwp:0018EF7C
<000008> 001D0774 R WM_WINDOWPOSCHANGING
<000009> 001D0774 S WM_NCPAINT hrgn:00000001
<000010> 001D0774 R WM_NCPAINT
<000011> 001D0774 S WM_ERASEBKGND hdc:33011920
<000012> 001D0774 R WM_ERASEBKGND fErased:True
<000013> 001D0774 S WM_WINDOWPOSCHANGED lpwp:0018EF7C
<000014> 001D0774 R WM_WINDOWPOSCHANGED
<000015> 001D0774 P WM_PAINT hdc:00000000
<000016> 001D0774 S WM_CTLCOLORSTATIC hdcStatic:FB01097B hwndStatic:001507D0
<000017> 001D0774 R WM_CTLCOLORSTATIC hBrush:261011F7
<000018> 001D0774 S WM_CTLCOLORSTATIC hdcStatic:FB01097B hwndStatic:001507D0
<000019> 001D0774 R WM_CTLCOLORSTATIC hBrush:261011F7
<000020> 001D0774 S WM_CTLCOLORSTATIC hdcStatic:530112DB hwndStatic:000608C2
<000021> 001D0774 R WM_CTLCOLORSTATIC hBrush:261011F7
<000022> 001D0774 S WM_CTLCOLORSTATIC hdcStatic:530112DB hwndStatic:000608C2
<000023> 001D0774 R WM_CTLCOLORSTATIC hBrush:261011F7
<000024> 001D0774 S WM_DRAWITEM idCtl:395458 lpdis:0018F728
<000025> 001D0774 R WM_DRAWITEM fProcessed:False
<000026> 001D0774 S WM_CTLCOLOREDIT hdcEdit:FB01097B hwndEdit:000808A8
<000027> 001D0774 R WM_CTLCOLOREDIT hBrush:3810149A
<000028> 001D0774 S WM_CTLCOLOREDIT hdcEdit:FB01097B hwndEdit:000808A8
<000029> 001D0774 R WM_CTLCOLOREDIT hBrush:3810149A
<000030> 001D0774 S WM_DRAWITEM idCtl:526504 lpdis:0018F728
<000031> 001D0774 R WM_DRAWITEM fProcessed:False
<000032> 001D0774 S WM_CTLCOLORSTATIC hdcStatic:530112DB hwndStatic:001A06F2
<000033> 001D0774 R WM_CTLCOLORSTATIC hBrush:261011F7
<000034> 001D0774 S WM_CTLCOLORSTATIC hdcStatic:530112DB hwndStatic:001A06F2
<000035> 001D0774 R WM_CTLCOLORSTATIC hBrush:261011F7发布于 2013-04-18 13:59:35
发现使用TCM_SETCURFOCUS消息而不是TCM_SETCURSEL就足以更改选项卡的内容。
procedure ChangeTab(PageControlHandle: HWND; TabIndex: Integer);
begin
SendMessage(PageControlHandle, TCM_SETCURFOCUS, TabIndex, 0);
end;但是,如果页面控件处于按钮模式(具有TCS_BUTTONS样式),这将无法工作,因为按钮可以在不更改内容的情况下接收焦点。
发布于 2013-04-17 20:07:09
通常,PageControl本身将TCN_...通知发送给自己的父节点,因此用于这些通知的参数存在于运行PageControl和父节点的同一地址空间中。您正在从另一个进程发送通知,因此您的TNMHdr指针位于发送应用程序的地址空间,而不是接收应用程序地址空间中的有效指针。更糟糕的是,不允许跨进程边界发送WM_NOTIFY,如MSDN记录
对于Windows2000及更高版本的系统,不能在进程之间发送WM_NOTIFY消息。
因此,您需要使用VirtualAllocEx()和WriteProcessMemory()在接收应用程序的地址空间中分配和操作TNMHdr记录。为了发送TCN_...消息,您需要在接收过程中注入代码。
试试这个:
// this is a Delphi translation of code written by David Ching:
//
// https://groups.google.com/d/msg/microsoft.public.vc.mfc/QMAHlPpEQyM/Nu9iQycmEykJ
//
// http://www.dcsoft.com/private/sendmessageremote.h
// http://www.dcsoft.com/private/sendmessageremote.cpp
const
MAX_BUF_SIZE = 512;
type
LPFN_SENDMESSAGE = function(Wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
PINJDATA = ^INJDATA;
INJDATA = record
fnSendMessage: LPFN_SENDMESSAGE; // pointer to user32!SendMessage
hwnd: HWND;
msg: UINT;
wParam: WPARAM;
arrLPARAM: array[0..MAX_BUF_SIZE-1] of Byte;
end;
function ThreadFunc(pData: PINJDATA): DWORD; stdcall;
begin
Result := pData.fnSendMessage(pData.hwnd, pData.msg, pData.wParam, LPARAM(@pData.arrLPARAM));
end;
procedure AfterThreadFunc;
begin
end;
function SendMessageRemote(dwProcessId: DWORD; hwnd: HWND; msg: UINT; wParam: WPARAM; pLPARAM: Pointer; sizeLParam: size_t): LRESULT;
var
hProcess: THandle; // the handle of the remote process
hUser32: THandle;
DataLocal: INJDATA;
pDataRemote: PINJDATA; // the address (in the remote process) where INJDATA will be copied to;
pCodeRemote: Pointer; // the address (in the remote process) where ThreadFunc will be copied to;
hThread: THandle; // the handle to the thread executing the remote copy of ThreadFunc;
dwThreadId: DWORD;
dwNumBytesXferred: SIZE_T; // number of bytes written/read to/from the remote process;
cbCodeSize: Integer;
lSendMessageResult: DWORD;
begin
Result := $FFFFFFFF;
hUser32 := GetModuleHandle('user32');
if hUser32 = 0 then RaiseLastOSError;
// Initialize INJDATA
@DataLocal.fnSendMessage := GetProcAddress(hUser32, 'SendMessageW');
if not Assigned(DataLocal.fnSendMessage) then RaiseLastOSError;
DataLocal.hwnd := hwnd;
DataLocal.msg := msg;
DataLocal.wParam := wParam;
Assert(sizeLParam <= MAX_BUF_SIZE);
Move(pLPARAM^, DataLocal.arrLPARAM, sizeLParam);
// Copy INJDATA to Remote Process
hProcess := OpenProcess(PROCESS_CREATE_THREAD or PROCESS_QUERY_INFORMATION or PROCESS_VM_OPERATION or PROCESS_VM_WRITE or PROCESS_VM_READ, FALSE, dwProcessId);
if hProcess = 0 then RaiseLastOSError;
try
// 1. Allocate memory in the remote process for INJDATA
// 2. Write a copy of DataLocal to the allocated memory
pDataRemote := PINJDATA(VirtualAllocEx(hProcess, nil, sizeof(INJDATA), MEM_COMMIT, PAGE_READWRITE));
if pDataRemote = nil then RaiseLastOSError;
try
if not WriteProcessMemory(hProcess, pDataRemote, @DataLocal, sizeof(INJDATA), dwNumBytesXferred) then RaiseLastOSError;
// Calculate the number of bytes that ThreadFunc occupies
cbCodeSize := Integer(LPBYTE(@AfterThreadFunc) - LPBYTE(@ThreadFunc));
// 1. Allocate memory in the remote process for the injected ThreadFunc
// 2. Write a copy of ThreadFunc to the allocated memory
pCodeRemote := VirtualAllocEx(hProcess, nil, cbCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if pCodeRemote = nil then RaiseLastOSError;
try
if not WriteProcessMemory(hProcess, pCodeRemote, @ThreadFunc, cbCodeSize, dwNumBytesXferred) then RaiseLastOSError;
// Start execution of remote ThreadFunc
hThread := CreateRemoteThread(hProcess, nil, 0, pCodeRemote, pDataRemote, 0, dwThreadId);
if hThread = 0 then RaiseLastOSError;
try
WaitForSingleObject(hThread, INFINITE);
// Copy LPARAM back (result is in it)
if not ReadProcessMemory(hProcess, @pDataRemote.arrLPARAM, pLPARAM, sizeLParam, dwNumBytesXferred) then RaiseLastOSError;
finally
GetExitCodeThread(hThread, lSendMessageResult);
CloseHandle(hThread);
Result := lSendMessageResult;
end;
finally
VirtualFreeEx(hProcess, pCodeRemote, 0, MEM_RELEASE);
end;
finally
VirtualFreeEx(hProcess, pDataRemote, 0, MEM_RELEASE);
end;
finally
CloseHandle(hProcess);
end;
end;
procedure ChangeTab(PageControlHandle: HWND; TabIndex: Integer);
var
dwProcessId: DWORD;
hParent: HWND;
Info: TNMHdr;
begin
GetWindowThreadProcessId(PageControlHandle, @dwProcessId);
hParent := GetParent(PageControlHandle);
Info.hwndFrom := PageControlHandle;
Info.idFrom := GetWindowLongPtr(PageControlHandle, GWL_ID);
Info.code := TCN_SELCHANGING;
if SendMessageRemote(dwProcessId, hParent, WM_NOTIFY, WPARAM(PageControlHandle), @Info, SizeOf(TNMHdr)) <> 0 then
raise Exception.Create('Page control didn''t allow tab to change.');
if SendMessage(PageControlHandle, TCM_SETCURSEL, TabIndex, 0) = -1 then
raise Exception.Create('Failed to change tab.');
Info.code := TCN_SELCHANGE;
SendMessageRemote(dwProcessId, hParent, WM_NOTIFY, WPARAM(PageControlHandle), @Info, SizeOf(TNMHdr));
end;https://stackoverflow.com/questions/16067512
复制相似问题