Finally, I was able to make it. Below are the codes. It works very nicely for me.
The 'runWord' script to execute by the Word desktop application (I use Menu Editor to create a desktop app, here the command is: "/my_path/runWord %F"):
Code: Select all
#!/bin/bash
# -- CODE DEVELOPERS/CONTRIBUTORS -- andpy73, sbnwl, 3Pilif, TVG
# https://forums.virtualbox.org/viewtopic.php?f=7&t=91799&start=15
clear
MY_VM_NAME="" # your VM name
MY_USERNAME="" # your Windows username
MY_PASSWORD="" # your Windows password
# If VM is not running
if !( vboxmanage showvminfo "$MY_VM_NAME" | grep -c "running (since" ); then
# Start the VM
vboxmanage startvm "$MY_VM_NAME" --type separate > /dev/null
# Sleep long enough so the VM is running before opening the app
sleep 30 # (change the number of seconds depending how fast is the loading)
fi
# Create a temporary executable file in which we will write a VBoxManage command with Windows path as argument
if [ ! -d ~/.tmp ]; then
mkdir ~/.tmp
fi
cd ~/.tmp
touch tmpfile
chmod +x tmpfile
# If a file is specified (double click on a Word file)
if [ -f "$1" ]; then
# Write in the temporary executable file the VBoxManage command (change MY_USERNAME and MY_PASSWORD by your Windows username and password)...
printf 'VBoxManage guestcontrol "'"$MY_VM_NAME"'" run --exe "C:\\Program Files\\Microsoft Office\\root\\Office16\\WINWORD.EXE" --username '$MY_USERNAME' --password '$MY_PASSWORD' ' >> tmpfile
# ...with the file as argument
FILE=$(echo $1 | awk -F '/MOUNTED_PATH/' '{print $2}' | sed 's/\//\\/g') # file with Windows path (slash replace) from "/MOUNTED_PATH/" (change MOUNTED_PATH by the path of your shared folder in the VM)
echo ' -- WINWORD/arg0 "'Z:\\$FILE'"' >> tmpfile # (change "Z:\\" with the your shared folder mounted drive in the VM)
else # else open blank file if no one opened or focus on the opened one (this is done by runWord.exe application in the VM)
printf 'VBoxManage guestcontrol "'"$MY_VM_NAME"'" run --exe "C:\\APP_PATH\\runWord.exe" --username '$MY_USERNAME' --password '$MY_PASSWORD' ' >> tmpfile # (change APP_PATH by the path of the runWord.exe application in the VM)
fi
# Show the contents of tmpfile (only for debugging purpose)
echo '-------------------------------------------------------------------------'
echo 'This is the content of tmpfile for debugging purpose:'
echo ' '
cat tmpfile
echo ' '
echo '-------------------------------------------------------------------------'
# # Run the command in tmpfile
./tmpfile &
rm tmpfile
# Focus on the VM window
window_id=$(wmctrl -l | grep "$MY_VM_NAME" | awk '{print $1;}' | head -1) # Get window id
wmctrl -ia $window_id # Un-mininized the window (i.e. focus on it)
This script call an executable file (runWord.exe) in the Windows VM. I wrote a PowerShell (runWord.ps1) script and then converted it to a Windows executable file (runWord.exe) with the software "PS1 To Exe". I mainly copied the code from here:
https://powershell.one/powershell-inter ... the-rescue. Here is the file runWord.ps1:
Code: Select all
$code = @'
using System;
using System.Runtime.InteropServices;
namespace API
{
public class FocusWindow
{
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool AttachThreadInput(IntPtr idAttach, IntPtr idAttachTo, bool fAttach);
[DllImport("User32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("User32.dll")]
private static extern IntPtr GetWindowThreadProcessId(IntPtr hwnd, IntPtr lpdwProcessId);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32")]
private static extern int BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SystemParametersInfo(uint uiAction, uint uiParam, IntPtr pvParam, uint fWinIni);
private const uint SPI_GETFOREGROUNDLOCKTIMEOUT = 0x2000;
private const uint SPI_SETFOREGROUNDLOCKTIMEOUT = 0x2001;
private const int SPIF_SENDCHANGE = 0x2;
private const int SW_HIDE = 0;
private const int SW_SHOWNORMAL = 1;
private const int SW_NORMAL = 1;
private const int SW_SHOWMINIMIZED = 2;
private const int SW_SHOWMAXIMIZED = 3;
private const int SW_MAXIMIZE = 3;
private const int SW_SHOWNOACTIVATE = 4;
private const int SW_SHOW = 5;
private const int SW_MINIMIZE = 6;
private const int SW_SHOWMINNOACTIVE = 7;
private const int SW_SHOWNA = 8;
private const int SW_RESTORE = 9;
private const int SW_SHOWDEFAULT = 10;
private const int SW_MAX = 10;
public static void Focus(IntPtr windowHandle)
{
IntPtr blockingThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
IntPtr ownThread = GetWindowThreadProcessId(windowHandle, IntPtr.Zero);
if (blockingThread == ownThread || blockingThread == IntPtr.Zero)
{
SetForegroundWindow(windowHandle);
ShowWindow(windowHandle, SW_NORMAL);
}
else
{
if (AttachThreadInput(ownThread, blockingThread, true))
{
BringWindowToTop(windowHandle);
SetForegroundWindow(windowHandle);
ShowWindow(windowHandle, SW_NORMAL);
AttachThreadInput(ownThread, blockingThread, false);
}
}
if (GetForegroundWindow() != windowHandle)
{
IntPtr Timeout = IntPtr.Zero;
SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, Timeout, 0);
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, IntPtr.Zero, SPIF_SENDCHANGE);
BringWindowToTop(windowHandle);
SetForegroundWindow(windowHandle);
ShowWindow(windowHandle, SW_NORMAL);
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, Timeout, SPIF_SENDCHANGE);
}
}
}
}
'@
# Remove -PassThru in production. It is used only to expose the added types
Add-Type -PassThru -TypeDefinition $code
# Get the main window handle for the process you want to switch to the foreground
# in this example, the first instance of WINWORD is used
$process = Get-Process -Name WINWORD -ErrorAction SilentlyContinue |
Select-Object -First 1
$mainWindowHandle = $process.MainWindowHandle
# If process exists
if ($process)
{
# Focus application window
[API.FocusWindow]::Focus($mainWindowHandle)
}else{
# Open application
Start-Process -FilePath "C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE"
}