Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

win32gui_struct.py 29KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  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 array
  29. import struct
  30. import sys
  31. import commctrl
  32. import pywintypes
  33. import win32con
  34. import win32gui
  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(
  98. "NMITEMACTIVATE hwndFrom idFrom code iItem iSubItem uNewState uOldState uChanged actionx actiony lParam",
  99. struct.unpack(format, buf),
  100. )
  101. # MENUITEMINFO struct
  102. # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/Resources/Menus/MenuReference/MenuStructures/MENUITEMINFO.asp
  103. # We use the struct module to pack and unpack strings as MENUITEMINFO
  104. # structures. We also have special handling for the 'fMask' item in that
  105. # structure to avoid the caller needing to explicitly check validity
  106. # (None is used if the mask excludes/should exclude the value)
  107. _menuiteminfo_fmt = "5i5PiP"
  108. def PackMENUITEMINFO(
  109. fType=None,
  110. fState=None,
  111. wID=None,
  112. hSubMenu=None,
  113. hbmpChecked=None,
  114. hbmpUnchecked=None,
  115. dwItemData=None,
  116. text=None,
  117. hbmpItem=None,
  118. dwTypeData=None,
  119. ):
  120. # 'extras' are objects the caller must keep a reference to (as their
  121. # memory is used) for the lifetime of the INFO item.
  122. extras = []
  123. # ack - dwItemData and dwTypeData were confused for a while...
  124. assert (
  125. dwItemData is None or dwTypeData is None
  126. ), "sorry - these were confused - you probably want dwItemData"
  127. # if we are a long way past 209, then we can nuke the above...
  128. if dwTypeData is not None:
  129. import warnings
  130. warnings.warn("PackMENUITEMINFO: please use dwItemData instead of dwTypeData")
  131. if dwItemData is None:
  132. dwItemData = dwTypeData or 0
  133. fMask = 0
  134. if fType is None:
  135. fType = 0
  136. else:
  137. fMask |= win32con.MIIM_FTYPE
  138. if fState is None:
  139. fState = 0
  140. else:
  141. fMask |= win32con.MIIM_STATE
  142. if wID is None:
  143. wID = 0
  144. else:
  145. fMask |= win32con.MIIM_ID
  146. if hSubMenu is None:
  147. hSubMenu = 0
  148. else:
  149. fMask |= win32con.MIIM_SUBMENU
  150. if hbmpChecked is None:
  151. assert hbmpUnchecked is None, "neither or both checkmark bmps must be given"
  152. hbmpChecked = hbmpUnchecked = 0
  153. else:
  154. assert hbmpUnchecked is not None, "neither or both checkmark bmps must be given"
  155. fMask |= win32con.MIIM_CHECKMARKS
  156. if dwItemData is None:
  157. dwItemData = 0
  158. else:
  159. fMask |= win32con.MIIM_DATA
  160. if hbmpItem is None:
  161. hbmpItem = 0
  162. else:
  163. fMask |= win32con.MIIM_BITMAP
  164. if text is not None:
  165. fMask |= win32con.MIIM_STRING
  166. str_buf = _make_text_buffer(text)
  167. cch = len(text)
  168. # We are taking address of strbuf - it must not die until windows
  169. # has finished with our structure.
  170. lptext = str_buf.buffer_info()[0]
  171. extras.append(str_buf)
  172. else:
  173. lptext = 0
  174. cch = 0
  175. # Create the struct.
  176. # 'P' format does not accept PyHANDLE's !
  177. item = struct.pack(
  178. _menuiteminfo_fmt,
  179. struct.calcsize(_menuiteminfo_fmt), # cbSize
  180. fMask,
  181. fType,
  182. fState,
  183. wID,
  184. int(hSubMenu),
  185. int(hbmpChecked),
  186. int(hbmpUnchecked),
  187. dwItemData,
  188. lptext,
  189. cch,
  190. int(hbmpItem),
  191. )
  192. # Now copy the string to a writable buffer, so that the result
  193. # could be passed to a 'Get' function
  194. return array.array("b", item), extras
  195. def UnpackMENUITEMINFO(s):
  196. (
  197. cb,
  198. fMask,
  199. fType,
  200. fState,
  201. wID,
  202. hSubMenu,
  203. hbmpChecked,
  204. hbmpUnchecked,
  205. dwItemData,
  206. lptext,
  207. cch,
  208. hbmpItem,
  209. ) = struct.unpack(_menuiteminfo_fmt, s)
  210. assert cb == len(s)
  211. if fMask & win32con.MIIM_FTYPE == 0:
  212. fType = None
  213. if fMask & win32con.MIIM_STATE == 0:
  214. fState = None
  215. if fMask & win32con.MIIM_ID == 0:
  216. wID = None
  217. if fMask & win32con.MIIM_SUBMENU == 0:
  218. hSubMenu = None
  219. if fMask & win32con.MIIM_CHECKMARKS == 0:
  220. hbmpChecked = hbmpUnchecked = None
  221. if fMask & win32con.MIIM_DATA == 0:
  222. dwItemData = None
  223. if fMask & win32con.MIIM_BITMAP == 0:
  224. hbmpItem = None
  225. if fMask & win32con.MIIM_STRING:
  226. text = win32gui.PyGetString(lptext, cch)
  227. else:
  228. text = None
  229. return _MakeResult(
  230. "MENUITEMINFO fType fState wID hSubMenu hbmpChecked "
  231. "hbmpUnchecked dwItemData text hbmpItem",
  232. (
  233. fType,
  234. fState,
  235. wID,
  236. hSubMenu,
  237. hbmpChecked,
  238. hbmpUnchecked,
  239. dwItemData,
  240. text,
  241. hbmpItem,
  242. ),
  243. )
  244. def EmptyMENUITEMINFO(mask=None, text_buf_size=512):
  245. # text_buf_size is number of *characters* - not necessarily no of bytes.
  246. extra = []
  247. if mask is None:
  248. mask = (
  249. win32con.MIIM_BITMAP
  250. | win32con.MIIM_CHECKMARKS
  251. | win32con.MIIM_DATA
  252. | win32con.MIIM_FTYPE
  253. | win32con.MIIM_ID
  254. | win32con.MIIM_STATE
  255. | win32con.MIIM_STRING
  256. | win32con.MIIM_SUBMENU
  257. )
  258. # Note: No MIIM_TYPE - this screws win2k/98.
  259. if mask & win32con.MIIM_STRING:
  260. text_buffer = _make_empty_text_buffer(text_buf_size)
  261. extra.append(text_buffer)
  262. text_addr, _ = text_buffer.buffer_info()
  263. else:
  264. text_addr = text_buf_size = 0
  265. # Now copy the string to a writable buffer, so that the result
  266. # could be passed to a 'Get' function
  267. buf = struct.pack(
  268. _menuiteminfo_fmt,
  269. struct.calcsize(_menuiteminfo_fmt), # cbSize
  270. mask,
  271. 0, # fType,
  272. 0, # fState,
  273. 0, # wID,
  274. 0, # hSubMenu,
  275. 0, # hbmpChecked,
  276. 0, # hbmpUnchecked,
  277. 0, # dwItemData,
  278. text_addr,
  279. text_buf_size,
  280. 0, # hbmpItem
  281. )
  282. return array.array("b", buf), extra
  283. # MENUINFO struct
  284. _menuinfo_fmt = "iiiiPiP"
  285. def PackMENUINFO(
  286. dwStyle=None,
  287. cyMax=None,
  288. hbrBack=None,
  289. dwContextHelpID=None,
  290. dwMenuData=None,
  291. fMask=0,
  292. ):
  293. if dwStyle is None:
  294. dwStyle = 0
  295. else:
  296. fMask |= win32con.MIM_STYLE
  297. if cyMax is None:
  298. cyMax = 0
  299. else:
  300. fMask |= win32con.MIM_MAXHEIGHT
  301. if hbrBack is None:
  302. hbrBack = 0
  303. else:
  304. fMask |= win32con.MIM_BACKGROUND
  305. if dwContextHelpID is None:
  306. dwContextHelpID = 0
  307. else:
  308. fMask |= win32con.MIM_HELPID
  309. if dwMenuData is None:
  310. dwMenuData = 0
  311. else:
  312. fMask |= win32con.MIM_MENUDATA
  313. # Create the struct.
  314. item = struct.pack(
  315. _menuinfo_fmt,
  316. struct.calcsize(_menuinfo_fmt), # cbSize
  317. fMask,
  318. dwStyle,
  319. cyMax,
  320. hbrBack,
  321. dwContextHelpID,
  322. dwMenuData,
  323. )
  324. return array.array("b", item)
  325. def UnpackMENUINFO(s):
  326. (cb, fMask, dwStyle, cyMax, hbrBack, dwContextHelpID, dwMenuData) = struct.unpack(
  327. _menuinfo_fmt, s
  328. )
  329. assert cb == len(s)
  330. if fMask & win32con.MIM_STYLE == 0:
  331. dwStyle = None
  332. if fMask & win32con.MIM_MAXHEIGHT == 0:
  333. cyMax = None
  334. if fMask & win32con.MIM_BACKGROUND == 0:
  335. hbrBack = None
  336. if fMask & win32con.MIM_HELPID == 0:
  337. dwContextHelpID = None
  338. if fMask & win32con.MIM_MENUDATA == 0:
  339. dwMenuData = None
  340. return _MakeResult(
  341. "MENUINFO dwStyle cyMax hbrBack dwContextHelpID dwMenuData",
  342. (dwStyle, cyMax, hbrBack, dwContextHelpID, dwMenuData),
  343. )
  344. def EmptyMENUINFO(mask=None):
  345. if mask is None:
  346. mask = (
  347. win32con.MIM_STYLE
  348. | win32con.MIM_MAXHEIGHT
  349. | win32con.MIM_BACKGROUND
  350. | win32con.MIM_HELPID
  351. | win32con.MIM_MENUDATA
  352. )
  353. buf = struct.pack(
  354. _menuinfo_fmt,
  355. struct.calcsize(_menuinfo_fmt), # cbSize
  356. mask,
  357. 0, # dwStyle
  358. 0, # cyMax
  359. 0, # hbrBack,
  360. 0, # dwContextHelpID,
  361. 0, # dwMenuData,
  362. )
  363. return array.array("b", buf)
  364. ##########################################################################
  365. #
  366. # Tree View structure support - TVITEM, TVINSERTSTRUCT and TVDISPINFO
  367. #
  368. ##########################################################################
  369. # XXX - Note that the following implementation of TreeView structures is ripped
  370. # XXX - from the SpamBayes project. It may not quite work correctly yet - I
  371. # XXX - intend checking them later - but having them is better than not at all!
  372. _tvitem_fmt = "iPiiPiiiiP"
  373. # Helpers for the ugly win32 structure packing/unpacking
  374. # XXX - Note that functions using _GetMaskAndVal run 3x faster if they are
  375. # 'inlined' into the function - see PackLVITEM. If the profiler points at
  376. # _GetMaskAndVal(), you should nuke it (patches welcome once they have been
  377. # tested)
  378. def _GetMaskAndVal(val, default, mask, flag):
  379. if val is None:
  380. return mask, default
  381. else:
  382. if flag is not None:
  383. mask |= flag
  384. return mask, val
  385. def PackTVINSERTSTRUCT(parent, insertAfter, tvitem):
  386. tvitem_buf, extra = PackTVITEM(*tvitem)
  387. tvitem_buf = tvitem_buf.tobytes()
  388. format = "PP%ds" % len(tvitem_buf)
  389. return struct.pack(format, parent, insertAfter, tvitem_buf), extra
  390. def PackTVITEM(hitem, state, stateMask, text, image, selimage, citems, param):
  391. extra = [] # objects we must keep references to
  392. mask = 0
  393. mask, hitem = _GetMaskAndVal(hitem, 0, mask, commctrl.TVIF_HANDLE)
  394. mask, state = _GetMaskAndVal(state, 0, mask, commctrl.TVIF_STATE)
  395. if not mask & commctrl.TVIF_STATE:
  396. stateMask = 0
  397. mask, text = _GetMaskAndVal(text, None, mask, commctrl.TVIF_TEXT)
  398. mask, image = _GetMaskAndVal(image, 0, mask, commctrl.TVIF_IMAGE)
  399. mask, selimage = _GetMaskAndVal(selimage, 0, mask, commctrl.TVIF_SELECTEDIMAGE)
  400. mask, citems = _GetMaskAndVal(citems, 0, mask, commctrl.TVIF_CHILDREN)
  401. mask, param = _GetMaskAndVal(param, 0, mask, commctrl.TVIF_PARAM)
  402. if text is None:
  403. text_addr = text_len = 0
  404. else:
  405. text_buffer = _make_text_buffer(text)
  406. text_len = len(text)
  407. extra.append(text_buffer)
  408. text_addr, _ = text_buffer.buffer_info()
  409. buf = struct.pack(
  410. _tvitem_fmt,
  411. mask,
  412. hitem,
  413. state,
  414. stateMask,
  415. text_addr,
  416. text_len, # text
  417. image,
  418. selimage,
  419. citems,
  420. param,
  421. )
  422. return array.array("b", buf), extra
  423. # Make a new buffer suitable for querying hitem's attributes.
  424. def EmptyTVITEM(hitem, mask=None, text_buf_size=512):
  425. extra = [] # objects we must keep references to
  426. if mask is None:
  427. mask = (
  428. commctrl.TVIF_HANDLE
  429. | commctrl.TVIF_STATE
  430. | commctrl.TVIF_TEXT
  431. | commctrl.TVIF_IMAGE
  432. | commctrl.TVIF_SELECTEDIMAGE
  433. | commctrl.TVIF_CHILDREN
  434. | commctrl.TVIF_PARAM
  435. )
  436. if mask & commctrl.TVIF_TEXT:
  437. text_buffer = _make_empty_text_buffer(text_buf_size)
  438. extra.append(text_buffer)
  439. text_addr, _ = text_buffer.buffer_info()
  440. else:
  441. text_addr = text_buf_size = 0
  442. buf = struct.pack(
  443. _tvitem_fmt, mask, hitem, 0, 0, text_addr, text_buf_size, 0, 0, 0, 0 # text
  444. )
  445. return array.array("b", buf), extra
  446. def UnpackTVITEM(buffer):
  447. (
  448. item_mask,
  449. item_hItem,
  450. item_state,
  451. item_stateMask,
  452. item_textptr,
  453. item_cchText,
  454. item_image,
  455. item_selimage,
  456. item_cChildren,
  457. item_param,
  458. ) = struct.unpack(_tvitem_fmt, buffer)
  459. # ensure only items listed by the mask are valid (except we assume the
  460. # handle is always valid - some notifications (eg, TVN_ENDLABELEDIT) set a
  461. # mask that doesn't include the handle, but the docs explicity say it is.)
  462. if not (item_mask & commctrl.TVIF_TEXT):
  463. item_textptr = item_cchText = None
  464. if not (item_mask & commctrl.TVIF_CHILDREN):
  465. item_cChildren = None
  466. if not (item_mask & commctrl.TVIF_IMAGE):
  467. item_image = None
  468. if not (item_mask & commctrl.TVIF_PARAM):
  469. item_param = None
  470. if not (item_mask & commctrl.TVIF_SELECTEDIMAGE):
  471. item_selimage = None
  472. if not (item_mask & commctrl.TVIF_STATE):
  473. item_state = item_stateMask = None
  474. if item_textptr:
  475. text = win32gui.PyGetString(item_textptr)
  476. else:
  477. text = None
  478. return _MakeResult(
  479. "TVITEM item_hItem item_state item_stateMask "
  480. "text item_image item_selimage item_cChildren item_param",
  481. (
  482. item_hItem,
  483. item_state,
  484. item_stateMask,
  485. text,
  486. item_image,
  487. item_selimage,
  488. item_cChildren,
  489. item_param,
  490. ),
  491. )
  492. # Unpack the lparm from a "TVNOTIFY" message
  493. def UnpackTVNOTIFY(lparam):
  494. item_size = struct.calcsize(_tvitem_fmt)
  495. format = _nmhdr_fmt + _nmhdr_align_padding
  496. if is64bit:
  497. format = format + "ixxxx"
  498. else:
  499. format = format + "i"
  500. format = format + "%ds%ds" % (item_size, item_size)
  501. buf = win32gui.PyGetMemory(lparam, struct.calcsize(format))
  502. hwndFrom, id, code, action, buf_old, buf_new = struct.unpack(format, buf)
  503. item_old = UnpackTVITEM(buf_old)
  504. item_new = UnpackTVITEM(buf_new)
  505. return _MakeResult(
  506. "TVNOTIFY hwndFrom id code action item_old item_new",
  507. (hwndFrom, id, code, action, item_old, item_new),
  508. )
  509. def UnpackTVDISPINFO(lparam):
  510. item_size = struct.calcsize(_tvitem_fmt)
  511. format = "PPi%ds" % (item_size,)
  512. buf = win32gui.PyGetMemory(lparam, struct.calcsize(format))
  513. hwndFrom, id, code, buf_item = struct.unpack(format, buf)
  514. item = UnpackTVITEM(buf_item)
  515. return _MakeResult("TVDISPINFO hwndFrom id code item", (hwndFrom, id, code, item))
  516. #
  517. # List view items
  518. _lvitem_fmt = "iiiiiPiiPi"
  519. def PackLVITEM(
  520. item=None,
  521. subItem=None,
  522. state=None,
  523. stateMask=None,
  524. text=None,
  525. image=None,
  526. param=None,
  527. indent=None,
  528. ):
  529. extra = [] # objects we must keep references to
  530. mask = 0
  531. # _GetMaskAndVal adds quite a bit of overhead to this function.
  532. if item is None:
  533. item = 0 # No mask for item
  534. if subItem is None:
  535. subItem = 0 # No mask for sibItem
  536. if state is None:
  537. state = 0
  538. stateMask = 0
  539. else:
  540. mask |= commctrl.LVIF_STATE
  541. if stateMask is None:
  542. stateMask = state
  543. if image is None:
  544. image = 0
  545. else:
  546. mask |= commctrl.LVIF_IMAGE
  547. if param is None:
  548. param = 0
  549. else:
  550. mask |= commctrl.LVIF_PARAM
  551. if indent is None:
  552. indent = 0
  553. else:
  554. mask |= commctrl.LVIF_INDENT
  555. if text is None:
  556. text_addr = text_len = 0
  557. else:
  558. mask |= commctrl.LVIF_TEXT
  559. text_buffer = _make_text_buffer(text)
  560. text_len = len(text)
  561. extra.append(text_buffer)
  562. text_addr, _ = text_buffer.buffer_info()
  563. buf = struct.pack(
  564. _lvitem_fmt,
  565. mask,
  566. item,
  567. subItem,
  568. state,
  569. stateMask,
  570. text_addr,
  571. text_len, # text
  572. image,
  573. param,
  574. indent,
  575. )
  576. return array.array("b", buf), extra
  577. def UnpackLVITEM(buffer):
  578. (
  579. item_mask,
  580. item_item,
  581. item_subItem,
  582. item_state,
  583. item_stateMask,
  584. item_textptr,
  585. item_cchText,
  586. item_image,
  587. item_param,
  588. item_indent,
  589. ) = struct.unpack(_lvitem_fmt, buffer)
  590. # ensure only items listed by the mask are valid
  591. if not (item_mask & commctrl.LVIF_TEXT):
  592. item_textptr = item_cchText = None
  593. if not (item_mask & commctrl.LVIF_IMAGE):
  594. item_image = None
  595. if not (item_mask & commctrl.LVIF_PARAM):
  596. item_param = None
  597. if not (item_mask & commctrl.LVIF_INDENT):
  598. item_indent = None
  599. if not (item_mask & commctrl.LVIF_STATE):
  600. item_state = item_stateMask = None
  601. if item_textptr:
  602. text = win32gui.PyGetString(item_textptr)
  603. else:
  604. text = None
  605. return _MakeResult(
  606. "LVITEM item_item item_subItem item_state "
  607. "item_stateMask text item_image item_param item_indent",
  608. (
  609. item_item,
  610. item_subItem,
  611. item_state,
  612. item_stateMask,
  613. text,
  614. item_image,
  615. item_param,
  616. item_indent,
  617. ),
  618. )
  619. # Unpack an "LVNOTIFY" message
  620. def UnpackLVDISPINFO(lparam):
  621. item_size = struct.calcsize(_lvitem_fmt)
  622. format = _nmhdr_fmt + _nmhdr_align_padding + ("%ds" % (item_size,))
  623. buf = win32gui.PyGetMemory(lparam, struct.calcsize(format))
  624. hwndFrom, id, code, buf_item = struct.unpack(format, buf)
  625. item = UnpackLVITEM(buf_item)
  626. return _MakeResult("LVDISPINFO hwndFrom id code item", (hwndFrom, id, code, item))
  627. def UnpackLVNOTIFY(lparam):
  628. format = _nmhdr_fmt + _nmhdr_align_padding + "7i"
  629. if is64bit:
  630. format = format + "xxxx" # point needs padding.
  631. format = format + "P"
  632. buf = win32gui.PyGetMemory(lparam, struct.calcsize(format))
  633. (
  634. hwndFrom,
  635. id,
  636. code,
  637. item,
  638. subitem,
  639. newstate,
  640. oldstate,
  641. changed,
  642. pt_x,
  643. pt_y,
  644. lparam,
  645. ) = struct.unpack(format, buf)
  646. return _MakeResult(
  647. "UnpackLVNOTIFY hwndFrom id code item subitem "
  648. "newstate oldstate changed pt lparam",
  649. (
  650. hwndFrom,
  651. id,
  652. code,
  653. item,
  654. subitem,
  655. newstate,
  656. oldstate,
  657. changed,
  658. (pt_x, pt_y),
  659. lparam,
  660. ),
  661. )
  662. # Make a new buffer suitable for querying an items attributes.
  663. def EmptyLVITEM(item, subitem, mask=None, text_buf_size=512):
  664. extra = [] # objects we must keep references to
  665. if mask is None:
  666. mask = (
  667. commctrl.LVIF_IMAGE
  668. | commctrl.LVIF_INDENT
  669. | commctrl.LVIF_TEXT
  670. | commctrl.LVIF_PARAM
  671. | commctrl.LVIF_STATE
  672. )
  673. if mask & commctrl.LVIF_TEXT:
  674. text_buffer = _make_empty_text_buffer(text_buf_size)
  675. extra.append(text_buffer)
  676. text_addr, _ = text_buffer.buffer_info()
  677. else:
  678. text_addr = text_buf_size = 0
  679. buf = struct.pack(
  680. _lvitem_fmt,
  681. mask,
  682. item,
  683. subitem,
  684. 0,
  685. 0,
  686. text_addr,
  687. text_buf_size, # text
  688. 0,
  689. 0,
  690. 0,
  691. )
  692. return array.array("b", buf), extra
  693. # List view column structure
  694. _lvcolumn_fmt = "iiiPiiii"
  695. def PackLVCOLUMN(fmt=None, cx=None, text=None, subItem=None, image=None, order=None):
  696. extra = [] # objects we must keep references to
  697. mask = 0
  698. mask, fmt = _GetMaskAndVal(fmt, 0, mask, commctrl.LVCF_FMT)
  699. mask, cx = _GetMaskAndVal(cx, 0, mask, commctrl.LVCF_WIDTH)
  700. mask, text = _GetMaskAndVal(text, None, mask, commctrl.LVCF_TEXT)
  701. mask, subItem = _GetMaskAndVal(subItem, 0, mask, commctrl.LVCF_SUBITEM)
  702. mask, image = _GetMaskAndVal(image, 0, mask, commctrl.LVCF_IMAGE)
  703. mask, order = _GetMaskAndVal(order, 0, mask, commctrl.LVCF_ORDER)
  704. if text is None:
  705. text_addr = text_len = 0
  706. else:
  707. text_buffer = _make_text_buffer(text)
  708. extra.append(text_buffer)
  709. text_addr, _ = text_buffer.buffer_info()
  710. text_len = len(text)
  711. buf = struct.pack(
  712. _lvcolumn_fmt, mask, fmt, cx, text_addr, text_len, subItem, image, order # text
  713. )
  714. return array.array("b", buf), extra
  715. def UnpackLVCOLUMN(lparam):
  716. mask, fmt, cx, text_addr, text_size, subItem, image, order = struct.unpack(
  717. _lvcolumn_fmt, lparam
  718. )
  719. # ensure only items listed by the mask are valid
  720. if not (mask & commctrl.LVCF_FMT):
  721. fmt = None
  722. if not (mask & commctrl.LVCF_WIDTH):
  723. cx = None
  724. if not (mask & commctrl.LVCF_TEXT):
  725. text_addr = text_size = None
  726. if not (mask & commctrl.LVCF_SUBITEM):
  727. subItem = None
  728. if not (mask & commctrl.LVCF_IMAGE):
  729. image = None
  730. if not (mask & commctrl.LVCF_ORDER):
  731. order = None
  732. if text_addr:
  733. text = win32gui.PyGetString(text_addr)
  734. else:
  735. text = None
  736. return _MakeResult(
  737. "LVCOLUMN fmt cx text subItem image order",
  738. (fmt, cx, text, subItem, image, order),
  739. )
  740. # Make a new buffer suitable for querying an items attributes.
  741. def EmptyLVCOLUMN(mask=None, text_buf_size=512):
  742. extra = [] # objects we must keep references to
  743. if mask is None:
  744. mask = (
  745. commctrl.LVCF_FMT
  746. | commctrl.LVCF_WIDTH
  747. | commctrl.LVCF_TEXT
  748. | commctrl.LVCF_SUBITEM
  749. | commctrl.LVCF_IMAGE
  750. | commctrl.LVCF_ORDER
  751. )
  752. if mask & commctrl.LVCF_TEXT:
  753. text_buffer = _make_empty_text_buffer(text_buf_size)
  754. extra.append(text_buffer)
  755. text_addr, _ = text_buffer.buffer_info()
  756. else:
  757. text_addr = text_buf_size = 0
  758. buf = struct.pack(
  759. _lvcolumn_fmt, mask, 0, 0, text_addr, text_buf_size, 0, 0, 0 # text
  760. )
  761. return array.array("b", buf), extra
  762. # List view hit-test.
  763. def PackLVHITTEST(pt):
  764. format = "iiiii"
  765. buf = struct.pack(format, pt[0], pt[1], 0, 0, 0)
  766. return array.array("b", buf), None
  767. def UnpackLVHITTEST(buf):
  768. format = "iiiii"
  769. x, y, flags, item, subitem = struct.unpack(format, buf)
  770. return _MakeResult(
  771. "LVHITTEST pt flags item subitem", ((x, y), flags, item, subitem)
  772. )
  773. def PackHDITEM(
  774. cxy=None, text=None, hbm=None, fmt=None, param=None, image=None, order=None
  775. ):
  776. extra = [] # objects we must keep references to
  777. mask = 0
  778. mask, cxy = _GetMaskAndVal(cxy, 0, mask, commctrl.HDI_HEIGHT)
  779. mask, text = _GetMaskAndVal(text, None, mask, commctrl.LVCF_TEXT)
  780. mask, hbm = _GetMaskAndVal(hbm, 0, mask, commctrl.HDI_BITMAP)
  781. mask, fmt = _GetMaskAndVal(fmt, 0, mask, commctrl.HDI_FORMAT)
  782. mask, param = _GetMaskAndVal(param, 0, mask, commctrl.HDI_LPARAM)
  783. mask, image = _GetMaskAndVal(image, 0, mask, commctrl.HDI_IMAGE)
  784. mask, order = _GetMaskAndVal(order, 0, mask, commctrl.HDI_ORDER)
  785. if text is None:
  786. text_addr = text_len = 0
  787. else:
  788. text_buffer = _make_text_buffer(text)
  789. extra.append(text_buffer)
  790. text_addr, _ = text_buffer.buffer_info()
  791. text_len = len(text)
  792. format = "iiPPiiPiiii"
  793. buf = struct.pack(
  794. format, mask, cxy, text_addr, hbm, text_len, fmt, param, image, order, 0, 0
  795. )
  796. return array.array("b", buf), extra
  797. # Device notification stuff
  798. # Generic function for packing a DEV_BROADCAST_* structure - generally used
  799. # by the other PackDEV_BROADCAST_* functions in this module.
  800. def PackDEV_BROADCAST(devicetype, rest_fmt, rest_data, extra_data=_make_bytes("")):
  801. # It seems a requirement is 4 byte alignment, even for the 'BYTE data[1]'
  802. # field (eg, that would make DEV_BROADCAST_HANDLE 41 bytes, but we must
  803. # be 44.
  804. extra_data += _make_bytes("\0" * (4 - len(extra_data) % 4))
  805. format = "iii" + rest_fmt
  806. full_size = struct.calcsize(format) + len(extra_data)
  807. data = (full_size, devicetype, 0) + rest_data
  808. return struct.pack(format, *data) + extra_data
  809. def PackDEV_BROADCAST_HANDLE(
  810. handle,
  811. hdevnotify=0,
  812. guid=_make_bytes("\0" * 16),
  813. name_offset=0,
  814. data=_make_bytes("\0"),
  815. ):
  816. return PackDEV_BROADCAST(
  817. win32con.DBT_DEVTYP_HANDLE,
  818. "PP16sl",
  819. (int(handle), int(hdevnotify), _make_memory(guid), name_offset),
  820. data,
  821. )
  822. def PackDEV_BROADCAST_VOLUME(unitmask, flags):
  823. return PackDEV_BROADCAST(win32con.DBT_DEVTYP_VOLUME, "II", (unitmask, flags))
  824. def PackDEV_BROADCAST_DEVICEINTERFACE(classguid, name=""):
  825. if win32gui.UNICODE:
  826. # This really means "is py3k?" - so not accepting bytes is OK
  827. if not isinstance(name, str):
  828. raise TypeError("Must provide unicode for the name")
  829. name = name.encode("utf-16le")
  830. else:
  831. # py2k was passed a unicode object - encode as mbcs.
  832. if isinstance(name, str):
  833. name = name.encode("mbcs")
  834. # 16 bytes for the IID followed by \0 term'd string.
  835. rest_fmt = "16s%ds" % len(name)
  836. # _make_memory(iid) hoops necessary to get the raw IID bytes.
  837. rest_data = (_make_memory(pywintypes.IID(classguid)), name)
  838. return PackDEV_BROADCAST(win32con.DBT_DEVTYP_DEVICEINTERFACE, rest_fmt, rest_data)
  839. # An object returned by UnpackDEV_BROADCAST.
  840. class DEV_BROADCAST_INFO:
  841. def __init__(self, devicetype, **kw):
  842. self.devicetype = devicetype
  843. self.__dict__.update(kw)
  844. def __str__(self):
  845. return "DEV_BROADCAST_INFO:" + str(self.__dict__)
  846. # Support for unpacking the 'lparam'
  847. def UnpackDEV_BROADCAST(lparam):
  848. if lparam == 0:
  849. return None
  850. hdr_format = "iii"
  851. hdr_size = struct.calcsize(hdr_format)
  852. hdr_buf = win32gui.PyGetMemory(lparam, hdr_size)
  853. size, devtype, reserved = struct.unpack("iii", hdr_buf)
  854. # Due to x64 alignment issues, we need to use the full format string over
  855. # the entire buffer. ie, on x64:
  856. # calcsize('iiiP') != calcsize('iii')+calcsize('P')
  857. buf = win32gui.PyGetMemory(lparam, size)
  858. extra = x = {}
  859. if devtype == win32con.DBT_DEVTYP_HANDLE:
  860. # 2 handles, a GUID, a LONG and possibly an array following...
  861. fmt = hdr_format + "PP16sl"
  862. (
  863. _,
  864. _,
  865. _,
  866. x["handle"],
  867. x["hdevnotify"],
  868. guid_bytes,
  869. x["nameoffset"],
  870. ) = struct.unpack(fmt, buf[: struct.calcsize(fmt)])
  871. x["eventguid"] = pywintypes.IID(guid_bytes, True)
  872. elif devtype == win32con.DBT_DEVTYP_DEVICEINTERFACE:
  873. fmt = hdr_format + "16s"
  874. _, _, _, guid_bytes = struct.unpack(fmt, buf[: struct.calcsize(fmt)])
  875. x["classguid"] = pywintypes.IID(guid_bytes, True)
  876. x["name"] = win32gui.PyGetString(lparam + struct.calcsize(fmt))
  877. elif devtype == win32con.DBT_DEVTYP_VOLUME:
  878. # int mask and flags
  879. fmt = hdr_format + "II"
  880. _, _, _, x["unitmask"], x["flags"] = struct.unpack(
  881. fmt, buf[: struct.calcsize(fmt)]
  882. )
  883. else:
  884. raise NotImplementedError("unknown device type %d" % (devtype,))
  885. return DEV_BROADCAST_INFO(devtype, **extra)