// // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. // // Copyright (c) Microsoft Corporation. All rights reserved. // // #include "CommandWindow.h" #include // Custom messages for managing the behavior of the window thread. #define WM_EXIT_THREAD WM_USER + 1 #define WM_TOGGLE_CONNECTED_STATUS WM_USER + 2 const WCHAR c_szClassName[] = L"EventWindow"; const WCHAR c_szConnected[] = L"Connected"; const WCHAR c_szDisconnected[] = L"Disconnected"; CCommandWindow::CCommandWindow() : _hWnd(NULL), _hInst(NULL), _fConnected(FALSE), _pProvider(NULL) { } CCommandWindow::~CCommandWindow() { // If we have an active window, we want to post it an exit message. if (_hWnd != NULL) { PostMessage(_hWnd, WM_EXIT_THREAD, 0, 0); _hWnd = NULL; } // We'll also make sure to release any reference we have to the provider. if (_pProvider != NULL) { _pProvider->Release(); _pProvider = NULL; } } // Performs the work required to spin off our message so we can listen for events. HRESULT CCommandWindow::Initialize(__in CSampleProvider *pProvider) { HRESULT hr = S_OK; // Be sure to add a release any existing provider we might have, then add a reference // to the provider we're working with now. if (_pProvider != NULL) { _pProvider->Release(); } _pProvider = pProvider; _pProvider->AddRef(); // Create and launch the window thread. HANDLE hThread = CreateThread(NULL, 0, _ThreadProc, this, 0, NULL); if (hThread == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); } return hr; } // Wraps our internal connected status so callers can easily access it. BOOL CCommandWindow::GetConnectedStatus() { return _fConnected; } // // FUNCTION: _MyRegisterClass() // // PURPOSE: Registers the window class. // // COMMENTS: // // This function and its usage are only necessary if you want this code // to be compatible with Win32 systems prior to the 'RegisterClassEx' // function that was added to Windows 95. It is important to call this function // so that the application will get 'well formed' small icons associated // with it. // HRESULT CCommandWindow::_MyRegisterClass() { WNDCLASSEX wcex = { sizeof(wcex) }; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = _WndProc; wcex.hInstance = _hInst; wcex.hIcon = NULL; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszClassName = c_szClassName; return RegisterClassEx(&wcex) ? S_OK : HRESULT_FROM_WIN32(GetLastError()); } // // FUNCTION: _InitInstance(HINSTANCE, int) // // PURPOSE: Saves instance handle and creates main window // // COMMENTS: // // In this function, we save the instance handle in a global variable and // create and display the main program window. // HRESULT CCommandWindow::_InitInstance() { HRESULT hr = S_OK; // Create our window to receive events. // // This dialog is for demonstration purposes only. It is not recommended to create // dialogs that are visible even before a credential enumerated by this credential // provider is selected. Additionally, any dialogs that are created by a credential // provider should not have a NULL hwndParent, but should be parented to the HWND // returned by ICredentialProviderCredentialEvents::OnCreatingWindow. _hWnd = CreateWindowEx( WS_EX_TOPMOST, c_szClassName, c_szDisconnected, WS_DLGFRAME, 200, 200, 200, 80, NULL, NULL, _hInst, NULL); if (_hWnd == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); } if (SUCCEEDED(hr)) { // Add a button to the window. _hWndButton = CreateWindow(L"Button", L"Press to connect", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 10, 10, 180, 30, _hWnd, NULL, _hInst, NULL); if (_hWndButton == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); } if (SUCCEEDED(hr)) { // Show and update the window. if (!ShowWindow(_hWnd, SW_NORMAL)) { hr = HRESULT_FROM_WIN32(GetLastError()); } if (SUCCEEDED(hr)) { if (!UpdateWindow(_hWnd)) { hr = HRESULT_FROM_WIN32(GetLastError()); } } } } return hr; } // Called from the separate thread to process the next message in the message queue. If // there are no messages, it'll wait for one. BOOL CCommandWindow::_ProcessNextMessage() { // Grab, translate, and process the message. MSG msg; GetMessage(&(msg), _hWnd, 0, 0); TranslateMessage(&(msg)); DispatchMessage(&(msg)); // This section performs some "post-processing" of the message. It's easier to do these // things here because we have the handles to the window, its button, and the provider // handy. switch (msg.message) { // Return to the thread loop and let it know to exit. case WM_EXIT_THREAD: return FALSE; // Toggle the connection status, which also involves updating the UI. case WM_TOGGLE_CONNECTED_STATUS: _fConnected = !_fConnected; if (_fConnected) { SetWindowText(_hWnd, c_szConnected); SetWindowText(_hWndButton, L"Press to disconnect"); } else { SetWindowText(_hWnd, c_szDisconnected); SetWindowText(_hWndButton, L"Press to connect"); } _pProvider->OnConnectStatusChanged(); break; } return TRUE; } // Manages window messages on the window thread. LRESULT CALLBACK CCommandWindow::_WndProc(__in HWND hWnd, __in UINT message, __in WPARAM wParam, __in LPARAM lParam) { switch (message) { // Originally we were going to work with USB keys being inserted and removed, but it // seems as though these events don't get to us on the secure desktop. However, you // might see some messageboxi in CredUI. // TODO: Remove if we can't use from LogonUI. case WM_DEVICECHANGE: MessageBox(NULL, L"Device change", L"Device change", 0); break; // We assume this was the button being clicked. case WM_COMMAND: PostMessage(hWnd, WM_TOGGLE_CONNECTED_STATUS, 0, 0); break; // To play it safe, we hide the window when "closed" and post a message telling the // thread to exit. case WM_CLOSE: ShowWindow(hWnd, SW_HIDE); PostMessage(hWnd, WM_EXIT_THREAD, 0, 0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // Our thread procedure. We actually do a lot of work here that could be put back on the // main thread, such as setting up the window, etc. DWORD WINAPI CCommandWindow::_ThreadProc(__in LPVOID lpParameter) { CCommandWindow *pCommandWindow = static_cast(lpParameter); if (pCommandWindow == NULL) { // TODO: What's the best way to raise this error? return 0; } HRESULT hr = S_OK; // Create the window. pCommandWindow->_hInst = GetModuleHandle(NULL); if (pCommandWindow->_hInst != NULL) { hr = pCommandWindow->_MyRegisterClass(); if (SUCCEEDED(hr)) { hr = pCommandWindow->_InitInstance(); } } else { hr = HRESULT_FROM_WIN32(GetLastError()); } // ProcessNextMessage will pump our message pump and return false if it comes across // a message telling us to exit the thread. if (SUCCEEDED(hr)) { while (pCommandWindow->_ProcessNextMessage()) { } } else { if (pCommandWindow->_hWnd != NULL) { pCommandWindow->_hWnd = NULL; } } return 0; }