How can I close windows by their handle?

4

Does anyone know of an application which would close a window given its handle? Command line is good.

Note, that I do not wish to kill the respective application, rather a modal window owned by that application.

Rationale:

Sometime, a modal dialog is opened beneath the main window on my laptop. This happened not once for VS and Firefox. Very annoying.

I can locate the window with Spy++, but have no means of killing it.

EDIT:

An application allowing to send messages to an arbitrary window is good as well, I guess I can then send something like WM_CLOSE or whatever.

EDIT:

I wish to stress, that I am not interesting in closing a visible window. The whole point is to deal with ugly abnormalities when a modal dialog gets open beneath the owning window, which did happen and not once for me while working with VS and Firefox. So, the desired solution is to close a window by its handle or, if it could specifically locate obscured windows and bring them forth.

mark

Posted 2009-08-26T18:54:28.660

Reputation: 726

Alt-Tab usually brings the modal dialog back on top. – Remus Rusanu – 2009-08-26T18:59:06.497

Well, not in my scenarios. It only does so if the modal dialog is open with a certain window style (do not remember exactly) which makes the dialog appear in the task bar. – None – 2009-08-26T20:36:30.780

Answers

4

Okay, I made a small app that does the trick.

Screenshot

You can download it here.

Usage:

  1. Start the program
  2. Hold your mouse over the window you want to close (don't click on it)
  3. Press delete.

It sends a wm_close to the window under the mouse cursor.

Delphi code below...

unit uCloseWindow;

interface

uses
  Windows, Forms, Messages, SysUtils, Variants, Classes, Controls;

type
  TfrmMain = class(TForm)
    procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  public
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

procedure TfrmMain.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  HandleUnderCursor:HWND;
begin
  if Key=VK_DELETE then
  begin
    HandleUnderCursor := WindowFromPoint(Mouse.CursorPos);
    SendMessage(HandleUnderCursor,WM_CLOSE,0,0)
  end;
end;

end.

Wouter van Nifterick

Posted 2009-08-26T18:54:28.660

Reputation: 161

Not good. Like I said the window is hidden behind another window. The whole purpose it tackle these ugly cases. – None – 2009-08-27T06:24:22.577

See the second EDIT in my post. – None – 2009-08-27T06:27:53.023

3No, you said it was "beneath" another window (implying y-position), and you didn't mention that the window was not visible. It's a bit rude of you to downvote after the effort i made to help you out. – None – 2009-08-27T08:21:54.027

On second thought, you are right. I should have explained myself better. You can downvote my question in return, because of this absense of clarity in it, I deserve it fairly. – None – 2009-08-27T09:07:39.673

There is no reason to downvote this answer. It is a valid programming answer to a not so programming related question. Reciprocal downvoting is hardly productive. – Sinan Ünür – 2009-08-27T10:18:07.940

3

I took this as an excuse to try out the Win32API for Ruby.

require 'Win32API'

WM_CLOSE = 0x0010
FindWindow = Win32API.new('user32', 'FindWindow', ["P", "P"], "L")
SendMessage = Win32API.new('user32', 'SendMessage', ["L", "L", "P", "P"], "L")

def Send_WM_CLOSE(title)
  handle = FindWindow.call(nil, title)
  SendMessage.call(handle, WM_CLOSE, nil, nil) if handle != 0
end

if ARGV[0].to_i==0
  title=String.new(ARGV[0])
  Send_WM_CLOSE(title)
else
  SendMessage.call(ARGV[0].to_i, WM_CLOSE, nil, nil)
end

Using this you can close a fresh notepad with

> ruby closewindow.rb "Untitled - Notepad"

or if you know the handle

> ruby closewindow.rb 15794730

Jonas Elfström

Posted 2009-08-26T18:54:28.660

Reputation: 225

Nice, but I am not going to install ruby for that :-) – None – 2009-08-26T20:42:28.410

There is a one-click installer, in case you change your mind. http://rubyforge.org/projects/rubyinstaller/

– Jonas Elfström – 2009-08-26T21:42:49.850

1

Here is a Perl script to do that:

#!/usr/bin/perl

use strict;
use warnings;

use Win32::GuiTest qw(FindWindowLike SendKeys SetForegroundWindow);

die "Need pattern to match against window titles\n" unless @ARGV;
my ($windowtitle) = @ARGV;

my ($myhandle) = FindWindowLike(0, qr/winclose\.pl/);

my @windows = FindWindowLike(0, qr/\Q$windowtitle\E/i);

