Python Com 服务器在用 py2exe 包装时无法创建实例 - 错误对象没有属性
Python Com Server unable to create instance when wrapped with py2exe - error object has no attribute
我遇到了这个问题。我需要创建一个基于 python 的 com 服务器,将其打包为 windows exe 并将其部署到 windows。它必须有一个 "full" 接口——因为消费者需要 idispatch 和特定的接口才能运行。现在我已经创建了 com 服务器并将其 运行 置于解释器之下,它可以完美地与我挑剔的客户端一起运行。但是,当打包为 EXE - 它是本地服务器时 - 当系统尝试实例化它时(即使是从 vbs 脚本),我在日志中收到错误消息。所以这里的一切。我在 itnernet 上上下搜索,它看起来像一个导入问题,但我不知道如何导入我自己的 python 对象供本地服务器使用。
这是安装了 pywin32 扩展的 python 2.7。
首先 - 我为服务器创建的 IDL:
imtg.idl
// This file will be processed by the MIDL tool to
// produce the type library (imtg.tlb) and marshalling code.
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(4fafbb23-6a38-4613-b93b-68ea66c67043),
dual,
helpstring("IImtGroupApp Interface"),
pointer_default(unique)
]
interface IImtGroupApp : IDispatch
{
[id(1), helpstring("method EchoString")] HRESULT EchoString([in] BSTR in1, [out, retval] BSTR *vals);
[id(2), helpstring("method AddNumbers")] HRESULT AddNumbers([in] long in1, [in] long in2, [out, retval] long *vali);
};
[
uuid(d665e9d0-71a9-4e23-a1b4-abe3376d5c58),
version(1.0),
helpstring("ImtGroup 1.0 Type Library")
]
library IMTGROUPLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
importlib("msado15.dll");
[
uuid(ced66424-93fb-4307-9062-7bee76d3d8eb),
helpstring("ImtGroupApp Class")
]
coclass ImtGroupApp {
[default] interface IImtGroupApp;
};
};
接下来是 Python 代码 - 现在这有点棘手,因为当我分发它时我不想创建 .tlb - 所以我不分发 .idy - 只要确保你有 .tbl 注册。如有必要,请使用 as admin cmd 提示符。
imtg_server.py
import sys, os
import pythoncom
import win32com
import winerror
# importers check was old py2exe current uses frozen
if hasattr(sys, 'frozen'):
# we are running as py2exe-packed executable
print "is an exe"
pythoncom.frozen = 1
else:
print "not an exe"
class CImtg:
_reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER
#
# COM declarations
#
_reg_clsid_ = "{24c0e3fe-58e7-4485-87dc-9f9e823b85e1}"
_reg_desc_ = "IMTGroup Python test object"
_reg_progid_ = "ImtGroup.Test"
if hasattr(sys, 'frozen'):
# In the py2exe-packed version, specify the module.class
# to use. In the python script version, python is able
# to figure it out itself.
_reg_class_spec_ = "__main__.CImtg"
print "set reg_class_spec"
print _reg_class_spec_
###
### Link to typelib - uuid matches uuid for type library in idl file
_typelib_guid_ = '{d665e9d0-71a9-4e23-a1b4-abe3376d5c58}'
_typelib_version_ = 1, 0
_com_interfaces_ = ['IImtGroupApp']
def __init__(self):
### initialize something here if necessary
### The item below is not used in this example
self.MyProp1 = 10
def EchoString(self,in1):
return "Echoing " + in1
def AddNumbers(self, in1, in2):
return in1 + in2
def BuildTypelib():
from distutils.dep_util import newer
this_dir = os.path.dirname(__file__)
idl = os.path.abspath(os.path.join(this_dir, "imtg.idl"))
tlb=os.path.splitext(idl)[0] + '.tlb'
if os.path.isfile(idl):
# test for idl - if no idl don't create tlb assume its there
# Comment below for building exe as we will have type library
if newer(idl, tlb):
print "Compiling %s" % (idl,)
rc = os.system ('midl "%s"' % (idl,))
if rc:
raise RuntimeError("Compiling MIDL failed!")
# Can't work out how to prevent MIDL from generating the stubs.
# just nuke them
for fname in "dlldata.c imtg_i.c imtg_p.c imtg.h".split():
os.remove(os.path.join(this_dir, fname))
print "Registering %s" % (tlb,)
tli=pythoncom.LoadTypeLib(tlb)
pythoncom.RegisterTypeLib(tli,tlb)
def UnregisterTypelib():
k = CImtg
try:
pythoncom.UnRegisterTypeLib(k._typelib_guid_,
k._typelib_version_[0],
k._typelib_version_[1],
0,
pythoncom.SYS_WIN32)
print "Unregistered typelib"
except pythoncom.error, details:
if details[0]==winerror.TYPE_E_REGISTRYACCESS:
pass
else:
raise
if __name__=='__main__':
print "checking frozen"
if hasattr(sys, 'frozen'):
# running as packed executable
if '--unregister' in sys.argv or '--register' in sys.argv:
if '--unregister' in sys.argv:
# Unregister the type-libraries.
UnregisterTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
else:
# Build and register the type-libraries.
BuildTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
else:
import win32com.server
from win32com.server import localserver
print "starting the server"
localserver.main()
else:
if '--unregister' in sys.argv:
# Unregister the type-libraries.
UnregisterTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
else:
if '--register' in sys.argv:
# Build and register the type-libraries.
BuildTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
接下来是 py2exe 的设置
我不得不添加 modulefinder 的时髦导入,因为 win32com.shell 没有包含在打包的可执行文件中
setup_imtg.py
# This setup script builds a single-file Python inprocess COM server.
#
import modulefinder
import win32com, sys
for p in win32com.__path__[1:]:
modulefinder.AddPackagePath("win32com",p)
for extra in ["win32com.shell"]:
__import__(extra)
m = sys.modules[extra]
for p in m.__path__[1:]:
modulefinder.AddPackagePath(extra, p)
from distutils.core import setup
import py2exe
import sys
# If run without args, build executables, in quiet mode.
if len(sys.argv) == 1:
sys.argv.append("py2exe")
sys.argv.append("-q")
class Target:
def __init__(self, **kw):
self.__dict__.update(kw)
# for the versioninfo resources
self.name = "IMTG Server"
################################################################
# pywin32 COM pulls in a lot of stuff which we don't want or need.
CImtg = Target(
description = "Sample COM server",
# what to build. For COM servers, the module name (not the
# filename) must be specified!
modules = ["imtg_server"],
# we only want the inproc server.
)
excludes = ["pywin", "pywin.debugger", "pywin.debugger.dbgcon",
"pywin.dialogs", "pywin.dialogs.list"]
options = {
"bundle_files": 1, # create singlefile exe
"compressed": 1, # compress the library archive
"excludes": excludes,
"dll_excludes": ["w9xpopen.exe"] # we don't need this
}
setup(
options = {"py2exe": options},
zipfile = None, # append zip-archive to the executable.
com_server = [CImtg]
)
当你运行生成的EXE你可以用
注册
imtg_server --注册
但您不会看到任何输出
--注销注销
你可以用这个vbs文件来测试一下。
t.vbs
dim MD
set MD = CreateObject("ImtGroup.Test")
dim response
response = MD.EchoString("Really")
MsgBox(response)
当您 运行 时,将创建一个如下所示的 .log:
pythoncom error: ERROR: server.policy could not create an instance.
Traceback (most recent call last):
File "win32com\server\policy.pyc", line 136, in CreateInstance
File "win32com\server\policy.pyc", line 194, in _CreateInstance_
File "win32com\server\policy.pyc", line 727, in call_func
File "win32com\server\policy.pyc", line 717, in resolve_func
AttributeError: 'module' object has no attribute 'CImtg'
pythoncom error: Unexpected gateway error
Traceback (most recent call last):
File "win32com\server\policy.pyc", line 136, in CreateInstance
File "win32com\server\policy.pyc", line 194, in _CreateInstance_
File "win32com\server\policy.pyc", line 727, in call_func
File "win32com\server\policy.pyc", line 717, in resolve_func
AttributeError: 'module' object has no attribute 'CImtg'
pythoncom error: CPyFactory::CreateInstance failed to create instance. (80004005)
所以我需要解决这个错误。这个class当然是我的对象。我担心我在服务器中指定的值是:
_reg_class_spec_ = "__main__.CImtg"
不正确。 main 可能指的是包装后的 exe 而不是我自己的没有指定 main 的服务器。我确实也尝试过创建 main 但没有更好的结果。我只是不知道 py2exe 如何代理 classes。我尝试使用我的文件名 imtg_server.CImtg 但由于找不到模块而失败。我只尝试了 CImtg,但失败了。我尝试使用 win32com 和 pythoncom 的变体 - 但它就是不这样做。我有什么似乎 "right" 所以也许我需要一个额外的 reg 标签或什么?任何帮助是极大的赞赏。谢谢。
我终于明白了。我希望它能帮助其他为此苦苦挣扎的人。
这是导入问题。当 运行ning 作为 com 服务器时,py2exe 打包它的方式就是找不到正确的部分。我在下面发布的内容有效。
所以第一部分是设置需要像这样。注意在选项中添加 "packages",我们在其中包含我的 com 服务器。这很重要,因为我们将更改 __reg_class_spec__
以明确指向它。
所以完整修改setup_imtg.py
# This setup script builds a single-file Python inprocess COM server.
#
import modulefinder
import win32com, sys
for p in win32com.__path__[1:]:
modulefinder.AddPackagePath("win32com",p)
for extra in ["win32com.shell"]:
__import__(extra)
m = sys.modules[extra]
for p in m.__path__[1:]:
modulefinder.AddPackagePath(extra, p)
from distutils.core import setup
import py2exe
import sys
# If run without args, build executables, in quiet mode.
if len(sys.argv) == 1:
sys.argv.append("py2exe")
sys.argv.append("-q")
class Target:
def __init__(self, **kw):
self.__dict__.update(kw)
# for the versioninfo resources
self.name = "Python TestServer"
################################################################
# pywin32 COM pulls in a lot of stuff which we don't want or need.
CImtg = Target(
description = "Sample COM Server",
# what to build. For COM servers, the module name (not the
# filename) must be specified!
modules = ["imtg_server"],
# we only want the inproc server.
)
excludes = ["pywin", "pywin.debugger", "pywin.debugger.dbgcon",
"pywin.dialogs", "pywin.dialogs.list"]
options = {
"bundle_files": 1, # create singlefile exe
"packages": ["imtg_server"],
"compressed": 1, # compress the library archive
"excludes": excludes,
"dll_excludes": ["w9xpopen.exe"] # we don't need this
}
setup(
options = {"py2exe": options},
zipfile = None, # append zip-archive to the executable.
com_server = [CImtg]
)
现在我更改了服务器代码以指定 _reg_class_spec_
指向 imtg_server.CImtg。
但这还不是全部!我们从代码开始执行此操作以启动 com 服务器。我们不需要那样做!我使用 py2exe 开始的初始示例构建了一个 CONSOLE 应用程序而不是 com_server,但是 py2exe 确实构建了一个 com 服务器,所以我们不需要再次启动它 - 所以这段代码被删除:
import win32com.server
from win32com.server import localserver
print "starting the server"
localserver.main()
但正如他们所说 - 这还不是全部!由于我们包含了包本身,这就是我们正在执行的 "name" 不是 __main__
而是 imtg_server!
因此,如果您正在开发但未部署,那么除了 main 之外,这里还有一个检查。现在的大部分代码是确定您 运行ning 的方向并适当地检查和 运行 因为还有一件事!获取我们 运行ning 从 IDL 构建 typelib 或注册 typelib 的目录会将 exe 的名称附加到路径 - 当 运行ning 解释器不会!所以我不得不把它去掉。
请记住,当您 运行 exe 时,打印语句会被抑制,因此当您注册时不会显示任何内容,请不要担心。如果出现问题,将创建一个 .log 文件。
这是 imtg_server.py 的工作代码 -- 享受
import sys, os
import pythoncom
import win32com
import winerror
# importers check was old py2exe current uses frozen
if hasattr(sys, 'frozen'):
# we are running as py2exe-packed executable
print "is an exe"
pythoncom.frozen = 1
else:
print "not an exe"
class CImtg:
_reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER
#
# COM declarations
#
_reg_clsid_ = "{24c0e3fe-58e7-4485-87dc-9f9e823b85e1}"
_reg_desc_ = "IMTGroup Python test object"
_reg_progid_ = "ImtGroup.Test"
if hasattr(sys, 'frozen'):
# In the py2exe-packed version, specify the module.class
# to use. In the python script version, python is able
# to figure it out itself.
_reg_class_spec_ = "imtg_server.CImtg"
print "set reg_class_spec"
print _reg_class_spec_
###
### Link to typelib - uuid matches uuid for type library in idl file
_typelib_guid_ = '{d665e9d0-71a9-4e23-a1b4-abe3376d5c58}'
_typelib_version_ = 1, 0
_com_interfaces_ = ['IImtGroupApp']
def __init__(self):
### initialize something here if necessary
### The item below is not used in this example
self.MyProp1 = 10
def EchoString(self,in1):
return "Echoing " + in1
def AddNumbers(self, in1, in2):
return in1 + in2
def BuildTypelib():
from distutils.dep_util import newer
this_dir = os.path.dirname(__file__)
# when running as a exe this directory includes the exe name
if this_dir.endswith('imtg_server.exe'):
this_dir = this_dir[:-15]
idl = os.path.abspath(os.path.join(this_dir, "imtg.idl"))
tlb=os.path.splitext(idl)[0] + '.tlb'
if os.path.isfile(idl):
# test for idl - if no idl don't create tlb assume its there
# Comment below for building exe as we will have type library
if newer(idl, tlb):
print "Compiling %s" % (idl,)
rc = os.system ('midl "%s"' % (idl,))
if rc:
raise RuntimeError("Compiling MIDL failed!")
# Can't work out how to prevent MIDL from generating the stubs.
# just nuke them
for fname in "dlldata.c imtg_i.c imtg_p.c imtg.h".split():
os.remove(os.path.join(this_dir, fname))
print "Registering %s" % (tlb,)
tli=pythoncom.LoadTypeLib(tlb)
pythoncom.RegisterTypeLib(tli,tlb)
def UnregisterTypelib():
k = CImtg
try:
pythoncom.UnRegisterTypeLib(k._typelib_guid_,
k._typelib_version_[0],
k._typelib_version_[1],
0,
pythoncom.SYS_WIN32)
print "Unregistered typelib"
except pythoncom.error, details:
if details[0]==winerror.TYPE_E_REGISTRYACCESS:
pass
else:
raise
if __name__=='__main__' or __name__ =='imtg_server':
print "checking frozen"
if hasattr(sys, 'frozen'):
# running as packed executable
if '--unregister' in sys.argv or '--register' in sys.argv:
if '--unregister' in sys.argv:
# Unregister the type-libraries.
UnregisterTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
else:
# Build and register the type-libraries.
BuildTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
else:
if '--unregister' in sys.argv:
# Unregister the type-libraries.
UnregisterTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
else:
if '--register' in sys.argv:
# Build and register the type-libraries.
BuildTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
我遇到了这个问题。我需要创建一个基于 python 的 com 服务器,将其打包为 windows exe 并将其部署到 windows。它必须有一个 "full" 接口——因为消费者需要 idispatch 和特定的接口才能运行。现在我已经创建了 com 服务器并将其 运行 置于解释器之下,它可以完美地与我挑剔的客户端一起运行。但是,当打包为 EXE - 它是本地服务器时 - 当系统尝试实例化它时(即使是从 vbs 脚本),我在日志中收到错误消息。所以这里的一切。我在 itnernet 上上下搜索,它看起来像一个导入问题,但我不知道如何导入我自己的 python 对象供本地服务器使用。
这是安装了 pywin32 扩展的 python 2.7。
首先 - 我为服务器创建的 IDL:
imtg.idl
// This file will be processed by the MIDL tool to
// produce the type library (imtg.tlb) and marshalling code.
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(4fafbb23-6a38-4613-b93b-68ea66c67043),
dual,
helpstring("IImtGroupApp Interface"),
pointer_default(unique)
]
interface IImtGroupApp : IDispatch
{
[id(1), helpstring("method EchoString")] HRESULT EchoString([in] BSTR in1, [out, retval] BSTR *vals);
[id(2), helpstring("method AddNumbers")] HRESULT AddNumbers([in] long in1, [in] long in2, [out, retval] long *vali);
};
[
uuid(d665e9d0-71a9-4e23-a1b4-abe3376d5c58),
version(1.0),
helpstring("ImtGroup 1.0 Type Library")
]
library IMTGROUPLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
importlib("msado15.dll");
[
uuid(ced66424-93fb-4307-9062-7bee76d3d8eb),
helpstring("ImtGroupApp Class")
]
coclass ImtGroupApp {
[default] interface IImtGroupApp;
};
};
接下来是 Python 代码 - 现在这有点棘手,因为当我分发它时我不想创建 .tlb - 所以我不分发 .idy - 只要确保你有 .tbl 注册。如有必要,请使用 as admin cmd 提示符。
imtg_server.py
import sys, os
import pythoncom
import win32com
import winerror
# importers check was old py2exe current uses frozen
if hasattr(sys, 'frozen'):
# we are running as py2exe-packed executable
print "is an exe"
pythoncom.frozen = 1
else:
print "not an exe"
class CImtg:
_reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER
#
# COM declarations
#
_reg_clsid_ = "{24c0e3fe-58e7-4485-87dc-9f9e823b85e1}"
_reg_desc_ = "IMTGroup Python test object"
_reg_progid_ = "ImtGroup.Test"
if hasattr(sys, 'frozen'):
# In the py2exe-packed version, specify the module.class
# to use. In the python script version, python is able
# to figure it out itself.
_reg_class_spec_ = "__main__.CImtg"
print "set reg_class_spec"
print _reg_class_spec_
###
### Link to typelib - uuid matches uuid for type library in idl file
_typelib_guid_ = '{d665e9d0-71a9-4e23-a1b4-abe3376d5c58}'
_typelib_version_ = 1, 0
_com_interfaces_ = ['IImtGroupApp']
def __init__(self):
### initialize something here if necessary
### The item below is not used in this example
self.MyProp1 = 10
def EchoString(self,in1):
return "Echoing " + in1
def AddNumbers(self, in1, in2):
return in1 + in2
def BuildTypelib():
from distutils.dep_util import newer
this_dir = os.path.dirname(__file__)
idl = os.path.abspath(os.path.join(this_dir, "imtg.idl"))
tlb=os.path.splitext(idl)[0] + '.tlb'
if os.path.isfile(idl):
# test for idl - if no idl don't create tlb assume its there
# Comment below for building exe as we will have type library
if newer(idl, tlb):
print "Compiling %s" % (idl,)
rc = os.system ('midl "%s"' % (idl,))
if rc:
raise RuntimeError("Compiling MIDL failed!")
# Can't work out how to prevent MIDL from generating the stubs.
# just nuke them
for fname in "dlldata.c imtg_i.c imtg_p.c imtg.h".split():
os.remove(os.path.join(this_dir, fname))
print "Registering %s" % (tlb,)
tli=pythoncom.LoadTypeLib(tlb)
pythoncom.RegisterTypeLib(tli,tlb)
def UnregisterTypelib():
k = CImtg
try:
pythoncom.UnRegisterTypeLib(k._typelib_guid_,
k._typelib_version_[0],
k._typelib_version_[1],
0,
pythoncom.SYS_WIN32)
print "Unregistered typelib"
except pythoncom.error, details:
if details[0]==winerror.TYPE_E_REGISTRYACCESS:
pass
else:
raise
if __name__=='__main__':
print "checking frozen"
if hasattr(sys, 'frozen'):
# running as packed executable
if '--unregister' in sys.argv or '--register' in sys.argv:
if '--unregister' in sys.argv:
# Unregister the type-libraries.
UnregisterTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
else:
# Build and register the type-libraries.
BuildTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
else:
import win32com.server
from win32com.server import localserver
print "starting the server"
localserver.main()
else:
if '--unregister' in sys.argv:
# Unregister the type-libraries.
UnregisterTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
else:
if '--register' in sys.argv:
# Build and register the type-libraries.
BuildTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
接下来是 py2exe 的设置
我不得不添加 modulefinder 的时髦导入,因为 win32com.shell 没有包含在打包的可执行文件中
setup_imtg.py
# This setup script builds a single-file Python inprocess COM server.
#
import modulefinder
import win32com, sys
for p in win32com.__path__[1:]:
modulefinder.AddPackagePath("win32com",p)
for extra in ["win32com.shell"]:
__import__(extra)
m = sys.modules[extra]
for p in m.__path__[1:]:
modulefinder.AddPackagePath(extra, p)
from distutils.core import setup
import py2exe
import sys
# If run without args, build executables, in quiet mode.
if len(sys.argv) == 1:
sys.argv.append("py2exe")
sys.argv.append("-q")
class Target:
def __init__(self, **kw):
self.__dict__.update(kw)
# for the versioninfo resources
self.name = "IMTG Server"
################################################################
# pywin32 COM pulls in a lot of stuff which we don't want or need.
CImtg = Target(
description = "Sample COM server",
# what to build. For COM servers, the module name (not the
# filename) must be specified!
modules = ["imtg_server"],
# we only want the inproc server.
)
excludes = ["pywin", "pywin.debugger", "pywin.debugger.dbgcon",
"pywin.dialogs", "pywin.dialogs.list"]
options = {
"bundle_files": 1, # create singlefile exe
"compressed": 1, # compress the library archive
"excludes": excludes,
"dll_excludes": ["w9xpopen.exe"] # we don't need this
}
setup(
options = {"py2exe": options},
zipfile = None, # append zip-archive to the executable.
com_server = [CImtg]
)
当你运行生成的EXE你可以用
注册imtg_server --注册
但您不会看到任何输出
--注销注销
你可以用这个vbs文件来测试一下。
t.vbs
dim MD
set MD = CreateObject("ImtGroup.Test")
dim response
response = MD.EchoString("Really")
MsgBox(response)
当您 运行 时,将创建一个如下所示的 .log:
pythoncom error: ERROR: server.policy could not create an instance.
Traceback (most recent call last):
File "win32com\server\policy.pyc", line 136, in CreateInstance
File "win32com\server\policy.pyc", line 194, in _CreateInstance_
File "win32com\server\policy.pyc", line 727, in call_func
File "win32com\server\policy.pyc", line 717, in resolve_func
AttributeError: 'module' object has no attribute 'CImtg'
pythoncom error: Unexpected gateway error
Traceback (most recent call last):
File "win32com\server\policy.pyc", line 136, in CreateInstance
File "win32com\server\policy.pyc", line 194, in _CreateInstance_
File "win32com\server\policy.pyc", line 727, in call_func
File "win32com\server\policy.pyc", line 717, in resolve_func
AttributeError: 'module' object has no attribute 'CImtg'
pythoncom error: CPyFactory::CreateInstance failed to create instance. (80004005)
所以我需要解决这个错误。这个class当然是我的对象。我担心我在服务器中指定的值是:
_reg_class_spec_ = "__main__.CImtg"
不正确。 main 可能指的是包装后的 exe 而不是我自己的没有指定 main 的服务器。我确实也尝试过创建 main 但没有更好的结果。我只是不知道 py2exe 如何代理 classes。我尝试使用我的文件名 imtg_server.CImtg 但由于找不到模块而失败。我只尝试了 CImtg,但失败了。我尝试使用 win32com 和 pythoncom 的变体 - 但它就是不这样做。我有什么似乎 "right" 所以也许我需要一个额外的 reg 标签或什么?任何帮助是极大的赞赏。谢谢。
我终于明白了。我希望它能帮助其他为此苦苦挣扎的人。
这是导入问题。当 运行ning 作为 com 服务器时,py2exe 打包它的方式就是找不到正确的部分。我在下面发布的内容有效。
所以第一部分是设置需要像这样。注意在选项中添加 "packages",我们在其中包含我的 com 服务器。这很重要,因为我们将更改 __reg_class_spec__
以明确指向它。
所以完整修改setup_imtg.py
# This setup script builds a single-file Python inprocess COM server.
#
import modulefinder
import win32com, sys
for p in win32com.__path__[1:]:
modulefinder.AddPackagePath("win32com",p)
for extra in ["win32com.shell"]:
__import__(extra)
m = sys.modules[extra]
for p in m.__path__[1:]:
modulefinder.AddPackagePath(extra, p)
from distutils.core import setup
import py2exe
import sys
# If run without args, build executables, in quiet mode.
if len(sys.argv) == 1:
sys.argv.append("py2exe")
sys.argv.append("-q")
class Target:
def __init__(self, **kw):
self.__dict__.update(kw)
# for the versioninfo resources
self.name = "Python TestServer"
################################################################
# pywin32 COM pulls in a lot of stuff which we don't want or need.
CImtg = Target(
description = "Sample COM Server",
# what to build. For COM servers, the module name (not the
# filename) must be specified!
modules = ["imtg_server"],
# we only want the inproc server.
)
excludes = ["pywin", "pywin.debugger", "pywin.debugger.dbgcon",
"pywin.dialogs", "pywin.dialogs.list"]
options = {
"bundle_files": 1, # create singlefile exe
"packages": ["imtg_server"],
"compressed": 1, # compress the library archive
"excludes": excludes,
"dll_excludes": ["w9xpopen.exe"] # we don't need this
}
setup(
options = {"py2exe": options},
zipfile = None, # append zip-archive to the executable.
com_server = [CImtg]
)
现在我更改了服务器代码以指定 _reg_class_spec_
指向 imtg_server.CImtg。
但这还不是全部!我们从代码开始执行此操作以启动 com 服务器。我们不需要那样做!我使用 py2exe 开始的初始示例构建了一个 CONSOLE 应用程序而不是 com_server,但是 py2exe 确实构建了一个 com 服务器,所以我们不需要再次启动它 - 所以这段代码被删除:
import win32com.server
from win32com.server import localserver
print "starting the server"
localserver.main()
但正如他们所说 - 这还不是全部!由于我们包含了包本身,这就是我们正在执行的 "name" 不是 __main__
而是 imtg_server!
因此,如果您正在开发但未部署,那么除了 main 之外,这里还有一个检查。现在的大部分代码是确定您 运行ning 的方向并适当地检查和 运行 因为还有一件事!获取我们 运行ning 从 IDL 构建 typelib 或注册 typelib 的目录会将 exe 的名称附加到路径 - 当 运行ning 解释器不会!所以我不得不把它去掉。
请记住,当您 运行 exe 时,打印语句会被抑制,因此当您注册时不会显示任何内容,请不要担心。如果出现问题,将创建一个 .log 文件。
这是 imtg_server.py 的工作代码 -- 享受
import sys, os
import pythoncom
import win32com
import winerror
# importers check was old py2exe current uses frozen
if hasattr(sys, 'frozen'):
# we are running as py2exe-packed executable
print "is an exe"
pythoncom.frozen = 1
else:
print "not an exe"
class CImtg:
_reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER
#
# COM declarations
#
_reg_clsid_ = "{24c0e3fe-58e7-4485-87dc-9f9e823b85e1}"
_reg_desc_ = "IMTGroup Python test object"
_reg_progid_ = "ImtGroup.Test"
if hasattr(sys, 'frozen'):
# In the py2exe-packed version, specify the module.class
# to use. In the python script version, python is able
# to figure it out itself.
_reg_class_spec_ = "imtg_server.CImtg"
print "set reg_class_spec"
print _reg_class_spec_
###
### Link to typelib - uuid matches uuid for type library in idl file
_typelib_guid_ = '{d665e9d0-71a9-4e23-a1b4-abe3376d5c58}'
_typelib_version_ = 1, 0
_com_interfaces_ = ['IImtGroupApp']
def __init__(self):
### initialize something here if necessary
### The item below is not used in this example
self.MyProp1 = 10
def EchoString(self,in1):
return "Echoing " + in1
def AddNumbers(self, in1, in2):
return in1 + in2
def BuildTypelib():
from distutils.dep_util import newer
this_dir = os.path.dirname(__file__)
# when running as a exe this directory includes the exe name
if this_dir.endswith('imtg_server.exe'):
this_dir = this_dir[:-15]
idl = os.path.abspath(os.path.join(this_dir, "imtg.idl"))
tlb=os.path.splitext(idl)[0] + '.tlb'
if os.path.isfile(idl):
# test for idl - if no idl don't create tlb assume its there
# Comment below for building exe as we will have type library
if newer(idl, tlb):
print "Compiling %s" % (idl,)
rc = os.system ('midl "%s"' % (idl,))
if rc:
raise RuntimeError("Compiling MIDL failed!")
# Can't work out how to prevent MIDL from generating the stubs.
# just nuke them
for fname in "dlldata.c imtg_i.c imtg_p.c imtg.h".split():
os.remove(os.path.join(this_dir, fname))
print "Registering %s" % (tlb,)
tli=pythoncom.LoadTypeLib(tlb)
pythoncom.RegisterTypeLib(tli,tlb)
def UnregisterTypelib():
k = CImtg
try:
pythoncom.UnRegisterTypeLib(k._typelib_guid_,
k._typelib_version_[0],
k._typelib_version_[1],
0,
pythoncom.SYS_WIN32)
print "Unregistered typelib"
except pythoncom.error, details:
if details[0]==winerror.TYPE_E_REGISTRYACCESS:
pass
else:
raise
if __name__=='__main__' or __name__ =='imtg_server':
print "checking frozen"
if hasattr(sys, 'frozen'):
# running as packed executable
if '--unregister' in sys.argv or '--register' in sys.argv:
if '--unregister' in sys.argv:
# Unregister the type-libraries.
UnregisterTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
else:
# Build and register the type-libraries.
BuildTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
else:
if '--unregister' in sys.argv:
# Unregister the type-libraries.
UnregisterTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)
else:
if '--register' in sys.argv:
# Build and register the type-libraries.
BuildTypelib()
import win32com.server.register
win32com.server.register.UseCommandLine(CImtg)