Python 将程序安装到程序文件时使用pywin32

Python 将程序安装到程序文件时使用pywin32,python,py2exe,pywin32,program-files,Python,Py2exe,Pywin32,Program Files,我正在尝试为一个程序创建一个安装程序,该程序使用pywin32绑定编辑一些excel电子表格。我使用py2exe创建了一个可执行文件,当在桌面上的文件夹中运行该可执行文件时,一切都正常。但是,我希望能够分发一个安装程序文件,将程序安装到任何系统上的C:\program Files\或等效文件夹中。不过,我也成功地做到了这一点,当使用pywin32绑定时,它们会在工作目录所在的任何位置创建临时文件 这是一个很大的问题,因为较新版本的windows使其成为只有管理员才有权限写入这些目录。因此,当从这

我正在尝试为一个程序创建一个安装程序,该程序使用pywin32绑定编辑一些excel电子表格。我使用py2exe创建了一个可执行文件,当在桌面上的文件夹中运行该可执行文件时,一切都正常。但是,我希望能够分发一个安装程序文件,将程序安装到任何系统上的C:\program Files\或等效文件夹中。不过,我也成功地做到了这一点,当使用pywin32绑定时,它们会在工作目录所在的任何位置创建临时文件

这是一个很大的问题,因为较新版本的windows使其成为只有管理员才有权限写入这些目录。因此,当从这些目录运行应用程序时,它会失败,并出现以下错误:

WindowsError: [Error 5] Access is denied: 'C:\\Program Files (x86)\\DataPlotter\\.\\win32com\\gen_py\
\00020813-0000-0000-C000-000000000046x0x1x6'
将应用程序更改为使用管理员权限运行是一个糟糕的解决方案,因为它可能会引入漏洞


有人知道这个问题的修复方法吗?或者知道如何更改pywin32绑定用作临时文件位置的位置。

这是一个愚蠢的黑客解决方案,但是可以通过在pywin32上进行狭条排印来避免这个问题

通过将当前工作目录切换到保证安全写入的目录(如temp目录),可以避免该问题

#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)
注意:末尾的开关返回到原始工作目录是不必要的,但如果您需要它,它将工作(正如我所做的)

martineau建议在下面使用ContextManager来实现这一点:

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")

我在对您的答案的评论中描述了如何使用
contextlib.contextmanager
decorator将代码转换为上下文管理器。如果您希望即使在发生未处理的异常时也恢复上一个当前目录,则这种方法稍微过于简化了。要实现这一点,还需要在
收益率
(见下文)周围添加
try
finally
子句

此外,我认为将其作为一个独立函数而不是某个类的方法,并将要切换到的目录作为参数传递给它会更好——这两者都使它更通用,更易于重用。我之所以称它为
pushd()
,是因为它和Windows和Unix shell命令同名

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")

执行
yield cwd
而不是不执行任何操作也可能很有用,这将允许任何可选的
as
变量接收一个值,该值指示前一个当前工作目录,以便在块内进行可能的引用。

您可以通过设置temp和TMP环境变量来更改temp文件的位置正在尝试写入'C:\Program Files(x86)\{Program Name}\.\win32com\gen\u py\。我看了一下源代码,据我所知,它只是在当前工作目录所在的位置创建了一个文件夹,而不是写入任何临时文件。(如果我在调用pywin32之前尝试更改工作目录,我将尝试并报告。)它确实应该使用受环境变量值影响的win32函数。由于情况并非如此,您的变通方法可能是唯一的选择(如果可行的话)。您可以将cwd更改为TEMP环境变量的值`我知道,但不管是谁写的,都认为他们知道得更多。不管怎么说,解决办法是有效的。我会把它写下来并作为答案提交。很高兴听到/知道。实际上,您应该自己调用
GetTempPath
,并将当前工作目录设置为返回的值。您应该创建一个上下文管理器来保存cwd,设置一个新的,并在
末尾使用
恢复原始目录。使用
contextlib.contextmanager
decorator将使创建一个非常简单的目录变得非常容易——只需在更改到新目录后
yield
,然后在下一条语句中恢复原始目录。您不认为对于这样一个简单的操作来说这有点过分吗?除了一行或两行代码之外,还有什么额外的好处呢?如果您使用decorator创建代码,就不会占用您已经拥有的更多代码。除了将您所做的全部工作编写成代码之外,最大的区别在于,即使在块中的某个地方发生异常,您也可以自动恢复工作目录。它也更容易被你自己以及在你发布的答案中看到它的其他人重用。这看起来很好…除了一件重要的事情——看到我刚才添加的答案。我明白你的意思,但我没有看到代码中任何地方需要将此代码与其他目录重用。(至少目前)我看到try-catch块是多么有用。文档有误导性,似乎暗示上下文管理器的退出功能在退出之前会传递异常,从而允许它执行。@Aaron S:我认为将临时文件目录设置为默认目录没有问题。至于文档,实际上关于装饰者的部分需要非常仔细地解析才能完全掌握。阅读相关的PEP 0343并查看一些示例也会有所帮助;-)。我假设这样做是为了让生成器函数能够完全按照自己的意愿处理任何异常——或者没有异常。