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.

740 lines
18 KiB

  1. //
  2. // Copyright 1997 - Microsoft
  3. //
  4. //
  5. // DPGUIDQY.CPP - The duplicate GUID query form
  6. //
  7. #include "pch.h"
  8. #include "dpguidqy.h"
  9. #include "mangdlg.h"
  10. DEFINE_MODULE("IMADMUI")
  11. DEFINE_THISCLASS("CRIQueryForm")
  12. #define THISCLASS CRIQueryForm
  13. #define LPTHISCLASS LPCRIQueryForm
  14. #define FILTER_QUERY_BOTH L"(&(objectClass=computer)(netbootGUID=%ws*)(netbootMachineFilePath=%s))"
  15. #define FILTER_QUERY_GUID_ONLY L"(&(objectClass=computer)(netbootGUID=%ws*))"
  16. #define FILTER_QUERY_SERVER_ONLY L"(&(objectClass=computer)(netbootMachineFilePath=%s))"
  17. #define StringByteCopy(pDest, iOffset, sz) \
  18. { memcpy(&(((LPBYTE)pDest)[iOffset]), sz, StringByteSize(sz)); }
  19. #define StringByteSize(sz) \
  20. ((lstrlen(sz)+1)*sizeof(TCHAR))
  21. DWORD aQueryHelpMap[] = {
  22. IDC_E_SERVER, HIDC_E_SERVER,
  23. IDC_E_GUID, HIDC_E_GUID,
  24. NULL, NULL
  25. };
  26. //
  27. // CRIQueryForm_CreateInstance( )
  28. //
  29. LPVOID
  30. CRIQueryForm_CreateInstance( void )
  31. {
  32. TraceFunc( "CRIQueryForm_CreateInstance()\n" );
  33. LPTHISCLASS lpcc = new THISCLASS( );
  34. if ( !lpcc ) {
  35. RETURN(lpcc);
  36. }
  37. HRESULT hr = THR( lpcc->Init( ) );
  38. if ( hr )
  39. {
  40. delete lpcc;
  41. RETURN(NULL);
  42. }
  43. RETURN(lpcc);
  44. }
  45. //
  46. // Constructor
  47. //
  48. THISCLASS::THISCLASS( )
  49. {
  50. TraceClsFunc( "CRIQueryForm( )\n" );
  51. InterlockIncrement( g_cObjects );
  52. TraceFuncExit();
  53. }
  54. //
  55. // Init( )
  56. //
  57. HRESULT
  58. THISCLASS::Init( )
  59. {
  60. TraceClsFunc( "Init( )\n" );
  61. HRESULT hr;
  62. // IUnknown stuff
  63. BEGIN_QITABLE_IMP( CRIQueryForm, IQueryForm );
  64. QITABLE_IMP( IQueryForm );
  65. END_QITABLE_IMP( CRIQueryForm );
  66. Assert( _cRef == 0);
  67. AddRef( );
  68. hr = CheckClipboardFormats( );
  69. HRETURN(hr);
  70. }
  71. //
  72. // Destructor
  73. //
  74. THISCLASS::~THISCLASS( )
  75. {
  76. TraceClsFunc( "~CRIQueryForm( )\n" );
  77. InterlockDecrement( g_cObjects );
  78. TraceFuncExit();
  79. }
  80. // ************************************************************************
  81. //
  82. // IUnknown
  83. //
  84. // ************************************************************************
  85. //
  86. // QueryInterface()
  87. //
  88. STDMETHODIMP
  89. THISCLASS::QueryInterface(
  90. REFIID riid,
  91. LPVOID *ppv )
  92. {
  93. TraceClsFunc( "" );
  94. HRESULT hr = ::QueryInterface( this, _QITable, riid, ppv );
  95. QIRETURN( hr, riid );
  96. }
  97. //
  98. // AddRef()
  99. //
  100. STDMETHODIMP_(ULONG)
  101. THISCLASS::AddRef( void )
  102. {
  103. TraceClsFunc( "[IUnknown] AddRef( )\n" );
  104. InterlockIncrement( _cRef );
  105. RETURN(_cRef);
  106. }
  107. //
  108. // Release()
  109. //
  110. STDMETHODIMP_(ULONG)
  111. THISCLASS::Release( void )
  112. {
  113. TraceClsFunc( "[IUnknown] Release( )\n" );
  114. InterlockDecrement( _cRef );
  115. if ( _cRef )
  116. RETURN(_cRef);
  117. TraceDo( delete this );
  118. RETURN(0);
  119. }
  120. // ************************************************************************
  121. //
  122. // IQueryForm
  123. //
  124. // ************************************************************************
  125. //
  126. // Initialize( )
  127. //
  128. STDMETHODIMP
  129. THISCLASS::Initialize(
  130. HKEY hkForm)
  131. {
  132. TraceClsFunc( "[IQueryForm] Initialize( )\n" );
  133. HRETURN(S_OK);
  134. }
  135. //
  136. // SetObject( )
  137. //
  138. STDMETHODIMP
  139. THISCLASS::AddForms(
  140. LPCQADDFORMSPROC pAddFormsProc,
  141. LPARAM lParam )
  142. {
  143. TraceClsFunc( "[IQueryForm] AddForms(" );
  144. TraceMsg( TF_FUNC, " pAddFormsProc = 0x%p, lParam = 0x%p )\n", pAddFormsProc, lParam );
  145. if ( !pAddFormsProc )
  146. HRETURN(E_INVALIDARG);
  147. HRESULT hr = S_OK;
  148. CQFORM cqf;
  149. WCHAR szTitle[ 255 ];
  150. DWORD dw;
  151. dw = LoadString( g_hInstance, IDS_REMOTE_INSTALL_CLIENTS, szTitle, ARRAYSIZE(szTitle) );
  152. Assert(dw);
  153. ZeroMemory( &cqf, sizeof(cqf) );
  154. cqf.cbStruct = sizeof(cqf);
  155. cqf.dwFlags = CQFF_ISOPTIONAL;
  156. cqf.clsid = CLSID_RIQueryForm;
  157. cqf.pszTitle = szTitle;
  158. hr = THR( pAddFormsProc(lParam, &cqf) );
  159. HRETURN(hr);
  160. }
  161. //
  162. // AddPages( )
  163. //
  164. STDMETHODIMP
  165. THISCLASS::AddPages(
  166. LPCQADDPAGESPROC pAddPagesProc,
  167. LPARAM lParam)
  168. {
  169. TraceClsFunc( "[IQueryForm] AddPages(" );
  170. TraceMsg( TF_FUNC, " pAddPagesProc = 0x%p, lParam = 0x%p )\n", pAddPagesProc, lParam );
  171. if ( !pAddPagesProc )
  172. HRETURN(E_INVALIDARG);
  173. HRESULT hr = S_OK;
  174. CQPAGE cqp;
  175. cqp.cbStruct = sizeof(cqp);
  176. cqp.dwFlags = 0x0;
  177. cqp.pPageProc = (LPCQPAGEPROC) PropSheetPageProc;
  178. cqp.hInstance = g_hInstance;
  179. cqp.idPageName = IDS_REMOTE_INSTALL_CLIENTS;
  180. cqp.idPageTemplate = IDD_GUID_QUERY_FORM;
  181. cqp.pDlgProc = PropSheetDlgProc;
  182. cqp.lParam = (LPARAM)this;
  183. hr = THR( pAddPagesProc(lParam, CLSID_RIQueryForm, &cqp) );
  184. HRETURN(hr);
  185. }
  186. // ************************************************************************
  187. //
  188. // Property Sheet Functions
  189. //
  190. // ************************************************************************
  191. //
  192. // PropSheetDlgProc()
  193. //
  194. INT_PTR CALLBACK
  195. THISCLASS::PropSheetDlgProc(
  196. HWND hDlg,
  197. UINT uMsg,
  198. WPARAM wParam,
  199. LPARAM lParam )
  200. {
  201. //TraceMsg( TEXT("PropSheetDlgProc(") );
  202. //TraceMsg( TF_FUNC, TEXT(" hDlg = 0x%p, uMsg = 0x%p, wParam = 0x%p, lParam = 0x%p )\n"),
  203. // hDlg, uMsg, wParam, lParam );
  204. LPTHISCLASS pcc = (LPTHISCLASS) GetWindowLongPtr( hDlg, GWLP_USERDATA );
  205. if ( uMsg == WM_INITDIALOG )
  206. {
  207. TraceMsg( TF_WM, TEXT("WM_INITDIALOG\n"));
  208. CQPAGE * pcqp = (CQPAGE *) lParam;
  209. SetWindowLongPtr( hDlg, GWLP_USERDATA, pcqp->lParam );
  210. pcc = (LPTHISCLASS) pcqp->lParam;
  211. pcc->_InitDialog( hDlg, lParam );
  212. }
  213. if (pcc)
  214. {
  215. Assert( hDlg == pcc->_hDlg );
  216. switch ( uMsg )
  217. {
  218. case WM_NOTIFY:
  219. TraceMsg( TF_WM, TEXT("WM_NOTIFY\n") );
  220. return pcc->_OnNotify( wParam, lParam );
  221. case WM_COMMAND:
  222. TraceMsg( TF_WM, TEXT("WM_COMMAND\n") );
  223. return pcc->_OnCommand( wParam, lParam );
  224. case WM_HELP:// F1
  225. {
  226. LPHELPINFO phelp = (LPHELPINFO) lParam;
  227. WinHelp( (HWND) phelp->hItemHandle, g_cszHelpFile, HELP_WM_HELP, (DWORD_PTR) &aQueryHelpMap );
  228. }
  229. break;
  230. case WM_CONTEXTMENU: // right mouse click
  231. WinHelp((HWND) wParam, g_cszHelpFile, HELP_CONTEXTMENU, (DWORD_PTR) &aQueryHelpMap );
  232. break;
  233. }
  234. }
  235. return FALSE;
  236. }
  237. //
  238. // PropSheetPageProc()
  239. //
  240. HRESULT CALLBACK
  241. THISCLASS::PropSheetPageProc(
  242. LPCQPAGE pQueryPage,
  243. HWND hwnd,
  244. UINT uMsg,
  245. WPARAM wParam,
  246. LPARAM lParam)
  247. {
  248. TraceClsFunc( "PropSheetPageProc( " );
  249. TraceMsg( TF_FUNC, TEXT("pQueryPage = 0x%p, hwnd = 0x%p, uMsg = 0x%p, wParam= 0x%p, lParam = 0x%p )\n"),
  250. pQueryPage, hwnd, uMsg, wParam, lParam );
  251. HRESULT hr = E_NOTIMPL;
  252. Assert( pQueryPage );
  253. LPTHISCLASS pQueryForm = (LPTHISCLASS )pQueryPage->lParam;
  254. Assert( pQueryForm );
  255. switch ( uMsg )
  256. {
  257. // Initialize so AddRef the object we are associated with so that
  258. // we don't get unloaded.
  259. case CQPM_INITIALIZE:
  260. TraceMsg( TF_WM, "CQPM_INITIALIZE\n" );
  261. pQueryForm->AddRef();
  262. hr = S_OK;
  263. break;
  264. // Release, therefore Release the object we are associated with to
  265. // ensure correct destruction etc.
  266. case CQPM_RELEASE:
  267. TraceMsg( TF_WM, "CQPM_RELEASE\n" );
  268. SetWindowLongPtr( pQueryForm->_hDlg, GWLP_USERDATA, NULL );
  269. pQueryForm->Release();
  270. hr = S_OK;
  271. break;
  272. // Enable so fix the state of our two controls within the window.
  273. case CQPM_ENABLE:
  274. TraceMsg( TF_WM, "CQPM_ENABLE\n" );
  275. EnableWindow( GetDlgItem( hwnd, IDC_E_GUID ), (BOOL)wParam );
  276. EnableWindow( GetDlgItem( hwnd, IDC_E_SERVER ), (BOOL)wParam );
  277. hr = S_OK;
  278. break;
  279. // Fill out the parameter structure to return to the caller, this is
  280. // handler specific. In our case we constructure a query of the CN
  281. // and objectClass properties, and we show a columns displaying both
  282. // of these. For further information about the DSQUERYPARAMs structure
  283. // see dsquery.h
  284. case CQPM_GETPARAMETERS:
  285. TraceMsg( TF_WM, "CQPM_GETPARAMETERS\n" );
  286. hr = pQueryForm->_GetQueryParams( hwnd, (LPDSQUERYPARAMS*)lParam );
  287. break;
  288. // Clear form, therefore set the window text for these two controls
  289. // to zero.
  290. case CQPM_CLEARFORM:
  291. TraceMsg( TF_WM, "CQPM_CLEARFORM\n" );
  292. SetDlgItemText( hwnd, IDC_E_GUID, L"" );
  293. SetDlgItemText( hwnd, IDC_E_SERVER, L"" );
  294. hr = S_OK;
  295. break;
  296. case CQPM_SETDEFAULTPARAMETERS:
  297. TraceMsg( TF_WM, "CQPM_SETDEFAULTPARAMETERS: wParam = %s lParam = 0x%p\n", BOOLTOSTRING(wParam), lParam );
  298. if ( wParam && lParam )
  299. {
  300. VARIANT var;
  301. LPOPENQUERYWINDOW poqwi = (LPOPENQUERYWINDOW) lParam;
  302. IPropertyBag * ppb = poqwi->ppbFormParameters;
  303. Assert( ppb );
  304. VariantInit( &var );
  305. hr = ppb->Read( L"ServerName", &var, NULL );
  306. if (SUCCEEDED( hr ))
  307. {
  308. SetDlgItemText( hwnd, IDC_E_SERVER, V_BSTR( &var ) );
  309. VariantClear( &var );
  310. }
  311. hr = ppb->Read( L"ClientGuid", &var, NULL );
  312. if (SUCCEEDED( hr ))
  313. {
  314. SetDlgItemText( hwnd, IDC_E_GUID, V_BSTR( &var ) );
  315. VariantClear( &var );
  316. }
  317. }
  318. else
  319. {
  320. DebugMsg( "No default parameters given.\n" );
  321. hr = S_OK;
  322. }
  323. break;
  324. default:
  325. TraceMsg( TF_WM, "CQPM_message 0x%08x *** NOT IMPL ***\n", uMsg );
  326. hr = E_NOTIMPL;
  327. break;
  328. }
  329. RETURN(hr);
  330. }
  331. //
  332. // _OnPSPCB_Create( )
  333. //
  334. HRESULT
  335. THISCLASS::_OnPSPCB_Create( )
  336. {
  337. TraceClsFunc( "_OnPSPCB_Create( )\n" );
  338. HRETURN(S_OK);
  339. }
  340. //
  341. // _InitDialog( )
  342. //
  343. HRESULT
  344. THISCLASS::_InitDialog(
  345. HWND hDlg,
  346. LPARAM lParam )
  347. {
  348. TraceClsFunc( "_InitDialog( )\n" );
  349. _hDlg = hDlg;
  350. Edit_LimitText( GetDlgItem( _hDlg, IDC_E_GUID ), MAX_INPUT_GUID_STRING - 1 );
  351. Edit_LimitText( GetDlgItem( _hDlg, IDC_E_SERVER), DNS_MAX_NAME_LENGTH );
  352. HRETURN(S_OK);
  353. }
  354. //
  355. // _OnCommand( )
  356. //
  357. INT
  358. THISCLASS::_OnCommand( WPARAM wParam, LPARAM lParam )
  359. {
  360. TraceClsFunc( "_OnCommand( " );
  361. TraceMsg( TF_FUNC, "wParam = 0x%p, lParam = 0x%p )\n", wParam, lParam );
  362. #if 0
  363. switch ( LOWORD(wParam) )
  364. {
  365. }
  366. #endif
  367. RETURN(FALSE);
  368. }
  369. //
  370. // _OnNotify( )
  371. //
  372. INT
  373. THISCLASS::_OnNotify(
  374. WPARAM wParam,
  375. LPARAM lParam )
  376. {
  377. TraceClsFunc( "_OnNotify( " );
  378. TraceMsg( TF_FUNC, "wParam = 0x%p, lParam = 0x%p )\n", wParam, lParam );
  379. LPNMHDR lpnmhdr = (LPNMHDR) lParam;
  380. TraceMsg( TF_WM, "NMHDR: HWND = 0x%p, idFrom = 0x%08x, code = 0x%08x\n",
  381. lpnmhdr->hwndFrom, lpnmhdr->idFrom, lpnmhdr->code );
  382. #if 0
  383. switch( lpnmhdr->code )
  384. {
  385. }
  386. #endif
  387. RETURN(FALSE);
  388. }
  389. struct
  390. {
  391. INT fmt;
  392. INT cx;
  393. INT uID;
  394. LPCTSTR pDisplayProperty;
  395. }
  396. columns[] =
  397. {
  398. 0, 20, IDS_NAME, TEXT("cn"),
  399. 0, 50, IDS_GUID, TEXT("netbootGuid"),
  400. };
  401. //
  402. // _GetQueryParams( )
  403. //
  404. HRESULT
  405. THISCLASS::_GetQueryParams(
  406. HWND hWnd,
  407. LPDSQUERYPARAMS* ppdsqp )
  408. {
  409. TraceClsFunc( "_GetQueryParams( )\n" );
  410. if ( !ppdsqp )
  411. HRETURN(E_POINTER);
  412. HRESULT hr = S_OK;
  413. INT i;
  414. WCHAR szServer[DNS_MAX_NAME_BUFFER_LENGTH];
  415. WCHAR szGuid[MAX_INPUT_GUID_STRING * 2]; // room for escaped GUID
  416. WCHAR szFilter[ARRAYSIZE(szGuid)+ARRAYSIZE(szServer)+ARRAYSIZE(FILTER_QUERY_BOTH)];
  417. BYTE uGuid[ 16 ];
  418. DWORD uGuidLength;
  419. ULONG offset;
  420. BOOL fIncludeGuid = FALSE, fIncludeServer = FALSE;
  421. ULONG cbStruct = 0;
  422. LPDSQUERYPARAMS pDsQueryParams = NULL;
  423. #if 0
  424. if ( *ppdsqp )
  425. {
  426. // This page doesn't support appending its query data to an
  427. // existing DSQUERYPARAMS strucuture, only creating a new block,
  428. // therefore bail if we see the pointer is not NULL.
  429. hr = THR(E_INVALIDARG);
  430. goto Error;
  431. }
  432. #endif
  433. //
  434. // This is how searches are done:
  435. // - if both guid and server are left blank, search for all
  436. // accounts with netbootGuid specified (all managed computers).
  437. // - if either or both are specified, search for all computers
  438. // that match.
  439. // - if guid is specified as '*', treat it the same as if it had
  440. // been left blank (searches for all accounts with netbootGUID).
  441. //
  442. // Note that currently any account with a server in netbootMachineFilePath
  443. // will also have netbootGUID specified, because prestaging a
  444. // remote install computer always puts netbootGUID, and that is
  445. // the only way that netbootMachineFilePath will be set. So if the
  446. // user specifies a server but no guid, we don't need to include
  447. // netbootGUID=* in our ldap filter.
  448. //
  449. // Compute the size of the argument block
  450. if ( GetDlgItemText( hWnd, IDC_E_GUID, szGuid, ARRAYSIZE(szGuid)) )
  451. {
  452. //
  453. // Allow only a * as the GUID, to search for all machines with
  454. // a GUID.
  455. //
  456. if (wcscmp(szGuid, L"*") != 0)
  457. {
  458. if ( ValidateGuid(szGuid,uGuid,&uGuidLength) == E_FAIL || !uGuidLength ) {
  459. MessageBoxFromStrings( hWnd,
  460. IDS_INVALID_GUID_CAPTION,
  461. IDS_INVALID_PARTIAL_GUID_TEXT,
  462. MB_OK );
  463. hr = E_INVALIDARG;
  464. goto Error;
  465. }
  466. ZeroMemory( szGuid, sizeof(szGuid) );
  467. ldap_escape_filter_element((PCHAR)uGuid, uGuidLength, szGuid, sizeof(szGuid) );
  468. }
  469. else
  470. {
  471. szGuid[0] = L'\0'; // the filter adds a *, and "**" doesn't work, so blank it here
  472. }
  473. fIncludeGuid = TRUE;
  474. }
  475. if ( GetDlgItemText( hWnd, IDC_E_SERVER, szServer, ARRAYSIZE(szServer)) )
  476. {
  477. fIncludeServer = TRUE;
  478. }
  479. else
  480. {
  481. //
  482. // If no server or guid was specified, search for any guid.
  483. //
  484. if (!fIncludeGuid)
  485. {
  486. szGuid[0] = L'\0';
  487. fIncludeGuid = TRUE;
  488. }
  489. }
  490. if ( fIncludeGuid && fIncludeServer )
  491. {
  492. wsprintf( szFilter, FILTER_QUERY_BOTH, szGuid, szServer );
  493. }
  494. else if ( fIncludeGuid )
  495. {
  496. wsprintf( szFilter, FILTER_QUERY_GUID_ONLY, szGuid );
  497. }
  498. else if ( fIncludeServer )
  499. {
  500. wsprintf( szFilter, FILTER_QUERY_SERVER_ONLY, szServer );
  501. }
  502. #ifdef DEBUG
  503. else
  504. {
  505. AssertMsg( 0, "How did we get here?\n" );
  506. szFilter[0] = L'\0';
  507. }
  508. #endif // DEBUG
  509. DebugMsg( "RI Filter: %s\n", szFilter );
  510. // compute the size of the new query block
  511. if ( !*ppdsqp )
  512. {
  513. offset = cbStruct = sizeof(DSQUERYPARAMS) + ((ARRAYSIZE(columns)-1)*sizeof(DSCOLUMN));
  514. cbStruct += StringByteSize(szFilter);
  515. for ( i = 0; i < ARRAYSIZE(columns); i++ )
  516. {
  517. cbStruct += StringByteSize(columns[i].pDisplayProperty);
  518. }
  519. }
  520. else
  521. {
  522. LPWSTR pszQuery;
  523. pszQuery = (LPWSTR) ((LPBYTE)(*ppdsqp) + (*ppdsqp)->offsetQuery);
  524. offset = (*ppdsqp)->cbStruct;
  525. cbStruct = (*ppdsqp)->cbStruct + StringByteSize( pszQuery ) + StringByteSize( szFilter );
  526. }
  527. // Allocate it and populate it with the data, the header is fixed
  528. // but the strings are referenced by offset.
  529. pDsQueryParams = (LPDSQUERYPARAMS)CoTaskMemAlloc(cbStruct);
  530. if ( !pDsQueryParams )
  531. {
  532. hr = E_OUTOFMEMORY;
  533. goto Error;
  534. }
  535. // Did they hand in a query that we need to modify?
  536. if ( !*ppdsqp)
  537. { // no... create our own query
  538. pDsQueryParams->cbStruct = cbStruct;
  539. pDsQueryParams->dwFlags = 0;
  540. pDsQueryParams->hInstance = g_hInstance;
  541. pDsQueryParams->offsetQuery = offset;
  542. pDsQueryParams->iColumns = ARRAYSIZE(columns);
  543. // Copy the filter string and bump the offset
  544. StringByteCopy(pDsQueryParams, offset, szFilter);
  545. offset += StringByteSize(szFilter);
  546. // Fill in the array of columns to dispaly, the cx is a percentage of the
  547. // current view, the propertie names to display are UNICODE strings and
  548. // are referenced by offset, therefore we bump the offset as we copy
  549. // each one.
  550. for ( i = 0 ; i < ARRAYSIZE(columns); i++ )
  551. {
  552. pDsQueryParams->aColumns[i].fmt = columns[i].fmt;
  553. pDsQueryParams->aColumns[i].cx = columns[i].cx;
  554. pDsQueryParams->aColumns[i].idsName = columns[i].uID;
  555. pDsQueryParams->aColumns[i].offsetProperty = offset;
  556. StringByteCopy(pDsQueryParams, offset, columns[i].pDisplayProperty);
  557. offset += StringByteSize(columns[i].pDisplayProperty);
  558. }
  559. }
  560. else
  561. { // yes... add our parameters to the query
  562. LPWSTR pszQuery;
  563. LPWSTR pszNewQuery;
  564. INT n;
  565. // duplicate the existing query
  566. Assert( offset == (*ppdsqp)->cbStruct );
  567. CopyMemory( pDsQueryParams, *ppdsqp, offset );
  568. pDsQueryParams->cbStruct = cbStruct;
  569. #if 0
  570. // add our columns
  571. for ( i = 0 ; i < ARRAYSIZE(columns); i++ )
  572. {
  573. pDsQueryParams->aColumns[n+i].fmt = columns[i].fmt;
  574. pDsQueryParams->aColumns[n+i].cx = columns[i].cx;
  575. pDsQueryParams->aColumns[n+i].idsName = columns[i].uID;
  576. pDsQueryParams->aColumns[n+i].offsetProperty = offset;
  577. StringByteCopy(pDsQueryParams, offset, columns[i].pDisplayProperty);
  578. offset += StringByteSize(columns[i].pDisplayProperty);
  579. }
  580. #endif
  581. // new query location
  582. pDsQueryParams->offsetQuery = offset;
  583. pszQuery = (LPWSTR) ((LPBYTE)(*ppdsqp) + (*ppdsqp)->offsetQuery);
  584. pszNewQuery = (LPWSTR) ((LPBYTE)pDsQueryParams + offset);
  585. Assert( pszQuery );
  586. // append to their query
  587. if ( StrCmpN( pszQuery, L"(&", 2 ) == 0 )
  588. {
  589. pszQuery[ wcslen( pszQuery ) - 1 ] = L'\0'; // remove ")"
  590. }
  591. else
  592. {
  593. wcscpy( pszNewQuery, L"(&" ); // add "(&" to begining of query
  594. }
  595. wcscat( pszNewQuery, pszQuery ); // add their query
  596. wcscat( pszNewQuery, &szFilter[2] ); // add our query starting after the "(&"
  597. offset += StringByteSize( pszNewQuery ); // compute new offset
  598. DebugMsg( "New Query String: %s\n", pszNewQuery );
  599. // Cleanup
  600. CoTaskMemFree( *ppdsqp );
  601. }
  602. // Success
  603. *ppdsqp = pDsQueryParams;
  604. Assert( hr == S_OK );
  605. Cleanup:
  606. HRETURN(hr);
  607. Error:
  608. if ( pDsQueryParams )
  609. CoTaskMemFree( pDsQueryParams );
  610. // If we aren't modifying the query and there wasn't
  611. // a query handed into us, indicate failure instead.
  612. if ( hr == S_FALSE && !*ppdsqp )
  613. {
  614. hr = E_FAIL;
  615. }
  616. goto Cleanup;
  617. }