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.

765 lines
21 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 1998, Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // iastlutl.h
  8. //
  9. // SYNOPSIS
  10. //
  11. // Declares various utility classes, functions, and macros that are
  12. // useful when implementating a request handler for the Internet
  13. // Authentication Service.
  14. //
  15. // MODIFICATION HISTORY
  16. //
  17. // 08/10/1998 Original version.
  18. // 10/27/1998 Added IASDictionaryView.
  19. // 01/25/1999 Removed IASDictionaryView.
  20. // 04/14/2000 Added IASDictionary.
  21. //
  22. ///////////////////////////////////////////////////////////////////////////////
  23. #ifndef _IASTLUTL_H_
  24. #define _IASTLUTL_H_
  25. #if _MSC_VER >= 1000
  26. #pragma once
  27. #endif
  28. //////////
  29. // 'C'-style API for manipulating IASATTRIBUTE struct's.
  30. //////////
  31. #include <iasattr.h>
  32. //////////
  33. // 'C'-style API for manipulating dictionary.
  34. //////////
  35. #include <iasapi.h>
  36. //////////
  37. // MIDL generated header files containing interfaces used by request handlers.
  38. //////////
  39. #include <iaspolcy.h>
  40. #include <sdoias.h>
  41. //////////
  42. // The entire library is contained within the IASTL namespace.
  43. //////////
  44. namespace IASTL {
  45. //////////
  46. // This function is called whenever an exception should be thrown. The
  47. // function is declared, but never defined. This allows the user to provide
  48. // their own implementation using an exception class of their choice.
  49. //////////
  50. void __stdcall issue_error(HRESULT hr);
  51. ///////////////////////////////////////////////////////////////////////////////
  52. //
  53. // CLASS
  54. //
  55. // IASAttribute
  56. //
  57. // DESCRIPTION
  58. //
  59. // Wrapper around an IASATTRIBUTE struct.
  60. //
  61. ///////////////////////////////////////////////////////////////////////////////
  62. class IASAttribute
  63. {
  64. public:
  65. //////////
  66. // Constructors.
  67. //////////
  68. IASAttribute() throw ()
  69. : p(NULL)
  70. { }
  71. explicit IASAttribute(bool alloc)
  72. {
  73. if (alloc) { _alloc(); } else { p = NULL; }
  74. }
  75. explicit IASAttribute(PIASATTRIBUTE attr, bool addRef = true) throw ()
  76. : p(attr)
  77. { if (addRef) { _addref(); } }
  78. IASAttribute(const IASAttribute& attr) throw ()
  79. : p(attr.p)
  80. { _addref(); }
  81. //////////
  82. // Destructor.
  83. //////////
  84. ~IASAttribute() throw ()
  85. { _release(); }
  86. //////////
  87. // Assignment operators.
  88. //////////
  89. IASAttribute& operator=(PIASATTRIBUTE attr) throw ();
  90. const IASAttribute& operator=(const IASAttribute& attr) throw ()
  91. { return operator=(attr.p); }
  92. // Allocate a new attribute. Any existing attribute is first released.
  93. void alloc()
  94. {
  95. _release();
  96. _alloc();
  97. }
  98. // Release the attribute (if any).
  99. void release() throw ()
  100. {
  101. if (p) { IASAttributeRelease(p); p = NULL; }
  102. }
  103. // Attach a new attribute to the object. Any existing attribute is first
  104. // released.
  105. void attach(PIASATTRIBUTE attr, bool addRef = true) throw ();
  106. // Detach the attribute from the object. The caller is responsible for
  107. // releasing the returned attribute.
  108. PIASATTRIBUTE detach() throw ()
  109. {
  110. PIASATTRIBUTE rv = p;
  111. p = NULL;
  112. return rv;
  113. }
  114. // Load an attribute with the given ID. Returns true if successful, false
  115. // if no such attribute exists.
  116. bool load(IAttributesRaw* request, DWORD dwId);
  117. // Load an attribute with the given ID and verify that it has an
  118. // appropriate value type. Returns true if successful, false if no such
  119. // attribute exists.
  120. bool load(IAttributesRaw* request, DWORD dwId, IASTYPE itType);
  121. // Store the attribute in a request.
  122. void store(IAttributesRaw* request) const;
  123. // Swap the contents of two objects.
  124. void swap(IASAttribute& attr) throw ()
  125. {
  126. PIASATTRIBUTE tmp = p;
  127. p = attr.p;
  128. attr.p = tmp;
  129. }
  130. //////////
  131. // Methods for setting the value of an attribute. The object must contain
  132. // a valid attribute before calling this method. The passed in data is
  133. // copied.
  134. //////////
  135. void setOctetString(DWORD dwLength, const BYTE* lpValue);
  136. void setOctetString(PCSTR szAnsi);
  137. void setOctetString(PCWSTR szWide);
  138. void setString(DWORD dwLength, const BYTE* lpValue);
  139. void setString(PCSTR szAnsi);
  140. void setString(PCWSTR szWide);
  141. //////////
  142. // Methods for manipulating the dwFlags field.
  143. //////////
  144. void clearFlag(DWORD flag) throw ()
  145. { p->dwFlags &= ~flag; }
  146. void setFlag(DWORD flag) throw ()
  147. { p->dwFlags |= flag; }
  148. bool testFlag(DWORD flag) const throw ()
  149. { return (p->dwFlags & flag) != 0; }
  150. // Address-of operator. Any existing attribute is first released.
  151. PIASATTRIBUTE* operator&() throw ()
  152. {
  153. release();
  154. return &p;
  155. }
  156. //////////
  157. // Assorted useful operators that allow an IASAttribute object to mimic
  158. // an IASATTRIBUTE pointer.
  159. //////////
  160. bool operator !() const throw () { return p == NULL; }
  161. operator bool() const throw () { return p != NULL; }
  162. operator PIASATTRIBUTE() const throw () { return p; }
  163. IASATTRIBUTE& operator*() const throw () { return *p; }
  164. PIASATTRIBUTE operator->() const throw () { return p; }
  165. protected:
  166. void _addref() throw ()
  167. { if (p) { IASAttributeAddRef(p); } }
  168. void _release() throw ()
  169. { if (p) { IASAttributeRelease(p); } }
  170. void _alloc()
  171. { if (IASAttributeAlloc(1, &p)) { issue_error(E_OUTOFMEMORY); } }
  172. void clearValue() throw ();
  173. PIASATTRIBUTE p; // The attribute being wrapped.
  174. };
  175. ///////////////////////////////////////////////////////////////////////////////
  176. //
  177. // CLASS
  178. //
  179. // IASAttributeVector
  180. //
  181. // DESCRIPTION
  182. //
  183. // Implements an STL-style vector of ATTRIBUTEPOSITION structs. The user
  184. // may provide an empty C-style array that will be used for initial storage.
  185. // This array will not be freed by the IASAttributeVector object and must
  186. // remain valid for the lifetime of the object. The purpose of this feature
  187. // is to allow an initial stack-based allocation that will meet most
  188. // conditions while still allowing a dynamically-sized heap-based array
  189. // when necessary.
  190. //
  191. ///////////////////////////////////////////////////////////////////////////////
  192. class IASAttributeVector
  193. {
  194. public:
  195. //////////
  196. // STL typedefs.
  197. //////////
  198. typedef DWORD size_type;
  199. typedef ptrdiff_t difference_type;
  200. typedef ATTRIBUTEPOSITION& reference;
  201. typedef const ATTRIBUTEPOSITION& const_reference;
  202. typedef ATTRIBUTEPOSITION value_type;
  203. typedef PATTRIBUTEPOSITION iterator;
  204. typedef const ATTRIBUTEPOSITION* const_iterator;
  205. // Construct a vector with zero capacity.
  206. IASAttributeVector() throw ();
  207. // Construct a vector with heap-allocated capacity of 'N'.
  208. explicit IASAttributeVector(size_type N);
  209. // Construct a vector with initial capacity of 'initCap' using the
  210. // user-provided C-style array beginning at 'init'.
  211. IASAttributeVector(PATTRIBUTEPOSITION init, size_type initCap) throw ();
  212. // Copy-constructor.
  213. IASAttributeVector(const IASAttributeVector& v);
  214. // Assignment operator.
  215. IASAttributeVector& operator=(const IASAttributeVector& v);
  216. // Destructor.
  217. ~IASAttributeVector() throw ();
  218. // Similar to 'erase' except the attribute is not released.
  219. iterator discard(iterator p) throw ();
  220. // Similar to 'discard' except the order of the elements following 'p' is
  221. // not necessarily preserved.
  222. iterator fast_discard(iterator p) throw ();
  223. // Similar to 'erase' except the order of the elements following 'p' is
  224. // not necessarily preserved.
  225. iterator fast_erase(iterator p) throw ()
  226. {
  227. IASAttributeRelease(p->pAttribute);
  228. return fast_discard(p);
  229. }
  230. // Load the requested attributes into the vector.
  231. DWORD load(IAttributesRaw* request, DWORD attrIDCount, LPDWORD attrIDs);
  232. // Load all attributes with a given ID into the vector.
  233. DWORD load(IAttributesRaw* request, DWORD attrID)
  234. { return load(request, 1, &attrID); }
  235. // Load all the attributes in the request into the vector.
  236. DWORD load(IAttributesRaw* request);
  237. // Adds an ATTRIBUTEPOSITION struct to the end of the vetor, resizing as
  238. // necessary. The 'addRef' flag indicates whether IASAttributeAddRef should
  239. // be called for the embedded attribute.
  240. void push_back(ATTRIBUTEPOSITION& p, bool addRef = true);
  241. // Adds an attribute to the end of the vetor, resizing as necessary.
  242. // The 'addRef' flag indicates whether IASAttributeAddRef should be
  243. // called for the attribute.
  244. void push_back(PIASATTRIBUTE p, bool addRef = true)
  245. {
  246. ATTRIBUTEPOSITION pos = { 0, p };
  247. push_back(pos, addRef);
  248. }
  249. // Remove the contents of the vector from the request.
  250. void remove(IAttributesRaw* request);
  251. // Store the contents of the vector in the request.
  252. void store(IAttributesRaw* request) const;
  253. //////////
  254. // The remainder of the public interface follows the semantics of the
  255. // STL vector class (q.v.).
  256. //////////
  257. const_reference at(size_type pos) const throw ()
  258. { return *(begin_ + pos); }
  259. reference at(size_type pos) throw ()
  260. { return *(begin_ + pos); }
  261. iterator begin() throw ()
  262. { return begin_; }
  263. const_iterator begin() const throw ()
  264. { return begin_; }
  265. size_type capacity() const throw ()
  266. { return capacity_; }
  267. void clear() throw ();
  268. bool empty() const throw ()
  269. { return begin_ == end_; }
  270. iterator end() throw ()
  271. { return end_; }
  272. const_iterator end() const throw ()
  273. { return end_; }
  274. iterator erase(iterator p) throw ()
  275. {
  276. IASAttributeRelease(p->pAttribute);
  277. return discard(p);
  278. }
  279. reference back() throw ()
  280. { return *(end_ - 1); }
  281. const_reference back() const throw ()
  282. { return *(end_ - 1); }
  283. reference front() throw ()
  284. { return *begin_; }
  285. const_reference front() const throw ()
  286. { return *begin_; }
  287. void reserve(size_type N);
  288. size_type size() const throw ()
  289. { return (size_type)(end_ - begin_); }
  290. const_reference operator[](size_type pos) const throw ()
  291. { return at(pos); }
  292. reference operator[](size_type pos) throw ()
  293. { return at(pos); }
  294. protected:
  295. PATTRIBUTEPOSITION begin_; // Beginning of the vector.
  296. PATTRIBUTEPOSITION end_; // Points one past the last element.
  297. size_type capacity_; // Capacity of the vector in elements.
  298. bool owner; // true if the memory should be freed.
  299. };
  300. ///////////////////////////////////////////////////////////////////////////////
  301. //
  302. // CLASS
  303. //
  304. // IASAttributeVectorWithBuffer<N>
  305. //
  306. // DESCRIPTION
  307. //
  308. // Extens IASAttributeVector to provide an initial non-heap allocation
  309. // of 'N' elements. The vector will still support heap-based resizing.
  310. //
  311. ///////////////////////////////////////////////////////////////////////////////
  312. template <IASAttributeVector::size_type N>
  313. class IASAttributeVectorWithBuffer
  314. : public IASAttributeVector
  315. {
  316. public:
  317. IASAttributeVectorWithBuffer()
  318. : IASAttributeVector(buffer, N)
  319. { }
  320. IASAttributeVectorWithBuffer(const IASAttributeVectorWithBuffer& vec)
  321. : IASAttributeVector(vec)
  322. { }
  323. IASAttributeVectorWithBuffer&
  324. operator=(const IASAttributeVectorWithBuffer& vec)
  325. {
  326. IASAttributeVector::operator=(vec);
  327. return *this;
  328. }
  329. protected:
  330. ATTRIBUTEPOSITION buffer[N]; // Initial storage.
  331. };
  332. ///////////////////////////////////////////////////////////////////////////////
  333. //
  334. // MACRO
  335. //
  336. // IASAttributeVectorOnStack(identifier, request, extra)
  337. //
  338. // DESCRIPTION
  339. //
  340. // Uses _alloca to create an IASAttributeVector on the stack that is
  341. // exactly the right size to hold all the attributes in 'request' plus
  342. // 'extra' additional attributes. The 'request' pointer may be null in
  343. // which case this will allocate space for exactly 'extra' attributes.
  344. //
  345. // CAVEAT
  346. //
  347. // This can only be used for temporary variables.
  348. //
  349. ///////////////////////////////////////////////////////////////////////////////
  350. // Must be included in an enclosing scope prior to IASAttributeVectorOnStack
  351. #define USES_IAS_STACK_VECTOR() \
  352. ULONG IAS_VECCAP;
  353. #define IASAttributeVectorOnStack(identifier, request, extra) \
  354. IAS_VECCAP = 0; \
  355. if (static_cast<IAttributesRaw*>(request) != NULL) \
  356. static_cast<IAttributesRaw*>(request)->GetAttributeCount(&IAS_VECCAP); \
  357. IAS_VECCAP += (extra); \
  358. IASAttributeVector identifier( \
  359. (PATTRIBUTEPOSITION) \
  360. _alloca(IAS_VECCAP * sizeof(ATTRIBUTEPOSITION)), \
  361. IAS_VECCAP \
  362. )
  363. ///////////////////////////////////////////////////////////////////////////////
  364. //
  365. // CLASS
  366. //
  367. // IASOrderByID
  368. //
  369. // DESCRIPTION
  370. //
  371. // Functor class for sorting/searching an IASAttributeVector by ID.
  372. //
  373. ///////////////////////////////////////////////////////////////////////////////
  374. class IASOrderByID
  375. {
  376. public:
  377. bool operator()(const ATTRIBUTEPOSITION& lhs,
  378. const ATTRIBUTEPOSITION& rhs) throw ()
  379. { return lhs.pAttribute->dwId < rhs.pAttribute->dwId; }
  380. };
  381. ///////////////////////////////////////////////////////////////////////////////
  382. //
  383. // CLASS
  384. //
  385. // IASSelectByID<T>
  386. //
  387. // DESCRIPTION
  388. //
  389. // Functor class for selecting elements from an IASAttributeVector based
  390. // on the attribute ID.
  391. //
  392. ///////////////////////////////////////////////////////////////////////////////
  393. template <DWORD ID>
  394. class IASSelectByID
  395. {
  396. public:
  397. bool operator()(const ATTRIBUTEPOSITION& pos) throw ()
  398. { return (pos.pAttribute->dwId == ID); }
  399. };
  400. ///////////////////////////////////////////////////////////////////////////////
  401. //
  402. // CLASS
  403. //
  404. // IASSelectByFlag<T>
  405. //
  406. // DESCRIPTION
  407. //
  408. // Functor class for selecting elements from an IASAttributeVector based
  409. // on the attribute flags.
  410. //
  411. ///////////////////////////////////////////////////////////////////////////////
  412. template <DWORD Flag, bool Set = true>
  413. class IASSelectByFlag
  414. {
  415. public:
  416. bool operator()(const ATTRIBUTEPOSITION& pos) throw ()
  417. {
  418. return Set ? (pos.pAttribute->dwFlags & Flag) != 0
  419. : (pos.pAttribute->dwFlags & Flag) == 0;
  420. }
  421. };
  422. ///////////////////////////////////////////////////////////////////////////////
  423. //
  424. // CLASS
  425. //
  426. // IASRequest
  427. //
  428. // DESCRIPTION
  429. //
  430. // Wrapper around a COM-based request object. Note that this is *not* a
  431. // smart pointer class. There are several important differences:
  432. //
  433. // 1) An IASRequest object is guaranteed to contain a valid request;
  434. // there is no concept of a NULL IASRequest.
  435. // 2) The IASRequest object does not take ownership of the IRequest
  436. // interface. In particular, it does not call AddRef or Release.
  437. // 3) Methods are invoked directly on the IASRequest object rather than
  438. // through the -> operator.
  439. //
  440. ///////////////////////////////////////////////////////////////////////////////
  441. class IASRequest
  442. {
  443. public:
  444. explicit IASRequest(IRequest* request);
  445. IASRequest(const IASRequest& request) throw ()
  446. : req(request.req), raw(request.raw)
  447. { _addref(); }
  448. IASRequest& operator=(const IASRequest& request) throw ();
  449. ~IASRequest() throw ()
  450. { _release(); }
  451. IASREQUEST get_Request() const
  452. {
  453. LONG val;
  454. checkError(req->get_Request(&val));
  455. return (IASREQUEST)val;
  456. }
  457. void put_Request(IASREQUEST newVal)
  458. {
  459. checkError(req->put_Request(newVal));
  460. }
  461. IASRESPONSE get_Response() const
  462. {
  463. LONG val;
  464. checkError(req->get_Response(&val));
  465. return (IASRESPONSE)val;
  466. }
  467. DWORD get_Reason() const
  468. {
  469. LONG val;
  470. checkError(req->get_Reason(&val));
  471. return val;
  472. }
  473. IASPROTOCOL get_Protocol() const
  474. {
  475. IASPROTOCOL val;
  476. checkError(req->get_Protocol(&val));
  477. return val;
  478. }
  479. void put_Protocol(IASPROTOCOL newVal)
  480. {
  481. checkError(req->put_Protocol(newVal));
  482. }
  483. IRequestSource* get_Source() const
  484. {
  485. IRequestSource* val;
  486. checkError(req->get_Source(&val));
  487. return val;
  488. }
  489. void put_Source(IRequestSource* newVal)
  490. {
  491. checkError(req->put_Source(newVal));
  492. }
  493. void SetResponse(IASRESPONSE eResponse, DWORD dwReason = S_OK)
  494. {
  495. checkError(req->SetResponse(eResponse, (LONG)dwReason));
  496. }
  497. void ReturnToSource(IASREQUESTSTATUS eStatus)
  498. {
  499. checkError(req->ReturnToSource(eStatus));
  500. }
  501. void AddAttributes(DWORD dwPosCount, PATTRIBUTEPOSITION pPositions)
  502. {
  503. checkError(raw->AddAttributes(dwPosCount, pPositions));
  504. }
  505. void RemoveAttributes(DWORD dwPosCount, PATTRIBUTEPOSITION pPositions)
  506. {
  507. checkError(raw->RemoveAttributes(dwPosCount, pPositions));
  508. }
  509. void RemoveAttributesByType(DWORD dwAttrIDCount, LPDWORD lpdwAttrIDs)
  510. {
  511. checkError(raw->RemoveAttributesByType(dwAttrIDCount, lpdwAttrIDs));
  512. }
  513. DWORD GetAttributeCount() const
  514. {
  515. DWORD count;
  516. checkError(raw->GetAttributeCount(&count));
  517. return count;
  518. }
  519. // Returns the number of attributes retrieved.
  520. DWORD GetAttributes(DWORD dwPosCount,
  521. PATTRIBUTEPOSITION pPositions,
  522. DWORD dwAttrIDCount,
  523. LPDWORD lpdwAttrIDs);
  524. //////////
  525. // Cast operators to extract the embedded interfaces.
  526. //////////
  527. operator IRequest*() { return req; }
  528. operator IAttributesRaw*() { return raw; }
  529. protected:
  530. // Throws an exception if a COM method fails.
  531. static void checkError(HRESULT hr)
  532. { if (FAILED(hr)) { issue_error(hr); } }
  533. void _addref() { raw->AddRef(); }
  534. void _release() { raw->Release(); }
  535. IRequest* req; // Underlying interfaces.
  536. IAttributesRaw* raw; // Underlying interfaces.
  537. };
  538. ///////////////////////////////////////////////////////////////////////////////
  539. //
  540. // CLASS
  541. //
  542. // IASDictionary
  543. //
  544. // DESCRIPTION
  545. //
  546. // Provides access to the attribute dictionary.
  547. //
  548. ///////////////////////////////////////////////////////////////////////////////
  549. class IASDictionary
  550. {
  551. public:
  552. // selectNames: null terminated array of strings containing the columns to
  553. // be selected
  554. // path: full path to the dictionary database or NULL to use the
  555. // local dictionary
  556. IASDictionary(
  557. const WCHAR* const* selectNames,
  558. PCWSTR path = NULL
  559. );
  560. ~IASDictionary() throw ();
  561. ULONG getNumRows() const throw ()
  562. { return table->numRows; }
  563. // Advance to the next row. This must be called on a newly constructed
  564. // dictionary to advance to the first row.
  565. bool next() throw ();
  566. // Reset the dictionary to its initial state.
  567. void reset() throw ();
  568. // Returns true if the specified column is empty in the current row.
  569. bool isEmpty(ULONG ordinal) const;
  570. // Retrieve column values from the current row.
  571. VARIANT_BOOL getBool(ULONG ordinal) const;
  572. BSTR getBSTR(ULONG ordinal) const;
  573. LONG getLong(ULONG ordinal) const;
  574. const VARIANT* getVariant(ULONG ordinal) const;
  575. private:
  576. const IASTable* table; // The table data.
  577. ULONG mapSize; // Number of columns selected.
  578. PULONG selectMap; // Maps select ordinals to table ordinals.
  579. ULONG nextRowNumber; // Next row.
  580. VARIANT* currentRow; // Current row -- may be NULL.
  581. IASTable data; // Local storage for non-local dictionaries.
  582. CComVariant storage; // Storage associated with the dictionary.
  583. // Not implemented.
  584. IASDictionary(const IASDictionary&);
  585. IASDictionary& operator=(const IASDictionary&);
  586. };
  587. //////////
  588. // End of the IASTL namespace.
  589. //////////
  590. }
  591. ///////////////////////////////////////////////////////////////////////////////
  592. //
  593. // OctetString conversion macros and functions.
  594. //
  595. ///////////////////////////////////////////////////////////////////////////////
  596. // Compute the size of the buffer required by IASOctetStringToAnsi
  597. #define IAS_OCT2ANSI_LEN(oct) \
  598. (((oct).dwLength + 1) * sizeof(CHAR))
  599. // Compute the size of the buffer required by IASOctetStringToWide
  600. #define IAS_OCT2WIDE_LEN(oct) \
  601. (((oct).dwLength + 1) * sizeof(WCHAR))
  602. // Coerce an OctetString to a null-terminated ANSI string. There is no check
  603. // for overflow. The dst buffer must be at least IAS_OCT2ANSI_LEN bytes.
  604. PSTR IASOctetStringToAnsi(const IAS_OCTET_STRING& src, PSTR dst) throw ();
  605. // Coerce an OctetString to a null-terminated Unicode string. There is no
  606. // check for overflow. The dst buffer must be at least IAS_OCT2UNI_LEN bytes.
  607. PWSTR IASOctetStringToWide(const IAS_OCTET_STRING& src, PWSTR dst) throw ();
  608. // Convert an OctetString to ANSI on the stack.
  609. #define IAS_OCT2ANSI(oct) \
  610. (IASOctetStringToAnsi((oct), (PSTR)_alloca(IAS_OCT2ANSI_LEN(oct))))
  611. // Convert an OctetString to Unicode on the stack.
  612. #define IAS_OCT2WIDE(oct) \
  613. (IASOctetStringToWide((oct), (PWSTR)_alloca(IAS_OCT2WIDE_LEN(oct))))
  614. ///////////////////////////////////////////////////////////////////////////////
  615. //
  616. // Miscellaneous utility functions.
  617. //
  618. ///////////////////////////////////////////////////////////////////////////////
  619. // Retrieves and returns a single attribute with the given ID and type. The
  620. // attribute should *not* be released and is only valid while the caller holds
  621. // a reference to 'request'. On error or if the attribute is not found, the
  622. // function returns NULL.
  623. PIASATTRIBUTE IASPeekAttribute(
  624. IAttributesRaw* request,
  625. DWORD dwId,
  626. IASTYPE itType
  627. ) throw ();
  628. #endif // _IASTLUTL_H_