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.

858 lines
20 KiB

  1. //
  2. // USER.CPP
  3. // User Class Members
  4. //
  5. // Copyright Microsoft 1998-
  6. //
  7. // PRECOMP
  8. #include "precomp.h"
  9. //
  10. // Local macros
  11. //
  12. #define ASSERT_LOCAL_USER() ASSERT(IsLocalUser() == TRUE);
  13. //
  14. // Init()
  15. // This could fail...
  16. //
  17. BOOL WbUser::Init(POM_OBJECT hUser)
  18. {
  19. ASSERT(hUser != NULL);
  20. m_hPageCurrent = WB_PAGE_HANDLE_NULL;
  21. m_zoomed = FALSE;
  22. m_hUser = hUser;
  23. m_pRemotePointer = new DCWbGraphicPointer(this);
  24. if (!m_pRemotePointer)
  25. {
  26. ERROR_OUT(("WbUser::Init - failed to create m_pRemotePointer"));
  27. return(FALSE);
  28. }
  29. Refresh();
  30. return(TRUE);
  31. }
  32. //
  33. //
  34. // Function: ~WbUser
  35. //
  36. // Purpose: Destructor
  37. //
  38. //
  39. WbUser::~WbUser(void)
  40. {
  41. // don't leave any loose ends
  42. if ((g_pMain != NULL) && (g_pMain->GetLockOwner() == this))
  43. {
  44. g_pMain->SetLockOwner(NULL);
  45. g_pMain->UpdateWindowTitle();
  46. }
  47. // Free the remote pointer
  48. if (m_pRemotePointer != NULL)
  49. {
  50. delete m_pRemotePointer;
  51. m_pRemotePointer = NULL;
  52. }
  53. }
  54. //
  55. //
  56. // Function: Refresh
  57. //
  58. // Purpose: Read the user details and copy them to member variables.
  59. //
  60. //
  61. void WbUser::Refresh(void)
  62. {
  63. MLZ_EntryOut(ZONE_FUNCTION, "WbUser::Refresh");
  64. ASSERT(m_pRemotePointer);
  65. // Set the flag indicating whether this is the local user
  66. POM_OBJECT hLocalUser;
  67. g_pwbCore->WBP_PersonHandleLocal(&hLocalUser);
  68. m_bLocalUser = (m_hUser == hLocalUser);
  69. // Read the external data
  70. WB_PERSON userDetails;
  71. UINT uiReturn = g_pwbCore->WBP_GetPersonData(m_hUser, &userDetails);
  72. if (uiReturn != 0)
  73. {
  74. DefaultExceptionHandler(WBFE_RC_WB, uiReturn);
  75. return;
  76. }
  77. // Get the user name
  78. lstrcpy(m_strName, userDetails.personName);
  79. // Get the sync flag
  80. m_bSynced = (userDetails.synced != FALSE);
  81. // Get the current page
  82. m_hPageCurrent = userDetails.currentPage;
  83. // Get the current position in the page
  84. m_rectVisible.left = userDetails.visibleRect.left;
  85. m_rectVisible.right = userDetails.visibleRect.right;
  86. m_rectVisible.top = userDetails.visibleRect.top;
  87. m_rectVisible.bottom = userDetails.visibleRect.bottom;
  88. // Get the pointer active flag. We go directly to the member variable
  89. // here since the SetActive member of the pointer class would re-write
  90. // the user information.
  91. m_pRemotePointer->m_bActive = (userDetails.pointerActive != 0);
  92. // Get the pointer page
  93. m_pRemotePointer->SetPage(userDetails.pointerPage);
  94. // Get the pointer position
  95. m_pRemotePointer->MoveTo(userDetails.pointerPos.x, userDetails.pointerPos.y);
  96. // Get the color
  97. m_color = g_ColorTable[userDetails.colorId % NUM_COLOR_ENTRIES];
  98. // Set the pointer color
  99. m_pRemotePointer->SetColor(m_color);
  100. }
  101. //
  102. // Function: Update
  103. //
  104. // Purpose: Update the external copy of the user information
  105. //
  106. void WbUser::Update()
  107. {
  108. MLZ_EntryOut(ZONE_FUNCTION, "WbUser::Update");
  109. // Can only update if we are the local user
  110. ASSERT_LOCAL_USER();
  111. ASSERT(m_pRemotePointer);
  112. // Get the local user details
  113. WB_PERSON userDetails;
  114. UINT uiReturn;
  115. uiReturn = g_pwbCore->WBP_GetPersonData(m_hUser, &userDetails);
  116. if (uiReturn != 0)
  117. {
  118. DefaultExceptionHandler(WBFE_RC_WB, uiReturn);
  119. return;
  120. }
  121. // Don't update the name
  122. // Set the sync flag
  123. userDetails.synced = (m_bSynced != FALSE);
  124. // Set the pointer active flag
  125. userDetails.pointerActive = (m_pRemotePointer->IsActive() != FALSE);
  126. // Set the page handle for the current page
  127. userDetails.currentPage = m_hPageCurrent;
  128. // Set the current position in the page
  129. userDetails.visibleRect.left = (short)m_rectVisible.left;
  130. userDetails.visibleRect.right = (short)m_rectVisible.right;
  131. userDetails.visibleRect.top = (short)m_rectVisible.top;
  132. userDetails.visibleRect.bottom = (short)m_rectVisible.bottom;
  133. // Set the pointer page
  134. userDetails.pointerPage = m_pRemotePointer->Page();
  135. // Set the pointer position within the page
  136. POINT pointerPos;
  137. m_pRemotePointer->GetPosition(&pointerPos);
  138. userDetails.pointerPos.x = (short)pointerPos.x;
  139. userDetails.pointerPos.y = (short)pointerPos.y;
  140. // Don't update the color
  141. // Write the user details back to the core
  142. uiReturn = g_pwbCore->WBP_SetLocalPersonData(&userDetails);
  143. if (uiReturn != 0)
  144. {
  145. // Throw exception
  146. DefaultExceptionHandler(WBFE_RC_WB, uiReturn);
  147. return;
  148. }
  149. }
  150. //
  151. //
  152. // Function: PutSyncPosition
  153. //
  154. // Purpose: Write the sync position from the current position of this
  155. // user.
  156. //
  157. //
  158. void WbUser::PutSyncPosition(void)
  159. {
  160. MLZ_EntryOut(ZONE_FUNCTION, "WbUser::PutSyncPosition");
  161. // Set up a sync object
  162. WB_SYNC sync;
  163. sync.length = sizeof(WB_SYNC);
  164. sync.currentPage = m_hPageCurrent;
  165. sync.visibleRect.top = (short)m_rectVisible.top;
  166. sync.visibleRect.left = (short)m_rectVisible.left;
  167. sync.visibleRect.bottom = (short)m_rectVisible.bottom;
  168. sync.visibleRect.right = (short)m_rectVisible.right;
  169. sync.zoomed = (TSHR_UINT16)m_zoomed;
  170. sync.dataOffset = (TSHR_UINT16)((BYTE *)&(sync.currentPage) - (BYTE *)&sync);
  171. UINT uiReturn = g_pwbCore->WBP_SyncPositionUpdate(&sync);
  172. if (uiReturn != 0)
  173. {
  174. // Throw an exception
  175. DefaultExceptionHandler(WBFE_RC_WB, uiReturn);
  176. return;
  177. }
  178. }
  179. //
  180. //
  181. // Function: GetSyncPosition
  182. //
  183. // Purpose: Get the position at which this user should be to
  184. // account for the current sync information. This function
  185. // assumes that there is a valid sync position available.
  186. //
  187. //
  188. void WbUser::GetSyncPosition(void)
  189. {
  190. MLZ_EntryOut(ZONE_FUNCTION, "WbUser::GetSyncPosition");
  191. // Get the current sync position
  192. WB_SYNC sync;
  193. UINT uiReturn = g_pwbCore->WBP_SyncPositionGet(&sync);
  194. if (uiReturn != 0)
  195. {
  196. // Throw an exception
  197. DefaultExceptionHandler(WBFE_RC_WB, uiReturn);
  198. return;
  199. }
  200. TRACE_DEBUG(("Sync page handle = %d", sync.currentPage));
  201. // If the sync page is not valid, do nothing
  202. if (sync.currentPage != WB_PAGE_HANDLE_NULL)
  203. {
  204. // Get the current sync position
  205. m_hPageCurrent = sync.currentPage;
  206. // Now calculate the new visible rectangle
  207. RECT rectSyncUser;
  208. rectSyncUser.left = sync.visibleRect.left;
  209. rectSyncUser.top = sync.visibleRect.top;
  210. rectSyncUser.right = sync.visibleRect.right;
  211. rectSyncUser.bottom = sync.visibleRect.bottom;
  212. // Check the y position of the visible rectangles
  213. if ((rectSyncUser.bottom - rectSyncUser.top) <= (m_rectVisible.bottom - m_rectVisible.top))
  214. {
  215. // The sync rectangle's height is smaller than our visible rectangle's
  216. if (rectSyncUser.top < m_rectVisible.top)
  217. {
  218. ::OffsetRect(&m_rectVisible, 0, rectSyncUser.top - m_rectVisible.top);
  219. }
  220. else if (rectSyncUser.bottom > m_rectVisible.bottom)
  221. {
  222. ::OffsetRect(&m_rectVisible, 0, rectSyncUser.bottom - m_rectVisible.bottom);
  223. }
  224. }
  225. else
  226. {
  227. // The sync rectangle is bigger than ours
  228. if (rectSyncUser.top > m_rectVisible.top)
  229. {
  230. ::OffsetRect(&m_rectVisible, 0, rectSyncUser.top - m_rectVisible.top);
  231. }
  232. else if (rectSyncUser.bottom < m_rectVisible.bottom)
  233. {
  234. ::OffsetRect(&m_rectVisible, 0, rectSyncUser.bottom - m_rectVisible.bottom);
  235. }
  236. }
  237. if ((rectSyncUser.right - rectSyncUser.left) <= (m_rectVisible.right - m_rectVisible.left))
  238. {
  239. // The sync rectangle's width is smaller than our visible rectangle's
  240. if (rectSyncUser.left < m_rectVisible.left)
  241. {
  242. ::OffsetRect(&m_rectVisible, rectSyncUser.left - m_rectVisible.left, 0);
  243. }
  244. else if (rectSyncUser.right > m_rectVisible.right)
  245. {
  246. ::OffsetRect(&m_rectVisible, rectSyncUser.right - m_rectVisible.right, 0);
  247. }
  248. }
  249. else
  250. {
  251. // The sync rectangle is bigger than ours
  252. if (rectSyncUser.left > m_rectVisible.left)
  253. {
  254. ::OffsetRect(&m_rectVisible, rectSyncUser.left - m_rectVisible.left, 0);
  255. }
  256. else if (rectSyncUser.right < m_rectVisible.right)
  257. {
  258. ::OffsetRect(&m_rectVisible, rectSyncUser.right - m_rectVisible.right, 0);
  259. }
  260. }
  261. m_zoomed = sync.zoomed;
  262. }
  263. }
  264. //
  265. //
  266. // Function: Sync
  267. //
  268. // Purpose: Sync the local user. The page and point passed as parameters
  269. // are used as the current sync position only if there is no
  270. // current sync position determined by another user.
  271. //
  272. //
  273. void WbUser::Sync(void)
  274. {
  275. MLZ_EntryOut(ZONE_FUNCTION, "WbUser::Sync");
  276. ASSERT_LOCAL_USER();
  277. ASSERT(m_pRemotePointer);
  278. // Determine whether any other users are currently synced
  279. WbUser* pUser = WB_GetFirstUser();
  280. while (pUser != NULL)
  281. {
  282. // If this user is synced, we are done
  283. if (pUser->IsSynced())
  284. {
  285. break;
  286. }
  287. // Try the next user
  288. pUser = WB_GetNextUser(pUser);
  289. }
  290. // If we found a synced user, and we don't have the contents lock
  291. if ( (pUser != NULL)
  292. && (!WB_GotContentsLock()))
  293. {
  294. // Get the sync position from the core
  295. GetSyncPosition();
  296. }
  297. else
  298. {
  299. // Set the sync position from our own position
  300. PutSyncPosition();
  301. }
  302. // Update the synced member flag
  303. m_bSynced = TRUE;
  304. // Write the user details back to the core
  305. Update();
  306. }
  307. //
  308. //
  309. // Function: Unsync
  310. //
  311. // Purpose: Unsynchronize the users page from other synced users
  312. //
  313. //
  314. void WbUser::Unsync(void)
  315. {
  316. MLZ_EntryOut(ZONE_FUNCTION, "WbUser::Unsync");
  317. ASSERT_LOCAL_USER();
  318. // Update the local member
  319. m_bSynced = FALSE;
  320. // Update the external details
  321. Update();
  322. }
  323. //
  324. //
  325. // Function: PutPointer
  326. //
  327. // Purpose: Turn on the user's remote pointer
  328. //
  329. //
  330. void WbUser::PutPointer(WB_PAGE_HANDLE hPage, POINT point)
  331. {
  332. MLZ_EntryOut(ZONE_FUNCTION, "WbUser::PutPointer");
  333. // Check that we are the local user (we cannot do the update if we are not)
  334. ASSERT_LOCAL_USER();
  335. ASSERT(m_pRemotePointer);
  336. m_pRemotePointer->SetActive(hPage, point);
  337. }
  338. //
  339. //
  340. // Function: RemovePointer
  341. //
  342. // Purpose: Turn off the user's remote pointer
  343. //
  344. //
  345. void WbUser::RemovePointer(void)
  346. {
  347. MLZ_EntryOut(ZONE_FUNCTION, "WbUser::RemovePointer");
  348. // Check that we are the local user (we cannot do the update if we are not)
  349. ASSERT_LOCAL_USER();
  350. // Update the remote pointer members
  351. ASSERT(m_pRemotePointer);
  352. m_pRemotePointer->m_bActive = FALSE;
  353. m_pRemotePointer->m_hPage = WB_PAGE_HANDLE_NULL;
  354. // Update the external user information
  355. Update();
  356. }
  357. //
  358. // Function: IsUsingPointer()
  359. //
  360. BOOL WbUser::IsUsingPointer(void) const
  361. {
  362. ASSERT(m_pRemotePointer);
  363. return(m_pRemotePointer->IsActive());
  364. }
  365. //
  366. // Function: PointerPage()
  367. //
  368. WB_PAGE_HANDLE WbUser::PointerPage(void) const
  369. {
  370. ASSERT(m_pRemotePointer);
  371. return(m_pRemotePointer->Page());
  372. }
  373. //
  374. // Function: GetPointerPosition()
  375. //
  376. void WbUser::GetPointerPosition(LPPOINT lpptPos)
  377. {
  378. ASSERT(m_pRemotePointer);
  379. m_pRemotePointer->GetPosition(lpptPos);
  380. }
  381. //
  382. //
  383. // Function: SetPage
  384. //
  385. // Purpose: Set the user's current page
  386. //
  387. //
  388. void WbUser::SetPage(WB_PAGE_HANDLE hPage)
  389. {
  390. MLZ_EntryOut(ZONE_FUNCTION, "WbUser::Page");
  391. // Check that we are the local user (we cannot do the update if we are not)
  392. ASSERT_LOCAL_USER();
  393. // Only make the update if it is a change
  394. if (m_hPageCurrent != hPage)
  395. {
  396. // Update the local member
  397. m_hPageCurrent = hPage;
  398. // Update the external information
  399. Update();
  400. }
  401. }
  402. //
  403. //
  404. // Function: CurrentPosition
  405. //
  406. // Purpose: Set the user's current position
  407. //
  408. //
  409. void WbUser::SetVisibleRect(LPCRECT lprcVisible)
  410. {
  411. MLZ_EntryOut(ZONE_FUNCTION, "WbUser::SetVisibleRect");
  412. // Check that we are the local user (we cannot do the update if we are not)
  413. ASSERT_LOCAL_USER();
  414. // Only make the update if it is a change
  415. if (!::EqualRect(&m_rectVisible, lprcVisible))
  416. {
  417. // Update the local member
  418. m_rectVisible = *lprcVisible;
  419. // Update the external information
  420. Update();
  421. }
  422. }
  423. //
  424. //
  425. // Function: operator==
  426. //
  427. // Purpose: Return TRUE if the specified user is the same as this user
  428. //
  429. //
  430. BOOL WbUser::operator==(const WbUser& user) const
  431. {
  432. return (m_hUser == user.m_hUser);
  433. }
  434. //
  435. //
  436. // Function: operator!=
  437. //
  438. // Purpose: Return FALSE if the specified user is the same as this user
  439. //
  440. //
  441. BOOL WbUser::operator!=(const WbUser& user) const
  442. {
  443. return (!((*this) == user));
  444. }
  445. //
  446. //
  447. // Function: operator=
  448. //
  449. // Purpose: Copy the specified user to this one
  450. //
  451. //
  452. WbUser& WbUser::operator=(const WbUser& user)
  453. {
  454. // Save the new handles
  455. m_hUser = user.m_hUser;
  456. // Read the details
  457. Refresh();
  458. return (*this);
  459. }
  460. //
  461. //
  462. // Function: HasContentsLock
  463. //
  464. // Purpose: Check whether this user has the whiteboard contents lock
  465. //
  466. //
  467. BOOL WbUser::HasContentsLock(void) const
  468. {
  469. // call the core to find out if we have the lock
  470. return (WB_LockUser() == this);
  471. }
  472. //
  473. //
  474. // Function: WbUserList::Clear
  475. //
  476. // Purpose: Clear the user handle map, removing all user objects
  477. //
  478. //
  479. void WbUserList::Clear(void)
  480. {
  481. // Delete all the user objects in the user map
  482. WbUser* pUser;
  483. POM_OBJECT hUser;
  484. ASSERT(g_pUsers);
  485. POSITION position = g_pUsers->GetHeadPosition();
  486. while (position)
  487. {
  488. POSITION posSav = position;
  489. pUser = (WbUser*)g_pUsers->GetNext(position);
  490. if (pUser != NULL)
  491. {
  492. delete pUser;
  493. }
  494. g_pUsers->RemoveAt(posSav);
  495. }
  496. // Remove all the map entries
  497. EmptyList();
  498. }
  499. //
  500. //
  501. // Function: ~WbUserList
  502. //
  503. // Purpose: Destructor
  504. //
  505. //
  506. WbUserList::~WbUserList(void)
  507. {
  508. // Delete all the user objects in the user map
  509. Clear();
  510. }
  511. //
  512. //
  513. // Function: LockUser
  514. //
  515. // Purpose: Return a user object showing who has the lock
  516. //
  517. //
  518. WbUser* WB_LockUser(void)
  519. {
  520. MLZ_EntryOut(ZONE_FUNCTION, "WB_LockUser");
  521. // Get the lock status from the core (cannot fail)
  522. POM_OBJECT hLockUser;
  523. WB_LOCK_TYPE lockType;
  524. lockType = g_pwbCore->WBP_LockStatus(&hLockUser);
  525. // Build a result
  526. WbUser* pUserResult = NULL;
  527. if (lockType != WB_LOCK_TYPE_NONE)
  528. {
  529. pUserResult = WB_GetUser(hLockUser);
  530. }
  531. return pUserResult;
  532. }
  533. //
  534. //
  535. // Function: Locked
  536. //
  537. // Purpose: Return TRUE if another user has a lock (contents or page).
  538. // NOTE that the page order lock implies the contents are
  539. // locked.
  540. //
  541. //
  542. BOOL WB_Locked(void)
  543. {
  544. POM_OBJECT pLockUser;
  545. return ( (g_pwbCore->WBP_LockStatus(&pLockUser) != WB_LOCK_TYPE_NONE)
  546. && (WB_LocalUser() != WB_LockUser()));
  547. }
  548. //
  549. //
  550. // Function: ContentsLocked
  551. //
  552. // Purpose: Return TRUE if another user has the contents lock
  553. //
  554. //
  555. BOOL WB_ContentsLocked(void)
  556. {
  557. POM_OBJECT pLockUser;
  558. return ( (g_pwbCore->WBP_LockStatus(&pLockUser) == WB_LOCK_TYPE_CONTENTS)
  559. && (WB_LocalUser() != WB_LockUser()));
  560. }
  561. //
  562. //
  563. // Function: GotLock
  564. //
  565. // Purpose: Return TRUE if the local user has a lock
  566. //
  567. //
  568. BOOL WB_GotLock(void)
  569. {
  570. POM_OBJECT pLockUser;
  571. return ( (g_pwbCore->WBP_LockStatus(&pLockUser) != WB_LOCK_TYPE_NONE)
  572. && (WB_LocalUser() == WB_LockUser()));
  573. }
  574. //
  575. //
  576. // Function: GotContentsLock
  577. //
  578. // Purpose: Return TRUE if the local user has the contents lock
  579. //
  580. //
  581. BOOL WB_GotContentsLock(void)
  582. {
  583. POM_OBJECT pLockUser;
  584. return ( (g_pwbCore->WBP_LockStatus(&pLockUser) == WB_LOCK_TYPE_CONTENTS)
  585. && (WB_LocalUser() == WB_LockUser()));
  586. }
  587. //
  588. //
  589. // Function: PresentationMode
  590. //
  591. // Purpose: Return TRUE if the whiteboard is in presentation mode, i.e.
  592. // another user has the contents lock, and is synced.
  593. //
  594. //
  595. BOOL WB_PresentationMode(void)
  596. {
  597. return ( (WB_ContentsLocked())
  598. && (WB_LockUser() != NULL)
  599. && (WB_LockUser()->IsSynced()));
  600. }
  601. //
  602. //
  603. // Function: GetUser
  604. //
  605. // Purpose: Return a pointer to a user object from a user handle
  606. //
  607. //
  608. WbUser* WB_GetUser(POM_OBJECT hUser)
  609. {
  610. MLZ_EntryOut(ZONE_FUNCTION, "WB_GetFirstUser");
  611. // Set up a return value
  612. WbUser* pUserResult = NULL;
  613. // if the user handle is null, we return a null object pointer
  614. if (hUser != NULL)
  615. {
  616. // Look the user up in the internal map
  617. ASSERT(g_pUsers);
  618. POSITION position = g_pUsers->GetHeadPosition();
  619. BOOL bFound = FALSE;
  620. while (position)
  621. {
  622. pUserResult = (WbUser*)g_pUsers->GetNext(position);
  623. if (hUser == pUserResult->Handle())
  624. {
  625. return pUserResult;
  626. }
  627. }
  628. // The user is not yet in our map
  629. pUserResult = new WbUser();
  630. if (!pUserResult)
  631. {
  632. ERROR_OUT(("Couldn't allocate user object for 0x%08x", hUser));
  633. }
  634. else
  635. {
  636. if (!pUserResult->Init(hUser))
  637. {
  638. ERROR_OUT(("Couldn't init user object for 0x%08x", hUser));
  639. delete pUserResult;
  640. pUserResult = NULL;
  641. }
  642. else
  643. {
  644. // Add the new user to the internal map
  645. g_pUsers->AddTail(pUserResult);
  646. }
  647. }
  648. }
  649. return pUserResult;
  650. }
  651. //
  652. //
  653. // Function: GetFirstUser
  654. //
  655. // Purpose: Return the first user in the call
  656. //
  657. //
  658. WbUser* WB_GetFirstUser(void)
  659. {
  660. MLZ_EntryOut(ZONE_FUNCTION, "WB_GetFirstUser");
  661. // Get the handle of the first user (cannot fail)
  662. POM_OBJECT hUser;
  663. g_pwbCore->WBP_PersonHandleFirst(&hUser);
  664. // Get a pointer to the user object for this handle
  665. WbUser* pUser = WB_GetUser(hUser);
  666. return pUser;
  667. }
  668. //
  669. //
  670. // Function: GetNextUser
  671. //
  672. // Purpose: Return the next user in the call
  673. //
  674. //
  675. WbUser* WB_GetNextUser(const WbUser* pUser)
  676. {
  677. MLZ_EntryOut(ZONE_FUNCTION, "WB_GetNextUser");
  678. ASSERT(pUser != NULL);
  679. // Get the handle of the next user
  680. POM_OBJECT hNextUser;
  681. UINT uiReturn = g_pwbCore->WBP_PersonHandleNext(pUser->Handle(),
  682. &hNextUser);
  683. WbUser* pUserResult = NULL;
  684. if (uiReturn == 0)
  685. {
  686. pUserResult = WB_GetUser(hNextUser);
  687. }
  688. return pUserResult;
  689. }
  690. //
  691. //
  692. // Function: LocalUser
  693. //
  694. // Purpose: Return an object representing the local user
  695. //
  696. //
  697. WbUser* WB_LocalUser(void)
  698. {
  699. MLZ_EntryOut(ZONE_FUNCTION, "WB_LocalUser");
  700. // Get the local user handle (cannot fail)
  701. POM_OBJECT hUser;
  702. g_pwbCore->WBP_PersonHandleLocal(&hUser);
  703. return WB_GetUser(hUser);
  704. }