Open files in linux file manager (with double click) using host application

Discussions related to using VirtualBox on Linux hosts.
andyp73
Volunteer
Posts: 1631
Joined: 25. May 2010, 23:48
Primary OS: Mac OS X other
VBox Version: PUEL
Guest OSses: Assorted Linux, Windows Server 2012, DOS, Windows 10, BIOS/UEFI emulation

Re: Open files in linux file manager (with double click) using host application

Post by andyp73 »

I would try something like this:

Code: Select all

#!/bin/bash

#
# Check that we have got the correct number of parameters.
#
if [ "$#" -ne 1 ]; then
	echo "Remote Winword script requires one parameter to be passed!"
	exit
fi

#
# Check if the file exists in the host file system incase we are
# called from a terminal.
#
if [ ! -f "$1" ]; then
	echo "File not found in host file system!"
	exit
fi

#
# Get the filename part from the full path given.
#
FILE=$(basename $1)

#
# Attempt to start the file in the guest.
#
VBoxManage guestcontrol "Win07" run \
     --exe "C:\\Program Files (x86)\\Microsoft Office\root\\Office16\\WINWORD.EXE" \
     --username sbnwl \
     --password myPassword \
     -- WINWORD/arg0 "y:\$FILE"
Put it into a file somewhere like /usr/share/bin, make it executable with chmod +x and associate .docx file extensions with it. You can test it in a terminal window by calling it with a single filename parameter.

NOTE: I haven't actually tested this so it may need a bit of tweaking.

-Andy.
My crystal ball is currently broken. If you want assistance you are going to have to give me all of the necessary information.
Please don't ask me to do your homework for you, I have more than enough of my own things to do.
sbnwl
Posts: 11
Joined: 13. Feb 2019, 07:52

Re: Open files in linux file manager (with double click) using host application

Post by sbnwl »

@andpy73

Thank you for this.

Saved your shell-commands in a file named runWord and made it executable.

Now tried to test this script:

Code: Select all

./runWord ~/Documents/myfile.docx
After this MS Word is launched and it complains that "The file Y:\$FILE not found"

If this script works then I can mention this in a desktop configuration file with a line

Code: Select all

Exec=runWord %u
and I am convinced it should work.
andyp73
Volunteer
Posts: 1631
Joined: 25. May 2010, 23:48
Primary OS: Mac OS X other
VBox Version: PUEL
Guest OSses: Assorted Linux, Windows Server 2012, DOS, Windows 10, BIOS/UEFI emulation

Re: Open files in linux file manager (with double click) using host application

Post by andyp73 »

Try changing it to "y:\${FILE}" and see if that fixes it.

-Andy.
My crystal ball is currently broken. If you want assistance you are going to have to give me all of the necessary information.
Please don't ask me to do your homework for you, I have more than enough of my own things to do.
sbnwl
Posts: 11
Joined: 13. Feb 2019, 07:52

Re: Open files in linux file manager (with double click) using host application

Post by sbnwl »

Code: Select all

sbnwl@sys-pc:~/Documents$ ./runWord ~/Documents/myfile.docx
./runWord: line 28: Y:$FILE: command not found
Although MS Word still launches without opening any document.

Clearly, this is not picking up the value of $FILE since this is not in Windows path syntax.
Now I am thinking:
1. First, we need to write a command to convert LINUX_PATH of the clicked file into equivalent WINDOWS_PATH string.
2. Use the sed command within this script which will dynamically update the argument in the last line where the WINDOWS_PATH is required.
sbnwl
Posts: 11
Joined: 13. Feb 2019, 07:52

Re: Open files in linux file manager (with double click) using host application

Post by sbnwl »

Hi andpy73

I reworked on your script and it works flawlessly now, except for files in sub-directories. :) :D

Code: Select all

#!/bin/bash

# THIS PROGRAM (runWord) OPENS *.docx FILES ON LINUX HOST, WITH MS Word APPLICATION 
# INSTALLED ON A WINDOWS GUEST (IN VirtualBox).
# PLACE THIS FILE IN THE DIRECTORY /home/sbnwl/.local/share/applications/myScripts/
# FIRST CHECK FOLLOWING REQUIREMENTS ARE MET:
# 1. THE FILES ARE EITHER IN Documents OR Downloads DIRECTORIES 
#    OF THE HOST FILESYSTEM (SUB-DIRECTORIES currently not supported)
# 2. THE Documents DIRECTORY IS MOUNTED AS Y:\ IN THE WINDOWS GUEST
# 3. THE Downloads DIRECTORY IS MOUNTED AS Z:\ IN THE WINDOWS GUEST
# YOU ALSO NEED TO CREATE A DESKTOP ENTRY FILE WITH FOLLOWING CONTENTS, IN THE
# /home/sbnwl/.local/share/applications PATH:
#
#[Desktop Entry]
#Version=1.0
#Encoding=UTF-8
#Type=Application
#Name=Word 365
#NoDsiplay=true
#Exec=/home/sbnwl/.local/share/applications/myScripts/runWord %u
#Name[en]=Word 365 ProPlus
#Icon=/usr/share/icons/MSWord.png
#

