Python Com服务器无法在用py2exe包装时创建实例错误对象没有属性

2024-10-01 11:27:46 发布

您现在位置:Python中文网/ 问答频道 /正文

我用这个撞了壁。我需要创建一个基于python的com服务器,将其打包为windowsexe并部署到windows上。它必须有一个“完整”的接口,因为消费者需要idispatch和一个特定的接口才能正常工作。现在我已经创建了com服务器,并在解释器下运行,它可以完美地与我挑剔的客户机一起工作。但是,当打包为EXE(它是一个localserver)时,当系统尝试实例化它时(即使是从vbs脚本中),我在日志中得到一个错误。一切都在这里。我在itnernet中搜索了高和低,这看起来像是一个导入问题,但我不知道如何导入我自己的python对象供localserver使用。在

这是安装了pywin32扩展的python2.7。在

首先,我为服务器创建的IDL:

在图3.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公司_服务器.py在

^{pr2}$

接下来是py2exe的设置

我不得不添加modulefinder的奇怪导入,因为win32com.shell公司未包含在打包的可执行文件中

设置_图像.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_服务器—注册

但你看不到输出

--注销注销

您可以使用此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)

所以我需要的是绕过这个错误。这个类当然是我的目标。我担心我在服务器中指定的值为:

_reg_class_spec_ = "__main__.CImtg"

不正确。main可能引用包装好的exe文件,而不是我自己的未指定main的服务器。我也尝试过创建main,但没有得到更好的结果。我只是不知道py2exe如何代理类。我试着用我的文件名imtg_服务器.CImtg如果找不到模块,则失败。我试过CImtg,但失败了。我尝试过使用win32com和pythoncom的变体,但它做不到。我所做的似乎“正确”,所以也许我需要一个额外的reg标签或其他什么东西?非常感谢任何帮助。谢谢您。在


Tags: theinimport服务器serversyspolicyline
1条回答
网友
1楼 · 发布于 2024-10-01 11:27:46

我终于弄明白了。我希望它能帮助其他人解决这个问题。在

这是导入的问题。py2exe打包的方式就是在作为com服务器运行时找不到合适的部分。我在下面发布的内容有效。在

所以第一件事是设置需要像这样。注意在选项中添加了“packages”,其中包括我的com服务器。这很重要,因为我们要将__reg_class_spec__改为显式地指向它。在

所以整个修改后的设置_图像.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_服务器.CImtg. 在

但还不止这些!我们从启动com服务器的代码开始这样做。我们不需要那样做!我开始使用的初始示例使用了py2exe并构建了一个控制台应用程序而不是com_服务器,但是py2exe确实构建了一个com服务器,因此我们不需要再次启动它-因此删除了以下代码:

^{pr2}$

但正如他们所说-这还不是全部!因为我们包含了包本身,所以我们正在执行的“name”不是__main__,而是imtg\u服务器!在

所以除了main之外,这里还有一个检查,以防您正在开发而不是部署。现在的大部分代码都是确定运行的方式,并进行适当的检查和运行,因为还有一件事!从IDL中获取用于从IDL构建typelib或注册typelib所运行的目录,会将exe的名称附加到路径中,而运行解释器时则不会!所以我不得不把它脱掉。在

请记住,当您运行exe时,打印语句将被抑制,因此当您注册时,不必担心什么也不显示。如果出现问题,将创建一个.log文件。在

以下是imtg的工作代码_服务器.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)

相关问题 更多 >