How can I close a window of external application programmatically in VB.net.
I just want to close the current window without closing the whole process.
use FindWindow and SendMessage APIs
here in C#, should be trivial to convert:
using Microsoft.Win32;
[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName,string lpWindowName);
[DllImport("user32.dll")]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_CLOSE = 0xF060;
private void closeWindow()
{
// retrieve the handler of the window
int iHandle = FindWindow("Notepad", "Untitled - Notepad");
if (iHandle > 0)
{
// close the window using API
SendMessage(iHandle, WM_SYSCOMMAND, SC_CLOSE, 0);
}
}
source: http://www.codeproject.com/KB/dialog/closewindow.aspx
Related
I'm trying to make a Unity3D Application that can control the volume of another application. So far I've gotten it to work to change the volume of ALL applications, but I'm having trouble to get it to change just one, e.g. Spotify. This is on Windows 8, but I'd like it to work for Windows 7 as well.
private const int APPCOMMAND_VOLUME_UP = 0xA0000;
private const int APPCOMMAND_VOLUME_DOWN = 0x90000;
private const int WM_APPCOMMAND = 0x319;
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string strClassName, string strWindowName);
[DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
[DllImport("user32.dll")]
public static extern IntPtr GetActiveWindow();
public void DecreaseVolume()
{
IntPtr hWnd = FindWindow("SpotifyMainWindow", "Spotify");
if (hWnd == IntPtr.Zero)
return;
uint pID;
GetWindowThreadProcessId(hWnd, out pID);
Debug.Log(pID);
SendMessage(hWnd, WM_APPCOMMAND, hWnd, (IntPtr)APPCOMMAND_VOLUME_DOWN);
}
public void IncreaseVolume()
{
// same as above, but with APPCOMMAND_VOLUME_UP
}
I'm pretty sure I'm getting the correct handle (hWnd) from FindWindow() because the pID printed out matches the PID of Spotify in my Task Manager, however all this does is change the volume of everything instead of just Spotify. This is the same result as if I didn't specify a specific handle e.g.
SendMessage(IntPtr.Zero, WM_APPCOMMAND, IntPtr.Zero, (IntPtr)APPCOMMAND_VOLUME_DOWN);
or if I use GetActiveWindow().
I've never messed around with these windows api stuff until today, so any help would be very appreciated!
I have list of open Applications. To get this list i have used following code
internal static class NativeMethods
{
public static readonly Int32 GWL_STYLE = -16;
public static readonly UInt64 WS_VISIBLE = 0x10000000L;
public static readonly UInt64 WS_BORDER = 0x00800000L;
public static readonly UInt64 DESIRED_WS = WS_BORDER | WS_VISIBLE;
public delegate Boolean EnumWindowsCallback(IntPtr hwnd, Int32 lParam);
public static List<WindowWrapper> GetAllWindows()
{
List<WindowWrapper> windows = new List<WindowWrapper>();
StringBuilder buffer = new StringBuilder(100);
EnumWindows(delegate(IntPtr hwnd, Int32 lParam)
{
if ((GetWindowLongA(hwnd, GWL_STYLE) & DESIRED_WS) == DESIRED_WS)
{
GetWindowText(hwnd, buffer, buffer.Capacity);
WindowWrapper wnd = new WindowWrapper();
wnd.handle = hwnd;
wnd.title = buffer.ToString();
windows.Add(wnd);
}
return true;
}, 0);
return windows;
}
[DllImport("user32.dll")]
static extern Int32 EnumWindows(EnumWindowsCallback lpEnumFunc, Int32 lParam);
[DllImport("user32.dll")]
public static extern void GetWindowText(IntPtr hWnd, StringBuilder lpString, Int32 nMaxCount);
[DllImport("user32.dll")]
static extern UInt64 GetWindowLongA(IntPtr hWnd, Int32 nIndex);
}
public class WindowWrapper : IWin32Window
{
internal IntPtr handle;
internal String title;
public IntPtr Handle
{
get { return handle; }
}
public String Title
{
get { return title; }
}
}
to call this i used following code
foreach (var wnd in NativeMethods.GetAllWindows())
{
string caption = wnd.title;
string handle = wnd.Handle
// Add this caption and handle to list
}
Now, User will select any of the opened window from the list and my task is to read caption of the selected window, get handle of process and maximize/minimize or close window. How can I do this.
You can use findwindowbycaption to get the handle then maximize or minimize with showwindow
private const int SW_MAXIMIZE = 3;
private const int SW_MINIMIZE = 6;
// more here: http://www.pinvoke.net/default.aspx/user32.showwindow
[DllImport("user32.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
Then in your code you use this:
IntPtr hwnd = FindWindowByCaption(IntPtr.Zero, "The window title");
ShowWindow(hwnd, SW_MAXIMIZE);
Although it seems you already have the window handle by using EnumWindows in that case you would only need:
ShowWindow(windows[i].handle, SW_MAXIMIZE);
i is the index of the window.
to close the window you will use:
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DestroyWindow(IntPtr hwnd);
in the code:
DestroyWindow(hwnd) //or DestroyWindow(windows[i].handle)
this is the unmanaged version of system.windows.forms.form.close()
or you can use:
Process [] proc Process.GetProcessesByName("process name");
proc[0].Kill();
or you can use:
static uint WM_CLOSE = 0x0010;
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
in code:
PostMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
You may use native method ShowWindow with SW_MAXIMIZE, SW_MINIMIZE for ncmdShow
Take a look at http://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
private const int SW_MAXIMIZE = 3;
private const int SW_MINIMIZE = 6;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
// in your code
ShowWindow(wnd.Handle, SW_MAXIMIZE);
you can use ShowWindowAsync
private const int SW_SHOWNORMAL = 1;
private const int SW_SHOWMINIMIZED = 2;
private const int SW_SHOWMAXIMIZED = 3;
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
ShowWindowAsync(wnd.Handle, SW_SHOWMINIMIZED );
and it's better and to use
var openWindows = Process.GetProcesses().Where(process=> String.IsNullOrEmpty(process.MainWindowTitle)==false);
to get opened windows
I have test MainWindowTitle in Porcess and it helps to search on window given it's caption.
var handles = Process.GetProcesses().Where(x => x.MainWindowTitle == "Untitled - Notepad").Select(y=>y.Handle).ToList();
Im trying to create a program where I can send some process id of a process (that may be firefox, ie, notepad etc) to a method that scrolls window of the process.
I have been trying with GetScrollBarInfo and SetScrollPos which I found at pinvoke without any success. Im not sure if this is the right way or not. I started playing with GetScrollBarInfo, but it doesn't seem to work.
I tried the code found at http://www.pinvoke.net/default.aspx/user32.getscrollbarinfo
[StructLayout(LayoutKind.Sequential)]
public struct SCROLLBARINFO
{
public int cbSize;
public Rectangle rcScrollBar;
public int dxyLineButton;
public int xyThumbTop;
public int xyThumbBottom;
public int reserved;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public int[] rgstate;
}
private const uint OBJID_HSCROLL = 0xFFFFFFFA;
private const uint OBJID_VSCROLL = 0xFFFFFFFB;
private const uint OBJID_CLIENT = 0xFFFFFFFC;
private int Scroll(int ProcessID)
{
IntPtr handle = Process.GetProcessById(ProcessID).MainWindowHandle;
SCROLLBARINFO psbi = new SCROLLBARINFO();
psbi.cbSize = Marshal.SizeOf(psbi);
int nResult = GetScrollBarInfo(handle, OBJID_CLIENT, ref psbi);
if (nResult == 0)
{
int nLatError = Marshal.GetLastWin32Error();
}
}
GetLastWin32Error() returns errorcode 122 which means "The data area passed to a system call is too small", according to http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
Im not sure what I do wrong. How can I solve this?
You could send a WM_MOUSEWHEEL message to do what you want. For example, to scroll down once in a new notepad window using C++:
HWND hwnd = FindWindowEx(FindWindow(NULL, "Untitled - Notepad"), NULL, "Edit", NULL);
RECT r;
GetClientRect(hwnd, &r);
SendMessage(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(0, WHEEL_DELTA * -1), MAKELPARAM(r.right / 2, r.bottom / 2));
To adapt that to C#, you could do something such as this:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, ref Point lParam);
private void ScrollWindow(IntPtr hwnd, Point p, int scrolls = -1)
{
SendMessage(hwnd, WM_MOUSEWHEEL, (WHEEL_DELTA * scrolls) << 16, ref p);
}
Which could be used to scroll down once in a new notepad window like this:
//Imports
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
...
//Actual code
IntPtr hwnd = FindWindowEx(FindWindow(null, "Untitled - Notepad"), IntPtr.Zero, "Edit", null);
Point p = new Point(0, 0);
ScrollWindow(hwnd, p);
Some programs will require the lParam sent to be a point that's actually above the scrolled area, while others such as notepad will not.
If you're trying to scroll the window of another process, you need to, in effect, simulate clicks on the scroll bar or key presses. The cleanest way to do that is to use UI Automation, which has both .NET and native interfaces.
By asking for the scrollbar info, you're simply getting information about how the scrollbar is displayed. That's not going to give you a way to scroll the window content. You have to get the target application to scroll the content by making it think the user is operating the scrollbar.
http://forums.codeguru.com/showthread.php?446352-How-to-scroll-active-window-SendMessage&p=1686041#post1686041
Final Code
class Program
{
[DllImport("user32.dll")]
static extern bool GetGUIThreadInfo(uint idThread, ref GUITHREADINFO lpgui);
[DllImport("user32.dll")]
public static extern int SetScrollPos(IntPtr hWnd, System.Windows.Forms.Orientation nBar, int nPos, bool bRedraw);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
public struct GUITHREADINFO
{
public int cbSize;
public int flags;
public IntPtr hwndActive;
public IntPtr hwndFocus;
public IntPtr hwndCapture;
public IntPtr hwndMenuOwner;
public IntPtr hwndMoveSize;
public IntPtr hwndCaret;
public System.Drawing.Rectangle rcCaret;
}
const Int32 WM_VSCROLL = 0x0115;
const Int32 SB_LINERIGHT = 1;
static void Main(string[] args)
{
//create process in focus
Process.Start("notepad++", "Source.cpp");
Thread.Sleep(1000);
GUITHREADINFO threadInfo = new GUITHREADINFO();
threadInfo.cbSize = Marshal.SizeOf(threadInfo);
GetGUIThreadInfo(0, ref threadInfo);
SendMessage(threadInfo.hwndFocus, WM_VSCROLL, SB_LINERIGHT, 0);
//SetScrollPos not work. Change only scrollbar without scroll window
//SetScrollPos(threadInfo.hwndFocus, System.Windows.Forms.Orientation.Vertical, 10, true);
}
}
How can I prevent WPF window from minimizing when users clicks on show desktop button?
This link will help you : Get the minimize box click of a WPF window
you need to catch the event and handle it yourself.
Edit : This method will alert you once the state is changed, so it might not be the "best" solution but it could work.
Windows are not minimized when "Show Desktop" is issued. Instead the "WorkerW" and "Desktop" windows are brought to the foreground.
I ended up developing my own solution.
I scoured the internet for weeks trying to find an answer so I'm kind of proud of this one.
So what we do is use pinvoke to create a hook for the EVENT_SYSTEM_FOREGROUND window event.
This event triggers whenever the foreground window is changed.
Now what I noticed is when the "Show Desktop" command is issued, the WorkerW window class becomes foreground.
Note this WorkerW window is not the desktop and I confirmed the hwnd of this WorkerW window is not the Desktop hwnd.
So what we do is whenever the WorkerW window becomes the foreground, we set our "WPF Gadget Window" to be topmost!
Whenever a window other the WorkerW window becomes the foreground, we remove topmost from our "WPF Gadget Window".
If you want to take it a step further, you can uncomment out the part where I check if the new foreground window is also "PROGMAN", which is the Desktop window.
However, this will lead to your window becoming topmost if the user clicks their desktop on a different monitor. In my case, I did not want this behavior, but I figured some of you might.
Confirmed to work in Windows 10. Should work in older versions of Windows.
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
namespace YourNamespace
{
internal static class NativeMethods
{
[DllImport("user32.dll")]
internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, ShowDesktop.WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll")]
internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll")]
internal static extern int GetClassName(IntPtr hwnd, StringBuilder name, int count);
}
public static class ShowDesktop
{
private const uint WINEVENT_OUTOFCONTEXT = 0u;
private const uint EVENT_SYSTEM_FOREGROUND = 3u;
private const string WORKERW = "WorkerW";
private const string PROGMAN = "Progman";
public static void AddHook(Window window)
{
if (IsHooked)
{
return;
}
IsHooked = true;
_delegate = new WinEventDelegate(WinEventHook);
_hookIntPtr = NativeMethods.SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, _delegate, 0, 0, WINEVENT_OUTOFCONTEXT);
_window = window;
}
public static void RemoveHook()
{
if (!IsHooked)
{
return;
}
IsHooked = false;
NativeMethods.UnhookWinEvent(_hookIntPtr.Value);
_delegate = null;
_hookIntPtr = null;
_window = null;
}
private static string GetWindowClass(IntPtr hwnd)
{
StringBuilder _sb = new StringBuilder(32);
NativeMethods.GetClassName(hwnd, _sb, _sb.Capacity);
return _sb.ToString();
}
internal delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
private static void WinEventHook(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (eventType == EVENT_SYSTEM_FOREGROUND)
{
string _class = GetWindowClass(hwnd);
if (string.Equals(_class, WORKERW, StringComparison.Ordinal) /*|| string.Equals(_class, PROGMAN, StringComparison.Ordinal)*/ )
{
_window.Topmost = true;
}
else
{
_window.Topmost = false;
}
}
}
public static bool IsHooked { get; private set; } = false;
private static IntPtr? _hookIntPtr { get; set; }
private static WinEventDelegate _delegate { get; set; }
private static Window _window { get; set; }
}
}
You can change your window's parent to not be affected by Show Desktop. (as stated here: Window "on desktop")
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += MainWindowLoaded;
}
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
private void MainWindowLoaded(object sender, RoutedEventArgs e)
{
var hwnd = new WindowInteropHelper(this).Handle;
var ProgmanHwnd = FindWindowEx(FindWindowEx(FindWindow("Progman", "Program Manager"), IntPtr.Zero, "SHELLDLL_DefView",""), IntPtr.Zero,"SysListView32", "FolderView");
SetParent(hwnd, ProgmanHwnd);
}
}
Hopefully this is a simple one, but can anyone provide some simple c# code that will launch the currently configured screensaver?
Here is a good site showing how to work with all aspects of the screensaver. See the comments at the end for the code to start the screensaver.
http://www.codeproject.com/KB/cs/ScreenSaverControl.aspx
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
//...
private const int SC_SCREENSAVE = 0xF140;
private const int WM_SYSCOMMAND = 0x0112;
//...
public static void SetScreenSaverRunning()
{
SendMessage
(GetDesktopWindow(), WM_SYSCOMMAND, SC_SCREENSAVE, 0);
}