I have a C# Windows Desktop app that needs to handle a given external app, - unmanaged code - and populate it's input fields.
Note that this must be done programatically, so the use of SPY++ to get windows names is out of discussion, I just can get windows classes using SPY++.
Looking and reading over there I'found that it can be accomplished with the following steps (pseducode):
Get or start the external app process.
Get the main window using FindWindow();
Get the childWindows, which are the input fields, using FindWindowEx.
Send messages using Sendmessage().
I've tested populating a notepad, however dealing with my given app has been a PITA. Can't populate anything.
here is my actual code:
/*
List of references, which includes System.Diagnostics,
System.Runtime.InteropServices,...
*/
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
private void click_test(object sender, RoutedEventArgs e)
{
Process[] apps = Process.GetProcessesByName("givenApp");
if (apps.Length == 0) return;
if (apps[0] != null)
{
IntPtr mainWindow = FindWindow("class name", null);
IntPtr child = FindWindowEx(apps[0].MainWindowHandle, new IntPtr(0), "Class Name", null);
SendMessage(child, 0x000C, 0, input_test.Text);
}
}
With this code I can populate notepad. However populating notepad is a test stage. I need to populate other app.
What is missing, what is wrong?
I found what the error was, simply:
When instantiating a child, instead of calling the generic MainWindowHadle, call the App Window handle, which is previously defined in a variable called MainWindow which invokes the FindWindow() Class.
This is wrong:
IntPtr child = FindWindowEx(apps[0].MainWindowHandle, new IntPtr(0), "Class Name", null);
This is Correct:
IntPtr child = FindWindowEx(mainWindow, new IntPtr(0), "Class Name", null);
Related
I have a third party application which I did not create myself. I need to create an application which is able to listen for button clicks and read data from tables in that application. I believe that the third party application is made in C# but I do not know for sure. Is there a way to know when UI buttons are pressed and to harvest data from the application? I do not mind which programming language the solution must be written in, as long as it fulfils the above tasks.
You can use few dlls like user32.dll, to get data from other apps.
Find parent handle of a window:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
public static IntPtr FindWindow(string windowName)
{
var hWnd = FindWindow(windowName, null);
return hWnd;
}
After that, find handle of a child window
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
private IntPtr FindSomeElement(IntPtr parent)
{
IntPtr childHandle;
childHandle = FindWindowEx(
parent,
IntPtr.Zero,
"WindowsForms10.EDIT.app21",
IntPtr.Zero);
return childHandle;}
and get text from it:
private static string GetText(IntPtr childHandle)
{
const uint WM_GETTEXTLENGTH = 0x000E;
const uint WM_GETTEXT = 0x000D;
var length = (int)SendMessage(handle, WM_GETTEXTLENGTH, IntPtr.Zero, null);
var sb = new StringBuilder(length + 1);
SendMessage(handle, WM_GETTEXT, (IntPtr)sb.Capacity, sb);
return sb.ToString();
}
//I didnt test this code, just gave an idea.
Visit www.pinvoke.net/default.aspx/ for more information. You can find alot about user32.dll
I want to save and close all kinds of application which is opened. The specific application is identified from Process command. with this I can kill it.
For saving I tried SendKeys class, First I set the SetForegroundWindow(h), and succeeded with ctrl+s by using
SendKeys.Send("^s"); //Ctrl+s
but, user defined applications are there, and also I have a situation to save a new(not existing file) which has to be saved by Alt+F+S.
So that it saves with default name
How do I achieve this,
I tried,
SendKeys.Send("%(fs)");
SendKeys.Send("%f"+"s"); //Alt+F
SendKeys.Send("%f" + "%s)"); //Alt+F
SendKeys.Send("%fs");
Please guide me, If its not possible with SendKeys, what else should I try.
Always use the breakets and you can avoid errors.
SendKeys.Send("%{f}"); //Alt+F
SendKeys.Send("{A}"); //A
or try
SendKeys.Send("%{f}{A}"); //Alt+F And A
What APP? SendKeys will send key strokes but under the hood most things use the good old message loop.
SendMessage(hWnd, WM_SETTEXT, wParam, lParam);
c#
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, String lpWindowName);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
IntPtr hwnd= FindWindow(null, "Window Title");
SendMessage(hwnd, id, IntPtr.Zero, IntPtr.Zero);
To get notepad to save it would be
const int WM_COMMAND = 0x111;
SendMessage(hwnd, WM_COMMAND,777 , NULL);
How can I programatically find the handle of a user control in a webpage running on IE?
I'm able to find it using Spy++ but since the handle keeps changing I'm stuck.
I've been trying using FindWindow() but no luck :( I also wonder if I am doing something wrong or it simply only work for Windows...
Thanks in advance,
Zubrowka
I had a similar problem finding a PDF ActiveX Control inside a IE control in WPF.
To overcome the problem I used the EnumChildWindows API to find the correct child window and thus get its handle.
I'll include as much code as I can.
private static IntPtr FindPdfControlWindow(IntPtr parentHandle)
{
IntPtr result = IntPtr.Zero;
IntPtr matchPointer = IntPtr.Zero;
try
{
//allocate unmanaged memory for the result of the callback delegate
matchPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
Marshal.WriteIntPtr(matchPointer, IntPtr.Zero)
//instantiate the delegate and pass to the API
NativeMethods.EnumWindowProc windowChecker = CheckForPdfControlWindow;
if (!NativeMethods.EnumChildWindows(parentHandle,
windowChecker,
matchPointer))
}
finally
{
if (matchPointer != IntPtr.Zero) Marshal.FreeHGlobal(matchPointer);
}
return result;
}
private static bool CheckForPdfControlWindow(IntPtr handle,
IntPtr matchPointer)
{
int captionLength = NativeMehtods.GetWindowTextLength(handle);
if (captionLength > 0)
{
StringBuilder buffer = new StringBuilder(captionLength + 1);
NativeMethods.GetWindowText(handle, buffer, buffer.Capacity);
if (buffer.ToString().Contains("Adobe"))
{
Marhsal.WriteIntPtr(matchPointer, handle)
return false;
}
}
return true;
}
private static class NativeMethods
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool EnumChildWindows(IntPtr window,
EnumWindowProc callback,
IntPtr i);
internal delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSer.Auto)]
internal static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern int GetWindowText(IntPtr hWnd,
StringBuilder lpString,
int nMaxCount);
}
transcribed in a rush so I hope it is both helpful and accurate.
If the ActiveX control is windowed, then you can query its IOleWindow interface to get the window handle.
Before you query interfaces from the ActiveX, you need to review the page's HTML to find a way to identify the activex in the document, such as element id.
By using this code I can get the title of the active window..
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
private string GetActiveWindowTitle()
{
const int nChars = 256;
IntPtr handle = IntPtr.Zero;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
But how should I do to get the classname of the active window?
Simply pinvoke GetClassName(). This returns the Windows class name for a window, it doesn't have anything to do with a C# class. Getting the C# class name for a window in another process is not possible. Take a look at the Managed Spy++ tool for possible hacks if this is a Winforms app.
I expanded Hans Passant's answer into working code:
Usage:
string className = Spy.GetForegroundWindowClassName();
Class:
using System.Runtime.InteropServices;
using System.Text;
public static class Spy
{
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
public static string GetForegroundWindowClassName()
{
IntPtr hWnd = GetForegroundWindow();
var className = new StringBuilder(256);
GetClassName(hWnd, className, className.Capacity);
return className.ToString();
}
}
Side Note: in my case, I just needed a basic utility to tell me the class name of a window so I could reference that in my C# code. After writing the code above, I realized I could achieve the same thing using pre-existing utilities. One such utility I see mentioned often in the C# community is Visual Studio's Spy++ tool. I didn't bother trying that since it requires downloading 2.5 GB of C++ components. Instead, I used the "Window Spy" tool that comes with Autohotkey. Autohotkey is a tiny download compared to what's needed for Spy++, so I think it's a good option if it suits your needs.
The title is partially static with an variable suffix. For example "Window Title {- user_id}".
How can I get the handle without knowing the exact title?
Look through all the Processes and check the MainWindowTitle. (You can use regexps, or StartsWith, etc)
foreach(Process proc in Process.GetProcesses())
{
if(proc.MainWindowTitle.StartsWith("Some String"))
{
IntPtr handle = proc.MainWindowHandle;
// ...
}
}
This CodeProject article describes how to enumerate Top level windows (Based on Win32 API EnumWindows).
You can easily modify it to filter on a partial window title: Modify EnumWindowsCallBack.
HTH.
Get by class name and parent window handle. For example: get start button handle using win32api. First you know parent window class name using spyxx tool.
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr handleParent, IntPtr handleChild, string className, string WindowName);
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string className, string windowTitle);
Usage:
IntPtr handle = FindWindowEx(FindWindow("Shell_TrayWnd",null), new IntPtr(0), "Button", null);