2025-11-28 00:35:46 +09:00

285 lines
8.2 KiB
C++

//
// 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 <strsafe.h>
// 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<CCommandWindow *>(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;
}