2016-03-02 1 views
4

J'ai frappé un mur avec celui-ci. J'ai besoin de créer un serveur com basé sur python, l'empaqueter sous Windows Exe et le déployer sur Windows. Il doit avoir une interface "complète" - parce que le consommateur a besoin d'une identité et d'une interface spécifique pour fonctionner. Maintenant, j'ai créé le serveur com et l'ai fait fonctionner sous l'interpréteur et il fonctionne parfaitement avec mon client picky. Cependant, lors de l'empaquetage en tant qu'exe - c'est un localserver - j'obtiens une erreur dans le journal lorsque le système essaie de l'instancier (même à partir d'un script vbs). Alors, voici tout. J'ai cherché haut et bas dans l'itnernet et cela ressemble à un problème d'importation mais je ne sais pas comment importer mon propre objet python pour que le localserver puisse l'utiliser.Python Com Server impossible de créer l'instance lorsqu'il est enveloppé avec py2exe - l'objet d'erreur n'a pas d'attribut

Ceci est python 2.7 avec les extensions pywin32 installées.

Alors d'abord - le IDL j'ai créé pour le serveur:

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; 
    }; 
}; 

Suivant le code Python - maintenant cela devient un peu difficile parce que quand je distribuerai cela, je ne veux pas pour créer le .tlb - donc je ne distribue pas le .idy - assurez-vous juste que vous avez le .tbl pour vous inscrire. Utilisez une invite cmd admin en cas de besoin.

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) 

Suivant la configuration de py2exe

je devais ajouter l'importation funky de la modulefinder parce win32com.shell n'a pas été inclus dans l'exécutable emballé

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

Lorsque vous exécutez l'EXE généré un registre avec

imtg_server --register

mais vous ne verrez pas abou sortie

--unregister désinscrit

Vous pouvez utiliser ce fichier vbs pour le tester.

t.vbs

dim MD 
set MD = CreateObject("ImtGroup.Test") 
dim response 
response = MD.EchoString("Really") 
MsgBox(response) 

Lorsque vous exécutez il y aura un .log créé qui ressemble à ceci:

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) 

Alors, que j'ai besoin est de contourner cette erreur. Cette classe est certainement dans mon objet. Je suis préoccupé que la valeur que j'ai spécifié dans mon serveur comme:

_reg_class_spec_ = "__main__.CImtg" 

Est incorrect. Le principal peut se référer à l'exe enveloppé et pas mon propre serveur qui n'a pas de principal spécifié. J'ai essayé de créer le principal aussi sans meilleurs résultats. Je ne sais pas comment py2exe représente les classes. J'ai essayé d'utiliser mon nom de fichier imtg_server.CImtg et cela échoue avec le module non trouvé. J'ai essayé juste CImtg et cela échoue. J'ai essayé d'utiliser des variantes de win32com et de pythoncom - mais ça ne marche tout simplement pas.Ce que j'ai semble "juste" alors peut-être que j'ai besoin d'un tag supplémentaire reg ou quelque chose? Toute aide est grandement appréciée. Je vous remercie.

+0

Le fait que la configuration de py2exe utiliser CImtg pour le nom du serveur com est hors de propos. J'ai essayé foo avec le même résultat. – user2709214

Répondre

1

J'ai finalement compris cela. J'espère que cela aidera quelqu'un d'autre à lutter contre cela.

Ceci est un problème lors de l'importation. La façon dont py2exe l'empaquette ne permet pas de trouver les bonnes pièces lors de l'exécution en tant que serveur com. Ce que je poste ci-dessous fonctionne.

Donc, la première chose est que la configuration doit ressembler à ceci. AVIS sur l'ajout de "packages" dans les options, où nous incluons mon serveur com. Ceci est important parce que nous allons changer le __reg_class_spec__ pour le montrer explicitement.

Ainsi, le setup_imtg.py révisé complet

# 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] 
    ) 

Maintenant j'ai changé le code du serveur pour spécifier le _reg_class_spec_ pour pointer vers imtg_server.CImtg.

Mais ce n'est pas tout! Nous faisions cela à partir du code pour démarrer le serveur com. Nous n'avons pas besoin de faire ça! L'échantillon initial, j'ai commencé avec py2exe utilisé et construit une application console et non un com_server, mais py2exe ne construit un serveur com donc on n'a pas besoin de le démarrer à nouveau - donc ce code est supprimé:

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

Mais ils disent - ce n'est pas tout! Puisque nous avons inclus le paquet lui-même et que c'est ce que nous sommes en train d'exécuter le "nom" n'est pas __main__ à la place c'est imtg_server!

Donc, il y a une vérification pour cela ici en plus de la fonction principale au cas où vous développez mais ne déployez pas. La plupart du code est maintenant de déterminer de quelle façon vous courez et de vérifier et de fonctionner de manière appropriée, car il y a une chose de plus! Obtenir le répertoire où nous sommes en train de construire le typelib à partir de l'IDL ou pour enregistrer le typelib ajoute le nom de l'exe au chemin - où en cours d'exécution l'interpréteur ne le fait pas! Donc j'ai dû enlever ça. N'oubliez pas que lorsque vous exécutez l'exe, les instructions d'impression sont supprimées. Ne vous inquiétez pas lorsque vous vous enregistrez et que rien ne s'affiche. S'il y a un problème, un fichier .log sera créé.

est ici le code de travail pour imtg_server.py - profiter

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)