// This is the main library that creates the Shell Extension DLL // // Debugging Hints: // // 1) After compiling the library go to the folder where the DLL was created. The // easyest way to register the server is to copy the Register and UnRegiser // batch files from one of the demos and edit it to point to your DLL. // Run the batch to register the DLL with the Shell. // The overridden UpdateRegistry method in the TComObjectFactory decendents // in EasyCOMFactories will be called and the necessary entries will be // added to create the new Junction Point for the NSE or Shell Handler. // // Note: // You can not use // Run > Register ActiveX Server // from within the IDE. This is because EasyNSE does not use ComServ or ComObj // units. There are a couple of reasons. // 1) I wanted to make the DLL as small as possible. // 2) I think the implementation of ComServ breaks COM rules in a couple of ways // 3) BCB can't compile Delphi files that use ComServ (so I am told) // // // 2) Now Explorer may be run to check the NSE for errors. // 3) Now Run the UnRegister batch file created in step (1) // 4) At this point you may not be able to recompile the dll as Windows // may have not released it yet: // // HINT: // To unload a DLL that Explorer won't let go of: // 1) On the Taskbar choose: Start > Shut Down // 2) Now hold down and click "Cancel" // 3) Explorer will be terminated // 4) Hit // 5) Choose "Task Manager" // 6) Choose "New Task" in the lower right corner // 7) Enter "Explorer.exe" in the edit box and hit "OK" // 8) A fresh instance of Explorer will be started and your // DLL will be unloaded // 5) It is suppose to be possible to Kill explorer by the above hint then // in Delphi choose // Run > Parameters // and in the "Host Application" edit box enter "Explorer.exe" // Unfortunately the Delphi Debugger will lock up Windows and you will need // to do a Power Down restart (at least with D7) // 6) // // HINT: Debugging your NSE with VirtualShellTools // 1) Open the VSTools Demo named "Namespace Browser" // 2) Choose // Project > Options > Compiler // Check all boxes in the Debugging area // 3) Choose // Project > Options > Linker // Check "Include Remote Debugging Symbols" // 4) Recomile // 5) Now open your NSE project // 6) Repeat steps 2 - 4 for this project // 7) Choose // Run > Parameters // and in the "Host Application" edit browse to the VSTools // NamespaceBrowserProject.exe file // 8) Now open the Unit1.pas file in the IDE that contains the // Namespace Browser form, DO NOT open the actual project // 9) On each Selection change the Browser loads about every possible // thing that is available from the NSE. Set a break point in the // procedure TForm1.FillInfo; // method // 10) Hit Run > Run // 11) You can now set break points in BOTH the demo and the DLL!!!! // NOTE: // The "blue dot" breakpoints in the DLL may not be visible // until the dll is loaded by the shell when the namespace is // first accessed (i.e. My Computer is expanded) // 12) Also using this method the DLL is almost always unloaded after // the NSE Browser is exited. // // 7) You may also include debugging traces within the interface encapsulation // 8) Currently the following Conditional Defines are available (for NSE's only not // Shell Handlers) // {$DEFINE DEBUGMESSAGES_ISHELLFOLDER} // {$DEFINE DEBUGMESSAGES_ATTRIBUTEOF} // {$DEFINE DEBUGMESSAGES_UIOBJECTOF} // {$DEFINE DEBUGMESSAGES_ISHELLFOLDER2} // {$DEFINE DEBUGMESSAGES_SHELLVIEW} // If you define any of these define through: // Project > Options > Conditionals/Defines // A message box will be shown as each Interface Method is called by // Explorer (or VSTools) // #include #include #include #include "EasyThreadSafeMenus.hpp" #include "EasyIDEComponents.hpp" #include "EasyCommonObjects.hpp" #include "EasyQueryInfoHandler.hpp" #include "EasyPropertySheetHandler.hpp" #include "EasyThumbnailHandler.hpp" #include "EasyContextMenuHandler.hpp" #include "EasyDragDropHandler.hpp" #include "EasyColumnHandler.hpp" #include "EasyCopyHookHandler.hpp" #include "EasyIconHandler.hpp" #include "EasyDropHandler.hpp" #include "EasyDataHandler.hpp" #include "EasyIconOverlayHandler.hpp" #include "EasyNamespaceHandler.hpp" #include "EasyBandHandlers.hpp" #include "EasyUtilities.hpp" #pragma resource "EasyPropSheetExt.res" #pragma resource "Winxp_DLL.res" bool RegisterLibrary(); bool UnRegisterLibrary(); void InitializeLibrary(); void FinalizeLibrary(); // D5 Fixes this problem; Word ControlWord; bool Initialized = False; // Ensures that the library is initialized (core objects are created) and is // initialized only once void Initialize() { if (!Initialized) { RegisterClass(__classid(TEasyQueryInfoHandler)); RegisterClass(__classid(TEasyPropertySheetHandler)); RegisterClass(__classid(TEasyThumbnailHandler)); RegisterClass(__classid(TEasyContextMenuHandler)); RegisterClass(__classid(TEasyContextMenuItem)); RegisterClass(__classid(TEasyDragDropHandler)); RegisterClass(__classid(TEasyColumnHandler)); RegisterClass(__classid(TEasyCopyHookHandler)); RegisterClass(__classid(TEasyIconHandler)); RegisterClass(__classid(TEasyDropHandler)); RegisterClass(__classid(TEasyDataHandler)); RegisterClass(__classid(TEasyIconOverlayHandler)); RegisterClass(__classid(TEasyNamespaceHandler)); RegisterClass(__classid(TEasyNamespaceFolder)); RegisterClass(__classid(TEasyNSEContextMenuItem)); RegisterClass(__classid(TEasyNSEContextMenu)); RegisterClass(__classid(TEasyIEBandForm)); RegisterClass(__classid(TEasyIEToolBandForm)); RegisterClass(__classid(TEasyDeskBandForm)); RegisterClass(__classid(TEasyInternetExplorerBandHandler)); RegisterClass(__classid(TEasyInternetExplorerToolBandHandler)); RegisterClass(__classid(TEasyDesktopBandHandler)); RegisterClass(__classid(TEasyFactoryManager)); RegisterClass(__classid(TEasyPopupMenuItem)); RegisterClass(__classid(TEasyPopupMenu)); RegisterClass(__classid(TEasyExplorerMenuItem)); RegisterClass(__classid(TEasyExplorerMenu)); OleInitialize(NULL); InitializeLibrary(); // Don't allow DllCanUnloadNow to not unload the DLL based on the DataModule // Template objects. If that is all there is then ok to unload. ObjectCount = 0; Initialized = true; } } // Ensures that the library is finalized (core objects are destroyed) void Finalize() { if (Initialized) { FinalizeLibrary(); OleUninitialize(); Initialized = false; } } STDAPI __export DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) { // Validate Obj Address if (!ppv) { return E_POINTER; } *ppv = NULL; try { // Make sure the objects are initialized Initialize(); if (IsEqualIID(riid, IID_IClassFactory)) { if (FactoryManager) { *ppv = FactoryManager->CreateInstance(rclsid); if (ppv) return S_OK; } } return CLASS_E_CLASSNOTAVAILABLE; } catch(...) { *ppv = NULL; return CLASS_E_CLASSNOTAVAILABLE; } } STDAPI __export DllCanUnloadNow(void) { try { if (ObjectCount <= 0) return S_OK; else return S_FALSE; } catch(...) { return S_FALSE; } } STDAPI __export DllRegisterServer(void) { try { // Make sure the objects are initialized Initialize(); if (RegisterLibrary() && FactoryManager) { FactoryManager->RegisterHandlers(); return S_OK; } else return E_FAIL; } catch(...) { return E_FAIL; } } STDAPI __export DllUnregisterServer(void) { try { // Make sure the objects are initialized Initialize(); if (UnRegisterLibrary() && FactoryManager) { FactoryManager->UnRegisterHandlers(); return S_OK; } else return E_FAIL; } catch(...) { return E_FAIL; } } // There has *always* been a stability problem with the Win32 Delphi RTL. // I finally got a demo to constantly crash and Mathias Rauen FINALLY tracked // it down! I now feel it is feasable to write Global Hooks and DLLs that // run in other processes address space, like a NSE. // Patch by Mathias Rauen // Until Borland fixed the RTL the fix is very clean to implement outside of the // RTL. /* "I have found the bug in the RTL and I have a clean workaround. Let me explain the situation: DLL_PROCESS_ATTACH -> SysInit.InitProcessTLS -> SysInit.InitThreadTLS DLL_THREAD_ATTACH -> SysInit.InitThreadTLS DLL_THREAD_DETACH -> SysInit.ExitThreadTLS DLL_PROCESS_DETACH -> SysInit.ExitProcessTLS -> SysInit.ExitThreadTLS As you can see, DLL_XXX_ATTACH always ends up in "InitThreadTLS", while DLL_XXX_DETACH always ends up in "ExitThreadTLS". We can forget about "Init/ExitProcessTLS". Now let's go through some situations. For all of the following cases please note that the events are meant to be for the very same thread (that's important!!). (1) InitThreadTLS ExitThreadTLS -> perfectly fine (standard case) (2) ExitThreadTLS -> strange situation, but no problems (3) InitThreadTLS -> memory leak (but I think this situation will never occur) (4) InitThreadTLS InitThreadTLS ExitThreadTLS -> memory leak (but I think this situation will never occur) (5) InitThreadTLS ExitThreadTLS ExitThreadTLS -> LocalAlloc gets called twice for the same pointer Now what happens when doing CBT hooking in win98 is situation (5). The very same thread gets a DLL_PROCESS_ATTACH + DLL_THREAD_DETACH + DLL_PROCESS_DETACH event. And the end result is the Explorer crash. If you ask me, the Borland programmers didn't believe, that case (5) can happen - but it does. Now let's look at my patched "ExitThreadTLS" function. I just added one line: " */ void madPatch_ExitThreadTLS() { void* p; if (!TlsLast) return; if (TlsIndex != -1) { p = TlsGetValue(TlsIndex); if (!p) { // In D5 the RTL is different. It calls the ExitThreadTLS in SysInit // BEFORE it calls DLLPROC (where this is called). In these compilers we // only need to nil the TLS! #ifdef COMPILER_6_UP LocalFree(Cardinal(p)); #endif COMPILER_6_UP TlsSetValue(TlsIndex, NULL); // <- this fixes case (5) } } } int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved) { switch (reason) { case DLL_PROCESS_DETACH: { // D5 Fixes this problem; #ifndef COMPILER_5_UP Set8087CW(ControlWord); #endif // The process is dumping us so clean up Finalize(); } break; case DLL_PROCESS_ATTACH: { // D5 Fixes this problem; #ifndef COMPILER_5_UP Set8087CW(0x133f); #endif } break; case DLL_THREAD_ATTACH: { } break; case DLL_THREAD_DETACH: { #ifndef NO_BANDHANDLERS DWORD ThreadID = GetCurrentThreadId(); PopupManager->RemoveThreadID(ThreadID); #endif NO_BANDHANDLERS // We are freeing the TLS AND setting it to nil before // the RTL can get to it to fix the Delphi global DLL stablility // issue. madPatch_ExitThreadTLS(); } break; } return 1; }