I am trying to create a program which will show me the status of my Gtalk (online/offline).
I can find the Status View 2 class, but how can I find the text within it.
Here's my code.
API decleration :
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
Code that calls Api :
IntPtr hwnd = IntPtr.Zero;
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Google Talk - Google Xmpp Client GUI Window", "Google Talk");
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Main View", "#main");
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Status View 2", "Status Box");
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "RichEdit20W", "String.Empty");
MessageBox.Show(hwnd.ToString());
Thanks.
I found a solution my self. Thanks to abazabam.
If you look at the figure, there is a panel whose class name is "#32770" and Window Caption is "Sign In Dialogue"
When the user is offline then this panel is visible, and when the user goes online the panel is not visible.
So the main logic is to detect the visibility of the panel.
You can use Spy++ to find the class name.
API decleration :
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool IsWindowVisible(IntPtr hWnd);
Code :
IntPtr hwnd = IntPtr.Zero;
bool check;
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Google Talk - Google Xmpp Client GUI Window", "Google Talk");
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Main View", "#main");
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "#32770", "Sign In Dialogue");
check = IsWindowVisible(hwnd);
if (check == true)
{
MessageBox.Show("User is offline.");
}
else
{
MessageBox.Show("User is online.");
}
Anyways thanks for reading my problem.
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 have simple helper method which allows me to open notepad.exe and fill it by provided text.
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", EntryPoint = "SendMessageW")]
public static extern IntPtr SendMessageW(IntPtr hWnd, UInt32 msg, IntPtr wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
public static void OpenNotepadWithText(string text)
{
var process = Process.Start("notepad.exe");
if (process == null)
throw new Exception("Unable to run notepad.exe process!");
process.WaitForInputIdle();
IntPtr child = FindWindowEx(process.MainWindowHandle, new IntPtr(0), "Edit", null);
SendMessageW(child, 0x000C, IntPtr.Zero, text);
}
When I click on close button of notepad, notepad is closed without asking for save. It seems that used approach does not set "need_to_save" flag in notepad and thus notepad thinks that there is no changed content to save. Can someone help me with setting this flag?
The title says it all, I have this code:
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
const UInt32 WM_CLOSE = 0x0010;
and here's what I added to Form1_Load:
IntPtr windowPtr = FindWindowByCaption(IntPtr.Zero, "Untitled - Notepad");
if (windowPtr == IntPtr.Zero)
{
MessageBox.Show("Window not found");
return;
}
SendMessage(windowPtr, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
so I added the code above to the Form1_Load function, and it actually works, it closes notepad when I open my program, but my question is, how to make the function repeat, like close notepad whenever it opens and not only on Form1_Load ?
You have to enumerate the windows yourself: EnumWindows and in the return procedure check if the title is the same as what you want (hardcoding 'Untitled' might not be the best way by the way). Alternatively traverse the window graph yourself with GetWindow, starting at the first desktop child and iterate the siblings from there.
Also you don't need the IntPtr version of FindWindow, you can pass null as a string parameter and it accomplishes the same.
C#'s GetForegroundWindow() returns the same result for multiple windows, EnumWindows does not really return that window at all. Each process really has its own tier.
I've also went through both of the GetWindowThreadProcessId() functions and enumerated through them with GetChildWindows(), but still they don't return the same window as GetForegroundWindow().
How do you properly start with GetForegroundWindow() and turn it into what you'd retrieve from a proper EnumWindows?
Goal: GetForegroundWindow + enumerate properly to retrieve proper top-level handles (all tabs in chrome, your project form(s)), but without having to sift through the mess GetAllWindows() brings you. Thanks.
private ArrayList GetAllWindows()
{
var windowHandles = new ArrayList();
EnumedWindow callBackPtr = GetWindowHandle;
EnumWindows(callBackPtr, windowHandles);
foreach (IntPtr windowHandle in windowHandles.ToArray())
{
EnumChildWindows(windowHandle, callBackPtr, windowHandles);
}
return windowHandles;
}
private delegate bool EnumedWindow(IntPtr handleWindow, ArrayList handles);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumWindows(EnumedWindow lpEnumFunc, ArrayList lParam);
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool EnumChildWindows(IntPtr window, EnumedWindow callback, ArrayList lParam);
List<IntPtr> ids = new List<IntPtr>();
private bool GetWindowHandle(IntPtr windowHandle, ArrayList windowHandles)
{
windowHandles.Add(windowHandle);
listBox1.Items.Add(windowHandle);
//ids.Add(GetWindowThreadProcessId(windowHandle, IntPtr.Zero));
return true;
}
Here is what I use for C++ in Windows:
TCHAR buf[255];
HWND foregroundWindow = GetForegroundWindow();
DWORD* processID = new DWORD;
GetWindowText(foregroundWindow, buf, 255);
GetWindowThreadProcessId(foregroundWindow, processID);
DWORD p = *processID;
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, p);
TCHAR szProcessName[MAX_PATH];
if (NULL != hProcess )
{
HMODULE hMod;
DWORD cbNeeded;
if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod),
&cbNeeded) )
{
GetModuleBaseName( hProcess, hMod, szProcessName,
sizeof(szProcessName)/sizeof(TCHAR) );
}
}
CloseHandle(hProcess);
long pid = (long)p;
I understand your application is C#, but you can perhaps use some of these Windows API calls to get the information you need. Let me know if there is anything I can do to further explain the code.
In a previous question, I asked how to send text to Notepad. It helped me immensely. For part 2, here's a simplified version of the same applied mIRC:
[DllImport("User32.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
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);
IntPtr mainHandle = FindWindow("mIRC", null);
IntPtr serverHandle = FindWindowEx(mainHandle, new IntPtr(0), "MDIClient", null);
IntPtr chanHandle = FindWindowEx(serverHandle, new IntPtr(0), "mIRC_Channel", null);
IntPtr editHandle = FindWindowEx(chanHandle, new IntPtr(0), "Edit", null);
SendMessage(editHandle, 0x000C, 0, textBox1.Text);
This seems correct to me, except that it doesn't work! Is it that the window names are incorrect (MDIClient, mIRC_Channel, and Edit)? These are values I found on a web site by googling "FindWindowEx mIRC".
1.) What am I doing wrong in the above?
2.) For reference, in general is there an easy way to find all the Window names for use with FindWindowEx()?
This code works for me (mirc 6.31):
IntPtr mainHandle = FindWindow("mIRC", null);
IntPtr serverHandle = FindWindowEx(mainHandle, new IntPtr(0), "MDIClient", null);
IntPtr chanHandle = FindWindowEx(serverHandle, new IntPtr(0), "mIRC_Channel", null);
IntPtr editHandle = FindWindowEx(chanHandle, new IntPtr(0), "richEdit20A", null);
SendMessage(editHandle, 0x000C, 0, "Hello World");
Notice the changed window class (richedit20A instead of edit). Just found the correct class by using Spy++.
As for the window handles, one possibility is to use the EnumWindows or EnumChildWindows API.