2016-03-02 82 views
4

我已經用這個打牆了。我需要創建一個基於python的com服務器,將它打包爲一個windows exe文件並將其部署在Windows上。它必須有一個「完整的」接口 - 因爲消費者需要idispatch和特定的interfce來運行。現在我已經創建了com服務器,並讓它在解釋器下運行,並且它與我挑剔的客戶端完美搭配。但是,當打包爲EXE時 - 它是一個本地服務器 - 當系統嘗試實例化它時(甚至是從一個vbs腳本),我會在日誌中看到一個錯誤。所以這裏是每一個。我在itnernet中搜索高低,它看起來像一個導入問題,但我不知道如何導入我自己的python對象的本地服務器使用。Python Com服務器在用py2exe包裝時無法創建實例 - 錯誤對象沒有任何屬性

這是安裝了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註冊。如有必要,使用管理員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你c與

imtg_server --register

,但你不會看到阿布輸出

--unregister註銷

的寄存器可以使用此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文件,而不是我自己的沒有指定主文件的服務器。我也嘗試創建主要的,沒有更好的結果。我只是不知道py2exe如何代理這些類。我嘗試使用我的文件名imtg_server.CImtg,並失敗,找不到模塊。我只嘗試了CImtg,但失敗了。我嘗試過使用win32com和pythoncom的變種 - 但它並沒有這樣做。我有什麼似乎「正確」,所以也許我需要額外的reg標籤或什麼?任何幫助是極大的讚賞。謝謝。

+0

,對於py2exe的安裝使用CImtg爲COM服務器名稱是無關緊要的事實。我用相同的結果嘗試過foo。 – user2709214

回答

1

我終於明白了這一點。我希望它能幫助別人爲此而努力。

這是導入問題。 py2exe打包的方式只是在作爲com服務器運行時找不到合適的部分。我在下面發表的作品。

因此,第一件事是設置需要看起來像這樣。注意在包含我的com服務器的選項中增加了「packages」。這很重要,因爲我們要更改__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並建立了一個控制檯應用程序,而不是一個com_server,但py2exe DOES建立一個COM服務器,所以我們並不需要再次啓動它 - 所以這段代碼被刪除:

import win32com.server 
    from win32com.server import localserver 
    print "starting the server" 
    localserver.main() 

但作爲他們說 - 這不是全部!既然我們包含了包本身,那我們正在執行的「名稱」不是__main__,而是它的imtg_server!

所以在這裏除了main之外還有一個檢查,以防你正在開發但不部署。現在大部分代碼都是要確定你正在運行哪種方式,並進行適當的檢查和運行,因爲還有一件事!從IDL獲取我們正在運行的用於構建typelib的目錄,或者註冊typelib將exe的名稱附加到路徑中 - 運行解釋程序的地方不會!所以我不得不剝奪它。

請記住,當您運行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) 
相關問題