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.

723 lines
19 KiB

  1. /*****************************************************************************
  2. *
  3. * fnd.h - Main private header file
  4. *
  5. *****************************************************************************/
  6. /*****************************************************************************
  7. *
  8. * Coding conventions:
  9. *
  10. * + Follow standard shell coding conventions.
  11. *
  12. * + Standard K&R brace placement and indentation style.
  13. *
  14. * + Indent by 4 spaces.
  15. *
  16. * + Fully-brace all dependent clauses. Never write "if (c) foo();"
  17. *
  18. * + Do not return in the middle of a function. If forced,
  19. * use a "goto exit". This way, you can stick entry/exit stuff
  20. * later without getting caught out. (I learned this rule the
  21. * hard way.)
  22. *
  23. * + Declare variables with narrowest possible scope.
  24. *
  25. * + Always test for success, not failure! The compiler will
  26. * thank you.
  27. *
  28. *****************************************************************************/
  29. /*****************************************************************************
  30. *
  31. * NOTE! This code was written for readability, not efficiency.
  32. *
  33. * I'm trusting the compiler to do optimizations like these:
  34. *
  35. * "Parameter alias":
  36. *
  37. * Function(LPFOO pfoo)
  38. * {
  39. * LPBAR pbar = (LPBAR)pfoo;
  40. * ... use pbar and never mention pfoo again ...
  41. * }
  42. *
  43. * --> becomes
  44. *
  45. * Function(LPFOO pfoo)
  46. * {
  47. * #define pbar ((LPBAR)pfoo)
  48. * ... use pbar and never mention pfoo again ...
  49. * #undef pbar
  50. * }
  51. *
  52. * "Speculative Execution":
  53. *
  54. * Function(PFOO pfoo)
  55. * {
  56. * BOOL fRc;
  57. * if (... condition 1 ...) {
  58. * ... complicated stuff ...
  59. * *pfoo = result;
  60. * fRc = 1;
  61. * } else { // condition 1 failed
  62. * *pfoo = 0;
  63. * fRc = 0;
  64. * }
  65. * return fRc;
  66. * }
  67. *
  68. * --> becomes
  69. *
  70. * Function(PFOO pfoo)
  71. * {
  72. * BOOL fRc = 0;
  73. * *pfoo = 0;
  74. * if (... condition 1 ...) {
  75. * ... complicated stuff ...
  76. * *pfoo = result;
  77. * fRc = 1;
  78. * }
  79. * return fRc;
  80. * }
  81. *
  82. * "Single Exit":
  83. *
  84. * Function(...)
  85. * {
  86. * BOOL fRc;
  87. * if (... condition 1 ...) {
  88. * ...
  89. * if (... condition 2 ...) {
  90. * ...
  91. * fRc = 1;
  92. * } else { // condition 2 failed
  93. * ... clean up ...
  94. * fRc = 0;
  95. * }
  96. * } else { // condition 1 failed
  97. * ... clean up ...
  98. * fRc = 0;
  99. * }
  100. * return fRc;
  101. * }
  102. *
  103. * --> becomes
  104. *
  105. * Function(...)
  106. * {
  107. * if (... condition 1 ...) {
  108. * ...
  109. * if (... condition 2 ...) {
  110. * ...
  111. * return 1;
  112. * } else { // condition 2 failed
  113. * ... clean up ...
  114. * return 0;
  115. * }
  116. * } else { // condition 1 failed
  117. * ... clean up ...
  118. * return 0;
  119. * }
  120. * NOTREACHED;
  121. * }
  122. *
  123. *
  124. *
  125. *****************************************************************************/
  126. #define STRICT
  127. #define WIN32_LEAN_AND_MEAN
  128. #define NOIME
  129. #define NOSERVICE
  130. #define WINVER 0x0400
  131. #define _WIN32_WINDOWS 0x0400
  132. #include <windows.h>
  133. #ifdef RC_INVOKED /* Define some tags to speed up rc.exe */
  134. #define __RPCNDR_H__ /* Don't need RPC network data representation */
  135. #define __RPC_H__ /* Don't need RPC */
  136. #include <oleidl.h> /* Get the DROPEFFECT stuff */
  137. #define _OLE2_H_ /* But none of the rest */
  138. #define _WINDEF_
  139. #define _WINBASE_
  140. #define _WINGDI_
  141. #define NONLS
  142. #define _WINCON_
  143. #define _WINREG_
  144. #define _WINNETWK_
  145. #define _INC_COMMCTRL
  146. #define _INC_SHELLAPI
  147. #else
  148. #include <windowsx.h>
  149. #endif
  150. #include <shlobj.h>
  151. #include <shellapi.h>
  152. /*****************************************************************************
  153. *
  154. * Resource identifiers
  155. *
  156. *****************************************************************************/
  157. /*****************************************************************************
  158. *
  159. * Icons
  160. *
  161. *****************************************************************************/
  162. #define IDI_INETFIND 1 /* My main icon */
  163. /*****************************************************************************
  164. *
  165. * Menus
  166. *
  167. *****************************************************************************/
  168. #define IDM_ONTHEINTERNET 1 /* Our sole menu item */
  169. /*****************************************************************************
  170. *
  171. * Strings
  172. *
  173. *****************************************************************************/
  174. #define IDS_ONTHEINTERNET 1
  175. #define IDS_FINDHELP 2
  176. #ifndef RC_INVOKED
  177. /*****************************************************************************
  178. *
  179. * Stuff I'm tired of typing over and over.
  180. *
  181. *****************************************************************************/
  182. typedef LPITEMIDLIST PIDL, *PPIDL;
  183. typedef LPCITEMIDLIST PCIDL;
  184. typedef LPSHELLFOLDER PSF;
  185. typedef LPVOID PV;
  186. typedef LPVOID *PPV;
  187. typedef LPCVOID PCV;
  188. typedef REFIID RIID;
  189. typedef LPUNKNOWN PUNK;
  190. /*****************************************************************************
  191. *
  192. * Baggage - Stuff I carry everywhere
  193. *
  194. *****************************************************************************/
  195. #define INTERNAL NTAPI /* Called only within a translation unit */
  196. #define EXTERNAL NTAPI /* Called from other translation units */
  197. #define INLINE static __inline
  198. #define BEGIN_CONST_DATA data_seg(".text", "CODE")
  199. #define END_CONST_DATA data_seg(".data", "DATA")
  200. #define OBJAT(T, v) (*(T *)(v)) /* Pointer punning */
  201. #define PUN(T, v) OBJAT(T, &(v)) /* General-purpose type-punning */
  202. /*
  203. * Convert a count of TCHAR's to a count of bytes.
  204. */
  205. #define cbCtch(ctch) ((ctch) * sizeof(TCHAR))
  206. /*
  207. * Convert an object (X) to a count of bytes (cb).
  208. */
  209. #define cbX(X) sizeof(X)
  210. /*
  211. * Convert an array name (A) to a generic count (c).
  212. */
  213. #define cA(a) (cbX(a)/cbX(a[0]))
  214. /*
  215. * Convert an array name (A) to a pointer to its Max.
  216. * (I.e., one past the last element.)
  217. */
  218. #define pvMaxA(a) (&a[cA(a)])
  219. #define pvSubPvCb(pv, cb) ((PV)((PBYTE)pv - (cb)))
  220. #define pvAddPvCb(pv, cb) ((PV)((PBYTE)pv + (cb)))
  221. #define cbSubPvPv(p1, p2) ((PBYTE)(p1) - (PBYTE)(p2))
  222. /*
  223. * Round cb up to the nearest multiple of cbAlign. cbAlign must be
  224. * a power of 2 whose evaluation entails no side-effects.
  225. */
  226. #define ROUNDUP(cb, cbAlign) ((((cb) + (cbAlign) - 1) / (cbAlign)) * (cbAlign))
  227. /*
  228. * lfNeVV
  229. *
  230. * Given two values, return zero if they are equal and nonzero if they
  231. * are different. This is the same as (v1) != (v2), except that the
  232. * return value on unequal is a random nonzero value instead of 1.
  233. * (lf = logical flag)
  234. *
  235. * lfNePvPv
  236. *
  237. * The same as lfNeVV, but for pointers.
  238. *
  239. * lfPv
  240. *
  241. * Nonzero if pv is not null.
  242. *
  243. */
  244. #define lfNeVV(v1, v2) ((v1) - (v2))
  245. #define lfNePvPv(v1, v2) lfNeVV((DWORD)(PV)(v1), (DWORD)(PV)(v2))
  246. #define lfPv(pv) ((BOOL)(PV)(pv))
  247. /*
  248. * land -- Logical and. Evaluate the first. If the first is zero,
  249. * then return zero. Otherwise, return the second.
  250. */
  251. #define fLandFF(f1, f2) ((f1) ? (f2) : 0)
  252. /*
  253. * lor -- Logical or. Evaluate the first. If the first is nonzero,
  254. * return it. Otherwise, return the second.
  255. *
  256. * Unfortunately, due to the limitations of the C language, this can't
  257. * be implemented, so we just return 1 if the first is nonzero.
  258. * GNU has an extension that supports this, which we use. //;Internal
  259. */
  260. #if defined(__GNUC__) //;Internal
  261. #define fLorFF(f1, f2) ((f1) ?: (f2)) //;Internal
  262. #else //;Internal
  263. #define fLorFF(f1, f2) ((f1) ? 1 : (f2))
  264. #endif //;Internal
  265. /*
  266. * limp - logical implication. True unless the first is nonzero and
  267. * the second is zero.
  268. */
  269. #define fLimpFF(f1, f2) (!(f1) || (f2))
  270. /*
  271. * leqv - logical equivalence. True if both are zero or both are nonzero.
  272. */
  273. #define fLeqvFF(f1, f2) (!(f1) == !(f2))
  274. /*
  275. * InOrder - checks that i1 <= i2 < i3.
  276. */
  277. #define fInOrder(i1, i2, i3) ((unsigned)((i2)-(i1)) < (unsigned)((i3)-(i1)))
  278. /*
  279. * CopyPvPvCb - Copy some memory around
  280. * MovePvPvCb - Move some memory around
  281. */
  282. #define CopyPvPvCb RtlCopyMemory
  283. #define MovePvPvCb RtlMoveMemory
  284. /*
  285. * memeq - Reverse of memcmp
  286. */
  287. #define memeq !memcmp
  288. /*
  289. * fPvPfnCmpPv - Compare two objects for equality using the comparison
  290. * function and the desired outcome. E.g.,
  291. *
  292. * fPvPfnCmpPv(psz1, lstrcmpi, >, psz2)
  293. *
  294. * returns nonzero if psz1 is greater than psz2 according
  295. * to lstrcmpi.
  296. */
  297. #define fPvPfnCmpPv(p1, pfn, cmp, p2) (pfn(p1, p2) cmp 0)
  298. /*
  299. * lstreq - nonzero if two strings (according to lstrcmp) are equal
  300. * lstrne - nonzero if two strings (according to lstrcmp) are different
  301. *
  302. * lstrieq - nonzero if two strings (according to lstrcmpi) are equal
  303. * lstrine - nonzero if two strings (according to lstrcmpi) are different
  304. *
  305. * lstrieqA - nonzero if two strings (according to lstrcmpiA) are equal
  306. * lstrineA - nonzero if two strings (according to lstrcmpiA) are different
  307. */
  308. #define lstreq !lstrcmp
  309. #define lstrne lstrcmp
  310. #define lstrieq !lstrcmpi
  311. #define lstrine lstrcmpi
  312. #define lstrieqA !lstrcmpiA
  313. #define lstrineA lstrcmpiA
  314. /*****************************************************************************
  315. *
  316. * Wrappers and other quickies
  317. *
  318. *****************************************************************************/
  319. #define pvExchangePpvPv(ppv, pv) \
  320. (PV)InterlockedExchangePointer((ppv), (pv))
  321. #define hresUs(us) MAKE_HRESULT(SEVERITY_SUCCESS, 0, (USHORT)(us))
  322. #define hresLe(le) MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, (USHORT)(le))
  323. /*****************************************************************************
  324. *
  325. * Static globals: Initialized at PROCESS_ATTACH and never modified.
  326. *
  327. *****************************************************************************/
  328. HINSTANCE g_hinst; /* My instance handle */
  329. DEFINE_GUID(CLSID_Fnd, 0x37865980, 0x75d1, 0x11cf,
  330. 0xbf,0xc7,0x44,0x45,0x53,0x54,0,0);
  331. /*****************************************************************************
  332. *
  333. * Dynamic Globals. There should be as few of these as possible.
  334. *
  335. * All access to dynamic globals must be thread-safe.
  336. *
  337. *****************************************************************************/
  338. ULONG g_cRef; /* Global reference count */
  339. /*****************************************************************************
  340. *
  341. * fndcf.c - Class Factory
  342. *
  343. *****************************************************************************/
  344. STDMETHODIMP CFndFactory_New(RIID riid, PPV ppvObj);
  345. /*****************************************************************************
  346. *
  347. * fndcm.c - IContextMenu, IShellExtInit
  348. *
  349. *****************************************************************************/
  350. STDMETHODIMP CFndCm_New(RIID riid, PPV ppvObj);
  351. /*****************************************************************************
  352. *
  353. * Common object managers.
  354. *
  355. *****************************************************************************/
  356. typedef struct PREVTBL0 { /* Simple (non-OLE) object */
  357. void (NTAPI *FinalizeProc)(PV pv); /* Finalization procedure */
  358. } PREVTBL0, *PPREVTBL0;
  359. typedef struct PREVTBL { /* Primary interface */
  360. REFIID riid; /* Type of this object */
  361. void (NTAPI *FinalizeProc)(PV pv); /* Finalization procedure */
  362. } PREVTBL, *PPREVTBL;
  363. typedef struct PREVTBL2 { /* Secondary interface */
  364. ULONG lib; /* offset from start of object */
  365. } PREVTBL2, *PPREVTBL2;
  366. #ifdef DEBUG
  367. #define Simple_Interface(C) Primary_Interface(C, IUnknown); \
  368. Default_QueryInterface(C) \
  369. Default_AddRef(C) \
  370. Default_Release(C)
  371. #define Simple_Vtbl(C) Primary_Vtbl(C)
  372. #define Simple_Interface_Begin(C) Primary_Interface_Begin(C, IUnknown)
  373. #define Simple_Interface_End(C) Primary_Interface_End(C, IUnknown)
  374. #else
  375. #define Simple_Interface(C) Primary_Interface(C, IUnknown)
  376. #define Simple_Vtbl(C) Primary_Vtbl(C)
  377. #define Simple_Interface_Begin(C) \
  378. struct S_##C##Vtbl c_####C##VI = { { \
  379. &IID_##IUnknown, \
  380. C##_Finalize, \
  381. }, { \
  382. Common##_QueryInterface, \
  383. Common##_AddRef, \
  384. Common##_Release, \
  385. #define Simple_Interface_End(C) Primary_Interface_End(C, IUnknown)
  386. #endif
  387. #define Primary_Interface(C, I) \
  388. extern struct S_##C##Vtbl { \
  389. PREVTBL prevtbl; \
  390. I##Vtbl vtbl; \
  391. } c_##C##VI \
  392. #define Primary_Vtbl(C) &c_##C##VI.vtbl
  393. #define Primary_Interface_Begin(C, I) \
  394. struct S_##C##Vtbl c_####C##VI = { { \
  395. &IID_##I, \
  396. C##_Finalize, \
  397. }, { \
  398. C##_QueryInterface, \
  399. C##_AddRef, \
  400. C##_Release, \
  401. #define Primary_Interface_End(C, I) \
  402. } }; \
  403. #define Secondary_Interface(C, I) \
  404. extern struct S_##I##_##C##Vtbl { \
  405. PREVTBL2 prevtbl; \
  406. I##Vtbl vtbl; \
  407. } c_##I##_##C##VI \
  408. #define Secondary_Vtbl(C, I) &c_##I##_##C##VI.vtbl
  409. #define Secondary_Interface_Begin(C, I, nm) \
  410. struct S_##I##_##C##Vtbl c_##I##_##C##VI = { { \
  411. _IOffset(C, nm), \
  412. }, { \
  413. Forward_QueryInterface, \
  414. Forward_AddRef, \
  415. Forward_Release, \
  416. #define Secondary_Interface_End(C, I, nm) \
  417. } }; \
  418. STDMETHODIMP Common_QueryInterface(PV, REFIID, PPV);
  419. STDMETHODIMP_(ULONG) _Common_AddRef(PV pv);
  420. STDMETHODIMP_(ULONG) _Common_Release(PV pv);
  421. /*
  422. * In DEBUG, go through the vtbl for additional squirties.
  423. */
  424. #ifdef DEBUG
  425. #define Common_AddRef(punk) \
  426. ((IUnknown *)(punk))->lpVtbl->AddRef((IUnknown *)(punk))
  427. #define Common_Release(punk) \
  428. ((IUnknown *)(punk))->lpVtbl->Release((IUnknown *)(punk))
  429. #else
  430. #define Common_AddRef _Common_AddRef
  431. #define Common_Release _Common_Release
  432. #endif
  433. void EXTERNAL Common_Finalize(PV);
  434. STDMETHODIMP _Common_New(ULONG cb, PV vtbl, PPV ppvObj);
  435. #define Common_NewCb(cb, C, ppvObj) _Common_New(cb, Primary_Vtbl(C), ppvObj)
  436. #define Common_New(C, ppvObj) Common_NewCb(cbX(C), C, ppvObj)
  437. STDMETHODIMP Forward_QueryInterface(PV pv, REFIID riid, PPV ppvObj);
  438. STDMETHODIMP_(ULONG) Forward_AddRef(PV pv);
  439. STDMETHODIMP_(ULONG) Forward_Release(PV pv);
  440. /*****************************************************************************
  441. *
  442. * Common_CopyAddRef
  443. *
  444. * Copy a pointer and increment its reference count.
  445. *
  446. * Cannot be a macro because Common_AddRef evaluates its argument
  447. * twice.
  448. *
  449. *****************************************************************************/
  450. INLINE void Common_CopyAddRef(PV pvDst, PV pvSrc)
  451. {
  452. PPV ppvDst = pvDst;
  453. *ppvDst = pvSrc;
  454. Common_AddRef(pvSrc);
  455. }
  456. /*****************************************************************************
  457. *
  458. * Invoking OLE methods.
  459. *
  460. * Invoke_Release is called with a pointer to the object, not with
  461. * the object itself. It zeros out the variable on the release.
  462. *
  463. *****************************************************************************/
  464. void EXTERNAL Invoke_AddRef(PV pv);
  465. void EXTERNAL Invoke_Release(PV pv);
  466. /*****************************************************************************
  467. *
  468. * assert.c - Assertion stuff
  469. *
  470. *****************************************************************************/
  471. typedef enum {
  472. sqflAlways = 0x00000000, /* Unconditional */
  473. sqflDll = 0x00000001, /* Dll bookkeeping */
  474. sqflFactory = 0x00000002, /* IClassFactory */
  475. sqflCm = 0x00000004, /* IContextMenu */
  476. sqflCommon = 0x00000000, /* common.c */
  477. sqflError = 0x80000000, /* Errors */
  478. } SQFL; /* squiffle */
  479. void EXTERNAL SquirtSqflPtszV(SQFL sqfl, LPCTSTR ptsz, ...);
  480. int EXTERNAL AssertPtszPtszLn(LPCTSTR ptszExpr, LPCTSTR ptszFile, int iLine);
  481. #ifndef DEBUG
  482. #define SquirtSqflPtszV sizeof
  483. #endif
  484. /*****************************************************************************
  485. *
  486. * Procedure enter/exit tracking.
  487. *
  488. * Start a procedure with
  489. *
  490. * EnterProc(ProcedureName, (_ "format", arg, arg, arg, ...));
  491. *
  492. * The format string is documented in EmitPal.
  493. *
  494. * End a procedure with one of the following:
  495. *
  496. * ExitProc();
  497. *
  498. * Procedure returns no value.
  499. *
  500. * ExitProcX();
  501. *
  502. * Procedure returns an arbitrary DWORD.
  503. *
  504. * ExitOleProc();
  505. *
  506. * Procedure returns an HRESULT (named "hres").
  507. *
  508. * ExitOleProcPpv(ppvOut);
  509. *
  510. * Procedure returns an HRESULT (named "hres") and, on success,
  511. * puts a new object in ppvOut.
  512. *
  513. *****************************************************************************/
  514. #define cpvArgMax 10 /* Max of 10 args per procedure */
  515. typedef struct ARGLIST {
  516. LPCSTR pszProc;
  517. LPCSTR pszFormat;
  518. PV rgpv[cpvArgMax];
  519. } ARGLIST, *PARGLIST;
  520. void EXTERNAL ArgsPalPszV(PARGLIST pal, LPCSTR psz, ...);
  521. void EXTERNAL EnterSqflPszPal(SQFL sqfl, LPCTSTR psz, PARGLIST pal);
  522. void EXTERNAL ExitSqflPalHresPpv(SQFL, PARGLIST, HRESULT, PPV);
  523. #ifdef DEBUG
  524. SQFL sqflCur;
  525. #define AssertFPtsz(c, ptsz) \
  526. ((c) ? 0 : AssertPtszPtszLn(ptsz, TEXT(__FILE__), __LINE__))
  527. #define ValidateF(c) \
  528. ((c) ? 0 : AssertPtszPtszLn(TEXT(#c), TEXT(__FILE__), __LINE__))
  529. #define D(x) x
  530. #define SetupEnterProc(nm) \
  531. static CHAR s_szProc[] = #nm; \
  532. ARGLIST _al[1] \
  533. #define _ _al,
  534. #define ppvBool ((PPV)1)
  535. #define ppvVoid ((PPV)2)
  536. #define DoEnterProc(v) \
  537. ArgsPalPszV v; \
  538. EnterSqflPszPal(sqfl, s_szProc, _al) \
  539. #define EnterProc(nm, v) \
  540. SetupEnterProc(nm); \
  541. DoEnterProc(v) \
  542. #define ExitOleProcPpv(ppv) \
  543. ExitSqflPalHresPpv(sqfl, _al, hres, (PPV)(ppv)) \
  544. #define ExitOleProc() \
  545. ExitOleProcPpv(0) \
  546. #define ExitProc() \
  547. ExitSqflPalHresPpv(sqfl, _al, 0, ppvVoid) \
  548. #define ExitProcX(x) \
  549. ExitSqflPalHresPpv(sqfl, _al, (HRESULT)(x), ppvBool) \
  550. #else
  551. #define AssertFPtsz(c, ptsz)
  552. #define ValidateF(c) (c)
  553. #define D(x)
  554. #define SetupEnterProc(nm)
  555. #define DoEnterProc(v)
  556. #define EnterProc(nm, v)
  557. #define ExitOleProcPpv(ppv)
  558. #define ExitOleProc()
  559. #define ExitProc()
  560. #endif
  561. #define AssertF(c) AssertFPtsz(c, TEXT(#c))
  562. /*****************************************************************************
  563. *
  564. * Macros that forward to the common handlers after squirting.
  565. * Use these only in DEBUG.
  566. *
  567. * It is assumed that sqfl has been #define'd to the appropriate sqfl.
  568. *
  569. *****************************************************************************/
  570. #ifdef DEBUG
  571. #define Default_QueryInterface(Class) \
  572. STDMETHODIMP \
  573. Class##_QueryInterface(PV pv, RIID riid, PPV ppvObj) \
  574. { \
  575. SquirtSqflPtszV(sqfl, TEXT(#Class) TEXT("_QueryInterface()")); \
  576. return Common_QueryInterface(pv, riid, ppvObj); \
  577. } \
  578. #define Default_AddRef(Class) \
  579. STDMETHODIMP_(ULONG) \
  580. Class##_AddRef(PV pv) \
  581. { \
  582. ULONG ulRc = _Common_AddRef(pv); \
  583. SquirtSqflPtszV(sqfl, TEXT(#Class) \
  584. TEXT("_AddRef(%08x) -> %d"), pv, ulRc); \
  585. return ulRc; \
  586. } \
  587. #define Default_Release(Class) \
  588. STDMETHODIMP_(ULONG) \
  589. Class##_Release(PV pv) \
  590. { \
  591. ULONG ulRc = _Common_Release(pv); \
  592. SquirtSqflPtszV(sqfl, TEXT(#Class) \
  593. TEXT("_Release(%08x) -> %d"), pv, ulRc); \
  594. return ulRc; \
  595. } \
  596. #endif
  597. /*****************************************************************************
  598. *
  599. * mem.c
  600. *
  601. * Be extremely careful with FreePv, because it doesn't work if
  602. * the pointer is null.
  603. *
  604. *****************************************************************************/
  605. STDMETHODIMP EXTERNAL ReallocCbPpv(UINT cb, PV ppvObj);
  606. STDMETHODIMP EXTERNAL AllocCbPpv(UINT cb, PV ppvObj);
  607. #define FreePpv(ppv) ReallocCbPpv(0, ppv)
  608. #define FreePv(pv) LocalFree((HLOCAL)(pv))
  609. #endif /* !RC_INVOKED */