Can't send WM_INPUTLANGCHANGEREQUEST to some controls - c#

I'm working on (yet another) keyboard layout switcher and got strange troubles with Skype window (ver 6.22 on win7 x64). Any combinations of GetForegroundWindow() / GetFocus() / GetParentWindow() don't succeed to change the layout only inside the message input and, even more strange, only if more than one character is entered. Other cases work perfectly nice except wpf apps which refuse to obey without focusedHandle stuff.
public static void SetNextKeyboardLayout()
{
IntPtr hWnd = GetForegroundWindow();
uint processId;
uint activeThreadId = GetWindowThreadProcessId(hWnd, out processId);
uint currentThreadId = GetCurrentThreadId();
AttachThreadInput(activeThreadId, currentThreadId, true);
IntPtr focusedHandle = GetFocus();
AttachThreadInput(activeThreadId, currentThreadId, false);
PostMessage(focusedHandle == IntPtr.Zero ? hWnd : focusedHandle, WM_INPUTLANGCHANGEREQUEST, INPUTLANGCHANGE_FORWARD, HKL_NEXT);
}
I'm new to winapi things, so any help will be kindly appreciated, thank you.

After disassembling some of working products i've figured out that i was close to the right algorythm which looks like this:
public static void SetNextKeyboardLayout()
{
IntPtr hWnd = IntPtr.Zero;
var threadId = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
var currentThreadId = GetCurrentThreadId();
var info = new GUITHREADINFO();
info.cbSize = Marshal.SizeOf(info);
var success = GetGUIThreadInfo(threadId, ref info);
// target = hwndCaret || hwndFocus || (AttachThreadInput + GetFocus) || hwndActive || GetForegroundWindow
AttachThreadInput(threadId, currentThreadId, true);
IntPtr focusedHandle = GetFocus();
AttachThreadInput(threadId, currentThreadId, false);
if (success)
{
if (info.hwndCaret != IntPtr.Zero) { hWnd = info.hwndCaret; }
else if (info.hwndFocus != IntPtr.Zero) { hWnd = info.hwndFocus; }
else if (focusedHandle != IntPtr.Zero) { hWnd = focusedHandle; }
else if (info.hwndActive != IntPtr.Zero) { hWnd = info.hwndActive; }
}
else
{
hWnd = focusedHandle;
}
if (hWnd == IntPtr.Zero) { hWnd = GetForegroundWindow(); }
PostMessage(hWnd, WM_INPUTLANGCHANGEREQUEST, INPUTLANGCHANGE_FORWARD, HKL_NEXT);
}
But the problem was not in finding PostMessage target hWnd, but in skype's input handling. I have solved it by adding a tiny delay before WM_INPUTLANGCHANGEREQUEST so skype can properly process all the input sent to it. Now i have to get things working without this delay but this is another story.

You should try this: PostMessage(hWnd,WM_INPUTLANGCHANGEREQUEST,0,(LPARAM)HKL_NEXT);
P.S.:
Under Windows 10 any WM_INPUTLANGCHANGEREQUEST crashes Skype.

