Leaked source code of windows server 2003
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.

719 lines
22 KiB

  1. /*//$$***************************************************************
  2. //
  3. //
  4. // WABEX.C
  5. //
  6. // Main source file for WABEX.DLL, a sample DLL that demonstrates
  7. // how to extend the wab properties UI - enabling WAB clients to add
  8. // their own PropertySheets to the UI displayed for details on contacts
  9. // and groups. This demo uses a couple of named properties to show
  10. // how you can extend the wab with your own UI for your own named props
  11. //
  12. //
  13. // Created: 9/26/97 vikramm
  14. //
  15. //********************************************************************/
  16. #include <windows.h>
  17. #include "resource.h"
  18. #include <wab.h>
  19. // Globally cached hInstance for the DLL
  20. //
  21. HINSTANCE hinstApp = NULL;
  22. // For the purposes of this sample, we will use 2 named properties,
  23. // HomeTown and SportsTeam
  24. // This demo's private GUID:
  25. // {2B6D7EE0-36AB-11d1-9ABC-00A0C91F9C8B}
  26. static const GUID WAB_ExtDemoGuid =
  27. { 0x2b6d7ee0, 0x36ab, 0x11d1, { 0x9a, 0xbc, 0x0, 0xa0, 0xc9, 0x1f, 0x9c, 0x8b } };
  28. static const LPTSTR lpMyPropNames[] =
  29. {
  30. "MyHomeTown",
  31. "MySportsTeam"
  32. };
  33. enum _MyTags
  34. {
  35. myHomeTown = 0,
  36. mySportsTeam,
  37. myMax
  38. };
  39. ULONG MyPropTags[myMax];
  40. ULONG PR_MY_HOMETOWN;
  41. ULONG PR_MY_SPORTSTEAM;
  42. //
  43. // Function prototypes:
  44. //
  45. HRESULT InitNamedProps(LPWABEXTDISPLAY lpWED);
  46. BOOL CALLBACK fnDetailsPropDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  47. void InitializeUI(HWND hDlg, LPWABEXTDISPLAY lpWED);
  48. void SetDataInUI(HWND hDlg, LPWABEXTDISPLAY lpWED);
  49. BOOL GetDataFromUI(HWND hDlg, LPWABEXTDISPLAY lpWED);
  50. UINT CALLBACK fnCallback( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp );
  51. void UpdateDisplayNameInfo(HWND hDlg, LPWABEXTDISPLAY lpWED);
  52. BOOL bUpdatePropSheetData(HWND hDlg, LPWABEXTDISPLAY lpWED);
  53. /*//$$****************************************************************
  54. //
  55. // DllEntryPoint
  56. //
  57. // Entry point for win32 - just used here to cache the DLL instance
  58. //
  59. //********************************************************************/
  60. BOOL WINAPI
  61. DllEntryPoint(HINSTANCE hinst, DWORD dwReason, LPVOID lpvReserved)
  62. {
  63. switch ((short)dwReason)
  64. {
  65. case DLL_PROCESS_ATTACH:
  66. hinstApp = hinst;
  67. break;
  68. }
  69. return TRUE;
  70. }
  71. /*//$$****************************************************************
  72. //
  73. // AddExtendedPropPage
  74. //
  75. // This is the main exported function that WAB will call. In this
  76. // function, you create your PropertyPage and pass it to the WAB
  77. // through the lpfnAddPage function. The WAB will automatically call
  78. // DestroyPropertySheetPage on exit to clean up the page you create.
  79. // The lParam passed into this function should be set as the lParam
  80. // on the property sheet you create, as shown below.
  81. //
  82. // Input Params:
  83. //
  84. // lpfnPage - pointer to AddPropSheetPage function proc you call to
  85. // pass your hpage to the WAB
  86. // lParam - LPARAM you set on your PropSheet page and also pass back to
  87. // wab in the lpfnAddPage. This lParam is a pointer to a
  88. // WABEXTDISPLAY struct that your propsheet will use to
  89. // exchange information with the WAB
  90. //
  91. // ***IMPORTANT*** Make sure your callback function is declared as a
  92. // WINAPI otherwise ugly things happen to the stack when
  93. // this function is called
  94. //
  95. //********************************************************************/
  96. HRESULT WINAPI AddExtendedPropPage(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam, int * lpnPage)
  97. {
  98. PROPSHEETPAGE psp;
  99. HPROPSHEETPAGE hpage;
  100. LPWABEXTDISPLAY * lppWED = (LPWABEXTDISPLAY *) lParam;
  101. LPWABEXTDISPLAY lpWED = NULL;
  102. // Check that there is space to create this property sheet
  103. // WAB can support a maximum of WAB_MAX_EXT_PROPSHEETS extension sheets
  104. if(WAB_MAX_EXT_PROPSHEETS <= *lpnPage)
  105. return E_FAIL;
  106. lpWED = &((*lppWED)[*lpnPage]);
  107. psp.dwSize = sizeof(psp);
  108. psp.dwFlags = PSP_USETITLE |
  109. PSP_USECALLBACK;// Specify this callback only if you need
  110. // a seperate function to perform special
  111. // initialization and cleanup when the
  112. // property sheet is created or destroyed.
  113. psp.hInstance = hinstApp;
  114. psp.pszTemplate = MAKEINTRESOURCE(IDD_PROP); // Dialog resource
  115. psp.pfnDlgProc = fnDetailsPropDlgProc; // Message handler function
  116. psp.pcRefParent = NULL; //ignored
  117. psp.pfnCallback = fnCallback; // Callback function if PSP_USECALLBACK specified
  118. psp.lParam = (LPARAM) lpWED; // *** VERY IMPORTANT *** dont forget to do this
  119. psp.pszTitle = "Extension 1"; // Title for your tab
  120. /*
  121. // If you have some private data of your own that you want to cache
  122. // on your page, you can add it to the WABEXTDISPLAY struct
  123. // However WAB will not free this data so you must do it yourself on
  124. // cleanup
  125. {
  126. LPMYDATA lpMyData;
  127. // Create Data Here
  128. lpWED->lParam = (LPARAM) lpMyData;
  129. }
  130. */
  131. // Check if we can retrieve our named props .. if we cant,
  132. // no point creating this dialog ..
  133. //
  134. if(HR_FAILED(InitNamedProps(lpWED)))
  135. return E_FAIL;
  136. // Create the property sheet
  137. //
  138. hpage = CreatePropertySheetPage(&psp);
  139. if(hpage)
  140. {
  141. // Pass this hpage back to the WAB
  142. //
  143. if(!lpfnAddPage(hpage, (LPARAM) lpWED))
  144. DestroyPropertySheetPage(hpage);
  145. else
  146. (*lpnPage)++;
  147. //return NOERROR;
  148. }
  149. // if you are creating more than one property sheet, repeat the above as follows
  150. // Check that there is space to create this property sheet
  151. // WAB can support a maximum of WAB_MAX_EXT_PROPSHEETS extension sheets
  152. if(WAB_MAX_EXT_PROPSHEETS <= *lpnPage)
  153. return E_FAIL;
  154. lpWED = &((*lppWED)[*lpnPage]);
  155. psp.pszTemplate = MAKEINTRESOURCE(IDD_PROP2); // Dialog resource
  156. psp.pfnDlgProc = fnDetailsPropDlgProc; // Message handler function
  157. psp.pszTitle = "Extension 2"; // Title for your tab
  158. psp.lParam = (LPARAM) lpWED; // *** VERY IMPORTANT *** dont forget to do this
  159. // Create the property sheet
  160. //
  161. hpage = CreatePropertySheetPage(&psp);
  162. if(hpage)
  163. {
  164. // Pass this hpage back to the WAB
  165. //
  166. if(!lpfnAddPage(hpage, (LPARAM) lpWED))
  167. DestroyPropertySheetPage(hpage);
  168. else
  169. (*lpnPage)++;
  170. return NOERROR;
  171. }
  172. return E_FAIL;
  173. }
  174. /*//$$****************************************************************
  175. //
  176. // InitNamedProps
  177. //
  178. // Gets the PropTags for the Named Props this app is interested in
  179. //
  180. //********************************************************************/
  181. HRESULT InitNamedProps(LPWABEXTDISPLAY lpWED)
  182. {
  183. // The lpWED provides a lpMailUser object for
  184. // the specific purpose of retrieving named properties by
  185. // calling GetNamesFromIDs. The lpMailUser object is otherwise
  186. // a blank object - you cant get properties from it and shouldnt
  187. // set properties on it
  188. //
  189. ULONG i;
  190. HRESULT hr = E_FAIL;
  191. LPSPropTagArray lptaMyProps = NULL;
  192. LPMAPINAMEID * lppMyPropNames;
  193. SCODE sc;
  194. LPMAILUSER lpMailUser = (LPMAILUSER) lpWED->lpPropObj;
  195. WCHAR szBuf[myMax][MAX_PATH];
  196. if(!lpMailUser)
  197. goto err;
  198. sc = lpWED->lpWABObject->lpVtbl->AllocateBuffer(lpWED->lpWABObject,
  199. sizeof(LPMAPINAMEID) * myMax,
  200. (LPVOID *) &lppMyPropNames);
  201. if(sc)
  202. {
  203. hr = ResultFromScode(sc);
  204. goto err;
  205. }
  206. for(i=0;i<myMax;i++)
  207. {
  208. sc = lpWED->lpWABObject->lpVtbl->AllocateMore(lpWED->lpWABObject,
  209. sizeof(MAPINAMEID),
  210. lppMyPropNames,
  211. &(lppMyPropNames[i]));
  212. if(sc)
  213. {
  214. hr = ResultFromScode(sc);
  215. goto err;
  216. }
  217. lppMyPropNames[i]->lpguid = (LPGUID) &WAB_ExtDemoGuid;
  218. lppMyPropNames[i]->ulKind = MNID_STRING;
  219. *(szBuf[i]) = '\0';
  220. // Convert prop name to wide-char
  221. if ( !MultiByteToWideChar( GetACP(), 0, lpMyPropNames[i], -1, szBuf[i], sizeof(szBuf[i])) )
  222. {
  223. continue;
  224. }
  225. lppMyPropNames[i]->Kind.lpwstrName = (LPWSTR) szBuf[i];
  226. }
  227. hr = lpMailUser->lpVtbl->GetIDsFromNames(lpMailUser,
  228. myMax,
  229. lppMyPropNames,
  230. MAPI_CREATE,
  231. &lptaMyProps);
  232. if(HR_FAILED(hr))
  233. goto err;
  234. if(lptaMyProps)
  235. {
  236. // Set the property types on the returned props
  237. MyPropTags[myHomeTown] = PR_MY_HOMETOWN = CHANGE_PROP_TYPE(lptaMyProps->aulPropTag[myHomeTown], PT_TSTRING);
  238. MyPropTags[mySportsTeam] = PR_MY_SPORTSTEAM = CHANGE_PROP_TYPE(lptaMyProps->aulPropTag[mySportsTeam], PT_TSTRING);
  239. }
  240. err:
  241. if(lptaMyProps)
  242. lpWED->lpWABObject->lpVtbl->FreeBuffer( lpWED->lpWABObject,
  243. lptaMyProps);
  244. if(lppMyPropNames)
  245. lpWED->lpWABObject->lpVtbl->FreeBuffer( lpWED->lpWABObject,
  246. lppMyPropNames);
  247. return hr;
  248. }
  249. #define lpW_E_D ((LPWABEXTDISPLAY)pps->lParam)
  250. /*//$$****************************************************************
  251. //
  252. // fnDetailsPropDlgProc
  253. //
  254. // The dialog procedure that will handle all the windows messages for
  255. // the extended property page.
  256. //
  257. //********************************************************************/
  258. BOOL CALLBACK fnDetailsPropDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  259. {
  260. PROPSHEETPAGE * pps;
  261. pps = (PROPSHEETPAGE *) GetWindowLong(hDlg, DWL_USER);
  262. switch(uMsg)
  263. {
  264. case WM_INITDIALOG:
  265. //
  266. // The lParam on InitDialog contains the application data
  267. // Cache this on the dialog so we can retrieve it later.
  268. //
  269. SetWindowLong(hDlg,DWL_USER,lParam);
  270. pps = (PROPSHEETPAGE *) lParam;
  271. // Initialize the UI appropriately
  272. InitializeUI(hDlg, lpW_E_D);
  273. // Fill the UI with appropriate data
  274. SetDataInUI(hDlg, lpW_E_D);
  275. return TRUE;
  276. break;
  277. case WM_COMMAND:
  278. switch(HIWORD(wParam)) //check the notification code
  279. {
  280. // If data changes, we should signal back to the WAB that
  281. // the data changed. If this flag is not set, the WAB will not
  282. // write the new data back to the store!!!
  283. case EN_CHANGE: //one of the edit boxes changed - dont care which
  284. lpW_E_D->fDataChanged = TRUE;
  285. break;
  286. }
  287. break;
  288. case WM_NOTIFY:
  289. switch(((NMHDR FAR *)lParam)->code)
  290. {
  291. case PSN_SETACTIVE: //Page being activated
  292. // Get the latest display name info and update the
  293. // corresponding control
  294. UpdateDisplayNameInfo(hDlg, lpW_E_D);
  295. break;
  296. case PSN_KILLACTIVE: //Losing activation to another page or OK
  297. //
  298. // Take all the data from this prop sheet and convert it to a
  299. // SPropValue array and place the data in an appropriate place.
  300. // The advantage of doing this in the KillActive notification is
  301. // that other property sheets can scan these property arrays and
  302. // if deisred, update data on other prop sheets based on this data
  303. //
  304. bUpdatePropSheetData(hDlg, lpW_E_D);
  305. break;
  306. case PSN_RESET: //cancel
  307. break;
  308. case PSN_APPLY: //ok pressed
  309. if (!(lpW_E_D->fReadOnly))
  310. {
  311. //
  312. // Check for any required properties here
  313. // If some required property is not filled in, you can prevent
  314. // the property sheet from closing
  315. //
  316. /*
  317. if (RequiredDataNotFilledIn())
  318. {
  319. // abort this OK ... ie dont let them close
  320. SetWindowLong(hDlg, DWL_MSGRESULT, TRUE);
  321. }
  322. */
  323. }
  324. break;
  325. }
  326. break;
  327. }
  328. return 0;
  329. }
  330. int EditControls[] =
  331. {
  332. IDC_EXT_EDIT_HOME,
  333. IDC_EXT_EDIT_TEAM
  334. };
  335. /*//$$****************************************************************
  336. //
  337. // InitializeUI
  338. //
  339. // Rearranges/Sets UI based on input params
  340. //
  341. //********************************************************************/
  342. void InitializeUI(HWND hDlg, LPWABEXTDISPLAY lpWED)
  343. {
  344. // The WAB property sheets can be readonly when opening LDAP entries,
  345. // or vCards or other things. If the READONLY flag is set, set this
  346. // prop sheets controls to readonly
  347. //
  348. int i;
  349. for(i=0;i<myMax;i++)
  350. {
  351. SendDlgItemMessage( hDlg, EditControls[i], EM_SETREADONLY,
  352. (WPARAM) lpWED->fReadOnly, 0);
  353. SendDlgItemMessage( hDlg, EditControls[i], EM_SETLIMITTEXT,
  354. (WPARAM) MAX_PATH-1, 0);
  355. }
  356. return;
  357. }
  358. /*//$$****************************************************************
  359. //
  360. // SetDataInUI
  361. //
  362. // Fills in the controls with data passed in by the WAB
  363. //
  364. //********************************************************************/
  365. void SetDataInUI(HWND hDlg, LPWABEXTDISPLAY lpWED)
  366. {
  367. // Search for our private named properties and set them in the UI
  368. //
  369. ULONG ulcPropCount = 0;
  370. LPSPropValue lpPropArray = NULL;
  371. ULONG i = 0, j =0;
  372. // Get all the props from this object - one can also selectively
  373. // ask for specific props by passing in an SPropTagArray
  374. //
  375. if(!HR_FAILED(lpWED->lpPropObj->lpVtbl->GetProps(lpWED->lpPropObj,
  376. NULL, 0,
  377. &ulcPropCount,
  378. &lpPropArray)))
  379. {
  380. if(ulcPropCount && lpPropArray)
  381. {
  382. for(i=0;i<ulcPropCount;i++)
  383. {
  384. for(j=0;j<myMax;j++)
  385. {
  386. if(lpPropArray[i].ulPropTag == MyPropTags[j])
  387. {
  388. SetWindowText( GetDlgItem(hDlg, EditControls[j]),
  389. lpPropArray[i].Value.LPSZ);
  390. break;
  391. }
  392. }
  393. }
  394. }
  395. }
  396. if(lpPropArray)
  397. lpWED->lpWABObject->lpVtbl->FreeBuffer(lpWED->lpWABObject, lpPropArray);
  398. return;
  399. }
  400. /*//$$****************************************************************
  401. //
  402. // GetDataFromUI
  403. //
  404. // Retrieves data from the UI and passes back to the WAB
  405. //
  406. //********************************************************************/
  407. BOOL GetDataFromUI(HWND hDlg, LPWABEXTDISPLAY lpWED)
  408. {
  409. TCHAR szData[myMax][MAX_PATH];
  410. int i;
  411. ULONG ulIndex = 0;
  412. ULONG ulcPropCount = 0;
  413. LPSPropValue lpPropArray = NULL;
  414. SCODE sc;
  415. BOOL bRet = FALSE;
  416. int nIndex = lpWED->nIndexNumber; // position of page in sheets
  417. // Did any data change that we have to care about ?
  418. // If nothing changed, old data will be retained by WAB
  419. //
  420. if(!lpWED->fDataChanged)
  421. return TRUE;
  422. // Check if we have any data to save ...
  423. for(i=0;i<myMax;i++)
  424. {
  425. *(szData[i]) = '\0';
  426. GetWindowText(GetDlgItem(hDlg, EditControls[i]), szData[i], MAX_PATH);
  427. if(lstrlen(szData[i]))
  428. ulcPropCount++;
  429. }
  430. if(!ulcPropCount) // no data
  431. return TRUE;
  432. // Else data exists. Create a return prop array to pass back to the WAB
  433. sc = lpWED->lpWABObject->lpVtbl->AllocateBuffer( lpWED->lpWABObject,
  434. sizeof(SPropValue) * ulcPropCount,
  435. &lpPropArray);
  436. if (sc!=S_OK)
  437. goto out;
  438. for(i=0;i<myMax;i++)
  439. {
  440. int nLen = lstrlen(szData[i]);
  441. if(nLen)
  442. {
  443. lpPropArray[ulIndex].ulPropTag = MyPropTags[i];
  444. sc = lpWED->lpWABObject->lpVtbl->AllocateMore( lpWED->lpWABObject,
  445. nLen+1, lpPropArray,
  446. &(lpPropArray[ulIndex].Value.LPSZ));
  447. if (sc!=S_OK)
  448. goto out;
  449. lstrcpy(lpPropArray[ulIndex].Value.LPSZ,szData[i]);
  450. ulIndex++;
  451. }
  452. }
  453. // Set this new data on the object
  454. //
  455. if(HR_FAILED(lpWED->lpPropObj->lpVtbl->SetProps( lpWED->lpPropObj,
  456. ulcPropCount, lpPropArray, NULL)))
  457. goto out;
  458. // ** Important - do not call SaveChanges on the object
  459. // SaveChanges makes persistent changes and may modify/lose data if called at this point
  460. // The WAB will determine if its appropriate or not to call SaveChanges after the
  461. // ** user has closed the property sheets
  462. bRet = TRUE;
  463. out:
  464. if(!bRet && lpPropArray)
  465. lpWED->lpWABObject->lpVtbl->FreeBuffer(lpWED->lpWABObject, lpPropArray);
  466. return bRet;
  467. }
  468. /*//$$****************************************************************
  469. //
  470. // UpdateDisplayNameInfo
  471. //
  472. // Demonstrates how to read information from other sibling property
  473. // sheets when the user switches between pages
  474. //
  475. // This demo function attempts to get the updated display name info
  476. // when the user switches to this page in the UI
  477. //
  478. //********************************************************************/
  479. const SizedSPropTagArray(1, ptaName)=
  480. {
  481. 1,
  482. {
  483. PR_DISPLAY_NAME
  484. }
  485. };
  486. void UpdateDisplayNameInfo(HWND hDlg, LPWABEXTDISPLAY lpWED)
  487. {
  488. //
  489. // Scan all the updated information from all the other property sheets
  490. //
  491. ULONG i = 0, j=0;
  492. LPTSTR lpName = NULL;
  493. ULONG ulcPropCount = 0;
  494. LPSPropValue lpPropArray = NULL;
  495. // Each sheet should update its data on the object when it looses
  496. // focus and gets the PSN_KILLACTIVE message, provided the user has
  497. // made any changes. We just scan the object for the desired properties
  498. // and use them.
  499. // Ask only for the display name
  500. if(!HR_FAILED(lpWED->lpPropObj->lpVtbl->GetProps( lpWED->lpPropObj,
  501. (LPSPropTagArray) &ptaName,
  502. 0,
  503. &ulcPropCount, &lpPropArray)))
  504. {
  505. if( ulcPropCount == 1 &&
  506. PROP_TYPE(lpPropArray[0].ulPropTag) == PT_TSTRING) // The call could succeed but there may be no DN
  507. { // in which case the PROP_TYPE will be PR_NULL
  508. lpName = lpPropArray[0].Value.LPSZ;
  509. }
  510. }
  511. if(lpName && lstrlen(lpName))
  512. SetDlgItemText(hDlg, IDC_STATIC_NAME, lpName);
  513. if(ulcPropCount && lpPropArray)
  514. lpWED->lpWABObject->lpVtbl->FreeBuffer(lpWED->lpWABObject, lpPropArray);
  515. return;
  516. }
  517. /*//$$*********************************************************************
  518. //
  519. // UpdateOldPropTagsArray
  520. //
  521. // When we update the data on a particular property sheet, we want to update
  522. // all the properties related to that particular sheet. Since some properties
  523. // may have been deleted from the UI, we delete all relevant properties from
  524. // the property object
  525. //
  526. //**************************************************************************/
  527. BOOL UpdateOldPropTagsArray(LPWABEXTDISPLAY lpWED, int nIndex)
  528. {
  529. LPSPropTagArray lpPTA = NULL;
  530. SCODE sc = 0;
  531. int i =0;
  532. sc = lpWED->lpWABObject->lpVtbl->AllocateBuffer(lpWED->lpWABObject,
  533. sizeof(SPropTagArray) + sizeof(ULONG)*(myMax),
  534. &lpPTA);
  535. if(!lpPTA || sc!=S_OK)
  536. return FALSE;
  537. lpPTA->cValues = myMax;
  538. for(i=0;i<myMax;i++)
  539. lpPTA->aulPropTag[i] = MyPropTags[i];
  540. // Delete any props in the original that may have been modified on this propsheet
  541. lpWED->lpPropObj->lpVtbl->DeleteProps(lpWED->lpPropObj,
  542. lpPTA,
  543. NULL);
  544. if(lpPTA)
  545. lpWED->lpWABObject->lpVtbl->FreeBuffer(lpWED->lpWABObject,
  546. lpPTA);
  547. return TRUE;
  548. }
  549. /*//$$*********************************************************************
  550. //
  551. // bUpdatePropSheetData
  552. //
  553. // We delete any properties relevant to us from the object, and set new
  554. // data from the property sheet onto the object
  555. //
  556. ****************************************************************************/
  557. BOOL bUpdatePropSheetData(HWND hDlg, LPWABEXTDISPLAY lpWED)
  558. {
  559. BOOL bRet = TRUE;
  560. // ****Dont**** do anything if this is a READ_ONLY operation
  561. // In that case the memory variables are not all set up and this
  562. // prop sheet is not expected to return anything at all
  563. //
  564. if(!lpWED->fReadOnly)
  565. {
  566. // Delete old
  567. if(!UpdateOldPropTagsArray(lpWED, lpWED->nIndexNumber))
  568. return FALSE;
  569. bRet = GetDataFromUI(hDlg, lpWED);
  570. }
  571. return bRet;
  572. }
  573. /*//$$****************************************************************
  574. //
  575. // fnCallback
  576. //
  577. // A callback function that is called when the property sheet is created
  578. // and when it is destroyed. This functional is optional - you dont need
  579. // it unless you want to do specific initialization and cleanup.
  580. //
  581. // See SDK documentation on PropSheetPageProc for more details
  582. //
  583. //********************************************************************/
  584. UINT CALLBACK fnCallback( HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp )
  585. {
  586. switch(uMsg)
  587. {
  588. case PSPCB_CREATE:
  589. // Propsheet is being created
  590. break;
  591. case PSPCB_RELEASE:
  592. // Propsheet is being destroyed
  593. break;
  594. }
  595. return TRUE;
  596. }