home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 July / CMCD0704.ISO / Software / Shareware / Comunicatii / jyte / jyte.exe / register.py < prev    next >
Text File  |  2004-01-26  |  19KB  |  497 lines

  1. """Utilities for registering objects.
  2.  
  3. This module contains utility functions to register Python objects as
  4. valid COM Servers.  The RegisterServer function provides all information
  5. necessary to allow the COM framework to respond to a request for a COM object,
  6. construct the necessary Python object, and dispatch COM events.
  7.  
  8. """
  9. import sys
  10. import win32api
  11. import win32con
  12. import pythoncom
  13. import winerror
  14. import os
  15.  
  16. CATID_PythonCOMServer = "{B3EF80D0-68E2-11D0-A689-00C04FD658FF}"
  17.  
  18. def _set_subkeys(keyName, valueDict, base=win32con.HKEY_CLASSES_ROOT):
  19.   hkey = win32api.RegCreateKey(base, keyName)
  20.   try:
  21.     for key, value in valueDict.items():
  22.       win32api.RegSetValueEx(hkey, key, None, win32con.REG_SZ, value)
  23.   finally:
  24.     win32api.RegCloseKey(hkey)
  25.             
  26. def _set_string(path, value, base=win32con.HKEY_CLASSES_ROOT):
  27.   "Set a string value in the registry."
  28.  
  29.   win32api.RegSetValue(base,
  30.                        path,
  31.                        win32con.REG_SZ,
  32.                        value)
  33.  
  34. def _get_string(path, base=win32con.HKEY_CLASSES_ROOT):
  35.   "Get a string value from the registry."
  36.  
  37.   try:
  38.     return win32api.RegQueryValue(base, path)
  39.   except win32api.error:
  40.     return None
  41.  
  42. def _remove_key(path, base=win32con.HKEY_CLASSES_ROOT):
  43.   "Remove a string from the registry."
  44.  
  45.   try:
  46.     win32api.RegDeleteKey(base, path)
  47.   except win32api.error, (code, fn, msg):
  48.     if code != winerror.ERROR_FILE_NOT_FOUND:
  49.       raise win32api.error, (code, fn, msg)
  50.  
  51. def recurse_delete_key(path, base=win32con.HKEY_CLASSES_ROOT):
  52.   """Recursively delete registry keys.
  53.  
  54.   This is needed since you can't blast a key when subkeys exist.
  55.   """
  56.   try:
  57.     h = win32api.RegOpenKey(base, path)
  58.   except win32api.error, (code, fn, msg):
  59.     if code != winerror.ERROR_FILE_NOT_FOUND:
  60.       raise win32api.error, (code, fn, msg)
  61.   else:
  62.     # parent key found and opened successfully. do some work, making sure
  63.     # to always close the thing (error or no).
  64.     try:
  65.       # remove all of the subkeys
  66.       while 1:
  67.         try:
  68.           subkeyname = win32api.RegEnumKey(h, 0)
  69.         except win32api.error, (code, fn, msg):
  70.           if code != winerror.ERROR_NO_MORE_ITEMS:
  71.             raise win32api.error, (code, fn, msg)
  72.           break
  73.         recurse_delete_key(path + '\\' + subkeyname, base)
  74.  
  75.       # remove the parent key
  76.       _remove_key(path, base)
  77.     finally:
  78.       win32api.RegCloseKey(h)
  79.  
  80. def _cat_registrar():
  81.   return pythoncom.CoCreateInstance(
  82.     pythoncom.CLSID_StdComponentCategoriesMgr,
  83.     None,
  84.     pythoncom.CLSCTX_INPROC_SERVER,
  85.     pythoncom.IID_ICatRegister
  86.     )
  87.     
  88. def _find_localserver_exe(mustfind):
  89.   if not sys.platform.startswith("win32"):
  90.     return sys.executable
  91.   if pythoncom.__file__.find("_d") < 0:
  92.     exeBaseName = "pythonw.exe"
  93.   else:
  94.     exeBaseName = "pythonw_d.exe"
  95.   # First see if in the same directory as this .EXE
  96.   exeName = os.path.join( os.path.split(sys.executable)[0], exeBaseName )
  97.   if not os.path.exists(exeName):
  98.     # See if in our sys.prefix directory
  99.     exeName = os.path.join( sys.prefix, exeBaseName )
  100.   if not os.path.exists(exeName):
  101.     # See if in our sys.prefix/pcbuild directory (for developers)
  102.     exeName = os.path.join( sys.prefix, "PCbuild",  exeBaseName )
  103.   if not os.path.exists(exeName):
  104.     # See if the registry has some info.
  105.     try:
  106.       key = "SOFTWARE\\Python\\PythonCore\\%s\\InstallPath" % sys.winver
  107.       path = win32api.RegQueryValue( win32con.HKEY_LOCAL_MACHINE, key )
  108.       exeName = os.path.join( path, exeBaseName )
  109.     except (AttributeError,win32api.error):
  110.       pass
  111.   if not os.path.exists(exeName):
  112.     if mustfind:
  113.       raise RuntimeError, "Can not locate the program '%s'" % exeBaseName
  114.     return None
  115.   return exeName
  116.  
  117. def _find_localserver_module():
  118.   import win32com.server
  119.   path = win32com.server.__path__[0]
  120.   baseName = "localserver"
  121.   pyfile = os.path.join(path, baseName + ".py")
  122.   try:
  123.     os.stat(pyfile)
  124.   except os.error:
  125.     # See if we have a compiled extension
  126.     if __debug__:
  127.       ext = ".pyc"
  128.     else:
  129.       ext = ".pyo"
  130.     pyfile = os.path.join(path, baseName + ext)
  131.     try:
  132.       os.stat(pyfile)
  133.     except os.error:
  134.       raise RuntimeError, "Can not locate the Python module 'win32com.server.%s'" % baseName
  135.   return pyfile
  136.  
  137. def RegisterServer(clsid, 
  138.                    pythonInstString=None, 
  139.                    desc=None,
  140.                    progID=None, verProgID=None,
  141.                    defIcon=None,
  142.                    threadingModel="both",
  143.                    policy=None,
  144.                    catids=[], other={},
  145.                    addPyComCat=1,
  146.                    dispatcher = None,
  147.                    clsctx = None,
  148.                    addnPath = None,
  149.                   ):
  150.   """Registers a Python object as a COM Server.  This enters almost all necessary
  151.      information in the system registry, allowing COM to use the object.
  152.  
  153.      clsid -- The (unique) CLSID of the server.
  154.      pythonInstString -- A string holding the instance name that will be created
  155.                    whenever COM requests a new object.
  156.      desc -- The description of the COM object.
  157.      progID -- The user name of this object (eg, Word.Document)
  158.      verProgId -- The user name of this version's implementation (eg Word.6.Document)
  159.      defIcon -- The default icon for the object.
  160.      threadingModel -- The threading model this object supports.
  161.      policy -- The policy to use when creating this object.
  162.      catids -- A list of category ID's this object belongs in.
  163.      other -- A dictionary of extra items to be registered.
  164.      addPyComCat -- A flag indicating if the object should be added to the list
  165.               of Python servers installed on the machine.
  166.      dispatcher -- The dispatcher to use when creating this object.
  167.      clsctx -- One of the CLSCTX_* constants.
  168.      addnPath -- An additional path the COM framework will add to sys.path
  169.                  before attempting to create the object.
  170.   """
  171.  
  172.  
  173.   ### backwards-compat check
  174.   ### Certain policies do not require a "class name", just the policy itself.
  175.   if not pythonInstString and not policy:
  176.     raise TypeError, 'You must specify either the Python Class or Python Policy which implement the COM object.'
  177.  
  178.   keyNameRoot = "CLSID\\%s" % str(clsid)
  179.   _set_string(keyNameRoot, desc)
  180.  
  181.   # Also register as an "Application" so DCOM etc all see us.
  182.   _set_string("AppID\\%s" % clsid, progID)
  183.   # Depending on contexts requested, register the specified server type.
  184.   # Set default clsctx.
  185.   if not clsctx:
  186.     clsctx = pythoncom.CLSCTX_INPROC_SERVER | pythoncom.CLSCTX_LOCAL_SERVER
  187.   # And if we are frozen, ignore the ones that don't make sense in this
  188.   # context.
  189.   if pythoncom.frozen:
  190.     assert sys.frozen, "pythoncom is frozen, but sys.frozen is not set - don't know the context!"
  191.     if sys.frozen == "dll":
  192.       clsctx = clsctx & pythoncom.CLSCTX_INPROC_SERVER
  193.     else:
  194.       clsctx = clsctx & pythoncom.CLSCTX_LOCAL_SERVER
  195.   # Now setup based on the clsctx left over.
  196.   if clsctx & pythoncom.CLSCTX_INPROC_SERVER:
  197.     # get the module to use for registration.
  198.     # nod to Gordon's installer - if sys.frozen and sys.frozendllhandle
  199.     # exist, then we are being registered via a DLL - use this DLL as the
  200.     # file name.
  201.     if pythoncom.frozen:
  202.       if hasattr(sys, "frozendllhandle"):
  203.         dllName = win32api.GetModuleFileName(sys.frozendllhandle)
  204.       else:
  205.         raise RuntimeError, "We appear to have a frozen DLL, but I don't know the DLL to use"
  206.     else:
  207.       # Normal case - running from .py file, so register pythoncom's DLL.
  208.       dllName = os.path.basename(pythoncom.__file__)
  209.  
  210.     _set_subkeys(keyNameRoot + "\\InprocServer32",
  211.                  { None : dllName,
  212.                    "ThreadingModel" : threadingModel,
  213.                    })
  214.   else: # Remove any old InProcServer32 registrations
  215.     _remove_key(keyNameRoot + "\\InprocServer32")
  216.  
  217.   if clsctx & pythoncom.CLSCTX_LOCAL_SERVER:
  218.     if pythoncom.frozen:
  219.       # If we are frozen, we write "{exe} /Automate", just
  220.       # like "normal" .EXEs do
  221.       exeName = win32api.GetShortPathName(sys.executable)
  222.       command = '%s /Automate' % (exeName,)
  223.     else:
  224.       # Running from .py sources - we need to write
  225.       # 'python.exe win32com\server\localserver.py {clsid}"
  226.       exeName = _find_localserver_exe(1)
  227.       exeName = win32api.GetShortPathName(exeName)
  228.       pyfile = _find_localserver_module()
  229.       command = '%s "%s" %s' % (exeName, pyfile, str(clsid))
  230.     _set_string(keyNameRoot + '\\LocalServer32', command)
  231.   else: # Remove any old LocalServer32 registrations
  232.     _remove_key(keyNameRoot + "\\LocalServer32")
  233.  
  234.   if pythonInstString:
  235.     _set_string(keyNameRoot + '\\PythonCOM', pythonInstString)
  236.   else:
  237.     _remove_key(keyNameRoot + '\\PythonCOM')
  238.   if policy:
  239.     _set_string(keyNameRoot + '\\PythonCOMPolicy', policy)
  240.   else:
  241.     _remove_key(keyNameRoot + '\\PythonCOMPolicy')
  242.  
  243.   if dispatcher:
  244.     _set_string(keyNameRoot + '\\PythonCOMDispatcher', dispatcher)
  245.   else:
  246.     _remove_key(keyNameRoot + '\\PythonCOMDispatcher')
  247.  
  248.   if defIcon:
  249.     _set_string(keyNameRoot + '\\DefaultIcon', defIcon)
  250.  
  251.   if addnPath:
  252.     _set_string(keyNameRoot + "\\PythonCOMPath", addnPath)
  253.   else:
  254.     _remove_key(keyNameRoot + "\\PythonCOMPath")
  255.  
  256.   if addPyComCat:
  257.     catids = catids + [ CATID_PythonCOMServer ]
  258.  
  259.   # Set up the implemented categories
  260.   if catids:
  261.     regCat = _cat_registrar()
  262.     regCat.RegisterClassImplCategories(clsid, catids)
  263.  
  264.   # set up any other reg values they might have
  265.   if other:
  266.     for key, value in other.items():
  267.       _set_string(keyNameRoot + '\\' + key, value)
  268.  
  269.   if progID:
  270.     # set the progID as the most specific that was given to us
  271.     if verProgID:
  272.       _set_string(keyNameRoot + '\\ProgID', verProgID)
  273.     else:
  274.       _set_string(keyNameRoot + '\\ProgID', progID)
  275.  
  276.     # Set up the root entries - version independent.
  277.     if desc:
  278.       _set_string(progID, desc)
  279.     _set_string(progID + '\\CLSID', str(clsid))
  280.  
  281.     # Set up the root entries - version dependent.
  282.     if verProgID:
  283.       # point from independent to the current version
  284.       _set_string(progID + '\\CurVer', verProgID)
  285.  
  286.       # point to the version-independent one
  287.       _set_string(keyNameRoot + '\\VersionIndependentProgID', progID)
  288.  
  289.       # set up the versioned progID
  290.       if desc:
  291.         _set_string(verProgID, desc)
  292.       _set_string(verProgID + '\\CLSID', str(clsid))
  293.  
  294. def GetUnregisterServerKeys(clsid, progID=None, verProgID=None, customKeys = None):
  295.   """Given a server, return a list of of ("key", root), which are keys recursively
  296.   and uncondtionally deleted at unregister or uninstall time.
  297.   """
  298.   # remove the main CLSID registration
  299.   ret = [("CLSID\\%s" % str(clsid), win32con.HKEY_CLASSES_ROOT)]
  300.   # remove the versioned ProgID registration
  301.   if verProgID:
  302.     ret.append((verProgID, win32con.HKEY_CLASSES_ROOT))
  303.   # blow away the independent ProgID. we can't leave it since we just
  304.   # torched the class.
  305.   ### could potentially check the CLSID... ?
  306.   if progID:
  307.     ret.append((progID, win32con.HKEY_CLASSES_ROOT))
  308.   # The DCOM config tool may write settings to the AppID key for our CLSID
  309.   ret.append( ("AppID\\%s" % str(clsid), win32con.HKEY_CLASSES_ROOT) )
  310.   # Any custom keys?
  311.   if customKeys:
  312.     ret = ret + customKeys
  313.    
  314.   return ret
  315.   
  316.  
  317. def UnregisterServer(clsid, progID=None, verProgID=None, customKeys = None):
  318.   """Unregisters a Python COM server."""
  319.  
  320.   for args in GetUnregisterServerKeys(clsid, progID, verProgID, customKeys ):
  321.     recurse_delete_key(*args)
  322.  
  323.   ### it might be nice at some point to "roll back" the independent ProgID
  324.   ### to an earlier version if one exists, and just blowing away the
  325.   ### specified version of the ProgID (and its corresponding CLSID)
  326.   ### another time, though...
  327.  
  328.   ### NOTE: ATL simply blows away the above three keys without the
  329.   ### potential checks that I describe.  Assuming that defines the
  330.   ### "standard" then we have no additional changes necessary.
  331.  
  332. def GetRegisteredServerOption(clsid, optionName):
  333.   """Given a CLSID for a server and option name, return the option value
  334.   """
  335.   keyNameRoot = "CLSID\\%s\\%s" % (str(clsid), str(optionName))
  336.   return _get_string(keyNameRoot)
  337.  
  338.  
  339. def _get(ob, attr, default=None):
  340.   try:
  341.     return getattr(ob, attr)
  342.   except AttributeError:
  343.     pass
  344.   # look down sub-classes
  345.   try:
  346.     bases = ob.__bases__
  347.   except AttributeError:
  348.     # ob is not a class - no probs.
  349.     return default
  350.   for base in bases:
  351.     val = _get(base, attr, None)
  352.     if val is not None:
  353.       return val
  354.   return default
  355.  
  356. def RegisterClasses(*classes, **flags):
  357.   quiet = flags.has_key('quiet') and flags['quiet']
  358.   debugging = flags.has_key('debug') and flags['debug']
  359.   for cls in classes:
  360.     clsid = cls._reg_clsid_
  361.     progID = _get(cls, '_reg_progid_')
  362.     desc = _get(cls, '_reg_desc_', progID)
  363.     spec = _get(cls, '_reg_class_spec_')
  364.     verProgID = _get(cls, '_reg_verprogid_')
  365.     defIcon = _get(cls, '_reg_icon_')
  366.     threadingModel = _get(cls, '_reg_threading_', 'both')
  367.     catids = _get(cls, '_reg_catids_', [])
  368.     options = _get(cls, '_reg_options_', {})
  369.     policySpec = _get(cls, '_reg_policy_spec_')
  370.     clsctx = _get(cls, '_reg_clsctx_')
  371.     tlb_filename = _get(cls, '_reg_typelib_filename_')
  372.     addPyComCat = not _get(cls, '_reg_disable_pycomcat_', 0)
  373.     addnPath = None
  374.     if debugging:
  375.       # If the class has a debugging dispatcher specified, use it, otherwise
  376.       # use our default dispatcher.
  377.       dispatcherSpec = _get(cls, '_reg_debug_dispatcher_spec_')
  378.       if dispatcherSpec is None:
  379.         dispatcherSpec = "win32com.server.dispatcher.DefaultDebugDispatcher"
  380.       # And remember the debugging flag as servers may wish to use it at runtime.
  381.       debuggingDesc = "(for debugging)"
  382.       options['Debugging'] = "1"
  383.     else:
  384.       dispatcherSpec = _get(cls, '_reg_dispatcher_spec_')
  385.       debuggingDesc = ""
  386.       options['Debugging'] = "0"
  387.  
  388.     if spec is None:
  389.       # Always write out path - the policy may or may not need it
  390.       scriptDir = os.path.split(sys.argv[0])[0]
  391.       if not scriptDir: scriptDir = "."
  392.       moduleName = cls.__module__
  393.       if moduleName == '__main__':
  394.         # Use argv[0] to determine the module name.
  395.         try:
  396.          # Use the win32api to find the case-sensitive name
  397.           moduleName = os.path.splitext(win32api.FindFiles(sys.argv[0])[0][8])[0]
  398.         except (IndexError, win32api.error):
  399.           # Can't find the script file - the user must explicitely set the _reg_... attribute.
  400.           raise TypeError, "Can't locate the script hosting the COM object - please set _reg_class_spec_ in your object"
  401.  
  402.       spec = moduleName + "." + cls.__name__
  403.       addnPath = win32api.GetFullPathName(scriptDir)
  404.  
  405.     RegisterServer(clsid, spec, desc, progID, verProgID, defIcon,
  406.                    threadingModel, policySpec, catids, options,
  407.                    addPyComCat, dispatcherSpec, clsctx, addnPath)
  408.     if not quiet:
  409.       print 'Registered:', progID or spec, debuggingDesc
  410.     # Register the typelibrary
  411.     if tlb_filename:
  412.       tlb_filename = os.path.abspath(tlb_filename)
  413.       typelib = pythoncom.LoadTypeLib(tlb_filename)
  414.       pythoncom.RegisterTypeLib(typelib, tlb_filename)
  415.       if not quiet:
  416.         print 'Registered type library:', tlb_filename
  417.   extra = flags.get('finalize_register')
  418.   if extra:
  419.     extra()
  420.  
  421. def UnregisterClasses(*classes, **flags):
  422.   quiet = flags.has_key('quiet') and flags['quiet']
  423.   for cls in classes:
  424.     clsid = cls._reg_clsid_
  425.     progID = _get(cls, '_reg_progid_')
  426.     verProgID = _get(cls, '_reg_verprogid_')
  427.     customKeys = _get(cls, '_reg_remove_keys_')
  428.     unregister_typelib = _get(cls, '_reg_typelib_filename_') is not None
  429.  
  430.     UnregisterServer(clsid, progID, verProgID, customKeys)
  431.     if not quiet:
  432.       print 'Unregistered:', progID or str(clsid)
  433.     if unregister_typelib:
  434.       tlb_guid = _get(cls, "_typelib_guid_")
  435.       if tlb_guid is None:
  436.         # I guess I could load the typelib, but they need the GUID anyway.
  437.         print "Have typelib filename, but no GUID - can't unregister"
  438.       else:
  439.         major, minor = _get(cls, "_typelib_version_", (1,0))
  440.         lcid = _get(cls, "_typelib_lcid_", 0)
  441.         try:
  442.           pythoncom.UnRegisterTypeLib(tlb_guid, major, minor, lcid)
  443.           if not quiet:
  444.             print 'Unregistered type library'
  445.         except pythoncom.com_error:
  446.           pass
  447.  
  448.   extra = flags.get('finalize_unregister')
  449.   if extra:
  450.     extra()
  451. #
  452. # Unregister info is for installers or external uninstallers.
  453. # The WISE installer, for example firstly registers the COM server,
  454. # then queries for the Unregister info, appending it to its
  455. # install log.  Uninstalling the package will the uninstall the server
  456. def UnregisterInfoClasses(*classes, **flags):
  457.   ret = []
  458.   for cls in classes:
  459.     clsid = cls._reg_clsid_
  460.     progID = _get(cls, '_reg_progid_')
  461.     verProgID = _get(cls, '_reg_verprogid_')
  462.     customKeys = _get(cls, '_reg_remove_keys_')
  463.  
  464.     ret = ret + GetUnregisterServerKeys(clsid, progID, verProgID, customKeys)
  465.   return ret
  466.  
  467. def UseCommandLine(*classes, **flags):
  468.   unregisterInfo = '--unregister_info' in sys.argv
  469.   unregister = '--unregister' in sys.argv
  470.   flags['quiet'] = flags.get('quiet',0) or '--quiet' in sys.argv
  471.   flags['debug'] = flags.get('debug',0) or '--debug' in sys.argv
  472.   if unregisterInfo:
  473.     return UnregisterInfoClasses(*classes, **flags)
  474.   if unregister:
  475.     UnregisterClasses(*classes, **flags)
  476.   else:
  477.     RegisterClasses(*classes, **flags)
  478.  
  479.  
  480. def RegisterPyComCategory():
  481.   """ Register the Python COM Server component category.
  482.   """
  483.   regCat = _cat_registrar()
  484.   regCat.RegisterCategories( [ (CATID_PythonCOMServer,
  485.                                 0x0409,
  486.                                 "Python COM Server") ] )
  487.  
  488. try:
  489.   win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT,
  490.                          'Component Categories\\%s' % CATID_PythonCOMServer)
  491. except win32api.error:
  492.   try:
  493.     RegisterPyComCategory()
  494.   except pythoncom.error: # Error with the COM category manager - oh well.
  495.     pass    
  496.  
  497.