Related
I have code on Delphi (but this not principal)
hWindow := GetForegroundWindow;
EditHandle := GetTopWindow(hWindow);
SendMessage(EditHandle, WM_COPY, 0, 0);
SClipboard := Clipboard.AsText;
If this code worked for Notepad - all very good. If another, not simple editor, selected text not copyed to clipboard.
How I can to execute copy text to clipboard?
I suspect that SendMessage does not work.
First, Not all the top windows are the edit control. So, EditHandle is unpredictable.
Then, WM_COPY is only supported in edit control or combo box. The same goes for WM_GETTEXT, They do not support all windows.
You are not able to change its message processing mechanism unless you're the owner of Windows.
However, there is a workaround--Simulate "Ctrl + C" keyboard input to target window.
//SetForegroundWindow(hWindow); Since hWindow is already a Foreground Window
keybd_event(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0), 0, 0); // Ctrl down
keybd_event(ord('C'), MapVirtualKey(ord('C'), 0), 0, 0); // C down
keybd_event(ord('C'), MapVirtualKey(ord('C'), 0), KEYEVENTF_KEYUP, 0); // C up
keybd_event(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0), KEYEVENTF_KEYUP, 0); // Ctrl up
EDIT:
Since the keybd_event has been superseded by SendInput, Here is a C++ sample for using SendInput:
INPUT input[4] = {0};
input[0].type = input[1].type = input[2].type = input[3].type = INPUT_KEYBOARD;
input[0].ki.wVk = input[3].ki.wVk = VK_CONTROL;
input[0].ki.wScan = input[3].ki.wScan = MapVirtualKey(VK_CONTROL, MAPVK_VK_TO_VSC);
input[1].ki.wVk = input[2].ki.wVk = 'C';
input[1].ki.wScan = input[2].ki.wScan = MapVirtualKey('C', MAPVK_VK_TO_VSC);
input[2].ki.dwFlags = input[3].ki.dwFlags = KEYEVENTF_KEYUP; // there is no KEYEVENTF_KEYDOWN
SendInput(4, input, sizeof(INPUT));
I need to perform the same task as WindowsKey + M through code, ie. minimize all open windows. This must be done through the Win32 API, not .Net.
I tried the following in FreeBasic, but nothing happens:
Dim hWndConsole As HWND
'Shell_TrayWnd = class name of taskbar
Dim WindowName as String = "Shell_TrayWnd"
hWndConsole = FindWindow(0, strptr(WindowName))
ShowWindow(hWndConsole, SW_MINIMIZE) 'outta my sight
Does someone know how to do this?
Thank you.
Edit: Here's the working solution:
#include "Windows.bi"
Dim hWndConsole As HWND
'Shell_TrayWnd = class name of taskbar
Dim WindowName as String = "Shell_TrayWnd"
Dim res as LRESULT
CONST minall = 419
hWndConsole = FindWindow( "Shell_TrayWnd",null)
res = postMessage(hWndConsole, WM_COMMAND, minall, null )
This seems like a bit of a hack to me, but the following does seem to accomplish what you are looking for (in C):
HANDLE hwnd = FindWindow( "Shell_TrayWnd", NULL );
LRESULT res = SendMessage( hwnd, WM_COMMAND, (WPARAM)419, 0 );
When you have the handle of a window, you can make it minimize with the WM_SYSCOMMAND message. E.g.:
SendMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
So all you would need to do is enumerate the top-level windows (with the EnumWindows command) and send that command to the windows you want to minimize (which wouldn't be all top-level windows - probably only visible, overlapped windows without the WS_EX_TOOLWINDOW extended style should be minimized like this).
Putting this out as an option, not a recommendation - simulating the keyboard events for Win-M:
keybd_event(VK_LWIN, 0, 0, 0);
keybd_event('M', 0, 0, 0);
keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, 0);
I am trying to access the ListView control (located within an Dialog) in another application, and get the data from within the control. Here is the Win32 code (with appropiate comments) that I am writing :
HWND hListView32 = hRoot; //HANDLE to the ListView control within the Dialog, having class name - "SysListView32"
int cnt = (int) ::SendMessage(hListView32, LVM_GETITEMCOUNT, 0, 0L); //returns CORRECT item count of the ListView Control
int nItem=0,nRes;
for(int nItem=0;nItem<cnt;nItem++)
{
LVITEM LvItem; // ListView Item struct
char Text[255]={0};
char Temp[255]={0};
char Temp1[255]={0};
memset(&LvItem,0,sizeof(LvItem));
LvItem.mask=LVIF_TEXT;
LvItem.iSubItem=1; //Trying to get the 2nd Colomn text
LvItem.pszText=Text; //Does not returns any Text, after the below SendMessage is executed???
LvItem.cchTextMax=256;
LvItem.iItem=nItem;
nRes = (int)::SendMessage(hListView32,LVM_GETITEMTEXT, nItem, (LPARAM)&LvItem);
DWORD dd = ::GetLastError(); //returns 0
}
Though the code is executing, I am not getting any data from within the control. However, I am able to retrieve the correct Item count from within the control, but no data.
Another approach maybe would be to use an MSAA hook to get the data. But that would be a very long and cumbersome process. Running out of ideas here. Pls help.
Thanks,
There a few possibilities.
DLL Injection Using windows hooks. Pros: simple and straight forward. Cons: many processes get this dll loaded.
DLL Injection Making process to load library by opening it for debugging, allocating a chunc of virtual memory using VallocEx in the context of this process, writing it's memory with WriteProcessMemory and creating a remote thread with start address of LoadLibrary function. Pros: a single process is affected. Cons: A bit more complex than hooks solution.
Read of process memory. Same as option 2 but instead of writing this memory and executing the code remotely, send the message LVM_GETITEMTEXT to the window in question providing a valid known memory location and then read that location with ReadProcessMemory.
ListView messages that pass around buffers only work within the address space of the process that owns the ListView. You will have to use VirtualAllocEx() to allocate a memory block within that same process, then you can write to it with WriteProcessMemory() and have the ListView fill it as needed, then you can read it with ReadProcessMemory() and deallocate it with VirtualFreeEx().
Try this (error handling omitted for brevity):
HWND hListView32 = hRoot;
int cnt = (int) ::SendMessage(hListView32, LVM_GETITEMCOUNT, 0, 0);
if (cnt > 0)
{
DWORD dwProcessId;
GetWindowThreadProcessId(hListView32, &dwProcessId);
HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwProcessId);
LVITEM *pLvItem = (LVITEM*) VirtualAllocEx(hProcess, NULL, sizeof(LVITEM), MEM_COMMIT, PAGE_READWRITE);
LPTSTR pText = (LPTSTR) VirtualAllocEx(hProcess, NULL, sizeof(TCHAR)*256, MEM_COMMIT, PAGE_READWRITE);
for(int nItem = 0; nItem < cnt; ++nItem)
{
TCHAR Text[256] = {0};
LVITEM LvItem = {0};
LvItem.mask = LVIF_TEXT;
LvItem.iSubItem = 1;
LvItem.pszText = pText;
LvItem.cchTextMax = 256;
LvItem.iItem = nItem;
WriteProcessMemory(hProcess, pLvItem, &LvItem, sizeof(LVITEM), NULL);
int nRes = (int) ::SendMessage(hListView32, LVM_GETITEMTEXT, nItem, (LPARAM)pLvItem);
if (nRes > 0)
ReadProcessMemory(hProcess, pText, &Text[0], sizeof(TCHAR)*nRes, NULL);
// use Text as needed...
}
VirtualFreeEx(hProcess, pText, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, pLvItem, 0, MEM_RELEASE);
CloseHandle(hProcess);
}
I copypasted code by Remy Lebeau, but it is working very strange in my particular case. The number of elements is retreived correctly via SendMessage(listview, LVM_GETITEMCOUNT, 0, 0)
, but the cycle reads jast the same element every time! It is not the first or last element, not the selected, but seems to be random. Here is my code:
HWND win=FindWindowEx(NULL, NULL, _("TEventLogView"), NULL);
HWND listview=FindWindowEx(win, NULL, _("TListView"), NULL);
int cnt = (int) ::SendMessage(listview, LVM_GETITEMCOUNT, 0, 0);
if (cnt > 0)
{
DWORD dwProcessId;
GetWindowThreadProcessId(listview, &dwProcessId);
int n = grdEvents->GetNumberRows();
HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwProcessId);
LVITEM *pLvItem = (LVITEM*) VirtualAllocEx(hProcess, NULL, sizeof(LVITEM), MEM_COMMIT, PAGE_READWRITE);
LPTSTR pText = (LPTSTR) VirtualAllocEx(hProcess, NULL, sizeof(TCHAR)*255, MEM_COMMIT, PAGE_READWRITE);
for(int nItem = 0; nItem < cnt; ++nItem)
{
// need to read 1 - 3 subitems
for (int j = 1; j < 4; j++)
{
TCHAR Text[255] = {0};
LVITEM LvItem = {0};
LvItem.mask = LVIF_STATE | LVIF_TEXT;
LvItem.pszText = pText;
LvItem.cchTextMax = sizeof(TCHAR)*255;
LvItem.iItem = nItem;
LvItem.iSubItem = j;
int nRes1 = WriteProcessMemory(hProcess, pLvItem, &LvItem, sizeof(LVITEM), NULL);
int nRes2 = (int) ::SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)nItem, (LPARAM)pLvItem);
if (nRes2 > 0)
{
ReadProcessMemory(hProcess, pText, &Text[0], sizeof(TCHAR)*nRes2, NULL);
// insert into wxWidgets grid
grdEvents->SetCellValue(nItem, j - 1, Text);
}
}
VirtualFreeEx(hProcess, pText, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, pLvItem, 0, MEM_RELEASE);
CloseHandle(hProcess);
}
}
HKEY hKey = 0;
DWORD dwType = REG_SZ;
TCHAR buf[255] = {0};
DWORD dwBufSize = sizeof(buf);
DWORD ret;
CComboBox m_portCombo;
if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM"), 0, KEY_QUERY_VALUE, &hKey ) == ERROR_SUCCESS )
{
if( RegQueryValueEx( hKey, TEXT("\\Device\\Serial0"), 0, &dwType, (LPBYTE)buf, &dwBufSize ) == ERROR_SUCCESS )
{
CString str = buf;
m_portCombo.AddString(str);
}
if( RegQueryValueEx( hKey, TEXT("\\Device\\Serial1"), 0, &dwType, (LPBYTE)buf, &dwBufSize ) == ERROR_SUCCESS )
{
CString str = buf;
}
if( RegQueryValueEx( hKey, TEXT("\\Device\\Serial2"), 0, &dwType, (LPBYTE)buf, &dwBufSize ) == ERROR_SUCCESS )
{
CString str = buf;
}
if( RegQueryValueEx( hKey, TEXT("\\Device\\Serial3"), 0, &dwType, (LPBYTE)buf, &dwBufSize ) == ERROR_SUCCESS )
{
CString str = buf;
}
}
First problem: i want to change the TEXT("\\Device\\Serial3") with something like TEXT("\\Device\\Serial",%i), so i can resume all that lines of code to a for loop.Is tehre a way to accomplish this?
Second problem: if i use the m_portCombo.AddString(str); i get an Debug Assertion Failed! error, and, of course, the combobox is not populated with that registry value. Why could that happen?
First Problem: Use the CString Format() function using %d for integer.
for (int i =0 ; i<10; i++)
{
CString szPath;
szPath.Format(TEXT("\\Device\\Serial%d"),i);
// ...
}
Second Problem:
There could be many reasons this would fail. Most likely of which would be having not created the combo box yet.(It needs a window handle before it can add strings) To figure out the cause of the debug assertion, click the "retry" button on the Debug Assertion Failed window and it should jump to the code which caused the assertion. For example it might be something like:
ASSERT(GetSafeHwnd()!=NULL);
Your combo box class won't be 'subclassed' until after the first DoDataExchange is called (and any attempt to use it before that happens will ASSERT). Either wait until the base class has run OnInitDialog or do something like this:
CComboBox * pcombo = static_cast<CComboBox*>(GetDlgItem( IDC_MYCOMBO ));
pcombo->AddString( szPath );
See #TheSteve's answer for string problem.
I am looking for a way to check if a given window has a taskbar button. That is, given a handle to a window, I need a TRUE if the window is in the taskbar, and FALSE otherwise.
Conversely, I am wondering if there is a way to get a handle to the window that belongs to a given taskbar button, which I suppose would require a way to enumerate through the taskbar buttons.
(The first former is the part that I need, and the latter part is optional.)
Thanks a lot.
Windows uses heuristics to decide whether or not to give a taskbar button to a window, and sometimes there is a delay before it can decide, so doing this 100% accurately is going to be quite hard. Here's a rough start on the rules. There are modern style flags that make it easy to know, but when those styles are missing the taskbar is reduced to guessing.
First off, you will need both of the the window style flags.
LONG Style = GetWindowLong(hwnd, GWL_STYLE);
LONG ExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
Now the rules, there are three rules that are certain.
if ExStyle & WS_EX_APPWINDOW, then TASKBAR
if ExStyle & WS_EX_TOOLWINDOW, then NOT_TASKBAR
if Style & WS_CHILD then NOT_TASKBAR
The rest are guesses:
Style & WS_OVERLAPPED suggests TASKBAR
Style & WS_POPUP suggests NOT_TASKBAR especially if GetParent() != NULL
ExStyle & WS_EX_OVERLAPPEDWINDOW suggests TASKBAR
ExStyle & WS_EX_CLIENTEDGE suggests NOT_TASKBAR
ExStyle & WS_EX_DLGMODALFRAME suggests NOT_TASKBAR
I'm sure that there are other rules for guessing, and in fact that the guessing rules have changed from version to version of Windows.
Toplevel window
WS_EX_APPWINDOW -> taskbar, no matter the other styles!
OWNER must be NULL (GetWindow(window, GW_OWNER))
no: WS_EX_NOACTIVATE or WS_EX_TOOLWINDOW:
order is important.
second question: in windows xp/vista it was possible to get into the process of the taskbar and get all window ID´s:
void EnumTasklistWindows()
{
int b2 = 0;
TBBUTTON tbButton;
DWORD dwProcessId = 0, dwThreadId = 0;
HWND hDesktop =::GetDesktopWindow();
HWND hTray =::FindWindowEx(hDesktop, 0, ("Shell_TrayWnd"), NULL);
HWND hReBar =::FindWindowEx(hTray, 0, ("ReBarWindow32"), NULL);
HWND hTask =::FindWindowEx(hReBar, 0, ("MSTaskSwWClass"), NULL);
HWND hToolbar =::FindWindowEx(hTask, 0, ("ToolbarWindow32"), NULL);
LRESULT count =::SendMessage(hToolbar, TB_BUTTONCOUNT, 0, 0);
dwThreadId = GetWindowThreadProcessId(hToolbar, &dwProcessId);
shared_ptr<void> hProcess (OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId), CloseHandle);
if (NULL == hProcess.get())
{
return;
}
memset(&tbButton, 0, sizeof(TBBUTTON));
for (int i = 0; i < count; i++)
{
memset(&tbButton, 0, sizeof(TBBUTTON));
shared_ptr<void> lpRemoteBuffer (
VirtualAllocEx(hProcess.get(), NULL, sizeof(TBBUTTON), MEM_COMMIT, PAGE_READWRITE),
bind<BOOL>(VirtualFreeEx, hProcess.get(), _1, 0, MEM_RELEASE));
if (NULL == lpRemoteBuffer.get())
{
return;
}
SendMessage(hToolbar, TB_GETBUTTON, i, (LPARAM) lpRemoteBuffer.get());
b2 = ReadProcessMemory(hProcess.get(), lpRemoteBuffer.get(),
(LPVOID) & tbButton, sizeof(TBBUTTON), NULL);
if (0 == b2)
{
continue;
}
BYTE localBuffer[0x1000];
BYTE *pLocalBuffer = localBuffer;
DWORD_PTR ipLocalBuffer = (DWORD_PTR) pLocalBuffer;
pLocalBuffer = localBuffer;
ipLocalBuffer = (DWORD_PTR) pLocalBuffer;
DWORD_PTR lpRemoteData = (DWORD_PTR) tbButton.dwData;
ReadProcessMemory(hProcess.get(), (LPVOID) lpRemoteData, (LPVOID) ipLocalBuffer,
sizeof(DWORD_PTR), NULL);
HWND windowHandle;
memcpy(&windowHandle, (void *) ipLocalBuffer, 4);
if (windowHandle != NULL)
{
trace ("adding button: %x\n", windowHandle);
}
}
}
this not possible with windows 7 anymore. so you need to loop over all toplevel windows.
This MSDN article has some good information about when and why the Shell decides to create a taskbar button for a window:
The Shell creates a button on the taskbar whenever an application creates a window that isn't owned. To ensure that the window button is placed on the taskbar, create an unowned window with the WS_EX_APPWINDOW extended style. To prevent the window button from being placed on the taskbar, create the unowned window with the WS_EX_TOOLWINDOW extended style. As an alternative, you can create a hidden window and make this hidden window the owner of your visible window.