Win32's Findwindow() Can Find a Particular Window with the Exact Title, But What About "Try.Bat - Notepad"

Win32's FindWindow() can find a particular window with the exact title, but what about try.bat - Notepad?

I would follow Eric's advice to use EnumWindows. You can provide Ruby callbacks to Windows API functions through win32-api. Here's an example that was trivially modified from the sample in the win32-api README:

require 'win32/api'
include Win32

# Callback example - Enumerate windows
EnumWindows = API.new('EnumWindows', 'KP', 'L', 'user32')
GetWindowText = API.new('GetWindowText', 'LPI', 'I', 'user32')
EnumWindowsProc = API::Callback.new('LP', 'I'){ |handle, param|
buf = "\0" * 200
GetWindowText.call(handle, buf, 200);

if (!buf.index(param).nil?)
puts "window was found: handle #{handle}"
0 # stop looking after we find it
else
1
end
}

EnumWindows.call(EnumWindowsProc, 'Firefox')

How do I change the Window Title after starting something with Process.Start?

You can't change the window title using Process.MainWindowTitle because the property is readonly.

In order to change the window title you will firstly need to obtain a handle to target window and then instruct the Operating System to change the title of the window associated with that handle using the Win32 API function SetWindowsText like this

<DllImport("user32.dll")> _
Shared Function SetWindowText(ByVal hwnd As IntPtr, ByVal windowName As String) As Boolean
End Function

Once you have defined the function above you can proceed to manipulate the window title using the following code:

Dim process As New Process()
process.StartInfo.FileName = "notepad.exe"
process.Start()

Thread.Sleep(100)
SetWindowText(process.MainWindowHandle, "Fancy Notepad")

You need to wait a short few milliseconds before changing the window title otherwise the window title will not change.

How to find a desktop window (by window name) in Windows 8.1 Update 2 OS, using the Win32 API FindWindow() in PowerShell environment?

First : not an answer but to help other people working on it, if you use the good class, for example here I code CalcFrame wich is the real class of the main window of calc.exe it works.

$fw::FindWindow("CalcFrame", $wname) # returns the right value for me if calc.exe is started.

Second : The following works for me ; accordind to Microsoft documentation the first parameter should be null, but accordin to PInvoke site you must pass IntPtr.Zero as the first parameter.

$sig = @"
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(IntPtr sClassName, String sAppName);

[DllImport("kernel32.dll")]
public static extern uint GetLastError();
"@

$fw = Add-Type -Namespace Win32 -Name Funcs -MemberDefinition $sig -PassThru
$wname='Calculatrice' # any existing window name

$fw::FindWindow([IntPtr]::Zero, $wname ) # returns the Window Handle
$a = $fw::GetLastError()
$a

getting a specific window name in c++

I don't think there's really any easier way by using the raw winapi, but here goes:

  1. Use the Toolhelp32 API to get a list of process IDs whose executable names match "notepad.exe".
  2. Enumerate the windows to find any whose PID matches one in the list.
  3. Grab the title of that window and do what you will with it.

Here's the code I came up with:

#include <iostream>
#include <string>
#include <vector>

#include <windows.h>
#include <tlhelp32.h>

bool isNotepad(const PROCESSENTRY32W &entry) {
return std::wstring(entry.szExeFile) == L"notepad.exe";
}

BOOL CALLBACK enumWindowsProc(HWND hwnd, LPARAM lParam) {
const auto &pids = *reinterpret_cast<std::vector<DWORD>*>(lParam);

DWORD winId;
GetWindowThreadProcessId(hwnd, &winId);

for (DWORD pid : pids) {
if (winId == pid) {
std::wstring title(GetWindowTextLength(hwnd) + 1, L'\0');
GetWindowTextW(hwnd, &title[0], title.size()); //note: C++11 only

std::cout << "Found window:\n";
std::cout << "Process ID: " << pid << '\n';
std::wcout << "Title: " << title << "\n\n";
}
}

return TRUE;
}

int main() {
std::vector<DWORD> pids;

HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
// Do use a proper RAII type for this so it's robust against exceptions and code changes.
auto cleanupSnap = [snap] { CloseHandle(snap); };

PROCESSENTRY32W entry;
entry.dwSize = sizeof entry;

if (!Process32FirstW(snap, &entry)) {
cleanupSnap();
return 0;
}

do {
if (isNotepad(entry)) {
pids.emplace_back(entry.th32ProcessID);
}
} while (Process32NextW(snap, &entry));
cleanupSnap();

EnumWindows(enumWindowsProc, reinterpret_cast<LPARAM>(&pids));
}

Going through in order:

First note the wide versions of functions and strings. TCHAR is not good to use and it would be a shame if one of the titles happened to have UTF-16 in it.

isNotepad just checks the executable name member of the PROCESSENTRY32W structure to see whether it equals "notepad.exe". This assumes Notepad uses this process name, and that nothing that isn't Notepad uses the process name. To eliminate false positives, you'd have to do more checking, but you can never be too sure.

In enumWindowsProc, take note that lParam is actually a pointer to a vector of PIDs (to save ourselves from having to use a global). This constitutes the cast at the beginning of the function. Next, we get the PID of the window we found. Then, we loop through the list of PIDs passed in and check whether it matches any. If it does, I chose to grab the title and output the PID and the window title. Note that using a standard string as a buffer is only guaranteed to work in C++11, and must not have the extra null character (not part of the length) overwritten. Lastly, we return TRUE so that it keeps enumerating until it has gone through every top-level window.

Onto main, the first thing you see is our initially empty list of PIDs. We take a snapshot of the processes and go through them. We use the helper function, isNotepad to check whether the process is "notepad.exe", and if so, store its PID. Lastly, we call EnumWindows to enumerate the windows, and pass in the list of PIDs, disguised as the required LPARAM.

It's a bit tricky if you haven't done this sort of thing, but I hope it makes sense. If you want the child windows, the correct thing to do would be to add a EnumChildWindowsProc and call EnumChildWindows with that in the spot where I output information about the found window. If I'm correct, you don't need to recursively call EnumChildWindows to get grandchildren etc., as they will be included in the first call.

How can a batch file run a program and set the position and size of the window?

Try launching your programs from VBS (Windows Script Host) script via the batch file. If your VBS looks like this:

'FILENAME: SetEnv.vbs
Set Shell = WScript.CreateObject("WScript.Shell")
Shell.Run "Explorer /n,c:\develop\jboss-4.2.3.GA\server\default\deploy", 4, False
Shell.Run "Explorer /n,c:\develop\Project\Mapping\deploy", 4, False

The 4 means to launch the window in its most recent size/position. False means it won't wait to return before executing the next line of your script. Unfortunately, this doesn't give you full control of your exact window size/positioning, but it should remember last size/positioning.

More info here: http://www.devguru.com/Technologies/wsh/quickref/wshshell_Run.html

So your new SetEnv.cmd could be:

@echo off
REM note there's a difference between cscript and wscript
REM wscript is usually the default launcher
cscript SetEnv.vbs
cd C:\develop\jboss-4.2.3.GA\bin
run


Related Topics



Leave a reply



Submit