So there is currently a SO bounty where poster wants to hide all windows for a program. They launched the program using the Windows Script Host library (c:\Windows\SysWOW64\wshom.ocx). They use the Run method of the Shell object (another link). But despite specifying WshWindowStyle.WshHide, popups still appear. Because of the bounty I decided to investigate, but I started with C# (see bottom of page), then I translated my findings into VBA (see first listing ).
Firstly, a key reading resource is Windows Features which tells that all windows are created with CreateWindowEx but popups are create by specifying WS_POPUP and child windows are created by specifying WS_CHILD. So popups and child windows are different.
On the same page in the section Window Visibility it explains that we can set the visibility of a main window and the change will cascade down to all child windows but there is no mention of this cascade affecting popups.
The short answer to the investigation is that to hide popups it is required to call ShowOwnedPopups(hwnd,0). The VBA declaration is given here
Declare Function ShowOwnedPopups Lib "user32" Alias "ShowOwnedPopups" _
(ByVal hwnd As Long, ByVal fShow As Long) As Long
And here is some final VBA code but which depends upon a simple C# demo program called VisibilityExperiment
Option Explicit
Private Declare Function ShowOwnedPopups Lib _
"user32" (ByVal hwnd As Long, _
ByVal fShow As Long) As Long
Private Declare Function EnumWindows _
Lib "user32" ( _
ByVal lpEnumFunc As Long, _
ByVal lParam As Long) _
As Long
Private Declare Function GetWindowThreadProcessId _
Lib "user32" (ByVal hwnd As Long, lpdwprocessid As Long) As Long
Private mlPid As Long
Private mlHWnd As Variant
Private Function EnumAllWindows(ByVal hwnd As Long, ByVal lParam As Long) As Long
Dim plProcID As Long
GetWindowThreadProcessId hwnd, plProcID
If plProcID = mlPid Then
If IsEmpty(mlHWnd) Then
mlHWnd = hwnd
Debug.Print "HWnd:&" & Hex$(mlHWnd) & " PID:&" & Hex$(mlPid) & "(" & mlPid & ")"
End If
End If
EnumAllWindows = True
End Function
Private Function GetPID(ByVal sExe As String) As Long
Static oServ As Object
If oServ Is Nothing Then Set oServ = GetObject("winmgmts:\\.\root\cimv2")
Dim cProc As Object
Set cProc = oServ.ExecQuery("Select * from Win32_Process")
Dim oProc As Object
For Each oProc In cProc
If oProc.Name = sExe Then
Dim lPid As Long
GetPID = oProc.ProcessID
End If
Next
End Function
Private Sub Test()
'* Tools->References "Windows Script Host library"
Dim wsh As IWshRuntimeLibrary.WshShell
Set wsh = New IWshRuntimeLibrary.WshShell
Dim lWinStyle As WshWindowStyle
lWinStyle = WshNormalFocus
Dim sExe As String
sExe = "VisibilityExperiment.exe"
Dim sExeFullPath As String
sExeFullPath = Environ$("USERPROFILE") & "\source\repos\VisibilityExperiment\VisibilityExperiment\bin\Debug\" & sExe
Dim x As Long
x = wsh.Run(sExeFullPath, lWinStyle, False)
mlPid = GetPID(sExe)
mlHWnd = Empty
Call EnumWindows(AddressOf EnumAllWindows, 0)
Stop
Call ShowOwnedPopups(mlHWnd, 0) '* o to hide, 1 to show
End Sub
To repeat, to hide popups one must call ShowOwnedPopups(). Sadly, I cannot see around this restriction. Even if we tried to use the Windows API directly to spawn the process there is nothing in the STARTUPINFO structure (Windows) which looks like it will help, there is nothing to specify the visibility of popups.
Here is the source code of the form to the C# program VisibilityExperiment.exe, it is a normal Windows Forms application and I have added a single button called button1,
using System;
using System.Windows.Forms;
namespace VisibilityExperiment
{
public partial class VisibilityExperiment : Form
{
public VisibilityExperiment()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("message", "caption", MessageBoxButtons.YesNo);
}
}
}
And here is my experiment program call VisibilityExperiment2.exe . It is again a simple WinForms Application. You run VisibilityExperiment manually and you can see the effect of calling different Windows API functions. You will see that the message box does will not be hidden unless you use ShowOwnedPopups.
Here is the form layout
.You will have to rename the controls btnGetMainWindowHandle, btnShow, btnHide, grpShowHide, chkShowOwnedPopups, chkShowWindowAsync, chkSetWindowPos for the following form code to work...
using System;
using System.Diagnostics;
using System.Dynamic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace VisibilityExperiment2
{
public partial class ShowerHider : Form
{
IntPtr mainWindowHandle;
[DllImport("user32.dll")]
static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
static extern bool ShowOwnedPopups(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
#region Constants
enum SetWindowPosFlags
{
NOSIZE = 0x0001,
NOMOVE = 0x0002,
NOZORDER = 0x0004,
NOREDRAW = 0x0008,
NOACTIVATE = 0x0010,
DRAWFRAME = 0x0020,
FRAMECHANGED = 0x0020,
SHOWWINDOW = 0x0040,
HIDEWINDOW = 0x0080,
NOCOPYBITS = 0x0100,
NOOWNERZORDER = 0x0200,
NOREPOSITION = 0x0200,
NOSENDCHANGING = 0x0400,
DEFERERASE = 0x2000,
ASYNCWINDOWPOS = 0x4000
};
static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
static readonly IntPtr HWND_TOP = new IntPtr(0);
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
private const int SW_HIDE = 0;
private const int SW_SHOWNORMAL = 1;
private const int SW_SHOW = 5;
#endregion Constants
public ShowerHider()
{
InitializeComponent();
}
private ExpandoObject CheckForPID()
{
dynamic retVal = new ExpandoObject();
Process[] processes = Process.GetProcessesByName("VisibilityExperiment");
if (processes.Length==0)
{
retVal.pid = -1;
} else if (processes.Length == 1) {
Process proc = processes[0];
retVal.pid = proc.Id;
retVal.hWnd = proc.MainWindowHandle;
} else
{
retVal.pid = -2;
}
return retVal;
}
private void btnGetMainWindowHandle_Click(object sender, EventArgs e)
{
dynamic retVal = CheckForPID();
if (retVal.pid>=0)
{
mainWindowHandle = retVal.hWnd;
this.textBox1.Text = retVal.hWnd.ToString("X8");
this.grpShowHide.Enabled = true;
}
}
private void btnShow_Click(object sender, EventArgs e)
{
if (this.chkSetWindowPos.Checked == true)
{
SetWindowPos(mainWindowHandle, IntPtr.Zero, 0, 0, 0, 0,SetWindowPosFlags.SHOWWINDOW |
SetWindowPosFlags.NOMOVE | SetWindowPosFlags.NOSIZE);
}
if (this.chkShowWindowAsync.Checked==true)
{
ShowWindowAsync(mainWindowHandle, SW_SHOW);
//* Need to call this twice of the first time
//* TODO Eastablish first time versus subsequent time logic
ShowWindowAsync(mainWindowHandle, SW_SHOW);
}
if (this.chkShowOwnedPopups.Checked == true)
ShowOwnedPopups(mainWindowHandle, 1);
}
private void btnHide_Click(object sender, EventArgs e)
{
if (this.chkSetWindowPos.Checked == true)
{
SetWindowPos(mainWindowHandle, IntPtr.Zero, 0, 0, 0, 0,SetWindowPosFlags.HIDEWINDOW |
SetWindowPosFlags.NOMOVE | SetWindowPosFlags.NOSIZE);
}
if (this.chkShowWindowAsync.Checked == true)
{
ShowWindowAsync(mainWindowHandle, SW_HIDE);
//* Need to call this twice of the first time
//* TODO Eastablish first time versus subsequent time logic
ShowWindowAsync(mainWindowHandle, SW_HIDE);
}
if (this.chkShowOwnedPopups.Checked == true)
ShowOwnedPopups(mainWindowHandle, 0);
}
private void chkShowWindowAsync_CheckedChanged(object sender, EventArgs e)
{
}
}
}
No comments:
Post a Comment