/*++ Copyright (c) 1997-1999 Microsoft Corporation Module Name: STIEXE.CPP Abstract: This module contains code for process, running STI+WIA services Author: Vlad Sadovsky (vlads) 09-20-97 Environment: User Mode - Win32 Revision History: 22-Sep-1997 VladS created 13-Apr-1999 VladS merged with WIA service code --*/ // // Include Headers // #include "precomp.h" #include "stiexe.h" #include "stirpc.h" #include "device.h" #include "wiapriv.h" #include "lockmgr.h" #include #include CComModule _Module; BEGIN_OBJECT_MAP(ObjectMap) // OBJECT_ENTRY(CLSID_Ubj, CObj) END_OBJECT_MAP() extern CRITICAL_SECTION g_semDeviceMan; extern CRITICAL_SECTION g_semEventNode; // // Local variables and types definitions // // // Event used for detecting previously started instance of the server // static HANDLE ServerStartedEvent; // // Flag to use service controller PnP event sink vs window message based sink // extern BOOL g_fUseServiceCtrlSink; // // Still Image entry in Registry // char* g_szStiKey = "SYSTEM\\CurrentControlSet\\Control\\StillImage"; // // WIA Debug Window value name // char* g_szWiaDebugValue = "ShowWiaDebugWindow"; // // String value to indicate that debug window should be shown // char* g_szShowWiaWinString = "Yes"; // // Bool values indicating whether Critical Section initialization was successful // BOOL g_bEventCritSectInitialized = FALSE; BOOL g_bDevManCritSectInitialized = FALSE; // // Local prototypes // DWORD InitGlobalConfigFromReg( VOID ); BOOL DoGlobalInit( UINT argc, LPTSTR *argv ); BOOL DoGlobalTermination( VOID ); BOOL UpdateRunningServer( VOID ); HWND CreateMasterWindow( VOID ); BOOL StartMasterLoop( LPVOID lpv ); BOOL StartOperation( VOID ); // // Code section // extern "C" BOOL APIENTRY DllEntryPoint( HINSTANCE hinst, DWORD dwReason, LPVOID lpReserved ) /*++ Routine Description: DllEntryPoint Main DLL entry point. Arguments: hinst - module instance dwReason - reason called lpReserved - reserved Return Value: Status Side effects: None --*/ { switch (dwReason) { case DLL_PROCESS_ATTACH: DBG_INIT(hinst); #if 0 #ifdef DEBUG StiSetDebugMask(0xffff); StiSetDebugParameters(TEXT("WIASERVC"),TEXT("")); #endif #endif g_hInst = hinst; ::DisableThreadLibraryCalls(hinst); InitializeCriticalSection(&g_RpcEvent.cs); _Module.Init(ObjectMap,hinst); break; case DLL_PROCESS_DETACH: DeleteCriticalSection(&g_RpcEvent.cs); _Module.Term(); break; } return 1; } extern "C" BOOL APIENTRY DllMain( HINSTANCE hinst, DWORD dwReason, LPVOID lpReserved ) { return DllEntryPoint(hinst, dwReason, lpReserved ); } VOID WINAPI ServiceMain( UINT argc, LPTSTR *argv ) /*++ Routine Description: Main initialization point of imaging services library Arguments: argc - counter of arguments argv - arguments array Return Value: None Side effects: None --*/ { DBG_FN(ServiceMain); // // Register our Service control handler. Note that we must do this as early as possible. // if (!RegisterServiceControlHandler()) { goto ExitMain; } // // Do global initialization, independent of specific service // if (!DoGlobalInit(argc,argv)) { goto ExitMain; } // // Start running service // StartOperation(); ExitMain: // // Global cleanup // DoGlobalTermination(); UpdateServiceStatus(SERVICE_STOPPED,NOERROR,0); } /* Endproc ServiceMain */ BOOL DoGlobalInit( UINT argc, LPTSTR *argv ) /*++ Routine Description: Perform one time service initialization Arguments: None Return Value: None. --*/ { DBG_FN(DoGlobalInit); // // To use built-in ATL conversion macros // USES_CONVERSION; #ifdef DEBUG // // Create a debug context. // #define VALUE_SIZE 5 char valueData[VALUE_SIZE]; DWORD retVal, type = 0, size = VALUE_SIZE; // // Search the registry // retVal = SHGetValueA(HKEY_LOCAL_MACHINE, g_szStiKey, g_szWiaDebugValue, &type, valueData, &size); // // Found the entry in the registry // BOOLEAN bDisplayUi = FALSE; if (retVal == ERROR_SUCCESS) { // // Compare value found in registry to g_szShowWinString // If same, then show it // if (lstrcmpiA(g_szShowWiaWinString, valueData) == 0) { bDisplayUi = TRUE; } } //remove //WIA_DEBUG_CREATE( g_hInst, // TEXT("OLD Debugging/TRACE Window (STI/WIA Service)"), // bDisplayUi, // FALSE); #endif // // Initialize COM. // HRESULT hr = CoInitializeEx(0,COINIT_MULTITHREADED); if (FAILED(hr)) { #ifdef DEBUG OutputDebugString(TEXT("DoGlobalInit, CoInitializeEx failed\n")); #endif return FALSE; } // // Initialize our global critical sections... // __try { // // Initialize the critical sections. // if (InitializeCriticalSectionAndSpinCount(&g_semDeviceMan, MINLONG)) { g_bDevManCritSectInitialized = TRUE; } if (InitializeCriticalSectionAndSpinCount(&g_semEventNode, MINLONG)) { g_bEventCritSectInitialized = TRUE; } if(!g_bDevManCritSectInitialized || !g_bEventCritSectInitialized) { #ifdef DEBUG OutputDebugString(TEXT("DoGlobalInit, InitializeCriticalSectionAndSpinCount failed\n")); #endif return FALSE; } } __except (EXCEPTION_EXECUTE_HANDLER) { // // Couldn't initialize critical sections - this is really bad, // so bail // #ifdef DEBUG OutputDebugString(TEXT("DoGlobalInit, InitializeCriticalSectionAndSpinCount threw an exception!\n")); #endif return FALSE; } // // Setup some global variables from the registry. // #ifndef WINNT g_fRunningAsService = FALSE; #endif InitGlobalConfigFromReg(); // // Create event log class object // g_EventLog = new EVENT_LOG(TEXT("StillImage")); if (!g_EventLog) { #ifdef DEBUG OutputDebugString(TEXT("DoGlobalInit, unable to allocate EVENT_LOG\n")); #endif return FALSE; } // // Create file log class object, requesting truncating file if set by user // g_StiFileLog = new STI_FILE_LOG(TEXT("STISVC"),NULL,STIFILELOG_CHECK_TRUNCATE_ON_BOOT, g_hInst); if (g_StiFileLog) { if(g_StiFileLog->IsValid()) { // Set UI bit in reporting if (g_fUIPermitted) { g_StiFileLog->SetReportMode(g_StiFileLog->QueryReportMode() | STI_TRACE_LOG_TOUI); } } else { #ifdef DEBUG OutputDebugString(TEXT("DoGlobalInit, could not open log file\n")); #endif return FALSE; } } else { #ifdef DEBUG OutputDebugString(TEXT("DoGlobalInit, unable to allocate STI_FILE_LOG\n")); #endif return FALSE; } // // Start Logging object's class factory // hr = StartLOGClassFactories(); if (SUCCEEDED(hr)) { // // Create COM Logging Object // IWiaLog *pWiaLog = NULL; hr = CWiaLog::CreateInstance(IID_IWiaLog,(void**)&g_pIWiaLog); if (SUCCEEDED(hr)) { g_pIWiaLog->InitializeLogEx((BYTE*)g_hInst); DBG_TRC(("Starting STI/WIA Service...")); } else { #ifdef DEBUG OutputDebugString(TEXT("Failed to QI for IWiaLogEx...\n")); #endif return FALSE; } } else { return FALSE; } // // Initialize the lock manager // g_pStiLockMgr = new StiLockMgr(); if (g_pStiLockMgr) { hr = g_pStiLockMgr->Initialize(); if (FAILED(hr)) { #ifdef DEBUG OutputDebugString(TEXT("DoGlobalInit, could not initialize Lock Manager\n")); #endif return FALSE; } } else { #ifdef DEBUG OutputDebugString(TEXT("DoGlobalInit, unable to allocate Lock Manager\n")); #endif return FALSE; } // // Initialize work item scheduler // SchedulerInitialize(); // // Create DeviceList event. // g_hDevListCompleteEvent = CreateEvent( NULL, // lpsaSecurity TRUE, // fManualReset FALSE, // fInitialState NULL ); // lpszEventName if( g_hDevListCompleteEvent == NULL ) { return FALSE; } // // Create and initialize global Device Manager // g_pDevMan = new CWiaDevMan(); if (g_pDevMan) { hr = g_pDevMan->Initialize(); if (SUCCEEDED(hr)) { hr = g_pDevMan->ReEnumerateDevices(DEV_MAN_FULL_REFRESH | DEV_MAN_STATUS_STARTP); if (FAILED(hr)) { DBG_ERR(("::DoGlobalInit, unable to enumerate devices")); } } else { DBG_ERR(("::DoGlobalInit, unable to initialize WIA device manager")); } } else { DBG_ERR(("::DoGlobalInit, Out of memory, could not create WIA device manager")); return FALSE; } // // Create and initialize global message handler // g_pMsgHandler = new CMsgHandler(); if (g_pMsgHandler) { hr = g_pMsgHandler->Initialize(); if (FAILED(hr)) { DBG_ERR(("::DoGlobalInit, unable to initialize internal Message handler")); } } else { DBG_ERR(("::DoGlobalInit, Out of memory, could not create internal Message handler")); return FALSE; } // // Create and initialize the object resposible for WIA Runtime event notifications // g_pWiaEventNotifier = new WiaEventNotifier(); if (g_pWiaEventNotifier) { hr = g_pWiaEventNotifier->Initialize(); if (FAILED(hr)) { DBG_ERR(("::DoGlobalInit, WIA runtime event notifier failed to initialize...exiting")); return FALSE; } } else { DBG_ERR(("::DoGlobalInit, Out of memory, could not create WIA runtime event notifier...exiting")); return FALSE; } // // Initialize the Wia Service controller // hr = CWiaSvc::Initialize(); if (FAILED(hr)) { #ifdef DEBUG OutputDebugString(TEXT("DoGlobalInit, unable to initialize Wia Service controller\n")); #endif return FALSE; } // // Read the command line arguments and set global data. // for (UINT uiParam = 0; uiParam < argc; uiParam ++ ) { switch (*argv[uiParam]) { case TEXT('A'): case TEXT('a'): // Running as user mode process g_fRunningAsService = FALSE; break; case TEXT('V'): case TEXT('v'): // Service UI is allowed g_fUIPermitted = TRUE; break; } } // // Do misc. cleanup, which we need to do on startup . // // 1. Some shipping packages for Win98 register STIMON entry to Run section, which // after upgrade creates problem with racing two copies of STIMON. Remove it. // { HKEY hkRun = NULL; LONG lRet ; LONG lcbValue = 0; BOOL fNeedToRegister = FALSE; if (RegOpenKey(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUN, &hkRun) == NO_ERROR) { DBG_TRC(("Removing erroneous entry on cleanup: HKLM\\..\\Run\\%S",REGSTR_VAL_MONITOR)); lRet = RegQueryValue(hkRun,REGSTR_VAL_MONITOR,NULL,&lcbValue); fNeedToRegister = (lRet == NOERROR); RegDeleteValue (hkRun, REGSTR_VAL_MONITOR); RegCloseKey(hkRun); } // If needed register service if (fNeedToRegister ) { StiServiceInstall(NULL,NULL); } } return TRUE; } BOOL DoGlobalTermination( VOID ) /*++ Routine Description: Final termination Arguments: None Return Value: None. --*/ { DBG_FN(DoGlobalTermination); // // Terminate work item scheduler // SchedulerTerminate(); // // Delete the lock manager (which also removes it from the ROT) // if (g_pStiLockMgr) { delete g_pStiLockMgr; g_pStiLockMgr = NULL; } // // Delete global device manager // if (g_pDevMan) { delete g_pDevMan; g_pDevMan = NULL; } // // Close hDevListRefreshCompleteEvent event handle // CloseHandle(g_hDevListCompleteEvent); g_hDevListCompleteEvent = NULL; // // Release WIA Logging object // if(g_pIWiaLog) { DBG_TRC(("Exiting STI/WIA Service...")); if (g_pIWiaLog) { g_pIWiaLog->Release(); } g_pIWiaLog = NULL; } if(g_EventLog) { delete g_EventLog; g_EventLog = NULL; } if (g_StiFileLog) { delete g_StiFileLog; g_StiFileLog = NULL; } if (ServerStartedEvent) { ::CloseHandle(ServerStartedEvent); } // // Shut down message loop // ::PostQuitMessage(0); // // Uninitialize COM. // ::CoUninitialize(); if (g_bEventCritSectInitialized) { DeleteCriticalSection(&g_semEventNode); } if (g_bDevManCritSectInitialized) { DeleteCriticalSection(&g_semDeviceMan); } // // Destroy the debug context. // // WIA_DEBUG_DESTROY(); return TRUE; } // // Guard to avoid reentrance in refresh routine // static LONG lRunningMessageLoop = 0L; HANDLE CreateMessageLoopThread( VOID ) /*++ Routine Description: Running primary service thread. If process is running as NT service, we don't have any messages to pump, so call it synchronously. Arguments: Return Value: None. --*/ { if (InterlockedExchange(&lRunningMessageLoop,1L)) { return 0; } HANDLE hThread = NULL; #ifndef WINNT hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StartMasterLoop, (LPVOID)NULL, 0, &g_dwMessagePumpThreadId); #else StartMasterLoop(NULL); #endif return hThread; } BOOL StartMasterLoop( LPVOID lpParam ) /*++ Routine Description: Running primary service thread Arguments: Return Value: None. --*/ { MSG msg; DBG_FN(StartMasterLoop); // // If visible create master window // CreateMasterWindow(); VisualizeServer(g_fUIPermitted); // // Initialize WIA. // InitWiaDevMan(WiaInitialize); #ifndef WINNT //Don't use windows messaging on NT // // Run message pump // while(GetMessage(&msg,NULL,0,0)) { // // Give WIA the first chance at dispatching messages. Note that // WIA hooks both message dispatch and the window proc. so that // both posted and sent messages can be detected. // // // currently there are no cases where sti needs to dispatch messages // to wia. Events are now dealt with directly. // #if 0 if (DispatchWiaMsg(&msg) == S_OK) { continue; } #endif TranslateMessage(&msg); DispatchMessage(&msg); } // Indicate we are entering shutdown g_fServiceInShutdown = TRUE; InterlockedExchange(&lRunningMessageLoop,0L); #endif return TRUE; } // StartMasterLoop BOOL StartOperation( VOID ) /*++ Routine Description: Running primary service thread. If process is running as NT service, separate thread is created to handle window messages. This is necessary because primary thread becomes blocked as a control dispatcher , but we still need to have message loop running to deliver window messages. Arguments: Return Value: None. --*/ { DBG_FN(StartOperation); DWORD dwError; #ifdef MAXDEBUG DBG_TRC(("Start operation entered")); #endif if (g_fRunningAsService) { // // If UI is permitted - create visible window // Note that we always create window for now, to allow hiding/unhiding it dynamically // g_hMessageLoopThread = CreateMessageLoopThread(); // !!!??? need to pass command line args down from svchost. StiServiceMain(0, NULL); if ( g_hMessageLoopThread ) { ::CloseHandle(g_hMessageLoopThread); g_hMessageLoopThread = NULL; } } else { // // Running as application // // First thing - allow PnP sink to register properly g_fUseServiceCtrlSink = FALSE; dwError = StiServiceInitialize(); g_dwMessagePumpThreadId = GetCurrentThreadId(); StartMasterLoop(NULL); StiServiceStop(); } return TRUE; } // StartOperation BOOL VisualizeServer( BOOL fVisualize ) /*++ Routine Description: Arguments: Return Value: None. --*/ { g_fUIPermitted = fVisualize; if (::IsWindow(g_hMainWindow)) { ::ShowWindow(g_hMainWindow,fVisualize ? SW_SHOWNORMAL : SW_HIDE); } if (g_StiFileLog) { if(g_StiFileLog->IsValid()) { // Set UI bit in reporting if (g_fUIPermitted) { g_StiFileLog->SetReportMode(g_StiFileLog->QueryReportMode() | STI_TRACE_LOG_TOUI); } } } return TRUE; } BOOL UpdateRunningServer( VOID ) /*++ Routine Description: Arguments: None. Return Value: None. --*/ { HWND hExistingWindow; hExistingWindow = FindWindow(g_szClass,NULL); if (!hExistingWindow) { return FALSE; } DBG_TRC(("Updating running service with refresh parameters")); // // Server already running , find it 's window and send a message // with new values of parameters // ::ShowWindow(hExistingWindow,g_fUIPermitted ? SW_SHOWNORMAL : SW_HIDE); // Refresh requested ? if (g_fRefreshDeviceList) { // Refresh device list ::PostMessage(hExistingWindow,STIMON_MSG_REFRESH,1,0L); } if (STIMON_AD_DEFAULT_POLL_INTERVAL != g_uiDefaultPollTimeout) { ::SendMessage(hExistingWindow,STIMON_MSG_SET_PARAMETERS,STIMON_MSG_SET_TIMEOUT,g_uiDefaultPollTimeout); } return TRUE; } STDAPI DllRegisterServer( VOID ) { StiServiceInstall(NULL,NULL); InitWiaDevMan(WiaRegister); return S_OK; } /***************************************************************************** * * @doc INTERNAL * * @func void | DllUnregisterServer | * * Unregister our classes from OLE/COM/ActiveX/whatever its name is. * *****************************************************************************/ STDAPI DllUnregisterServer( VOID ) { InitWiaDevMan(WiaUnregister); StiServiceRemove(); return S_OK; } // // Methods needed for linking to STIRT (stiobj.c calls these). // Some of these methods are simply dummies. // #ifdef __cplusplus extern "C" { #endif BOOL EXTERNAL DllInitializeCOM( void ) { // // Initialize COM. // HRESULT hr = CoInitializeEx(0,COINIT_MULTITHREADED); if (FAILED(hr)) { return FALSE; } return TRUE; } BOOL EXTERNAL DllUnInitializeCOM( void ) { // // Uninitialize COM // CoUninitialize(); return TRUE; } void EXTERNAL DllAddRef(void) { return; } void EXTERNAL DllRelease(void) { return; } #ifdef __cplusplus }; #endif