Best way with Windows 10 -- is emulate keys for switch keyboard layout, like this:
keybd_event(VK_LWIN, 0, KEYEVENTF_EXTENDEDKEY, 0);
keybd_event(VK_SPACE,0, KEYEVENTF_EXTENDEDKEY, 0);
Sleep(10);
keybd_event(VK_SPACE,0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
keybd_event(VK_LWIN, 0, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);

Related

Spy++ doesn't reach button in Qt application

I'm trying to get the properties of a button but when I drag the spy++'s find window tool it doesn't reach the button (of caption "iniciar gravação" in that case) but rather the button grid parent, like this:
Why is that and how can I get the button properties? I tried with auto it info tool but I get the same behavior.
I tried get that button properties from C# code, like this:
public class WindowHandleInfo
{
private delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam);
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr lParam);
private IntPtr _MainHandle;
public WindowHandleInfo(IntPtr handle)
{
this._MainHandle = handle;
}
public List<IntPtr> GetAllChildHandles()
{
List<IntPtr> childHandles = new List<IntPtr>();
GCHandle gcChildhandlesList = GCHandle.Alloc(childHandles);
IntPtr pointerChildHandlesList = GCHandle.ToIntPtr(gcChildhandlesList);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(this._MainHandle, childProc, pointerChildHandlesList);
}
finally
{
gcChildhandlesList.Free();
}
return childHandles;
}
private bool EnumWindow(IntPtr hWnd, IntPtr lParam)
{
GCHandle gcChildhandlesList = GCHandle.FromIntPtr(lParam);
if (gcChildhandlesList == null || gcChildhandlesList.Target == null)
{
return false;
}
List<IntPtr> childHandles = gcChildhandlesList.Target as List<IntPtr>;
childHandles.Add(hWnd);
return true;
}
}
void printWindows(IntPtr parent, int tabLevel)
{
var w = new WindowHandleInfo(parent);
foreach (IntPtr h in w.GetAllChildHandles())
{
string caption = GetTextBoxText(h) ?? "not found";
Debug.Write(new string('\t', tabLevel));
Debug.WriteLine(caption);
printWindows(h, tabLevel + 1);
}
}
int GetTextBoxTextLength(IntPtr hTextBox)
{
// helper for GetTextBoxText
const int WM_GETTEXTLENGTH = 0x000E;
int result = SendMessage(hTextBox, WM_GETTEXTLENGTH, 0, IntPtr.Zero);
return result;
}
string GetTextBoxText(IntPtr hTextBox)
{
const int WM_GETTEXT = 0x000D;
int len = GetTextBoxTextLength(hTextBox);
if (len <= 0) return null; // no text
StringBuilder sb = new StringBuilder(len + 1);
SendMessage(hTextBox, WM_GETTEXT, len + 1, sb);
return sb.ToString();
}
using like this:
IntPtr parent = FindWindow(IntPtr.Zero, "my app title");
printWindows(parent, 0);
But I can't see anyhing related to Iniciar gravação button.
Qt UI controls (widgets, in Qt lexicon. Aka buttons, line edits, comboboxes, etc.) are not backed by native controls. They just steal the native UI look and feel from them and mimic it.
For this reason, you won't find those controls using some of those "spy" tools -- you may still find them through accessibility, though (*).
Or, you can inspect your application using other Qt-specific tools, such as GammaRay for debugging or Squish for UI testing.
(*) controls shipped with Qt are accessibile, but if someone manually reimplements something that looks like a button and doesn't provide it with accessibility then you can't do much about that.

How to get all windows being show on screen?

I asked a similar kind of question last month but unfortunately i did not get a single response to that question. I am asking it again.
In one of my projects i want to get all the windows being shown on screen. I tried this code
Process[] procs = Process.GetProcesses();
if (proc.MainWindowHandle!=IntPtr.Zero)
{
GetWindowPlacement(proc.MainWindowHandle, ref placement);
if (proc.ProcessName!="McUICnt" && proc.ProcessName!="devenv" && proc.ProcessName!="DCSHelper" && proc.ProcessName!="explorer")
{
if (placement.showCmd == 1)
{
handles[temp] = proc.MainWindowHandle;
GetWindowRect(proc.MainWindowHandle, ref rct);
rect[temp] = rct;
temp++;
MessageBox.Show(proc.ProcessName.ToString());
}
}
}
As you can see i have put a check for McUICnt,devenv. placement.cmd==1 checks for the windows with normal state. But McUICnt and devenv are also passing that condition but are not showing on the screen. When i run again program then i get new process like stated above.
How to get all the windows being shown on the screen right now?
UPDATE:
When I use EnumWindows i use this code
EnumWindows(new EnumWindowsProc(EnumTheWindows), IntPtr.Zero);
and
protected static bool EnumTheWindows(IntPtr hWnd, IntPtr lParam)
{
int size = GetWindowTextLength(hWnd);
RECT rct = new RECT();
if (size++ > 0 && IsWindowVisible(hWnd))
{
StringBuilder sb = new StringBuilder(size);
GetWindowText(hWnd, sb, size);
MessageBox.Show(sb.ToString());
handles[tempHandle] = hWnd;
GetWindowRect(hWnd, ref rct);
rect[tempHandle] = rct;
tempHandle++;
}
return true;
}
But it also shows minimized windows. Even I have put a check IsWindowVisible

