I can't get the "Edit" field handle when selecting a photo in a third-party application.
Spy++ shows everything correctly, but FindWindow fails. I can get the handle of the window itself, the parent. I assume I need to look for child windows. I can get some handles with GetWindow but but it's not clear what they are. The window title is empty. FindWindowEx doesn't work at all, returns 0. I indicate it like this:
IntPtr hwndchild = (hwnd, IntPtr.Zero, null, "Edit")
Based on the screenshot you provided, and using just the FindWindow/Ex() functions, you can get the HWND of the Edit control like this:
IntPtr hwndDlg = FindWindow(null, "Choose an image");
IntPtr hwndCBEx = FindWindowEx(hwndDlg, IntPtr.Zero, "ComboBoxEx32", null);
IntPtr hwndCB = FindWindowEx(hwndCBEx, IntPtr.Zero, "ComboBox", null);
IntPtr hwndEdit = FindWindowEx(hwndCB, IntPtr.Zero, "Edit", null);
However, once you have the HWND to the ComboBoxEx control, the correct way to get the HWND of its Edit control is to use the CBEM_GETEDITCONTROL message:
const int CBEM_GETEDITCONTROL = 1031;
IntPtr hwndDlg = FindWindow(null, "Choose an image");
IntPtr hwndCBEx = FindWindowEx(hwndDlg, IntPtr.Zero, "ComboBoxEx32", null);
IntPtr hwndEdit = SendMessage(hwndCBEx, CBEM_GETEDITCONTROL, 0, 0);
Note, for a standard ComboBox control (which you can get from a ComboBoxEx control using the CBEM_GETCOMBOCONTROL message), you can use the CB_GETCOMBOBOXINFO message or the GetComboBoxInfo() function. The HWND of the Edit control is returned in the COMBOBOXINFO.hwndItem field.
If you are looking for a child window of a parent you should use EnumChildWindows. The following is C++ code but could be pinvoked easily: you can marshal delegates as function pointers for the callback.
std::vector<HWND> FindChildrenByClass(HWND parent, const std::string& target_class)
{
struct EnumWndParam {
std::vector<HWND> output;
std::string target;
} enum_param;
enum_param.target = target_class;
EnumChildWindows(
parent,
[](HWND wnd, LPARAM lparam) -> BOOL {
auto param = reinterpret_cast<EnumWndParam*>(lparam);
char class_name[512];
GetClassName(wnd, class_name, 512);
if (param->target == class_name)
param->output.push_back(wnd);
return TRUE;
},
reinterpret_cast<LPARAM>(&enum_param)
);
return enum_param.output;
}
int main()
{
auto windows = FindChildrenByClass( reinterpret_cast<HWND>(0x0061024A), "Edit");
for (auto wnd : windows) {
std::cout << std::hex << wnd << std::endl;
}
}
Note in the above that I do not recursively call FindChildrenByClass in the callback lambda. This is not a mistake. EnumChildWindows already performs this recursion. It runs over a parent window's children and grand children, etc., out of the box without you having to specify this behavior or implement it.
Like someone already assumed. Try the EnumChildWindow method.
Here is an complete sample already on stackoverflow
Related
I am implementing a plugin for a product called AmiBroker in C#.
AmiBroker is a trading software it has exposed a few functions which can be used by 3rd party vendors to pass the stock data to solution. So, we can create a plugin in C# which can be recognized by AmiBroker.
In my scenario I am getting a handler of Main Window of AmiBroker [Note : AmiBroker is fully written in C++] In C# we can retrieve the handler of Main Window, so using this handle can I read the data of the window e.g. Child windows, Panels showing stock lists or things which are visible to the User, and if so, how would I go about doing this?
You can, but it's messy. I literally just worked on something very similar. Pinvoke.net is great for this stuff, but I'll show you some examples of how I'd find controls. If AmiBroker has any documentation for control names or AccessibleNames or anything that allows you to find the exact controls you're looking for, that'd be killer. Because if they're ambiguously named, you're gonna have a helluva time finding the ones you're specifically looking for. But basically, what you'll want to do is EnumChildWindows on the handle you have, iterate through them and look for a unique property to allow you to find the control you want. Then you'll need to execute a specific SendMessage to get the text off of a control (GetWindowText or whatever it's called only works for labels). Code as follows, adapted or swiped from Pinvoke.net at some point (great starting point):
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
public static extern uint GetClassName(IntPtr handle, StringBuilder name, int maxLength);
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
private 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);
// You can modify this to check to see if you want to cancel the operation, then return a null here
return true;
}
//THIS IS THE ONE YOU'LL CALL!
public static IntPtr GetWindowByClass(IntPtr mainWindow, string name)
{
List<IntPtr> windows = GetChildWindows(mainWindow);
foreach (IntPtr window in windows)
{
StringBuilder response = new StringBuilder();
response.Capacity = 500;
if (GetClassName(window, response, response.Capacity) > 0)
if (response.ToString() == name)
return window;
}
return IntPtr.Zero;
}
So basically it iterates through a whole set of child windows for the handle you have on the app, sees if the class name matches a control you're looking for, then returns it. There are thousands of ways to improve it (search for all the ones you want in a single shot, FindWindow MAY work by class name, etc.) but I wanted to show you more how it's done, not declare this is how it should be done. Finally, the call to get the text from the window/control is as follows (also adapted from pinvoke.net: look under User32.dll for all this stuff):
public static string GetText(IntPtr control)
{
StringBuilder builder = new StringBuilder(40);
IntPtr result = IntPtr.Zero;
uint response = SendMessageTimeoutText(control, 0xd, 40, builder, APITypes.SendMessageTimeoutFlags.SMTO_NORMAL, 2000, out result);
return builder.ToString();
}
[DllImport("user32.dll", EntryPoint = "SendMessageTimeout", SetLastError = true, CharSet = CharSet.Auto)]
public static extern uint SendMessageTimeoutText(
IntPtr hWnd,
int Msg, // Use WM_GETTEXT
int countOfChars,
StringBuilder text,
APITypes.SendMessageTimeoutFlags flags,
uint uTImeoutj,
out IntPtr result);
[Flags]
public enum SendMessageTimeoutFlags : uint
{
SMTO_NORMAL = 0x0,
SMTO_BLOCK = 0x1,
SMTO_ABORTIFHUNG = 0x2,
SMTO_NOTIMEOUTIFNOTHUNG = 0x8
}
EDIT: An addendum: the application I worked on to access another form like this actually didn't have unique control names, so I ended up using Spy++ to determine its place in the window hierarchy and pulling the children and selecting each child in turn. God help if you have to go that route, especially because it may not be consistent at all, especially if what you need is on a form that isn't created, or it's hidden behind another one that jumped it in the Z-Order (breaking your hierarchical list you're searching from). That said, you should know that EnumChildWindows will always enum ALL CHILD WINDOWS for a given window, no matter where they are in the hierarchy. If you really have to drill down and search for each control by its parent and its parent's parent, you'll need to use FindWindowEx, and declare the last child you looked at (or IntPtr.Zero if you want the first child):
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
What you are asking for seems counter intuitive if the software you are writing the plug-in for provides an API. You should really be using that.
While it is possible to use the Win32 API to enumerate child windows of the main window given the handle and then use more Win32 API functions to determine the state of the UI (i.e. "read the data") it's going to be very tedious and error-prone.
Here's a link to MSDN for EnumChildWindows which will allow you to enumerate child windows for the main window given the handle.
If you want to go down that rabbit hole you might also find SendMessage and GetWindowText useful. And most definitely you should check out pinvoke.net if you are going to be using these Win32 APIs from C#.
I would like to know if there was any way to lock onto a Flash window and post a message to it? Another person here had the answer to it, his name is Spencer K. His question was:
Sending simulated click via WebBrowser in C# to flash object embedded in HTML
Unfortunately, Mr. K wasn't very specific, and all he left behind for people reading his question was that he "got the handle and then iterated through the handles." I'm not extremely sure what he meant by that. I iterated through all visible handles using EnumWindows to no avail, as that did not return a window that was a flash window.
I hope somebody here could tell me, as it's been driving me mad for the past few days.
EDIT: I've just settled on inserting an SWF Object into my form and posting messages to the handle of that.
Actually flash window has its own handle too. To get it you have to get the class names of the controls it is embedded in from Spy++, then you can reach it like this:
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle);
public IntPtr Flash()
{
IntPtr pControl;
pControl = FindWindowEx(webBrowser1.Handle, IntPtr.Zero, "Shell Embedding", IntPtr.Zero);
pControl = FindWindowEx(pControl, IntPtr.Zero, "Shell DocObject View", IntPtr.Zero);
pControl = FindWindowEx(pControl, IntPtr.Zero, "Internet Explorer_Server", IntPtr.Zero);
pControl = FindWindowEx(pControl, IntPtr.Zero, "MacromediaFlashPlayerActiveX", IntPtr.Zero);
return pControl;
}
When you get the handle, you can post the clicks:
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
public enum WMessages : int
{
WM_LBUTTONDOWN = 0x201,
WM_LBUTTONUP = 0x202
}
private int MAKELPARAM(int p, int p_2)
{
return ((p_2 << 16) | (p & 0xFFFF));
}
public void DoMouseLeftClick(IntPtr handle, Point x)
{
PostMessage(handle, (uint)WMessages.WM_LBUTTONDOWN, 0, MAKELPARAM(x.X, x.Y));
PostMessage(handle, (uint)WMessages.WM_LBUTTONUP, 0, MAKELPARAM(x.X, x.Y));
}
The points will be relative to the client, so when you save them, you should save it like this:
List<Point> plist = new List<Point>();
private void webBrowser1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
switch (e.KeyCode)
{
case Keys.C:
plist.Add(webBrowser1.PointToClient(Cursor.Position));
break;
default:
break;
}
}
Hope this was helpful
You can do it via javascript.
Import this:
import flash.external.ExternalInterface;
Ad this to your AS code:
if (ExternalInterface.available) {
// add external interface
ExternalInterface.addCallback("jsFunction", asFunction);
}
public static function asFunction(message:String):void {
}
On your JS object of the flash object you can call this function:
jsObject.jsFunction("message");
This is the function to get the js object of the flash object:
var InternetExplorer = navigator.appName.indexOf("Microsoft") != -1;
jsObject = InternetExplorer ? window.jsObjectName: window.document.jsObjectName;
I did not test this code, I just copied it out of a project.
edit: added js function to get js object
The Flash window will probably not have its own handle, since it is embedded in a webpage. You would want to post the message to the web browser window (that's what Mr. K did).
If that doesn't work for you, the only other option that I'm aware of is to gain control of the browser via WebDriver or WatiN and interact with the flash object using javascript.
for calling a function in flash object u can use this code
swfobject.CallFunction(
"<invoke name=\"yourfunction\" returntype=\"xml\">" +
" <arguments><number>" yourvalue "</number></arguments> </invoke> ");
for more information follow this link:communicate-betwen-c-and-an-embeded-flash-application
i try it for a flash object in my form application and it work,but i did not use it for webbrowser
I'm trying to click on a menu item inside a program called Media Subtitler and whatever I'm trying to do it's not working.
First, I tried to use the function GetMenu but it returned IntPtr.Zero.
Then, I tried using the ALT key + using the first letter of my menu (F stands for file) but it did nothing.
Then, I tried using a simple MOUSEDOWN and MOUSEUP messages but again, it did nothing (I also tried creating a loop that clicks on everything in that range but there was no click in that area).
What I clearly know is that I'm working on the correct window.
What am I doing wrong?
If someone wants to test it out you can download Media Subtitler for free and it doesn't weight that much.
Also, Here's the code I've been testing:
Process p = Process.Start(#"C:\Program Files\DivXLand\Media Subtitler\MediaSub.exe");
p.WaitForInputIdle(1500);
Thread.Sleep(3000);
SetForegroundWindow(p.MainWindowHandle);
ShowWindow(p.MainWindowHandle, SW_MAXIMIZE);
IntPtr handle = p.MainWindowHandle;
SendMessage(handle, WM_NCHITTEST, 0, MakeLParam(18, 29));
//for (int i = 0; i < 200; i++)
//{
// for (int x = 0; x < 200; x++)
// {
// SendMessage(p.MainWindowHandle, WM_LBUTTONDOWN, 0, MakeLParam(i, x));
// SendMessage(p.MainWindowHandle, WM_LBUTTONUP, 0, MakeLParam(i, x));
// }
//}
//IntPtr menuItems = GetMenu(p.MainWindowHandle);
return;
//SendMessage(p.MainWindowHandle, WM_COMMAND, 6, 0);
SendMessage(p.MainWindowHandle, WM_KEYDOWN, VK_MENU, 0);
SendMessage(p.MainWindowHandle, WM_KEYUP, VK_MENU, 0);
SendMessage(p.MainWindowHandle, WM_KEYDOWN, VK_F, 0);
SendMessage(p.MainWindowHandle, WM_KEYUP, VK_F, 0);
Thanks for any help!
By monitoring the messages sent to the main window of the application, I extracted the menu identifiers for the menu items. You can post WM_COMMAND message to the window, with the ID of the menu items as the wParam:
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, Message msg, int wParam, int lParam);
PostMessage(handle, WM_COMMAND, 2, 0); // File->New subtitle
PostMessage(handle, WM_COMMAND, 3, 0); // File->New from clipboard
PostMessage(handle, WM_COMMAND, 5, 0); // File->Open text or subtitle
PostMessage(handle, WM_COMMAND, 6, 0); // File->Open video
...
I've tested the code with Media Subtitler, and it works like a charm! The only situation that this will not work, is when on windows Vista or Seven, your target program is running as Administrator and you C# program is not. Be aware of that!
The menu IDs can be easily examined by monitoring the WM_COMMAND message (using Spy++).
You can also use SendMessage instead of PostMessage, but then your program freezes until the user closes the window opened by the menu action.
You can use the same approach to send other command to other windows of the application. For example, clicking the 'Open' button of the 'Open video' window.
You can also do all of this using the System.Windows.Automation namespace: http://msdn.microsoft.com/en-us/library/ms590934.aspx
Using this namespace, you do not need to do any interop with the Win32 API. Here's an example of how to get a window by searching for a string that its name contains:
public static AutomationElement GetWindowByName(string name)
{
AutomationElement root = AutomationElement.RootElement;
foreach (AutomationElement window in root.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Window)))
{
if (window.Current.Name.Contains(name) && window.Current.IsKeyboardFocusable)
{
return window;
}
}
return null;
}
After you have the window as an AutomationElement object, you can search it for controls and perform operations on those controls, etc.
Hope this helps!
In Visual Studio there is a tool Spy++ with which you should be able to see handle of objects.
If you see it there, you should be able to get to it using user32.dll function
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string className, string lpszWindow);
to get to them(handle will be different everytime you run your app)
If you get the right handle, you should be able to use SendMessage to send enter, or click.
Tried this only on one app, and I used it only to read and write text, so sorry if it wont work
I would like to simulate keystrokes within an embedded System.Windows.Controls.WebBrowser. Various techniques for simulating keystrokes are documented already here on StackOverflow, however they do not seem to work for the WebBrowser control.
Knowing that the control wraps another window/hwnd, I would have expected the following to work however it's not:
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
...
SendMessage(myWebBrowser.Handle, WM_CHAR, key, 0);
I am already using SendMessage to forward simulated keystrokes to other parts of the WPF application, and would prefer a consistent solution; however this is failing for the WebBrowser.
How can I forward simulated keystrokes to WebBrowser?
My solution was to use SendInput() instead of SendMessage().
The import:
[DllImport("user32.dll", SetLastError = true)]
public static extern uint SendInput(uint nInputs, User32.Input[] pInputs, int cbSize);
For the additional types and constants see here: http://pinvoke.net/default.aspx/user32/SendInput.html
For the expected behavior see here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx.
My virtual keypress method:
private void VirtualKeypress(Key keyCode, bool shift, char keyChar)
{
User32.Input[] inputSequence;
if (keyChar == '\0' && keyCode == Key.None)
{
throw new ArgumentException("Expected a key code or key char, not both.");
}
else if (keyChar != '\0')
{
inputSequence = KeyboardUtils.ConvertCharToInputArray(keyChar);
}
else
{
inputSequence = KeyboardUtils.ConvertKeyToInputArray(keyCode, shift);
}
User32.SendInput(
(uint)inputSequence.Length,
inputSequence,
Marshal.SizeOf(typeof(User32.Input))
);
}
I have two helper methods, ConvertCharToInputArray() and ConvertKeyToInputArray(), which return an array of length 2 or 4 depending if we need to tell windows that the shift key is depressed. For example:
'A' -> [] { shift down, A down, A up, shift up }
while just
'a' -> [] { A down, A up }
.
You were so close! The handle reported by WebBrowser.Handle is the outter most handle, while all of the input is directed to the inner most handle:
var hwnd = _browser.Handle;
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Shell Embedding", null);
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Shell DocObject View", null);
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Internet Explorer_Server", null);
SendMessage(hwnd, WM_CHAR, new IntPtr(0x0D), IntPtr.Zero);
FindWindowEx definition from pinvoke.net:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
Highlighted is the WebBrowser control:
Well i'm only used to using this in VB6 but Try sending to myWebBrowser.object.Handle or myWebBrowser.object.HWND is what i see in VB6, but you probably have .Handle in your .net version.
try the .object and let me know how it goes!!
I found I could do movement around an HTML form (Chrome Browser) from within a C# program by using sendmessage to the process #.
However, I couldn't insert text into an input field. Tried most everything (from pure C#).
While hacking, I noticed I could pop-up a context editing menu while the cursor was on the input I was trying to set, and one of the items on the menu was paste! WhatDoYouKnow! I could interact with that!
Here are the codes I used, once I had tabbed to the input I wanted to set:
Clipboard.SetText("52118"); // from C#, put the input value onto the clipboard
chrome.SendKey((char)93); // char 93 opens pop-up menu that includes paste
System.Threading.Thread.Sleep(30);
chrome.SendKey((char)0x28); // down to the first menu item
System.Threading.Thread.Sleep(30);
chrome.SendKey((char)0x28); // down to the second menu item (paste)
System.Threading.Thread.Sleep(100);
chrome.SendKey((char)0x0D); // fire the paste
Check here for the code used for the ChromeWrapper (Thanks for that!):
Sending keyboard key to browser in C# using sendkey function
You have have to use PostMessage instead of SendMessage, then it should work.
PS: 9 years late I know
I have a WPF TaskBar like application that i am developing for fun and i want to be able to open the System Menu for the application (that is in my custom taskbar) that i right click on, preserving any custom menu that the app might create (i.e. Google Chrome). I have the following code that works for a borderless window app i made previously, but it doesn't seem to work and i am wondering why? And what do i need to do to get it to work?
public static void ShowContextMenu(IntPtr hWnd)
{
SetForegroundWindow(hWnd);
IntPtr wMenu = GetSystemMenu(hWnd, false);
// Display the menu
uint command = TrackPopupMenuEx(wMenu,
TPM.LEFTBUTTON | TPM.RETURNCMD, 0, 0, hWnd, IntPtr.Zero);
if (command == 0)
return;
PostMessage(hWnd, WM.SYSCOMMAND, new IntPtr(command), IntPtr.Zero);
}
Tip: It appears that TrackPopupMenuEx(...) returns immediately with the value of 0, instead of waiting for a response...
It appears there is an issue with giving TrackPopupMenuEx the owner window handle of the Menu... instead i used the handle of my wpf window and then when posting the message, i used the Menu's owner... seems a bit strange to me but it works!
public static void ShowContextMenu(IntPtr hAppWnd, Window taskBar, System.Windows.Point pt)
{
WindowInteropHelper helper = new WindowInteropHelper(taskBar);
IntPtr callingTaskBarWindow = helper.Handle;
IntPtr wMenu = GetSystemMenu(hAppWnd, false);
// Display the menu
uint command = TrackPopupMenuEx(wMenu,
TPM.LEFTBUTTON | TPM.RETURNCMD, (int) pt.X, (int) pt.Y, callingTaskBarWindow, IntPtr.Zero);
if (command == 0)
return;
PostMessage(hAppWnd, WM.SYSCOMMAND, new IntPtr(command), IntPtr.Zero);
}