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.

746 lines
20 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. ((wcslen(sz)+1)*sizeof(WCHAR))
  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 ( FAILED(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. if (!LoadString(
  151. g_hInstance,
  152. IDS_REMOTE_INSTALL_CLIENTS,
  153. szTitle,
  154. ARRAYSIZE(szTitle) )) {
  155. HRETURN(HRESULT_FROM_WIN32(GetLastError()));
  156. }
  157. ZeroMemory( &cqf, sizeof(cqf) );
  158. cqf.cbStruct = sizeof(cqf);
  159. cqf.dwFlags = CQFF_ISOPTIONAL;
  160. cqf.clsid = CLSID_RIQueryForm;
  161. cqf.pszTitle = szTitle;
  162. hr = THR( pAddFormsProc(lParam, &cqf) );
  163. HRETURN(hr);
  164. }
  165. //
  166. // AddPages( )
  167. //
  168. STDMETHODIMP
  169. THISCLASS::AddPages(
  170. LPCQADDPAGESPROC pAddPagesProc,
  171. LPARAM lParam)
  172. {
  173. TraceClsFunc( "[IQueryForm] AddPages(" );
  174. TraceMsg( TF_FUNC, " pAddPagesProc = 0x%p, lParam = 0x%p )\n", pAddPagesProc, lParam );
  175. if ( !pAddPagesProc )
  176. HRETURN(E_INVALIDARG);
  177. HRESULT hr = S_OK;
  178. CQPAGE cqp;
  179. cqp.cbStruct = sizeof(cqp);
  180. cqp.dwFlags = 0x0;
  181. cqp.pPageProc = (LPCQPAGEPROC) PropSheetPageProc;
  182. cqp.hInstance = g_hInstance;
  183. cqp.idPageName = IDS_REMOTE_INSTALL_CLIENTS;
  184. cqp.idPageTemplate = IDD_GUID_QUERY_FORM;
  185. cqp.pDlgProc = PropSheetDlgProc;
  186. cqp.lParam = (LPARAM)this;
  187. hr = THR( pAddPagesProc(lParam, CLSID_RIQueryForm, &cqp) );
  188. HRETURN(hr);
  189. }
  190. // ************************************************************************
  191. //
  192. // Property Sheet Functions
  193. //
  194. // ************************************************************************
  195. //
  196. // PropSheetDlgProc()
  197. //
  198. INT_PTR CALLBACK
  199. THISCLASS::PropSheetDlgProc(
  200. HWND hDlg,
  201. UINT uMsg,
  202. WPARAM wParam,
  203. LPARAM lParam )
  204. {
  205. //TraceMsg( L"PropSheetDlgProc(" );
  206. //TraceMsg( TF_FUNC, L" hDlg = 0x%p, uMsg = 0x%p, wParam = 0x%p, lParam = 0x%p )\n",
  207. // hDlg, uMsg, wParam, lParam );
  208. LPTHISCLASS pcc = (LPTHISCLASS) GetWindowLongPtr( hDlg, GWLP_USERDATA );
  209. if ( uMsg == WM_INITDIALOG )
  210. {
  211. TraceMsg( TF_WM, L"WM_INITDIALOG\n");
  212. CQPAGE * pcqp = (CQPAGE *) lParam;
  213. // bugbug can this fail?
  214. SetWindowLongPtr( hDlg, GWLP_USERDATA, pcqp->lParam );
  215. pcc = (LPTHISCLASS) pcqp->lParam;
  216. pcc->_InitDialog( hDlg, lParam );
  217. }
  218. if (pcc)
  219. {
  220. Assert( hDlg == pcc->_hDlg );
  221. switch ( uMsg )
  222. {
  223. case WM_HELP:// F1
  224. {
  225. LPHELPINFO phelp = (LPHELPINFO) lParam;
  226. WinHelp( (HWND) phelp->hItemHandle, g_cszHelpFile, HELP_WM_HELP, (DWORD_PTR) &aQueryHelpMap );
  227. }
  228. break;
  229. case WM_CONTEXTMENU: // right mouse click
  230. WinHelp((HWND) wParam, g_cszHelpFile, HELP_CONTEXTMENU, (DWORD_PTR) &aQueryHelpMap );
  231. break;
  232. }
  233. }
  234. return FALSE;
  235. }
  236. //
  237. // PropSheetPageProc()
  238. //
  239. HRESULT CALLBACK
  240. THISCLASS::PropSheetPageProc(
  241. LPCQPAGE pQueryPage,
  242. HWND hwnd,
  243. UINT uMsg,
  244. WPARAM wParam,
  245. LPARAM lParam)
  246. {
  247. TraceClsFunc( "PropSheetPageProc( " );
  248. TraceMsg( TF_FUNC, L"pQueryPage = 0x%p, hwnd = 0x%p, uMsg = 0x%p, wParam= 0x%p, lParam = 0x%p )\n",
  249. pQueryPage, hwnd, uMsg, wParam, lParam );
  250. HRESULT hr = E_NOTIMPL;
  251. Assert( pQueryPage );
  252. LPTHISCLASS pQueryForm = (LPTHISCLASS )pQueryPage->lParam;
  253. Assert( pQueryForm );
  254. switch ( uMsg )
  255. {
  256. // Initialize so AddRef the object we are associated with so that
  257. // we don't get unloaded.
  258. case CQPM_INITIALIZE:
  259. TraceMsg( TF_WM, "CQPM_INITIALIZE\n" );
  260. pQueryForm->AddRef();
  261. hr = S_OK;
  262. break;
  263. // Release, therefore Release the object we are associated with to
  264. // ensure correct destruction etc.
  265. case CQPM_RELEASE:
  266. TraceMsg( TF_WM, "CQPM_RELEASE\n" );
  267. SetWindowLongPtr( pQueryForm->_hDlg, GWLP_USERDATA, NULL );
  268. pQueryForm->Release();
  269. hr = S_OK;
  270. break;
  271. // Enable so fix the state of our two controls within the window.
  272. case CQPM_ENABLE:
  273. TraceMsg( TF_WM, "CQPM_ENABLE\n" );
  274. EnableWindow( GetDlgItem( hwnd, IDC_E_GUID ), (BOOL)wParam );
  275. EnableWindow( GetDlgItem( hwnd, IDC_E_SERVER ), (BOOL)wParam );
  276. hr = S_OK;
  277. break;
  278. // Fill out the parameter structure to return to the caller, this is
  279. // handler specific. In our case we constructure a query of the CN
  280. // and objectClass properties, and we show a columns displaying both
  281. // of these. For further information about the DSQUERYPARAMs structure
  282. // see dsquery.h
  283. case CQPM_GETPARAMETERS:
  284. TraceMsg( TF_WM, "CQPM_GETPARAMETERS\n" );
  285. hr = pQueryForm->_GetQueryParams( hwnd, (LPDSQUERYPARAMS*)lParam );
  286. break;
  287. // Clear form, therefore set the window text for these two controls
  288. // to zero.
  289. case CQPM_CLEARFORM:
  290. TraceMsg( TF_WM, "CQPM_CLEARFORM\n" );
  291. SetDlgItemText( hwnd, IDC_E_GUID, L"" );
  292. SetDlgItemText( hwnd, IDC_E_SERVER, L"" );
  293. hr = S_OK;
  294. break;
  295. case CQPM_SETDEFAULTPARAMETERS:
  296. TraceMsg( TF_WM, "CQPM_SETDEFAULTPARAMETERS: wParam = %s lParam = 0x%p\n", BOOLTOSTRING(wParam), lParam );
  297. if ( wParam && lParam )
  298. {
  299. VARIANT var;
  300. LPOPENQUERYWINDOW poqwi = (LPOPENQUERYWINDOW) lParam;
  301. IPropertyBag * ppb = poqwi->ppbFormParameters;
  302. Assert( ppb );
  303. VariantInit( &var );
  304. hr = ppb->Read( L"ServerName", &var, NULL );
  305. if (SUCCEEDED( hr ))
  306. {
  307. SetDlgItemText( hwnd, IDC_E_SERVER, V_BSTR( &var ) );
  308. VariantClear( &var );
  309. }
  310. hr = ppb->Read( L"ClientGuid", &var, NULL );
  311. if (SUCCEEDED( hr ))
  312. {
  313. SetDlgItemText( hwnd, IDC_E_GUID, V_BSTR( &var ) );
  314. VariantClear( &var );
  315. }
  316. }
  317. else
  318. {
  319. DebugMsg( "No default parameters given.\n" );
  320. hr = S_OK;
  321. }
  322. break;
  323. default:
  324. TraceMsg( TF_WM, "CQPM_message 0x%08x *** NOT IMPL ***\n", uMsg );
  325. hr = E_NOTIMPL;
  326. break;
  327. }
  328. RETURN(hr);
  329. }
  330. //
  331. // _OnPSPCB_Create( )
  332. //
  333. HRESULT
  334. THISCLASS::_OnPSPCB_Create( )
  335. {
  336. TraceClsFunc( "_OnPSPCB_Create( )\n" );
  337. HRETURN(S_OK);
  338. }
  339. //
  340. // _InitDialog( )
  341. //
  342. HRESULT
  343. THISCLASS::_InitDialog(
  344. HWND hDlg,
  345. LPARAM lParam )
  346. {
  347. TraceClsFunc( "_InitDialog( )\n" );
  348. _hDlg = hDlg;
  349. Edit_LimitText( GetDlgItem( _hDlg, IDC_E_GUID ), MAX_INPUT_GUID_STRING - 1 );
  350. Edit_LimitText( GetDlgItem( _hDlg, IDC_E_SERVER), DNS_MAX_NAME_LENGTH );
  351. HRETURN(S_OK);
  352. }
  353. struct
  354. {
  355. INT fmt;
  356. INT cx;
  357. INT uID;
  358. PCWSTR pDisplayProperty;
  359. }
  360. columns[] =
  361. {
  362. 0, 20, IDS_NAME, L"cn",
  363. 0, 50, IDS_GUID, L"netbootGuid",
  364. };
  365. //
  366. // _GetQueryParams( )
  367. //
  368. HRESULT
  369. THISCLASS::_GetQueryParams(
  370. HWND hWnd,
  371. LPDSQUERYPARAMS* ppdsqp )
  372. {
  373. TraceClsFunc( "_GetQueryParams( )\n" );
  374. if ( !ppdsqp )
  375. HRETURN(E_POINTER);
  376. HRESULT hr = S_OK;
  377. INT i;
  378. WCHAR szServer[DNS_MAX_NAME_BUFFER_LENGTH];
  379. WCHAR szGuid[MAX_INPUT_GUID_STRING * 2]; // room for escaped GUID
  380. WCHAR szFilter[ARRAYSIZE(szGuid)+ARRAYSIZE(szServer)+ARRAYSIZE(FILTER_QUERY_BOTH)];
  381. GUID Guid;
  382. DWORD uGuidLength;
  383. ULONG offset;
  384. BOOL fIncludeGuid = FALSE, fIncludeServer = FALSE;
  385. BOOL CallerSpecifiedQuery = FALSE;
  386. BOOL CallerQueryStartsWithAmpersand = FALSE;
  387. ULONG cbStruct = 0;
  388. LPDSQUERYPARAMS pDsQueryParams = NULL;
  389. #if 0
  390. if ( *ppdsqp )
  391. {
  392. // This page doesn't support appending its query data to an
  393. // existing DSQUERYPARAMS strucuture, only creating a new block,
  394. // therefore bail if we see the pointer is not NULL.
  395. hr = THR(E_INVALIDARG);
  396. goto Error;
  397. }
  398. #endif
  399. //
  400. // This is how searches are done:
  401. // - if both guid and server are left blank, search for all
  402. // accounts with netbootGuid specified (all managed computers).
  403. // - if either or both are specified, search for all computers
  404. // that match.
  405. // - if guid is specified as '*', treat it the same as if it had
  406. // been left blank (searches for all accounts with netbootGUID).
  407. //
  408. // Note that currently any account with a server in netbootMachineFilePath
  409. // will also have netbootGUID specified, because prestaging a
  410. // remote install computer always puts netbootGUID, and that is
  411. // the only way that netbootMachineFilePath will be set. So if the
  412. // user specifies a server but no guid, we don't need to include
  413. // netbootGUID=* in our ldap filter.
  414. //
  415. // Compute the size of the argument block
  416. if ( GetDlgItemText( hWnd, IDC_E_GUID, szGuid, ARRAYSIZE(szGuid)) )
  417. {
  418. //
  419. // Allow only a * as the GUID, to search for all machines with
  420. // a GUID.
  421. //
  422. if (wcscmp(szGuid, L"*") != 0)
  423. {
  424. if ( ValidateGuid(szGuid,&Guid,&uGuidLength) == E_FAIL || !uGuidLength ) {
  425. MessageBoxFromStrings( hWnd,
  426. IDS_INVALID_GUID_CAPTION,
  427. IDS_INVALID_PARTIAL_GUID_TEXT,
  428. MB_OK );
  429. hr = E_INVALIDARG;
  430. goto Error;
  431. }
  432. ZeroMemory( szGuid, sizeof(szGuid) );
  433. ldap_escape_filter_element((PCHAR)&Guid, uGuidLength, szGuid, sizeof(szGuid) );
  434. }
  435. else
  436. {
  437. szGuid[0] = L'\0'; // the filter adds a *, and "**" doesn't work, so blank it here
  438. }
  439. fIncludeGuid = TRUE;
  440. }
  441. if ( GetDlgItemText( hWnd, IDC_E_SERVER, szServer, ARRAYSIZE(szServer)) )
  442. {
  443. fIncludeServer = TRUE;
  444. }
  445. else
  446. {
  447. //
  448. // If no server or guid was specified, search for any guid.
  449. //
  450. if (!fIncludeGuid)
  451. {
  452. szGuid[0] = L'\0';
  453. fIncludeGuid = TRUE;
  454. }
  455. }
  456. if ( fIncludeGuid && fIncludeServer )
  457. {
  458. wsprintf( szFilter, FILTER_QUERY_BOTH, szGuid, szServer );
  459. }
  460. else if ( fIncludeGuid )
  461. {
  462. wsprintf( szFilter, FILTER_QUERY_GUID_ONLY, szGuid );
  463. }
  464. else if ( fIncludeServer )
  465. {
  466. wsprintf( szFilter, FILTER_QUERY_SERVER_ONLY, szServer );
  467. }
  468. #ifdef DEBUG
  469. else
  470. {
  471. AssertMsg( 0, "How did we get here?\n" );
  472. szFilter[0] = L'\0';
  473. }
  474. #endif // DEBUG
  475. DebugMsg( "RI Filter: %s\n", szFilter );
  476. // compute the size of the new query block
  477. if ( !*ppdsqp )
  478. {
  479. CallerSpecifiedQuery = FALSE;
  480. // bugbug arraysize(columns)-1?
  481. offset = cbStruct = sizeof(DSQUERYPARAMS) + ((ARRAYSIZE(columns)-1)*sizeof(DSCOLUMN));
  482. cbStruct += StringByteSize(szFilter);
  483. for ( i = 0; i < ARRAYSIZE(columns); i++ )
  484. {
  485. cbStruct += StringByteSize(columns[i].pDisplayProperty);
  486. }
  487. }
  488. else
  489. {
  490. CallerSpecifiedQuery = TRUE;
  491. LPWSTR pszQuery = (LPWSTR) ((LPBYTE)(*ppdsqp) + (*ppdsqp)->offsetQuery);
  492. offset = (*ppdsqp)->cbStruct;
  493. //
  494. // save off the size that we're gonna need.
  495. // note that when we concatenate the current query with our query,
  496. // we need to make sure the query starts with "(&". If it doesn't
  497. // already, then we make this the case -- the buffer szFilter contains
  498. // these bytes, which ensures that cbStruct is large enough. If the current
  499. // query contains these strings, then the allocated buffer is a little
  500. // bigger than it needs to be.
  501. cbStruct = (*ppdsqp)->cbStruct + StringByteSize( pszQuery ) + StringByteSize( szFilter );
  502. //
  503. // do some extra query validation.
  504. // does the query start with "(&"?
  505. //
  506. if (pszQuery[0] == L'(' && pszQuery[1] == L'&' ) {
  507. CallerQueryStartsWithAmpersand = TRUE;
  508. //
  509. // we're assuming below that if the specified query
  510. // doesn't start with "(&", that it will end with ")".
  511. // If that's not the case then bail out.
  512. //
  513. Assert( pszQuery[ wcslen( pszQuery ) - 1 ] == L')' );
  514. if (pszQuery[ wcslen( pszQuery ) - 1 ] != L')' ) {
  515. hr = E_INVALIDARG;
  516. goto Error;
  517. }
  518. } else {
  519. //
  520. // conversely, if the query doesn't start with '(&', then
  521. // we assume that the query doesn't end with ')', and if it
  522. // does, we bail
  523. //
  524. CallerQueryStartsWithAmpersand = FALSE;
  525. Assert( pszQuery[ wcslen( pszQuery ) - 1 ] != L')' );
  526. if (pszQuery[ wcslen( pszQuery ) - 1 ] == L')' ) {
  527. hr = E_INVALIDARG;
  528. goto Error;
  529. }
  530. }
  531. }
  532. // Allocate it and populate it with the data, the header is fixed
  533. // but the strings are referenced by offset.
  534. pDsQueryParams = (LPDSQUERYPARAMS)CoTaskMemAlloc(cbStruct);
  535. if ( !pDsQueryParams )
  536. {
  537. hr = E_OUTOFMEMORY;
  538. goto Error;
  539. }
  540. // Did they hand in a query that we need to modify?
  541. if ( !CallerSpecifiedQuery)
  542. { // no... create our own query
  543. pDsQueryParams->cbStruct = cbStruct;
  544. pDsQueryParams->dwFlags = 0;
  545. pDsQueryParams->hInstance = g_hInstance;
  546. pDsQueryParams->offsetQuery = offset;
  547. pDsQueryParams->iColumns = ARRAYSIZE(columns);
  548. // Copy the filter string and bump the offset
  549. StringByteCopy(pDsQueryParams, offset, szFilter);
  550. offset += StringByteSize(szFilter);
  551. // Fill in the array of columns to dispaly, the cx is a percentage of the
  552. // current view, the propertie names to display are UNICODE strings and
  553. // are referenced by offset, therefore we bump the offset as we copy
  554. // each one.
  555. for ( i = 0 ; i < ARRAYSIZE(columns); i++ )
  556. {
  557. pDsQueryParams->aColumns[i].fmt = columns[i].fmt;
  558. pDsQueryParams->aColumns[i].cx = columns[i].cx;
  559. pDsQueryParams->aColumns[i].idsName = columns[i].uID;
  560. pDsQueryParams->aColumns[i].offsetProperty = offset;
  561. StringByteCopy(pDsQueryParams, offset, columns[i].pDisplayProperty);
  562. offset += StringByteSize(columns[i].pDisplayProperty);
  563. }
  564. } else {
  565. // yes, the caller specifed a query... add our parameters to the query
  566. LPWSTR pszQuery;
  567. LPWSTR pszNewQuery;
  568. INT n;
  569. // duplicate the existing query
  570. Assert( offset == (*ppdsqp)->cbStruct );
  571. CopyMemory( pDsQueryParams, *ppdsqp, offset );
  572. pDsQueryParams->cbStruct = cbStruct;
  573. // new query location
  574. pDsQueryParams->offsetQuery = offset;
  575. pszQuery = (LPWSTR) ((LPBYTE)(*ppdsqp) + (*ppdsqp)->offsetQuery);
  576. pszNewQuery = (LPWSTR) ((LPBYTE)pDsQueryParams + offset);
  577. Assert( pszQuery );
  578. // append to their query
  579. // append to their query
  580. if ( CallerQueryStartsWithAmpersand ) {
  581. //
  582. // remove ")" from current query so that we can
  583. // add in our additional filter. We've got the trailing ")" in our
  584. // filter.
  585. // Also note that we can't really fail at this point, and we're
  586. // about to free the caller's memory so it's ok for us to overwrite their
  587. // query text.
  588. //
  589. Assert( pszQuery[ wcslen( pszQuery ) - 1 ] == L')' );
  590. pszQuery[ wcslen( pszQuery ) - 1 ] = L'\0'; // remove ")"
  591. // start with their query
  592. wcscpy(pszNewQuery, pszQuery);
  593. //
  594. // put it back to be a good citizen ")"
  595. //
  596. pszQuery[ wcslen( pszQuery )] = L')';
  597. //
  598. // now tack on our query, skipping the "(&" part
  599. //
  600. wcscat(pszNewQuery,&szFilter[2]);
  601. } else {
  602. Assert( pszQuery[ wcslen( pszQuery ) - 1 ] != L')' );
  603. wcscpy( pszNewQuery, L"(&" ); // add "(&" to begining of query
  604. wcscat( pszNewQuery, pszQuery ); // add their query
  605. wcscat( pszNewQuery, &szFilter[2] ); // add our query starting after the "(&"
  606. }
  607. offset += StringByteSize( pszNewQuery ); // compute new offset
  608. DebugMsg( "New Query String: %s\n", pszNewQuery );
  609. // Cleanup
  610. CoTaskMemFree( *ppdsqp );
  611. }
  612. // Success
  613. *ppdsqp = pDsQueryParams;
  614. Assert( hr == S_OK );
  615. Cleanup:
  616. HRETURN(hr);
  617. Error:
  618. if ( pDsQueryParams ) {
  619. CoTaskMemFree( pDsQueryParams );
  620. }
  621. // If we aren't modifying the query and there wasn't
  622. // a query handed into us, indicate failure instead.
  623. if ( hr == S_FALSE && !*ppdsqp ) {
  624. Assert(FALSE); // how did we get here?
  625. hr = E_FAIL;
  626. }
  627. goto Cleanup;
  628. }