C# Pinvoke can't find the Hwnd of Controls after List count was 0 at first time

I'm trying to click a Button in another Application (started from my Programm with Process.Start)
The problem: I need to wait until the Loading screen is disappeared and the GUI pop's up...
My idea was to read all (Hwnd)Controls until a specific Control (Button: "Kill Client") from the GUI was found (=GUI Opened).
But this only works if I wait manually for the GUI and press a "Search Control" button.
If I press the "Search Button" if the Loading Screen is aktive I get a Hwnd = 0 (List<'IntPtr> Count is also 0...) and if i press it again if the GUI is opened it is 0 again(List<'IntPtr> Count too...) !!!
Here my Code:
public class WndSearcher
{
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
public static List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
}
list.Add(handle);
return true;
}
}
My Button:
List<IntPtr> AllControlHandles = WndSearcher.GetChildWindows(selectedCharacter.Botprocess.MainWindowHandle);
IntPtr ControlHandle = AllControlHandles.Find(x => PInvoke.GetWindowTextRaw(x) == "Kill Client" ? true : false);
MessageBox.Show(ControlHandle.ToString());
Part of PInvoke (Class):
const int WM_GETTEXT = 0x000D;
const int WM_GETTEXTLENGTH = 0x000E;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, [Out] StringBuilder lParam);
public static string GetWindowTextRaw(IntPtr hwnd)
{
// Allocate correct string length first
int length = (int)SendMessage(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, null);
StringBuilder sb = new StringBuilder(length + 1);
SendMessage(hwnd, WM_GETTEXT, (IntPtr)sb.Capacity, sb);
return sb.ToString();
}
Found no solution till now.
So I decided to use AutoHotKey in combination with C#.
In C# I start my AutoHotKey Script and wait until the Script is finished. (Then the external Programm is started completely)
Starting Arguments: 1.Processid 2.NewExternalProgramName
Here my AutoHotKey Script:
counter := 0
Loop, %0% ; For each parameter:
{
param := %A_Index%
if (counter = 0) ; do sth with parameter 1
winwait, ahk_pid %param% ; Not logged in ;wait until the text "Not logged in" can be read (Program started completely)
if (counter = 1) ; do sth with parameter 2
WinSetTitle, %param%
counter += 1
}

SetKeyboardState not working in Windows 8

