Source code of Windows XP (NT5)
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.

768 lines
19 KiB

  1. //=--------------------------------------------------------------------------=
  2. // PropPage.Cpp
  3. //=--------------------------------------------------------------------------=
  4. // Copyright 1995-1996 Microsoft Corporation. All Rights Reserved.
  5. //
  6. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  7. // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  8. // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  9. // PARTICULAR PURPOSE.
  10. //=--------------------------------------------------------------------------=
  11. //
  12. // implementation of CPropertyPage object.
  13. //
  14. #include "IPServer.H"
  15. #include "PropPage.H"
  16. #include "Util.H"
  17. #include "Globals.H"
  18. // for ASSERT and FAIL
  19. //
  20. SZTHISFILE
  21. // this variable is used to pass the pointer to the object to the hwnd.
  22. //
  23. static CPropertyPage *s_pLastPageCreated;
  24. //=--------------------------------------------------------------------------=
  25. // CPropertyPage::CPropertyPage
  26. //=--------------------------------------------------------------------------=
  27. // constructor.
  28. //
  29. // Parameters:
  30. // IUnknown * - [in] controlling unknown
  31. // int - [in] object type.
  32. //
  33. // Notes:
  34. //
  35. #pragma warning(disable:4355) // using 'this' in constructor
  36. CPropertyPage::CPropertyPage
  37. (
  38. IUnknown *pUnkOuter,
  39. int iObjectType
  40. )
  41. : CUnknownObject(pUnkOuter, this), m_ObjectType(iObjectType)
  42. {
  43. // initialize various dudes.
  44. //
  45. m_pPropertyPageSite = NULL;
  46. m_hwnd = NULL;
  47. m_fDirty = FALSE;
  48. m_fActivated = FALSE;
  49. m_cObjects = 0;
  50. }
  51. #pragma warning(default:4355) // using 'this' in constructor
  52. //=--------------------------------------------------------------------------=
  53. // CPropertyPage::~CPropertyPage
  54. //=--------------------------------------------------------------------------=
  55. // destructor.
  56. //
  57. // Notes:
  58. //
  59. CPropertyPage::~CPropertyPage()
  60. {
  61. // clean up our window.
  62. //
  63. if (m_hwnd) {
  64. SetWindowLongPtr(m_hwnd, GWLP_USERDATA, (LONG_PTR)0xffffffff);
  65. DestroyWindow(m_hwnd);
  66. }
  67. // release all the objects we're holding on to.
  68. //
  69. m_ReleaseAllObjects();
  70. // release the site
  71. //
  72. QUICK_RELEASE(m_pPropertyPageSite);
  73. }
  74. //=--------------------------------------------------------------------------=
  75. // CPropertyPage::InternalQueryInterface
  76. //=--------------------------------------------------------------------------=
  77. // we support IPP and IPP2.
  78. //
  79. // Parameters:
  80. // REFIID - [in] interface they want
  81. // void ** - [out] where they want to put the resulting object ptr.
  82. //
  83. // Output:
  84. // HRESULT - S_OK, E_NOINTERFACE
  85. //
  86. // Notes:
  87. //
  88. HRESULT CPropertyPage::InternalQueryInterface
  89. (
  90. REFIID riid,
  91. void **ppvObjOut
  92. )
  93. {
  94. IUnknown *pUnk;
  95. *ppvObjOut = NULL;
  96. if (DO_GUIDS_MATCH(IID_IPropertyPage, riid)) {
  97. pUnk = (IUnknown *)this;
  98. } else if (DO_GUIDS_MATCH(IID_IPropertyPage2, riid)) {
  99. pUnk = (IUnknown *)this;
  100. } else {
  101. return CUnknownObject::InternalQueryInterface(riid, ppvObjOut);
  102. }
  103. pUnk->AddRef();
  104. *ppvObjOut = (void *)pUnk;
  105. return S_OK;
  106. }
  107. //=--------------------------------------------------------------------------=
  108. // CPropertyPage::SetPageSite [IPropertyPage]
  109. //=--------------------------------------------------------------------------=
  110. // the initialization function for a property page through which the page
  111. // receives an IPropertyPageSite pointer.
  112. //
  113. // Parameters:
  114. // IPropertyPageSite * - [in] new site.
  115. //
  116. // Output:
  117. // HRESULT
  118. //
  119. // Notes;
  120. //
  121. STDMETHODIMP CPropertyPage::SetPageSite
  122. (
  123. IPropertyPageSite *pPropertyPageSite
  124. )
  125. {
  126. RELEASE_OBJECT(m_pPropertyPageSite);
  127. m_pPropertyPageSite = pPropertyPageSite;
  128. ADDREF_OBJECT(pPropertyPageSite);
  129. return S_OK;
  130. }
  131. //=--------------------------------------------------------------------------=
  132. // CPropertyPage::Activate [IPropertyPage]
  133. //=--------------------------------------------------------------------------=
  134. // instructs the page to create it's display window as a child of hwndparent
  135. // and to position it according to prc.
  136. //
  137. // Parameters:
  138. // HWND - [in] parent window
  139. // LPCRECT - [in] where to position ourselves
  140. // BOOL - [in] whether we're modal or not.
  141. //
  142. // Output:
  143. // HRESULT
  144. //
  145. // Notes:
  146. //
  147. STDMETHODIMP CPropertyPage::Activate
  148. (
  149. HWND hwndParent,
  150. LPCRECT prcBounds,
  151. BOOL fModal
  152. )
  153. {
  154. HRESULT hr;
  155. // first make sure the dialog window is loaded and created.
  156. //
  157. hr = m_EnsureLoaded();
  158. RETURN_ON_FAILURE(hr);
  159. // set our parent window if we haven't done so yet.
  160. //
  161. if (!m_fActivated) {
  162. SetParent(m_hwnd, hwndParent);
  163. m_fActivated = TRUE;
  164. }
  165. // now move ourselves to where we're told to be and show ourselves
  166. //
  167. Move(prcBounds);
  168. ShowWindow(m_hwnd, SW_SHOW);
  169. return S_OK;
  170. }
  171. //=--------------------------------------------------------------------------=
  172. // CPropertyPage::Deactivate [IPropertyPage]
  173. //=--------------------------------------------------------------------------=
  174. // instructs the page to destroy the window created in activate
  175. //
  176. // Output:
  177. // HRESULT
  178. //
  179. // Notes:
  180. //
  181. STDMETHODIMP CPropertyPage::Deactivate
  182. (
  183. void
  184. )
  185. {
  186. // blow away yon window.
  187. //
  188. if (m_hwnd)
  189. DestroyWindow(m_hwnd);
  190. m_hwnd = NULL;
  191. m_fActivated = FALSE;
  192. return S_OK;
  193. }
  194. //=--------------------------------------------------------------------------=
  195. // CPropertyPage::GetPageInfo [IPropertyPage]
  196. //=--------------------------------------------------------------------------=
  197. // asks the page to fill a PROPPAGEINFO structure
  198. //
  199. // Parameters:
  200. // PROPPAGEINFO * - [out] where to put info.
  201. //
  202. // Output:
  203. // HRESULT
  204. //
  205. // Notes:
  206. //
  207. STDMETHODIMP CPropertyPage::GetPageInfo
  208. (
  209. PROPPAGEINFO *pPropPageInfo
  210. )
  211. {
  212. RECT rect;
  213. CHECK_POINTER(pPropPageInfo);
  214. m_EnsureLoaded();
  215. // clear it out first.
  216. //
  217. memset(pPropPageInfo, 0, sizeof(PROPPAGEINFO));
  218. pPropPageInfo->pszTitle = OLESTRFROMRESID(TITLEIDOFPROPPAGE(m_ObjectType));
  219. pPropPageInfo->pszDocString = OLESTRFROMRESID(DOCSTRINGIDOFPROPPAGE(m_ObjectType));
  220. pPropPageInfo->pszHelpFile = OLESTRFROMANSI(HELPFILEOFPROPPAGE(m_ObjectType));
  221. pPropPageInfo->dwHelpContext = HELPCONTEXTOFPROPPAGE(m_ObjectType);
  222. if (!(pPropPageInfo->pszTitle && pPropPageInfo->pszDocString && pPropPageInfo->pszHelpFile))
  223. goto CleanUp;
  224. // if we've got a window yet, go and set up the size information they want.
  225. //
  226. if (m_hwnd) {
  227. GetWindowRect(m_hwnd, &rect);
  228. pPropPageInfo->size.cx = rect.right - rect.left;
  229. pPropPageInfo->size.cy = rect.bottom - rect.top;
  230. }
  231. return S_OK;
  232. CleanUp:
  233. if (pPropPageInfo->pszDocString) CoTaskMemFree(pPropPageInfo->pszDocString);
  234. if (pPropPageInfo->pszHelpFile) CoTaskMemFree(pPropPageInfo->pszHelpFile);
  235. if (pPropPageInfo->pszTitle) CoTaskMemFree(pPropPageInfo->pszTitle);
  236. return E_OUTOFMEMORY;
  237. }
  238. //=--------------------------------------------------------------------------=
  239. // CPropertyPage::SetObjects [IPropertyPage]
  240. //=--------------------------------------------------------------------------=
  241. // provides the page with the objects being affected by the changes.
  242. //
  243. // Parameters:
  244. // ULONG - [in] count of objects.
  245. // IUnknown ** - [in] objects.
  246. //
  247. // Output:
  248. // HRESULT
  249. //
  250. // Notes:
  251. //
  252. STDMETHODIMP CPropertyPage::SetObjects
  253. (
  254. ULONG cObjects,
  255. IUnknown **ppUnkObjects
  256. )
  257. {
  258. HRESULT hr;
  259. ULONG x;
  260. // free up all the old objects first.
  261. //
  262. m_ReleaseAllObjects();
  263. if (!cObjects)
  264. return S_OK;
  265. // now go and set up the new ones.
  266. //
  267. m_ppUnkObjects = (IUnknown **)HeapAlloc(g_hHeap, 0, cObjects * sizeof(IUnknown *));
  268. RETURN_ON_NULLALLOC(m_ppUnkObjects);
  269. // loop through and copy over all the objects.
  270. //
  271. for (x = 0; x < cObjects; x++) {
  272. m_ppUnkObjects[x] = ppUnkObjects[x];
  273. ADDREF_OBJECT(m_ppUnkObjects[x]);
  274. }
  275. // go and tell the object that there are new objects
  276. //
  277. hr = S_OK;
  278. m_cObjects = cObjects;
  279. // if we've got a window, go and notify it that we've got new objects.
  280. //
  281. if (m_hwnd)
  282. SendMessage(m_hwnd, PPM_NEWOBJECTS, 0, (LPARAM)&hr);
  283. if (SUCCEEDED(hr)) m_fDirty = FALSE;
  284. return hr;
  285. }
  286. //=--------------------------------------------------------------------------=
  287. // CPropertyPage::Show [IPropertyPage]
  288. //=--------------------------------------------------------------------------=
  289. // asks the page to show or hide its window
  290. //
  291. // Parameters:
  292. // UINT - [in] whether to show or hide
  293. //
  294. // Output:
  295. // HRESULT
  296. //
  297. // Notes:
  298. //
  299. STDMETHODIMP CPropertyPage::Show
  300. (
  301. UINT nCmdShow
  302. )
  303. {
  304. if (m_hwnd)
  305. ShowWindow(m_hwnd, nCmdShow);
  306. else
  307. return E_UNEXPECTED;
  308. return S_OK;
  309. }
  310. //=--------------------------------------------------------------------------=
  311. // CPropertyPage::Move [IPropertyPage]
  312. //=--------------------------------------------------------------------------=
  313. // asks the page to relocate and resize itself to a position other than what
  314. // was specified through Activate
  315. //
  316. // Parameters:
  317. // LPCRECT - [in] new position and size
  318. //
  319. // Output:
  320. // HRESULT
  321. //
  322. // Notes:
  323. //
  324. STDMETHODIMP CPropertyPage::Move
  325. (
  326. LPCRECT prcBounds
  327. )
  328. {
  329. // do what they sez
  330. //
  331. if (m_hwnd)
  332. SetWindowPos(m_hwnd, NULL, prcBounds->left, prcBounds->top,
  333. prcBounds->right - prcBounds->left,
  334. prcBounds->bottom - prcBounds->top,
  335. SWP_NOZORDER);
  336. else
  337. return E_UNEXPECTED;
  338. return S_OK;
  339. }
  340. //=--------------------------------------------------------------------------=
  341. // CPropertyPage::IsPageDirty [IPropertyPage]
  342. //=--------------------------------------------------------------------------=
  343. // asks the page whether it has changed its state
  344. //
  345. // Output
  346. // S_OK - yep
  347. // S_FALSE - nope
  348. //
  349. // Notes:
  350. //
  351. STDMETHODIMP CPropertyPage::IsPageDirty
  352. (
  353. void
  354. )
  355. {
  356. return m_fDirty ? S_OK : S_FALSE;
  357. }
  358. //=--------------------------------------------------------------------------=
  359. // CPropertyPage::Apply [IPropertyPage]
  360. //=--------------------------------------------------------------------------=
  361. // instructs the page to send its changes to all the objects passed through
  362. // SetObjects()
  363. //
  364. // Output:
  365. // HRESULT
  366. //
  367. // Notes:
  368. //
  369. STDMETHODIMP CPropertyPage::Apply
  370. (
  371. void
  372. )
  373. {
  374. HRESULT hr = S_OK;
  375. if (m_hwnd) {
  376. SendMessage(m_hwnd, PPM_APPLY, 0, (LPARAM)&hr);
  377. RETURN_ON_FAILURE(hr);
  378. if (m_fDirty) {
  379. m_fDirty = FALSE;
  380. if (m_pPropertyPageSite)
  381. m_pPropertyPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY);
  382. }
  383. } else
  384. return E_UNEXPECTED;
  385. return S_OK;
  386. }
  387. //=--------------------------------------------------------------------------=
  388. // CPropertyPage::Help [IPropertyPage]
  389. //=--------------------------------------------------------------------------=
  390. // instructs the page that the help button was clicked.
  391. //
  392. // Parameters:
  393. // LPCOLESTR - [in] help directory
  394. //
  395. // Output:
  396. // HRESULT
  397. //
  398. // Notes:
  399. //
  400. STDMETHODIMP CPropertyPage::Help
  401. (
  402. LPCOLESTR pszHelpDir
  403. )
  404. {
  405. BOOL f;
  406. ASSERT(m_hwnd, "How can somebody have clicked Help, but we don't have an hwnd?");
  407. // oblige them and show the help.
  408. //
  409. MAKE_ANSIPTR_FROMWIDE(psz, pszHelpDir);
  410. f = WinHelp(m_hwnd, psz, HELP_CONTEXT, HELPCONTEXTOFPROPPAGE(m_ObjectType));
  411. return f ? S_OK : E_FAIL;
  412. }
  413. //=--------------------------------------------------------------------------=
  414. // CPropertyPage::TranslateAccelerator [IPropertyPage]
  415. //=--------------------------------------------------------------------------=
  416. // informs the page of keyboard events, allowing it to implement it's own
  417. // keyboard interface.
  418. //
  419. // Parameters:
  420. // LPMSG - [in] message that triggered this
  421. //
  422. // Output:
  423. // HRESULT
  424. //
  425. // Notes:
  426. //
  427. STDMETHODIMP CPropertyPage::TranslateAccelerator
  428. (
  429. LPMSG pmsg
  430. )
  431. {
  432. ASSERT(m_hwnd, "How can we get a TranslateAccelerator call if we're not visible?");
  433. // just pass this message on to the dialog proc and see if they want it.
  434. //
  435. return IsDialogMessage(m_hwnd, pmsg) ? S_OK : S_FALSE;
  436. }
  437. //=--------------------------------------------------------------------------=
  438. // CPropertyPage::EditProperty [IPropertyPage2]
  439. //=--------------------------------------------------------------------------=
  440. // instructs the page to set the focus to the property matching the dispid.
  441. //
  442. // Parameters:
  443. // DISPID - [in] dispid of property to set focus to.
  444. //
  445. // Output:
  446. // HRESULT
  447. //
  448. // Notes:
  449. //
  450. STDMETHODIMP CPropertyPage::EditProperty
  451. (
  452. DISPID dispid
  453. )
  454. {
  455. HRESULT hr = E_NOTIMPL;
  456. // send the message on to the control, and see what they want to do with it.
  457. //
  458. SendMessage(m_hwnd, PPM_EDITPROPERTY, (WPARAM)dispid, (LPARAM)&hr);
  459. return hr;
  460. }
  461. //=--------------------------------------------------------------------------=
  462. // CPropertyPage::m_EnsureLoaded
  463. //=--------------------------------------------------------------------------=
  464. // makes sure the dialog is actually loaded
  465. //
  466. // Output:
  467. // HRESULT
  468. //
  469. // Notes:
  470. //
  471. HRESULT CPropertyPage::m_EnsureLoaded
  472. (
  473. void
  474. )
  475. {
  476. HRESULT hr = S_OK;
  477. // duh
  478. //
  479. if (m_hwnd)
  480. return S_OK;
  481. // set up the global variable so that when we're in the dialog proc, we can
  482. // stuff this in the hwnd
  483. //
  484. // crit sect this whole creation process for apartment threading support.
  485. //
  486. EnterCriticalSection(&g_CriticalSection);
  487. s_pLastPageCreated = this;
  488. // create the dialog window
  489. //
  490. CreateDialog(GetResourceHandle(), TEMPLATENAMEOFPROPPAGE(m_ObjectType), GetParkingWindow(),
  491. CPropertyPage::PropPageDlgProc);
  492. ASSERT(m_hwnd, "Couldn't load Dialog Resource!!!");
  493. if (!m_hwnd) {
  494. LeaveCriticalSection(&g_CriticalSection);
  495. return HRESULT_FROM_WIN32(GetLastError());
  496. }
  497. // clean up variables and leave the critical section
  498. //
  499. s_pLastPageCreated = NULL;
  500. LeaveCriticalSection(&g_CriticalSection);
  501. // go and notify the window that it should pick up any objects that are
  502. // available
  503. //
  504. SendMessage(m_hwnd, PPM_NEWOBJECTS, 0, (LPARAM)&hr);
  505. return hr;
  506. }
  507. //=--------------------------------------------------------------------------=
  508. // CPropertyPage::m_ReleaseAllObjects
  509. //=--------------------------------------------------------------------------=
  510. // releases all the objects that we're working with
  511. //
  512. // Notes:
  513. //
  514. void CPropertyPage::m_ReleaseAllObjects
  515. (
  516. void
  517. )
  518. {
  519. HRESULT hr;
  520. UINT x;
  521. if (!m_cObjects)
  522. return;
  523. // some people will want to stash pointers in the PPM_INITOBJECTS case, so
  524. // we want to tell them to release them now.
  525. //
  526. SendMessage(m_hwnd, PPM_FREEOBJECTS, 0, (LPARAM)&hr);
  527. // loop through and blow them all away.
  528. //
  529. for (x = 0; x < m_cObjects; x++)
  530. QUICK_RELEASE(m_ppUnkObjects[x]);
  531. HeapFree(g_hHeap, 0, m_ppUnkObjects);
  532. m_ppUnkObjects = NULL;
  533. }
  534. //=--------------------------------------------------------------------------=
  535. // CPropertyPage::PropPageDlgProc
  536. //=--------------------------------------------------------------------------=
  537. // static global helper dialog proc that gets called before we pass the message
  538. // on to anybody ..
  539. //
  540. // Parameters:
  541. // - see win32sdk docs on DialogProc
  542. //
  543. // Notes:
  544. //
  545. INT_PTR CALLBACK CPropertyPage::PropPageDlgProc
  546. (
  547. HWND hwnd,
  548. UINT msg,
  549. WPARAM wParam,
  550. LPARAM lParam
  551. )
  552. {
  553. CPropertyPage *pPropertyPage;
  554. // get the window long, and see if it's been set to the object this hwnd
  555. // is operating against. if not, go and set it now.
  556. //
  557. pPropertyPage = (CPropertyPage *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  558. if (pPropertyPage == (CPropertyPage *)-1)
  559. return FALSE;
  560. if (!pPropertyPage) {
  561. SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)s_pLastPageCreated);
  562. pPropertyPage = s_pLastPageCreated;
  563. pPropertyPage->m_hwnd = hwnd;
  564. }
  565. ASSERT(pPropertyPage, "Uh oh. Got a window, but no CpropertyPage for it!");
  566. // just call the user dialog proc and see if they want to do anything.
  567. //
  568. return pPropertyPage->DialogProc(hwnd, msg, wParam, lParam);
  569. }
  570. //=--------------------------------------------------------------------------=
  571. // CPropertyPage::FirstControl
  572. //=--------------------------------------------------------------------------=
  573. // returns the first controlish object that we are showing ourselves for.
  574. // returns a cookie that must be passed in for Next ...
  575. //
  576. // Parameters:
  577. // DWORD * - [out] cookie to be used for Next
  578. //
  579. // Output:
  580. // IUnknown *
  581. //
  582. // Notes:
  583. //
  584. IUnknown *CPropertyPage::FirstControl
  585. (
  586. DWORD *pdwCookie
  587. )
  588. {
  589. // just use the implementation of NEXT.
  590. //
  591. *pdwCookie = 0;
  592. return NextControl(pdwCookie);
  593. }
  594. //=--------------------------------------------------------------------------=
  595. // CPropertyPage::NextControl
  596. //=--------------------------------------------------------------------------=
  597. // returns the next control in the chain of people to work with given a cookie
  598. //
  599. // Parameters:
  600. // DWORD * - [in/out] cookie to get next from, and new cookie.
  601. //
  602. // Output:
  603. // IUnknown *
  604. //
  605. // Notes:
  606. //
  607. IUnknown *CPropertyPage::NextControl
  608. (
  609. DWORD *pdwCookie
  610. )
  611. {
  612. UINT i;
  613. // go looking through all the objects that we've got, and find the
  614. // first non-null one.
  615. //
  616. for (i = *pdwCookie; i < m_cObjects; i++) {
  617. if (!m_ppUnkObjects[i]) continue;
  618. *pdwCookie = i + 1; // + 1 so we start at next item next time
  619. return m_ppUnkObjects[i];
  620. }
  621. // couldn't find it .
  622. //
  623. *pdwCookie = 0xffffffff;
  624. return NULL;
  625. }
  626. //=--------------------------------------------------------------------------=
  627. // CPropertyPage::MakeDirty [helper, callable]
  628. //=--------------------------------------------------------------------------=
  629. // marks a page as dirty.
  630. //
  631. // Notes:
  632. //
  633. void CPropertyPage::MakeDirty
  634. (
  635. void
  636. )
  637. {
  638. m_fDirty = TRUE;
  639. if (m_pPropertyPageSite)
  640. m_pPropertyPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY|PROPPAGESTATUS_VALIDATE);
  641. }
  642. // from Globals.C
  643. //
  644. extern HINSTANCE g_hInstResources;
  645. //=--------------------------------------------------------------------------=
  646. // CPropertyPage::GetResourceHandle [helper, callable]
  647. //=--------------------------------------------------------------------------=
  648. // returns current resource handle, based on pagesites ambient LCID.
  649. //
  650. // Output:
  651. // HINSTANCE
  652. //
  653. // Notes:
  654. //
  655. HINSTANCE CPropertyPage::GetResourceHandle
  656. (
  657. void
  658. )
  659. {
  660. if (!g_fSatelliteLocalization)
  661. return g_hInstance;
  662. // if we've already got it, then there's not all that much to do.
  663. // don't need to crit sect this one right here since even if they do fall
  664. // into the ::GetResourceHandle call, it'll properly deal with things.
  665. //
  666. if (g_hInstResources)
  667. return g_hInstResources;
  668. // we'll get the ambient localeid from the host, and pass that on to the
  669. // automation object.
  670. //
  671. // enter a critical section for g_lcidLocale and g_fHavelocale
  672. //
  673. EnterCriticalSection(&g_CriticalSection);
  674. if (!g_fHaveLocale) {
  675. if (m_pPropertyPageSite) {
  676. m_pPropertyPageSite->GetLocaleID(&g_lcidLocale);
  677. g_fHaveLocale = TRUE;
  678. }
  679. }
  680. LeaveCriticalSection(&g_CriticalSection);
  681. return ::GetResourceHandle();
  682. }