selecttlb.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. """Utilities for selecting and enumerating the Type Libraries installed on the system
  2. """
  3. import win32api, win32con, pythoncom
  4. class TypelibSpec:
  5. def __init__(self, clsid, lcid, major, minor, flags=0):
  6. self.clsid = str(clsid)
  7. self.lcid = int(lcid)
  8. # We avoid assuming 'major' or 'minor' are integers - when
  9. # read from the registry there is some confusion about if
  10. # they are base 10 or base 16 (they *should* be base 16, but
  11. # how they are written is beyond our control.)
  12. self.major = major
  13. self.minor = minor
  14. self.dll = None
  15. self.desc = None
  16. self.ver_desc = None
  17. self.flags = flags
  18. # For the SelectList
  19. def __getitem__(self, item):
  20. if item==0:
  21. return self.ver_desc
  22. raise IndexError("Cant index me!")
  23. def __lt__(self, other): # rich-cmp/py3k-friendly version
  24. me = (self.ver_desc or "").lower(), (self.desc or "").lower(), self.major, self.minor
  25. them = (other.ver_desc or "").lower(), (other.desc or "").lower(), other.major, other.minor
  26. return me < them
  27. def __eq__(self, other): # rich-cmp/py3k-friendly version
  28. return ((self.ver_desc or "").lower() == (other.ver_desc or "").lower() and
  29. (self.desc or "").lower() == (other.desc or "").lower() and
  30. self.major == other.major and
  31. self.minor == other.minor)
  32. def Resolve(self):
  33. if self.dll is None:
  34. return 0
  35. tlb = pythoncom.LoadTypeLib(self.dll)
  36. self.FromTypelib(tlb, None)
  37. return 1
  38. def FromTypelib(self, typelib, dllName = None):
  39. la = typelib.GetLibAttr()
  40. self.clsid = str(la[0])
  41. self.lcid = la[1]
  42. self.major = la[3]
  43. self.minor = la[4]
  44. if dllName:
  45. self.dll = dllName
  46. def EnumKeys(root):
  47. index = 0
  48. ret = []
  49. while 1:
  50. try:
  51. item = win32api.RegEnumKey(root, index)
  52. except win32api.error:
  53. break
  54. try:
  55. # Note this doesn't handle REG_EXPAND_SZ, but the implementation
  56. # here doesn't need to - that is handled as the data is read.
  57. val = win32api.RegQueryValue(root, item)
  58. except win32api.error:
  59. val = "" # code using this assumes a string.
  60. ret.append((item, val))
  61. index = index + 1
  62. return ret
  63. FLAG_RESTRICTED=1
  64. FLAG_CONTROL=2
  65. FLAG_HIDDEN=4
  66. def EnumTlbs(excludeFlags = 0):
  67. """Return a list of TypelibSpec objects, one for each registered library.
  68. """
  69. key = win32api.RegOpenKey(win32con.HKEY_CLASSES_ROOT, "Typelib")
  70. iids = EnumKeys(key)
  71. results = []
  72. for iid, crap in iids:
  73. try:
  74. key2 = win32api.RegOpenKey(key, str(iid))
  75. except win32api.error:
  76. # A few good reasons for this, including "access denied".
  77. continue
  78. for version, tlbdesc in EnumKeys(key2):
  79. major_minor = version.split('.', 1)
  80. if len(major_minor) < 2:
  81. major_minor.append('0')
  82. # For some reason, this code used to assume the values were hex.
  83. # This seems to not be true - particularly for CDO 1.21
  84. # *sigh* - it appears there are no rules here at all, so when we need
  85. # to know the info, we must load the tlb by filename and request it.
  86. # The Resolve() method on the TypelibSpec does this.
  87. # For this reason, keep the version numbers as strings - that
  88. # way we can't be wrong! Let code that really needs an int to work
  89. # out what to do. FWIW, http://support.microsoft.com/kb/816970 is
  90. # pretty clear that they *should* be hex.
  91. major = major_minor[0]
  92. minor = major_minor[1]
  93. key3 = win32api.RegOpenKey(key2, str(version))
  94. try:
  95. # The "FLAGS" are at this point
  96. flags = int(win32api.RegQueryValue(key3, "FLAGS"))
  97. except (win32api.error, ValueError):
  98. flags = 0
  99. if flags & excludeFlags==0:
  100. for lcid, crap in EnumKeys(key3):
  101. try:
  102. lcid = int(lcid)
  103. except ValueError: # not an LCID entry
  104. continue
  105. # Check for both "{lcid}\win32" and "{lcid}\win64" keys.
  106. try:
  107. key4 = win32api.RegOpenKey(key3, "%s\\win32" % (lcid,))
  108. except win32api.error:
  109. try:
  110. key4 = win32api.RegOpenKey(key3, "%s\\win64" % (lcid,))
  111. except win32api.error:
  112. continue
  113. try:
  114. dll, typ = win32api.RegQueryValueEx(key4, None)
  115. if typ==win32con.REG_EXPAND_SZ:
  116. dll = win32api.ExpandEnvironmentStrings(dll)
  117. except win32api.error:
  118. dll = None
  119. spec = TypelibSpec(iid, lcid, major, minor, flags)
  120. spec.dll = dll
  121. spec.desc = tlbdesc
  122. spec.ver_desc = tlbdesc + " (" + version + ")"
  123. results.append(spec)
  124. return results
  125. def FindTlbsWithDescription(desc):
  126. """Find all installed type libraries with the specified description
  127. """
  128. ret = []
  129. items = EnumTlbs()
  130. for item in items:
  131. if item.desc==desc:
  132. ret.append(item)
  133. return ret
  134. def SelectTlb(title="Select Library", excludeFlags = 0):
  135. """Display a list of all the type libraries, and select one. Returns None if cancelled
  136. """
  137. import pywin.dialogs.list
  138. items = EnumTlbs(excludeFlags)
  139. # fixup versions - we assume hex (see __init__ above)
  140. for i in items:
  141. i.major = int(i.major, 16)
  142. i.minor = int(i.minor, 16)
  143. items.sort()
  144. rc = pywin.dialogs.list.SelectFromLists(title, items, ["Type Library"])
  145. if rc is None:
  146. return None
  147. return items[rc]
  148. # Test code.
  149. if __name__=='__main__':
  150. print(SelectTlb().__dict__)