I'm trying to use a combination of attaching thread input to another thread and setting key states to send a shift+a combination (A) to Notepad. The problem is, the code below prints a instead of A.
I have tried debugging the code and holding down the shift while stepping through breakpoints and it works great when holding down shift. So I know that the thread attachment is working.
So it seems like the SetKeyboardState(...) command isn't working, though. What am I doing wrong?
Here is the code:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetKeyboardState(byte[] lpKeyState);
[DllImport("user32.dll")]
static extern bool SetKeyboardState(byte[] lpKeyState);
public static void simKeyPressWithModifier(IntPtr winHandle)
{
uint threadId = User32.GetWindowThreadProcessId(winHandle, IntPtr.Zero);
byte[] keys = new byte[256];
if (!GetKeyboardState(keys))
{
int err = Marshal.GetLastWin32Error();
throw new Win32Exception(err);
}
User32.AttachThreadInput((uint)AppDomain.GetCurrentThreadId(), threadId, true);
int sKey = (int)VK.VK_LSHIFT;
keys[sKey] = 0xFF;
if (!SetKeyboardState(keys))
{
int err = Marshal.GetLastWin32Error();
throw new Win32Exception(err);
}
User32.PostMessage(winHandle, WM.WM_KEYDOWN, (IntPtr)Keys.A, IntPtr.Zero);
keys[sKey] = 0;
if (!SetKeyboardState(keys))
{
int err = Marshal.GetLastWin32Error();
throw new Win32Exception(err);
}
User32.AttachThreadInput((uint)AppDomain.GetCurrentThreadId(), threadId, false);
}
Update
Posting as four commands:
public static void simKeyPressWithModifier(IntPtr winHandle)
{
User32.PostMessage(winHandle, WM.WM_KEYDOWN, (IntPtr)VK.VK_LSHIFT, IntPtr.Zero);
User32.PostMessage(winHandle, WM.WM_KEYDOWN, (IntPtr)Keys.A, IntPtr.Zero);
User32.PostMessage(winHandle, WM.WM_KEYUP, (IntPtr)Keys.A, IntPtr.Zero);
User32.PostMessage(winHandle, WM.WM_KEYUP, (IntPtr)VK.VK_LSHIFT, IntPtr.Zero);
}
Results in two lowercase as.
If I do SendMessage instead of PostMessage, nothing appears at all:
public static void simKeyPressWithModifier(IntPtr winHandle)
{
User32.SendMessage(winHandle, WM.WM_KEYDOWN, (IntPtr)VK.VK_LSHIFT, IntPtr.Zero);
User32.SendMessage(winHandle, WM.WM_KEYDOWN, (IntPtr)Keys.A, IntPtr.Zero);
User32.SendMessage(winHandle, WM.WM_KEYUP, (IntPtr)Keys.A, IntPtr.Zero);
User32.SendMessage(winHandle, WM.WM_KEYUP, (IntPtr)VK.VK_LSHIFT, IntPtr.Zero);
}
Using .NET Framework 4 on Windows 8.1 in C#.
How I'm getting the context handle:
Process p = Process.Start("notepad");
IntPtr windowHandle = p.MainWindowHandle;
RECT bounds = new RECT();
User32.GetWindowRect(windowHandle, out bounds);
POINT currentContextLocation = new POINT();
currentContextLocation.x = bounds.left + 100;
currentContextLocation.y = bounds.top + 100;
IntPtr contextHandle = User32.WindowFromPoint(currentContextLocation);
simKeyPressWithModifier(contextHandle);

Getting a Button handle from another application

I have a program that needs to send the BM_CLICK message to another applications button.
I can get the parent window handle but when I try to get the button handle if always returns 0
I got the button caption name and button type from Spy++ it seems right but I know I must have gotten something wrong. below is my code
public const Int BM_CLICK = 0x00F5;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hwnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
private void button1_Click(object sender, EventArgs e)
{
Process[] processes = Process.GetProcessesByName("QSXer");
foreach (Process p in processes)
{
////the Button's Caption is "Send" and it is a "Button".
IntPtr ButtonHandle = FindWindowEx(p.MainWindowHandle, IntPtr.Zero, "Button", "Send");
//ButtonHandle is always zero thats where I think the problem is
SendMessage(ButtonHandle, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
}
}
Spy screen shot
Try to pass null for the window text and instead try to find any button:
IntPtr ButtonHandle = FindWindowEx(p.MainWindowHandle, IntPtr.Zero, "Button", null);
After that you can use the second parameter and a new call to get the next button handle a couple more times.
Also could you please try checking Marshal.GetLastWin32Error to see what the error result is?
Try this:
IntPtr ButtonHandle = FindWindowEx(p.MainWindowHandle, IntPtr.Zero, null, "Send");
You can do somthing like this:
//Program finds a window and looks for button in window and clicks it
HWND buttonHandle = 0;
BOOL CALLBACK GetButtonHandle(HWND handle, LPARAM)
{
char label[100];
int size = GetWindowTextA(handle, label, sizeof(label));
if (strcmp(label, "Send") == 0) // your button name
{
buttonHandle = handle;
cout << "button id is: " << handle << endl;
return false;
}
return true;
}
int main()
{
HWND windowHandle = FindWindowA(NULL, "**Your Window Name**");
if (windowHandle == NULL)
{
cout << "app isn't open." << endl;
}
else
{
cout << "app is open :) " << endl;
cout << "ID is: " << windowHandle << endl;
SetForegroundWindow(windowHandle);
BOOL ret = EnumChildWindows(windowHandle, GetButtonHandle, 0); //find the button
cout << buttonHandle << endl;
if (buttonHandle != 0)
{
LRESULT res = SendMessage(buttonHandle, BM_CLICK, 0, 0);
}
}
}
This should do the trick.
Try to build project as x86. I try and successed!

Categories