for my $handle ( @windows ) {
    next if $handle == $myhandle;
    SetForegroundWindow($handle);
    SendKeys("%{F4}");
}

And here is some entertainment using such a script (please do not consider this spam, I am just trying to illustrate a use of Perl's Win32::GuiTest: http://www.youtube.com/watch?v=BAg7K_uwNZs

Sinan Ünür

Posted 2009-08-26T18:54:28.660

Reputation: 991

Cute. But I do not feel like installing perl just for this. – None – 2009-08-27T06:29:33.083

1

This would be mind-numbingly simple to cook up for yourself. I see you've rejected Perl. What's your favorite language?

Here's a simple C example (not tested, going from memory):

#include <windows.h>
#include <stdio.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){
    HWND hWnd;

    if(!sscanf(lpCmdLine, "%i", &hWnd)){
        MessageBox(null, "Invalid argument", "Close Window", MB_OK | MB_ICONERROR);
        return 1;
    }

    PostMessage(hWnd, WM_CLOSE, 0, 0);
}

This is a simple C# example (again, not tested):

using System;
using System.Runtime.Interop;

static class Program{
    [DllImport("user32.dll", CharSet=CharSet.Auto)]
    static extern int PostMessage(int hWnd, int msg, int wParam, int lParam);

    const int WM_CLOSE = 16;

