/*++ Copyright (c) 2000 Microsoft Corporation Module Name: Rcontrol Main Abstract: This includes WinMain and the WndProc for the system tray icon. Author: Marc Reyhner 7/5/2000 --*/ #include "stdafx.h" #include #ifdef TRC_FILE #undef TRC_FILE #endif #define TRC_FILE "rcm" #include "rcontrol.h" #include "resource.h" #include "exception.h" #include "DirectPlayConnection.h" #include "RemoteDesktopClientSession.h" #include "RemoteDesktopServer.h" #include "RemoteDesktopServerEventSink.h" // This is the message that will will get when someone does something to the // taskbar icon. #define WM_SYSICONMESSAGE (WM_USER + 1) // // // #define MUTEX_NAME (TEXT("Local\\MICROSOFT_SALEM_IM_MUTEX")) // This is a pointer to our instance so that we can pass it off to some // of the menu functions. HINSTANCE g_hInstance; // This is the structure for setting parameters for the taskbar icon. We keep one copy // of it around that we can pass in to Shell_NotifyIcon NOTIFYICONDATA g_iconData; // This is a global pointer to the direct play connection so that // the server can close the DP connection on connect. CDirectPlayConnection *g_DpConnection; // This helper function lauches the executable for the gui passing it the // connection parameters on the command line. static VOID LaunchClient( HINSTANCE hInstance, BSTR parms ); // This helper function runs the system tray icon if this is a server. static VOID DoSystemTray( ); // This is the WndProc for the taskbar icon static LRESULT CALLBACK SysTrayWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); INT WINAPI WinMain( IN HINSTANCE hInstance, IN HINSTANCE hPrevInstance, IN LPSTR lpCmdLine, IN INT nShowCmd) /*++ Routine Description: The entry point for the application. This figures out if we are a server or client and then behaves accordingly. Arguments: hInstance - The instance for this application. hPrevInstance - Previous instance, should be NULL lpCmdLine - Command line for the application. nShowCmd - Flags for how to show the application. Return value: INT - The return code for the application. --*/ { HANDLE lpMutex = NULL; DWORD error; BSTR serverParms, clientParms; DC_BEGIN_FN("WinMain"); serverParms = NULL; clientParms = NULL; g_hInstance = hInstance; CoInitialize(NULL); try { CDirectPlayConnection connection; g_DpConnection = &connection; connection.ConnectToRemoteApplication(); if (connection.IsServer()) { // we are a server CRemoteDesktopServer server; CRemoteDesktopServerEventSink sink; HRESULT hr; lpMutex = CreateMutex(NULL,TRUE,MUTEX_NAME); if (!lpMutex) { throw CException(IDS_INITERRORMUTEX); } error = GetLastError(); if (error == ERROR_ALREADY_EXISTS) { throw CException(IDS_INITALREADYEXISTS); } serverParms = server.StartListening(); hr = server.EventSinkAdvise(&sink); if (hr != S_OK) { throw CException(IDS_ADVISEERROR); } connection.SendConnectionParameters(serverParms); DoSystemTray(); try { server.StopListening(); } catch (CException e) { TRC_ERR((TB,TEXT("Caught Exception: %s"),e.GetErrorStr())); // We are shutting down so just suppress the error. } CloseHandle(lpMutex); } else { // we are the client clientParms = connection.ReceiveConnectionParameters(); connection.DisconnectRemoteApplication(); LaunchClient(hInstance,clientParms); } } catch (CException e) { TCHAR dlgTitle[MAX_STR_LEN]; TRC_ERR((TB,TEXT("Caught Exception: %s"),e.GetErrorStr())); LoadStringSimple(IDS_ERRORDDLGTITLE,dlgTitle); MessageBox(NULL,e.GetErrorStr(),dlgTitle,MB_OK|MB_ICONERROR); } if (clientParms) { delete clientParms; } CoUninitialize(); DC_END_FN(); return 0; } static VOID LaunchClient( IN OUT HINSTANCE hInstance, IN BSTR parms ) /*++ Routine Description: This starts (and does) the client GUI side of the application. DoClientSession will not returne until the client GUI is totally done. Arguments: hInstance - The instance for this application parms - The connection parameters for connecting to the server. Return value: None --*/ { DC_BEGIN_FN("LaunchClient"); CRemoteDesktopClientSession clientSession(hInstance); clientSession.DoClientSession(parms); DC_END_FN(); } static VOID DoSystemTray( ) /*++ Routine Description: This creates the system tray and enters the event loop until the a WM_QUIT message is generated. Arguments: None Return value: None --*/ { MSG msg; WNDCLASS wndClass; ATOM className; HWND hWnd; TCHAR tipText[MAX_STR_LEN]; DC_BEGIN_FN("DoSystemTray"); wndClass.style = 0; wndClass.lpfnWndProc = SysTrayWndProc; wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = g_hInstance; wndClass.hIcon = NULL; wndClass.hCursor = NULL; wndClass.hbrBackground = NULL; wndClass.lpszMenuName = NULL; // This is an internal name so we don't need to localize this. wndClass.lpszClassName = TEXT("SysTrayWindowClass"); className = RegisterClass(&wndClass); hWnd = CreateWindow((LPCTSTR)className,TEXT(""),0,0,0,0,0,/*HWND_MESSAGE*/0,NULL,NULL,NULL); if (hWnd == NULL) { throw "Failed to create system tray icon."; } g_iconData.cbSize = sizeof(g_iconData); g_iconData.hIcon = ::LoadIcon(g_hInstance,MAKEINTRESOURCE(IDI_TRAYICON)); g_iconData.hWnd = hWnd; LoadStringSimple(IDS_TRAYTOOLTIPDISCONNECTED,tipText); // The buffer is only 128 chars. _tcsncpy(g_iconData.szTip,tipText,128 - 1); g_iconData.uCallbackMessage = WM_SYSICONMESSAGE; g_iconData.uFlags = NIF_ICON|NIF_MESSAGE |NIF_TIP; g_iconData.uID = 0; g_iconData.uVersion = NOTIFYICON_VERSION; Shell_NotifyIcon(NIM_ADD,&g_iconData); g_iconData.uFlags = 0; Shell_NotifyIcon(NIM_SETVERSION,&g_iconData); // Do our message loop. while (GetMessage(&msg, (HWND) NULL, 0, 0)>0) { TranslateMessage(&msg); DispatchMessage(&msg); } Shell_NotifyIcon(NIM_DELETE,&g_iconData); DC_END_FN(); } static LRESULT CALLBACK SysTrayWndProc( IN HWND hWnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam ) /*++ Routine Description: Callback for our taskbar icon. This handles all the window messages related to the icon. Arguments: hWnd - Window the message is for uMsg - The message code wParam - First message flag lParam - Second message flag Return value: LRESULT - The result of processing the message --*/ { // This is the window message for when the taskbar // is recreated. We will just initialize it once on // WM_CREATE static UINT g_wmTaskbarCreated = 0; // This is the code we will return. We will initialize it to 1. LRESULT result = 1; DC_BEGIN_FN("SysTrayWndProc"); // If the taskbar is recreates (i.e. explorer crashed) it will // give us this message. When that happens we want to re-create // the taskbar icon. if (uMsg == g_wmTaskbarCreated) { g_iconData.uFlags = NIF_ICON|NIF_MESSAGE |NIF_TIP; Shell_NotifyIcon(NIM_ADD,&g_iconData); g_iconData.uFlags = 0; Shell_NotifyIcon(NIM_SETVERSION,&g_iconData); DC_END_FN(); return 0; } switch (uMsg) { case WM_CREATE: g_wmTaskbarCreated = RegisterWindowMessage(TEXT("TaskbarCreated")); if (!g_wmTaskbarCreated) { result = -1; } else { result = 0; } case WM_SYSICONMESSAGE: switch (lParam) { case WM_CONTEXTMENU: POINT pos; HMENU hMenu, hSubmenu; GetCursorPos(&pos); hMenu = LoadMenu(g_hInstance,MAKEINTRESOURCE(IDR_TRAYMENU)); hSubmenu = GetSubMenu(hMenu,0); SetForegroundWindow(hWnd); TrackPopupMenu(hSubmenu,TPM_VERNEGANIMATION,pos.x,pos.y,0,hWnd,NULL); // this also destroys the submenu DestroyMenu(hMenu); g_iconData.uFlags = 0; Shell_NotifyIcon(NIM_SETFOCUS,&g_iconData); result = 0; break; case WM_LBUTTONDOWN: PostMessage(hWnd,WM_COMMAND,ID_QUIT,NULL); result = 0; break; default: // Any other messages we will ignore result = 0; } break; case WM_COMMAND: switch (wParam) { case ID_QUIT: TCHAR dlgText[MAX_STR_LEN], dlgTitle[MAX_STR_LEN]; LoadStringSimple(IDS_TRAYEXITDLGTEXT,dlgText); LoadStringSimple(IDS_TRAYEXITDLGTITLE,dlgTitle); if (IDYES == MessageBox(hWnd,dlgText,dlgTitle,MB_YESNO)) { PostQuitMessage(0); } result = 0; break; } break; default: // We don't understand this message so do the default. result = DefWindowProc(hWnd,uMsg,wParam,lParam); } DC_END_FN(); return result; } INT LoadStringSimple( IN UINT uID, OUT LPTSTR lpBuffer ) /*++ Routine Description: This will load the given string from the applications string table. If it is longer than MAX_STR_LEN it is truncated. lpBuffer should be at least MAX_STR_LEN characters long. If the string does not exist we return 0 and set the buffer to IDS_STRINGMISSING, if that failes then we set it to the hard coded STR_RES_MISSING. Arguments: uID - Id of the resource to load. lpBuffer - Buffer of MAX_STR_LEN to hold the string Return value: 0 - String resource could not be loaded. postive integer - length of the string loaded. --*/ { INT length; DC_BEGIN_FN("LoadStringSimple"); length = LoadString(g_hInstance,uID,lpBuffer,MAX_STR_LEN); if (length == 0) { length = LoadString(g_hInstance,IDS_STRINGMISSING,lpBuffer,MAX_STR_LEN); if (length == 0) { _tcscpy(lpBuffer,STR_RES_MISSING); } length = 0; } DC_END_FN(); return length; }