Author Topic: Universal Show/Minimize VoiceAttack Window v1.4.1  (Read 9898 times)

Exergist

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 405
  • Ride the lightning
Universal Show/Minimize VoiceAttack Window v1.4.1
« on: March 26, 2018, 11:31:54 AM »
Updated to v1.4.1 on 10/24/21
  • Executing the 'Toggle VoiceAttack Window State' function by itself is disallowed and will result in an error message.
  • Executing the '[Minimize;Show] VoiceAttack' command without using your voice (i.e., triggered via mouse, keyboard, gamepad, etc.) is disallowed and will result in an error message.


Short Summary
This is a profile containing a C# inline function that allows you to show, minimize, or maximize the VoiceAttack window regardless of the Minimize to Tray option. Explanation, inline highlights, and the actual profile are provided below. This profile requires VoiceAttack version 1.8.7.4 (or later).



Longer Summary
I like to keep VoiceAttack minimized to the system tray when I don't need to view the event log. However a while back I noticed that the native VoiceAttack commands for showing, hiding, minimizing, maximizing, etc. VoiceAttack's window do not work when the window starts out hidden (which happens to be its state when minimized to the system tray). Now that we can retrieve VoiceAttack's window handle and execute native 'restore' and 'minimize' window actions (via inline function), the stage is set to manipulate how VoiceAttack's window is shown.

Everything needed to perform the window actions is included in the attached profile. The functionality works with VoiceAttack v1.8.7.4 and later. Virtually all the VoiceAttack and C# code has comments so you can follow along with the processing. The bulk of the work is handled by the inline function contained within the Toggle VoiceAttack Window State command (function) outlined here:

Show/Minimize VoiceAttack Via Handle
Code: [Select]
using System;
using System.Drawing;
using System.Runtime.InteropServices;

public class VAInline
{
public void main()
{
string WindowAction = VA.GetText("~~VAWindowAction").ToLower(); // Retrieve requested window action
if (WindowAction != "show" && WindowAction != "minimize") // Check if inputted WindowAction is valid
{
VA.WriteToLog("Inputted window action '" + WindowAction + "' is not valid (options are 'show' or 'minimize')", "red"); // Output info to event log
VA.SetText("~~WindowChangeResult", "Window action canceled"); // Send data in WindowResult back to VoiceAttack as text variable ~~WindowChangeResult
return; // Return from inline function
}
IntPtr vaHandle = VA.MainWindowHandle; // Retrieve VoiceAttack window handle
IntPtr ActiveWindowHandle = GetForegroundWindow(); // Retrieve handle of active window
string StartingWindowPlacement = GetPlacement(vaHandle).showCmd.ToString(); // Get the current (starting) window placement and store it as a string

switch (StartingWindowPlacement) // Switch statement based on StartingWindowPlacement
{
case "Hide":
VA.WriteToLog("Unexpected window state condition ('Hide'). Please contact profile author.", "red"); // Output info to VA event log
break; // Break out of switch
case "Normal":
if (WindowAction == "show") // Check if requested action is to "Show" the window
{
SetForegroundWindow(vaHandle); // Bring the window corresponding to vaHandle to the foreground
if (ActiveWindowHandle == vaHandle) // Check if the active window's handle equals the handle of interest
ShowWindow(vaHandle, ShowWindowCommands.Maximize); // Maximize the window corresponding to vaHandle
else
VA.Utility.RestoreUI(); // Restore the window corresponding to vaHandle
}
else
VA.Utility.MinimizeUI(); // Minimize the window corresponding to vaHandle
break; // Break out of switch
case "Minimized":
if (WindowAction == "show") // Check if requested action is to "Show" the window
{
VA.Utility.RestoreUI(); // Restore the window corresponding to vaHandle
SetForegroundWindow(vaHandle); // Bring the window corresponding to vaHandle to the foreground
}
break; // Break out of switch
case "Maximized":
if (WindowAction == "show") // Check if requested action is to "Show" the window
{
if (ActiveWindowHandle == vaHandle) // Check if the active window's handle equals the handle of interest
ShowWindow(vaHandle, ShowWindowCommands.ShowNormal); // Normalize the window corresponding to vaHandle
else
ShowWindow(vaHandle, ShowWindowCommands.Maximize); // Maximize the window corresponding to vaHandle
SetForegroundWindow(vaHandle); // Bring the window corresponding to vaHandle to the foreground
}
else
VA.Utility.MinimizeUI(); // Minimize the window corresponding to vaHandle
break; // Break out of switch
default:
VA.WriteToLog("Unexpected input and state combination. Please contact profile author.", "red"); // Output info to VA event log
break; // Break out of switch
}

string EndingWindowPlacement = GetPlacement(vaHandle).showCmd.ToString(); // Get the current (ending) window placement and store it as a string
string WindowResult = "Window State Change: "; // Initialize string for storing result of window action

if (StartingWindowPlacement == EndingWindowPlacement) // Check if the window placement did not change
{
if (ActiveWindowHandle == vaHandle || EndingWindowPlacement == "Minimized") // Check if the active window is the window of interest OR the EndingWindowPlacement is "Minimized"
WindowResult = "Window already " + EndingWindowPlacement; // Update WindowResult with relevant information
else
WindowResult = "Window activated and shown as " + EndingWindowPlacement; // Update WindowResult with relevant information
}
else if ((WindowAction == "show" && (EndingWindowPlacement == "Normal" || EndingWindowPlacement == "Maximized")) || (WindowAction == "minimize" && (EndingWindowPlacement == "Hide" || EndingWindowPlacement == "Minimized"))) // Check if requested window action was successfully completed
WindowResult += StartingWindowPlacement + " ==> " + EndingWindowPlacement; // Update WindowResult with relevant information
else
WindowResult = "Window state change failed"; // Update WindowResult with relevant information

VA.SetText("~~WindowChangeResult", WindowResult); // Send data in WindowResult back to VoiceAttack as text variable ~~WindowChangeResult
vaHandle = IntPtr.Zero; // Set vaHandle to IntPtr.Zero
}

#region Get Window State
private static WINDOWPLACEMENT GetPlacement(IntPtr hwnd)
{
WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
placement.length = Marshal.SizeOf(placement);
GetWindowPlacement(hwnd, ref placement);
return placement;
}

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);

