win32gui_struct.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. # This is a work in progress - see Demos/win32gui_menu.py
  2. # win32gui_struct.py - helpers for working with various win32gui structures.
  3. # As win32gui is "light-weight", it does not define objects for all possible
  4. # win32 structures - in general, "buffer" objects are passed around - it is
  5. # the callers responsibility to pack the buffer in the correct format.
  6. #
  7. # This module defines some helpers for the commonly used structures.
  8. #
  9. # In general, each structure has 3 functions:
  10. #
  11. # buffer, extras = PackSTRUCTURE(items, ...)
  12. # item, ... = UnpackSTRUCTURE(buffer)
  13. # buffer, extras = EmtpySTRUCTURE(...)
  14. #
  15. # 'extras' is always items that must be held along with the buffer, as the
  16. # buffer refers to these object's memory.
  17. # For structures that support a 'mask', this mask is hidden from the user - if
  18. # 'None' is passed, the mask flag will not be set, or on return, None will
  19. # be returned for the value if the mask is not set.
  20. #
  21. # NOTE: I considered making these structures look like real classes, and
  22. # support 'attributes' etc - however, ctypes already has a good structure
  23. # mechanism - I think it makes more sense to support ctype structures
  24. # at the win32gui level, then there will be no need for this module at all.
  25. # XXX - the above makes sense in terms of what is built and passed to
  26. # win32gui (ie, the Pack* functions) - but doesn't make as much sense for
  27. # the Unpack* functions, where the aim is user convenience.
  28. import sys
  29. import win32gui
  30. import win32con
  31. import struct
  32. import array
  33. import commctrl
  34. import pywintypes
  35. is64bit = "64 bit" in sys.version
  36. try:
  37. from collections import namedtuple
  38. def _MakeResult(names_str, values):
  39. names = names_str.split()
  40. nt = namedtuple(names[0], names[1:])
  41. return nt(*values)
  42. except ImportError:
  43. # no namedtuple support - just return the values as a normal tuple.
  44. def _MakeResult(names_str, values):
  45. return values
  46. _nmhdr_fmt = "PPi"
  47. if is64bit:
  48. # When the item past the NMHDR gets aligned (eg, when it is a struct)
  49. # we need this many bytes padding.
  50. _nmhdr_align_padding = "xxxx"
  51. else:
  52. _nmhdr_align_padding = ""
  53. # Encode a string suitable for passing in a win32gui related structure
  54. # If win32gui is built with UNICODE defined (ie, py3k), then functions
  55. # like InsertMenuItem are actually calling InsertMenuItemW etc, so all
  56. # strings will need to be unicode.
  57. if win32gui.UNICODE:
  58. def _make_text_buffer(text):
  59. # XXX - at this stage win32gui.UNICODE is only True in py3k,
  60. # and in py3k is makes sense to reject bytes.
  61. if not isinstance(text, str):
  62. raise TypeError('MENUITEMINFO text must be unicode')
  63. data = (text+'\0').encode("utf-16le")
  64. return array.array("b", data)
  65. else:
  66. def _make_text_buffer(text):
  67. if isinstance(text, str):
  68. text = text.encode("mbcs")
  69. return array.array("b", text+'\0')
  70. # make an 'empty' buffer, ready for filling with cch characters.
  71. def _make_empty_text_buffer(cch):
  72. return _make_text_buffer("\0" * cch)
  73. if sys.version_info < (3,0):
  74. def _make_memory(ob):
  75. return str(buffer(ob))
  76. def _make_bytes(sval):
  77. return sval
  78. else:
  79. def _make_memory(ob):
  80. return bytes(memoryview(ob))
  81. def _make_bytes(sval):
  82. return sval.encode('ascii')
  83. # Generic WM_NOTIFY unpacking
  84. def UnpackWMNOTIFY(lparam):
  85. format = "PPi"
  86. buf = win32gui.PyGetMemory(lparam, struct.calcsize(format))
  87. return _MakeResult("WMNOTIFY hwndFrom idFrom code", struct.unpack(format, buf))
  88. def UnpackNMITEMACTIVATE(lparam):
  89. format = _nmhdr_fmt + _nmhdr_align_padding
  90. if is64bit:
  91. # the struct module doesn't handle this correctly as some of the items
  92. # are actually structs in structs, which get individually aligned.
  93. format = format + "iiiiiiixxxxP"
  94. else:
  95. format = format + "iiiiiiiP"
  96. buf = win32gui.PyMakeBuffer(struct.calcsize(format), lparam)
  97. return _MakeResult("NMITEMACTIVATE hwndFrom idFrom code iItem iSubItem uNewState uOldState uChanged actionx actiony lParam",
  98. struct.unpack(format, buf))
  99. # MENUITEMINFO struct
  100. # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/Resources/Menus/MenuReference/MenuStructures/MENUITEMINFO.asp
  101. # We use the struct module to pack and unpack strings as MENUITEMINFO
  102. # structures. We also have special handling for the 'fMask' item in that
  103. # structure to avoid the caller needing to explicitly check validity
  104. # (None is used if the mask excludes/should exclude the value)
  105. _menuiteminfo_fmt = '5i5PiP'
  106. def PackMENUITEMINFO(fType=None, fState=None, wID=None, hSubMenu=None,
  107. hbmpChecked=None, hbmpUnchecked=None, dwItemData=None,
  108. text=None, hbmpItem=None, dwTypeData=None):
  109. # 'extras' are objects the caller must keep a reference to (as their
  110. # memory is used) for the lifetime of the INFO item.
  111. extras = []
  112. # ack - dwItemData and dwTypeData were confused for a while...
  113. assert dwItemData is None or dwTypeData is None, \
  114. "sorry - these were confused - you probably want dwItemData"
  115. # if we are a long way past 209, then we can nuke the above...
  116. if dwTypeData is not None:
  117. import warnings
  118. warnings.warn("PackMENUITEMINFO: please use dwItemData instead of dwTypeData")
  119. if dwItemData is None:
  120. dwItemData = dwTypeData or 0
  121. fMask = 0
  122. if fType is None: fType = 0
  123. else: fMask |= win32con.MIIM_FTYPE
  124. if fState is None: fState = 0
  125. else: fMask |= win32con.MIIM_STATE
  126. if wID is None: wID = 0
  127. else: fMask |= win32con.MIIM_ID
  128. if hSubMenu is None: hSubMenu = 0
  129. else: fMask |= win32con.MIIM_SUBMENU
  130. if hbmpChecked is None:
  131. assert hbmpUnchecked is None, \
  132. "neither or both checkmark bmps must be given"
  133. hbmpChecked = hbmpUnchecked = 0
  134. else:
  135. assert hbmpUnchecked is not None, \
  136. "neither or both checkmark bmps must be given"
  137. fMask |= win32con.MIIM_CHECKMARKS
  138. if dwItemData is None: dwItemData = 0
  139. else: fMask |= win32con.MIIM_DATA
  140. if hbmpItem is None: hbmpItem = 0
  141. else: fMask |= win32con.MIIM_BITMAP
  142. if text is not None:
  143. fMask |= win32con.MIIM_STRING
  144. str_buf = _make_text_buffer(text)
  145. cch = len(text)
  146. # We are taking address of strbuf - it must not die until windows
  147. # has finished with our structure.
  148. lptext = str_buf.buffer_info()[0]
  149. extras.append(str_buf)
  150. else:
  151. lptext = 0
  152. cch = 0
  153. # Create the struct.
  154. # 'P' format does not accept PyHANDLE's !
  155. item = struct.pack(
  156. _menuiteminfo_fmt,
  157. struct.calcsize(_menuiteminfo_fmt), # cbSize
  158. fMask,
  159. fType,
  160. fState,
  161. wID,
  162. int(hSubMenu),
  163. int(hbmpChecked),
  164. int(hbmpUnchecked),
  165. dwItemData,
  166. lptext,
  167. cch,
  168. int(hbmpItem)
  169. )
  170. # Now copy the string to a writable buffer, so that the result
  171. # could be passed to a 'Get' function
  172. return array.array("b", item), extras
  173. def UnpackMENUITEMINFO(s):
  174. (cb,
  175. fMask,
  176. fType,
  177. fState,
  178. wID,
  179. hSubMenu,
  180. hbmpChecked,
  181. hbmpUnchecked,
  182. dwItemData,
  183. lptext,
  184. cch,
  185. hbmpItem) = struct.unpack(_menuiteminfo_fmt, s)
  186. assert cb==len(s)
  187. if fMask & win32con.MIIM_FTYPE==0: fType = None
  188. if fMask & win32con.MIIM_STATE==0: fState = None
  189. if fMask & win32con.MIIM_ID==0: wID = None
  190. if fMask & win32con.MIIM_SUBMENU==0: hSubMenu = None
  191. if fMask & win32con.MIIM_CHECKMARKS==0: hbmpChecked = hbmpUnchecked = None
  192. if fMask & win32con.MIIM_DATA==0: dwItemData = None
  193. if fMask & win32con.MIIM_BITMAP==0: hbmpItem = None
  194. if fMask & win32con.MIIM_STRING:
  195. text = win32gui.PyGetString(lptext, cch)
  196. else:
  197. text = None
  198. return _MakeResult("MENUITEMINFO fType fState wID hSubMenu hbmpChecked "
  199. "hbmpUnchecked dwItemData text hbmpItem",
  200. (fType, fState, wID, hSubMenu, hbmpChecked, hbmpUnchecked, \
  201. dwItemData, text, hbmpItem))
  202. def EmptyMENUITEMINFO(mask = None, text_buf_size=512):
  203. # text_buf_size is number of *characters* - not necessarily no of bytes.
  204. extra = []
  205. if mask is None:
  206. mask = win32con.MIIM_BITMAP | win32con.MIIM_CHECKMARKS | \
  207. win32con.MIIM_DATA | win32con.MIIM_FTYPE | \
  208. win32con.MIIM_ID | win32con.MIIM_STATE | \
  209. win32con.MIIM_STRING | win32con.MIIM_SUBMENU
  210. # Note: No MIIM_TYPE - this screws win2k/98.
  211. if mask & win32con.MIIM_STRING:
  212. text_buffer = _make_empty_text_buffer(text_buf_size)
  213. extra.append(text_buffer)
  214. text_addr, _ = text_buffer.buffer_info()
  215. else:
  216. text_addr = text_buf_size = 0
  217. # Now copy the string to a writable buffer, so that the result
  218. # could be passed to a 'Get' function
  219. buf = struct.pack(
  220. _menuiteminfo_fmt,
  221. struct.calcsize(_menuiteminfo_fmt), # cbSize
  222. mask,
  223. 0, #fType,
  224. 0, #fState,
  225. 0, #wID,
  226. 0, #hSubMenu,
  227. 0, #hbmpChecked,
  228. 0, #hbmpUnchecked,
  229. 0, #dwItemData,
  230. text_addr,
  231. text_buf_size,
  232. 0, #hbmpItem
  233. )
  234. return array.array("b", buf), extra
  235. # MENUINFO struct
  236. _menuinfo_fmt = 'iiiiPiP'
  237. def PackMENUINFO(dwStyle = None, cyMax = None,
  238. hbrBack = None, dwContextHelpID = None, dwMenuData = None,
  239. fMask = 0):
  240. if dwStyle is None: dwStyle = 0
  241. else: fMask |= win32con.MIM_STYLE
  242. if cyMax is None: cyMax = 0
  243. else: fMask |= win32con.MIM_MAXHEIGHT
  244. if hbrBack is None: hbrBack = 0
  245. else: fMask |= win32con.MIM_BACKGROUND
  246. if dwContextHelpID is None: dwContextHelpID = 0
  247. else: fMask |= win32con.MIM_HELPID
  248. if dwMenuData is None: dwMenuData = 0
  249. else: fMask |= win32con.MIM_MENUDATA
  250. # Create the struct.
  251. item = struct.pack(
  252. _menuinfo_fmt,
  253. struct.calcsize(_menuinfo_fmt), # cbSize
  254. fMask,
  255. dwStyle,
  256. cyMax,
  257. hbrBack,
  258. dwContextHelpID,
  259. dwMenuData)
  260. return array.array("b", item)
  261. def UnpackMENUINFO(s):
  262. (cb,
  263. fMask,
  264. dwStyle,
  265. cyMax,
  266. hbrBack,
  267. dwContextHelpID,
  268. dwMenuData) = struct.unpack(_menuinfo_fmt, s)
  269. assert cb==len(s)
  270. if fMask & win32con.MIM_STYLE==0: dwStyle = None
  271. if fMask & win32con.MIM_MAXHEIGHT==0: cyMax = None
  272. if fMask & win32con.MIM_BACKGROUND==0: hbrBack = None
  273. if fMask & win32con.MIM_HELPID==0: dwContextHelpID = None
  274. if fMask & win32con.MIM_MENUDATA==0: dwMenuData = None
  275. return _MakeResult("MENUINFO dwStyle cyMax hbrBack dwContextHelpID dwMenuData",
  276. (dwStyle, cyMax, hbrBack, dwContextHelpID, dwMenuData))
  277. def EmptyMENUINFO(mask = None):
  278. if mask is None:
  279. mask = win32con.MIM_STYLE | win32con.MIM_MAXHEIGHT| \
  280. win32con.MIM_BACKGROUND | win32con.MIM_HELPID | \
  281. win32con.MIM_MENUDATA
  282. buf = struct.pack(
  283. _menuinfo_fmt,
  284. struct.calcsize(_menuinfo_fmt), # cbSize
  285. mask,
  286. 0, #dwStyle
  287. 0, #cyMax
  288. 0, #hbrBack,
  289. 0, #dwContextHelpID,
  290. 0, #dwMenuData,
  291. )
  292. return array.array("b", buf)
  293. ##########################################################################
  294. #
  295. # Tree View structure support - TVITEM, TVINSERTSTRUCT and TVDISPINFO
  296. #
  297. ##########################################################################
  298. # XXX - Note that the following implementation of TreeView structures is ripped
  299. # XXX - from the SpamBayes project. It may not quite work correctly yet - I
  300. # XXX - intend checking them later - but having them is better than not at all!
  301. _tvitem_fmt = "iPiiPiiiiP"
  302. # Helpers for the ugly win32 structure packing/unpacking
  303. # XXX - Note that functions using _GetMaskAndVal run 3x faster if they are
  304. # 'inlined' into the function - see PackLVITEM. If the profiler points at
  305. # _GetMaskAndVal(), you should nuke it (patches welcome once they have been
  306. # tested)
  307. def _GetMaskAndVal(val, default, mask, flag):
  308. if val is None:
  309. return mask, default
  310. else:
  311. if flag is not None:
  312. mask |= flag
  313. return mask, val
  314. def PackTVINSERTSTRUCT(parent, insertAfter, tvitem):
  315. tvitem_buf, extra = PackTVITEM(*tvitem)
  316. tvitem_buf = tvitem_buf.tobytes()
  317. format = "PP%ds" % len(tvitem_buf)
  318. return struct.pack(format, parent, insertAfter, tvitem_buf), extra
  319. def PackTVITEM(hitem, state, stateMask, text, image, selimage, citems, param):
  320. extra = [] # objects we must keep references to
  321. mask = 0
  322. mask, hitem = _GetMaskAndVal(hitem, 0, mask, commctrl.TVIF_HANDLE)
  323. mask, state = _GetMaskAndVal(state, 0, mask, commctrl.TVIF_STATE)
  324. if not mask & commctrl.TVIF_STATE:
  325. stateMask = 0
  326. mask, text = _GetMaskAndVal(text, None, mask, commctrl.TVIF_TEXT)
  327. mask, image = _GetMaskAndVal(image, 0, mask, commctrl.TVIF_IMAGE)
  328. mask, selimage = _GetMaskAndVal(selimage, 0, mask, commctrl.TVIF_SELECTEDIMAGE)
  329. mask, citems = _GetMaskAndVal(citems, 0, mask, commctrl.TVIF_CHILDREN)
  330. mask, param = _GetMaskAndVal(param, 0, mask, commctrl.TVIF_PARAM)
  331. if text is None:
  332. text_addr = text_len = 0
  333. else:
  334. text_buffer = _make_text_buffer(text)
  335. text_len = len(text)
  336. extra.append(text_buffer)
  337. text_addr, _ = text_buffer.buffer_info()
  338. buf = struct.pack(_tvitem_fmt,
  339. mask, hitem,
  340. state, stateMask,
  341. text_addr, text_len, # text
  342. image, selimage,
  343. citems, param)
  344. return array.array("b", buf), extra
  345. # Make a new buffer suitable for querying hitem's attributes.
  346. def EmptyTVITEM(hitem, mask = None, text_buf_size=512):
  347. extra = [] # objects we must keep references to
  348. if mask is None:
  349. mask = commctrl.TVIF_HANDLE | commctrl.TVIF_STATE | commctrl.TVIF_TEXT | \
  350. commctrl.TVIF_IMAGE | commctrl.TVIF_SELECTEDIMAGE | \
  351. commctrl.TVIF_CHILDREN | commctrl.TVIF_PARAM
  352. if mask & commctrl.TVIF_TEXT:
  353. text_buffer = _make_empty_text_buffer(text_buf_size)
  354. extra.append(text_buffer)
  355. text_addr, _ = text_buffer.buffer_info()
  356. else:
  357. text_addr = text_buf_size = 0
  358. buf = struct.pack(_tvitem_fmt,
  359. mask, hitem,
  360. 0, 0,
  361. text_addr, text_buf_size, # text
  362. 0, 0,
  363. 0, 0)
  364. return array.array("b", buf), extra
  365. def UnpackTVITEM(buffer):
  366. item_mask, item_hItem, item_state, item_stateMask, \
  367. item_textptr, item_cchText, item_image, item_selimage, \
  368. item_cChildren, item_param = struct.unpack(_tvitem_fmt, buffer)
  369. # ensure only items listed by the mask are valid (except we assume the
  370. # handle is always valid - some notifications (eg, TVN_ENDLABELEDIT) set a
  371. # mask that doesn't include the handle, but the docs explicity say it is.)
  372. if not (item_mask & commctrl.TVIF_TEXT): item_textptr = item_cchText = None
  373. if not (item_mask & commctrl.TVIF_CHILDREN): item_cChildren = None
  374. if not (item_mask & commctrl.TVIF_IMAGE): item_image = None
  375. if not (item_mask & commctrl.TVIF_PARAM): item_param = None
  376. if not (item_mask & commctrl.TVIF_SELECTEDIMAGE): item_selimage = None
  377. if not (item_mask & commctrl.TVIF_STATE): item_state = item_stateMask = None
  378. if item_textptr:
  379. text = win32gui.PyGetString(item_textptr)
  380. else:
  381. text = None
  382. return _MakeResult("TVITEM item_hItem item_state item_stateMask "
  383. "text item_image item_selimage item_cChildren item_param",
  384. (item_hItem, item_state, item_stateMask, text,
  385. item_image, item_selimage, item_cChildren, item_param))
  386. # Unpack the lparm from a "TVNOTIFY" message
  387. def UnpackTVNOTIFY(lparam):
  388. item_size = struct.calcsize(_tvitem_fmt)
  389. format = _nmhdr_fmt + _nmhdr_align_padding
  390. if is64bit:
  391. format = format + "ixxxx"
  392. else:
  393. format = format + "i"
  394. format = format + "%ds%ds" % (item_size, item_size)
  395. buf = win32gui.PyGetMemory(lparam, struct.calcsize(format))
  396. hwndFrom, id, code, action, buf_old, buf_new \
  397. = struct.unpack(format, buf)
  398. item_old = UnpackTVITEM(buf_old)
  399. item_new = UnpackTVITEM(buf_new)
  400. return _MakeResult("TVNOTIFY hwndFrom id code action item_old item_new",
  401. (hwndFrom, id, code, action, item_old, item_new))
  402. def UnpackTVDISPINFO(lparam):
  403. item_size = struct.calcsize(_tvitem_fmt)
  404. format = "PPi%ds" % (item_size,)
  405. buf = win32gui.PyGetMemory(lparam, struct.calcsize(format))
  406. hwndFrom, id, code, buf_item = struct.unpack(format, buf)
  407. item = UnpackTVITEM(buf_item)
  408. return _MakeResult("TVDISPINFO hwndFrom id code item",
  409. (hwndFrom, id, code, item))
  410. #
  411. # List view items
  412. _lvitem_fmt = "iiiiiPiiPi"
  413. def PackLVITEM(item=None, subItem=None, state=None, stateMask=None, text=None, image=None, param=None, indent=None):
  414. extra = [] # objects we must keep references to
  415. mask = 0
  416. # _GetMaskAndVal adds quite a bit of overhead to this function.
  417. if item is None: item = 0 # No mask for item
  418. if subItem is None: subItem = 0 # No mask for sibItem
  419. if state is None:
  420. state = 0
  421. stateMask = 0
  422. else:
  423. mask |= commctrl.LVIF_STATE
  424. if stateMask is None: stateMask = state
  425. if image is None: image = 0
  426. else: mask |= commctrl.LVIF_IMAGE
  427. if param is None: param = 0
  428. else: mask |= commctrl.LVIF_PARAM
  429. if indent is None: indent = 0
  430. else: mask |= commctrl.LVIF_INDENT
  431. if text is None:
  432. text_addr = text_len = 0
  433. else:
  434. mask |= commctrl.LVIF_TEXT
  435. text_buffer = _make_text_buffer(text)
  436. text_len = len(text)
  437. extra.append(text_buffer)
  438. text_addr, _ = text_buffer.buffer_info()
  439. buf = struct.pack(_lvitem_fmt,
  440. mask, item, subItem,
  441. state, stateMask,
  442. text_addr, text_len, # text
  443. image, param, indent)
  444. return array.array("b", buf), extra
  445. def UnpackLVITEM(buffer):
  446. item_mask, item_item, item_subItem, \
  447. item_state, item_stateMask, \
  448. item_textptr, item_cchText, item_image, \
  449. item_param, item_indent = struct.unpack(_lvitem_fmt, buffer)
  450. # ensure only items listed by the mask are valid
  451. if not (item_mask & commctrl.LVIF_TEXT): item_textptr = item_cchText = None
  452. if not (item_mask & commctrl.LVIF_IMAGE): item_image = None
  453. if not (item_mask & commctrl.LVIF_PARAM): item_param = None
  454. if not (item_mask & commctrl.LVIF_INDENT): item_indent = None
  455. if not (item_mask & commctrl.LVIF_STATE): item_state = item_stateMask = None
  456. if item_textptr:
  457. text = win32gui.PyGetString(item_textptr)
  458. else:
  459. text = None
  460. return _MakeResult("LVITEM item_item item_subItem item_state "
  461. "item_stateMask text item_image item_param item_indent",
  462. (item_item, item_subItem, item_state, item_stateMask,
  463. text, item_image, item_param, item_indent))
  464. # Unpack an "LVNOTIFY" message
  465. def UnpackLVDISPINFO(lparam):
  466. item_size = struct.calcsize(_lvitem_fmt)
  467. format = _nmhdr_fmt + _nmhdr_align_padding + ("%ds" % (item_size,))
  468. buf = win32gui.PyGetMemory(lparam, struct.calcsize(format))
  469. hwndFrom, id, code, buf_item = struct.unpack(format, buf)
  470. item = UnpackLVITEM(buf_item)
  471. return _MakeResult("LVDISPINFO hwndFrom id code item",
  472. (hwndFrom, id, code, item))
  473. def UnpackLVNOTIFY(lparam):
  474. format = _nmhdr_fmt + _nmhdr_align_padding + "7i"
  475. if is64bit:
  476. format = format + "xxxx" # point needs padding.
  477. format = format + "P"
  478. buf = win32gui.PyGetMemory(lparam, struct.calcsize(format))
  479. hwndFrom, id, code, item, subitem, newstate, oldstate, \
  480. changed, pt_x, pt_y, lparam = struct.unpack(format, buf)
  481. return _MakeResult("UnpackLVNOTIFY hwndFrom id code item subitem "
  482. "newstate oldstate changed pt lparam",
  483. (hwndFrom, id, code, item, subitem, newstate, oldstate,
  484. changed, (pt_x, pt_y), lparam))
  485. # Make a new buffer suitable for querying an items attributes.
  486. def EmptyLVITEM(item, subitem, mask = None, text_buf_size=512):
  487. extra = [] # objects we must keep references to
  488. if mask is None:
  489. mask = commctrl.LVIF_IMAGE | commctrl.LVIF_INDENT | commctrl.LVIF_TEXT | \
  490. commctrl.LVIF_PARAM | commctrl.LVIF_STATE
  491. if mask & commctrl.LVIF_TEXT:
  492. text_buffer = _make_empty_text_buffer(text_buf_size)
  493. extra.append(text_buffer)
  494. text_addr, _ = text_buffer.buffer_info()
  495. else:
  496. text_addr = text_buf_size = 0
  497. buf = struct.pack(_lvitem_fmt,
  498. mask, item, subitem,
  499. 0, 0,
  500. text_addr, text_buf_size, # text
  501. 0, 0, 0)
  502. return array.array("b", buf), extra
  503. # List view column structure
  504. _lvcolumn_fmt = "iiiPiiii"
  505. def PackLVCOLUMN(fmt=None, cx=None, text=None, subItem=None, image=None, order=None):
  506. extra = [] # objects we must keep references to
  507. mask = 0
  508. mask, fmt = _GetMaskAndVal(fmt, 0, mask, commctrl.LVCF_FMT)
  509. mask, cx = _GetMaskAndVal(cx, 0, mask, commctrl.LVCF_WIDTH)
  510. mask, text = _GetMaskAndVal(text, None, mask, commctrl.LVCF_TEXT)
  511. mask, subItem = _GetMaskAndVal(subItem, 0, mask, commctrl.LVCF_SUBITEM)
  512. mask, image = _GetMaskAndVal(image, 0, mask, commctrl.LVCF_IMAGE)
  513. mask, order= _GetMaskAndVal(order, 0, mask, commctrl.LVCF_ORDER)
  514. if text is None:
  515. text_addr = text_len = 0
  516. else:
  517. text_buffer = _make_text_buffer(text)
  518. extra.append(text_buffer)
  519. text_addr, _ = text_buffer.buffer_info()
  520. text_len = len(text)
  521. buf = struct.pack(_lvcolumn_fmt,
  522. mask, fmt, cx,
  523. text_addr, text_len, # text
  524. subItem, image, order)
  525. return array.array("b", buf), extra
  526. def UnpackLVCOLUMN(lparam):
  527. mask, fmt, cx, text_addr, text_size, subItem, image, order = \
  528. struct.unpack(_lvcolumn_fmt, lparam)
  529. # ensure only items listed by the mask are valid
  530. if not (mask & commctrl.LVCF_FMT): fmt = None
  531. if not (mask & commctrl.LVCF_WIDTH): cx = None
  532. if not (mask & commctrl.LVCF_TEXT): text_addr = text_size = None
  533. if not (mask & commctrl.LVCF_SUBITEM): subItem = None
  534. if not (mask & commctrl.LVCF_IMAGE): image = None
  535. if not (mask & commctrl.LVCF_ORDER): order = None
  536. if text_addr:
  537. text = win32gui.PyGetString(text_addr)
  538. else:
  539. text = None
  540. return _MakeResult("LVCOLUMN fmt cx text subItem image order",
  541. (fmt, cx, text, subItem, image, order))
  542. # Make a new buffer suitable for querying an items attributes.
  543. def EmptyLVCOLUMN(mask = None, text_buf_size=512):
  544. extra = [] # objects we must keep references to
  545. if mask is None:
  546. mask = commctrl.LVCF_FMT | commctrl.LVCF_WIDTH | commctrl.LVCF_TEXT | \
  547. commctrl.LVCF_SUBITEM | commctrl.LVCF_IMAGE | commctrl.LVCF_ORDER
  548. if mask & commctrl.LVCF_TEXT:
  549. text_buffer = _make_empty_text_buffer(text_buf_size)
  550. extra.append(text_buffer)
  551. text_addr, _ = text_buffer.buffer_info()
  552. else:
  553. text_addr = text_buf_size = 0
  554. buf = struct.pack(_lvcolumn_fmt,
  555. mask, 0, 0,
  556. text_addr, text_buf_size, # text
  557. 0, 0, 0)
  558. return array.array("b", buf), extra
  559. # List view hit-test.
  560. def PackLVHITTEST(pt):
  561. format = "iiiii"
  562. buf = struct.pack(format,
  563. pt[0], pt[1],
  564. 0, 0, 0)
  565. return array.array("b", buf), None
  566. def UnpackLVHITTEST(buf):
  567. format = "iiiii"
  568. x, y, flags, item, subitem = struct.unpack(format, buf)
  569. return _MakeResult("LVHITTEST pt flags item subitem",
  570. ((x,y), flags, item, subitem))
  571. def PackHDITEM(cxy = None, text = None, hbm = None, fmt = None,
  572. param = None, image = None, order = None):
  573. extra = [] # objects we must keep references to
  574. mask = 0
  575. mask, cxy = _GetMaskAndVal(cxy, 0, mask, commctrl.HDI_HEIGHT)
  576. mask, text = _GetMaskAndVal(text, None, mask, commctrl.LVCF_TEXT)
  577. mask, hbm = _GetMaskAndVal(hbm, 0, mask, commctrl.HDI_BITMAP)
  578. mask, fmt = _GetMaskAndVal(fmt, 0, mask, commctrl.HDI_FORMAT)
  579. mask, param = _GetMaskAndVal(param, 0, mask, commctrl.HDI_LPARAM)
  580. mask, image = _GetMaskAndVal(image, 0, mask, commctrl.HDI_IMAGE)
  581. mask, order = _GetMaskAndVal(order, 0, mask, commctrl.HDI_ORDER)
  582. if text is None:
  583. text_addr = text_len = 0
  584. else:
  585. text_buffer = _make_text_buffer(text)
  586. extra.append(text_buffer)
  587. text_addr, _ = text_buffer.buffer_info()
  588. text_len = len(text)
  589. format = "iiPPiiPiiii"
  590. buf = struct.pack(format,
  591. mask, cxy, text_addr, hbm, text_len,
  592. fmt, param, image, order, 0, 0)
  593. return array.array("b", buf), extra
  594. # Device notification stuff
  595. # Generic function for packing a DEV_BROADCAST_* structure - generally used
  596. # by the other PackDEV_BROADCAST_* functions in this module.
  597. def PackDEV_BROADCAST(devicetype, rest_fmt, rest_data, extra_data=_make_bytes('')):
  598. # It seems a requirement is 4 byte alignment, even for the 'BYTE data[1]'
  599. # field (eg, that would make DEV_BROADCAST_HANDLE 41 bytes, but we must
  600. # be 44.
  601. extra_data += _make_bytes('\0' * (4-len(extra_data)%4))
  602. format = "iii" + rest_fmt
  603. full_size = struct.calcsize(format) + len(extra_data)
  604. data = (full_size, devicetype, 0) + rest_data
  605. return struct.pack(format, *data) + extra_data
  606. def PackDEV_BROADCAST_HANDLE(handle, hdevnotify=0, guid=_make_bytes("\0"*16), name_offset=0, data=_make_bytes("\0")):
  607. return PackDEV_BROADCAST(win32con.DBT_DEVTYP_HANDLE, "PP16sl",
  608. (int(handle), int(hdevnotify), _make_memory(guid), name_offset),
  609. data)
  610. def PackDEV_BROADCAST_VOLUME(unitmask, flags):
  611. return PackDEV_BROADCAST(win32con.DBT_DEVTYP_VOLUME, "II",
  612. (unitmask, flags))
  613. def PackDEV_BROADCAST_DEVICEINTERFACE(classguid, name=""):
  614. if win32gui.UNICODE:
  615. # This really means "is py3k?" - so not accepting bytes is OK
  616. if not isinstance(name, str):
  617. raise TypeError("Must provide unicode for the name")
  618. name = name.encode('utf-16le')
  619. else:
  620. # py2k was passed a unicode object - encode as mbcs.
  621. if isinstance(name, str):
  622. name = name.encode('mbcs')
  623. # 16 bytes for the IID followed by \0 term'd string.
  624. rest_fmt = "16s%ds" % len(name)
  625. # _make_memory(iid) hoops necessary to get the raw IID bytes.
  626. rest_data = (_make_memory(pywintypes.IID(classguid)), name)
  627. return PackDEV_BROADCAST(win32con.DBT_DEVTYP_DEVICEINTERFACE, rest_fmt, rest_data)
  628. # An object returned by UnpackDEV_BROADCAST.
  629. class DEV_BROADCAST_INFO:
  630. def __init__(self, devicetype, **kw):
  631. self.devicetype = devicetype
  632. self.__dict__.update(kw)
  633. def __str__(self):
  634. return "DEV_BROADCAST_INFO:" + str(self.__dict__)
  635. # Support for unpacking the 'lparam'
  636. def UnpackDEV_BROADCAST(lparam):
  637. if lparam == 0:
  638. return None
  639. hdr_format = "iii"
  640. hdr_size = struct.calcsize(hdr_format)
  641. hdr_buf = win32gui.PyGetMemory(lparam, hdr_size)
  642. size, devtype, reserved = struct.unpack("iii", hdr_buf)
  643. # Due to x64 alignment issues, we need to use the full format string over
  644. # the entire buffer. ie, on x64:
  645. # calcsize('iiiP') != calcsize('iii')+calcsize('P')
  646. buf = win32gui.PyGetMemory(lparam, size)
  647. extra = x = {}
  648. if devtype == win32con.DBT_DEVTYP_HANDLE:
  649. # 2 handles, a GUID, a LONG and possibly an array following...
  650. fmt = hdr_format + "PP16sl"
  651. _, _, _, x['handle'], x['hdevnotify'], guid_bytes, x['nameoffset'] = \
  652. struct.unpack(fmt, buf[:struct.calcsize(fmt)])
  653. x['eventguid'] = pywintypes.IID(guid_bytes, True)
  654. elif devtype == win32con.DBT_DEVTYP_DEVICEINTERFACE:
  655. fmt = hdr_format + "16s"
  656. _, _, _, guid_bytes = struct.unpack(fmt, buf[:struct.calcsize(fmt)])
  657. x['classguid'] = pywintypes.IID(guid_bytes, True)
  658. x['name'] = win32gui.PyGetString(lparam + struct.calcsize(fmt))
  659. elif devtype == win32con.DBT_DEVTYP_VOLUME:
  660. # int mask and flags
  661. fmt = hdr_format + "II"
  662. _, _, _, x['unitmask'], x['flags'] = struct.unpack(fmt, buf[:struct.calcsize(fmt)])
  663. else:
  664. raise NotImplementedError("unknown device type %d" % (devtype,))
  665. return DEV_BROADCAST_INFO(devtype, **extra)