I'm trying to do two things:
Send "key down" events, both for keyboard keys, as well as mouse keys, to an external process' window
Later on, query whether a given keyboard or mouse key is currently pressed in that window, which could happen if
My tool had previously sent a "key down" (but obviously no key up) event to the window
The user had any manual interaction with the window
One of the cases could for example be that I send a "key down" event for the left mouse button to the window. The user then clicks inside the window themselves manually. As a consequence after both of those interactions, the window recognizes the mouse key as "up" again.
So far, for the key presses, I've been using user32.dll API calls PostMessage (for keyboard events) and mouse_event (for mouse keys).
As for retrieving the current state, I've been trying to use "GetKeyState". However, its return values are inconcistent with actual behaviour. For example, when I post a key down through my tool, and there are no further interactions (neither by me, nor by the user), GetKeyState() still returns 0 (= key is NOT pressed).
I presume I'm using the wrong API for retrieving the state here (in particular as GetKeyState doesn't take in a window handle either?).
Is there any way to figure out the actual key state for the given window? Or am I going wrong elsewhere here?
Thanks
Example code:
class NativeCalls
{
[DllImport("user32.dll", SetLastError = true)]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
[DllImport("user32.dll")]
public static extern short GetKeyState(int nVirtKey);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern short GetAsyncKeyState(int vKey);
public static void test()
{
var state_x1 = GetAsyncKeyState(0x41);
var ok1 = SetForegroundWindow(Globals.Hwnd);
var ok2 = PostMessage(Globals.Hwnd, 0x0100, 0x41, 0); // key down "A"
Thread.Sleep(10);
var state = GetKeyState(0x41); // check if key "A" Is down
var state_x2 = GetAsyncKeyState(0x41);
// state_x1, state, and state_x2 are all 0
}
}
Related
How can I have a Windows tray notification icon for an out-of-process COM server, developed using VS2019?
So far I have tried just adding one with Shell_NotifyIconA(NIM_ADD, &n); as per the MSDN documentation. .However if I set the NOTIFYICONDATA::m_hWnd to 0 then this call is rejected with 0x80004005 (Invalid handle).
So I have to specify a window handle that the icon's messages will go to, but the application currently doesn't have any windows. It does have a message pump which is found at ATL::CAtlExeModule<T>::RunMessageLoop() (that's part of the ATL boilerplate code) but I can't see any mention of where a window handle is to send messages to this loop.
I've tried using a Message-only Window created with CWindowImpl::Create, however when the program runs, the behaviour is unexpected. A blank space appears in the notification tray (the icon does not show properly), and mousing or clicking on the space does not cause the message handler to be entered. The log message appears indicating Shell_NotifyIcon() succeeded and the handles are valid, but no further log messages.
What's the right way to do this in VS2019? (I have done it before in C++Builder which lets you simply add a form, mark it as the main form, and add a notification icon component to it).
Code for the ATLExeModule (this is the boilerplate code plus my modifications):
class CNotifyWnd : public CWindowImpl<CNotifyWnd>
{
public:
BEGIN_MSG_MAP(CMyCustomWnd)
MESSAGE_HANDLER(WM_USER+1, OnMsg)
END_MSG_MAP()
LRESULT OnMsg(UINT, WPARAM, LPARAM, BOOL&)
{
DEBUG_LOG("Received notification");
return 0;
}
};
static void create_notifyicon()
{
auto * pw = new CNotifyWnd;
HWND hwnd = pw->Create(HWND_MESSAGE);
auto hInst = GetModuleHandle(NULL);
NOTIFYICONDATAA n{};
n.cbSize = sizeof n;
n.hIcon = LoadIcon(NULL, IDI_SHIELD);
#pragma warning(disable : 4996)
strcpy(n.szTip, "Tooltip string");
n.dwInfoFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
n.uVersion = NOTIFYICON_VERSION;
n.hWnd = hwnd;
n.uID = 1234;
n.uCallbackMessage = WM_USER + 1;
int hr = Shell_NotifyIconA(NIM_ADD, &n);
DEBUG_LOG("Shell_NotifyIcon = {}; Icon handle {}, window {}",
hr, (uint64_t)n.hIcon, (uint64_t)n.hWnd);
}
class CMyProjectModule : public ATL::CAtlExeModuleT< CMyProjectModule >
{
public :
DECLARE_LIBID(LIBID_MyProjectLib)
DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MYPROJECT, "{d0d2e9f7-8578-412a-9311-77ff62291751}")
using Parent = ATL::CAtlExeModuleT< CMyProjectModule >;
HRESULT PreMessageLoop(int nShowCmd) throw()
{
HRESULT hr = Parent::PreMessageLoop(nShowCmd);
create_notifyicon();
return hr;
}
};
CMyProjectModule _AtlModule;
extern "C" int WINAPI _tWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/,
LPTSTR /*lpCmdLine*/, int nShowCmd)
{
return _AtlModule.WinMain(nShowCmd);
}
The code in the question is mostly correct, however dwInfoFlags should be uFlags. After making that change the notify icon worked as intended.
Thanks to commentors who suggested ways to simplify the original code in the question, and the idea of a "message-only window" created by setting the parent to HWND_MESSAGE.
I have a strange Requierment for a Task. In gernerall terms, i need to prepare Windows PCs for Presentations with a PowerShell Script and one of the Requierments is to activate the "hightlight pointer position if ctrl is hit" function in the extended pointer settings.
My first idea was to change the reg-key but if that even works, i think it would be linked to a PC-Restart. I allready tryed to change the Value of the key after finding the diffrenece in the on and off state of the function but i cant force it to "refresh the setting" or it simply does not work.
Any ideas, workaround or the like would be appriciated.
The function is controlled by the Reg-Key HKCU:\Control Panel\Desktop - UserPrefernecesMask
The 'Mouse Sonar' function can be enabled or disabled using the SystemParametersInfo function inside user32.dll.
To use that in PowerShell, add a bit of CSharp:
Add-Type -TypeDefinition #'
using System.Runtime.InteropServices;
public class MouseSonar {
public const uint SPI_SETMOUSESONAR = 0x101D;
public const uint SPIF_UPDATEINIFILE = 0x01;
public const uint SPIF_SENDCHANGE = 0x02;
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo (uint uAction, uint uParam, bool lpvParam, uint fuWinIni);
public static void SetSonar (bool Enable) {
SystemParametersInfo(SPI_SETMOUSESONAR, 0, Enable, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
}
'#
and use it like this:
# turn Mouse Sonar on
[MouseSonar]::SetSonar($true)
# to switch Mouse Sonar off again:
[MouseSonar]::SetSonar($false)
No need to restart the computer, this works immediately.
is it possible to simulate Click on a process without actually clicking on it?
e.g. I wanna Click on a running Calculator with mouse stand still. is this possible?
If you are just trying to click a button within a fairly typical labels, fields, and buttons application, you can use a little bit of P/Invoke to use FindWindow and SendMessage to the control.
If you are not already familiar with Spy++, now is the time to start!
It is packaged with Visual Studio 2012 RC in: C:\Program Files\Microsoft Visual Studio 11.0\Common7\Tools. It should be similarly found for other versions.
Try this as Console C# application:
class Program
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
private const uint BM_CLICK = 0x00F5;
static void Main(string[] args)
{
// Get the handle of the window
var windowHandle = FindWindow((string)null, "Form1");
// Get button handle
var buttonHandle = FindWindowEx(windowHandle, IntPtr.Zero, (string)null, "A Huge Button");
// Send click to the button
SendMessage(buttonHandle, BM_CLICK, 0, 0);
}
}
This gets the handle to the window captioned "Form1". Using that handle, it gets a handle to the Button within the Window. Then sends a message of type "BM_CLICK" with no useful params to the button control.
I used a test WinForms app as my target. A single button and some code behind to increment a counter.
You should see the counter increment when you run the P/Invoke console application. However, you will not see the button animate.
You could also use the Spy++ Message Logger function. I recommend a filter to BM_CLICK and maybe WM_LBUTTONDOWN/WM_LBUTTONUP (what a manual click will give you).
Hope that helps!
I would like to write a program, which lets me change a values in text box of different program, or automatically copy a values from one program to another.
I found a way to get hWnd to most (no idea if all of them) of controls in targer program, and to point them with mouse cursor. I made a simple struct to do so, and an array of it
struct hWndpointer
{
HWND hWnd;
AnsiString text;
};
hWndpointer tbl[250];
The EnumWindowProc and EnumChildWindowProc loads handles and text of the window into the array and into the list control in my program, so i can click an item on the list (or select it with keyboard) and the cursor points the control (like button or textbox) like expected... Unfortunately there are some controls with no text (or rather GetWindowText returns no text) so there is no way to identify the control.
The question is:
Is there any way to get/read a NAME of the control?
Is there any way to get/read and set a specyfic value like 'enabled' or 'text' or 'value'?
Thanks in advance
PS: Sorry for my english ;)
You can use SendMessage and PostMessage to send WM_GETTEXT, WM_SETTEXT, WM_ENABLE to windows owned by other processes. (SendMessage for queries, PostMessage for write-only actions)
Often the child ID will be used to identify subwindows (especially in a dialog), but it's also possible for a program to rely purely on the dynamic HWND values, in which case you'll have to fall back to window positions to differentiate.
From the Win32 API's perspective, UI controls do not have Names, so you cannot ask the API to return the Name of a UI control in another process because such a value does not exist. Names are strictly a feature of the UI framework being used by the app (VCL in the case of C++Builder), and you cannot directly access frameworks across process boundaries. You would need cooperation from the control's owning app.
For instance, one way would be to have both apps call RegisterWindowMessage() to register a custom window message, then your app can post that message to the other app specifying the desired control's HWND and your own HWND as parameters. The other app can then SendMessage() the control's Name back to your app's HWND using the WM_COPYDATA message, which you can use to update your list accordingly.
In the VCL framework, you can convert an HWND to a TWinControl* pointer using the FindControl() function. It will return NULL if the HWND does not belong to the calling process, otherwise you can then copy the value from its Name property. For example:
const UINT WM_GETCONTROLNAME = RegisterWindowMessage("WM_GetControlName");
const UINT WM_GETCONTROLNAME_RESULT = RegisterWindowMessage("WM_GetControlName_Result");
#include <pshpack1.h>
struct sControlName
{
HWND hWnd;
int Length;
char Value[1];
};
#include <poppack.h>
void __fastcall TMyForm::WndProc(TMessage &Message)
{
if ((Message.Msg == WM_COPYDATA) && (WM_GETCONTROLNAME_RESULT != 0))
{
LPCOPYDATASTRUCT cds = (LPCOPYDATASTRUCT) Message.LParam;
if (cds->dwData == WM_GETCONTROLNAME_RESULT)
{
sControlName *pName = (sControlName*) cds->lpData;
AnsiString sName(pName->Value, pName->Length);
// locate pName->hWnd in your list and assign sName to it as needed...
return;
}
}
TForm::WndProc(Message);
}
void ___fastcall TMyForm::FillList()
{
...
if (WM_GETCONTROLNAME != 0)
{
HWND TheControlHWND = ...;
HWND OtherAppHWND = ...;
PostMessage(OtherAppHWND, WM_GETCONTROLNAME, (WPARAM)TheControlHWND, (LPARAM)this->Handle);
}
...
}
.
const UINT WM_GETCONTROLNAME = RegisterWindowMessage("WM_GetControlName");
const UINT WM_GETCONTROLNAME_RESULT = RegisterWindowMessage("WM_GetControlName_Result");
#include <pshpack1.h>
struct sControlName
{
HWND hWnd;
int Length;
char Value[1];
};
#include <poppack.h>
void __fastcall TMyForm::WndProc(TMessage &Message)
{
if ((Message.Msg == WM_GETCONTROLNAME) && (WM_GETCONTROLNAME != 0) && (WM_GETCONTROLNAME_RESULT != 0))
{
HWND hWnd = (HWND) Message.WParam;
TWinControl *Ctrl = FindControl(hWnd);
if (Ctrl)
{
AnsiString sName = Ctrl->Name;
std::vector<unsigned char> buffer((sizeof(sControlName) - 1) + sName.Length());
sControlName *pName = (sControlName*) &buffer[0];
pName->hWnd = hWnd;
pName->Length = sName.Length();
strncpy(pName->Value, sName.c_str(), pName->Length);
COPYDATASTRUCT cds = {0};
cds.dwData = WM_GETCONTROLNAME_RESULT;
cds.cdData = buffer.size();
cds.lpData = pName;
SendMessage((HWND)Message.LParam, WM_COPYDATA, (WPARAM)this->Handle, (LPARAM)&cds);
}
return;
}
TForm::WndProc(Message);
}
I have a simple VB.NET application using Scintilla. I don`t know how can I make the control auto scroll when text is added to it.
Can anyone help?
Thanks
Done.
Scintilla can auto-scroll by calling:
Scintilla1.Scrolling.ScrollBy(0, Scintilla1.Lines.Count)
so it scrolls to the last text line.
The accepted solution didn't work for me when trying to make a ScintillaNET editor control scroll to the bottom line after updating the Text property. Perhaps it's because I am embedding it in a WPF WindowsFormsHost. In any event, here is the code I used to make the ScintillaNET editor control auto-scroll in my context. (Note, the code is in C#):
// Declaration for the WinAPI SendMessage() method.
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint wMsg, UIntPtr wParam, IntPtr lParam);
/// WM_VSCROLL -> 0x0115
public const int WM_VSCROLL = 277;
/// SB_BOTTOM -> 7
public const int SB_BOTTOM = 7;
// scintillaCtl should be a reference to the Scintilla control you want to scroll vertically.
SendMessage(scintillaCtl.Handle, WM_VSCROLL, new UIntPtr(SB_BOTTOM), IntPtr.Zero);