#
# THE CURRENT LIMITATION FOR USAGE OF THIS SCRIPT IS THAT THE FILES TO BE 
# OPENED SHOULD BE IN ~/Documents and ~/Downloads PATHS AND NOT IN ANY 
# SUB-DIRECTORY.
#

#
##--CODE DEVELOPERS/CONTRIBUTORS -- andpy73, sbnwl
##-- andpy73 https://forums.virtualbox.org/memberlist.php?mode=viewprofile&u=50134
##-- sbnwl https://forums.virtualbox.org/memberlist.php?mode=viewprofile&u=116543
##-- Original post at https://forums.virtualbox.org/viewtopic.php?f=7&t=91799
#

clear

#
# Check that we have got the correct number of parameters.
#
if [ "$#" -ne 1 ]; then
   echo "The runWord script requires one parameter to be passed!"
   exit
fi

#
# Check if the file exists in the host file system in case we are
# called from a terminal.
#
if [ ! -f "$1" ]; then
   echo "File not found in the host file system!"
   echo 'Make sure the file' $1 'exists!'
   exit
fi

#
# Get the filename part from the full path given.
#
FILE=$(basename "$1")

echo 'Opening' $FILE ' ...'
echo 'Full path of the file is:' $1

#
# Make a new executable file having WINDOWS_PATH as argument at the end of 
# VBoxManage command and write the command sting in it.
#
mkdir ~/.tmp
cd ~/.tmp
rm tmpfile
touch tmpfile
chmod +x tmpfile
printf 'VBoxManage guestcontrol "Win10" run --exe "C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\WINWORD.EXE" --username sbnwl --password myPassword -- WINWORD/arg0 ' >> tmpfile
if [[ $1 == *"/Documents/"* ]]; then
   echo "The file is in Documents folder, i.e., in Y:\ of Windows host!"
   echo '"'Y:\\$FILE'"' >> tmpfile
fi
if [[ $1 == *"/Downloads/"* ]]; then
   echo "The file is in Downloads folder, i.e., in Z:\ of Windows host!"
   echo '"'Z:\\$FILE'"' >> tmpfile
fi

#
# Show the contents of tmpfile (for only debugging purpose)
#
echo '-------------------------------------------------------------------------'
echo 'This is the content of tmpfile for debugging purpose:'
echo ' '
cat tmpfile
echo ' '
echo '-------------------------------------------------------------------------'

#
# Run the commands in tmpfile for opening the double-clicked file!
#
./tmpfile &
rm tmpfile
3Pilif
Posts: 1
Joined: 12. Nov 2019, 14:32

Re: Open files in linux file manager (with double click) using host application

Post by 3Pilif »

I updated the script to include sub-directories and to start the VM if it is not running.

Code: Select all

#!/bin/bash

##--CODE DEVELOPERS/CONTRIBUTORS -- andpy73, sbnwl, 3Pilif
#

clear

#
# Check that we have got the correct number of parameters.
#
if [ "$#" -ne 1 ]; then
   echo "The runWord script requires one parameter to be passed!"
   exit
fi

#
# Check if the file exists in the host file system in case we are
# called from a terminal.
#
if [ ! -f "$1" ]; then
   echo "File not found in the host file system!"
   echo 'Make sure the file' $1 'exists!'
   exit
fi

#
# Get the filename part from the full path given.
#
#FILE=$(basename "$1")

#echo 'Opening' $FILE ' ...'
echo 'Full path of the file is:' $1

#start Vm
# CHANGE "Win10" to your VM name
if !( vboxmanage showvminfo "Win10" | grep -c "running (since" ); then
    vboxmanage startvm "Win10" --type separate > /dev/null
    sleep 30 # change this if you Windows is loading faster 
    #echo "start"
fi


#
# Make a new executable file having WINDOWS_PATH as argument at the end of
# VBoxManage command and write the command sting in it.
#
#mkdir ~/.tmp
cd ~/.tmp
#rm tmpfile
touch tmpfile
chmod +x tmpfile
# CHANGE XXXX to your windows username and passwort
printf 'VBoxManage guestcontrol "Win10" run --exe "C:\\Program Files\\Microsoft Office\\root\\Office16\\WINWORD.EXE" --username XXXX --password XXXXX -- WINWORD/arg0 ' >> tmpfile
if [[ $1 == *"/Documents/"* ]]; then
   #echo "The file is in Documents folder, i.e., in F:\ of Windows host!"
   FILE=$(echo $1 | awk -F 'Documents/' '{print $2}' | sed 's/\//\\/g')
   echo '"'F:\\$FILE'"' >> tmpfile
