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.

764 lines
20 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997 - 2000.
  5. //
  6. // File: ixsutil.cxx
  7. //
  8. // Contents: Utility SSO class
  9. //
  10. // History: 04 Apr 1997 Alanw Created
  11. //
  12. //----------------------------------------------------------------------------
  13. #include "pch.cxx"
  14. #pragma hdrstop
  15. //-----------------------------------------------------------------------------
  16. // Include Files
  17. //-----------------------------------------------------------------------------
  18. // debugging macros
  19. #include "ssodebug.hxx"
  20. // class declaration
  21. #include "stdcf.hxx"
  22. #include "ixsso.hxx"
  23. #include "ixsutil.hxx"
  24. #include <string.hxx>
  25. #include <htmlchar.hxx>
  26. extern WCHAR * g_pwszProgIdUtil;
  27. #if CIDBG
  28. extern ULONG g_ulObjCount;
  29. extern LONG g_lUtlCount;
  30. #endif // CIDBG
  31. //-----------------------------------------------------------------------------
  32. //
  33. // Member: CixssoUtil::CixssoUtil - public
  34. //
  35. // Synopsis: Constructor of CixssoUtil
  36. //
  37. // Arguments: [pitlb] - pointer to ITypeLib for ixsso
  38. //
  39. // History: 04 Apr 1997 Alanw Created
  40. //
  41. //-----------------------------------------------------------------------------
  42. CixssoUtil::CixssoUtil( ITypeLib * pitlb ) :
  43. _ptinfo( 0 ),
  44. _err( IID_IixssoUtil )
  45. {
  46. _cRef = 1;
  47. SCODE sc = pitlb->GetTypeInfoOfGuid( IID_IixssoUtil, &_ptinfo );
  48. if (FAILED(sc))
  49. {
  50. ixssoDebugOut(( DEB_ERROR, "Util - GetTypeInfoOfGuid failed (%x)\n", sc ));
  51. Win4Assert(SUCCEEDED(sc));
  52. THROW( CException(sc) );
  53. }
  54. INC_OBJECT_COUNT();
  55. ixssoDebugOut((DEB_REFCOUNTS, "[DLL]: Create util: refcounts: glob %d util %d\n",
  56. g_ulObjCount,
  57. g_lUtlCount ));
  58. } //CixssoUtil
  59. //-----------------------------------------------------------------------------
  60. //
  61. // Member: CixssoUtil::~CixssoUtil - public
  62. //
  63. // Synopsis: Destructor of CixssoUtil
  64. //
  65. // History: 04 Apr 1997 Alanw Created
  66. //
  67. //-----------------------------------------------------------------------------
  68. CixssoUtil::~CixssoUtil( )
  69. {
  70. if (_ptinfo)
  71. _ptinfo->Release();
  72. DEC_OBJECT_COUNT();
  73. #if CIDBG
  74. extern LONG g_lUtlCount;
  75. LONG l = InterlockedDecrement( &g_lUtlCount );
  76. Win4Assert( l >= 0 );
  77. #endif //CIDBG
  78. ixssoDebugOut((DEB_REFCOUNTS, "[DLL]: Delete util: refcounts: glob %d util %d\n",
  79. g_ulObjCount,
  80. g_lUtlCount ));
  81. } //~CixssoUtl
  82. #if 0 // NOTE: OnStartPage and OnEndPage are unneeded
  83. //
  84. // ASP Methods
  85. //
  86. #include <asp/asptlb.h>
  87. STDMETHODIMP CixssoUtil::OnStartPage (IUnknown* pUnk)
  88. {
  89. // reset the error structure
  90. _err.Reset();
  91. SCODE sc;
  92. IScriptingContext *piContext;
  93. //Get IScriptingContext Interface
  94. sc = pUnk->QueryInterface(IID_IScriptingContext, (void**)&piContext);
  95. if (FAILED(sc))
  96. return E_FAIL;
  97. //Get Request Object Pointer
  98. IRequest* piRequest = NULL;
  99. sc = piContext->get_Request(&piRequest);
  100. //Get ServerVariables Pointer
  101. IRequestDictionary *piRequestDict = NULL;
  102. sc = piRequest->get_ServerVariables(&piRequestDict);
  103. VARIANT vtOut;
  104. VariantInit(&vtOut);
  105. //Get the HTTP_ACCEPT_LANGUAGE Item
  106. sc = piRequestDict->get_Item(g_vtAcceptLanguageHeader, &vtOut);
  107. //vtOut Contains an IDispatch Pointer. To fetch the value
  108. //for HTTP_ACCEPT_LANGUAGE you must get the Default Value for the
  109. //Object stored in vtOut using VariantChangeType.
  110. if (V_VT(&vtOut) != VT_BSTR)
  111. VariantChangeType(&vtOut, &vtOut, 0, VT_BSTR);
  112. if (V_VT(&vtOut) == VT_BSTR)
  113. {
  114. ixssoDebugOut((DEB_TRACE, "OnStartPage: HTTP_ACCEPT_LANGUAGE = %ws\n",
  115. V_BSTR(&vtOut) ));
  116. SetLocaleString(V_BSTR(&vtOut));
  117. }
  118. else
  119. {
  120. ixssoDebugOut(( DEB_TRACE,
  121. "OnStart: HTTP_ACCEPT_LANGAUGE was not set is ServerVariables; using lcid=0x%x\n",
  122. GetSystemDefaultLCID() ));
  123. put_LocaleID( GetSystemDefaultLCID() );
  124. }
  125. VariantClear(&vtOut);
  126. piRequestDict->Release();
  127. piRequest->Release();
  128. piContext->Release();
  129. return S_OK;
  130. }
  131. HRESULT CixssoUtil::OnEndPage(void)
  132. {
  133. return S_OK;
  134. }
  135. #endif // 0 NOTE: OnStartPage and OnEndPage are unneeded
  136. //-----------------------------------------------------------------------------
  137. // CixssoUtil IUnknown Methods
  138. //-----------------------------------------------------------------------------
  139. STDMETHODIMP
  140. CixssoUtil::QueryInterface(REFIID iid, void * * ppv)
  141. {
  142. *ppv = 0;
  143. if (iid == IID_IUnknown || iid == IID_IDispatch)
  144. *ppv = (IDispatch *)this;
  145. else if (iid == IID_ISupportErrorInfo )
  146. *ppv = (ISupportErrorInfo *) this;
  147. else if (iid == IID_IixssoUtil )
  148. *ppv = (IixssoUtil *) this;
  149. else
  150. return E_NOINTERFACE;
  151. AddRef();
  152. return S_OK;
  153. } //QueryInterface
  154. STDMETHODIMP_(ULONG)
  155. CixssoUtil::AddRef(void)
  156. {
  157. return InterlockedIncrement((long *)&_cRef);
  158. }
  159. STDMETHODIMP_(ULONG)
  160. CixssoUtil::Release(void)
  161. {
  162. ULONG uTmp = InterlockedDecrement((long *)&_cRef);
  163. if (uTmp == 0)
  164. {
  165. delete this;
  166. return 0;
  167. }
  168. return uTmp;
  169. }
  170. //-----------------------------------------------------------------------------
  171. // CixssoUtil IDispatch Methods
  172. //-----------------------------------------------------------------------------
  173. STDMETHODIMP
  174. CixssoUtil::GetTypeInfoCount(UINT * pctinfo)
  175. {
  176. *pctinfo = 1;
  177. return S_OK;
  178. }
  179. STDMETHODIMP
  180. CixssoUtil::GetTypeInfo(
  181. UINT itinfo,
  182. LCID lcid,
  183. ITypeInfo * * pptinfo)
  184. {
  185. _ptinfo->AddRef();
  186. *pptinfo = _ptinfo;
  187. return S_OK;
  188. }
  189. STDMETHODIMP
  190. CixssoUtil::GetIDsOfNames(
  191. REFIID riid,
  192. OLECHAR * * rgszNames,
  193. UINT cNames,
  194. LCID lcid,
  195. DISPID * rgdispid)
  196. {
  197. return DispGetIDsOfNames(_ptinfo, rgszNames, cNames, rgdispid);
  198. }
  199. STDMETHODIMP
  200. CixssoUtil::Invoke(
  201. DISPID dispidMember,
  202. REFIID riid,
  203. LCID lcid,
  204. WORD wFlags,
  205. DISPPARAMS * pParams,
  206. VARIANT * pvarResult,
  207. EXCEPINFO * pexcepinfo,
  208. UINT * puArgErr)
  209. {
  210. ixssoDebugOut((DEB_IDISPATCH, "Util - Invoking method dispid=%d wFlags=%d\n",
  211. dispidMember, wFlags ));
  212. _err.Reset();
  213. SCODE sc = DispInvoke( this, _ptinfo,
  214. dispidMember, wFlags, pParams,
  215. pvarResult, pexcepinfo, puArgErr );
  216. if ( _err.IsError() )
  217. sc = DISP_E_EXCEPTION;
  218. return sc;
  219. }
  220. STDMETHODIMP
  221. CixssoUtil::InterfaceSupportsErrorInfo(
  222. REFIID riid)
  223. {
  224. if (riid == IID_IixssoUtil)
  225. return S_OK;
  226. else
  227. return S_FALSE;
  228. }
  229. //+---------------------------------------------------------------------------
  230. //
  231. // Member: CixssoQuery::CopyWstrToBstr - private inline
  232. //
  233. // Synopsis: Copies a Unicode string to a BSTR
  234. //
  235. // Arguments: [pbstr] - destination BSTR
  236. // [pwstr] - string to be copied
  237. //
  238. // Returns: SCODE - status return
  239. //
  240. // History: 25 Oct 1996 Alanw Created
  241. //
  242. //----------------------------------------------------------------------------
  243. inline
  244. SCODE CixssoUtil::CopyWstrToBstr( BSTR * pbstr, WCHAR const * pwstr )
  245. {
  246. *pbstr = 0;
  247. if (pwstr)
  248. {
  249. *pbstr = SysAllocString( pwstr );
  250. if (0 == *pbstr)
  251. return E_OUTOFMEMORY;
  252. }
  253. return S_OK;
  254. }
  255. //-----------------------------------------------------------------------------
  256. // CixssoUtil Methods
  257. //-----------------------------------------------------------------------------
  258. //+---------------------------------------------------------------------------
  259. //
  260. // Member: CixssoUtil::ISOToLocaleID - public
  261. //
  262. // Synopsis: Parse the input string for a recognizable locale name
  263. //
  264. // Arguments: [bstrLocale] - input string
  265. // [pLcid] - pointer where corresponding LCID is returned
  266. //
  267. // Returns: SCODE - status return
  268. //
  269. // History: 04 Apr 1997 Alanw Created
  270. //
  271. //----------------------------------------------------------------------------
  272. STDMETHODIMP
  273. CixssoUtil::ISOToLocaleID(BSTR bstrLocale, LONG *pLcid)
  274. {
  275. _err.Reset();
  276. if ( 0 == pLcid )
  277. return E_INVALIDARG;
  278. *pLcid = GetLCIDFromString( bstrLocale );
  279. return S_OK;
  280. }
  281. //+---------------------------------------------------------------------------
  282. //
  283. // Member: CixssoUtil::LocaleIDToISO - public
  284. //
  285. // Synopsis: Return the ISO locale name for an LCID
  286. //
  287. // Arguments: [Lcid] - input LCID
  288. // [pstr] - pointer where output string is returned
  289. //
  290. // Returns: SCODE - status return
  291. //
  292. // History: 04 Apr 1997 Alanw Created
  293. //
  294. //----------------------------------------------------------------------------
  295. STDMETHODIMP
  296. CixssoUtil::LocaleIDToISO(LONG lcid, BSTR * pstr)
  297. {
  298. _err.Reset();
  299. if ( 0 == pstr )
  300. return E_INVALIDARG;
  301. WCHAR awc[100];
  302. GetStringFromLCID( lcid, awc );
  303. return CopyWstrToBstr( pstr, awc );
  304. }
  305. //+---------------------------------------------------------------------------
  306. //
  307. // Member: CixssoUtil::AddScopeToQuery - public
  308. //
  309. // Synopsis: Parse the input string for a recognizable locale name
  310. //
  311. // Arguments: [pDisp] - an IDispatch for the query object
  312. // [bstrScope] - input scope
  313. // [bstrDepth] - input depth (optional)
  314. //
  315. // Returns: SCODE - status return
  316. //
  317. // Notes: In the future, this will operate by modifying the query
  318. // property to include a scope restriction.
  319. // For now, it just adds the scope and depth via a private
  320. // interface.
  321. //
  322. // History: 04 Apr 1997 Alanw Created
  323. //
  324. //----------------------------------------------------------------------------
  325. STDMETHODIMP
  326. CixssoUtil::AddScopeToQuery( IDispatch * pDisp,
  327. BSTR bstrScope,
  328. BSTR bstrDepth)
  329. {
  330. _err.Reset();
  331. SCODE sc = S_OK;
  332. CTranslateSystemExceptions translate;
  333. TRY
  334. {
  335. if ( 0 == pDisp )
  336. THROW( CException( E_INVALIDARG ) );
  337. IixssoQueryPrivate * pIQueryPvt = 0;
  338. sc = pDisp->QueryInterface( IID_IixssoQueryPrivate, (void **)&pIQueryPvt );
  339. if (FAILED(sc))
  340. {
  341. THROW(CException(sc));
  342. }
  343. XInterface<IixssoQueryPrivate> pQry(pIQueryPvt);
  344. pQry->AddScopeToQuery( bstrScope, bstrDepth );
  345. }
  346. CATCH( CIxssoException, e )
  347. {
  348. sc = e.GetErrorCode();
  349. SetError( sc, OLESTR("AddScopeToQuery"), eIxssoError );
  350. }
  351. AND_CATCH( CException, e )
  352. {
  353. sc = e.GetErrorCode();
  354. SetError( sc, OLESTR("AddScopeToQuery") );
  355. }
  356. END_CATCH
  357. return sc;
  358. } //AddScopeToQuery
  359. //+---------------------------------------------------------------------------
  360. //
  361. // Member: CixssoUtil::TruncateToWhitespace - public
  362. //
  363. // Synopsis: Truncate a string, preferably at a white space character.
  364. //
  365. // Arguments: [bstrIn] - input string
  366. // [maxLen] - maximum number of characters in output string
  367. // [pbstrOut] - pointer where output string is returned
  368. //
  369. // Returns: SCODE - status return
  370. //
  371. // Notes: The implementation does not take into account real word breaks.
  372. // This may not work too well on far eastern languages.
  373. //
  374. // History: 04 Apr 1997 Alanw Created
  375. //
  376. //----------------------------------------------------------------------------
  377. STDMETHODIMP
  378. CixssoUtil::TruncateToWhitespace(BSTR bstrIn, LONG maxLen, BSTR * pbstrOut)
  379. {
  380. _err.Reset();
  381. ULONG cchString = 0;
  382. if (maxLen <= 0)
  383. return E_INVALIDARG;
  384. if (0 != bstrIn)
  385. cchString = SysStringLen(bstrIn);
  386. if (cchString > (unsigned)maxLen)
  387. {
  388. cchString = maxLen;
  389. for (unsigned i=0; i <= (unsigned)maxLen; i++)
  390. {
  391. if (iswspace(bstrIn[i]))
  392. cchString = i;
  393. }
  394. }
  395. *pbstrOut = SysAllocStringLen( bstrIn, cchString );
  396. if (0 == *pbstrOut)
  397. return E_OUTOFMEMORY;
  398. return S_OK;
  399. } //TruncateToWhitespace
  400. class XVariant
  401. {
  402. public:
  403. XVariant() : _pVar( 0 ) {}
  404. XVariant( VARIANT & var ) : _pVar( &var ) {}
  405. ~XVariant() { if ( 0 != _pVar ) VariantClear( _pVar ); }
  406. void Set( VARIANT & var ) { Win4Assert( 0 == _pVar ); _pVar = &var; }
  407. private:
  408. VARIANT * _pVar;
  409. };
  410. //+---------------------------------------------------------------------------
  411. //
  412. // Member: CixssoUtil::GetArrayElement - public
  413. //
  414. // Synopsis: Returns an element in an array as a variant
  415. //
  416. // Arguments: [pVarIn] - The input array (IDispatch or VT_ARRAY)
  417. // [iElement] - The element to retrieve
  418. // [pVarOut] - Where the array element result is written
  419. //
  420. // Returns: SCODE - status return
  421. //
  422. // History: 10 Sep 1997 dlee Created
  423. // 18 Jan 2000 KLam DECIMAL needs to fit into a VARIANT
  424. // on Win64 VARIANT is bigger than DECIMAL
  425. //
  426. //----------------------------------------------------------------------------
  427. STDMETHODIMP CixssoUtil::GetArrayElement(
  428. VARIANT * pVarIn,
  429. LONG iElement,
  430. VARIANT * pVarOut )
  431. {
  432. _err.Reset();
  433. //
  434. // Validate the variant arguments.
  435. //
  436. if ( ( 0 == pVarIn ) || ( 0 == pVarOut ) )
  437. return SetError( E_INVALIDARG, OLESTR( "GetArrayElement" ) );
  438. //
  439. // Get the source array, either from the IDispatch or just copy it.
  440. //
  441. XVariant xvar;
  442. VARIANT varArray;
  443. VariantInit( &varArray );
  444. if ( VT_DISPATCH == pVarIn->vt )
  445. {
  446. //
  447. // The first argument is an IDispatch, not the array value, so we
  448. // have to invoke it to get the value out.
  449. //
  450. if ( 0 == pVarIn->pdispVal )
  451. return SetError( E_INVALIDARG, OLESTR( "GetArrayElement" ) );
  452. DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
  453. SCODE sc = pVarIn->pdispVal->Invoke( DISPID_VALUE,
  454. IID_NULL,
  455. GetSystemDefaultLCID(),
  456. DISPATCH_PROPERTYGET,
  457. &dispparamsNoArgs,
  458. &varArray,
  459. 0,
  460. 0 );
  461. ixssoDebugOut(( DEB_ITRACE, "result of invoke: 0x%x\n", sc ));
  462. if ( FAILED( sc ) )
  463. return SetError( sc, OLESTR( "GetArrayElement" ) );
  464. xvar.Set( varArray );
  465. }
  466. else
  467. {
  468. varArray = *pVarIn;
  469. }
  470. ixssoDebugOut(( DEB_ITRACE, "value vt: 0x%x\n", varArray.vt ));
  471. //
  472. // Check for a valid variant array argument.
  473. //
  474. if ( ( 0 == ( VT_ARRAY & varArray.vt ) ) ||
  475. ( 0 == varArray.parray ) )
  476. return SetError( E_INVALIDARG, OLESTR( "GetArrayElement" ) );
  477. SAFEARRAY *psa = varArray.parray;
  478. //
  479. // This function only deals with 1-dimensional safearrays.
  480. //
  481. if ( 1 != SafeArrayGetDim( psa ) )
  482. return SetError( E_INVALIDARG, OLESTR( "GetArrayElement" ) );
  483. //
  484. // Make sure iElement is in the bounds of the array.
  485. //
  486. long lLowBound;
  487. SCODE sc = SafeArrayGetLBound( psa, 1, &lLowBound );
  488. if ( FAILED( sc ) )
  489. return SetError( sc, OLESTR( "GetArrayElement" ) );
  490. long lUpBound;
  491. sc = SafeArrayGetUBound( psa, 1, &lUpBound );
  492. if ( FAILED( sc ) )
  493. return SetError( sc, OLESTR( "GetArrayElement" ) );
  494. if ( ( iElement < lLowBound ) || ( iElement > lUpBound ) )
  495. return SetError( E_INVALIDARG, OLESTR( "GetArrayElement" ) );
  496. //
  497. // Get a pointer to the element.
  498. //
  499. void * pvData;
  500. sc = SafeArrayPtrOfIndex( psa, &iElement, &pvData );
  501. if ( FAILED( sc ) )
  502. return SetError( sc, OLESTR( "GetArrayElement" ) );
  503. //
  504. // Put the element in a local variant so it can be copied.
  505. //
  506. VARIANT var;
  507. VariantInit( &var );
  508. var.vt = varArray.vt & (~VT_ARRAY);
  509. unsigned cbElem = SafeArrayGetElemsize( psa );
  510. if ( VT_VARIANT == var.vt )
  511. {
  512. Win4Assert( sizeof( VARIANT ) == cbElem );
  513. RtlCopyMemory( &var, pvData, cbElem );
  514. }
  515. else if ( VT_DECIMAL == var.vt )
  516. {
  517. Win4Assert( sizeof( VARIANT ) >= cbElem &&
  518. sizeof( DECIMAL ) == cbElem );
  519. RtlCopyMemory( &var, pvData, cbElem );
  520. var.vt = VT_DECIMAL;
  521. }
  522. else
  523. {
  524. Win4Assert( cbElem <= 8 );
  525. RtlCopyMemory( &var.lVal, pvData, cbElem );
  526. }
  527. //
  528. // Make a copy of the value into another local variant.
  529. //
  530. VARIANT varCopy;
  531. VariantInit( &varCopy );
  532. sc = VariantCopy( &varCopy, &var );
  533. if ( FAILED( sc ) )
  534. return SetError( sc, OLESTR( "GetArrayElement" ) );
  535. //
  536. // Free anything still allocated in the output variant, and transfer
  537. // the value to the output variant.
  538. //
  539. VariantClear( pVarOut );
  540. *pVarOut = varCopy;
  541. return S_OK;
  542. } //GetArrayElement
  543. //+---------------------------------------------------------------------------
  544. //
  545. // Member: CixssoUtil::HTMLEncode - public
  546. //
  547. // Synopsis: Encode a string for use in HTML. Take the output code page
  548. // into account so that unicode characters not representable in
  549. // the code page are output as HTML numeric entities.
  550. //
  551. // Arguments: [bstrIn] - input string
  552. // [codepage] - code page for output string
  553. // [pbstrOut] - pointer where output string is returned
  554. //
  555. // Returns: SCODE - status return
  556. //
  557. // History: 04 Apr 1997 Alanw Created
  558. //
  559. //----------------------------------------------------------------------------
  560. STDMETHODIMP
  561. CixssoUtil::HTMLEncode(BSTR bstrIn, LONG codepage, BSTR * pbstrOut)
  562. {
  563. _err.Reset();
  564. SCODE sc = S_OK;
  565. CTranslateSystemExceptions translate;
  566. TRY
  567. {
  568. if ( ( codepage < 0 ) || ( 0 == pbstrOut ) )
  569. THROW( CException( E_INVALIDARG ) );
  570. CVirtualString vString( 512 );
  571. if ( 0 != bstrIn )
  572. HTMLEscapeW( bstrIn, vString, codepage );
  573. BSTR bstr = SysAllocStringLen( vString.GetPointer(), vString.StrLen() );
  574. if ( 0 == bstr )
  575. THROW( CException( E_OUTOFMEMORY ) );
  576. *pbstrOut = bstr;
  577. }
  578. CATCH( CException, e )
  579. {
  580. sc = e.GetErrorCode();
  581. SetError( sc, OLESTR("HTMLEncode"), eIxssoError );
  582. }
  583. END_CATCH;
  584. return sc;
  585. } //HTMLEncode
  586. //+---------------------------------------------------------------------------
  587. //
  588. // Member: CixssoUtil::URLEncode - public
  589. //
  590. // Synopsis: Encode a string for use in a URL. Take the output code page
  591. // into account so that unicode characters not representable in
  592. // the code page are output as %uxxxx escapes.
  593. //
  594. // Arguments: [bstrIn] - input string
  595. // [codepage] - code page for output string
  596. // [pbstrOut] - pointer where output string is returned
  597. //
  598. // Returns: SCODE - status return
  599. //
  600. // History: 04 Apr 1997 Alanw Created
  601. //
  602. //----------------------------------------------------------------------------
  603. STDMETHODIMP
  604. CixssoUtil::URLEncode(BSTR bstrIn, LONG codepage, BSTR * pbstrOut)
  605. {
  606. _err.Reset();
  607. SCODE sc = S_OK;
  608. CTranslateSystemExceptions translate;
  609. TRY
  610. {
  611. if ( ( codepage < 0 ) || ( 0 == pbstrOut ) )
  612. THROW( CException( E_INVALIDARG ) );
  613. CVirtualString vString( 512 );
  614. if ( 0 != bstrIn )
  615. URLEscapeW( bstrIn, vString, codepage, FALSE );
  616. BSTR bstr = SysAllocStringLen( vString.GetPointer(), vString.StrLen() );
  617. if ( 0 == bstr )
  618. THROW( CException( E_OUTOFMEMORY ) );
  619. *pbstrOut = bstr;
  620. }
  621. CATCH( CException, e )
  622. {
  623. sc = e.GetErrorCode();
  624. SetError( sc, OLESTR("URLEncode"), eIxssoError );
  625. }
  626. END_CATCH;
  627. return sc;
  628. } //URLEncode