質問

I am trying to create an installer for a program that uses the pywin32 bindings to edit some excel spreadsheets. I have created an executable using py2exe and everything works when running the executable out of a folder on the desktop. However, I wish to be able to distribute a single installer file that will install the program into C:\Program Files\ or equivalent folder on whatever system. I have also succeeded in this, however, when the pywin32 bindings are used they create temporary files wherever the working directory is.

This is highly problematic as newer versions of windows have made it so only administrators have permission to write to these directories. Because of this when the app is run from these directories it fails with the error:

WindowsError: [Error 5] Access is denied: 'C:\\Program Files (x86)\\DataPlotter\\.\\win32com\\gen_py\
\00020813-0000-0000-C000-000000000046x0x1x6'

Changing the app to run with administrator permissions is a bad solution as it can introduce vulnerabilities.

Does anybody know of a fix to this problem or how to change the location that the pywin32 bindings use as a temporary file location.

役に立ちましたか?

解決 2

It is a stupid hack-y solution but this problem can be avoided by doing a runaround on pywin32.

By switching the current working directory to one that is guaranteed to be safe to write to such as the temp directory it is possible to avoid the problem.

#Save the current working directory and then switch back once
#excel has been started. This is so pywin32 does not ruin everything
#by trying to write in the current directory without the proper 
#permission. This mainly happens (for me) when the program is installed in 
#program files which requires administrator permissions to write to.

import os
import tempfile
import win32com.client

cwd = os.getcwd()
tdir =  tempfile.gettempdir()
os.chdir(tdir)
self.xl = win32com.client.gencache.EnsureDispatch("Excel.Application") 
os.chdir(cwd)

N.B. the switch at the end back to the original working directory is not necessary but it will work if you need it (as I did).

martineau suggested a more robust way of doing this below using contextmanagers:

from contextlib import contextmanager

@contextmanager
def tempManager(self):
    cwd = os.getcwd()
    tdir =  tempfile.gettempdir()
    os.chdir(tdir)
    yield
    os.chdir(cwd)

with self.tempManager():
    self.xl = win32com.client.gencache.EnsureDispatch("Excel.Application")

他のヒント

The way I described in my comment to your answer about how to turn your code into a context manager using the contextlib.contextmanager decorator was slightly oversimplified if you want the previous current directory restored even when an unhandled exception occurs. To make that happen requires also adding a try...finally clause around the yield (see below).

Also, I think it would be even better to make it a standalone function rather than a method of some class as well as pass it the directory to which to switch as an argument -- both of which make it more generic and readily reused. I've called it pushd() because of the similarities it has with the Windows and Unix shell command of the same name.

from contextlib import contextmanager
import os
import tempfile

@contextmanager
def pushd(dir=None):
    """ Context manager which saves the current working directory before
        changing either to the one passed or the default folder for temporary
        files and then restores the former after the controlled suite of
        statements have executed. This will happened even if an unhandled
        exception occurs within the block. """
    cwd = os.getcwd()
    os.chdir(dir if dir is not None else tempfile.gettempdir())
    try:
        yield
    finally:
        os.chdir(cwd)

# sample usage

with pushd():
    self.xl = win32com.client.gencache.EnsureDispatch("Excel.Application")

It might also be useful to do a yield cwd instead of nothing which would allow any optional as variable to receive a value indicating the previous current working directory for possible reference inside the block.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top