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.

604 lines
18 KiB

  1. //#define DONT_USE_ATL
  2. #include "priv.h"
  3. #define ATL_ENABLED
  4. #include "atl.h"
  5. #include "sccls.h"
  6. #include <ntverp.h>
  7. #include <shlobj.h> // for CLSID_ACLMRU
  8. #include <schedule.h>
  9. #include "shbrows2.h" // CWinInetNotify_szWindowClass
  10. #include "desktop.h" // DESKTOPPROXYCLASS
  11. #include "mluisupp.h"
  12. #define DECL_CRTFREE
  13. #include <crtfree.h>
  14. #include "shfusion.h"
  15. STDAPI_(void) InitURLIDs(UINT uPlatform); // from shdocfl.cpp
  16. STDAPI SHIsThereASystemScheduler(void); // from schedule.cpp
  17. STDAPI SHFreeSystemScheduler(void);
  18. //
  19. // Downlevel delay load support (we forward to shlwapi)
  20. //
  21. #include <delayimp.h>
  22. PfnDliHook __pfnDliFailureHook;
  23. LONG g_cRefThisDll = 0; // per-instance
  24. CRITICAL_SECTION g_csDll = {0}; // per-instance
  25. HINSTANCE g_hinst = NULL;
  26. HANDLE g_hMutexHistory = NULL;
  27. BOOL g_fNashInNewProcess = FALSE; // Are we running in a separate process
  28. BOOL g_fRunningOnNT = FALSE;
  29. BOOL g_bRunOnNT5 = FALSE;
  30. BOOL g_fRunOnWhistler = FALSE;
  31. BOOL g_bRunOnMemphis = FALSE;
  32. BOOL g_fRunOnFE = FALSE;
  33. DWORD g_dwStopWatchMode = 0; // Shell perf automation
  34. HANDLE g_hCabStateChange = NULL;
  35. BOOL g_fIE = FALSE;
  36. // Is Mirroring enabled
  37. BOOL g_bMirroredOS = FALSE;
  38. HPALETTE g_hpalHalftone = NULL;
  39. void DestroyZoneIconNameCache(void);
  40. //
  41. // This array holds information needed for ClassFacory.
  42. // OLEMISC_ flags are used by shembed and shocx.
  43. //
  44. // PERF: this table should be ordered in most-to-least used order
  45. //
  46. CF_TABLE_BEGIN(g_ObjectInfo)
  47. CF_TABLE_ENTRY(&CLSID_InternetToolbar, CInternetToolbar_CreateInstance,
  48. COCREATEONLY),
  49. CF_TABLE_ENTRY(&CLSID_BrandBand, CBrandBand_CreateInstance,
  50. COCREATEONLY),
  51. CF_TABLE_ENTRY(&CLSID_MenuBandSite, CMenuBandSite_CreateInstance,
  52. COCREATEONLY),
  53. CF_TABLE_ENTRY(&CLSID_MenuDeskBar, CMenuDeskBar_CreateInstance,
  54. COCREATEONLY),
  55. CF_TABLE_ENTRY(&CLSID_AugmentedShellFolder, CAugmentedISF_CreateInstance,
  56. COCREATEONLY),
  57. CF_TABLE_ENTRY(&CLSID_AugmentedShellFolder2, CAugmentedISF2_CreateInstance,
  58. COCREATEONLY),
  59. CF_TABLE_ENTRY(&CLSID_AddressBand, CAddressBand_CreateInstance,
  60. COCREATEONLY),
  61. CF_TABLE_ENTRY(&CLSID_AddressEditBox, CAddressEditBox_CreateInstance,
  62. COCREATEONLY),
  63. CF_TABLE_ENTRY(&CLSID_BandProxy, CBandProxy_CreateInstance,
  64. COCREATEONLY),
  65. CF_TABLE_ENTRY_NOFLAGS( &CLSID_RebarBandSite, CBandSite_CreateInstance,
  66. COCREATEONLY_NOFLAGS, OIF_ALLOWAGGREGATION),
  67. CF_TABLE_ENTRY(&CLSID_DeskBarApp, CDeskBarApp_CreateInstance,
  68. COCREATEONLY),
  69. CF_TABLE_ENTRY(&CLSID_DeskBar, CDeskBar_CreateInstance,
  70. COCREATEONLY),
  71. CF_TABLE_ENTRY(&CLSID_AutoComplete, CAutoComplete_CreateInstance,
  72. COCREATEONLY),
  73. CF_TABLE_ENTRY(&CLSID_ACLHistory, CACLHistory_CreateInstance,
  74. COCREATEONLY),
  75. CF_TABLE_ENTRY(&CLSID_ACListISF, CACLIShellFolder_CreateInstance,
  76. COCREATEONLY),
  77. CF_TABLE_ENTRY(&CLSID_ACLMRU, CACLMRU_CreateInstance,
  78. COCREATEONLY),
  79. CF_TABLE_ENTRY(&CLSID_ACLMulti, CACLMulti_CreateInstance,
  80. COCREATEONLY),
  81. CF_TABLE_ENTRY_NOFLAGS( &CLSID_CCommonBrowser, CCommonBrowser_CreateInstance,
  82. COCREATEONLY_NOFLAGS, OIF_ALLOWAGGREGATION),
  83. CF_TABLE_ENTRY(&CLSID_CDockingBarPropertyBag, CDockingBarPropertyBag_CreateInstance,
  84. COCREATEONLY),
  85. CF_TABLE_ENTRY(&CLSID_CRegTreeOptions, CRegTreeOptions_CreateInstance,
  86. COCREATEONLY),
  87. CF_TABLE_ENTRY(&CLSID_BrowserBand, CBrowserBand_CreateInstance,
  88. COCREATEONLY),
  89. CF_TABLE_ENTRY(&CLSID_SearchBand, CSearchBand_CreateInstance,
  90. COCREATEONLY),
  91. CF_TABLE_ENTRY(&CLSID_MediaBand, CMediaBand_CreateInstance,
  92. COCREATEONLY),
  93. CF_TABLE_ENTRY(&CLSID_CommBand, CCommBand_CreateInstance,
  94. COCREATEONLY),
  95. CF_TABLE_ENTRY(&CLSID_BandSiteMenu, CBandSiteMenu_CreateInstance,
  96. COCREATEONLY),
  97. CF_TABLE_ENTRY(&CLSID_ComCatCacheTask, CComCatCacheTask_CreateInstance,
  98. COCREATEONLY),
  99. CF_TABLE_ENTRY(&CLSID_ComCatConditionalCacheTask,CComCatConditionalCacheTask_CreateInstance,
  100. COCREATEONLY),
  101. CF_TABLE_ENTRY(&CLSID_ImgCtxThumbnailExtractor, CImgCtxThumb_CreateInstance,
  102. COCREATEONLY),
  103. CF_TABLE_ENTRY(&CLSID_ImageListCache, CImageListCache_CreateInstance,
  104. COCREATEONLY),
  105. CF_TABLE_ENTRY(&CLSID_ShellTaskScheduler, CShellTaskScheduler_CreateInstance,
  106. COCREATEONLY),
  107. CF_TABLE_ENTRY(&CLSID_SharedTaskScheduler, CSharedTaskScheduler_CreateInstance,
  108. COCREATEONLY),
  109. CF_TABLE_ENTRY(&CLSID_BrowseuiPreloader, CBitmapPreload_CreateInstance,
  110. COCREATEONLY),
  111. CF_TABLE_ENTRY(&CLSID_ShellSearchExt, CShellSearchExt_CreateInstance,
  112. COCREATEONLY),
  113. CF_TABLE_ENTRY(&CLSID_WebSearchExt, CWebSearchExt_CreateInstance,
  114. COCREATEONLY),
  115. CF_TABLE_ENTRY(&CLSID_OrderListExport, COrderList_CreateInstance,
  116. COCREATEONLY),
  117. CF_TABLE_ENTRY(&CLSID_UserAssist, CUserAssist_CreateInstance,
  118. COCREATEONLY),
  119. CF_TABLE_ENTRY(&CLSID_GlobalFolderSettings, CGlobalFolderSettings_CreateInstance,
  120. COCREATEONLY),
  121. CF_TABLE_ENTRY(&CLSID_ProgressDialog, CProgressDialog_CreateInstance,
  122. COCREATEONLY),
  123. CF_TABLE_ENTRY(&CLSID_ACLCustomMRU, CACLCustomMRU_CreateInstance,
  124. COCREATEONLY),
  125. CF_TABLE_ENTRY_NOFLAGS( &CLSID_MenuBand, CMenuBand_CreateInstance,
  126. COCREATEONLY_NOFLAGS, OIF_DONTIECREATE), // legacy component, dont default to browseui's impl
  127. CF_TABLE_ENTRY_NOFLAGS( &CLSID_QuickLinks, CQuickLinks_CreateInstance,
  128. COCREATEONLY_NOFLAGS, OIF_DONTIECREATE), // legacy component, dont default to browseui's impl
  129. CF_TABLE_ENTRY_NOFLAGS( &CLSID_ISFBand, CISFBand_CreateInstance,
  130. COCREATEONLY_NOFLAGS, OIF_DONTIECREATE), // legacy component, dont default to browseui's impl
  131. CF_TABLE_ENTRY_NOFLAGS( &CLSID_Thumbnail, CThumbnail_CreateInstance,
  132. COCREATEONLY_NOFLAGS, OIF_DONTIECREATE), // legacy component, dont default to browseui's impl
  133. CF_TABLE_ENTRY_NOFLAGS(&CLSID_TrackShellMenu, CTrackShellMenu_CreateInstance,
  134. COCREATEONLY_NOFLAGS, OIF_DONTIECREATE), // legacy component, dont default to browseui's impl
  135. CF_TABLE_END(g_ObjectInfo)
  136. // constructor for CObjectInfo.
  137. CObjectInfo::CObjectInfo(CLSID const* pclsidin, LPFNCREATEOBJINSTANCE pfnCreatein, IID const* piidIn,
  138. IID const* piidEventsIn, long lVersionIn, DWORD dwOleMiscFlagsIn,
  139. DWORD dwClassFactFlagsIn)
  140. {
  141. pclsid = pclsidin;
  142. pfnCreateInstance = pfnCreatein;
  143. piid = piidIn;
  144. piidEvents = piidEventsIn;
  145. lVersion = lVersionIn;
  146. dwOleMiscFlags = dwOleMiscFlagsIn;
  147. dwClassFactFlags = dwClassFactFlagsIn;
  148. }
  149. // static class factory (no allocs!)
  150. STDMETHODIMP CClassFactory::QueryInterface(REFIID riid, void **ppvObj)
  151. {
  152. if (IsEqualIID(riid, IID_IClassFactory) || IsEqualIID(riid, IID_IUnknown))
  153. {
  154. *ppvObj = (void *)GET_ICLASSFACTORY(this);
  155. DllAddRef();
  156. return NOERROR;
  157. }
  158. *ppvObj = NULL;
  159. return E_NOINTERFACE;
  160. }
  161. STDMETHODIMP_(ULONG) CClassFactory::AddRef()
  162. {
  163. DllAddRef();
  164. return 2;
  165. }
  166. STDMETHODIMP_(ULONG) CClassFactory::Release()
  167. {
  168. DllRelease();
  169. return 1;
  170. }
  171. STDMETHODIMP CClassFactory::CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  172. {
  173. *ppv = NULL;
  174. if (punkOuter && !IsEqualIID(riid, IID_IUnknown))
  175. {
  176. // It is technically illegal to aggregate an object and request
  177. // any interface other than IUnknown. Enforce this.
  178. //
  179. return CLASS_E_NOAGGREGATION;
  180. }
  181. else
  182. {
  183. LPOBJECTINFO pthisobj = (LPOBJECTINFO)this;
  184. if (punkOuter && !(pthisobj->dwClassFactFlags & OIF_ALLOWAGGREGATION))
  185. return CLASS_E_NOAGGREGATION;
  186. IUnknown *punk;
  187. HRESULT hres = pthisobj->pfnCreateInstance(punkOuter, &punk, pthisobj);
  188. if (SUCCEEDED(hres))
  189. {
  190. hres = punk->QueryInterface(riid, ppv);
  191. punk->Release();
  192. }
  193. ASSERT(FAILED(hres) ? *ppv == NULL : TRUE);
  194. return hres;
  195. }
  196. }
  197. STDMETHODIMP CClassFactory::LockServer(BOOL fLock)
  198. {
  199. if (fLock)
  200. DllAddRef();
  201. else
  202. DllRelease();
  203. TraceMsg(DM_TRACE, "sccls: LockServer(%s) to %d", fLock ? TEXT("LOCK") : TEXT("UNLOCK"), g_cRefThisDll);
  204. return S_OK;
  205. }
  206. STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
  207. {
  208. TraceMsg(TF_SHDLIFE, "DllGetClassObject called with riid=%x (%x)", riid, &riid);
  209. if (IsEqualIID(riid, IID_IClassFactory) || IsEqualIID(riid, IID_IUnknown))
  210. {
  211. for (LPCOBJECTINFO pcls = g_ObjectInfo; pcls->pclsid; pcls++)
  212. {
  213. if (IsEqualGUID(rclsid, *(pcls->pclsid)))
  214. {
  215. *ppv = (void*)pcls;
  216. DllAddRef(); // class factory holds DLL ref count
  217. return NOERROR;
  218. }
  219. }
  220. #ifdef ATL_ENABLED
  221. // Try the ATL class factory
  222. if (SUCCEEDED(AtlGetClassObject(rclsid, riid, ppv)))
  223. return NOERROR;
  224. #endif
  225. }
  226. *ppv = NULL;
  227. return CLASS_E_CLASSNOTAVAILABLE;
  228. }
  229. STDAPI DllCanUnloadNow(void)
  230. {
  231. #ifndef UNIX
  232. // special case for the system scheduler we hang onto
  233. if ( g_cRefThisDll == 1 && SHIsThereASystemScheduler() == S_OK )
  234. {
  235. // this will drop the ref count by one to zero....
  236. SHFreeSystemScheduler();
  237. }
  238. #ifdef ATL_ENABLED
  239. if (0 != g_cRefThisDll || 0 != AtlGetLockCount())
  240. return S_FALSE;
  241. #else
  242. if (0 != g_cRefThisDll)
  243. return S_FALSE;
  244. #endif
  245. #else
  246. if (g_cRefThisDll)
  247. return S_FALSE;
  248. #endif
  249. TraceMsg(DM_TRACE, "DllCanUnloadNow returning S_OK (bye, bye...)");
  250. return S_OK;
  251. }
  252. // DllGetVersion
  253. //
  254. // All we have to do is declare this puppy and CCDllGetVersion does the rest
  255. //
  256. DLLVER_SINGLEBINARY(VER_PRODUCTVERSION_DW, VER_PRODUCTBUILD_QFE);
  257. UINT g_msgMSWheel;
  258. #ifdef DEBUG
  259. EXTERN_C DWORD g_TlsMem = 0xffffffff;
  260. #endif
  261. // imports from isfband.cpp
  262. STDAPI_(void) CLogoBase_Initialize( void );
  263. STDAPI_(void) CLogoBase_Cleanup( void );
  264. //
  265. // Table of all window classes we register so we can unregister them
  266. // at DLL unload.
  267. //
  268. const LPCTSTR c_rgszClasses[] = {
  269. TEXT("BaseBar"), // basebar.cpp
  270. TEXT("MenuSite"), // menusite.cpp
  271. DESKTOPPROXYCLASS, // proxy.cpp
  272. c_szExploreClass, // shbrows2.cpp
  273. c_szIExploreClass, // shbrows2.cpp
  274. c_szCabinetClass, // shbrows2.cpp
  275. c_szAutoSuggestClass, // autocomp.cpp
  276. TEXT("MediaPane"), //Mediaband.cpp
  277. TEXT("MediaPopupPane"), //Mediaband.cpp
  278. TEXT("MediaLayoutPane") //Mediaband.cpp
  279. };
  280. //
  281. // Since we are single-binary, we have to play it safe and do
  282. // this cleanup (needed only on NT, but harmless on Win95).
  283. //
  284. #define UnregisterWindowClasses() \
  285. SHUnregisterClasses(HINST_THISDLL, c_rgszClasses, ARRAYSIZE(c_rgszClasses))
  286. void InitNFCtl()
  287. {
  288. INITCOMMONCONTROLSEX icc;
  289. icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
  290. icc.dwICC = ICC_NATIVEFNTCTL_CLASS;
  291. InitCommonControlsEx(&icc);
  292. }
  293. const LPCTSTR s_aryExplorerFileName[] =
  294. {
  295. TEXT("iexplore.exe"),
  296. };
  297. BOOL IsRootExeExplorer(void)
  298. {
  299. TCHAR szApp[MAX_PATH];
  300. LPCTSTR pszApp;
  301. GetModuleFileName(NULL, szApp, ARRAYSIZE(szApp));
  302. pszApp = PathFindFileName(szApp);
  303. if (pszApp)
  304. {
  305. for (int i = 0; i < ARRAYSIZE(s_aryExplorerFileName); i++)
  306. {
  307. if (!lstrcmpi(pszApp, s_aryExplorerFileName[i]))
  308. return TRUE;
  309. }
  310. }
  311. return FALSE;
  312. }
  313. //
  314. // we use shlwapi as our delayload error handler.
  315. // NOTE: this only works if we are statically linked to shlwapi!
  316. //
  317. void SetupDelayloadErrorHandler()
  318. {
  319. HMODULE hmod = GetModuleHandleA("shlwapi.dll");
  320. ASSERTMSG(hmod != NULL, "BROWSEUI must be statically linked to shlwapi.dll for delayload failure handling to work!");
  321. __pfnDliFailureHook = (PfnDliHook)GetProcAddress(hmod, "DelayLoadFailureHook");
  322. }
  323. STDAPI_(BOOL) DllMain(HINSTANCE hDll, DWORD dwReason, void *fProcessUnload)
  324. {
  325. if (dwReason == DLL_PROCESS_ATTACH)
  326. {
  327. SHFusionInitializeFromModule(hDll);
  328. SetupDelayloadErrorHandler();
  329. #ifdef ATL_ENABLED
  330. AtlInit(hDll);
  331. #endif
  332. DisableThreadLibraryCalls(hDll); // perf
  333. g_hinst = hDll;
  334. InitializeCriticalSection(&g_csDll);
  335. g_msgMSWheel = RegisterWindowMessage(TEXT("MSWHEEL_ROLLMSG"));
  336. MLLoadResources(g_hinst, TEXT("browselc.dll"));
  337. g_fIE = IsRootExeExplorer();
  338. if (g_fIE)
  339. InitMUILanguage(MLGetUILanguage());
  340. // Don't put it under #ifdef DEBUG
  341. CcshellGetDebugFlags();
  342. #ifdef DEBUG
  343. g_TlsMem = TlsAlloc();
  344. if (IsFlagSet(g_dwBreakFlags, BF_ONLOADED))
  345. {
  346. TraceMsg(TF_ALWAYS, "DllMain() - SHDOCVW.DLL has just loaded");
  347. DEBUG_BREAK;
  348. }
  349. #endif
  350. g_fRunningOnNT = IsOS(OS_NT);
  351. if (g_fRunningOnNT)
  352. {
  353. g_bRunOnNT5 = IsOS(OS_WIN2000ORGREATER);
  354. g_fRunOnWhistler = IsOS(OS_WHISTLERORGREATER);
  355. }
  356. else
  357. g_bRunOnMemphis = IsOS(OS_WIN98ORGREATER);
  358. g_fRunOnFE = GetSystemMetrics(SM_DBCSENABLED);
  359. g_bMirroredOS = IS_MIRRORING_ENABLED();
  360. InitNFCtl();
  361. // See if perfmode is enabled
  362. g_dwStopWatchMode = StopWatchMode();
  363. // Cache a palette handle for use throughout shdocvw
  364. g_hpalHalftone = SHCreateShellPalette( NULL );
  365. CLogoBase_Initialize( );
  366. }
  367. else if (dwReason == DLL_PROCESS_DETACH)
  368. {
  369. #ifdef ATL_ENABLED
  370. AtlTerm();
  371. #endif
  372. CBrandBand_CleanUp();
  373. CInternetToolbar_CleanUp();
  374. CUserAssist_CleanUp(dwReason, fProcessUnload);
  375. CLogoBase_Cleanup();
  376. // let go of the resource DLL...
  377. MLFreeResources(g_hinst);
  378. ENTERCRITICAL;
  379. DESTROY_OBJ_WITH_HANDLE(g_hpalHalftone, DeletePalette);
  380. DESTROY_OBJ_WITH_HANDLE(g_hCabStateChange, SHGlobalCounterDestroy);
  381. DESTROY_OBJ_WITH_HANDLE(g_hMutexHistory, CloseHandle);
  382. DestroyZoneIconNameCache();
  383. UnregisterWindowClasses();
  384. LEAVECRITICAL;
  385. DeleteCriticalSection(&g_csDll);
  386. SHFusionUninitialize();
  387. }
  388. return TRUE;
  389. }
  390. STDAPI_(void) DllAddRef(void)
  391. {
  392. InterlockedIncrement(&g_cRefThisDll);
  393. }
  394. STDAPI_(void) DllRelease(void)
  395. {
  396. ASSERT( 0 != g_cRefThisDll );
  397. InterlockedDecrement(&g_cRefThisDll);
  398. }
  399. // IEUNIX
  400. // CoCreateInstance is #defined as IECreateInstance #ifdef __cplusplus,
  401. // so I #undef it here to prevent the recursive call.
  402. // On Windows it works, because this file is C file.
  403. #ifdef CoCreateInstance
  404. #undef CoCreateInstance
  405. #endif
  406. HRESULT IECreateInstance(REFCLSID rclsid, IUnknown *pUnkOuter,
  407. DWORD dwClsContext, REFIID riid, void **ppv)
  408. {
  409. #ifndef NO_MARSHALLING
  410. if (dwClsContext == CLSCTX_INPROC_SERVER)
  411. #else
  412. if (dwClsContext & CLSCTX_INPROC_SERVER)
  413. #endif
  414. {
  415. LPCOBJECTINFO pcls;
  416. for (pcls = g_ObjectInfo; pcls->pclsid; pcls++)
  417. {
  418. // Note that we do pointer comparison (instead of IsEuqalGUID)
  419. if ((&rclsid == pcls->pclsid) && !(pcls->dwClassFactFlags & OIF_DONTIECREATE))
  420. {
  421. // const -> non-const expclit casting (this is OK)
  422. IClassFactory* pcf = GET_ICLASSFACTORY(pcls);
  423. return pcf->CreateInstance(pUnkOuter, riid, ppv);
  424. }
  425. }
  426. }
  427. // Use SHCoCreateInstanceAC to go through the app compat layer
  428. return SHCoCreateInstanceAC(rclsid, pUnkOuter, dwClsContext, riid, ppv);
  429. }
  430. #ifdef DEBUG
  431. //
  432. // In DEBUG, make sure every class we register lives in the c_rgszClasses
  433. // table so we can clean up properly at DLL unload. NT does not automatically
  434. // unregister classes when a DLL unloads, so we have to do it manually.
  435. //
  436. STDAPI_(BOOL) SHRegisterClassD(CONST WNDCLASS *pwc)
  437. {
  438. for (int i = 0; i < ARRAYSIZE(c_rgszClasses); i++)
  439. {
  440. if (lstrcmpi(c_rgszClasses[i], pwc->lpszClassName) == 0)
  441. {
  442. return RealSHRegisterClass(pwc);
  443. }
  444. }
  445. AssertMsg(0, TEXT("Class %s needs to be added to the c_rgszClasses list"), pwc->lpszClassName);
  446. return 0;
  447. }
  448. STDAPI_(ATOM) RegisterClassD(CONST WNDCLASS *pwc)
  449. {
  450. for (int i = 0; i < ARRAYSIZE(c_rgszClasses); i++)
  451. {
  452. if (lstrcmpi(c_rgszClasses[i], pwc->lpszClassName) == 0)
  453. {
  454. return RealRegisterClass(pwc);
  455. }
  456. }
  457. AssertMsg(0, TEXT("Class %s needs to be added to the c_rgszClasses list"), pwc->lpszClassName);
  458. return 0;
  459. }
  460. //
  461. // In DEBUG, send FindWindow through a wrapper that ensures that the
  462. // critical section is not taken. FindWindow'ing for a window title
  463. // sends inter-thread WM_GETTEXT messages, which is not obvious.
  464. //
  465. STDAPI_(HWND) FindWindowD(LPCTSTR lpClassName, LPCTSTR lpWindowName)
  466. {
  467. return FindWindowExD(NULL, NULL, lpClassName, lpWindowName);
  468. }
  469. STDAPI_(HWND) FindWindowExD(HWND hwndParent, HWND hwndChildAfter, LPCTSTR lpClassName, LPCTSTR lpWindowName)
  470. {
  471. if (lpWindowName)
  472. {
  473. ASSERTNONCRITICAL;
  474. }
  475. return RealFindWindowEx(hwndParent, hwndChildAfter, lpClassName, lpWindowName);
  476. }
  477. #endif // DEBUG