[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();

[Serializable]
[StructLayout(LayoutKind.Sequential)]
private struct WINDOWPLACEMENT
{
public int length;
public int flags;
public ShowMyWindowCommands showCmd;
public Point ptMinPosition;
public Point ptMaxPosition;
public Rectangle rcNormalPosition;
}

private enum ShowMyWindowCommands : int
{
Hide = 0,
Normal = 1,
Minimized = 2,
Maximized = 3,
}
#endregion

#region Set Window State
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr handle);

[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);

// Enumeration of the different ways of showing a window using ShowWindow
private enum ShowWindowCommands : uint
{
/// <summary>Hides the window and activates another window.</summary>
/// <remarks>See SW_HIDE</remarks>
Hide = 0,
/// <summary>Activates and displays a window. If the window is minimized
/// or maximized, the system restores it to its original size and
/// position. An application should specify this flag when displaying
/// the window for the first time.</summary>
/// <remarks>See SW_SHOWNORMAL</remarks>
ShowNormal = 1,
/// <summary>Activates the window and displays it as a minimized window.</summary>
/// <remarks>See SW_SHOWMINIMIZED</remarks>
ShowMinimized = 2,
/// <summary>Activates the window and displays it as a maximized window.</summary>
/// <remarks>See SW_SHOWMAXIMIZED</remarks>
ShowMaximized = 3,
/// <summary>Maximizes the specified window.</summary>
/// <remarks>See SW_MAXIMIZE</remarks>
Maximize = 3,
/// <summary>Displays a window in its most recent size and position.
/// This value is similar to "ShowNormal", except the window is not
/// actived.</summary>
/// <remarks>See SW_SHOWNOACTIVATE</remarks>
ShowNormalNoActivate = 4,
/// <summary>Activates the window and displays it in its current size
/// and position.</summary>
/// <remarks>See SW_SHOW</remarks>
Show = 5,
/// <summary>Minimizes the specified window and activates the next
/// top-level window in the Z order.</summary>
/// <remarks>See SW_MINIMIZE</remarks>
Minimize = 6,
/// <summary>Displays the window as a minimized window. This value is
/// similar to "ShowMinimized", except the window is not activated.</summary>
/// <remarks>See SW_SHOWMINNOACTIVE</remarks>
ShowMinNoActivate = 7,
/// <summary>Displays the window in its current size and position. This
/// value is similar to "Show", except the window is not activated.</summary>
/// <remarks>See SW_SHOWNA</remarks>
ShowNoActivate = 8,
/// <summary>Activates and displays the window. If the window is
/// minimized or maximized, the system restores it to its original size
/// and position. An application should specify this flag when restoring
/// a minimized window.</summary>
/// <remarks>See SW_RESTORE</remarks>
Restore = 9,
/// <summary>Sets the show state based on the SW_ value specified in the
/// STARTUPINFO structure passed to the CreateProcess function by the
/// program that started the application.</summary>
/// <remarks>See SW_SHOWDEFAULT</remarks>
ShowDefault = 10,
/// <summary>Windows 2000/XP: Minimizes a window, even if the thread
/// that owns the window is hung. This flag should only be used when
/// minimizing windows from a different thread.</summary>
/// <remarks>See SW_FORCEMINIMIZE</remarks>
ForceMinimized = 11
}
#endregion
}

