0

I want to start an MS-Office document from a Python-based GUI application. I'm using the command line by way of the Subprocess module to let Windows do the grunt work of mapping executable applications to files, but it fails silently if the file is already open somewhere on the network. I'd prefer to give a messagebox instead of silently failing. How can I tell this scenario without changing the file modification time?

1 Answer 1

1

I've discovered that you can attempt to rename a file to its current name. If another application has the file open, this will fail with a PermissionError exception. If it does not fail, then it is a complete no-operation. Therefore:

def start_file(file):
    if os.path.exists(file):
        try: os.rename(file, file) # This does NOTHING if the file is available, or fails.
        except PermissionError: messagebox.showwarning('File in Use', 'The file appears to be already open. Sorry about that.')
        else: subprocess.call(['start', '/B', file], shell=True)
    else: messagebox.showwarning('Missing File', 'That files does not yet exist. Sorry about that.')

Purists will point out that this is subject to at least one race condition, but from a human-interaction point of view, it does solve the problem 99.999% of the time. For the remainder, we'll blame Bill :)

Sign up to request clarification or add additional context in comments.

5 Comments

rename fails if the file is already open without delete sharing (i.e. a rename acts like an unlink). It happens that the C runtime defaults to opening files with read and write sharing, but not delete sharing, so your code detects an open handle based on a common default sharing mode. It won't work if a program opens the file with delete sharing. The target app probably doesn't share delete access, but I suspect it also wants exclusive write access. Checking the latter requires calling WinAPI CreateFile without write sharing.
The /B option of start prevents creating a new console for a target console application, which only matters when the current process is also a console application, and it's a mess if programs try to access the console simultaneously. /B also creates a new process group, but only if cmd can directly call CreateProcess instead of using ShellExecuteEx, which doesn't apply in your case. So I think you should run "file" without start. If ShellExecuteEx indirectly calls CreateProcess intsead of using DDE, then cmd gets a process handle and can wait for the exit code.
@eryksun, The target application is MS-Office, which does indeed prevent you from renaming open files. The "start /b file.xlsx" shell=True technique, used in a GUI app, is because experience shows that it's necessary for good behavior. I tested it various ways.
I've had good luck with the os.startfile(file) function call on Windows as an alternative to the subprocess call. It seems to do the right thing in a few more cases. I wish I'd found it earlier.
os.startfile calls ShellExecute. It's good for data files, but it doesn't support command-line arguments for running scripts. The standard library needs a more complete interface to ShellExecuteEx.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.