    static void Main(string[] args){
        int hWnd;
        if(args.Length == 1 && int.TryParse(args[0], out hWnd))
            PostMessage(hWnd, WM_CLOSE, 0, 0);
        else MessageBox.Show("Invalid Argument", "CloseWindow", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

P Daddy

Posted 2009-08-26T18:54:28.660

Reputation: 391

You are right. I was wondering if there are more user friendly solutions. Although, I have mentioned simple command line is good enough, usually one expects some nifty UI which lets you select the window, tells you all kind of info about it, etc... As of now, I still have to pair it with Spy++. But you are right nonetheless. – None – 2009-08-27T06:33:38.533

Making it into a GUI wouldn't take much more, except for a small investment in time. Spy++'s drag-a-target-and-highlight-a-window behavior is pretty easy to duplicate with an appropriate icon and cursor. The window-highlighting is easily done with the WinAPI functions GetWindowRgn and FrameRgn. There are other API functions that will give you whatever info about the window you're looking for. And SetWindowPos can be used to bring the target dialog to the top of the Z order so you can see it and interact with it instead of just closing it. – None – 2009-08-27T12:12:28.413

Everything is easy and everything takes time we do not have. Why invent a wheel if it is probably already invented? I know how this works, you develop a little nifty utility for self usage, then you extend it, then you debug it to fix bugs and one easily finds itself wasting a couple of days on something already done and tested by others. – None – 2009-08-28T19:11:48.397

You've got a good point. But on the other hand, in a case like this, I believe more time might be spent searching for an existing solution than building one. Not every esoteric need is already satisfactorily met by an existing product—a fact to which I owe my prosperity. – None – 2009-08-28T20:56:21.900

0

So this answer involves downloading a free script runner LINQPad to run the script then running it. run it, and it asks you to place your cursor over the window you wish to close. It interrogates that window for the title, shows it to you, then asks if you wish to close it.

// close an app's foremost window - will try to just close whatever window the mouse is over first, see if that does the trick
// close works when linqpad runs as administrator. Used to close a modal in linqpad that was stuck
open System.Drawing

module PInvoke = 
    type WindowHandle = nativeint
    module Native = 
        open System.Drawing
        open System.Runtime.InteropServices


        //http://pinvoke.net/default.aspx/user32.SendMessage
        // IntPtr would be fine here, nativeint is more idiomatic
        [<DllImport("User32", SetLastError=true)>]
        extern nativeint private SendMessage(WindowHandle hWnd, int Msg, nativeint wParam, nativeint lParam)

        [<DllImport("User32", EntryPoint="WindowFromPoint", ExactSpelling = true)>]
        extern WindowHandle private WindowFromPoint (Point point)

        // https://stackoverflow.com/questions/18184654/find-process-id-by-windows-handle
        //https://stackoverflow.com/a/18184700/57883
        [<DllImport("User32", SetLastError=true)>]
        extern int private GetWindowThreadProcessId(WindowHandle hWnd, int& processId)

        // https://stackoverflow.com/questions/1316681/getting-mouse-position-in-c-sharp
        [<DllImport("User32", SetLastError=true)>]
        extern bool private GetCursorPos(Point& lpPoint);

        //https://stackoverflow.com/questions/647236/moving-mouse-cursor-programmatically
        // claims sendInput is better than send messages for clicking
        [<DllImport("User32", SetLastError=true)>]
        extern System.Int64 SetCursorPos(int x, int y);
//        let dll = DllImportAttribute("")

        // But, if you need to get text from a control in another process, GetWindowText() won't work. Use WM_GETTEXT instead.
        // another mapping for StringBuilder out
        // you might need to get the string length from another call before calling this: https://www.pinvoke.net/default.aspx/user32.getwindowtext
        [<DllImport("User32",CharSet=CharSet.Auto,EntryPoint="SendMessage")>]
        extern nativeint SendWMText(WindowHandle hWnd, int msg, nativeint wParam, StringBuilder sb);

    open Native

    type SendMessageRaw = {hWnd:nativeint; msg:int; wParam:nativeint; lParam:nativeint}
    type Message<'t> = 
        | Close of windowHandle:nativeint
        | GetText of windowHandle:nativeint * withReturn :(string -> unit)
        | [<Obsolete("Use only for testing, don't leave things unmapped")>]
            Raw of SendMessageRaw

    let ptToParam (pt:System.Drawing.Point) = nativeint (pt.Y <<< 16 ||| pt.X)
    let sendMessage message = 
        let sendMessage a b c d = SendMessage(a,b,c,d)
        match message with
        | Close hwnd ->
            let WM_CLOSE = 0x0010
            printfn "Attempting close"
            sendMessage hwnd WM_CLOSE IntPtr.Zero IntPtr.Zero
        | GetText (hWnd,f) ->

            let WM_GETTEXTLENGTH = 0x000E
            printfn "Getting text length"
            let length: int =int<|SendMessage(hWnd,WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero)
            printfn "Got text length: %i " length
            let mutable sb = StringBuilder(length + 1)

            let WM_GETTEXT = 0x000D

            let result = SendWMText(hWnd,WM_GETTEXT,nativeint (length + 1),sb)
            printfn "Text returned is length %i" sb.Length
            sb.ToString()
            |> f
            result
        | Raw x -> SendMessage(x.hWnd, x.msg, x.wParam, x.lParam)

    let windowFromPoint(pt:Point) = 
        let mutable pt = pt
        let hwnd = WindowFromPoint(pt)
        if hwnd <> IntPtr.Zero then
            Some hwnd
        else None
    let getCursorPos() = 
        let mutable pt = Point()
        match GetCursorPos(&pt) with
        | true -> Some pt
        | false -> None
    let getWindowThreadProcessId hwnd = 
        let mutable pId = 0
        let result = GetWindowThreadProcessId(hwnd, &pId)
        if pId <> 0 then
            Some pId
        else None

type WindowInfo = {PId:int;MainWindowHandle:nativeint; ProcessName:string}
let getWindowInfo hwnd = 
    hwnd
    |> PInvoke.getWindowThreadProcessId
    |> Option.map (Process.GetProcessById)
    |> Option.map (fun p ->
        {PId=p.Id; MainWindowHandle=p.MainWindowHandle; ProcessName=p.ProcessName}
    )

Util.ReadLine("Put the cursor of the desired window") |> ignore
let currentPt = PInvoke.getCursorPos()
printfn "Current Pos is %A" currentPt
currentPt
|> Option.bind(fun pt ->
    PInvoke.windowFromPoint pt
)
|> Option.map(fun hWnd ->
    printfn "Current hWnd is %A" hWnd
    let wi = getWindowInfo hWnd
    printfn "CurrentWindowInfo is %A" wi
    wi.Dump()
    let pid = PInvoke.getWindowThreadProcessId hWnd
    printfn "With pId = %A" pid
    hWnd
)
|> Option.iter(fun hWnd ->
    let text =
        let mutable text:string = null
        let r = PInvoke.sendMessage <| PInvoke.GetText(hWnd,
                    (fun s -> 
                        printfn " got text?"
                        text <- s))
        text
    printfn "Window text:%s" text
    if Util.ReadLine<bool>("Attempt close?") then
        PInvoke.sendMessage (PInvoke.Message.Close( hWnd))
        |> ignore<nativeint>
)

Maslow

Posted 2009-08-26T18:54:28.660

Reputation: 786

0

Press Alt + Esc to send the current foreground window to the back. Keep pressing this until you come to the dialog. This will cycle through even windows not in the Alt + Tab list.

recursive

Posted 2009-08-26T18:54:28.660

Reputation: 857