0001 """Utilities for registering objects. 0002 0003 This module contains utility functions to register Python objects as 0004 valid COM Servers. The RegisterServer function provides all information 0005 necessary to allow the COM framework to respond to a request for a COM object, 0006 construct the necessary Python object, and dispatch COM events. 0007 0008 """ 0009 import sys 0010 import win32api 0011 import win32con 0012 import pythoncom 0013 import winerror 0014 import os 0015 0016 CATID_PythonCOMServer = "{B3EF80D0-68E2-11D0-A689-00C04FD658FF}" 0017 0018 def _set_subkeys(keyName, valueDict, base=win32con.HKEY_CLASSES_ROOT): 0019 hkey = win32api.RegCreateKey(base, keyName) 0020 try: 0021 for key, value in valueDict.items(): 0022 win32api.RegSetValueEx(hkey, key, None, win32con.REG_SZ, value) 0023 finally: 0024 win32api.RegCloseKey(hkey) 0025 0026 def _set_string(path, value, base=win32con.HKEY_CLASSES_ROOT): 0027 "Set a string value in the registry." 0028 0029 win32api.RegSetValue(base, 0030 path, 0031 win32con.REG_SZ, 0032 value) 0033 0034 def _get_string(path, base=win32con.HKEY_CLASSES_ROOT): 0035 "Get a string value from the registry." 0036 0037 try: 0038 return win32api.RegQueryValue(base, path) 0039 except win32api.error: 0040 return None 0041 0042 def _remove_key(path, base=win32con.HKEY_CLASSES_ROOT): 0043 "Remove a string from the registry." 0044 0045 try: 0046 win32api.RegDeleteKey(base, path) 0047 except win32api.error, (code, fn, msg): 0048 if code != winerror.ERROR_FILE_NOT_FOUND: 0049 raise win32api.error, (code, fn, msg) 0050 0051 def recurse_delete_key(path, base=win32con.HKEY_CLASSES_ROOT): 0052 """Recursively delete registry keys. 0053 0054 This is needed since you can't blast a key when subkeys exist. 0055 """ 0056 try: 0057 h = win32api.RegOpenKey(base, path) 0058 except win32api.error, (code, fn, msg): 0059 if code != winerror.ERROR_FILE_NOT_FOUND: 0060 raise win32api.error, (code, fn, msg) 0061 else: 0062 # parent key found and opened successfully. do some work, making sure 0063 # to always close the thing (error or no). 0064 try: 0065 # remove all of the subkeys 0066 while 1: 0067 try: 0068 subkeyname = win32api.RegEnumKey(h, 0) 0069 except win32api.error, (code, fn, msg): 0070 if code != winerror.ERROR_NO_MORE_ITEMS: 0071 raise win32api.error, (code, fn, msg) 0072 break 0073 recurse_delete_key(path + '\\' + subkeyname, base) 0074 0075 # remove the parent key 0076 _remove_key(path, base) 0077 finally: 0078 win32api.RegCloseKey(h) 0079 0080 def _cat_registrar(): 0081 return pythoncom.CoCreateInstance( 0082 pythoncom.CLSID_StdComponentCategoriesMgr, 0083 None, 0084 pythoncom.CLSCTX_INPROC_SERVER, 0085 pythoncom.IID_ICatRegister 0086 ) 0087 0088 def _find_localserver_exe(mustfind): 0089 if not sys.platform.startswith("win32"): 0090 return sys.executable 0091 if pythoncom.__file__.find("_d") < 0: 0092 exeBaseName = "pythonw.exe" 0093 else: 0094 exeBaseName = "pythonw_d.exe" 0095 # First see if in the same directory as this .EXE 0096 exeName = os.path.join( os.path.split(sys.executable)[0], exeBaseName ) 0097 if not os.path.exists(exeName): 0098 # See if in our sys.prefix directory 0099 exeName = os.path.join( sys.prefix, exeBaseName ) 0100 if not os.path.exists(exeName): 0101 # See if in our sys.prefix/pcbuild directory (for developers) 0102 exeName = os.path.join( sys.prefix, "PCbuild", exeBaseName ) 0103 if not os.path.exists(exeName): 0104 # See if the registry has some info. 0105 try: 0106 key = "SOFTWARE\\Python\\PythonCore\\%s\\InstallPath" % sys.winver 0107 path = win32api.RegQueryValue( win32con.HKEY_LOCAL_MACHINE, key ) 0108 exeName = os.path.join( path, exeBaseName ) 0109 except (AttributeError,win32api.error): 0110 pass 0111 if not os.path.exists(exeName): 0112 if mustfind: 0113 raise RuntimeError, "Can not locate the program '%s'" % exeBaseName 0114 return None 0115 return exeName 0116 0117 def _find_localserver_module(): 0118 import win32com.server 0119 path = win32com.server.__path__[0] 0120 baseName = "localserver" 0121 pyfile = os.path.join(path, baseName + ".py") 0122 try: 0123 os.stat(pyfile) 0124 except os.error: 0125 # See if we have a compiled extension 0126 if __debug__: 0127 ext = ".pyc" 0128 else: 0129 ext = ".pyo" 0130 pyfile = os.path.join(path, baseName + ext) 0131 try: 0132 os.stat(pyfile) 0133 except os.error: 0134 raise RuntimeError, "Can not locate the Python module 'win32com.server.%s'" % baseName 0135 return pyfile 0136 0137 def RegisterServer(clsid, 0138 pythonInstString=None, 0139 desc=None, 0140 progID=None, verProgID=None, 0141 defIcon=None, 0142 threadingModel="both", 0143 policy=None, 0144 catids=[], other={}, 0145 addPyComCat=None, 0146 dispatcher = None, 0147 clsctx = None, 0148 addnPath = None, 0149 ): 0150 """Registers a Python object as a COM Server. This enters almost all necessary 0151 information in the system registry, allowing COM to use the object. 0152 0153 clsid -- The (unique) CLSID of the server. 0154 pythonInstString -- A string holding the instance name that will be created 0155 whenever COM requests a new object. 0156 desc -- The description of the COM object. 0157 progID -- The user name of this object (eg, Word.Document) 0158 verProgId -- The user name of this version's implementation (eg Word.6.Document) 0159 defIcon -- The default icon for the object. 0160 threadingModel -- The threading model this object supports. 0161 policy -- The policy to use when creating this object. 0162 catids -- A list of category ID's this object belongs in. 0163 other -- A dictionary of extra items to be registered. 0164 addPyComCat -- A flag indicating if the object should be added to the list 0165 of Python servers installed on the machine. If None (the default) 0166 then it will be registered when running from python source, but 0167 not registered if running in a frozen environment. 0168 dispatcher -- The dispatcher to use when creating this object. 0169 clsctx -- One of the CLSCTX_* constants. 0170 addnPath -- An additional path the COM framework will add to sys.path 0171 before attempting to create the object. 0172 """ 0173 0174 0175 ### backwards-compat check 0176 ### Certain policies do not require a "class name", just the policy itself. 0177 if not pythonInstString and not policy: 0178 raise TypeError, 'You must specify either the Python Class or Python Policy which implement the COM object.' 0179 0180 keyNameRoot = "CLSID\\%s" % str(clsid) 0181 _set_string(keyNameRoot, desc) 0182 0183 # Also register as an "Application" so DCOM etc all see us. 0184 _set_string("AppID\\%s" % clsid, progID) 0185 # Depending on contexts requested, register the specified server type. 0186 # Set default clsctx. 0187 if not clsctx: 0188 clsctx = pythoncom.CLSCTX_INPROC_SERVER | pythoncom.CLSCTX_LOCAL_SERVER 0189 # And if we are frozen, ignore the ones that don't make sense in this 0190 # context. 0191 if pythoncom.frozen: 0192 assert sys.frozen, "pythoncom is frozen, but sys.frozen is not set - don't know the context!" 0193 if sys.frozen == "dll": 0194 clsctx = clsctx & pythoncom.CLSCTX_INPROC_SERVER 0195 else: 0196 clsctx = clsctx & pythoncom.CLSCTX_LOCAL_SERVER 0197 # Now setup based on the clsctx left over. 0198 if clsctx & pythoncom.CLSCTX_INPROC_SERVER: 0199 # get the module to use for registration. 0200 # nod to Gordon's installer - if sys.frozen and sys.frozendllhandle 0201 # exist, then we are being registered via a DLL - use this DLL as the 0202 # file name. 0203 if pythoncom.frozen: 0204 if hasattr(sys, "frozendllhandle"): 0205 dllName = win32api.GetModuleFileName(sys.frozendllhandle) 0206 else: 0207 raise RuntimeError, "We appear to have a frozen DLL, but I don't know the DLL to use" 0208 else: 0209 # Normal case - running from .py file, so register pythoncom's DLL. 0210 dllName = os.path.basename(pythoncom.__file__) 0211 0212 _set_subkeys(keyNameRoot + "\\InprocServer32", 0213 { None : dllName, 0214 "ThreadingModel" : threadingModel, 0215 }) 0216 else: # Remove any old InProcServer32 registrations 0217 _remove_key(keyNameRoot + "\\InprocServer32") 0218 0219 if clsctx & pythoncom.CLSCTX_LOCAL_SERVER: 0220 if pythoncom.frozen: 0221 # If we are frozen, we write "{exe} /Automate", just 0222 # like "normal" .EXEs do 0223 exeName = win32api.GetShortPathName(sys.executable) 0224 command = '%s /Automate' % (exeName,) 0225 else: 0226 # Running from .py sources - we need to write 0227 # 'python.exe win32com\server\localserver.py {clsid}" 0228 exeName = _find_localserver_exe(1) 0229 exeName = win32api.GetShortPathName(exeName) 0230 pyfile = _find_localserver_module() 0231 command = '%s "%s" %s' % (exeName, pyfile, str(clsid)) 0232 _set_string(keyNameRoot + '\\LocalServer32', command) 0233 else: # Remove any old LocalServer32 registrations 0234 _remove_key(keyNameRoot + "\\LocalServer32") 0235 0236 if pythonInstString: 0237 _set_string(keyNameRoot + '\\PythonCOM', pythonInstString) 0238 else: 0239 _remove_key(keyNameRoot + '\\PythonCOM') 0240 if policy: 0241 _set_string(keyNameRoot + '\\PythonCOMPolicy', policy) 0242 else: 0243 _remove_key(keyNameRoot + '\\PythonCOMPolicy') 0244 0245 if dispatcher: 0246 _set_string(keyNameRoot + '\\PythonCOMDispatcher', dispatcher) 0247 else: 0248 _remove_key(keyNameRoot + '\\PythonCOMDispatcher') 0249 0250 if defIcon: 0251 _set_string(keyNameRoot + '\\DefaultIcon', defIcon) 0252 0253 if addnPath: 0254 _set_string(keyNameRoot + "\\PythonCOMPath", addnPath) 0255 else: 0256 _remove_key(keyNameRoot + "\\PythonCOMPath") 0257 0258 if addPyComCat is None: 0259 addPyComCat = pythoncom.frozen == 0 0260 if addPyComCat: 0261 catids = catids + [ CATID_PythonCOMServer ] 0262 0263 # Set up the implemented categories 0264 if catids: 0265 regCat = _cat_registrar() 0266 regCat.RegisterClassImplCategories(clsid, catids) 0267 0268 # set up any other reg values they might have 0269 if other: 0270 for key, value in other.items(): 0271 _set_string(keyNameRoot + '\\' + key, value) 0272 0273 if progID: 0274 # set the progID as the most specific that was given to us 0275 if verProgID: 0276 _set_string(keyNameRoot + '\\ProgID', verProgID) 0277 else: 0278 _set_string(keyNameRoot + '\\ProgID', progID) 0279 0280 # Set up the root entries - version independent. 0281 if desc: 0282 _set_string(progID, desc) 0283 _set_string(progID + '\\CLSID', str(clsid)) 0284 0285 # Set up the root entries - version dependent. 0286 if verProgID: 0287 # point from independent to the current version 0288 _set_string(progID + '\\CurVer', verProgID) 0289 0290 # point to the version-independent one 0291 _set_string(keyNameRoot + '\\VersionIndependentProgID', progID) 0292 0293 # set up the versioned progID 0294 if desc: 0295 _set_string(verProgID, desc) 0296 _set_string(verProgID + '\\CLSID', str(clsid)) 0297 0298 def GetUnregisterServerKeys(clsid, progID=None, verProgID=None, customKeys = None): 0299 """Given a server, return a list of of ("key", root), which are keys recursively 0300 and uncondtionally deleted at unregister or uninstall time. 0301 """ 0302 # remove the main CLSID registration 0303 ret = [("CLSID\\%s" % str(clsid), win32con.HKEY_CLASSES_ROOT)] 0304 # remove the versioned ProgID registration 0305 if verProgID: 0306 ret.append((verProgID, win32con.HKEY_CLASSES_ROOT)) 0307 # blow away the independent ProgID. we can't leave it since we just 0308 # torched the class. 0309 ### could potentially check the CLSID... ? 0310 if progID: 0311 ret.append((progID, win32con.HKEY_CLASSES_ROOT)) 0312 # The DCOM config tool may write settings to the AppID key for our CLSID 0313 ret.append( ("AppID\\%s" % str(clsid), win32con.HKEY_CLASSES_ROOT) ) 0314 # Any custom keys? 0315 if customKeys: 0316 ret = ret + customKeys 0317 0318 return ret 0319 0320 0321 def UnregisterServer(clsid, progID=None, verProgID=None, customKeys = None): 0322 """Unregisters a Python COM server.""" 0323 0324 for args in GetUnregisterServerKeys(clsid, progID, verProgID, customKeys ): 0325 recurse_delete_key(*args) 0326 0327 ### it might be nice at some point to "roll back" the independent ProgID 0328 ### to an earlier version if one exists, and just blowing away the 0329 ### specified version of the ProgID (and its corresponding CLSID) 0330 ### another time, though... 0331 0332 ### NOTE: ATL simply blows away the above three keys without the 0333 ### potential checks that I describe. Assuming that defines the 0334 ### "standard" then we have no additional changes necessary. 0335 0336 def GetRegisteredServerOption(clsid, optionName): 0337 """Given a CLSID for a server and option name, return the option value 0338 """ 0339 keyNameRoot = "CLSID\\%s\\%s" % (str(clsid), str(optionName)) 0340 return _get_string(keyNameRoot) 0341 0342 0343 def _get(ob, attr, default=None): 0344 try: 0345 return getattr(ob, attr) 0346 except AttributeError: 0347 pass 0348 # look down sub-classes 0349 try: 0350 bases = ob.__bases__ 0351 except AttributeError: 0352 # ob is not a class - no probs. 0353 return default 0354 for base in bases: 0355 val = _get(base, attr, None) 0356 if val is not None: 0357 return val 0358 return default 0359 0360 def RegisterClasses(*classes, **flags): 0361 quiet = flags.has_key('quiet') and flags['quiet'] 0362 debugging = flags.has_key('debug') and flags['debug'] 0363 for cls in classes: 0364 clsid = cls._reg_clsid_ 0365 progID = _get(cls, '_reg_progid_') 0366 desc = _get(cls, '_reg_desc_', progID) 0367 spec = _get(cls, '_reg_class_spec_') 0368 verProgID = _get(cls, '_reg_verprogid_') 0369 defIcon = _get(cls, '_reg_icon_') 0370 threadingModel = _get(cls, '_reg_threading_', 'both') 0371 catids = _get(cls, '_reg_catids_', []) 0372 options = _get(cls, '_reg_options_', {}) 0373 policySpec = _get(cls, '_reg_policy_spec_') 0374 clsctx = _get(cls, '_reg_clsctx_') 0375 tlb_filename = _get(cls, '_reg_typelib_filename_') 0376 # default to being a COM category only when not frozen. 0377 addPyComCat = not _get(cls, '_reg_disable_pycomcat_', pythoncom.frozen!=0) 0378 addnPath = None 0379 if debugging: 0380 # If the class has a debugging dispatcher specified, use it, otherwise 0381 # use our default dispatcher. 0382 dispatcherSpec = _get(cls, '_reg_debug_dispatcher_spec_') 0383 if dispatcherSpec is None: 0384 dispatcherSpec = "win32com.server.dispatcher.DefaultDebugDispatcher" 0385 # And remember the debugging flag as servers may wish to use it at runtime. 0386 debuggingDesc = "(for debugging)" 0387 options['Debugging'] = "1" 0388 else: 0389 dispatcherSpec = _get(cls, '_reg_dispatcher_spec_') 0390 debuggingDesc = "" 0391 options['Debugging'] = "0" 0392 0393 if spec is None: 0394 moduleName = cls.__module__ 0395 if moduleName == '__main__': 0396 # Use argv[0] to determine the module name. 0397 try: 0398 # Use the win32api to find the case-sensitive name 0399 moduleName = os.path.splitext(win32api.FindFiles(sys.argv[0])[0][8])[0] 0400 except (IndexError, win32api.error): 0401 # Can't find the script file - the user must explicitely set the _reg_... attribute. 0402 raise TypeError, "Can't locate the script hosting the COM object - please set _reg_class_spec_ in your object" 0403 0404 spec = moduleName + "." + cls.__name__ 0405 # Frozen apps don't need their directory on sys.path 0406 if not pythoncom.frozen: 0407 scriptDir = os.path.split(sys.argv[0])[0] 0408 if not scriptDir: scriptDir = "." 0409 addnPath = win32api.GetFullPathName(scriptDir) 0410 0411 RegisterServer(clsid, spec, desc, progID, verProgID, defIcon, 0412 threadingModel, policySpec, catids, options, 0413 addPyComCat, dispatcherSpec, clsctx, addnPath) 0414 if not quiet: 0415 print 'Registered:', progID or spec, debuggingDesc 0416 # Register the typelibrary 0417 if tlb_filename: 0418 tlb_filename = os.path.abspath(tlb_filename) 0419 typelib = pythoncom.LoadTypeLib(tlb_filename) 0420 pythoncom.RegisterTypeLib(typelib, tlb_filename) 0421 if not quiet: 0422 print 'Registered type library:', tlb_filename 0423 extra = flags.get('finalize_register') 0424 if extra: 0425 extra() 0426 0427 def UnregisterClasses(*classes, **flags): 0428 quiet = flags.has_key('quiet') and flags['quiet'] 0429 for cls in classes: 0430 clsid = cls._reg_clsid_ 0431 progID = _get(cls, '_reg_progid_') 0432 verProgID = _get(cls, '_reg_verprogid_') 0433 customKeys = _get(cls, '_reg_remove_keys_') 0434 unregister_typelib = _get(cls, '_reg_typelib_filename_') is not None 0435 0436 UnregisterServer(clsid, progID, verProgID, customKeys) 0437 if not quiet: 0438 print 'Unregistered:', progID or str(clsid) 0439 if unregister_typelib: 0440 tlb_guid = _get(cls, "_typelib_guid_") 0441 if tlb_guid is None: 0442 # I guess I could load the typelib, but they need the GUID anyway. 0443 print "Have typelib filename, but no GUID - can't unregister" 0444 else: 0445 major, minor = _get(cls, "_typelib_version_", (1,0)) 0446 lcid = _get(cls, "_typelib_lcid_", 0) 0447 try: 0448 pythoncom.UnRegisterTypeLib(tlb_guid, major, minor, lcid) 0449 if not quiet: 0450 print 'Unregistered type library' 0451 except pythoncom.com_error: 0452 pass 0453 0454 extra = flags.get('finalize_unregister') 0455 if extra: 0456 extra() 0457 # 0458 # Unregister info is for installers or external uninstallers. 0459 # The WISE installer, for example firstly registers the COM server, 0460 # then queries for the Unregister info, appending it to its 0461 # install log. Uninstalling the package will the uninstall the server 0462 def UnregisterInfoClasses(*classes, **flags): 0463 ret = [] 0464 for cls in classes: 0465 clsid = cls._reg_clsid_ 0466 progID = _get(cls, '_reg_progid_') 0467 verProgID = _get(cls, '_reg_verprogid_') 0468 customKeys = _get(cls, '_reg_remove_keys_') 0469 0470 ret = ret + GetUnregisterServerKeys(clsid, progID, verProgID, customKeys) 0471 return ret 0472 0473 def UseCommandLine(*classes, **flags): 0474 unregisterInfo = '--unregister_info' in sys.argv 0475 unregister = '--unregister' in sys.argv 0476 flags['quiet'] = flags.get('quiet',0) or '--quiet' in sys.argv 0477 flags['debug'] = flags.get('debug',0) or '--debug' in sys.argv 0478 if unregisterInfo: 0479 return UnregisterInfoClasses(*classes, **flags) 0480 if unregister: 0481 UnregisterClasses(*classes, **flags) 0482 else: 0483 RegisterClasses(*classes, **flags) 0484 0485 0486 def RegisterPyComCategory(): 0487 """ Register the Python COM Server component category. 0488 """ 0489 regCat = _cat_registrar() 0490 regCat.RegisterCategories( [ (CATID_PythonCOMServer, 0491 0x0409, 0492 "Python COM Server") ] ) 0493 0494 if not pythoncom.frozen: 0495 try: 0496 win32api.RegQueryValue(win32con.HKEY_CLASSES_ROOT, 0497 'Component Categories\\%s' % CATID_PythonCOMServer) 0498 except win32api.error: 0499 try: 0500 RegisterPyComCategory() 0501 except pythoncom.error: # Error with the COM category manager - oh well. 0502 pass 0503 0504
Generated by PyXR 0.9.4