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.

1649 lines
34 KiB

  1. //
  2. // Copyright 1997 - Microsoft
  3. //
  4. // CCOMPUTR.CPP - Handles the computer object property pages.
  5. //
  6. #include "pch.h"
  7. #include "imos.h"
  8. #include "newclnts.h"
  9. #include "tools.h"
  10. #include "varconv.h"
  11. #include "cservice.h"
  12. #include "cenumsif.h"
  13. DEFINE_MODULE("IMADMUI")
  14. DEFINE_THISCLASS("CService")
  15. #define THISCLASS CService
  16. #define LPTHISCLASS LPSERVICE
  17. // ************************************************************************
  18. //
  19. // Constructor / Destructor
  20. //
  21. // ************************************************************************
  22. //
  23. // CreateInstance()
  24. //
  25. LPVOID
  26. CService_CreateInstance( void )
  27. {
  28. TraceFunc( "CService_CreateInstance()\n" );
  29. LPTHISCLASS lpcc = new THISCLASS( );
  30. HRESULT hr = THR( lpcc->Init( ) );
  31. if ( FAILED(hr) ) {
  32. delete lpcc;
  33. RETURN(NULL);
  34. }
  35. RETURN((LPVOID)lpcc);
  36. }
  37. //
  38. // Constructor
  39. //
  40. THISCLASS::THISCLASS( ) :
  41. _uMode(MODE_SHELL),
  42. _pszSCPDN(NULL),
  43. //_pszGroupDN(NULL),
  44. _pszMachineName(NULL),
  45. _pszDSServerName(NULL),
  46. _cRef(0),
  47. _pDataObj(NULL),
  48. _hwndNotify(NULL),
  49. _pads(NULL)
  50. {
  51. TraceClsFunc( "CService()\n" );
  52. InterlockIncrement( g_cObjects );
  53. ZeroMemory( &_InitParams, sizeof(_InitParams));
  54. _InitParams.dwSize = sizeof(_InitParams);
  55. TraceFuncExit();
  56. }
  57. //
  58. // Init()
  59. //
  60. STDMETHODIMP
  61. THISCLASS::Init( )
  62. {
  63. TraceClsFunc( "Init()\n" );
  64. // IUnknown stuff
  65. BEGIN_QITABLE_IMP( CService, IShellExtInit );
  66. QITABLE_IMP( IShellExtInit );
  67. QITABLE_IMP( IShellPropSheetExt );
  68. QITABLE_IMP( IIntelliMirrorSAP );
  69. END_QITABLE_IMP( CService );
  70. Assert( _cRef == 0);
  71. Assert( !_pads );
  72. Assert( !_pszSCPDN );
  73. //Assert( !_pszGroupDN );
  74. Assert( !_pszMachineName );
  75. AddRef( );
  76. HRESULT hr = S_OK;
  77. hr = CheckClipboardFormats( );
  78. HRETURN(hr);
  79. }
  80. STDMETHODIMP
  81. THISCLASS::Init2( IADs * pads )
  82. {
  83. TraceClsFunc( "Init2( " );
  84. TraceMsg( TF_FUNC, "pads = 0x%08x )\n", pads );
  85. HRESULT hr;
  86. if ( !pads )
  87. HRETURN(E_INVALIDARG);
  88. _pads = pads;
  89. _pads->AddRef( );
  90. hr = _GetComputerNameFromADs( );
  91. if (FAILED( hr ))
  92. goto Error;
  93. Error:
  94. HRETURN(hr);
  95. }
  96. //
  97. // Destructor
  98. //
  99. THISCLASS::~THISCLASS( )
  100. {
  101. TraceClsFunc( "~CService()\n" );
  102. // Private Members
  103. if ( _pads ) {
  104. //
  105. // note: we shouldn't commit anything in the destructor -- we can't
  106. // catch failures here. We'll just have to make sure that we
  107. // explicitly commit changes when necessary
  108. //
  109. #if 0
  110. // Commit any changes before we release
  111. THR( _pads->SetInfo( ) );
  112. #endif
  113. _pads->Release( );
  114. }
  115. if ( _pszSCPDN )
  116. TraceFree( _pszSCPDN );
  117. //if ( _pszGroupDN )
  118. // TraceFree( _pszGroupDN );
  119. if ( _pszMachineName )
  120. TraceFree( _pszMachineName );
  121. if ( _pszDSServerName )
  122. TraceFree( _pszDSServerName );
  123. if ( _pDataObj )
  124. _pDataObj->Release( );
  125. InterlockDecrement( g_cObjects );
  126. TraceFuncExit();
  127. };
  128. // ************************************************************************
  129. //
  130. // IUnknown
  131. //
  132. // ************************************************************************
  133. //
  134. // QueryInterface()
  135. //
  136. STDMETHODIMP
  137. THISCLASS::QueryInterface(
  138. REFIID riid,
  139. LPVOID *ppv )
  140. {
  141. TraceClsFunc( "" );
  142. HRESULT hr = ::QueryInterface( this, _QITable, riid, ppv );
  143. QIRETURN( hr, riid );
  144. }
  145. //
  146. // AddRef()
  147. //
  148. STDMETHODIMP_(ULONG)
  149. THISCLASS::AddRef( void )
  150. {
  151. TraceClsFunc( "[IUnknown] AddRef( )\n" );
  152. InterlockIncrement( _cRef );
  153. RETURN(_cRef);
  154. }
  155. //
  156. // Release()
  157. //
  158. STDMETHODIMP_(ULONG)
  159. THISCLASS::Release( void )
  160. {
  161. TraceClsFunc( "[IUnknown] Release( )\n" );
  162. InterlockDecrement( _cRef );
  163. if ( _cRef )
  164. RETURN(_cRef);
  165. TraceDo( delete this );
  166. RETURN(0);
  167. }
  168. // ************************************************************************
  169. //
  170. // IShellExtInit
  171. //
  172. // ************************************************************************
  173. //
  174. // Initialize()
  175. //
  176. HRESULT
  177. THISCLASS::Initialize(
  178. LPCITEMIDLIST pidlFolder,
  179. LPDATAOBJECT lpdobj, HKEY hkeyProgID)
  180. {
  181. TraceClsFunc( "[IShellExtInit] Initialize( " );
  182. TraceMsg( TF_FUNC, " pidlFolder = 0x%08x, lpdobj = 0x%08x, hkeyProgID = 0x%08x )\n",
  183. pidlFolder, lpdobj, hkeyProgID );
  184. if ( !lpdobj )
  185. RETURN(E_INVALIDARG);
  186. HRESULT hr = S_OK;
  187. FORMATETC fmte;
  188. STGMEDIUM stg = { 0};
  189. STGMEDIUM stgOptions = { 0};
  190. LPWSTR pszObjectName;
  191. LPWSTR pszClassName;
  192. LPWSTR pszAttribPrefix;
  193. LPDSOBJECT pDsObject;
  194. LPDSOBJECTNAMES pDsObjectNames;
  195. LPDSDISPLAYSPECOPTIONS pDsDisplayOptions;
  196. _bstr_t Str;
  197. BOOL b;
  198. _pDataObj = lpdobj;
  199. _pDataObj->AddRef( );
  200. //
  201. // Retrieve the Object Names
  202. //
  203. fmte.cfFormat = (CLIPFORMAT)g_cfDsObjectNames;
  204. fmte.tymed = TYMED_HGLOBAL;
  205. fmte.dwAspect = DVASPECT_CONTENT;
  206. fmte.lindex = -1;
  207. fmte.ptd = 0;
  208. hr = THR( _pDataObj->GetData( &fmte, &stg) );
  209. if ( FAILED(hr) ) {
  210. goto Cleanup;
  211. }
  212. pDsObjectNames = (LPDSOBJECTNAMES) stg.hGlobal;
  213. Assert( stg.tymed == TYMED_HGLOBAL );
  214. TraceMsg( TF_ALWAYS, "Object's Namespace CLSID: " );
  215. TraceMsgGUID( TF_ALWAYS, pDsObjectNames->clsidNamespace );
  216. TraceMsg( TF_ALWAYS, "\tNumber of Objects: %u \n", pDsObjectNames->cItems );
  217. Assert( pDsObjectNames->cItems == 1 );
  218. pDsObject = (LPDSOBJECT) pDsObjectNames->aObjects;
  219. pszObjectName = (LPWSTR) PtrToByteOffset( pDsObjectNames, pDsObject->offsetName );
  220. pszClassName = (LPWSTR) PtrToByteOffset( pDsObjectNames, pDsObject->offsetClass );
  221. TraceMsg( TF_ALWAYS, "Object Name (Class): %s (%s)\n", pszObjectName, pszClassName );
  222. _pszDSServerName = TraceStrDup( pszObjectName );
  223. if (!_pszDSServerName) {
  224. hr = E_OUTOFMEMORY;
  225. goto Error;
  226. }
  227. //
  228. // If they handed us a "Computer" object, look at the NETBOOTSAP
  229. // attribute to get the IMSAP object.
  230. //
  231. if ( StrCmp( pszClassName, DSCOMPUTERCLASSNAME ) == 0 ) {
  232. IADs* pads = NULL;
  233. VARIANT var;
  234. VariantInit( &var );
  235. Assert( !_pszMachineName );
  236. //
  237. // Bind to the MAO in the DS
  238. //
  239. hr = THR( ADsGetObject( pszObjectName, IID_IADs, (void **)&pads ) );
  240. if (FAILED( hr ))
  241. goto Computer_Cleanup;
  242. Str = NETBOOTSAP;
  243. hr = THR( pads->Get( Str, &var ) );
  244. if (FAILED( hr ) ||
  245. V_VT( &var ) != VT_BSTR ) {
  246. goto Computer_Cleanup;
  247. }
  248. Assert( V_VT( &var ) == VT_BSTR );
  249. // Try to parse the string to connect to the same server as the DSADMIN
  250. hr = FixObjectPath( _pszDSServerName, V_BSTR( &var ), &_pszSCPDN );
  251. if (FAILED( hr ))
  252. goto Computer_Cleanup;
  253. DebugMsg( "SCP Path: %s\n", _pszSCPDN );
  254. VariantClear( &var );
  255. // while we are here, might as well get the server's NETBIOS name
  256. Str = SAMNAME;
  257. hr = THR( pads->Get( Str, &var ) );
  258. if (FAILED( hr ) ||
  259. V_VT( &var) != VT_BSTR) {
  260. goto Computer_Cleanup;
  261. }
  262. Assert( V_VT( &var) == VT_BSTR );
  263. _pszMachineName = TraceStrDup( V_BSTR( &var ) );
  264. if ( _pszMachineName ) {
  265. LPWSTR psz = &_pszMachineName[ wcslen( _pszMachineName ) - 1 ];
  266. if ( *psz == L'$' ) {
  267. *psz = L'\0';
  268. }
  269. DebugMsg( "Server Name: %ws\n", _pszMachineName );
  270. } else {
  271. hr = E_OUTOFMEMORY;
  272. }
  273. Computer_Cleanup:
  274. VariantClear( &var );
  275. if ( pads )
  276. pads->Release( );
  277. if (FAILED( hr ))
  278. goto Error;
  279. // create the DS notify object
  280. hr = THR( ADsPropCreateNotifyObj( _pDataObj, pszObjectName, &_hwndNotify ) );
  281. if (FAILED( hr ))
  282. goto Error;
  283. b = ADsPropGetInitInfo( _hwndNotify, &_InitParams );
  284. if ( !b ) {
  285. hr = E_FAIL;
  286. goto Error;
  287. }
  288. hr = THR( _InitParams.hr );
  289. if (FAILED( hr ))
  290. goto Error;
  291. // Bind to the IMSAP in the DS
  292. Assert( _pszSCPDN );
  293. hr = THR( ADsGetObject( _pszSCPDN, IID_IADs, (void **)&_pads ) );
  294. if (FAILED( hr ))
  295. goto Error;
  296. } else if ( StrCmp( pszClassName, DSIMSAPCLASSNAME ) ) {
  297. //
  298. // This should be a IMSAP.
  299. //
  300. hr = E_INVALIDARG;
  301. goto Error;
  302. } else {
  303. // Keep the DN around
  304. _pszSCPDN = TraceStrDup( pszObjectName );
  305. if (!_pszSCPDN) {
  306. hr = E_OUTOFMEMORY;
  307. goto Error;
  308. }
  309. // create the DS notify object
  310. hr = THR( ADsPropCreateNotifyObj( _pDataObj, pszObjectName, &_hwndNotify ) );
  311. if (FAILED( hr ))
  312. goto Error;
  313. b = ADsPropGetInitInfo( _hwndNotify, &_InitParams );
  314. if ( !b ) {
  315. hr = E_FAIL;
  316. goto Error;
  317. }
  318. hr = THR( _InitParams.hr );
  319. if (FAILED( hr ))
  320. goto Error;
  321. // Bind to the IMSAP in the DS
  322. Assert( _pszSCPDN );
  323. hr = THR( _InitParams.pDsObj->QueryInterface( IID_IADs, (void **)&_pads ) );
  324. if (FAILED( hr ))
  325. goto Error;
  326. }
  327. Assert( _pads );
  328. //
  329. // Retrieve the Display Spec Options
  330. //
  331. fmte.cfFormat = (CLIPFORMAT)g_cfDsDisplaySpecOptions;
  332. fmte.tymed = TYMED_HGLOBAL;
  333. fmte.dwAspect = DVASPECT_CONTENT;
  334. fmte.lindex = -1;
  335. fmte.ptd = 0;
  336. hr = THR( _pDataObj->GetData( &fmte, &stgOptions ) );
  337. if ( FAILED(hr) ) {
  338. goto Error;
  339. }
  340. pDsDisplayOptions = (LPDSDISPLAYSPECOPTIONS) stgOptions.hGlobal;
  341. Assert( stgOptions.tymed == TYMED_HGLOBAL );
  342. Assert( pDsDisplayOptions->dwSize == sizeof(DSDISPLAYSPECOPTIONS) );
  343. pszAttribPrefix = (LPWSTR) PtrToByteOffset( pDsDisplayOptions, pDsDisplayOptions->offsetAttribPrefix );
  344. // TraceMsg( TF_ALWAYS, TEXT("Attribute Prefix: %s\n"), pszAttribPrefix );
  345. if ( StrCmpW( pszAttribPrefix, STRING_ADMIN ) == 0 ) {
  346. _uMode = MODE_ADMIN;
  347. }
  348. // else default from Init()
  349. TraceMsg( TF_ALWAYS, TEXT("Mode: %s\n"), _uMode == MODE_ADMIN ? TEXT("Admin") : TEXT("Shell") );
  350. ReleaseStgMedium( &stgOptions );
  351. Cleanup:
  352. HRETURN(hr);
  353. Error:
  354. MessageBoxFromHResult( NULL, IDS_ERROR_READINGCOMPUTERACCOUNT, hr );
  355. goto Cleanup;
  356. }
  357. // ************************************************************************
  358. //
  359. // IShellPropSheetExt
  360. //
  361. // ************************************************************************
  362. //
  363. // AddPages()
  364. //
  365. HRESULT
  366. THISCLASS::AddPages(
  367. LPFNADDPROPSHEETPAGE lpfnAddPage,
  368. LPARAM lParam)
  369. {
  370. TraceClsFunc( "[IShellPropSheetExt] AddPages( )\n" );
  371. if ( !lpfnAddPage )
  372. RRETURN(E_POINTER);
  373. HRESULT hr = S_OK;
  374. hr = THR( ::AddPagesEx( NULL,
  375. CNewClientsTab_CreateInstance,
  376. lpfnAddPage,
  377. lParam,
  378. (LPUNKNOWN) (IShellExtInit*) this ) );
  379. if (FAILED( hr ))
  380. goto Error;
  381. hr = THR( ::AddPagesEx( NULL,
  382. CIntelliMirrorOSTab_CreateInstance,
  383. lpfnAddPage,
  384. lParam,
  385. (LPUNKNOWN) (IShellExtInit*) this ) );
  386. if (FAILED( hr ))
  387. goto Error;
  388. hr = THR( ::AddPagesEx( NULL,
  389. CToolsTab_CreateInstance,
  390. lpfnAddPage,
  391. lParam,
  392. (LPUNKNOWN) (IShellExtInit*) this ) );
  393. if (FAILED( hr ))
  394. goto Error;
  395. Error:
  396. HRETURN(hr);
  397. }
  398. //
  399. // ReplacePage()
  400. //
  401. HRESULT
  402. THISCLASS::ReplacePage(
  403. UINT uPageID,
  404. LPFNADDPROPSHEETPAGE lpfnReplaceWith,
  405. LPARAM lParam )
  406. {
  407. TraceClsFunc( "[IShellPropSheetExt] ReplacePage( ) *** NOT_IMPLEMENTED ***\n" );
  408. RETURN(E_NOTIMPL);
  409. }
  410. // ************************************************************************
  411. //
  412. // IIntelliMirrorSAP
  413. //
  414. // ************************************************************************
  415. //
  416. // CommitChanges( )
  417. //
  418. HRESULT
  419. THISCLASS::CommitChanges( void )
  420. {
  421. TraceClsFunc( "[IIntelliMirrorSAP] CommitChanges( )\n" );
  422. Assert( _pads );
  423. HRESULT hr;
  424. SC_HANDLE schManager = NULL;
  425. SC_HANDLE sch = NULL;
  426. SERVICE_STATUS ss;
  427. hr = THR( _pads->SetInfo( ) );
  428. if ( FAILED( hr ) )
  429. goto Error;
  430. // if we don't have this yet, get it now.
  431. if ( !_pszMachineName ) {
  432. hr = _GetComputerNameFromADs( );
  433. if (FAILED( hr ))
  434. goto Error;
  435. }
  436. schManager = OpenSCManager( _pszMachineName, NULL, SC_MANAGER_CONNECT );
  437. if (!schManager) {
  438. DWORD dwErr = GetLastError( );
  439. hr = HRESULT_FROM_WIN32( dwErr );
  440. goto Error;
  441. }
  442. sch = OpenService( schManager, BINL_SERVER_NAME, SERVICE_USER_DEFINED_CONTROL);
  443. if (!sch) {
  444. DWORD dwErr = GetLastError( );
  445. hr = HRESULT_FROM_WIN32( dwErr );
  446. goto Error;
  447. }
  448. if ( !ControlService( sch, BINL_SERVICE_REREAD_SETTINGS, &ss ) ) {
  449. DWORD dwErr = GetLastError( );
  450. hr = THR(HRESULT_FROM_WIN32( dwErr ));
  451. goto Error;
  452. }
  453. hr = HRESULT_FROM_WIN32( ss.dwWin32ExitCode );
  454. Error:
  455. if ( sch )
  456. CloseServiceHandle( sch );
  457. if ( schManager )
  458. CloseServiceHandle( schManager );
  459. if ( hr == HRESULT_FROM_WIN32( ERROR_SERVICE_NOT_ACTIVE ) ) {
  460. hr = S_OK; // ignore error... by design
  461. }
  462. HRETURN(hr);
  463. }
  464. //
  465. // IsAdmin( )
  466. //
  467. HRESULT
  468. THISCLASS::IsAdmin(
  469. BOOL * fAdmin )
  470. {
  471. TraceClsFunc( "[IIntelliMirrorSAP] IsAdmin( )\n" );
  472. if ( !fAdmin )
  473. RRETURN( E_POINTER );
  474. HRESULT hr = S_OK;
  475. *fAdmin = (_uMode == MODE_ADMIN);
  476. HRETURN(hr);
  477. }
  478. //
  479. // GetAllowNewClients( )
  480. //
  481. HRESULT
  482. THISCLASS::GetAllowNewClients(
  483. BOOL *pbool )
  484. {
  485. TraceClsFunc("[IIntelliMirrorSAP] GetAllowNewClients( ... )\n" );
  486. if ( !pbool )
  487. RRETURN(E_POINTER);
  488. HRESULT hr;
  489. VARIANT var;
  490. _bstr_t Str;
  491. VariantInit( &var );
  492. Assert( _pads );
  493. Str = NETBOOTALLOWNEWCLIENTS;
  494. hr = _pads->Get( Str, &var );
  495. if (hr && hr != E_ADS_PROPERTY_NOT_FOUND )
  496. goto Cleanup;
  497. if ( V_VT(&var) == VT_BOOL ) {
  498. *pbool = V_BOOL(&var);
  499. hr = S_OK;
  500. }
  501. Cleanup:
  502. VariantClear( &var );
  503. HRETURN(hr);
  504. }
  505. //
  506. // SetAllowNewClients( )
  507. //
  508. HRESULT
  509. THISCLASS::SetAllowNewClients(
  510. BOOL boolval )
  511. {
  512. TraceClsFunc("[IIntelliMirrorSAP] SetAllowNewClients( ... )\n" );
  513. HRESULT hr;
  514. VARIANT var;
  515. _bstr_t Str;
  516. VariantInit( &var );
  517. V_VT( &var ) = VT_BOOL;
  518. V_BOOL( &var ) = (VARIANT_BOOL)boolval;
  519. Assert( _pads );
  520. Str = NETBOOTALLOWNEWCLIENTS;
  521. hr = THR( _pads->Put( Str, var ) );
  522. VariantClear( &var );
  523. HRETURN(hr);
  524. }
  525. //
  526. // GetLimitClients( )
  527. //
  528. HRESULT
  529. THISCLASS::GetLimitClients(
  530. BOOL *pbool )
  531. {
  532. TraceClsFunc("[IIntelliMirrorSAP] GetLimitClients( ... )\n" );
  533. if ( !pbool )
  534. RRETURN(E_POINTER);
  535. HRESULT hr;
  536. VARIANT var;
  537. _bstr_t Str;
  538. VariantInit( &var );
  539. Assert( _pads );
  540. Str = NETBOOTLIMITCLIENTS;
  541. hr = _pads->Get( Str, &var );
  542. if (hr && hr != E_ADS_PROPERTY_NOT_FOUND )
  543. goto Cleanup;
  544. if ( V_VT(&var) == VT_BOOL ) {
  545. *pbool = V_BOOL(&var);
  546. hr = S_OK;
  547. }
  548. Cleanup:
  549. VariantClear( &var );
  550. HRETURN(hr);
  551. }
  552. //
  553. // SetLimitClients( )
  554. //
  555. HRESULT
  556. THISCLASS::SetLimitClients(
  557. BOOL boolval )
  558. {
  559. TraceClsFunc("[IIntelliMirrorSAP] SetLimitClients( ... )\n" );
  560. HRESULT hr;
  561. VARIANT var;
  562. _bstr_t Str;
  563. VariantInit( &var );
  564. V_VT( &var ) = VT_BOOL;
  565. V_BOOL( &var ) = (VARIANT_BOOL)boolval;
  566. Assert( _pads );
  567. Str = NETBOOTLIMITCLIENTS;
  568. hr = THR( _pads->Put( Str, var ) );
  569. VariantClear( &var );
  570. HRETURN(hr);
  571. }
  572. //
  573. // GetMaxClients( )
  574. //
  575. HRESULT
  576. THISCLASS::GetMaxClients(
  577. UINT *puMax )
  578. {
  579. TraceClsFunc("[IIntelliMirrorSAP] GetMaxClients( ... )\n" );
  580. if ( !puMax )
  581. RRETURN(E_POINTER);
  582. HRESULT hr;
  583. VARIANT var;
  584. _bstr_t Str;
  585. VariantInit( &var );
  586. Assert( _pads );
  587. Str = NETBOOTMAXCLIENTS;
  588. hr = _pads->Get( Str, &var );
  589. if (hr && hr != E_ADS_PROPERTY_NOT_FOUND )
  590. goto Cleanup;
  591. if ( V_VT(&var) == VT_I4 ) {
  592. *puMax = V_I4(&var);
  593. hr = S_OK;
  594. }
  595. Cleanup:
  596. VariantClear( &var );
  597. HRETURN(hr);
  598. }
  599. //
  600. // SetMaxClients( )
  601. //
  602. HRESULT
  603. THISCLASS::SetMaxClients(
  604. UINT uMax )
  605. {
  606. TraceClsFunc("[IIntelliMirrorSAP] SetMaxClients( ... )\n" );
  607. HRESULT hr;
  608. VARIANT var;
  609. _bstr_t Str;
  610. VariantInit( &var );
  611. V_VT( &var ) = VT_I4;
  612. V_I4( &var ) = uMax;
  613. Assert( _pads );
  614. Str = NETBOOTMAXCLIENTS;
  615. hr = THR( _pads->Put( Str, var ) );
  616. VariantClear( &var );
  617. HRETURN(hr);
  618. }
  619. //
  620. // GetCurrentClientCount( )
  621. //
  622. HRESULT
  623. THISCLASS::GetCurrentClientCount(
  624. UINT *puCount )
  625. {
  626. TraceClsFunc("[IIntelliMirrorSAP] GetCurrentClientCount( ... )\n" );
  627. if ( !puCount )
  628. RRETURN(E_POINTER);
  629. HRESULT hr;
  630. VARIANT var;
  631. _bstr_t Str;
  632. VariantInit( &var );
  633. Assert( _pads );
  634. Str = NETBOOTCURRENTCLIENTCOUNT;
  635. hr = _pads->Get( Str, &var );
  636. if (hr && hr != E_ADS_PROPERTY_NOT_FOUND )
  637. goto Cleanup;
  638. if ( V_VT(&var) == VT_I4 ) {
  639. *puCount = V_I4(&var);
  640. hr = S_OK;
  641. }
  642. Cleanup:
  643. VariantClear( &var );
  644. HRETURN(hr);
  645. }
  646. //
  647. // SetCurrentClientCount( )
  648. //
  649. HRESULT
  650. THISCLASS::SetCurrentClientCount(
  651. UINT uCount )
  652. {
  653. TraceClsFunc("[IIntelliMirrorSAP] SetCurrentClientCount( ... )\n" );
  654. HRESULT hr;
  655. VARIANT var;
  656. _bstr_t Str;
  657. VariantInit( &var );
  658. V_VT( &var ) = VT_I4;
  659. V_I4( &var ) = uCount;
  660. Assert( _pads );
  661. Str = NETBOOTCURRENTCLIENTCOUNT;
  662. hr = THR( _pads->Put( Str, var ) );
  663. VariantClear( &var );
  664. HRETURN(hr);
  665. }
  666. //
  667. // GetAnswerRequests( )
  668. //
  669. HRESULT
  670. THISCLASS::GetAnswerRequests(
  671. BOOL *pbool )
  672. {
  673. TraceClsFunc("[IIntelliMirrorSAP] GetAnswerRequests( ... )\n" );
  674. if ( !pbool )
  675. RRETURN(E_POINTER);
  676. HRESULT hr;
  677. VARIANT var;
  678. _bstr_t Str;
  679. VariantInit( &var );
  680. Assert( _pads );
  681. Str = NETBOOTANSWERREQUESTS;
  682. hr = _pads->Get( Str, &var );
  683. if (hr && hr != E_ADS_PROPERTY_NOT_FOUND )
  684. goto Cleanup;
  685. if ( V_VT(&var) == VT_BOOL ) {
  686. *pbool = V_BOOL(&var);
  687. hr = S_OK;
  688. }
  689. Cleanup:
  690. VariantClear( &var );
  691. HRETURN(hr);
  692. }
  693. //
  694. // SetAnswerRequests( )
  695. //
  696. HRESULT
  697. THISCLASS::SetAnswerRequests(
  698. BOOL boolval )
  699. {
  700. TraceClsFunc("[IIntelliMirrorSAP] SetAnswerRequests( ... )\n" );
  701. HRESULT hr;
  702. VARIANT var;
  703. _bstr_t Str;
  704. VariantInit( &var );
  705. V_VT( &var ) = VT_BOOL;
  706. V_BOOL( &var ) = (VARIANT_BOOL)boolval;
  707. Assert( _pads );
  708. Str = NETBOOTANSWERREQUESTS;
  709. hr = THR( _pads->Put( Str, var ) );
  710. VariantClear( &var );
  711. HRETURN(hr);
  712. }
  713. //
  714. // GetAnswerOnlyValidClients( )
  715. //
  716. HRESULT
  717. THISCLASS::GetAnswerOnlyValidClients(
  718. BOOL *pbool )
  719. {
  720. TraceClsFunc("[IIntelliMirrorSAP] GetAnswerOnlyValidClients( ... )\n" );
  721. if ( !pbool )
  722. RRETURN(E_POINTER);
  723. HRESULT hr;
  724. VARIANT var;
  725. _bstr_t Str;
  726. VariantInit( &var );
  727. Assert( _pads );
  728. Str = NETBOOTANSWERONLYVALIDCLIENTS;
  729. hr = _pads->Get( Str, &var );
  730. if (hr && hr != E_ADS_PROPERTY_NOT_FOUND )
  731. goto Cleanup;
  732. if ( V_VT(&var) == VT_BOOL ) {
  733. *pbool = V_BOOL(&var);
  734. hr = S_OK;
  735. }
  736. Cleanup:
  737. VariantClear( &var );
  738. HRETURN(hr);
  739. }
  740. //
  741. // SetAnswerOnlyValidClients( )
  742. //
  743. HRESULT
  744. THISCLASS::SetAnswerOnlyValidClients(
  745. BOOL boolval )
  746. {
  747. TraceClsFunc("[IIntelliMirrorSAP] SetAnswerOnlyValidClients( ... )\n" );
  748. HRESULT hr;
  749. VARIANT var;
  750. _bstr_t Str;
  751. VariantInit( &var );
  752. V_VT( &var ) = VT_BOOL;
  753. V_BOOL( &var ) = (VARIANT_BOOL)boolval;
  754. Assert( _pads );
  755. Str = NETBOOTANSWERONLYVALIDCLIENTS;
  756. hr = THR( _pads->Put( Str , var ) );
  757. VariantClear( &var );
  758. HRETURN(hr);
  759. }
  760. //
  761. // GetNewMachineNamingPolicy( )
  762. //
  763. HRESULT
  764. THISCLASS::GetNewMachineNamingPolicy(
  765. LPWSTR *pwsz )
  766. {
  767. TraceClsFunc("[IIntelliMirrorSAP] GetNewMachineNamingPolicy( ... )\n" );
  768. if ( !pwsz )
  769. RRETURN(E_POINTER);
  770. HRESULT hr;
  771. VARIANT var;
  772. _bstr_t Str;
  773. VariantInit( &var );
  774. Assert( _pads );
  775. Str = NETBOOTNEWMACHINENAMINGPOLICY;
  776. hr = _pads->Get( Str, &var );
  777. if (hr && hr != E_ADS_PROPERTY_NOT_FOUND )
  778. goto Cleanup;
  779. if ( V_VT(&var) == VT_BSTR ) {
  780. *pwsz = TraceStrDup( V_BSTR(&var) );
  781. if (!*pwsz) {
  782. hr = E_OUTOFMEMORY;
  783. }
  784. }
  785. Cleanup:
  786. VariantClear( &var );
  787. HRETURN(hr);
  788. }
  789. //
  790. // SetNewMachineNamingPolicy( )
  791. //
  792. HRESULT
  793. THISCLASS::SetNewMachineNamingPolicy(
  794. LPWSTR pwsz )
  795. {
  796. TraceClsFunc("[IIntelliMirrorSAP] SetNewMachineNamingPolicy( ... )\n" );
  797. HRESULT hr;
  798. VARIANT var;
  799. _bstr_t Str;
  800. Assert( _pads );
  801. if ( pwsz ) {
  802. V_VT( &var ) = VT_BSTR;
  803. V_BSTR( &var ) = SysAllocString( pwsz );
  804. if (V_BSTR(&var) == NULL) {
  805. RRETURN(E_OUTOFMEMORY);
  806. }
  807. Str = NETBOOTNEWMACHINENAMINGPOLICY;
  808. hr = THR( _pads->Put( Str, var ) );
  809. VariantClear( &var );
  810. } else {
  811. VariantInit( &var );
  812. Str = NETBOOTNEWMACHINENAMINGPOLICY;
  813. hr = THR( _pads->PutEx( ADS_PROPERTY_CLEAR, Str, var ) );
  814. }
  815. HRETURN(hr);
  816. }
  817. //
  818. // GetNewMachineOU( )
  819. //
  820. HRESULT
  821. THISCLASS::GetNewMachineOU(
  822. LPWSTR *pwsz )
  823. {
  824. TraceClsFunc("[IIntelliMirrorSAP] GetNewMachineOU( ... )\n" );
  825. if ( !pwsz )
  826. RRETURN(E_POINTER);
  827. HRESULT hr;
  828. VARIANT var;
  829. _bstr_t Str;
  830. VariantInit( &var );
  831. Assert( _pads );
  832. Str = NETBOOTNEWMACHINEOU;
  833. hr = _pads->Get( Str, &var );
  834. if (hr && hr != E_ADS_PROPERTY_NOT_FOUND )
  835. goto Cleanup;
  836. if ( V_VT(&var) == VT_BSTR ) {
  837. *pwsz = TraceStrDup( V_BSTR(&var) );
  838. if (!*pwsz) {
  839. hr = E_OUTOFMEMORY;
  840. }
  841. }
  842. Cleanup:
  843. VariantClear( &var );
  844. HRETURN(hr);
  845. }
  846. //
  847. // SetNewMachineOU( )
  848. //
  849. HRESULT
  850. THISCLASS::SetNewMachineOU(
  851. LPWSTR pwsz )
  852. {
  853. TraceClsFunc("[IIntelliMirrorSAP] SetNewMachineOU( ... )\n" );
  854. HRESULT hr;
  855. VARIANT var;
  856. _bstr_t Str;
  857. Assert( _pads );
  858. if ( pwsz ) {
  859. V_VT( &var ) = VT_BSTR;
  860. V_BSTR( &var ) = SysAllocString( pwsz );
  861. if (V_BSTR(&var) == NULL) {
  862. RRETURN(E_OUTOFMEMORY);
  863. }
  864. Str = NETBOOTNEWMACHINEOU;
  865. hr = THR( _pads->Put( Str, var ) );
  866. VariantClear( &var );
  867. } else {
  868. VariantInit( &var );
  869. Str = NETBOOTNEWMACHINEOU;
  870. hr = THR( _pads->PutEx( ADS_PROPERTY_CLEAR, Str, var ) );
  871. }
  872. HRETURN(hr);
  873. }
  874. //
  875. // EnumIntelliMirrorOSes( )
  876. //
  877. HRESULT
  878. THISCLASS::EnumIntelliMirrorOSes(
  879. DWORD dwFlags,
  880. LPUNKNOWN *punk )
  881. {
  882. TraceClsFunc("[IIntelliMirrorSAP] EnumIntelliMirrorOSes( ... )\n" );
  883. if ( !punk )
  884. RRETURN(E_POINTER);
  885. HRESULT hr = S_OK;
  886. Assert( _pads );
  887. *punk = (LPUNKNOWN)
  888. CEnumIMSIFs_CreateInstance( REMOTE_INSTALL_IMAGE_DIR_W, NETBOOTINTELLIMIRROROSES, dwFlags, _pads );
  889. if ( !*punk ) {
  890. hr = E_FAIL;
  891. }
  892. HRETURN(hr);
  893. }
  894. //
  895. // EnumTools( )
  896. //
  897. HRESULT
  898. THISCLASS::EnumTools(
  899. DWORD dwFlags,
  900. LPUNKNOWN *punk )
  901. {
  902. TraceClsFunc("[IIntelliMirrorSAP] EnumTools( ... )\n" );
  903. if ( !punk )
  904. RETURN(E_POINTER);
  905. HRESULT hr = S_OK;
  906. Assert( _pads );
  907. *punk = (LPUNKNOWN)
  908. CEnumIMSIFs_CreateInstance( REMOTE_INSTALL_TOOLS_DIR_W, NETBOOTTOOLS, dwFlags, _pads );
  909. if ( !*punk ) {
  910. hr = E_FAIL;
  911. }
  912. HRETURN(hr);
  913. }
  914. //
  915. // GetServerDN( )
  916. //
  917. HRESULT
  918. THISCLASS::GetServerDN(
  919. LPWSTR *pwsz )
  920. {
  921. TraceClsFunc("[IIntelliMirrorSAP] GetServerDN( ... )\n" );
  922. if ( !pwsz )
  923. RRETURN(E_POINTER);
  924. HRESULT hr;
  925. VARIANT var;
  926. _bstr_t Str;
  927. VariantInit( &var );
  928. Assert( _pads );
  929. Str = NETBOOTSERVER;
  930. hr = _pads->Get( Str, &var );
  931. if (hr && hr != E_ADS_PROPERTY_NOT_FOUND )
  932. goto Cleanup;
  933. if ( V_VT(&var) == VT_BSTR ) {
  934. *pwsz = TraceStrDup( V_BSTR(&var) );
  935. if ( !*pwsz ) {
  936. hr = E_OUTOFMEMORY;
  937. }
  938. }
  939. Cleanup:
  940. VariantClear( &var );
  941. HRETURN(hr);
  942. }
  943. //
  944. // SetServerDN( )
  945. //
  946. HRESULT
  947. THISCLASS::SetServerDN(
  948. LPWSTR pwsz )
  949. {
  950. TraceClsFunc("[IIntelliMirrorSAP] SetServerDN( ... )\n" );
  951. if ( !pwsz )
  952. RRETURN(E_POINTER);
  953. HRESULT hr;
  954. VARIANT var;
  955. _bstr_t Str;
  956. V_VT( &var ) = VT_BSTR;
  957. V_BSTR( &var ) = SysAllocString( pwsz );
  958. if (V_BSTR(&var) == NULL) {
  959. RRETURN(E_OUTOFMEMORY);
  960. }
  961. Assert( _pads );
  962. Str = NETBOOTSERVER;
  963. hr = THR( _pads->Put( Str, var ) );
  964. VariantClear( &var );
  965. HRETURN(hr);
  966. }
  967. #if 0
  968. //
  969. // GetDefaultIntelliMirrorOS( )
  970. //
  971. HRESULT
  972. THISCLASS::GetDefaultIntelliMirrorOS(
  973. LPWSTR * pszName,
  974. LPWSTR * pszTimeout )
  975. {
  976. TraceClsFunc( "[IIntelliMirrorSAP] GetDefaultIntelliMirrorOS( ...)\n" );
  977. HRESULT hr;
  978. hr = _GetDefaultSIF( NETBOOTINTELLIMIRROROSES, pszName, pszTimeout );
  979. HRETURN(hr);
  980. }
  981. //
  982. // SetDefaultIntelliMirrorOS( )
  983. //
  984. HRESULT
  985. THISCLASS::SetDefaultIntelliMirrorOS(
  986. LPWSTR pszName,
  987. LPWSTR pszTimeout )
  988. {
  989. TraceClsFunc( "[IIntelliMirrorSAP] SetDefaultIntelliMirrorOS( " );
  990. TraceMsg( TF_FUNC, "pszName = '%s', pszTimeout = '%s' )\n",
  991. pszName, pszTimeout );
  992. HRESULT hr;
  993. hr = _SetDefaultSIF( NETBOOTINTELLIMIRROROSES, pszName, pszTimeout );
  994. HRETURN(hr);
  995. }
  996. //
  997. // _GetDefaultSIF( )
  998. //
  999. HRESULT
  1000. THISCLASS::_GetDefaultSIF(
  1001. LPWSTR pszAttribute,
  1002. LPWSTR * pszName,
  1003. LPWSTR * pszTimeout )
  1004. {
  1005. TraceClsFunc( "_GetDefaultSIF( " );
  1006. TraceMsg( TF_FUNC, "pszAttribute = '%s', ... )\n" , pszAttribute );
  1007. if ( !pszAttribute )
  1008. RRETURN(E_POINTER);
  1009. if ( !pszName )
  1010. RRETURN(E_POINTER);
  1011. if ( !pszTimeout )
  1012. RRETURN(E_POINTER);
  1013. HRESULT hr;
  1014. LONG lUBound;
  1015. VARIANT var;
  1016. VARIANT * pvar;
  1017. _bstr_t Str;
  1018. VariantInit( &var );
  1019. *pszName = NULL;
  1020. *pszTimeout = NULL;
  1021. Assert( _pads );
  1022. Str = pszAttribute;
  1023. hr = THR( _pads->GetEx( Str, &var ) );
  1024. if (FAILED( hr )) {
  1025. goto Error;
  1026. }
  1027. //
  1028. // Make sure that the var is an array of VARIANTs
  1029. //
  1030. if ( V_VT( &var ) != ( VT_ARRAY | VT_VARIANT ) ) {
  1031. hr = ERROR_INVALID_DATA;
  1032. goto Error;
  1033. }
  1034. Assert( SafeArrayGetDim( V_ARRAY( &var ) ) == 1 );
  1035. #ifdef DEBUG
  1036. {
  1037. LONG lLBound;
  1038. SafeArrayGetLBound( V_ARRAY( &var ), 1, &lLBound );
  1039. Assert( lLBound == 0 );
  1040. }
  1041. #endif // DEBUG
  1042. SafeArrayGetUBound( V_ARRAY( &var ), 1, &lUBound );
  1043. //
  1044. // Copy the required data
  1045. //
  1046. SafeArrayAccessData( V_ARRAY( &var ), (void **)&pvar );
  1047. *pszName = (LPWSTR) TraceStrDup( V_BSTR( &pvar[ 0 ] ) );
  1048. if (!*pszName) {
  1049. SafeArrayUnaccessData( V_ARRAY( &var ) );
  1050. hr = E_OUTOFMEMORY;
  1051. goto Error;
  1052. }
  1053. if ( lUBound == 2 ) {
  1054. *pszTimeout = (LPWSTR) TraceStrDup( V_BSTR( &pvar[ 1 ] ) );
  1055. if ( !*pszTimeout ) {
  1056. SafeArrayUnaccessData( V_ARRAY( &var ) );
  1057. hr = E_OUTOFMEMORY;
  1058. goto Error;
  1059. }
  1060. }
  1061. SafeArrayUnaccessData( V_ARRAY( &var ) );
  1062. Error:
  1063. VariantClear( &var );
  1064. HRETURN(hr);
  1065. }
  1066. //
  1067. // _SetDefaultSIF( )
  1068. //
  1069. HRESULT
  1070. THISCLASS::_SetDefaultSIF(
  1071. LPWSTR pszAttribute,
  1072. LPWSTR pszName,
  1073. LPWSTR pszTimeout )
  1074. {
  1075. TraceClsFunc( "_SetDefaultSIF( " );
  1076. TraceMsg( TF_FUNC, "pszAttribute = '%s', ... )\n" , pszAttribute );
  1077. if ( !pszAttribute )
  1078. RRETURN(E_POINTER);
  1079. if ( !pszName )
  1080. RRETURN(E_POINTER);
  1081. if ( !pszTimeout )
  1082. RRETURN(E_POINTER);
  1083. HRESULT hr;
  1084. LONG lUBound;
  1085. VARIANT var;
  1086. LPWSTR pszStrings[ 2 ];
  1087. _bstr_t Str;
  1088. pszStrings[0] = pszName;
  1089. pszStrings[1] = pszTimeout;
  1090. VariantInit( &var );
  1091. hr = THR( StringArrayToVariant( &var, pszStrings, 2 ) );
  1092. if (FAILED( hr ))
  1093. goto Error;
  1094. Assert( _pads );
  1095. Str = pszAttribute;
  1096. hr = THR( _pads->Put( Str, var ) );
  1097. if (FAILED( hr ))
  1098. goto Error;
  1099. Error:
  1100. VariantClear( &var );
  1101. HRETURN(hr);
  1102. }
  1103. #endif
  1104. //
  1105. // GetSCPDN( )
  1106. //
  1107. HRESULT
  1108. THISCLASS::GetSCPDN(
  1109. LPWSTR * ppwsz )
  1110. {
  1111. TraceClsFunc( "[IIntelliMirrorSAP] GetSCPDN( )\n" );
  1112. HRESULT hr = S_OK;
  1113. if ( !ppwsz )
  1114. HRETURN( E_POINTER );
  1115. if ( _pszSCPDN ) {
  1116. LPWSTR psz = StrRChr( _pszSCPDN, NULL, L'/' );
  1117. if ( psz ) {
  1118. psz++;
  1119. } else {
  1120. psz = _pszSCPDN;
  1121. }
  1122. *ppwsz = (LPWSTR) TraceStrDup( psz );
  1123. if ( !*ppwsz ) {
  1124. hr = E_OUTOFMEMORY;
  1125. }
  1126. } else {
  1127. *ppwsz = NULL;
  1128. }
  1129. HRETURN(hr);
  1130. }
  1131. #if 0
  1132. //
  1133. // GetGroupDN( )
  1134. //
  1135. HRESULT
  1136. THISCLASS::GetGroupDN(
  1137. LPWSTR * ppwsz )
  1138. {
  1139. TraceClsFunc( "[IIntelliMirrorSAP] GetGroupDN( )\n" );
  1140. HRESULT hr = S_OK;
  1141. if ( !ppwsz )
  1142. HRETURN( E_POINTER );
  1143. if ( _pszGroupDN ) {
  1144. *ppwsz = (LPWSTR) TraceStrDup( _pszGroupDN );
  1145. if ( !*ppwsz ) {
  1146. hr = E_OUTOFMEMORY;
  1147. }
  1148. } else {
  1149. *ppwsz = NULL;
  1150. hr = S_FALSE;
  1151. }
  1152. HRETURN(hr);
  1153. }
  1154. #endif
  1155. //
  1156. // GetServerName( )
  1157. //
  1158. STDMETHODIMP
  1159. THISCLASS::GetServerName(
  1160. LPWSTR * ppwsz )
  1161. {
  1162. TraceClsFunc( "[IIntelliMirrorSAP] GetServerName( )\n" );
  1163. HRESULT hr = S_OK;
  1164. if ( !ppwsz )
  1165. HRETURN(E_POINTER);
  1166. *ppwsz = NULL;
  1167. if ( !_pszMachineName ) {
  1168. hr = _GetComputerNameFromADs( );
  1169. if (FAILED( hr ))
  1170. goto Error;
  1171. }
  1172. if ( _pszMachineName ) {
  1173. *ppwsz = (LPWSTR) TraceStrDup( _pszMachineName );
  1174. if ( !*ppwsz ) {
  1175. hr = E_OUTOFMEMORY;
  1176. }
  1177. } else {
  1178. hr = S_FALSE;
  1179. }
  1180. Error:
  1181. HRETURN(hr);
  1182. }
  1183. //
  1184. // _GetComputerNameFromADs( )
  1185. //
  1186. HRESULT
  1187. THISCLASS::_GetComputerNameFromADs( )
  1188. {
  1189. TraceClsFunc( "_GetComputerNameFromADs( )\n" );
  1190. if ( _pszMachineName )
  1191. HRETURN(S_OK); // nop
  1192. HRESULT hr;
  1193. VARIANT var;
  1194. IADs *pads = NULL;
  1195. LPWSTR pszMachinePath = NULL;
  1196. LPWSTR psz;
  1197. _bstr_t Str;
  1198. VariantInit( &var );
  1199. // Retrieve the NETBOOTSERVER attribute
  1200. Str = NETBOOTSERVER;
  1201. hr = THR( _pads->Get( Str, &var ) );
  1202. if (FAILED( hr )) {
  1203. goto Cleanup;
  1204. }
  1205. Assert( V_VT( &var ) == VT_BSTR );
  1206. hr = FixObjectPath( _pszDSServerName, V_BSTR( &var ), &pszMachinePath );
  1207. if (FAILED( hr )) {
  1208. goto Cleanup;
  1209. }
  1210. VariantClear( &var );
  1211. hr = THR( ADsGetObject( pszMachinePath, IID_IADs, (void**) &pads ) );
  1212. if (FAILED( hr )) {
  1213. goto Cleanup;
  1214. }
  1215. Str = SAMNAME;
  1216. hr = THR( pads->Get( Str, &var ) );
  1217. if (FAILED( hr ) ||
  1218. V_VT( &var ) != VT_BSTR) {
  1219. if (hr == S_OK) {
  1220. hr = E_FAIL;
  1221. }
  1222. goto Cleanup;
  1223. }
  1224. Assert( V_VT( &var ) == VT_BSTR );
  1225. _pszMachineName = TraceStrDup( V_BSTR( &var ) );
  1226. if ( !_pszMachineName ) {
  1227. hr = E_OUTOFMEMORY;
  1228. goto Cleanup;
  1229. }
  1230. psz = &_pszMachineName[ wcslen( _pszMachineName ) - 1 ];
  1231. if ( *psz == L'$' ) {
  1232. *psz = L'\0';
  1233. }
  1234. DebugMsg( "Server Name: %ws\n", _pszMachineName );
  1235. Cleanup:
  1236. VariantClear( &var );
  1237. if ( pads )
  1238. pads->Release( );
  1239. if ( pszMachinePath )
  1240. TraceFree( pszMachinePath );
  1241. HRETURN(hr);
  1242. }
  1243. //
  1244. // GetDataObject( )
  1245. //
  1246. STDMETHODIMP
  1247. THISCLASS::GetDataObject(
  1248. LPDATAOBJECT * pDataObj
  1249. )
  1250. {
  1251. TraceClsFunc( "GetDataObject( ... )\n" );
  1252. if ( !pDataObj )
  1253. HRETURN( E_POINTER );
  1254. *pDataObj = _pDataObj;
  1255. _pDataObj->AddRef( );
  1256. HRETURN(S_OK);
  1257. }
  1258. //
  1259. // GetNotifyWindow( )
  1260. //
  1261. STDMETHODIMP
  1262. THISCLASS::GetNotifyWindow(
  1263. HWND * phNotifyObj
  1264. )
  1265. {
  1266. TraceClsFunc( "GetNotifyWindow( ... )\n" );
  1267. if ( !phNotifyObj )
  1268. HRETURN(E_POINTER);
  1269. *phNotifyObj = _hwndNotify;
  1270. HRETURN(S_OK);
  1271. }