fi
if [[ $1 == *"/Downloads/"* ]]; then
   #echo "The file is in Downloads folder, i.e., in Z:\ of Windows host!"
   # from basename get the path after the folder and make it with windows slash
   FILE=$(echo $1 | awk -F 'Downloads/' '{print $2}' | sed 's/\//\\/g')
   echo '"'Z:\\$FILE'"' >> tmpfile
fi

#
# Show the contents of tmpfile (for only debugging purpose)
#
echo '-------------------------------------------------------------------------'
echo 'This is the content of tmpfile for debugging purpose:'
echo ' '
cat tmpfile
echo ' '
echo '-------------------------------------------------------------------------'

#
# Run the commands in tmpfile for opening the double-clicked file!
#
./tmpfile &
rm tmpfile

TVG
Posts: 34
Joined: 8. Aug 2022, 16:16

Re: Open files in linux file manager (with double click) using host application

Post by TVG »

Hi everyone,

Thank you so much for the useful script.

I've made a few changes so it opens a new file if no argument is provided. I've duplicated the script for each Microsoft Office application and have added the associated desktop applications to my Dock.
Office apps in Linux Dock
Office apps in Linux Dock
office_apps_in_linux_dock.png (29.38 KiB) Viewed 3964 times
When I click on the Word icon for example, it opens a new Word file as expected. However, if I click again on the Word icon, it creates a new one. If a Word document is opened, I would like clicking on the icon to focus on the opened file and not to create a new one. Would that be possible? I also would like to know if it would be possible to show if there are (and how many) active files with dots below the app icon in the Dock?

Note 1: Using wmctrl command, I'm able to focus on the window of the VM after creating a Word file (see the last lines of the scripts).

Note 2: It is possible to use mouse macros in Windows to get something working but that's not optimal.

Many thanks for your help!

Code: Select all

#!/bin/bash

# -- CODE DEVELOPERS/CONTRIBUTORS -- andpy73, sbnwl, 3Pilif, TVG

clear

# Start VM
# Change "Windows 10 VM" to your VM name
if !( vboxmanage showvminfo "Windows 10 VM" | grep -c "running (since" ); then
    vboxmanage startvm "Windows 10 VM" --type separate > /dev/null
    sleep 25 # change this if you Windows is loading faster
fi

# Make a new executable file having WINDOWS_PATH as argument at the end of VBoxManage command 
# and write the command sting in it.
if [ ! -d ~/.tmp ]; then
  mkdir ~/.tmp
fi
cd ~/.tmp
touch tmpfile
chmod +x tmpfile

# Command line without file specified to open blank file
# CHANGE XXXX to your windows username and password
printf 'VBoxManage guestcontrol "Windows 10 VM" run --exe "C:\\Program Files\\Microsoft Office\\root\\Office16\\WINWORD.EXE" --username XXXX --password XXXX ' >> tmpfile
# If file specified and if exist, open it, else open blank file
if [ -f "$1" ]; then
   # From basename get the path after the folder and make it with windows slash replace "/home/XXXX/" by "Z:\\"
    FILE=$(echo $1 | awk -F '/home/XXXX/' '{print $2}' | sed 's/\//\\/g')
    echo ' -- WINWORD/arg0 "'Z:\\$FILE'"' >> tmpfile
fi

# Show the contents of tmpfile (for only debugging purpose)
echo '-------------------------------------------------------------------------'
echo 'This is the content of tmpfile for debugging purpose:'
echo ' '
cat tmpfile
echo ' '
echo '-------------------------------------------------------------------------'

# Run the commands in tmpfile for opening the double-clicked file!
./tmpfile &
rm tmpfile

# Get window id of the VM window and un-mininized the window (i.e. focus on it)
window_id=$(wmctrl -l | grep "Windows 10 VM" | awk '{print $1;}' | head -1)
sleep 1 # if no sleep, do not open the app
wmctrl -ia $window_id
TVG
Posts: 34
Joined: 8. Aug 2022, 16:16

Re: Open files in linux file manager (with double click) using host application

Post by TVG »

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" 
}
sbnwl
Posts: 11
Joined: 13. Feb 2019, 07:52

Re: Open files in linux file manager (with double click) using host application

Post by sbnwl »

Hey TVG,
Maybe you read this some day. Could you explain why did you write ps1 script and use executable file runWord.exe in your main script?
TVG
Posts: 34
Joined: 8. Aug 2022, 16:16

Re: Open files in linux file manager (with double click) using host application

Post by TVG »

Hi @sbnwl,

I mainly copied a PS1 script from the web page I mentioned in my previous post. Then, in order to call it with the

Code: Select all

VBoxManage guestcontrol $MY_VM_NAME run --exe
command, I converted it into an executable file. I'm not an expert of any of these languages. There is most certainly a more elegant way to do that. Any help is welcomed! :D
Post Reply