// References:
// https://stackoverflow.com/questions/8210095/c-sharp-show-hidden-window
// https://stackoverflow.com/questions/11065026/get-window-state-of-another-process
// https://stackoverflow.com/questions/115868/how-do-i-get-the-title-of-the-current-active-window-using-c

When VoiceAttack is the focused application you can switch back and forth between 'normal' and 'maximize' window states by repeatedly executing the profile's 'show' command(s). Feel free to activate the 'Show_VoiceAttack' and 'Minimize_VoiceAttack' commands via any trigger you desire (mouse, keyboard, gamepad, etc.).

PLEASE NOTE
  • The 'Toggle VoiceAttack Window State' is classified as a FUNCTION. This means that command should only be run by other commands (i.e., do not try to execute that command by itself). If you do you'll get an error message.
  • The '[Minimize;Show] VoiceAttack' command is NOT meant to be executed from the 'Edit a Profile' window. It is meant to be executed via a voice command. You'll receive an error message if that command is triggered via a method other than your voice.


Disclaimer
I have tested this profile in Windows 10 on both the 32 and 64-bit versions of VoiceAttack. Everything seems to work as expected on my end, though there could be lingering bugs. I am not responsible for anything you do to your PC. If that doesn't sit well with you, then it's time to bail out now ;)


Support This and Future Efforts
If you find this profile useful, please consider buying me a cup of coffee. Thank you for your support!
 


Cheers!  :)
« Last Edit: October 24, 2021, 08:59:07 PM by Exergist »

Exergist

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 405
  • Ride the lightning
Re: Universal Show/Minimize/Maximize VoiceAttack Window
« Reply #1 on: July 19, 2018, 11:51:11 AM »
New post to lessen the clutter. Updated to v1.2.0 which provides:
  • Toggling between "Normal" and "Maximized" window states via the "Show" command when VA is the active window
  • Better status output to the VA event log when VA's window state is "Normal" or "Maximized" but it isn't the active window
« Last Edit: July 19, 2018, 01:55:27 PM by Exergist »

Exergist

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 405
  • Ride the lightning
Re: Universal Show/Minimize/Maximize VoiceAttack Window v1.2.1
« Reply #2 on: April 16, 2019, 02:20:42 PM »
UPDATE: I modified the original post and uploaded v1.2.1, which contains some minor tidying and comment edits. Any further revisions will be placed into the original post for simplicity.

JoshuaJSlone

  • Newbie
  • *
  • Posts: 1
Re: Universal Show/Minimize/Maximize VoiceAttack Window v1.2.1
« Reply #3 on: October 24, 2020, 03:46:01 PM »
Inability to summon the VoiceAttack window from hidden is actually the problem that got me looking to the forum today. However, trying to import your profile in VoiceAttack 1.8.6, I get the following error:
Quote
Error loading profile data.
[There is an error in XML document (1488, 5).]
'(square symbol I can't copy/paste)', hexadecimal value 0x06, is an invalid character. Line 1488, position 5.

Pfeil

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4759
  • RTFM
Re: Universal Show/Minimize/Maximize VoiceAttack Window v1.2.1
« Reply #4 on: October 24, 2020, 03:53:16 PM »
"Universal Show-Minimize VoiceAttack v1.2.1.vap" causes the same error message on my machine.

You can open it in Notepad and change line 1488 from
Code: [Select]
}&#x6;update:</Context>to
Code: [Select]
}</Context>which will allow it to import (and presumably function) correctly.

Exergist

  • Global Moderator
  • Sr. Member
  • *****
  • Posts: 405
  • Ride the lightning
Re: Universal Show/Minimize VoiceAttack Window v1.4.0
« Reply #5 on: November 02, 2020, 10:43:07 AM »
Thanks for the feedback everyone! The original post has been updated with a new profile version (v1.4.0) which corrects the previously mentioned issues and improves overall performance (requires VA v1.8.7.4 or later).

Enjoy!