//--------------------------------------------------------------------- // Copyright (c)1998-1999 Microsoft Corporation, All Rights Reserved. // // irtranp.cpp // // This file holds the main entry points for the IrTran-P service. // IrTranP() is the entry point that starts the listening, and // UninitializeIrTranP() shuts it down (and cleans everything up). // // Author: // // Edward Reus (edwardr) 02-26-98 Initial coding. // // Edward Reus (edwardr) 08-27-99 Finish modifications for // WIA Millennium port. // // Note: Currently the Millennium version will only listen on IrCOMM. // //--------------------------------------------------------------------- #include "precomp.h" #include #define SZ_REG_KEY_IRTRANP "Control Panel\\Infrared\\IrTranP" #define SZ_REG_DISABLE_IRCOMM "DisableIrCOMM" //--------------------------------------------------------------------- // Listen ports array: //--------------------------------------------------------------------- typedef struct _LISTEN_PORT { char *pszService; // Service to start. BOOL fIsIrCOMM; // TRUE iff IrCOMM 9-wire mode. DWORD dwListenStatus; // Status for port. } LISTEN_PORT; static LISTEN_PORT aListenPorts[] = { // Service Name IrCOMM ListenStatus {IRCOMM_9WIRE, TRUE, STATUS_STOPPED }, // {IRTRANP_SERVICE, FALSE, STATUS_STOPPED }, // {IR_TEST_SERVICE, FALSE, STATUS_STOPPED }, 2nd test listen port. {0, FALSE, STATUS_STOPPED } }; #define INDEX_IRCOMM 0 #define INDEX_IRTRANPV1 1 CCONNECTION_MAP *g_pConnectionMap = 0; CIOSTATUS *g_pIoStatus = 0; HANDLE g_hShutdownEvent; BOOL g_fShuttingDownTRANPThread = FALSE; DWORD g_dwTRANPThreadId = 0; extern HINSTANCE g_hInst; // Handle to ircamera.dll USD //--------------------------------------------------------------------- // Globals: //--------------------------------------------------------------------- HANDLE g_UserToken = NULL; HKEY g_hUserKey = NULL; BOOL g_fDisableIrTranPv1 = FALSE; BOOL g_fDisableIrCOMM = FALSE; BOOL g_fExploreOnCompletion = TRUE; BOOL g_fSaveAsUPF = FALSE; BOOL g_fAllowReceives = TRUE; char *g_pszTempPicturesFolder = 0; BOOL g_fWSAStartupCalled = FALSE; void *g_pvIrUsdDevice = 0; // WIA IrUsdDevice Object. //--------------------------------------------------------------------- // GetUserToken() // // The "main" part of irxfer.dll (in ..\irxfer) maintains a token // for user that is currently logged in (if any). //--------------------------------------------------------------------- HANDLE GetUserToken() { return g_UserToken; } //--------------------------------------------------------------------- // GetUserKey() // //--------------------------------------------------------------------- HKEY GetUserKey() { return g_hUserKey; } //--------------------------------------------------------------------- // GetModule() // //--------------------------------------------------------------------- HINSTANCE GetModule() { return g_hInst; } //--------------------------------------------------------------------- // CheckSaveAsUPF() // // Return TRUE iff pictures need to be saved in .UPF (as opposed to // .JPEG) format. //--------------------------------------------------------------------- BOOL CheckSaveAsUPF() { return g_fSaveAsUPF; } //--------------------------------------------------------------------- // CheckExploreOnCompletion() // // Return TRUE iff we want to popup an explorer on the directory // containing the newly transfered pictures. //--------------------------------------------------------------------- BOOL CheckExploreOnCompletion() { return g_fExploreOnCompletion; } /* FlushInputQueue is a private routine to collect and dispatch all * messages in the input queue. It returns TRUE if a WM_QUIT message * was detected in the queue, FALSE otherwise. */ BOOL FlushInputQueue(VOID) { MSG msgTemp; while (PeekMessage(&msgTemp, NULL, 0, 0, PM_REMOVE)) { DispatchMessage(&msgTemp); // If we see a WM_QUIT in the queue, we need to do the same // sort of thing that a modal dialog does: break out of our // waiting, and repost the WM_QUIT to the queue so that the // next message loop up in the app will also see it. if (msgTemp.message == WM_QUIT) { PostQuitMessage((int)msgTemp.wParam); return TRUE; } } return FALSE; } /* WaitAndYield() waits for the specified object using * MsgWaitForMultipleObjects. If messages are received, * they are dispatched and waiting continues. The return * value is the same as from MsgWaitForMultipleObjects. */ DWORD WaitAndYield(HANDLE hObject, DWORD dwTimeout) { DWORD dwTickCount, dwWakeReason, dwTemp; do { /* Flush any messages before we wait. This is because * MsgWaitForMultipleObjects will only return when NEW * messages are put in the queue. */ if (FlushInputQueue()) { dwWakeReason = WAIT_TIMEOUT; break; } // in case we handle messages, we want close to a true timeout if ((dwTimeout != 0) && (dwTimeout != (DWORD)-1)) { // if we can timeout, store the current tick count // every time through dwTickCount = GetTickCount(); } dwWakeReason = MsgWaitForMultipleObjects(1, &hObject, FALSE, dwTimeout, QS_ALLINPUT); // if we got a message, dispatch it, then try again if (dwWakeReason == 1) { // if we can timeout, see if we did before processing the message // that way, if we haven't timed out yet, we'll get at least one // more shot at the event if ((dwTimeout != 0) && (dwTimeout != (DWORD)-1)) { if ((dwTemp = (GetTickCount()-dwTickCount)) >= dwTimeout) { // if we timed out, make us drop through dwWakeReason = WAIT_TIMEOUT; } else { // subtract elapsed time from timeout and continue // (we don't count time spent dispatching message) dwTimeout -= dwTemp; } } if (FlushInputQueue()) { dwWakeReason = WAIT_TIMEOUT; break; } } } while (dwWakeReason == 1); return dwWakeReason; } //--------------------------------------------------------------------- // GetImageDirectory(); // // This is the temporary directory where the pictures sent by the // camera will be held. WIA will then "down load" these to their // final destination (usually this will be My Pictures). // //--------------------------------------------------------------------- CHAR *GetImageDirectory() { char *pszPicturesFolder; char szTempFolder[1+MAX_PATH]; DWORD dwPicturesFolderLen; DWORD dwStatus = NO_ERROR; DWORD dwLen; if (!g_pszTempPicturesFolder) { dwLen = GetTempPath(MAX_PATH,szTempFolder); if ((!dwLen)||(dwLen > MAX_PATH)) { dwStatus = GetLastError(); WIAS_TRACE((g_hInst,"GetUserDirectroy(): GetTempPath() failed: %d",dwStatus)); return NULL; } // // Make sure the directory exists: // if (!CreateDirectory(szTempFolder,0)) { dwStatus = GetLastError(); if ( (dwStatus == ERROR_ALREADY_EXISTS) || (dwStatus == ERROR_ACCESS_DENIED) ) { dwStatus = NO_ERROR; } else if (dwStatus != NO_ERROR) { return 0; } } // // Construct the subdirectory path string that will actually hold the pictures: // This will be something like: C:\temp\irtranp // dwPicturesFolderLen = sizeof(CHAR)*( strlen(szTempFolder) + sizeof(SZ_SLASH) + sizeof(SZ_SUBDIRECTORY) + 1 ); g_pszTempPicturesFolder = (CHAR*)AllocateMemory(dwPicturesFolderLen); if (!g_pszTempPicturesFolder) { return 0; // Memory allocation failed! } strcpy(g_pszTempPicturesFolder,szTempFolder); if (szTempFolder[dwLen-1] != SLASH) { strcat(g_pszTempPicturesFolder,SZ_SLASH); } strcat(g_pszTempPicturesFolder,SZ_SUBDIRECTORY); // // Make sure the subdirectory exists: // if (!CreateDirectory(g_pszTempPicturesFolder,0)) { dwStatus = GetLastError(); if (dwStatus == ERROR_ALREADY_EXISTS) { dwStatus = NO_ERROR; } else if (dwStatus != NO_ERROR) { return 0; } } } pszPicturesFolder = g_pszTempPicturesFolder; return pszPicturesFolder; } //--------------------------------------------------------------------- // ReceivesAllowed() // // Using the IR configuration window (available from the wireless network // icon in the control panel) you can disable communications with IR // devices. This function returns the state of IR communications, FALSE // is disabled, TRUE is enabled. //--------------------------------------------------------------------- BOOL ReceivesAllowed() { return g_fAllowReceives; } //--------------------------------------------------------------------- // SetupListenConnection() // //--------------------------------------------------------------------- DWORD SetupListenConnection( IN CHAR *pszService, IN BOOL fIsIrCOMM, IN HANDLE hIoCompletionPort ) { DWORD dwStatus = NO_ERROR; CIOPACKET *pIoPacket; CCONNECTION *pConnection; // See if the connection already exists: if (g_pConnectionMap->LookupByServiceName(pszService)) { return NO_ERROR; } // Makeup and initialize a new connection object: pConnection = new CCONNECTION; if (!pConnection) { return E_OUTOFMEMORY; } dwStatus = pConnection->InitializeForListen( pszService, fIsIrCOMM, hIoCompletionPort ); if (dwStatus) { WIAS_ERROR((g_hInst,"SetupForListen(): InitializeForListen(%s) failed: %d",pszService, dwStatus)); return dwStatus; } pIoPacket = new CIOPACKET; if (!pIoPacket) { WIAS_ERROR((g_hInst,"SetupForListen(): new CIOPACKET failed")); delete pConnection; return E_OUTOFMEMORY; } // Setup the IO packet: dwStatus = pIoPacket->Initialize( PACKET_KIND_LISTEN, pConnection->GetListenSocket(), INVALID_SOCKET, hIoCompletionPort ); if (dwStatus != NO_ERROR) { return dwStatus; } pConnection->SetSocket(pIoPacket->GetSocket()); if (!g_pConnectionMap->Add(pConnection,pIoPacket->GetListenSocket())) { WIAS_ERROR((g_hInst,"SetupForListen(): Add(pConnection) ConnectionMap Failed.")); return 1; } return dwStatus; } //--------------------------------------------------------------------- // TeardownListenConnection() // //--------------------------------------------------------------------- DWORD TeardownListenConnection( IN char *pszService ) { DWORD dwStatus = NO_ERROR; CCONNECTION *pConnection; // Look for the connection associated with the service name: pConnection = g_pConnectionMap->LookupByServiceName(pszService); if (pConnection) { g_pConnectionMap->RemoveConnection(pConnection); pConnection->CloseSocket(); pConnection->CloseListenSocket(); } return dwStatus; } //--------------------------------------------------------------------- // EnableDisableIrCOMM() // //--------------------------------------------------------------------- DWORD EnableDisableIrCOMM( IN BOOL fDisable ) { DWORD dwStatus; if (fDisable) { dwStatus = TeardownListenConnection( aListenPorts[INDEX_IRCOMM].pszService); WIAS_ERROR((g_hInst,"IrTranP: TeardownListenConnection(%s): %d", aListenPorts[INDEX_IRCOMM].pszService,dwStatus)); } else { dwStatus = SetupListenConnection( aListenPorts[INDEX_IRCOMM].pszService, aListenPorts[INDEX_IRCOMM].fIsIrCOMM, g_pIoStatus->GetIoCompletionPort() ); WIAS_TRACE((g_hInst,"IrTranP: SetupListenConnection(%s): %d", aListenPorts[INDEX_IRCOMM].pszService, dwStatus)); } return dwStatus; } //--------------------------------------------------------------------- // EnableDisableIrTranPv1() // //--------------------------------------------------------------------- DWORD EnableDisableIrTranPv1( IN BOOL fDisable ) { DWORD dwStatus; if (fDisable) { dwStatus = TeardownListenConnection( aListenPorts[INDEX_IRTRANPV1].pszService); } else { dwStatus = SetupListenConnection( aListenPorts[INDEX_IRTRANPV1].pszService, aListenPorts[INDEX_IRTRANPV1].fIsIrCOMM, g_pIoStatus->GetIoCompletionPort() ); } return dwStatus; } //--------------------------------------------------------------------- // IrTranp() // //--------------------------------------------------------------------- DWORD WINAPI IrTranP( IN void *pvIrUsdDevice ) { int i = 0; WSADATA wsaData; WORD wVersion = MAKEWORD(1,1); DWORD dwStatus; CCONNECTION *pConnection; g_dwTRANPThreadId = ::GetCurrentThreadId(); // // Initialize Memory Management: // dwStatus = InitializeMemory(); if (dwStatus) { WIAS_ERROR((g_hInst,"IrTranP(): InitializeMemory() failed: %d\n",dwStatus)); return dwStatus; } // // This directory will be set as needed. It is only non-null in the case // where we are re-starting the IrTran-P thread: // if (g_pszTempPicturesFolder) { FreeMemory(g_pszTempPicturesFolder); g_pszTempPicturesFolder = 0; } // // Initialize Winsock2 if neccessary: // if (!g_fWSAStartupCalled) { if (WSAStartup(wVersion,&wsaData) == SOCKET_ERROR) { dwStatus = WSAGetLastError(); WIAS_ERROR((g_hInst,"WSAStartup(0x%x) failed with error %d\n", wVersion, dwStatus )); return dwStatus; } g_fWSAStartupCalled = TRUE; } // Event used to signal back to "main" thread that the // IrTran-P thread is exiting. // // NoSecurity, Auto-Reset, Initially Not Signaled, No Name. // g_hShutdownEvent = CreateEventA( NULL, FALSE, FALSE, NULL ); if (!g_hShutdownEvent) { dwStatus = GetLastError(); WIAS_ERROR((g_hInst,"IrTranP(): CreateEvent() Failed: %d",dwStatus)); return dwStatus; } // Create/initialize a object to keep track of the threading... g_pIoStatus = new CIOSTATUS; if (!g_pIoStatus) { WIAS_ERROR((g_hInst,"new CIOSTATUS failed.")); return E_OUTOFMEMORY; } dwStatus = g_pIoStatus->Initialize(); if (dwStatus != NO_ERROR) { WIAS_ERROR((g_hInst,"g_pIoStatus->Initialize(): Failed: %d",dwStatus)); return dwStatus; } // Need to keep track of the open sockets and the number of // pending IOs on each... g_pConnectionMap = new CCONNECTION_MAP; if (!g_pConnectionMap) { WIAS_ERROR((g_hInst,"new CCONNECTION_MAP failed.")); return E_OUTOFMEMORY; } if (!g_pConnectionMap->Initialize()) { return 1; } // Create a CIOPACKET for each defined listen port. These are // what we will listen on. // // BUGBUG Should we really loop indefintely setting up connection or establish some limit on retries ? VS // while (!g_fShuttingDownTRANPThread ) { dwStatus = SetupListenConnection( aListenPorts[INDEX_IRCOMM].pszService, aListenPorts[INDEX_IRCOMM].fIsIrCOMM, g_pIoStatus->GetIoCompletionPort() ); if (dwStatus) { WIAS_TRACE((g_hInst,"SetupListenConnection(%s) Status: %d",aListenPorts[i].pszService,dwStatus)); // // BUGBUG Analyze error and stop processing if it doesn't make sense !!! VS // } else { WIAS_TRACE((g_hInst,"SetupListenConnection(%s) Ready",aListenPorts[i].pszService)); aListenPorts[INDEX_IRCOMM].dwListenStatus = STATUS_RUNNING; break; } // Wait for timeout period, but wake up if we need to stop // Sleep(5000); WaitAndYield(g_hShutdownEvent,5000); } if (!g_fShuttingDownTRANPThread) { // // Wait on incomming connections and data, then process it. // g_pvIrUsdDevice = pvIrUsdDevice; dwStatus = ProcessIoPackets(g_pIoStatus); } // // Shutting down // g_pvIrUsdDevice = 0; WIAS_TRACE((g_hInst,"ProcessIoPackets(): dwStatus: %d",dwStatus)); // // Cleanup and close any open handles: // while (pConnection=g_pConnectionMap->RemoveNext()) { delete pConnection; } delete g_pConnectionMap; g_pConnectionMap = 0; delete g_pIoStatus; g_pIoStatus = 0; // Signal the shutdown event that the IrTran-P thread is exiting: if (g_hShutdownEvent) { SetEvent(g_hShutdownEvent); } return dwStatus; } //--------------------------------------------------------------------- // IrTranPEnableIrCOMMFailed() // //--------------------------------------------------------------------- void IrTranPEnableIrCOMMFailed( DWORD dwErrorCode ) { DWORD dwStatus; // An error occured on enable, make sure the registry value // is set to disable (so UI will match the actual state). HKEY hKey = 0; HKEY hUserKey = GetUserKey(); HANDLE hUserToken = GetUserToken(); HINSTANCE hInstance = GetModule(); DWORD dwDisposition; if (RegCreateKeyEx(hUserKey, SZ_REG_KEY_IRTRANP, 0, // reserved MBZ 0, // class name REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, 0, // security attributes &hKey, &dwDisposition)) { WIAS_TRACE((g_hInst,"IrTranP: RegCreateKeyEx(): '%' failed %d", SZ_REG_KEY_IRTRANP, GetLastError())); } if ( (hKey) && (hUserToken) && (::ImpersonateLoggedOnUser(hUserToken))) { DWORD dwDisableIrCOMM = TRUE; dwStatus = RegSetValueEx(hKey, SZ_REG_DISABLE_IRCOMM, 0, REG_DWORD, (UCHAR*)&dwDisableIrCOMM, sizeof(dwDisableIrCOMM) ); if (dwStatus != ERROR_SUCCESS) { WIAS_TRACE((g_hInst,"IrTranP: Can't set DisableIrCOMM to TRUE in registry. Error: %d",dwStatus)); } ::RevertToSelf(); } if (hKey) { RegCloseKey(hKey); } #if FALSE WCHAR *pwszMessage = NULL; WCHAR *pwszCaption = NULL; DWORD dwFlags = ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE); dwStatus = FormatMessageW(dwFlags, hInstance, CAT_IRTRANP, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), (LPTSTR)(&pwszCaption), 0, // Minimum size to allocate. NULL); // va_list args... if (dwStatus == 0) { #ifdef DBG_ERROR DbgPrint("IrTranP: FormatMessage() failed: %d\n",GetLastError() ); #endif return; } // // Hack: Make sure the caption doesn't end with newline-formfeed... // WCHAR *pwsz = pwszCaption; while (*pwsz) { if (*pwsz < 0x20) // 0x20 is always a space... { *pwsz = 0; break; } else { pwsz++; } } WCHAR wszErrorCode[20]; WCHAR *pwszErrorCode = (WCHAR*)wszErrorCode; wsprintfW(wszErrorCode,L"%d",dwErrorCode); dwFlags = ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_HMODULE); dwStatus = FormatMessageW(dwFlags, hInstance, MC_IRTRANP_IRCOM_FAILED, MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), (LPTSTR)(&pwszMessage), 0, // Minimum size to allocate. (va_list*)&pwszErrorCode); if (dwStatus == 0) { #ifdef DBG_ERROR DbgPrint("IrTranP: FormatMessage() failed: %d\n",GetLastError() ); #endif if (pwszMessage) { LocalFree(pwszMessage); } return; } dwStatus = MessageBoxW( NULL, pwszMessage, pwszCaption, (MB_OK|MB_ICONERROR|MB_SETFOREGROUND|MB_TOPMOST) ); if (pwszMessage) { LocalFree(pwszMessage); } if (pwszCaption) { LocalFree(pwszCaption); } #endif } //--------------------------------------------------------------------- // UninitializeIrTranP() // //--------------------------------------------------------------------- BOOL UninitializeIrTranP( HANDLE hThread ) { BOOL fSuccess = TRUE; DWORD dwStatus; HANDLE hIoCP = g_pIoStatus->GetIoCompletionPort(); g_fShuttingDownTRANPThread = TRUE; // Inform TRANP thread it has to die ::PostThreadMessage(g_dwTRANPThreadId,WM_QUIT,0,0); if (hIoCP != INVALID_HANDLE_VALUE) { if (!PostQueuedCompletionStatus(hIoCP,0,IOKEY_SHUTDOWN,0)) { // Unexpected error... dwStatus = GetLastError(); } while (WAIT_TIMEOUT == WaitForSingleObject(g_hShutdownEvent,0)) { Sleep(100); } CloseHandle(g_hShutdownEvent); } // // TRANP thread should be dead by now . In case it isn't wait on it's handle and terminate // Otherwise we have a small chance of unloading DLL before thread is dead, shutting down WIA service // dwStatus = ::WaitForSingleObject(hThread,100); if (dwStatus == WAIT_TIMEOUT) { // Have to be rude // BUGBUG Assert ::TerminateThread(hThread,NOERROR); } // Shutdown memory management: dwStatus = UninitializeMemory(); return fSuccess; } #ifdef RUN_AS_EXE //--------------------------------------------------------------------- // main() // //--------------------------------------------------------------------- int __cdecl main( int argc, char **argv ) { DWORD dwStatus; printf("IrTran-P: Start\n"); dwStatus = IrTranP( NULL ); if (dwStatus) { printf("IrTran-P: Status: 0x%x (%d)\n",dwStatus,dwStatus); } return 0; } #endif