mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5815 lines
159 KiB
5815 lines
159 KiB
//=============================================================================
|
|
// Copyright (c) 2000 Microsoft Corporation
|
|
//
|
|
// controls.cpp
|
|
//
|
|
// User interface control classes.
|
|
//
|
|
// Created 02/29/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
#include "precomp.hpp"
|
|
#include "controls.hpp"
|
|
#include "resource.h"
|
|
#include "utils.hpp"
|
|
#include <shlguid.h>
|
|
#include <htmlhelp.h>
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Values
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// These are the positions of the children controls of the credential control,
|
|
// in DLUs:
|
|
|
|
// make more space to localize the edit control tags.
|
|
#define SIZEFIX 20
|
|
|
|
#define CREDUI_CONTROL_USERNAME_STATIC_X 0
|
|
#define CREDUI_CONTROL_USERNAME_STATIC_Y 2
|
|
#define CREDUI_CONTROL_USERNAME_STATIC_WIDTH (48 + SIZEFIX)
|
|
#define CREDUI_CONTROL_USERNAME_STATIC_HEIGHT 8
|
|
|
|
#define CREDUI_CONTROL_USERNAME_X (50 + SIZEFIX)
|
|
#define CREDUI_CONTROL_USERNAME_Y 0
|
|
#define CREDUI_CONTROL_USERNAME_WIDTH (121 - SIZEFIX)
|
|
#define CREDUI_CONTROL_USERNAME_HEIGHT 96
|
|
|
|
#define CREDUI_CONTROL_VIEW_X 175
|
|
#define CREDUI_CONTROL_VIEW_Y 0
|
|
#define CREDUI_CONTROL_VIEW_WIDTH 13
|
|
#define CREDUI_CONTROL_VIEW_HEIGHT 13
|
|
|
|
#define CREDUI_CONTROL_PASSWORD_STATIC_X 0
|
|
#define CREDUI_CONTROL_PASSWORD_STATIC_Y 19
|
|
#define CREDUI_CONTROL_PASSWORD_STATIC_WIDTH (48 + SIZEFIX)
|
|
#define CREDUI_CONTROL_PASSWORD_STATIC_HEIGHT 8
|
|
|
|
#define CREDUI_CONTROL_PASSWORD_X (50 + SIZEFIX)
|
|
#define CREDUI_CONTROL_PASSWORD_Y 17
|
|
#define CREDUI_CONTROL_PASSWORD_WIDTH (121 - SIZEFIX)
|
|
#define CREDUI_CONTROL_PASSWORD_HEIGHT 12
|
|
|
|
#define CREDUI_CONTROL_SAVE_X (50 + SIZEFIX)
|
|
#define CREDUI_CONTROL_SAVE_Y 36
|
|
#define CREDUI_CONTROL_SAVE_WIDTH 138
|
|
#define CREDUI_CONTROL_SAVE_HEIGHT 10
|
|
|
|
// Use a common maximum string length for certificate display names:
|
|
|
|
#define CREDUI_MAX_CERT_NAME_LENGTH 256
|
|
#define CREDUI_MAX_CMDLINE_MSG_LENGTH 256
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Global Variables
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CLSID CreduiStringArrayClassId = // 82BD0E67-9FEA-4748-8672-D5EFE5B779B0
|
|
{
|
|
0x82BD0E67,
|
|
0x9FEA,
|
|
0x4748,
|
|
{0x86, 0x72, 0xD5, 0xEF, 0xE5, 0xB7, 0x79, 0xB0}
|
|
};
|
|
|
|
// Balloon tip infos for PasswordBox control:
|
|
CONST CREDUI_BALLOON_TIP_INFO CreduiCapsLockTipInfo =
|
|
{
|
|
CreduiStrings.CapsLockTipTitle,
|
|
CreduiStrings.CapsLockTipText,
|
|
TTI_WARNING, 90, 76
|
|
};
|
|
// Balloon tip infos for Credential control:
|
|
|
|
CONST CREDUI_BALLOON_TIP_INFO CreduiBackwardsTipInfo =
|
|
{
|
|
CreduiStrings.BackwardsTipTitle,
|
|
CreduiStrings.BackwardsTipText,
|
|
TTI_ERROR, 90, 76
|
|
};
|
|
|
|
WCHAR CreduiCustomTipTitle[CREDUI_MAX_BALLOON_TITLE_LENGTH + 1];
|
|
WCHAR CreduiCustomTipMessage[CREDUI_MAX_BALLOON_MESSAGE_LENGTH + 1];
|
|
|
|
CREDUI_BALLOON_TIP_INFO CreduiCustomTipInfo =
|
|
{
|
|
CreduiCustomTipTitle,
|
|
CreduiCustomTipMessage,
|
|
TTI_INFO, 90, 76
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CreduiBalloonTip Class Implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//=============================================================================
|
|
// CreduiBalloonTip::CreduiBalloonTip
|
|
//
|
|
// Created 02/24/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
CreduiBalloonTip::CreduiBalloonTip()
|
|
{
|
|
Window = NULL;
|
|
|
|
ParentWindow = NULL;
|
|
ControlWindow = NULL;
|
|
|
|
TipInfo = NULL;
|
|
|
|
Visible = FALSE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiBalloonTip::~CreduiBalloonTip
|
|
//
|
|
// Created 02/24/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
CreduiBalloonTip::~CreduiBalloonTip()
|
|
{
|
|
if (Window != NULL)
|
|
{
|
|
DestroyWindow(Window);
|
|
Window = NULL;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiBalloonTip::Init
|
|
//
|
|
// Creates and initializes the balloon window.
|
|
//
|
|
// Arguments:
|
|
// instance (in) - this module
|
|
// parentWindow (in) - the parent of the tool tip window
|
|
//
|
|
// Returns TRUE on success or FALSE otherwise.
|
|
//
|
|
// Created 02/24/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiBalloonTip::Init(
|
|
HINSTANCE instance,
|
|
HWND parentWindow
|
|
)
|
|
{
|
|
if (Window != NULL)
|
|
{
|
|
DestroyWindow(Window);
|
|
|
|
Window = NULL;
|
|
|
|
ParentWindow = NULL;
|
|
ControlWindow = NULL;
|
|
|
|
TipInfo = NULL;
|
|
|
|
Visible = FALSE;
|
|
}
|
|
|
|
Window = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL,
|
|
WS_POPUP | TTS_NOPREFIX | TTS_BALLOON,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
CW_USEDEFAULT, CW_USEDEFAULT,
|
|
parentWindow, NULL, instance, NULL);
|
|
|
|
// Only assign class member values once we have successfully created the
|
|
// window:
|
|
|
|
if (Window != NULL)
|
|
{
|
|
ParentWindow = parentWindow;
|
|
TipInfo = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiBalloonTip::SetInfo
|
|
//
|
|
// Sets the tool tip information and adds or updates the tool.
|
|
//
|
|
// Returns TRUE on success or FALSE otherwise.
|
|
//
|
|
// Created 03/02/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL CreduiBalloonTip::SetInfo(
|
|
HWND controlWindow,
|
|
CONST CREDUI_BALLOON_TIP_INFO *tipInfo
|
|
)
|
|
{
|
|
//if ((controlWindow != ControlWindow) || (tipInfo != TipInfo))
|
|
{
|
|
TOOLINFO info;
|
|
|
|
ZeroMemory(&info, sizeof info);
|
|
|
|
info.cbSize = sizeof info;
|
|
info.hwnd = ParentWindow;
|
|
info.uId = reinterpret_cast<WPARAM>(ParentWindow);
|
|
|
|
// If the tool already exists, hide it, then update the information.
|
|
// Otherwise, add the tool now:
|
|
|
|
if (SendMessage(Window, TTM_GETTOOLINFO, 0,
|
|
reinterpret_cast<LPARAM>(&info)))
|
|
{
|
|
if (Visible)
|
|
{
|
|
Hide();
|
|
}
|
|
|
|
ZeroMemory(&info, sizeof info);
|
|
|
|
info.cbSize = sizeof info;
|
|
info.hwnd = ParentWindow;
|
|
info.uId = reinterpret_cast<WPARAM>(ParentWindow);
|
|
|
|
info.uFlags = TTF_IDISHWND | TTF_TRACK;
|
|
info.hinst = NULL;
|
|
info.lpszText = const_cast<WCHAR *>(tipInfo->Text);
|
|
info.lParam = 0;
|
|
|
|
SendMessage(Window, TTM_SETTOOLINFO, 0,
|
|
reinterpret_cast<LPARAM>(&info));
|
|
}
|
|
else
|
|
{
|
|
info.uFlags = TTF_IDISHWND | TTF_TRACK;
|
|
info.hinst = NULL;
|
|
info.lpszText = const_cast<WCHAR *>(tipInfo->Text);
|
|
info.lParam = 0;
|
|
|
|
if (!SendMessage(Window, TTM_ADDTOOL, 0,
|
|
reinterpret_cast<LPARAM>(&info)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
SendMessage(Window, TTM_SETTITLE, tipInfo->Icon,
|
|
reinterpret_cast<LPARAM>(tipInfo->Title));
|
|
|
|
TipInfo = const_cast<CREDUI_BALLOON_TIP_INFO *>(tipInfo);
|
|
ControlWindow = controlWindow;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiBalloonTip::Show
|
|
//
|
|
// Updates the position of the balloon window, and then displays it.
|
|
//
|
|
// Returns TRUE on success or FALSE otherwise.
|
|
//
|
|
// Created 02/24/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiBalloonTip::Show()
|
|
{
|
|
if (!Visible && IsWindowEnabled(ControlWindow))
|
|
{
|
|
SetFocus(ControlWindow);
|
|
|
|
RECT rect;
|
|
GetWindowRect(ControlWindow, &rect);
|
|
|
|
SendMessage(Window,
|
|
TTM_TRACKPOSITION, 0,
|
|
MAKELONG(
|
|
rect.left + TipInfo->XPercent *
|
|
(rect.right - rect.left) / 100,
|
|
rect.top + TipInfo->YPercent *
|
|
(rect.bottom - rect.top) / 100));
|
|
|
|
TOOLINFO info;
|
|
|
|
ZeroMemory(&info, sizeof info);
|
|
|
|
info.cbSize = sizeof info;
|
|
info.hwnd = ParentWindow;
|
|
info.uId = reinterpret_cast<WPARAM>(ParentWindow);
|
|
|
|
SendMessage(Window, TTM_TRACKACTIVATE, TRUE,
|
|
reinterpret_cast<LPARAM>(&info));
|
|
|
|
Visible = TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiBalloonTip::Hide
|
|
//
|
|
// Hides the balloon window.
|
|
//
|
|
// Returns TRUE on success or FALSE otherwise.
|
|
//
|
|
// Created 02/24/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiBalloonTip::Hide()
|
|
{
|
|
if (Visible)
|
|
{
|
|
SendMessage(Window, TTM_TRACKACTIVATE, (WPARAM) FALSE, 0);
|
|
|
|
Visible = FALSE;
|
|
|
|
if (ParentWindow)
|
|
{
|
|
HWND hD = GetParent(ParentWindow);
|
|
if (hD)
|
|
{
|
|
InvalidateRgn(hD,NULL,FALSE);
|
|
UpdateWindow(hD);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CreduiPasswordBox Class Implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//=============================================================================
|
|
// CreduiPasswordBox::CreduiPasswordBox
|
|
//
|
|
// Created 06/06/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
CreduiPasswordBox::CreduiPasswordBox()
|
|
{
|
|
OriginalMessageHandler = NULL;
|
|
|
|
Window = NULL;
|
|
PasswordFont = NULL;
|
|
BalloonTip = NULL;
|
|
CapsLockTipInfo = NULL;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiPasswordBox::~CreduiPasswordBox
|
|
//
|
|
// Created 06/06/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
CreduiPasswordBox::~CreduiPasswordBox()
|
|
{
|
|
if (PasswordFont != NULL)
|
|
{
|
|
DeleteObject(static_cast<HGDIOBJ>(PasswordFont));
|
|
PasswordFont = NULL;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiPasswordBox::Init
|
|
//
|
|
// Created 06/06/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiPasswordBox::Init(
|
|
HWND window,
|
|
CreduiBalloonTip *balloonTip,
|
|
CONST CREDUI_BALLOON_TIP_INFO *capsLockTipInfo,
|
|
HFONT passwordFont,
|
|
WCHAR passwordChar)
|
|
{
|
|
// If passwordFont was passed, use it here, but leave the class
|
|
// PasswordFont NULL so it will not be cleaned up by the destructor. If
|
|
// it was not passed, create a font here, which will be freed by the
|
|
// destructor:
|
|
|
|
if (passwordFont == NULL)
|
|
{
|
|
passwordFont = PasswordFont;
|
|
}
|
|
|
|
Window = window;
|
|
|
|
// If we still failed to create the font, and are not planning to display
|
|
// balloon tips, then there's nothing do to, just return.
|
|
|
|
if ((passwordFont == NULL) && (balloonTip == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (balloonTip != NULL)
|
|
{
|
|
if (capsLockTipInfo == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BalloonTip = balloonTip;
|
|
CapsLockTipInfo = capsLockTipInfo;
|
|
|
|
OriginalMessageHandler =
|
|
reinterpret_cast<WNDPROC>(
|
|
GetWindowLongPtr(Window, GWLP_WNDPROC));
|
|
|
|
if (OriginalMessageHandler != NULL)
|
|
{
|
|
SetLastError(ERROR_SUCCESS);
|
|
|
|
if ((SetWindowLongPtr(
|
|
Window,
|
|
GWLP_USERDATA,
|
|
reinterpret_cast<LONG_PTR>(this)) == 0) &&
|
|
(GetLastError() != ERROR_SUCCESS))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
|
|
if (SetWindowLongPtr(
|
|
Window,
|
|
GWLP_WNDPROC,
|
|
reinterpret_cast<LONG_PTR>(MessageHandlerCallback)) &&
|
|
(GetLastError() != ERROR_SUCCESS))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (passwordFont != NULL)
|
|
{
|
|
SendMessage(Window,
|
|
WM_SETFONT,
|
|
reinterpret_cast<WPARAM>(passwordFont),
|
|
0);
|
|
SendMessage(Window, EM_SETPASSWORDCHAR, passwordChar, 0);
|
|
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
// CreduiPasswordBox::MessageHandler
|
|
//
|
|
// This callback function just calls through to the original, except in a
|
|
// special case where Caps Lock is pressed. We then check to see if the tip is
|
|
// currently being displayed, and if the new state of Caps Lock is off, hide
|
|
// the tip.
|
|
//
|
|
// Arguments:
|
|
// message (in)
|
|
// wParam (in)
|
|
// lParam (in)
|
|
//
|
|
// Returns the result of calling the original message handler in every case.
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
LRESULT
|
|
CreduiPasswordBox::MessageHandler(
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
switch (message)
|
|
{
|
|
case WM_KEYDOWN:
|
|
if (wParam == VK_CAPITAL)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
if (BalloonTip->IsVisible())
|
|
{
|
|
BalloonTip->Hide();
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_SETFOCUS:
|
|
// Make sure no one can steal the focus while a user is
|
|
// entering their password:
|
|
|
|
LockSetForegroundWindow(LSFW_LOCK);
|
|
|
|
// If the Caps Lock key is down, notify the user, unless the
|
|
// password tip is already visible:
|
|
|
|
if (!BalloonTip->IsVisible() && CreduiIsCapsLockOn())
|
|
{
|
|
// BalloonTip->SetInfo(Window, CapsLockTipInfo);
|
|
// BalloonTip->Show();
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_PASTE:
|
|
if (BalloonTip->IsVisible())
|
|
{
|
|
BalloonTip->Hide();
|
|
}
|
|
break;
|
|
|
|
case WM_KILLFOCUS:
|
|
if (BalloonTip->IsVisible())
|
|
{
|
|
BalloonTip->Hide();
|
|
}
|
|
|
|
// Make sure other processes can set foreground window
|
|
// once again:
|
|
|
|
LockSetForegroundWindow(LSFW_UNLOCK);
|
|
|
|
break;
|
|
}
|
|
|
|
return CallWindowProc(OriginalMessageHandler,
|
|
Window,
|
|
message,
|
|
wParam,
|
|
lParam);
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiPasswordBox::MessageHandlerCallback
|
|
//
|
|
// This calls through to CreduiPasswordBox::MessageHandler, from the this
|
|
// pointer.
|
|
//
|
|
// Arguments:
|
|
// passwordWindow (in)
|
|
// message (in)
|
|
// wParam (in)
|
|
// lParam (in)
|
|
//
|
|
// Returns the result of calling the original message handler in every case.
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
LRESULT
|
|
CALLBACK
|
|
CreduiPasswordBox::MessageHandlerCallback(
|
|
HWND passwordWindow,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
CreduiPasswordBox *that =
|
|
reinterpret_cast<CreduiPasswordBox *>(
|
|
GetWindowLongPtr(passwordWindow, GWLP_USERDATA));
|
|
|
|
ASSERT(that != NULL);
|
|
ASSERT(that->BalloonTip != NULL);
|
|
ASSERT(that->CapsLockTipInfo != NULL);
|
|
|
|
ASSERT(that->Window == passwordWindow);
|
|
|
|
return that->MessageHandler(message, wParam, lParam);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CreduiStringArrayFactory Class Implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//=============================================================================
|
|
// CreduiStringArrayFactory::CreduiStringArrayFactory
|
|
//
|
|
// Created 04/03/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
CreduiStringArrayFactory::CreduiStringArrayFactory()
|
|
{
|
|
ReferenceCount = 1;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArrayFactory::~CreduiStringArrayFactory
|
|
//
|
|
// Created 04/03/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
CreduiStringArrayFactory::~CreduiStringArrayFactory()
|
|
{
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArrayFactory::QueryInterface (IUnknown)
|
|
//
|
|
// Created 04/03/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
HRESULT
|
|
CreduiStringArrayFactory::QueryInterface(
|
|
CONST IID &interfaceId,
|
|
VOID **outInterface
|
|
)
|
|
{
|
|
if ((interfaceId == IID_IUnknown) || (interfaceId == IID_IClassFactory))
|
|
{
|
|
*outInterface = static_cast<void *>(static_cast<IClassFactory *>(this));
|
|
}
|
|
else
|
|
{
|
|
*outInterface = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static_cast<IUnknown *>(*outInterface)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArrayFactory::Addref (IUnknown)
|
|
//
|
|
// Created 04/03/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
ULONG
|
|
CreduiStringArrayFactory::AddRef()
|
|
{
|
|
return InterlockedIncrement(reinterpret_cast<LONG *>(&ReferenceCount));
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArrayFactory::Release (IUnknown)
|
|
//
|
|
// Created 04/03/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
ULONG
|
|
CreduiStringArrayFactory::Release()
|
|
{
|
|
if (InterlockedDecrement(reinterpret_cast<LONG *>(&ReferenceCount)) > 0)
|
|
{
|
|
return ReferenceCount;
|
|
}
|
|
|
|
delete this;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiClassFactory::CreateInstance (IClassFactory)
|
|
//
|
|
// Created 04/03/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
HRESULT
|
|
CreduiStringArrayFactory::CreateInstance(
|
|
IUnknown *unknownOuter,
|
|
CONST IID &interfaceId,
|
|
VOID **outInterface
|
|
)
|
|
{
|
|
if (unknownOuter != NULL)
|
|
{
|
|
return CLASS_E_NOAGGREGATION;
|
|
}
|
|
|
|
CreduiStringArray *stringArray = new CreduiStringArray;
|
|
|
|
if (stringArray == NULL)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
HRESULT result = stringArray->QueryInterface(interfaceId, outInterface);
|
|
|
|
// Release the string array object in any case, because of the
|
|
// QueryInterface succeeded, it already took another reference count on
|
|
// the object:
|
|
|
|
stringArray->Release();
|
|
|
|
return result;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiClassFactory::LockServer (IClassFactory)
|
|
//
|
|
// Created 04/03/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
HRESULT
|
|
CreduiStringArrayFactory::LockServer(
|
|
BOOL lock
|
|
)
|
|
{
|
|
if (lock)
|
|
{
|
|
InterlockedIncrement(reinterpret_cast<LONG *>(
|
|
&CreduiComReferenceCount));
|
|
}
|
|
else
|
|
{
|
|
InterlockedDecrement(reinterpret_cast<LONG *>(
|
|
&CreduiComReferenceCount));
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CreduiStringArray Class Implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//=============================================================================
|
|
// CreduiStringArray::CreduiStringArray
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
CreduiStringArray::CreduiStringArray()
|
|
{
|
|
ReferenceCount = 1;
|
|
Index = 0;
|
|
Count = 0;
|
|
MaxCount = 0;
|
|
Array = NULL;
|
|
|
|
InterlockedIncrement(reinterpret_cast<LONG *>(&CreduiComReferenceCount));
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArray::~CreduiStringArray
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
CreduiStringArray::~CreduiStringArray()
|
|
{
|
|
if (Array != NULL)
|
|
{
|
|
while (Count > 0)
|
|
{
|
|
delete [] Array[--Count];
|
|
}
|
|
|
|
delete [] Array;
|
|
MaxCount = 0;
|
|
Count = 0;
|
|
}
|
|
|
|
InterlockedDecrement(reinterpret_cast<LONG *>(&CreduiComReferenceCount));
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArray::Init
|
|
//
|
|
// Initializes the string array.
|
|
//
|
|
// Arguments:
|
|
// count (in) - number of strings in the array
|
|
//
|
|
// Returns TRUE on success or FALSE otherwise.
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiStringArray::Init(
|
|
UINT count
|
|
)
|
|
{
|
|
Count = 0;
|
|
MaxCount = count;
|
|
|
|
Array = new WCHAR *[count];
|
|
|
|
if (Array != NULL)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// Clean up:
|
|
|
|
MaxCount = 0;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArray::Find
|
|
//
|
|
// Searches for a string in the array.
|
|
//
|
|
// Arguments:
|
|
// string (in) - string to search for
|
|
//
|
|
// Returns TRUE if the string was found or FALSE otherwise.
|
|
//
|
|
// Created 02/27/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL CreduiStringArray::Find(
|
|
CONST WCHAR *string
|
|
)
|
|
{
|
|
// Search for the string:
|
|
|
|
for (UINT i = 0; i < Count; ++i)
|
|
{
|
|
ASSERT(Array[i] != NULL);
|
|
|
|
if (lstrcmpi(Array[i], string) == 0)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArray::Add
|
|
//
|
|
// Adds a string to the array.
|
|
//
|
|
// Arguments:
|
|
// string (in) - string to add
|
|
//
|
|
// Returns TRUE if the string was added or FALSE otherwise.
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiStringArray::Add(
|
|
CONST WCHAR *string
|
|
)
|
|
{
|
|
// The array does not grow, so once we reach the limit, no more:
|
|
|
|
if (Count < MaxCount)
|
|
{
|
|
Array[Count] = new WCHAR[lstrlen(string) + 1];
|
|
|
|
if (Array[Count] != NULL)
|
|
{
|
|
lstrcpy(Array[Count++], string);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArray::QueryInterface (IUnknown)
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
HRESULT
|
|
CreduiStringArray::QueryInterface(
|
|
CONST IID &interfaceId,
|
|
VOID **outInterface
|
|
)
|
|
{
|
|
if ((interfaceId == IID_IUnknown) || (interfaceId == IID_IEnumString))
|
|
{
|
|
*outInterface = static_cast<void *>(static_cast<IEnumString *>(this));
|
|
}
|
|
else
|
|
{
|
|
*outInterface = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static_cast<IUnknown *>(*outInterface)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArray::Addref (IUnknown)
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
ULONG
|
|
CreduiStringArray::AddRef()
|
|
{
|
|
return InterlockedIncrement(reinterpret_cast<LONG *>(&ReferenceCount));
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArray::Release (IUnknown)
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
ULONG
|
|
CreduiStringArray::Release()
|
|
{
|
|
if (InterlockedDecrement(reinterpret_cast<LONG *>(&ReferenceCount)) > 0)
|
|
{
|
|
return ReferenceCount;
|
|
}
|
|
|
|
delete this;
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArray::Next (IEnumString)
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
HRESULT
|
|
CreduiStringArray::Next(
|
|
ULONG count,
|
|
LPOLESTR *array,
|
|
ULONG *countFetched
|
|
)
|
|
{
|
|
if ((count > 1) && (countFetched == NULL))
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
count = min(count, Count - Index);
|
|
|
|
for (UINT i = 0; i < count; ++i)
|
|
{
|
|
array[i] = static_cast<WCHAR *>(CoTaskMemAlloc(
|
|
(sizeof (WCHAR)) * (lstrlen(Array[Index]) + 1)));
|
|
|
|
if (array[i] != NULL)
|
|
{
|
|
lstrcpy(array[i], Array[Index]);
|
|
}
|
|
else
|
|
{
|
|
while (i > 0)
|
|
{
|
|
CoTaskMemFree(array[--i]);
|
|
array[i] = NULL;
|
|
}
|
|
|
|
if (countFetched != NULL)
|
|
{
|
|
*countFetched = 0;
|
|
}
|
|
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
Index++;
|
|
}
|
|
|
|
if (countFetched != NULL)
|
|
{
|
|
*countFetched = count;
|
|
}
|
|
|
|
return (count > 0) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArray::Skip (IEnumString)
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
HRESULT
|
|
CreduiStringArray::Skip(
|
|
ULONG
|
|
)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArray::Reset (IEnumString)
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
HRESULT
|
|
CreduiStringArray::Reset()
|
|
{
|
|
Index = 0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiStringArray::Clone (IEnumString)
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
HRESULT
|
|
CreduiStringArray::Clone(
|
|
IEnumString **
|
|
)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CreduiAutoCompleteComboBox Class Implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//=============================================================================
|
|
// CreduiAutoCompleteComboBox::CreduiAutoCompleteComboBox
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
CreduiAutoCompleteComboBox::CreduiAutoCompleteComboBox()
|
|
{
|
|
Window = NULL;
|
|
ImageList = NULL;
|
|
StringArray = NULL;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiAutoCompleteComboBox::~CreduiAutoCompleteComboBox
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
CreduiAutoCompleteComboBox::~CreduiAutoCompleteComboBox()
|
|
{
|
|
if (StringArray != NULL)
|
|
{
|
|
StringArray->Release();
|
|
StringArray = NULL;
|
|
}
|
|
|
|
if (ImageList != NULL)
|
|
{
|
|
ImageList_Destroy(ImageList);
|
|
ImageList = NULL;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiAutoCompleteComboBox::Init
|
|
//
|
|
// Initializes the shell auto complete list control for the given combo box,
|
|
// and sets the auto complete string list.
|
|
//
|
|
// Arguments:
|
|
// instance (in)
|
|
// comboBoxWindow (in)
|
|
// stringCount (in)
|
|
// imageListResourceId (in) - optional image list for the combo box
|
|
//
|
|
// Returns TRUE on success or FALSE otherwise.
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiAutoCompleteComboBox::Init(
|
|
HMODULE instance,
|
|
HWND comboBoxWindow,
|
|
UINT stringCount,
|
|
INT imageListResourceId,
|
|
INT initialImage
|
|
)
|
|
{
|
|
Window = comboBoxWindow;
|
|
|
|
if (imageListResourceId != 0)
|
|
{
|
|
ImageList = ImageList_LoadImage(
|
|
instance,
|
|
MAKEINTRESOURCE(imageListResourceId),
|
|
0, 16, RGB(0, 128, 128), IMAGE_BITMAP,
|
|
LR_DEFAULTSIZE | LR_SHARED);
|
|
|
|
if (ImageList != NULL)
|
|
{
|
|
SendMessage(Window,
|
|
CBEM_SETIMAGELIST,
|
|
0, reinterpret_cast<LPARAM>(ImageList));
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL success = FALSE;
|
|
|
|
if (stringCount > 0)
|
|
{
|
|
HRESULT result =
|
|
CoCreateInstance(CreduiStringArrayClassId,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IEnumString,
|
|
reinterpret_cast<VOID **>(&StringArray));
|
|
|
|
if (SUCCEEDED(result))
|
|
{
|
|
if (StringArray->Init(stringCount))
|
|
{
|
|
success = TRUE;
|
|
}
|
|
else
|
|
{
|
|
StringArray->Release();
|
|
StringArray = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
success = TRUE;
|
|
}
|
|
|
|
if (success == TRUE)
|
|
{
|
|
COMBOBOXEXITEMW item;
|
|
|
|
item.mask = CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
|
|
item.iItem = -1;
|
|
item.iImage = initialImage;
|
|
item.iSelectedImage = initialImage;
|
|
|
|
SendMessage(Window, CBEM_SETITEM, 0,
|
|
reinterpret_cast<LPARAM>(&item));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
if (ImageList != NULL)
|
|
{
|
|
ImageList_Destroy(ImageList);
|
|
ImageList = NULL;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiAutoCompleteComboBox::Add
|
|
//
|
|
// Returns the index of the new item or -1 on failure.
|
|
//
|
|
// Created 02/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
INT
|
|
CreduiAutoCompleteComboBox::Add(
|
|
WCHAR *string,
|
|
INT image,
|
|
BOOL autoComplete,
|
|
BOOL addUnique,
|
|
INT indexBefore,
|
|
INT indent
|
|
)
|
|
{
|
|
INT index = -1;
|
|
|
|
if (addUnique)
|
|
{
|
|
index = (INT) SendMessage(Window, CB_FINDSTRINGEXACT, 0,
|
|
reinterpret_cast<LPARAM>(string));
|
|
}
|
|
|
|
if (index == -1)
|
|
{
|
|
if (!autoComplete || StringArray->Add(string))
|
|
{
|
|
COMBOBOXEXITEMW item;
|
|
|
|
item.mask = CBEIF_TEXT | CBEIF_INDENT;
|
|
item.iItem = indexBefore;
|
|
item.pszText = string;
|
|
item.iIndent = indent;
|
|
|
|
if (ImageList != NULL)
|
|
{
|
|
item.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
|
|
item.iImage = image;
|
|
item.iSelectedImage = image;
|
|
}
|
|
|
|
index = (INT) SendMessage(Window, CBEM_INSERTITEM, 0,
|
|
reinterpret_cast<LPARAM>(&item));
|
|
}
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiAutoCompleteComboBox::Update
|
|
//
|
|
// Updates an existing item. This does not update the associated string for
|
|
// auto complete items.
|
|
//
|
|
// Created 04/15/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiAutoCompleteComboBox::Update(
|
|
INT index,
|
|
WCHAR *string,
|
|
INT image
|
|
)
|
|
{
|
|
COMBOBOXEXITEMW item;
|
|
|
|
item.iItem = index;
|
|
|
|
// Use CBEM_SETITEM in these cases:
|
|
//
|
|
// 1. We're updating the default (-1) item.
|
|
// 2. The dropdown is closed.
|
|
//
|
|
// For other cases, we delete and recreate the item for the desired
|
|
// result.
|
|
|
|
BOOL isDropped = (BOOL) SendMessage(Window, CB_GETDROPPEDSTATE, 0, 0);
|
|
|
|
if ((index == -1) || !isDropped)
|
|
{
|
|
item.mask = CBEIF_TEXT;
|
|
item.pszText = string;
|
|
|
|
if (ImageList != NULL)
|
|
{
|
|
item.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
|
|
item.iImage = image;
|
|
item.iSelectedImage = image;
|
|
}
|
|
|
|
if (SendMessage(Window, CBEM_SETITEM, 0,
|
|
reinterpret_cast<LPARAM>(&item)) != 0)
|
|
{
|
|
RECT rect;
|
|
|
|
GetClientRect(Window, &rect);
|
|
InvalidateRect(Window, &rect, FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
item.mask = CBEIF_IMAGE | CBEIF_INDENT | CBEIF_SELECTEDIMAGE;
|
|
|
|
if (SendMessage(Window, CBEM_GETITEM,
|
|
0, reinterpret_cast<LPARAM>(&item)))
|
|
{
|
|
item.mask |= CBEIF_TEXT;
|
|
item.pszText = string;
|
|
|
|
LPARAM data = SendMessage(Window, CB_GETITEMDATA, index, 0);
|
|
|
|
if (ImageList != NULL)
|
|
{
|
|
item.mask |= CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
|
|
item.iImage = image;
|
|
item.iSelectedImage = image;
|
|
}
|
|
|
|
SendMessage(Window, CBEM_DELETEITEM, index, 0);
|
|
|
|
index = (INT) SendMessage(Window, CBEM_INSERTITEM, 0,
|
|
reinterpret_cast<LPARAM>(&item));
|
|
|
|
if (index != -1)
|
|
{
|
|
SendMessage(Window, CB_SETITEMDATA, index, data);
|
|
|
|
INT current = (INT) SendMessage(Window, CB_GETCURSEL, 0, 0);
|
|
|
|
if (current == index)
|
|
{
|
|
SendMessage(Window, CB_SETCURSEL, current, 0);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiAutoCompleteComboBox::Enable
|
|
//
|
|
// Created 02/27/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiAutoCompleteComboBox::Enable()
|
|
{
|
|
BOOL success = TRUE;
|
|
|
|
if (StringArray != NULL)
|
|
{
|
|
success = FALSE;
|
|
|
|
IAutoComplete2 *autoCompleteInterface;
|
|
|
|
HRESULT result =
|
|
CoCreateInstance(
|
|
CLSID_AutoComplete,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IAutoComplete2,
|
|
reinterpret_cast<void **>(&autoCompleteInterface));
|
|
|
|
if (SUCCEEDED(result))
|
|
{
|
|
result = autoCompleteInterface->Init((HWND)
|
|
SendMessage(Window, CBEM_GETEDITCONTROL, 0, 0),
|
|
StringArray, NULL, NULL);
|
|
|
|
if (SUCCEEDED(result))
|
|
{
|
|
result = autoCompleteInterface->SetOptions(ACO_AUTOSUGGEST);
|
|
|
|
if (SUCCEEDED(result))
|
|
{
|
|
success = TRUE;
|
|
}
|
|
else
|
|
{
|
|
CreduiDebugLog("CreduiAutoCompleteComboBox::Enable: "
|
|
"SetOptions failed: 0x%08X\n", result);
|
|
}
|
|
}
|
|
|
|
autoCompleteInterface->Release();
|
|
autoCompleteInterface = NULL;
|
|
}
|
|
else
|
|
{
|
|
CreduiDebugLog(
|
|
"CreduiAutoCompleteComboBox::Enable: "
|
|
"CoCreateInstance CLSID_AutoComplete failed: 0x%08X\n",
|
|
result);
|
|
}
|
|
|
|
StringArray->Release();
|
|
StringArray = NULL;
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CreduiIconParentWindow Class Implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CONST WCHAR *CreduiIconParentWindow::ClassName = L"CreduiIconParentWindow";
|
|
HINSTANCE CreduiIconParentWindow::Instance = NULL;
|
|
LONG CreduiIconParentWindow::Registered = FALSE;
|
|
|
|
//=============================================================================
|
|
// CreduiIconParentWindow::CreduiIconParentWindow
|
|
//
|
|
// Created 02/29/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
CreduiIconParentWindow::CreduiIconParentWindow()
|
|
{
|
|
Window = NULL;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiIconParentWindow::~CreduiIconParentWindow
|
|
//
|
|
// Created 02/29/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
CreduiIconParentWindow::~CreduiIconParentWindow()
|
|
{
|
|
if (Window != NULL)
|
|
{
|
|
DestroyWindow(Window);
|
|
Window = NULL;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiIconParentWindow::Register
|
|
//
|
|
// Set the instance to allow registration, which will be deferred until a
|
|
// window needs to be created.
|
|
//
|
|
// Arguments:
|
|
// instance (in)
|
|
//
|
|
// Returns TRUE on success or FALSE otherwise.
|
|
//
|
|
// Created 04/16/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiIconParentWindow::Register(
|
|
HINSTANCE instance
|
|
)
|
|
{
|
|
Instance = instance;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiIconParentWindow::Unegister
|
|
//
|
|
// Unregisters the window class, if registered.
|
|
//
|
|
// Returns TRUE on success or FALSE otherwise.
|
|
//
|
|
// Created 04/16/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL CreduiIconParentWindow::Unregister()
|
|
{
|
|
if (InterlockedCompareExchange(&Registered, FALSE, TRUE))
|
|
{
|
|
return UnregisterClass(ClassName, Instance);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiIconParentWindow::Init
|
|
//
|
|
// Registers the window class, if not already registered, and creates the
|
|
// window.
|
|
//
|
|
// Arguments:
|
|
// instance (in) - module to load the icon from
|
|
// iconResourceId (in)
|
|
//
|
|
// Returns TRUE on success or FALSE otherwise.
|
|
//
|
|
// Created 02/29/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiIconParentWindow::Init(
|
|
HINSTANCE instance,
|
|
UINT iconResourceId
|
|
)
|
|
{
|
|
WNDCLASS windowClass;
|
|
|
|
ZeroMemory(&windowClass, sizeof windowClass);
|
|
|
|
if (!InterlockedCompareExchange(&Registered, TRUE, FALSE))
|
|
{
|
|
windowClass.lpfnWndProc = DefWindowProc;
|
|
windowClass.hInstance = Instance;
|
|
windowClass.hIcon =
|
|
LoadIcon(instance, MAKEINTRESOURCE(iconResourceId));
|
|
windowClass.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
|
|
windowClass.lpszClassName = ClassName;
|
|
|
|
InterlockedExchange(&Registered, RegisterClass(&windowClass) != 0);
|
|
|
|
if (!InterlockedCompareExchange(&Registered, FALSE, FALSE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
Window = CreateWindow(
|
|
L"CreduiIconParentWindow",
|
|
NULL,
|
|
WS_CAPTION | WS_SYSMENU,
|
|
0, 0, 0, 0,
|
|
NULL, NULL, instance, NULL);
|
|
|
|
return (Window != NULL);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CreduiCredentialControl Class Implementation
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CONST WCHAR *CreduiCredentialControl::ClassName = WC_CREDENTIAL;
|
|
HINSTANCE CreduiCredentialControl::Instance = NULL;
|
|
LONG CreduiCredentialControl::Registered = FALSE;
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::CreduiCredentialControl
|
|
//
|
|
// Created 06/20/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
CreduiCredentialControl::CreduiCredentialControl()
|
|
{
|
|
IsInitialized = FALSE;
|
|
|
|
DisabledControlMask = 0;
|
|
|
|
Window = NULL;
|
|
Style = 0;
|
|
|
|
UserNameStaticWindow = NULL;
|
|
UserNameControlWindow = NULL;
|
|
ViewCertControlWindow = NULL;
|
|
PasswordStaticWindow = NULL;
|
|
PasswordControlWindow = NULL;
|
|
|
|
FirstPaint = FALSE;
|
|
ShowBalloonTip = FALSE;
|
|
|
|
IsAutoComplete = FALSE;
|
|
NoEditUserName = FALSE;
|
|
KeepUserName = FALSE;
|
|
|
|
IsPassport = FALSE;
|
|
|
|
CertHashes = NULL;
|
|
CertCount = 0;
|
|
CertBaseInComboBox = 0;
|
|
UserNameCertHash = NULL;
|
|
SmartCardBaseInComboBox = 0;
|
|
SmartCardReadCount = 0;
|
|
IsChangingUserName = FALSE;
|
|
IsChangingPassword = FALSE;
|
|
|
|
UserNameSelection = 0;
|
|
ScardUiHandle = NULL;
|
|
|
|
DoingCommandLine = FALSE;
|
|
TargetName = NULL;
|
|
InitialUserName = NULL;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::~CreduiCredentialControl
|
|
//
|
|
// Created 06/20/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
CreduiCredentialControl::~CreduiCredentialControl()
|
|
{
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::Register
|
|
//
|
|
// Arguments:
|
|
// instance (in)
|
|
//
|
|
// Returns TRUE on success or FALSE otherwise.
|
|
//
|
|
// Created 06/20/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::Register(
|
|
HINSTANCE instance
|
|
)
|
|
{
|
|
Instance = instance;
|
|
|
|
WNDCLASS windowClass;
|
|
|
|
ZeroMemory(&windowClass, sizeof windowClass);
|
|
|
|
if (!InterlockedCompareExchange(&Registered, TRUE, FALSE))
|
|
{
|
|
windowClass.style = CS_GLOBALCLASS;
|
|
windowClass.lpfnWndProc = MessageHandlerCallback;
|
|
windowClass.cbWndExtra = sizeof (CreduiCredentialControl *);
|
|
windowClass.hInstance = Instance;
|
|
windowClass.hIcon = NULL;
|
|
windowClass.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
|
|
windowClass.lpszClassName = ClassName;
|
|
|
|
InterlockedExchange(&Registered, RegisterClass(&windowClass) != 0);
|
|
|
|
if (!InterlockedCompareExchange(&Registered, FALSE, FALSE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::Unegister
|
|
//
|
|
// Unregisters the window class, if registered.
|
|
//
|
|
// Returns TRUE on success or FALSE otherwise.
|
|
//
|
|
// Created 06/20/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL CreduiCredentialControl::Unregister()
|
|
{
|
|
if (InterlockedCompareExchange(&Registered, FALSE, TRUE))
|
|
{
|
|
return UnregisterClass(ClassName, Instance);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::ViewCertificate
|
|
//
|
|
// Views the certificate at index in our combo box.
|
|
//
|
|
// Arguments:
|
|
// index (in) - index in the user name combo box
|
|
//
|
|
// Returns TRUE if the certificate was viewed, otherwise FALSE.
|
|
//
|
|
// Created 03/27/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::ViewCertificate(
|
|
INT index
|
|
)
|
|
{
|
|
BOOL success = FALSE;
|
|
|
|
if (index < CertBaseInComboBox)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CONST CERT_CONTEXT *certContext = NULL;
|
|
HCERTSTORE certStore = NULL;
|
|
|
|
// If this is not a smart card, open the MY store and find the certificate
|
|
// from the hash. Otherwise, just grab the certificate context from the
|
|
// CERT_ENUM structure:
|
|
|
|
if ((SmartCardBaseInComboBox > 0) &&
|
|
(index >= SmartCardBaseInComboBox))
|
|
{
|
|
CERT_ENUM *certEnum =
|
|
reinterpret_cast<CERT_ENUM *>(
|
|
SendMessage(UserNameControlWindow,
|
|
CB_GETITEMDATA, index, 0));
|
|
|
|
if (certEnum != NULL)
|
|
{
|
|
certContext = certEnum->pCertContext;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
certStore = CertOpenSystemStore(NULL, L"MY");
|
|
|
|
if (certStore != NULL)
|
|
{
|
|
CRYPT_HASH_BLOB hashBlob;
|
|
|
|
hashBlob.cbData = CERT_HASH_LENGTH;
|
|
hashBlob.pbData = reinterpret_cast<BYTE *>(
|
|
CertHashes[index - CertBaseInComboBox]);
|
|
|
|
certContext = CertFindCertificateInStore(
|
|
certStore,
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
0,
|
|
CERT_FIND_SHA1_HASH,
|
|
&hashBlob,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
// If we found a certificate context, view the certificate:
|
|
|
|
if (certContext != NULL)
|
|
{
|
|
// Now, show the certificate with the common UI:
|
|
|
|
CRYPTUI_VIEWCERTIFICATE_STRUCT certViewInfo;
|
|
|
|
ZeroMemory(&certViewInfo, sizeof certViewInfo);
|
|
certViewInfo.dwSize = sizeof certViewInfo;
|
|
certViewInfo.hwndParent = Window;
|
|
certViewInfo.pCertContext = certContext;
|
|
|
|
BOOL changed;
|
|
changed = FALSE;
|
|
CryptUIDlgViewCertificate(&certViewInfo, &changed);
|
|
|
|
// Get the name again, in case it changed. However, skip this if this
|
|
// is a card reader, and is now invalid:
|
|
|
|
COMBOBOXEXITEMW item;
|
|
BOOL updateName = TRUE;
|
|
|
|
if (index >= SmartCardBaseInComboBox)
|
|
{
|
|
item.mask = CBEIF_IMAGE;
|
|
item.iItem = index;
|
|
|
|
if (!SendMessage(UserNameControlWindow,
|
|
CBEM_GETITEM,
|
|
0,
|
|
reinterpret_cast<LPARAM>(&item)) ||
|
|
(item.iImage == IMAGE_SMART_CARD_MISSING))
|
|
{
|
|
updateName = FALSE;
|
|
}
|
|
}
|
|
|
|
if (updateName)
|
|
{
|
|
WCHAR displayName[CREDUI_MAX_CERT_NAME_LENGTH];
|
|
|
|
if (!CreduiGetCertificateDisplayName(
|
|
certContext,
|
|
displayName,
|
|
CREDUI_MAX_CERT_NAME_LENGTH,
|
|
CreduiStrings.Certificate,
|
|
CERT_NAME_FRIENDLY_DISPLAY_TYPE))
|
|
{
|
|
lstrcpyn(displayName,
|
|
CreduiStrings.Certificate,
|
|
CREDUI_MAX_CERT_NAME_LENGTH);
|
|
}
|
|
|
|
COMBOBOXEXITEMW item;
|
|
|
|
item.mask = CBEIF_TEXT;
|
|
item.iItem = index;
|
|
item.pszText = displayName;
|
|
|
|
SendMessage(UserNameControlWindow,
|
|
CBEM_SETITEM,
|
|
0,
|
|
reinterpret_cast<LPARAM>(&item));
|
|
}
|
|
|
|
success = TRUE;
|
|
}
|
|
|
|
// If we opened a store, free the certificate and close the store:
|
|
|
|
if (certStore != NULL)
|
|
{
|
|
if (certContext != NULL)
|
|
{
|
|
CertFreeCertificateContext(certContext);
|
|
}
|
|
|
|
if (!CertCloseStore(certStore, 0))
|
|
{
|
|
CreduiDebugLog("CreduiCredentialControl::ViewCertificate: "
|
|
"CertCloseStore failed: %u\n", GetLastError());
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::AddCertificates
|
|
//
|
|
// Adds interesting certificates to the combo box, and allocates an array of
|
|
// hashes to match. The hash is all we need to store the credential, and can
|
|
// be used to get a CERT_CONTEXT later to view the certificate.
|
|
//
|
|
// Assume CertCount is 0 upon entry.
|
|
//
|
|
// Stack space is used for temporary storage of hashes, since each hash is
|
|
// only 160 bits. We use a linked list structure, so including the next
|
|
// pointer and worst case alignment (8-byte) on 64-bit, the maximum structure
|
|
// size is 32 bytes. We don't want to consume too much stack space, so limit
|
|
// the number of entries to 256, which will consume up to 8 KB of stack space.
|
|
//
|
|
// Returns TRUE if at least one interesting certificate exists, and all were
|
|
// added to the combo box without error. Otherwise, returns FALSE.
|
|
//
|
|
// Created 03/25/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::AddCertificates()
|
|
{
|
|
BOOL success = FALSE;
|
|
|
|
ASSERT(CertCount == 0);
|
|
|
|
HCERTSTORE certStore = CertOpenSystemStore(NULL, L"MY");
|
|
|
|
if (certStore != NULL)
|
|
{
|
|
struct HASH_ENTRY
|
|
{
|
|
UCHAR Hash[CERT_HASH_LENGTH];
|
|
HASH_ENTRY *Next;
|
|
};
|
|
|
|
HASH_ENTRY *hashList = NULL;
|
|
HASH_ENTRY *current = NULL;
|
|
HASH_ENTRY *next = NULL;
|
|
|
|
CONST CERT_CONTEXT *certContext = NULL;
|
|
|
|
// NOTE: Currently, add all client authentication certificates. This
|
|
// should be revisited.
|
|
|
|
CHAR *ekUsageIdentifiers[] = {
|
|
szOID_PKIX_KP_CLIENT_AUTH
|
|
};
|
|
|
|
CERT_ENHKEY_USAGE ekUsage = { 1, ekUsageIdentifiers };
|
|
|
|
// We allow a maximum of 256 certificates to be added. This is a
|
|
// reasonable limit, given the current user interface. If this is an
|
|
// unreasonable limit for the personal certificate store, then this
|
|
// can always be changed.
|
|
|
|
while (CertCount < 256)
|
|
{
|
|
certContext =
|
|
CertFindCertificateInStore(
|
|
certStore,
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
|
|
CERT_FIND_ENHKEY_USAGE,
|
|
static_cast<VOID *>(&ekUsage),
|
|
certContext);
|
|
|
|
if (certContext != NULL)
|
|
{
|
|
DWORD length = CERT_HASH_LENGTH;
|
|
|
|
// Only allocate a new entry if necessary. Something may have
|
|
// failed from the previous loop iteration, so we can just
|
|
// reuse the entry allocated then:
|
|
|
|
if (next == NULL)
|
|
{
|
|
// Wrap the alloca in an exception handler because it will
|
|
// throw a stack overflow exception on failure. Of course,
|
|
// of we're out of stack space, we may not even be able to
|
|
// clean up properly without throwing an exception.
|
|
|
|
__try
|
|
{
|
|
next = static_cast<HASH_ENTRY *>(
|
|
alloca(sizeof HASH_ENTRY));
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
next = NULL;
|
|
}
|
|
|
|
// If this fails, for whatever reason, break out of the
|
|
// loop:
|
|
|
|
if (next == NULL)
|
|
{
|
|
CertFreeCertificateContext(certContext);
|
|
break;
|
|
}
|
|
|
|
next->Next = NULL;
|
|
}
|
|
|
|
if (!CertGetCertificateContextProperty(
|
|
certContext,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
static_cast<VOID *>(&next->Hash),
|
|
&length))
|
|
{
|
|
// If we failed to get the hash for this certificate, just
|
|
// ignore it and continue with the next. The memory we
|
|
// allocation for this entry will be used on the next
|
|
// iteration if we do not set it to NULL.
|
|
|
|
continue;
|
|
}
|
|
|
|
if (CreduiIsRemovableCertificate(certContext))
|
|
{
|
|
// If this certificate requires a removable component,
|
|
// such as a smart card, then skip it. We will enumerate
|
|
// these later.
|
|
|
|
continue;
|
|
}
|
|
|
|
WCHAR displayName[CREDUI_MAX_CERT_NAME_LENGTH];
|
|
|
|
if (!CreduiGetCertificateDisplayName(
|
|
certContext,
|
|
displayName,
|
|
CREDUI_MAX_CERT_NAME_LENGTH,
|
|
CreduiStrings.Certificate,
|
|
CERT_NAME_FRIENDLY_DISPLAY_TYPE))
|
|
{
|
|
lstrcpyn(displayName,
|
|
CreduiStrings.Certificate,
|
|
CREDUI_MAX_CERT_NAME_LENGTH);
|
|
}
|
|
|
|
// Add the certificate to the combo box. Certificate names may
|
|
// not be unique, so allow duplicates:
|
|
|
|
if (UserNameComboBox.Add(
|
|
displayName,
|
|
CreduiIsExpiredCertificate(certContext) ?
|
|
IMAGE_CERT_EXPIRED :
|
|
IMAGE_CERT,
|
|
FALSE,
|
|
FALSE) == -1)
|
|
{
|
|
CertFreeCertificateContext(certContext);
|
|
break;
|
|
}
|
|
|
|
// Everything succeeded, so add the certificate to our list:
|
|
|
|
if (current == NULL)
|
|
{
|
|
current = next;
|
|
hashList = current;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(current->Next == NULL);
|
|
|
|
current->Next = next;
|
|
current = current->Next;
|
|
}
|
|
|
|
if (current == NULL)
|
|
{
|
|
CertFreeCertificateContext(certContext);
|
|
break;
|
|
}
|
|
|
|
// Set next to NULL so we will allocate new memory on the
|
|
// next iteration:
|
|
|
|
next = NULL;
|
|
|
|
CertCount++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (CertCount > 0)
|
|
{
|
|
current = hashList;
|
|
|
|
// Now, allocate the final array of certificates. We allocate
|
|
// this in a single block to help avoid thrashing the heap:
|
|
|
|
CertHashes = new UCHAR [CertCount][CERT_HASH_LENGTH];
|
|
|
|
if (CertHashes != NULL)
|
|
{
|
|
for (UINT i = 0; i < CertCount; ++i)
|
|
{
|
|
CopyMemory(CertHashes[i],
|
|
current->Hash,
|
|
CERT_HASH_LENGTH);
|
|
|
|
current = current->Next;
|
|
}
|
|
|
|
success = TRUE;
|
|
}
|
|
}
|
|
|
|
CertCloseStore(certStore, 0);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::FindSmartCardInComboBox
|
|
//
|
|
// Finds a smart card entry in the user name combo box based on a CERT_ENUM.
|
|
//
|
|
// Arguments:
|
|
// certEnum (in)
|
|
//
|
|
// Returns the index of the smart card or -1 if not found.
|
|
//
|
|
// Created 04/15/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
INT
|
|
CreduiCredentialControl::FindSmartCardInComboBox(
|
|
CERT_ENUM *certEnum
|
|
)
|
|
{
|
|
UINT count = (UINT) SendMessage(UserNameControlWindow, CB_GETCOUNT, 0, 0);
|
|
|
|
if (count == CB_ERR)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
CERT_ENUM *findCertEnum;
|
|
|
|
for (UINT i = SmartCardBaseInComboBox; i < count; ++i)
|
|
{
|
|
findCertEnum =
|
|
reinterpret_cast<CERT_ENUM *>(
|
|
SendMessage(UserNameControlWindow, CB_GETITEMDATA, i, 0));
|
|
|
|
ASSERT(findCertEnum != NULL);
|
|
|
|
if (lstrcmpi(findCertEnum->pszReaderName,
|
|
certEnum->pszReaderName) == 0)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::RemoveSmartCardFromComboBox
|
|
//
|
|
// Removes all entries for this smart card from the user name combo box.
|
|
//
|
|
// Arguments:
|
|
// certEnum (in)
|
|
// removeParent (in)
|
|
//
|
|
// Created 07/12/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
VOID
|
|
CreduiCredentialControl::RemoveSmartCardFromComboBox(
|
|
CERT_ENUM *certEnum,
|
|
BOOL removeParent
|
|
)
|
|
{
|
|
INT count = (INT) SendMessage(UserNameControlWindow, CB_GETCOUNT, 0, 0);
|
|
INT current = (INT) SendMessage(UserNameControlWindow,
|
|
CB_GETCURSEL, 0, 0);
|
|
|
|
if (count != CB_ERR)
|
|
{
|
|
CERT_ENUM *findCertEnum;
|
|
BOOL parentEntry = TRUE;
|
|
BOOL currentRemoved = FALSE;
|
|
|
|
for (INT i = SmartCardBaseInComboBox; i < count; ++i)
|
|
{
|
|
findCertEnum =
|
|
reinterpret_cast<CERT_ENUM *>(
|
|
SendMessage(UserNameControlWindow, CB_GETITEMDATA, i, 0));
|
|
|
|
ASSERT(findCertEnum != NULL);
|
|
|
|
if (lstrcmpi(findCertEnum->pszReaderName,
|
|
certEnum->pszReaderName) == 0)
|
|
{
|
|
if (parentEntry)
|
|
{
|
|
parentEntry = FALSE;
|
|
|
|
if (!removeParent)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (current == i)
|
|
{
|
|
currentRemoved = TRUE;
|
|
}
|
|
|
|
SendMessage(
|
|
UserNameControlWindow,
|
|
CBEM_DELETEITEM,
|
|
i,
|
|
0);
|
|
|
|
i--, count--;
|
|
}
|
|
else if (!parentEntry)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (currentRemoved)
|
|
{
|
|
if (removeParent)
|
|
{
|
|
IsChangingUserName = TRUE;
|
|
SendMessage(UserNameControlWindow, CB_SETCURSEL, -1, 0);
|
|
UserNameComboBox.Update(-1, L"", IMAGE_USERNAME);
|
|
IsChangingUserName = FALSE;
|
|
|
|
IsChangingPassword = TRUE;
|
|
SetWindowText(PasswordControlWindow, NULL);
|
|
IsChangingPassword = FALSE;
|
|
|
|
OnUserNameSelectionChange();
|
|
}
|
|
else
|
|
{
|
|
IsChangingUserName = TRUE;
|
|
SendMessage(UserNameControlWindow, CB_SETCURSEL, --i, 0);
|
|
IsChangingUserName = FALSE;
|
|
}
|
|
|
|
OnUserNameSelectionChange();
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::HandleSmartCardMessages
|
|
//
|
|
// Handle smart card messages.
|
|
//
|
|
// Arguments:
|
|
// message (in)
|
|
// certEnum (in)
|
|
//
|
|
// Returns TRUE if the message was handled or FALSE otherwise.
|
|
//
|
|
// Created 04/14/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::HandleSmartCardMessages(
|
|
UINT message,
|
|
CERT_ENUM *certEnum
|
|
)
|
|
{
|
|
ASSERT(ScardUiHandle != NULL);
|
|
|
|
// This is sort of ugly since we cannot use a switch. First check for any
|
|
// possible smart card message because we must do some things in common
|
|
// for any of the messages:
|
|
|
|
if ((message == CreduiScarduiWmReaderArrival) ||
|
|
(message == CreduiScarduiWmReaderRemoval) ||
|
|
(message == CreduiScarduiWmCardInsertion) ||
|
|
(message == CreduiScarduiWmCardRemoval) ||
|
|
(message == CreduiScarduiWmCardCertAvail) ||
|
|
(message == CreduiScarduiWmCardStatus))
|
|
{
|
|
if (certEnum == NULL)
|
|
{
|
|
CreduiDebugLog(
|
|
"CreduiCredentialControl::HandleSmartCardMessages: "
|
|
"NULL was passed for the CERT_ENUM!");
|
|
|
|
// We handled the message, even though it was invalid:
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
ASSERT(certEnum->pszReaderName != NULL);
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
WCHAR *displayString;
|
|
WCHAR string[256]; // Must be >= CREDUI_MAX_CERT_NAME_LENGTH
|
|
|
|
ASSERT((sizeof string / (sizeof string[0])) >=
|
|
CREDUI_MAX_CERT_NAME_LENGTH);
|
|
|
|
INT index = FindSmartCardInComboBox(certEnum);
|
|
|
|
if (message == CreduiScarduiWmReaderArrival)
|
|
{
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: Reader arrival event for %0x\n",this->Window);
|
|
#endif
|
|
// Add the reader, if it is not already there; it should not be:
|
|
|
|
if (index == -1)
|
|
{
|
|
|
|
//
|
|
// Reset command line Hearbeat timer.
|
|
//
|
|
|
|
Heartbeats = 0;
|
|
|
|
INT index =
|
|
UserNameComboBox.Add(
|
|
DoingCommandLine ?
|
|
CreduiStrings.NoCard :
|
|
CreduiStrings.EmptyReader,
|
|
IMAGE_SMART_CARD_MISSING,
|
|
FALSE,
|
|
FALSE);
|
|
|
|
if (index != -1)
|
|
{
|
|
SendMessage(UserNameControlWindow,
|
|
CB_SETITEMDATA,
|
|
index,
|
|
reinterpret_cast<LPARAM>(certEnum));
|
|
|
|
if (UserNameCertHash != NULL)
|
|
{
|
|
EnableWindow(ViewCertControlWindow, FALSE);
|
|
DisabledControlMask |= DISABLED_CONTROL_VIEW;
|
|
|
|
SetWindowText(
|
|
PasswordStaticWindow,
|
|
CreduiStrings.PinStatic);
|
|
|
|
IsChangingPassword = TRUE;
|
|
SetWindowText(PasswordControlWindow, NULL);
|
|
IsChangingPassword = FALSE;
|
|
|
|
EnableWindow(PasswordControlWindow, TRUE);
|
|
EnableWindow(PasswordStaticWindow, TRUE);
|
|
DisabledControlMask &= ~DISABLED_CONTROL_PASSWORD;
|
|
|
|
SetWindowText(
|
|
UserNameStaticWindow,
|
|
CreduiStrings.SmartCardStatic);
|
|
|
|
if (SaveControlWindow != NULL)
|
|
{
|
|
EnableWindow(SaveControlWindow, FALSE);
|
|
DisabledControlMask |= DISABLED_CONTROL_SAVE;
|
|
}
|
|
|
|
IsChangingUserName = TRUE;
|
|
UserNameComboBox.Update(
|
|
-1,
|
|
DoingCommandLine ?
|
|
CreduiStrings.NoCard :
|
|
CreduiStrings.EmptyReader,
|
|
IMAGE_SMART_CARD_MISSING);
|
|
IsChangingUserName = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CreduiDebugLog(
|
|
"CreduiCredentialControl::HandleSmartCardMessages: "
|
|
"Failed to add smart card\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CreduiDebugLog(
|
|
"CreduiCredentialControl::HandleSmartCardMessages: "
|
|
"Reader arrived more than once");
|
|
}
|
|
|
|
}
|
|
else if (message == CreduiScarduiWmReaderRemoval)
|
|
{
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: Reader removal event for %0x\n",this->Window);
|
|
#endif
|
|
if (index != -1)
|
|
{
|
|
RemoveSmartCardFromComboBox(certEnum, TRUE);
|
|
}
|
|
else
|
|
{
|
|
CreduiDebugLog(
|
|
"CreduiCredentialControl::HandleSmartCardMessages: "
|
|
"Reader removed more than once");
|
|
}
|
|
}
|
|
else if (message == CreduiScarduiWmCardInsertion)
|
|
{
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: card insertion event for %0x\n",this->Window);
|
|
#endif
|
|
if (index != -1)
|
|
{
|
|
//
|
|
// Reset command line Hearbeat timer.
|
|
//
|
|
|
|
Heartbeats = 0;
|
|
|
|
SmartCardReadCount++;
|
|
|
|
if (UserNameCertHash != NULL)
|
|
{
|
|
IsChangingUserName = TRUE;
|
|
UserNameComboBox.Update(
|
|
-1,
|
|
CreduiStrings.ReadingCard,
|
|
IMAGE_SMART_CARD_MISSING);
|
|
IsChangingUserName = FALSE;
|
|
}
|
|
|
|
IsChangingUserName = TRUE;
|
|
UserNameComboBox.Update(index,
|
|
CreduiStrings.ReadingCard,
|
|
IMAGE_SMART_CARD_MISSING);
|
|
IsChangingUserName = FALSE;
|
|
}
|
|
else
|
|
{
|
|
CreduiDebugLog(
|
|
"CreduiCredentialControl::HandleSmartCardMessages: "
|
|
"Card insertion to absent reader\n");
|
|
}
|
|
}
|
|
else if (message == CreduiScarduiWmCardRemoval)
|
|
{
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: card removal event for %0x\n",this->Window);
|
|
#endif
|
|
if (index != -1)
|
|
{
|
|
if (BalloonTip.GetInfo() == &CreduiBackwardsTipInfo)
|
|
{
|
|
BalloonTip.Hide();
|
|
}
|
|
|
|
IsChangingUserName = TRUE;
|
|
UserNameComboBox.Update(index,
|
|
DoingCommandLine ?
|
|
CreduiStrings.NoCard :
|
|
CreduiStrings.EmptyReader,
|
|
IMAGE_SMART_CARD_MISSING);
|
|
IsChangingUserName = FALSE;
|
|
|
|
RemoveSmartCardFromComboBox(certEnum, FALSE);
|
|
}
|
|
else
|
|
{
|
|
CreduiDebugLog(
|
|
"CreduiCredentialControl::HandleSmartCardMessages: "
|
|
"Card removal from absent reader\n");
|
|
}
|
|
}
|
|
else if (message == CreduiScarduiWmCardCertAvail)
|
|
{
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: cert available event for %0x\n",this->Window);
|
|
#endif
|
|
if (index != -1)
|
|
{
|
|
// Filter certificates which are not for client authentication:
|
|
|
|
if (!CreduiIsClientAuthCertificate(certEnum->pCertContext))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
UINT image = IMAGE_SMART_CARD_MISSING;
|
|
COMBOBOXEXITEM item;
|
|
|
|
item.mask = CBEIF_IMAGE;
|
|
item.iItem = index;
|
|
|
|
SendMessage(UserNameControlWindow, CBEM_GETITEM,
|
|
0, reinterpret_cast<LPARAM>(&item));
|
|
|
|
//
|
|
// For command line,
|
|
// get the UPN display name since the user is expected to type it.
|
|
// For GUI,
|
|
// get the friendly display name since it is "friendly".
|
|
//
|
|
|
|
if (!CreduiGetCertificateDisplayName(
|
|
certEnum->pCertContext,
|
|
string,
|
|
CREDUI_MAX_CERT_NAME_LENGTH,
|
|
CreduiStrings.Certificate,
|
|
DoingCommandLine ?
|
|
CERT_NAME_UPN_TYPE :
|
|
CERT_NAME_FRIENDLY_DISPLAY_TYPE))
|
|
{
|
|
lstrcpyn(string,
|
|
CreduiStrings.Certificate,
|
|
CREDUI_MAX_CERT_NAME_LENGTH);
|
|
}
|
|
|
|
displayString = string;
|
|
|
|
//
|
|
// Trim trailing spaces and -'s so it doesn't look cheesy
|
|
//
|
|
|
|
if ( DoingCommandLine ) {
|
|
DWORD StringLength = wcslen(string);
|
|
|
|
while ( StringLength > 0 ) {
|
|
if ( string[StringLength-1] == ' ' || string[StringLength-1] == '-' ) {
|
|
string[StringLength-1] = '\0';
|
|
StringLength--;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: cert name '%ws' %0x\n", string, this->Window);
|
|
#endif
|
|
|
|
if (SendMessage(UserNameControlWindow,
|
|
CB_GETCURSEL,
|
|
0,
|
|
0) == index)
|
|
{
|
|
EnableWindow(ViewCertControlWindow, TRUE);
|
|
DisabledControlMask &= ~DISABLED_CONTROL_VIEW;
|
|
}
|
|
|
|
image =
|
|
CreduiIsExpiredCertificate(certEnum->pCertContext) ?
|
|
IMAGE_SMART_CARD_EXPIRED :
|
|
IMAGE_SMART_CARD;
|
|
|
|
INT newIndex = index;
|
|
|
|
if (item.iImage != IMAGE_SMART_CARD_MISSING)
|
|
{
|
|
newIndex = UserNameComboBox.Add(displayString,
|
|
image,
|
|
FALSE,
|
|
FALSE,
|
|
index + 1,
|
|
1);
|
|
|
|
if (newIndex != -1)
|
|
{
|
|
SendMessage(UserNameControlWindow,
|
|
CB_SETITEMDATA,
|
|
newIndex,
|
|
reinterpret_cast<LPARAM>(certEnum));
|
|
}
|
|
else
|
|
{
|
|
newIndex = index;
|
|
}
|
|
}
|
|
|
|
if (newIndex == index)
|
|
{
|
|
IsChangingUserName = TRUE;
|
|
UserNameComboBox.Update(index, displayString, image);
|
|
IsChangingUserName = FALSE;
|
|
}
|
|
|
|
if (UserNameCertHash != NULL)
|
|
{
|
|
UCHAR hash[CERT_HASH_LENGTH];
|
|
DWORD length = CERT_HASH_LENGTH;
|
|
|
|
if (CertGetCertificateContextProperty(
|
|
certEnum->pCertContext,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
static_cast<VOID *>(&hash),
|
|
&length))
|
|
{
|
|
if (RtlCompareMemory(UserNameCertHash,
|
|
hash,
|
|
CERT_HASH_LENGTH) ==
|
|
CERT_HASH_LENGTH)
|
|
{
|
|
delete [] UserNameCertHash;
|
|
UserNameCertHash = NULL;
|
|
|
|
IsChangingUserName = TRUE;
|
|
SendMessage(UserNameControlWindow,
|
|
CB_SETCURSEL, newIndex, 0);
|
|
IsChangingUserName = FALSE;
|
|
|
|
OnUserNameSelectionChange();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CreduiDebugLog(
|
|
"CreduiCredentialControl::HandleSmartCardMessages: "
|
|
"Card certificate to absent reader\n");
|
|
}
|
|
}
|
|
else if (message == CreduiScarduiWmCardStatus)
|
|
{
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: card status event for %0x\n",this->Window);
|
|
#endif
|
|
if (index != -1)
|
|
{
|
|
if (--SmartCardReadCount == 0)
|
|
{
|
|
if (UserNameCertHash != NULL)
|
|
{
|
|
IsChangingUserName = TRUE;
|
|
SetWindowText(UserNameControlWindow,
|
|
DoingCommandLine ?
|
|
CreduiStrings.NoCard :
|
|
CreduiStrings.EmptyReader);
|
|
IsChangingUserName = FALSE;
|
|
}
|
|
}
|
|
|
|
UINT image = IMAGE_SMART_CARD_MISSING;
|
|
BOOL showBalloon = FALSE;
|
|
|
|
switch (certEnum->dwStatus)
|
|
{
|
|
case SCARD_S_SUCCESS:
|
|
UINT image;
|
|
COMBOBOXEXITEM item;
|
|
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: card status SUCCESS: %ws\n", certEnum->pszCardName );
|
|
#endif
|
|
item.mask = CBEIF_IMAGE;
|
|
item.iItem = index;
|
|
|
|
if (SendMessage(UserNameControlWindow, CBEM_GETITEM,
|
|
0, reinterpret_cast<LPARAM>(&item)) &&
|
|
(item.iImage != IMAGE_SMART_CARD_MISSING))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
displayString = CreduiStrings.EmptyCard;
|
|
break;
|
|
|
|
case SCARD_E_UNKNOWN_CARD:
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: card status UNKNOWN CARD\n");
|
|
#endif
|
|
displayString = CreduiStrings.UnknownCard;
|
|
break;
|
|
|
|
case SCARD_W_UNRESPONSIVE_CARD:
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: card status UNRESPONSIVE CARD\n");
|
|
#endif
|
|
displayString = CreduiStrings.BackwardsCard;
|
|
if (!DoingCommandLine) showBalloon = TRUE;
|
|
break;
|
|
|
|
case NTE_KEYSET_NOT_DEF:
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: card status NTE_KEYSET_NOT_DEF\n");
|
|
#endif
|
|
// TODO: This case should be removed eventually.
|
|
|
|
displayString = CreduiStrings.EmptyCard;
|
|
break;
|
|
|
|
case SCARD_W_REMOVED_CARD:
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: card status REMOVED CARD\n");
|
|
#endif
|
|
displayString = DoingCommandLine ?
|
|
CreduiStrings.NoCard :
|
|
CreduiStrings.EmptyReader;
|
|
CreduiStrings.EmptyReader;
|
|
break;
|
|
|
|
default:
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: card status ERROR\n");
|
|
#endif
|
|
displayString = CreduiStrings.CardError;
|
|
break;
|
|
}
|
|
|
|
IsChangingUserName = TRUE;
|
|
UserNameComboBox.Update(index, displayString, image);
|
|
IsChangingUserName = FALSE;
|
|
|
|
if (showBalloon && !BalloonTip.IsVisible())
|
|
{
|
|
BalloonTip.SetInfo(UserNameControlWindow,
|
|
&CreduiBackwardsTipInfo);
|
|
|
|
BalloonTip.Show();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CreduiDebugLog(
|
|
"CreduiCredentialControl::HandleSmartCardMessages: "
|
|
"Card status to absent reader\n");
|
|
}
|
|
}
|
|
|
|
// We handled the message:
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::CreateControls
|
|
//
|
|
// Created 06/23/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::CreateControls()
|
|
{
|
|
// First we need the parent window:
|
|
|
|
HWND dialogWindow = GetParent(Window);
|
|
|
|
if (dialogWindow == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Create the various windows:
|
|
|
|
RECT clientRect;
|
|
RECT rect;
|
|
UINT add;
|
|
BOOL noViewCert = FALSE;
|
|
|
|
if ( Style & CRS_KEEPUSERNAME )
|
|
{
|
|
KeepUserName = TRUE;
|
|
}
|
|
|
|
if (!(Style & CRS_USERNAMES) )
|
|
{
|
|
NoEditUserName = TRUE;
|
|
}
|
|
else if ((Style & (CRS_CERTIFICATES | CRS_SMARTCARDS)) == 0)
|
|
{
|
|
noViewCert = TRUE;
|
|
}
|
|
|
|
if ( Style & CRS_SINGLESIGNON )
|
|
IsPassport = TRUE;
|
|
else
|
|
IsPassport = FALSE;
|
|
|
|
// Determine how much wider the control is than the minimum to resize and
|
|
// reposition controls as necessary:
|
|
|
|
GetClientRect(Window, &clientRect);
|
|
|
|
rect.left = 0;
|
|
rect.top = 0;
|
|
rect.right = CREDUI_CONTROL_MIN_WIDTH;
|
|
rect.bottom = CREDUI_CONTROL_MIN_HEIGHT;
|
|
|
|
if ( !DoingCommandLine && !MapDialogRect(dialogWindow, &rect))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ((clientRect.right - clientRect.left) >
|
|
(rect.right - rect.left))
|
|
{
|
|
add = (clientRect.right - clientRect.left) -
|
|
(rect.right - rect.left);
|
|
}
|
|
else
|
|
{
|
|
add = 0;
|
|
}
|
|
|
|
// Create user name static text control:
|
|
|
|
rect.left = CREDUI_CONTROL_USERNAME_STATIC_X;
|
|
rect.top = CREDUI_CONTROL_USERNAME_STATIC_Y;
|
|
rect.right = rect.left + CREDUI_CONTROL_USERNAME_STATIC_WIDTH;
|
|
rect.bottom = rect.top + CREDUI_CONTROL_USERNAME_STATIC_HEIGHT;
|
|
|
|
if ( !DoingCommandLine && !MapDialogRect(dialogWindow, &rect))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
WCHAR* pUserNameLabel;
|
|
if ( IsPassport )
|
|
pUserNameLabel = CreduiStrings.EmailName;
|
|
else
|
|
pUserNameLabel = CreduiStrings.UserNameStatic;
|
|
|
|
|
|
UserNameStaticWindow =
|
|
CreateWindowEx(
|
|
WS_EX_NOPARENTNOTIFY,
|
|
L"STATIC",
|
|
pUserNameLabel,
|
|
WS_VISIBLE | WS_CHILD | WS_GROUP,
|
|
rect.left,
|
|
rect.top,
|
|
rect.right - rect.left,
|
|
rect.bottom - rect.top,
|
|
Window,
|
|
reinterpret_cast<HMENU>(IDC_USERNAME_STATIC),
|
|
CreduiCredentialControl::Instance,
|
|
NULL);
|
|
|
|
if (UserNameStaticWindow == NULL)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// Create user name combo box:
|
|
|
|
rect.left = CREDUI_CONTROL_USERNAME_X;
|
|
rect.top = CREDUI_CONTROL_USERNAME_Y;
|
|
|
|
if (!noViewCert)
|
|
{
|
|
rect.right = rect.left + CREDUI_CONTROL_USERNAME_WIDTH;
|
|
}
|
|
else
|
|
{
|
|
rect.right = CREDUI_CONTROL_VIEW_X + CREDUI_CONTROL_VIEW_WIDTH;
|
|
}
|
|
|
|
if ( KeepUserName )
|
|
{
|
|
rect.top += 2; // fudge it to make them line up better
|
|
rect.bottom = rect.top + CREDUI_CONTROL_PASSWORD_STATIC_HEIGHT; // make it the same height as the password edit
|
|
}
|
|
else
|
|
{
|
|
rect.bottom = rect.top + CREDUI_CONTROL_USERNAME_HEIGHT; // set the height
|
|
}
|
|
|
|
|
|
if ( !DoingCommandLine && !MapDialogRect(dialogWindow, &rect))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// This block of statements and the usage of lExStyles : see bug 439840
|
|
LONG_PTR lExStyles = GetWindowLongPtr(Window,GWL_EXSTYLE);
|
|
SetWindowLongPtr(Window,GWL_EXSTYLE,(lExStyles | WS_EX_NOINHERITLAYOUT));
|
|
|
|
if ( KeepUserName )
|
|
{
|
|
|
|
// create an edit box instead of a combo box
|
|
|
|
UserNameControlWindow =
|
|
CreateWindowEx(
|
|
WS_EX_NOPARENTNOTIFY,
|
|
L"Edit",
|
|
L"",
|
|
WS_VISIBLE | WS_CHILD | WS_TABSTOP | ES_READONLY,
|
|
rect.left,
|
|
rect.top,
|
|
rect.right - rect.left + add,
|
|
rect.bottom - rect.top,
|
|
Window,
|
|
reinterpret_cast<HMENU>(IDC_USERNAME),
|
|
CreduiCredentialControl::Instance,
|
|
NULL);
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
UserNameControlWindow =
|
|
CreateWindowEx(
|
|
WS_EX_NOPARENTNOTIFY,
|
|
L"ComboBoxEx32",
|
|
L"",
|
|
WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_VSCROLL |
|
|
(NoEditUserName ? CBS_DROPDOWNLIST : CBS_DROPDOWN) |
|
|
CBS_AUTOHSCROLL,
|
|
rect.left,
|
|
rect.top,
|
|
rect.right - rect.left + add,
|
|
rect.bottom - rect.top,
|
|
Window,
|
|
reinterpret_cast<HMENU>(IDC_USERNAME),
|
|
CreduiCredentialControl::Instance,
|
|
NULL);
|
|
}
|
|
|
|
SetWindowLongPtr(Window,GWL_EXSTYLE,lExStyles);
|
|
|
|
if (UserNameControlWindow == NULL)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// Create view button:
|
|
|
|
if (!noViewCert)
|
|
{
|
|
rect.left = CREDUI_CONTROL_VIEW_X;
|
|
rect.top = CREDUI_CONTROL_VIEW_Y;
|
|
rect.right = rect.left + CREDUI_CONTROL_VIEW_WIDTH;
|
|
rect.bottom = rect.top + CREDUI_CONTROL_VIEW_HEIGHT;
|
|
|
|
if ( !DoingCommandLine && !MapDialogRect(dialogWindow, &rect))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
ViewCertControlWindow =
|
|
CreateWindowEx(
|
|
WS_EX_NOPARENTNOTIFY,
|
|
L"BUTTON",
|
|
L"&...",
|
|
WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_GROUP |
|
|
BS_PUSHBUTTON | BS_CENTER,
|
|
rect.left + add,
|
|
rect.top,
|
|
rect.right - rect.left,
|
|
rect.bottom - rect.top,
|
|
Window,
|
|
reinterpret_cast<HMENU>(IDC_VIEW_CERT),
|
|
CreduiCredentialControl::Instance,
|
|
NULL);
|
|
|
|
if (ViewCertControlWindow == NULL)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
EnableWindow(ViewCertControlWindow, FALSE);
|
|
DisabledControlMask |= DISABLED_CONTROL_VIEW;
|
|
}
|
|
|
|
// Create password static text control:
|
|
|
|
rect.left = CREDUI_CONTROL_PASSWORD_STATIC_X;
|
|
rect.top = CREDUI_CONTROL_PASSWORD_STATIC_Y;
|
|
rect.right = rect.left + CREDUI_CONTROL_PASSWORD_STATIC_WIDTH;
|
|
rect.bottom = rect.top + CREDUI_CONTROL_PASSWORD_STATIC_HEIGHT;
|
|
|
|
if ( !DoingCommandLine && !MapDialogRect(dialogWindow, &rect))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
PasswordStaticWindow =
|
|
CreateWindowEx(
|
|
WS_EX_NOPARENTNOTIFY,
|
|
L"STATIC",
|
|
CreduiStrings.PasswordStatic,
|
|
WS_VISIBLE | WS_CHILD | WS_GROUP,
|
|
rect.left,
|
|
rect.top,
|
|
rect.right - rect.left,
|
|
rect.bottom - rect.top,
|
|
Window,
|
|
reinterpret_cast<HMENU>(IDC_PASSWORD_STATIC),
|
|
CreduiCredentialControl::Instance,
|
|
NULL);
|
|
|
|
if (PasswordStaticWindow == NULL)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// Create password edit control:
|
|
|
|
rect.left = CREDUI_CONTROL_PASSWORD_X;
|
|
rect.top = CREDUI_CONTROL_PASSWORD_Y;
|
|
if (!noViewCert)
|
|
{
|
|
rect.right = rect.left + CREDUI_CONTROL_PASSWORD_WIDTH;
|
|
}
|
|
else
|
|
{
|
|
rect.right = CREDUI_CONTROL_VIEW_X + CREDUI_CONTROL_VIEW_WIDTH;
|
|
}
|
|
rect.bottom = rect.top + CREDUI_CONTROL_PASSWORD_HEIGHT;
|
|
|
|
if (!DoingCommandLine && !MapDialogRect(dialogWindow, &rect))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// This block of statements and the usage of lExStyles : see bug 439840
|
|
lExStyles = GetWindowLongPtr(Window,GWL_EXSTYLE);
|
|
SetWindowLongPtr(Window,GWL_EXSTYLE,(lExStyles | WS_EX_NOINHERITLAYOUT));
|
|
|
|
PasswordControlWindow =
|
|
CreateWindowEx(
|
|
WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE,
|
|
L"EDIT",
|
|
L"",
|
|
WS_VISIBLE | WS_CHILD | WS_TABSTOP | ES_PASSWORD | ES_AUTOHSCROLL,
|
|
rect.left,
|
|
rect.top,
|
|
rect.right - rect.left + add,
|
|
rect.bottom - rect.top + 1, // NOTE: Add 1 for now, investigate
|
|
Window,
|
|
reinterpret_cast<HMENU>(IDC_PASSWORD),
|
|
CreduiCredentialControl::Instance,
|
|
NULL);
|
|
|
|
SetWindowLongPtr(Window,GWL_EXSTYLE,lExStyles);
|
|
|
|
if (PasswordControlWindow == NULL)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// Create save check box:
|
|
|
|
if (Style & CRS_SAVECHECK )
|
|
{
|
|
rect.left = CREDUI_CONTROL_SAVE_X;
|
|
rect.top = CREDUI_CONTROL_SAVE_Y;
|
|
rect.right = rect.left + CREDUI_CONTROL_SAVE_WIDTH;
|
|
rect.bottom = rect.top + CREDUI_CONTROL_SAVE_HEIGHT;
|
|
|
|
if (!DoingCommandLine && !MapDialogRect(dialogWindow, &rect))
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
WCHAR* pSavePromptString;
|
|
|
|
if ( IsPassport )
|
|
pSavePromptString = CreduiStrings.PassportSave;
|
|
else
|
|
pSavePromptString = CreduiStrings.Save;
|
|
|
|
SaveControlWindow =
|
|
CreateWindowEx(
|
|
WS_EX_NOPARENTNOTIFY,
|
|
L"BUTTON",
|
|
pSavePromptString,
|
|
WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_GROUP |
|
|
BS_AUTOCHECKBOX,
|
|
rect.left,
|
|
rect.top,
|
|
rect.right - rect.left + add,
|
|
rect.bottom - rect.top,
|
|
Window,
|
|
reinterpret_cast<HMENU>(IDC_SAVE),
|
|
CreduiCredentialControl::Instance,
|
|
NULL);
|
|
|
|
if (SaveControlWindow == NULL)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
SendMessage(SaveControlWindow, BM_SETCHECK, BST_UNCHECKED, 0);
|
|
}
|
|
|
|
SendMessage(
|
|
Window,
|
|
WM_SETFONT,
|
|
SendMessage(dialogWindow, WM_GETFONT, 0, 0),
|
|
FALSE);
|
|
|
|
return TRUE;
|
|
|
|
ErrorExit:
|
|
|
|
if (SaveControlWindow != NULL)
|
|
{
|
|
DestroyWindow(SaveControlWindow);
|
|
SaveControlWindow = NULL;
|
|
}
|
|
|
|
if (PasswordControlWindow != NULL)
|
|
{
|
|
DestroyWindow(PasswordControlWindow);
|
|
PasswordControlWindow = NULL;
|
|
}
|
|
|
|
if (PasswordStaticWindow != NULL)
|
|
{
|
|
DestroyWindow(PasswordStaticWindow);
|
|
PasswordStaticWindow = NULL;
|
|
}
|
|
|
|
if (ViewCertControlWindow != NULL)
|
|
{
|
|
DestroyWindow(ViewCertControlWindow);
|
|
ViewCertControlWindow = NULL;
|
|
}
|
|
|
|
if (UserNameControlWindow != NULL)
|
|
{
|
|
DestroyWindow(UserNameControlWindow);
|
|
UserNameControlWindow = NULL;
|
|
}
|
|
|
|
if (UserNameStaticWindow != NULL)
|
|
{
|
|
DestroyWindow(UserNameStaticWindow);
|
|
UserNameStaticWindow = NULL;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
LPWSTR
|
|
TrimUsername(
|
|
IN LPWSTR AccountDomainName OPTIONAL,
|
|
IN LPWSTR UserName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a pointer to the substring of UserName past any AccountDomainName prefix.
|
|
|
|
Arguments:
|
|
|
|
AccountDomainName - The DomainName to check to see if it prefixes the UserName.
|
|
|
|
UserName - The UserName to check
|
|
|
|
Return Values:
|
|
|
|
Return a pointer to the non-prefixed username
|
|
|
|
--*/
|
|
{
|
|
DWORD AccountDomainNameLength;
|
|
DWORD UserNameLength;
|
|
WCHAR Temp[CNLEN+1];
|
|
|
|
//
|
|
// If we couldn't determine the AccountDomainName,
|
|
// return the complete user name.
|
|
//
|
|
|
|
if ( AccountDomainName == NULL ) {
|
|
return UserName;
|
|
}
|
|
|
|
//
|
|
// If the user name isn't prefixed by the account domain name,
|
|
// return the complete user name.
|
|
//
|
|
|
|
AccountDomainNameLength = lstrlen( AccountDomainName );
|
|
UserNameLength = lstrlen( UserName );
|
|
|
|
if ( AccountDomainNameLength > CNLEN || AccountDomainNameLength < 1 ) {
|
|
return UserName;
|
|
}
|
|
|
|
if ( AccountDomainNameLength+2 > UserNameLength ) {
|
|
return UserName;
|
|
}
|
|
|
|
if ( UserName[AccountDomainNameLength] != '\\' ) {
|
|
return UserName;
|
|
}
|
|
|
|
RtlCopyMemory( Temp, UserName, AccountDomainNameLength*sizeof(WCHAR) );
|
|
Temp[AccountDomainNameLength] = '\0';
|
|
|
|
if ( lstrcmpi( Temp, AccountDomainName ) != 0 ) {
|
|
return UserName;
|
|
}
|
|
|
|
return &UserName[AccountDomainNameLength+1];
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::InitComboBoxUserNames
|
|
//
|
|
// Created 06/23/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::InitComboBoxUserNames()
|
|
{
|
|
CREDENTIAL **credentialSet = NULL;
|
|
LOCALGROUP_MEMBERS_INFO_2 *groupInfo = NULL;
|
|
DWORD nameCount = 0;
|
|
LPWSTR AccountDomainName = NULL;
|
|
|
|
if (Style & CRS_ADMINISTRATORS)
|
|
{
|
|
//
|
|
// Enumerate the members of LocalAdministrators
|
|
//
|
|
|
|
if ( !CreduiGetAdministratorsGroupInfo(&groupInfo, &nameCount)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!LocalCredEnumerateW(NULL, 0, &nameCount, &credentialSet))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Initialize COM for STA, unless there are zero names:
|
|
|
|
if ((Style & CRS_AUTOCOMPLETE) && nameCount > 0)
|
|
{
|
|
HRESULT comResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
|
|
if (SUCCEEDED(comResult))
|
|
{
|
|
IsAutoComplete = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// The auto complete object and our string object require a STA.
|
|
// Our object could easily support a MTA, but we do not support
|
|
// marshaling between apartments.
|
|
|
|
if (comResult == RPC_E_CHANGED_MODE)
|
|
{
|
|
CreduiDebugLog("CreduiCredentialControl: "
|
|
"Auto complete disabled for MTA\n");
|
|
}
|
|
|
|
IsAutoComplete = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IsAutoComplete = FALSE;
|
|
}
|
|
|
|
// Initialize the auto complete combo box:
|
|
|
|
if (!UserNameComboBox.Init(CreduiInstance,
|
|
UserNameControlWindow,
|
|
IsAutoComplete ? nameCount : 0,
|
|
IDB_TYPES,
|
|
IMAGE_USERNAME))
|
|
{
|
|
// If initialization failed, and we had attempted for auto complete
|
|
// support, try again without auto complete:
|
|
|
|
if (IsAutoComplete)
|
|
{
|
|
IsAutoComplete = FALSE;
|
|
|
|
CoUninitialize();
|
|
|
|
if (!UserNameComboBox.Init(CreduiInstance,
|
|
UserNameControlWindow,
|
|
0,
|
|
IDB_TYPES,
|
|
IMAGE_USERNAME))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we'll complete the user name,
|
|
// truncate any username displayed here.
|
|
// (We'll complete it later.)
|
|
//
|
|
|
|
if ( Style & CRS_COMPLETEUSERNAME ) {
|
|
AccountDomainName = GetAccountDomainName();
|
|
}
|
|
|
|
// Add user names from credentials, if not requesting an
|
|
// Administrator:
|
|
|
|
if (!(Style & CRS_KEEPUSERNAME))
|
|
{
|
|
// only add usernames if we're not keeping the one set
|
|
|
|
UINT i = 0;
|
|
|
|
if (!(Style & CRS_ADMINISTRATORS))
|
|
{
|
|
for (i = 0; i < nameCount; ++i)
|
|
{
|
|
// Skip domain certificates:
|
|
|
|
if (credentialSet[i]->Type == CRED_TYPE_DOMAIN_CERTIFICATE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// If this is a generic credential, look for a marshaled
|
|
// credential, and skip, if found:
|
|
|
|
if ((credentialSet[i]->Type == CRED_TYPE_GENERIC) &&
|
|
LocalCredIsMarshaledCredentialW(credentialSet[i]->UserName))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Skip this credential if the name is empty:
|
|
|
|
if (credentialSet[i]->UserName == NULL)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Add the user name to the combo box with auto complete. If
|
|
// this fails, do not continue:
|
|
|
|
if (UserNameComboBox.Add(
|
|
TrimUsername( AccountDomainName, credentialSet[i]->UserName),
|
|
0, IsAutoComplete, TRUE) == -1)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
LocalCredFree(static_cast<VOID *>(credentialSet));
|
|
}
|
|
else if (groupInfo != NULL)
|
|
{
|
|
PSID adminSid = NULL;
|
|
|
|
if ( !CreduiLookupLocalSidFromRid(DOMAIN_USER_RID_ADMIN, &adminSid)) {
|
|
adminSid = NULL;
|
|
}
|
|
|
|
// Add local administrators to the combo box:
|
|
|
|
for (i = 0; i < nameCount; ++i)
|
|
{
|
|
if ( groupInfo[i].lgrmi2_sidusage == SidTypeUser )
|
|
{
|
|
DWORD ComboBoxIndex;
|
|
BOOLEAN IsAdminAccount;
|
|
BOOLEAN RememberComboBoxIndex;
|
|
|
|
|
|
//
|
|
// If this is Personal and not safe mode,
|
|
// Ignore the well-known Administrator account.
|
|
//
|
|
|
|
IsAdminAccount = (adminSid != NULL) &&
|
|
EqualSid(adminSid, groupInfo[i].lgrmi2_sid);
|
|
|
|
if ( CreduiIsPersonal &&
|
|
!CreduiIsSafeMode &&
|
|
IsAdminAccount ) {
|
|
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If the caller wants to prepopulate the edit box,
|
|
// flag that we need to remember this account
|
|
//
|
|
// Detect the well known admin account
|
|
//
|
|
|
|
RememberComboBoxIndex = FALSE;
|
|
|
|
if ( (Style & CRS_PREFILLADMIN) != 0 &&
|
|
IsAdminAccount ) {
|
|
|
|
RememberComboBoxIndex = TRUE;
|
|
|
|
}
|
|
|
|
//
|
|
// Add the name to the combo box
|
|
//
|
|
|
|
ComboBoxIndex = UserNameComboBox.Add(
|
|
TrimUsername( AccountDomainName, groupInfo[i].lgrmi2_domainandname),
|
|
0,
|
|
IsAutoComplete,
|
|
TRUE);
|
|
|
|
if ( ComboBoxIndex == -1 ) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we're to remember the index,
|
|
// do so.
|
|
//
|
|
|
|
if ( RememberComboBoxIndex ) {
|
|
|
|
UserNameSelection = ComboBoxIndex;
|
|
|
|
IsChangingUserName = TRUE;
|
|
SendMessage(UserNameControlWindow,
|
|
CB_SETCURSEL,
|
|
ComboBoxIndex,
|
|
0);
|
|
IsChangingUserName = FALSE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
delete [] adminSid;
|
|
NetApiBufferFree(groupInfo);
|
|
}
|
|
}
|
|
|
|
if ( AccountDomainName != NULL ) {
|
|
NetApiBufferFree( AccountDomainName );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::InitWindow
|
|
//
|
|
// Created 06/20/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::InitWindow()
|
|
{
|
|
// Set that we're intialized here, even though the controls have not yet
|
|
// been created, etc.:
|
|
|
|
IsInitialized = TRUE;
|
|
|
|
// Make sure WS_EX_CONTROLPARENT is set:
|
|
|
|
SetWindowLong(Window,
|
|
GWL_EXSTYLE,
|
|
GetWindowLong(Window, GWL_EXSTYLE) |
|
|
WS_EX_CONTROLPARENT);
|
|
|
|
// Initialize the balloon tip for this window:
|
|
|
|
if (!CreateControls() ||
|
|
!BalloonTip.Init(CreduiInstance, Window))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Limit the number of characters entered into the user name and password
|
|
// edit controls:
|
|
|
|
SendMessage(UserNameControlWindow,
|
|
CB_LIMITTEXT,
|
|
CREDUI_MAX_USERNAME_LENGTH,
|
|
0);
|
|
|
|
SendMessage(PasswordControlWindow,
|
|
EM_LIMITTEXT,
|
|
CREDUI_MAX_PASSWORD_LENGTH,
|
|
0);
|
|
|
|
// Set the password character to something cooler:
|
|
|
|
PasswordBox.Init(PasswordControlWindow,
|
|
&BalloonTip,
|
|
&CreduiCapsLockTipInfo);
|
|
|
|
// Initialize the user name auto complete combo box:
|
|
|
|
if ( !KeepUserName )
|
|
{
|
|
if (((Style & CRS_USERNAMES) && InitComboBoxUserNames()) ||
|
|
UserNameComboBox.Init(CreduiInstance,
|
|
UserNameControlWindow,
|
|
0,
|
|
IDB_TYPES,
|
|
IMAGE_USERNAME))
|
|
{
|
|
// Since we're finished adding auto complete names, enable it now.
|
|
// On failure, the UI can still be presented:
|
|
|
|
UserNameComboBox.Enable();
|
|
|
|
BOOL haveCertificates = FALSE;
|
|
|
|
CertBaseInComboBox = (ULONG)
|
|
SendMessage(UserNameControlWindow,
|
|
CB_GETCOUNT, 0, 0);
|
|
|
|
if (Style & CRS_CERTIFICATES)
|
|
{
|
|
haveCertificates = AddCertificates();
|
|
}
|
|
|
|
SmartCardBaseInComboBox = CertBaseInComboBox + CertCount;
|
|
|
|
if ((Style & CRS_SMARTCARDS) && CreduiHasSmartCardSupport)
|
|
{
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: Call to SCardUIInit for %0x\n",Window);
|
|
#endif
|
|
ScardUiHandle = SCardUIInit(Window);
|
|
|
|
if (ScardUiHandle == NULL)
|
|
{
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: Call to SCardUIInit failed\n");
|
|
#endif
|
|
CreduiDebugLog("CreduiCredentialControl::InitWindow: "
|
|
"SCardUIInit failed\n");
|
|
}
|
|
}
|
|
|
|
// If NoEditUserName is allowed, make sure we eithet have at least one certificate
|
|
// or a prefilled username for the control, otherwise fail
|
|
|
|
if (NoEditUserName )
|
|
{
|
|
if (!haveCertificates &&
|
|
(ScardUiHandle == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
IsChangingUserName = TRUE;
|
|
SendMessage(UserNameControlWindow,
|
|
CB_SETCURSEL,
|
|
0,
|
|
0);
|
|
IsChangingUserName = FALSE;
|
|
|
|
// If we have at least one certificate, enable the view control
|
|
// now. If a smart card, it will be enabled later:
|
|
|
|
if (CertCount > 0)
|
|
{
|
|
EnableWindow(ViewCertControlWindow, TRUE);
|
|
DisabledControlMask &= ~DISABLED_CONTROL_VIEW;
|
|
}
|
|
}
|
|
|
|
// Wait until everything has been initialized before
|
|
// we have the update. This will now properly determine if the default
|
|
// user name is a smart card or not.
|
|
OnUserNameSelectionChange();
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if ( !DoingCommandLine ) {
|
|
SetFocus(UserNameControlWindow);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//=============================================================================
|
|
// CredioCredentialControl::Enable
|
|
//
|
|
// Enables or disables all the user controls in the control.
|
|
//
|
|
// Arguments:
|
|
// enable (in) - TRUE to enable the controls, FALSE to disable.
|
|
//
|
|
// Created 06/20/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
VOID
|
|
CreduiCredentialControl::Enable(
|
|
BOOL enable
|
|
)
|
|
{
|
|
if (enable && (DisabledControlMask & DISABLED_CONTROL))
|
|
{
|
|
DisabledControlMask &= ~DISABLED_CONTROL;
|
|
|
|
//EnableWindow(UserNameStaticWindow, TRUE);
|
|
//EnableWindow(UserNameControlWindow, TRUE);
|
|
|
|
if (!(DisabledControlMask & DISABLED_CONTROL_USERNAME))
|
|
{
|
|
EnableWindow(UserNameControlWindow, TRUE);
|
|
EnableWindow(UserNameStaticWindow, TRUE);
|
|
}
|
|
|
|
if (!(DisabledControlMask & DISABLED_CONTROL_PASSWORD))
|
|
{
|
|
EnableWindow(PasswordControlWindow, TRUE);
|
|
EnableWindow(PasswordStaticWindow, TRUE);
|
|
}
|
|
if (!(DisabledControlMask & DISABLED_CONTROL_VIEW))
|
|
{
|
|
EnableWindow(ViewCertControlWindow, TRUE);
|
|
}
|
|
if (SaveControlWindow != NULL)
|
|
{
|
|
if (!(DisabledControlMask & DISABLED_CONTROL_SAVE))
|
|
{
|
|
EnableWindow(SaveControlWindow, TRUE);
|
|
}
|
|
}
|
|
|
|
IsChangingUserName = TRUE;
|
|
SendMessage(UserNameControlWindow,
|
|
CB_SETCURSEL,
|
|
UserNameSelection,
|
|
0);
|
|
IsChangingUserName = FALSE;
|
|
|
|
OnUserNameSelectionChange();
|
|
}
|
|
else if (!(DisabledControlMask & DISABLED_CONTROL))
|
|
{
|
|
// Hide the balloon tip before disabling the window:
|
|
|
|
if (BalloonTip.IsVisible())
|
|
{
|
|
BalloonTip.Hide();
|
|
}
|
|
|
|
DisabledControlMask |= DISABLED_CONTROL;
|
|
|
|
UserNameSelection = (LONG) SendMessage(UserNameControlWindow,
|
|
CB_GETCURSEL, 0, 0);
|
|
|
|
EnableWindow(UserNameStaticWindow, FALSE);
|
|
EnableWindow(UserNameControlWindow, FALSE);
|
|
EnableWindow(ViewCertControlWindow, FALSE);
|
|
|
|
EnableWindow(PasswordControlWindow, FALSE);
|
|
SetFocus(UserNameControlWindow);
|
|
EnableWindow(PasswordStaticWindow, FALSE);
|
|
|
|
if (SaveControlWindow != NULL)
|
|
{
|
|
EnableWindow(SaveControlWindow, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::MessageHandlerCallback
|
|
//
|
|
// This is the actual callback function for the control window.
|
|
//
|
|
// Arguments:
|
|
// window (in)
|
|
// message (in)
|
|
// wParam (in)
|
|
// lParam (in)
|
|
//
|
|
// Created 06/20/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
LRESULT
|
|
CALLBACK
|
|
CreduiCredentialControl::MessageHandlerCallback(
|
|
HWND window,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
// CreduiDebugLog( "Control Callback: %8.8lx %8.8lx %8.8lx\n", message, wParam, lParam );
|
|
CreduiCredentialControl *that =
|
|
reinterpret_cast<CreduiCredentialControl *>(
|
|
GetWindowLongPtr(window, 0));
|
|
|
|
if (that != NULL)
|
|
{
|
|
LRESULT result2;
|
|
ASSERT(window == that->Window);
|
|
// CreduiDebugLog( "Certhashes: %8.8lx %8.8lx\n", that, that->CertHashes );
|
|
|
|
result2 = that->MessageHandler(message, wParam, lParam);
|
|
|
|
// CreduiDebugLog( "Certhashes2: %8.8lx %8.8lx\n", that, that->CertHashes );
|
|
return result2;
|
|
}
|
|
|
|
if (message == WM_CREATE)
|
|
{
|
|
CreduiCredentialControl *control = new CreduiCredentialControl;
|
|
|
|
if (control != NULL)
|
|
{
|
|
// Initialize some state:
|
|
|
|
control->FirstPaint = TRUE;
|
|
control->ShowBalloonTip = FALSE;
|
|
|
|
control->Window = window;
|
|
control->Style = GetWindowLong(window, GWL_STYLE);
|
|
|
|
// Store this object's pointer in the user data window long:
|
|
|
|
SetLastError(0);
|
|
LONG_PTR retPtr = SetWindowLongPtr(window,
|
|
0,
|
|
reinterpret_cast<LONG_PTR>(control));
|
|
|
|
if ( retPtr != 0 || GetLastError() == 0 )
|
|
{
|
|
// we sucessfully set the window pointer
|
|
|
|
// If any of the required styles are set, initialize the window
|
|
// now. Otherwise, defer until CRM_INITSTYLE:
|
|
|
|
if (control->Style & (CRS_USERNAMES |
|
|
CRS_CERTIFICATES |
|
|
CRS_SMARTCARDS))
|
|
{
|
|
if (control->InitWindow())
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
SetWindowLongPtr(window, 0, 0);
|
|
|
|
delete control;
|
|
control = NULL;
|
|
}
|
|
|
|
DestroyWindow(window);
|
|
return 0;
|
|
}
|
|
|
|
return DefWindowProc(window, message, wParam, lParam);
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::OnSetUserNameA
|
|
//
|
|
// Created 06/22/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::OnSetUserNameA(
|
|
CHAR *userNameA
|
|
)
|
|
{
|
|
BOOL success = FALSE;
|
|
|
|
if (userNameA != NULL)
|
|
{
|
|
ULONG bufferSize = lstrlenA(userNameA) + 1;
|
|
|
|
WCHAR *userName = new WCHAR[bufferSize];
|
|
|
|
if (userName != NULL)
|
|
{
|
|
if (MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
userNameA,
|
|
-1,
|
|
userName,
|
|
bufferSize) > 0)
|
|
{
|
|
success = OnSetUserName(userName);
|
|
}
|
|
|
|
delete [] userName;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
success = OnSetUserName(NULL);
|
|
}
|
|
|
|
return success;
|
|
};
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::OnSetUserName
|
|
//
|
|
// Created 06/22/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::OnSetUserName(
|
|
WCHAR *userName
|
|
)
|
|
{
|
|
if ((userName == NULL) || !LocalCredIsMarshaledCredentialW(userName))
|
|
{
|
|
|
|
//
|
|
// Save the initial user name for command line
|
|
//
|
|
|
|
if ( DoingCommandLine ) {
|
|
InitialUserName = new WCHAR[lstrlen(userName) + 1];
|
|
|
|
if ( InitialUserName == NULL ) {
|
|
return FALSE;
|
|
}
|
|
|
|
lstrcpy( InitialUserName, userName );
|
|
}
|
|
|
|
return SetWindowText(UserNameControlWindow, userName);
|
|
}
|
|
else
|
|
{
|
|
CRED_MARSHAL_TYPE credMarshalType;
|
|
CERT_CREDENTIAL_INFO *certCredInfo = NULL;
|
|
BOOL foundCert = FALSE;
|
|
|
|
if (LocalCredUnmarshalCredentialW(
|
|
userName,
|
|
&credMarshalType,
|
|
reinterpret_cast<VOID **>(&certCredInfo)))
|
|
{
|
|
// Search for the certificate. What can we do if it is a
|
|
// smart card? Well, at least we can still search for it,
|
|
// but it is a bit more work because we must retrieve the
|
|
// hash from the context.
|
|
|
|
if (credMarshalType == CertCredential)
|
|
{
|
|
for (UINT i = 0; i < CertCount; ++i)
|
|
{
|
|
if (RtlCompareMemory(CertHashes[i],
|
|
certCredInfo->rgbHashOfCert,
|
|
CERT_HASH_LENGTH) ==
|
|
CERT_HASH_LENGTH)
|
|
{
|
|
IsChangingUserName = TRUE;
|
|
SendMessage(UserNameControlWindow,
|
|
CB_SETCURSEL,
|
|
CertBaseInComboBox + i,
|
|
0);
|
|
IsChangingUserName = FALSE;
|
|
|
|
OnUserNameSelectionChange();
|
|
|
|
EnableWindow(ViewCertControlWindow, TRUE);
|
|
DisabledControlMask &= ~DISABLED_CONTROL_VIEW;
|
|
|
|
foundCert = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we couldn't find the certificate in our list, determine
|
|
// if this is a smart card certificate, based on its entry in
|
|
// the MY certificate store. If it is, store the hash and
|
|
// check for it on certificate arrival messages:
|
|
|
|
if (!foundCert)
|
|
{
|
|
CONST CERT_CONTEXT *certContext = NULL;
|
|
HCERTSTORE certStore = NULL;
|
|
|
|
certStore = CertOpenSystemStore(NULL, L"MY");
|
|
|
|
if (certStore != NULL)
|
|
{
|
|
CRYPT_HASH_BLOB hashBlob;
|
|
|
|
hashBlob.cbData = CERT_HASH_LENGTH;
|
|
hashBlob.pbData = reinterpret_cast<BYTE *>(
|
|
certCredInfo->rgbHashOfCert);
|
|
|
|
certContext = CertFindCertificateInStore(
|
|
certStore,
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
0,
|
|
CERT_FIND_SHA1_HASH,
|
|
&hashBlob,
|
|
NULL);
|
|
}
|
|
|
|
// If we found a certificate context, check to see if it
|
|
// is from a smart card:
|
|
|
|
if ((certContext != NULL) &&
|
|
CreduiIsRemovableCertificate(certContext))
|
|
{
|
|
UserNameCertHash = new UCHAR [1][CERT_HASH_LENGTH];
|
|
|
|
if (UserNameCertHash != NULL)
|
|
{
|
|
CopyMemory(UserNameCertHash,
|
|
certCredInfo->rgbHashOfCert,
|
|
CERT_HASH_LENGTH);
|
|
|
|
foundCert = TRUE;
|
|
}
|
|
}
|
|
|
|
// If we opened a store, free the certificate and close
|
|
// the store:
|
|
|
|
if (certStore != NULL)
|
|
{
|
|
if (certContext != NULL)
|
|
{
|
|
CertFreeCertificateContext(certContext);
|
|
}
|
|
|
|
if (!CertCloseStore(certStore, 0))
|
|
{
|
|
CreduiDebugLog(
|
|
"CreduiCredentialControl::OnSetUserName: "
|
|
"CertCloseStore failed: %u\n",
|
|
GetLastError());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
LocalCredFree(static_cast<VOID *>(certCredInfo));
|
|
}
|
|
else
|
|
{
|
|
// Could not unmarshal, so just forget it:
|
|
|
|
CreduiDebugLog(
|
|
"CreduiCredentialControl::OnSetUserName: "
|
|
"CredUnmarshalCredential failed: %u\n",
|
|
GetLastError());
|
|
}
|
|
|
|
return foundCert;
|
|
}
|
|
};
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::OnGetUserNameA
|
|
//
|
|
// Created 06/22/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::OnGetUserNameA(
|
|
CHAR *userNameA,
|
|
ULONG maxChars
|
|
)
|
|
{
|
|
BOOL success = FALSE;
|
|
|
|
if ((userNameA != NULL) && (maxChars != 0))
|
|
{
|
|
WCHAR *userName = new WCHAR[maxChars + 1];
|
|
|
|
if (userName != NULL)
|
|
{
|
|
if (OnGetUserName(userName, maxChars) &&
|
|
WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
userName,
|
|
-1,
|
|
userNameA,
|
|
maxChars + 1, NULL, NULL))
|
|
{
|
|
success = TRUE;
|
|
}
|
|
|
|
delete [] userName;
|
|
}
|
|
}
|
|
|
|
return success;
|
|
};
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::OnGetUserName
|
|
//
|
|
// Created 06/22/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::OnGetUserName(
|
|
WCHAR *userName,
|
|
ULONG maxChars
|
|
)
|
|
{
|
|
|
|
if ( KeepUserName )
|
|
{
|
|
SetLastError(0);
|
|
|
|
return (GetWindowText(UserNameControlWindow,
|
|
userName,
|
|
maxChars + 1) > 0) ||
|
|
(GetLastError() == ERROR_SUCCESS);
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
COMBOBOXEXITEM item;
|
|
|
|
item.iItem = SendMessage(UserNameControlWindow, CB_GETCURSEL, 0, 0);
|
|
|
|
// If we are trying to match a smart card certificate, fail this:
|
|
|
|
if (UserNameCertHash != NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// If this is not a certificate, it's easy:
|
|
|
|
if ((item.iItem == CB_ERR) || (item.iItem < CertBaseInComboBox))
|
|
{
|
|
BOOL RetVal;
|
|
SetLastError(0);
|
|
|
|
RetVal = GetWindowText(UserNameControlWindow,
|
|
userName,
|
|
maxChars + 1) > 0;
|
|
|
|
if ( !RetVal ) {
|
|
return ( GetLastError() == ERROR_SUCCESS );
|
|
}
|
|
|
|
//
|
|
// Complete the typed in username
|
|
|
|
|
|
if ( Style & CRS_COMPLETEUSERNAME) {
|
|
|
|
RetVal = CompleteUserName(
|
|
userName,
|
|
maxChars,
|
|
NULL, // No target info
|
|
NULL,
|
|
0); // No target name
|
|
|
|
} else {
|
|
|
|
RetVal = TRUE;
|
|
}
|
|
|
|
return RetVal;
|
|
|
|
}
|
|
|
|
// This is a certificate, maybe from a smart card:
|
|
|
|
item.mask = CBEIF_IMAGE | CBEIF_TEXT;
|
|
item.pszText = userName;
|
|
item.cchTextMax = maxChars + 1;
|
|
|
|
if (!SendMessage(UserNameControlWindow,
|
|
CBEM_GETITEM,
|
|
0,
|
|
reinterpret_cast<LPARAM>(&item)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CERT_CREDENTIAL_INFO certCredInfo;
|
|
|
|
certCredInfo.cbSize = sizeof certCredInfo;
|
|
|
|
if (item.iItem >= SmartCardBaseInComboBox)
|
|
{
|
|
if (item.iImage == IMAGE_SMART_CARD_MISSING)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CERT_ENUM *certEnum =
|
|
reinterpret_cast<CERT_ENUM *>(
|
|
SendMessage(UserNameControlWindow,
|
|
CB_GETITEMDATA, item.iItem, 0));
|
|
|
|
// NOTE: Consider more complete error handling here.
|
|
|
|
if (certEnum != NULL)
|
|
{
|
|
DWORD length = CERT_HASH_LENGTH;
|
|
|
|
if (!CertGetCertificateContextProperty(
|
|
certEnum->pCertContext,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
static_cast<VOID *>(
|
|
certCredInfo.rgbHashOfCert),
|
|
&length))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CopyMemory(certCredInfo.rgbHashOfCert,
|
|
&CertHashes[item.iItem - CertBaseInComboBox],
|
|
CERT_HASH_LENGTH);
|
|
}
|
|
|
|
WCHAR *marshaledCred;
|
|
|
|
if (LocalCredMarshalCredentialW(CertCredential,
|
|
&certCredInfo,
|
|
&marshaledCred))
|
|
{
|
|
lstrcpyn(userName,
|
|
marshaledCred,
|
|
maxChars + 1);
|
|
|
|
LocalCredFree(static_cast<VOID *>(marshaledCred));
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
CreduiDebugLog("CreduiCredentialControl::OnGetUserName: "
|
|
"CredMarshalCredential failed: %u\n",
|
|
GetLastError());
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::OnSetPasswordA
|
|
//
|
|
// Created 06/22/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::OnSetPasswordA(
|
|
CHAR *passwordA
|
|
)
|
|
{
|
|
return SetWindowTextA(PasswordControlWindow, passwordA);
|
|
};
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::OnSetPassword
|
|
//
|
|
// Created 06/22/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::OnSetPassword(
|
|
WCHAR *password
|
|
)
|
|
{
|
|
return SetWindowText(PasswordControlWindow, password);
|
|
};
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::OnGetPasswordA
|
|
//
|
|
// Created 06/22/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::OnGetPasswordA(
|
|
CHAR *passwordA,
|
|
ULONG maxChars
|
|
)
|
|
{
|
|
if (DisabledControlMask & DISABLED_CONTROL_PASSWORD)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
SetLastError(0);
|
|
|
|
return (GetWindowTextA(PasswordControlWindow,
|
|
passwordA,
|
|
maxChars + 1) > 0) ||
|
|
(GetLastError() == ERROR_SUCCESS);
|
|
};
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::OnGetPassword
|
|
//
|
|
// Created 06/22/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::OnGetPassword(
|
|
WCHAR *password,
|
|
ULONG maxChars
|
|
)
|
|
{
|
|
if (DisabledControlMask & DISABLED_CONTROL_PASSWORD)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
SetLastError(0);
|
|
|
|
return (GetWindowText(PasswordControlWindow,
|
|
password,
|
|
maxChars + 1) > 0) ||
|
|
(GetLastError() == ERROR_SUCCESS);
|
|
};
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::OnGetUserNameLength
|
|
//
|
|
// Created 07/19/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
LONG
|
|
CreduiCredentialControl::OnGetUserNameLength()
|
|
{
|
|
COMBOBOXEXITEM item;
|
|
|
|
if (UserNameCertHash != NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
item.iItem = SendMessage(UserNameControlWindow, CB_GETCURSEL, 0, 0);
|
|
|
|
// If this is not a certificate, it's easy:
|
|
|
|
if ((item.iItem == CB_ERR) || (item.iItem < CertBaseInComboBox))
|
|
{
|
|
return GetWindowTextLength(UserNameControlWindow);
|
|
}
|
|
else
|
|
{
|
|
WCHAR userName[CREDUI_MAX_USERNAME_LENGTH + 1];
|
|
|
|
if (OnGetUserName(userName, CREDUI_MAX_USERNAME_LENGTH))
|
|
{
|
|
return lstrlen(userName);
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::OnShowBalloonA
|
|
//
|
|
// Created 06/23/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::OnShowBalloonA(
|
|
CREDUI_BALLOONA *balloonA
|
|
)
|
|
{
|
|
// If NULL was passed, this means to hide the balloon:
|
|
|
|
if (balloonA == NULL)
|
|
{
|
|
if (BalloonTip.IsVisible())
|
|
{
|
|
BalloonTip.Hide();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// Argument validation, should match OnShowBalloon:
|
|
|
|
if ((balloonA->dwVersion != 1) ||
|
|
(balloonA->pszTitleText == NULL) ||
|
|
(balloonA->pszMessageText == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ((balloonA->pszTitleText[0] == '\0') ||
|
|
(balloonA->pszMessageText[0] == '\0'))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL success = FALSE;
|
|
|
|
CREDUI_BALLOON balloon;
|
|
|
|
balloon.dwVersion = balloonA->dwVersion;
|
|
balloon.iControl = balloonA->iControl;
|
|
balloon.iIcon = balloonA->iIcon;
|
|
|
|
ULONG titleTextLength = lstrlenA(balloonA->pszTitleText);
|
|
ULONG messageTextLength = lstrlenA(balloonA->pszMessageText);
|
|
|
|
balloon.pszTitleText = new WCHAR[titleTextLength + 1];
|
|
|
|
if (balloon.pszTitleText != NULL)
|
|
{
|
|
if (MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
balloonA->pszTitleText,
|
|
-1,
|
|
balloon.pszTitleText,
|
|
titleTextLength + 1) > 0)
|
|
{
|
|
balloon.pszMessageText = new WCHAR[messageTextLength + 1];
|
|
|
|
if (balloon.pszMessageText != NULL)
|
|
{
|
|
if (MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
balloonA->pszMessageText,
|
|
-1,
|
|
balloon.pszMessageText,
|
|
messageTextLength + 1) > 0)
|
|
{
|
|
success = OnShowBalloon(&balloon);
|
|
}
|
|
|
|
delete [] balloon.pszMessageText;
|
|
}
|
|
}
|
|
|
|
delete [] balloon.pszTitleText;
|
|
}
|
|
|
|
return success;
|
|
};
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::OnShowBalloon
|
|
//
|
|
// Created 06/23/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
BOOL
|
|
CreduiCredentialControl::OnShowBalloon(
|
|
CREDUI_BALLOON *balloon
|
|
)
|
|
{
|
|
// If NULL was passed, this means to hide the balloon:
|
|
|
|
if (balloon == NULL)
|
|
{
|
|
if (BalloonTip.IsVisible())
|
|
{
|
|
BalloonTip.Hide();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// Argument validation:
|
|
|
|
if ((balloon->dwVersion != 1) ||
|
|
(balloon->pszTitleText == NULL) ||
|
|
(balloon->pszMessageText == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if ((balloon->pszTitleText[0] == L'\0') ||
|
|
(balloon->pszMessageText[0] == L'\0'))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
lstrcpyn(CreduiCustomTipInfo.Title,
|
|
balloon->pszTitleText,
|
|
CREDUI_MAX_BALLOON_TITLE_LENGTH + 1);
|
|
|
|
lstrcpyn(CreduiCustomTipInfo.Text,
|
|
balloon->pszMessageText,
|
|
CREDUI_MAX_BALLOON_MESSAGE_LENGTH + 1);
|
|
|
|
CreduiCustomTipInfo.Icon = balloon->iIcon;
|
|
|
|
// BalloonTip.SetInfo(
|
|
// (balloon->iControl == CREDUI_CONTROL_PASSWORD) ?
|
|
// PasswordControlWindow : UserNameControlWindow,
|
|
// &CreduiCustomTipInfo);
|
|
|
|
if ( balloon->iControl != CREDUI_CONTROL_PASSWORD )
|
|
BalloonTip.SetInfo( UserNameControlWindow, &CreduiCustomTipInfo);
|
|
|
|
BalloonTip.Show();
|
|
|
|
return TRUE;
|
|
};
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::OnUserNameSelectionChange
|
|
//
|
|
// Created 06/21/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
VOID
|
|
CreduiCredentialControl::OnUserNameSelectionChange()
|
|
{
|
|
COMBOBOXEXITEM item;
|
|
LRESULT current;
|
|
|
|
// Delete the user name certificate hash if the user has changed the
|
|
// selection:
|
|
|
|
if (UserNameCertHash != NULL)
|
|
{
|
|
delete [] UserNameCertHash;
|
|
UserNameCertHash = NULL;
|
|
}
|
|
|
|
current = SendMessage(UserNameControlWindow,
|
|
CB_GETCURSEL, 0, 0);
|
|
|
|
item.mask = CBEIF_IMAGE;
|
|
item.iItem = current;
|
|
|
|
SendMessage(UserNameControlWindow, CBEM_GETITEM,
|
|
0, reinterpret_cast<LPARAM>(&item));
|
|
|
|
if (current < CertBaseInComboBox)
|
|
{
|
|
EnableWindow(ViewCertControlWindow, FALSE);
|
|
DisabledControlMask |= DISABLED_CONTROL_VIEW;
|
|
|
|
SetWindowText(
|
|
PasswordStaticWindow,
|
|
CreduiStrings.PasswordStatic);
|
|
|
|
EnableWindow(PasswordControlWindow, TRUE);
|
|
EnableWindow(PasswordStaticWindow, TRUE);
|
|
DisabledControlMask &= ~DISABLED_CONTROL_PASSWORD;
|
|
|
|
WCHAR* pUserNameLabel;
|
|
if ( IsPassport )
|
|
pUserNameLabel = CreduiStrings.EmailName;
|
|
else
|
|
pUserNameLabel = CreduiStrings.UserNameStatic;
|
|
|
|
SetWindowText(
|
|
UserNameStaticWindow,
|
|
pUserNameLabel);
|
|
|
|
if (SaveControlWindow != NULL)
|
|
{
|
|
EnableWindow(SaveControlWindow, TRUE);
|
|
DisabledControlMask &= ~DISABLED_CONTROL_SAVE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetWindowText(
|
|
PasswordStaticWindow,
|
|
CreduiStrings.PinStatic);
|
|
|
|
if (item.iImage != IMAGE_SMART_CARD_MISSING)
|
|
{
|
|
EnableWindow(ViewCertControlWindow, TRUE);
|
|
DisabledControlMask &= ~DISABLED_CONTROL_VIEW;
|
|
}
|
|
else
|
|
{
|
|
EnableWindow(ViewCertControlWindow, FALSE);
|
|
DisabledControlMask |= DISABLED_CONTROL_VIEW;
|
|
}
|
|
|
|
IsChangingPassword = TRUE;
|
|
SetWindowText(PasswordControlWindow, NULL);
|
|
IsChangingPassword = FALSE;
|
|
|
|
if (current >= SmartCardBaseInComboBox)
|
|
{
|
|
EnableWindow(PasswordControlWindow, TRUE);
|
|
EnableWindow(PasswordStaticWindow, TRUE);
|
|
DisabledControlMask &= ~DISABLED_CONTROL_PASSWORD;
|
|
}
|
|
else
|
|
{
|
|
EnableWindow(PasswordControlWindow, FALSE);
|
|
EnableWindow(PasswordStaticWindow, FALSE);
|
|
DisabledControlMask |= DISABLED_CONTROL_PASSWORD;
|
|
}
|
|
|
|
SetWindowText(
|
|
UserNameStaticWindow,
|
|
item.iImage >= IMAGE_SMART_CARD ?
|
|
CreduiStrings.SmartCardStatic :
|
|
CreduiStrings.CertificateStatic);
|
|
|
|
if (SaveControlWindow != NULL)
|
|
{
|
|
EnableWindow(SaveControlWindow, FALSE);
|
|
DisabledControlMask |= DISABLED_CONTROL_SAVE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// CreduiCredentialControl::MessageHandler
|
|
//
|
|
// Called from the control window callback to handle the window messages.
|
|
//
|
|
// Arguments:
|
|
// message (in)
|
|
// wParam (in)
|
|
// lParam (in)
|
|
//
|
|
// Created 06/20/2000 johnstep (John Stephens)
|
|
//=============================================================================
|
|
|
|
LRESULT
|
|
CreduiCredentialControl::MessageHandler(
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
// If not initialized, only handle CRM_INITSTYLE:
|
|
|
|
if (!IsInitialized)
|
|
{
|
|
if (message == CRM_INITSTYLE)
|
|
{
|
|
wParam &= CRS_USERNAMES |
|
|
CRS_CERTIFICATES |
|
|
CRS_SMARTCARDS |
|
|
CRS_ADMINISTRATORS |
|
|
CRS_PREFILLADMIN |
|
|
CRS_COMPLETEUSERNAME |
|
|
CRS_SAVECHECK |
|
|
CRS_KEEPUSERNAME;
|
|
|
|
if (wParam != 0)
|
|
{
|
|
Style |= wParam;
|
|
|
|
SetWindowLong(Window,
|
|
GWL_STYLE,
|
|
GetWindowLong(Window, GWL_STYLE) | Style);
|
|
|
|
DoingCommandLine = (BOOL) lParam;
|
|
|
|
return InitWindow();
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
return DefWindowProc(Window, message, wParam, lParam);
|
|
}
|
|
}
|
|
else if (message == WM_ENABLE)
|
|
{
|
|
Enable((BOOL) wParam);
|
|
}
|
|
|
|
// Always handle smart card messages, if support is available:
|
|
|
|
if (ScardUiHandle != NULL)
|
|
{
|
|
// This function call will return TRUE if the message was handled:
|
|
|
|
if (HandleSmartCardMessages(
|
|
message,
|
|
reinterpret_cast<CERT_ENUM *>(wParam)))
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
switch (message)
|
|
{
|
|
case CRM_SETUSERNAMEMAX:
|
|
SendMessage(UserNameControlWindow, CB_LIMITTEXT, wParam, 0);
|
|
return TRUE;
|
|
|
|
case CRM_SETPASSWORDMAX:
|
|
SendMessage(PasswordControlWindow, EM_LIMITTEXT, wParam, 0);
|
|
return TRUE;
|
|
|
|
case CRM_DISABLEUSERNAME:
|
|
{
|
|
DisabledControlMask |= DISABLED_CONTROL_USERNAME;
|
|
EnableWindow(UserNameControlWindow,FALSE);
|
|
EnableWindow(UserNameStaticWindow,FALSE);
|
|
return TRUE;
|
|
}
|
|
case CRM_ENABLEUSERNAME:
|
|
{
|
|
DisabledControlMask &= ~DISABLED_CONTROL_USERNAME;
|
|
EnableWindow(UserNameControlWindow,TRUE);
|
|
EnableWindow(UserNameStaticWindow,TRUE);
|
|
return TRUE;
|
|
}
|
|
|
|
case CRM_GETUSERNAMEMAX:
|
|
return
|
|
SendMessage(
|
|
reinterpret_cast<HWND>(
|
|
SendMessage(Window, CBEM_GETEDITCONTROL, 0, 0)),
|
|
EM_GETLIMITTEXT,
|
|
0,
|
|
0);
|
|
|
|
case CRM_GETPASSWORDMAX:
|
|
return SendMessage(UserNameControlWindow, EM_GETLIMITTEXT, 0, 0);
|
|
|
|
case CRM_SETUSERNAMEA:
|
|
return OnSetUserNameA(reinterpret_cast<CHAR *>(lParam));
|
|
case CRM_SETUSERNAMEW:
|
|
return OnSetUserName(reinterpret_cast<WCHAR *>(lParam));
|
|
|
|
case CRM_GETUSERNAMEA:
|
|
return OnGetUserNameA(reinterpret_cast<CHAR *>(lParam), (ULONG) wParam);
|
|
case CRM_GETUSERNAMEW:
|
|
return OnGetUserName(reinterpret_cast<WCHAR *>(lParam), (ULONG) wParam);
|
|
|
|
case CRM_SETPASSWORDA:
|
|
return OnSetPasswordA(reinterpret_cast<CHAR *>(lParam));
|
|
case CRM_SETPASSWORDW:
|
|
return OnSetPassword(reinterpret_cast<WCHAR *>(lParam));
|
|
|
|
case CRM_GETPASSWORDA:
|
|
return OnGetPasswordA(reinterpret_cast<CHAR *>(lParam), (ULONG) wParam);
|
|
case CRM_GETPASSWORDW:
|
|
return OnGetPassword(reinterpret_cast<WCHAR *>(lParam), (ULONG) wParam);
|
|
|
|
case CRM_GETUSERNAMELENGTH:
|
|
return OnGetUserNameLength();
|
|
|
|
case CRM_GETPASSWORDLENGTH:
|
|
if (IsWindowEnabled(PasswordControlWindow))
|
|
{
|
|
return GetWindowTextLength(PasswordControlWindow);
|
|
}
|
|
return -1;
|
|
|
|
case CRM_SETFOCUS:
|
|
if ( DoingCommandLine ) {
|
|
return 0;
|
|
}
|
|
switch (wParam)
|
|
{
|
|
case CREDUI_CONTROL_USERNAME:
|
|
SetFocus(UserNameControlWindow);
|
|
return TRUE;
|
|
|
|
case CREDUI_CONTROL_PASSWORD:
|
|
if (IsWindowEnabled(PasswordControlWindow))
|
|
{
|
|
SetFocus(PasswordControlWindow);
|
|
|
|
// NOTE: Is it OK to always select the entire password text
|
|
// on this explicit set focus message?
|
|
|
|
SendMessage(PasswordControlWindow, EM_SETSEL, 0, -1);
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return 0;
|
|
|
|
case CRM_SHOWBALLOONA:
|
|
return OnShowBalloonA(reinterpret_cast<CREDUI_BALLOONA *>(lParam));
|
|
case CRM_SHOWBALLOONW:
|
|
return OnShowBalloon(reinterpret_cast<CREDUI_BALLOON *>(lParam));
|
|
|
|
case CRM_GETMINSIZE:
|
|
SIZE *minSize;
|
|
|
|
minSize = reinterpret_cast<SIZE *>(lParam);
|
|
|
|
if (minSize != NULL)
|
|
{
|
|
minSize->cx = CREDUI_CONTROL_MIN_WIDTH;
|
|
minSize->cy = CREDUI_CONTROL_MIN_HEIGHT;
|
|
|
|
if (Style & CRS_SAVECHECK )
|
|
{
|
|
minSize->cy += CREDUI_CONTROL_ADD_SAVE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
case CRM_SETCHECK:
|
|
switch (wParam)
|
|
{
|
|
case CREDUI_CONTROL_SAVE:
|
|
if ((Style & CRS_SAVECHECK ) &&
|
|
IsWindowEnabled(SaveControlWindow))
|
|
{
|
|
CheckDlgButton(Window, IDC_SAVE,
|
|
lParam ? BST_CHECKED : BST_UNCHECKED);
|
|
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
}
|
|
return FALSE;
|
|
|
|
case CRM_GETCHECK:
|
|
switch (wParam)
|
|
{
|
|
case CREDUI_CONTROL_SAVE:
|
|
return
|
|
(Style & CRS_SAVECHECK ) &&
|
|
IsWindowEnabled(SaveControlWindow) &&
|
|
IsDlgButtonChecked(Window, IDC_SAVE);
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
case CRM_DOCMDLINE:
|
|
ASSERT( DoingCommandLine );
|
|
|
|
//
|
|
// For smartcards,
|
|
// just start the timer and we'll prompt when the timer has gone off.
|
|
//
|
|
|
|
TargetName = (LPWSTR)lParam;
|
|
if ( Style & CRS_SMARTCARDS) {
|
|
DWORD WinStatus;
|
|
|
|
Heartbeats = 0;
|
|
{
|
|
WCHAR szMsg[CREDUI_MAX_CMDLINE_MSG_LENGTH + 1];
|
|
szMsg[0] = 0;
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
CreduiInstance,
|
|
IDS_READING_SMARTCARDS,
|
|
0,
|
|
szMsg,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
NULL);
|
|
CredPutStdout(szMsg);
|
|
}
|
|
|
|
if ( SetTimer ( Window, CREDUI_HEARTBEAT_TIMER, CREDUI_HEARTBEAT_TIMER_VALUE, NULL ) == 0 ) {
|
|
// bail out of our wait loop if we couldn't set a timer
|
|
return GetLastError();
|
|
}
|
|
|
|
|
|
//
|
|
// For passwords,
|
|
// just do the prompt to save.
|
|
//
|
|
|
|
} else {
|
|
|
|
CmdlineSavePrompt();
|
|
PostQuitMessage( NO_ERROR );
|
|
}
|
|
|
|
return NO_ERROR;
|
|
|
|
|
|
case WM_HELP:
|
|
return OnHelpInfo(lParam);
|
|
|
|
case WM_SETFONT:
|
|
// Forward font setting from dialog to each control, except the
|
|
// password control since we use a special font there:
|
|
|
|
if (UserNameStaticWindow != NULL)
|
|
{
|
|
SendMessage(UserNameStaticWindow, message, wParam, lParam);
|
|
}
|
|
|
|
if (UserNameControlWindow != NULL)
|
|
{
|
|
SendMessage(UserNameControlWindow, message, wParam, lParam);
|
|
}
|
|
|
|
if (ViewCertControlWindow != NULL)
|
|
{
|
|
SendMessage(ViewCertControlWindow, message, wParam, lParam);
|
|
}
|
|
|
|
if (PasswordStaticWindow != NULL)
|
|
{
|
|
SendMessage(PasswordStaticWindow, message, wParam, lParam);
|
|
}
|
|
|
|
if (PasswordControlWindow != NULL)
|
|
{
|
|
SendMessage(PasswordControlWindow, message, wParam, lParam);
|
|
}
|
|
|
|
if (SaveControlWindow != NULL)
|
|
{
|
|
SendMessage(SaveControlWindow, message, wParam, lParam);
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam))
|
|
{
|
|
case IDC_VIEW_CERT:
|
|
ViewCertificate((INT)
|
|
SendMessage(UserNameControlWindow,
|
|
CB_GETCURSEL, 0, 0));
|
|
return 0;
|
|
|
|
case IDC_PASSWORD:
|
|
if (HIWORD(wParam) == EN_CHANGE)
|
|
{
|
|
// Always send the change message?
|
|
|
|
SendMessage(
|
|
GetParent(Window),
|
|
WM_COMMAND,
|
|
MAKELONG(GetWindowLongPtr(Window, GWLP_ID),
|
|
CRN_PASSWORDCHANGE),
|
|
reinterpret_cast<LPARAM>(Window));
|
|
}
|
|
return 0;
|
|
|
|
case IDC_USERNAME:
|
|
switch (HIWORD(wParam))
|
|
{
|
|
case CBN_EDITCHANGE:
|
|
case CBN_DROPDOWN:
|
|
case CBN_KILLFOCUS:
|
|
if ((HIWORD(wParam) != CBN_EDITCHANGE) || !IsChangingUserName)
|
|
{
|
|
if (BalloonTip.IsVisible())
|
|
{
|
|
BalloonTip.Hide();
|
|
}
|
|
}
|
|
|
|
if (HIWORD(wParam) == CBN_EDITCHANGE)
|
|
{
|
|
// Always send the change message?
|
|
|
|
SendMessage(
|
|
GetParent(Window),
|
|
WM_COMMAND,
|
|
MAKELONG(GetWindowLongPtr(Window, GWLP_ID),
|
|
CRN_USERNAMECHANGE),
|
|
reinterpret_cast<LPARAM>(Window));
|
|
|
|
// If the name has changed as a result of user editing,
|
|
// reset to user name settings:
|
|
|
|
BOOL isDropped = (BOOL)
|
|
SendMessage(UserNameControlWindow,
|
|
CB_GETDROPPEDSTATE, 0, 0);
|
|
|
|
if (isDropped)
|
|
{
|
|
OnUserNameSelectionChange();
|
|
|
|
RECT rect;
|
|
|
|
GetClientRect(UserNameControlWindow, &rect);
|
|
InvalidateRect(UserNameControlWindow, &rect, FALSE);
|
|
|
|
SendMessage(Window,
|
|
CB_SETCURSEL,
|
|
SendMessage(Window, CB_GETCURSEL, 0, 0),
|
|
0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (IsChangingUserName)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (((UserNameCertHash != NULL) ||
|
|
(SendMessage(UserNameControlWindow,
|
|
CB_GETCURSEL, 0, 0) >= CertBaseInComboBox)) &&
|
|
!isDropped)
|
|
{
|
|
if (UserNameCertHash != NULL)
|
|
{
|
|
delete [] UserNameCertHash;
|
|
UserNameCertHash = NULL;
|
|
}
|
|
|
|
if (!SendMessage(UserNameControlWindow,
|
|
CB_GETDROPPEDSTATE, 0, 0))
|
|
{
|
|
SetFocus(UserNameControlWindow);
|
|
|
|
if (SendMessage(UserNameControlWindow,
|
|
CB_GETCURSEL, 0, 0) == CB_ERR)
|
|
{
|
|
IsChangingUserName = TRUE;
|
|
UserNameComboBox.Update(
|
|
-1,
|
|
L"",
|
|
IMAGE_USERNAME);
|
|
IsChangingUserName = FALSE;
|
|
|
|
IsChangingPassword = TRUE;
|
|
SetWindowText(PasswordControlWindow, NULL);
|
|
IsChangingPassword = FALSE;
|
|
|
|
OnUserNameSelectionChange();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (HIWORD(wParam) == CBN_DROPDOWN)
|
|
{
|
|
if (UserNameCertHash != NULL)
|
|
{
|
|
delete [] UserNameCertHash;
|
|
UserNameCertHash = NULL;
|
|
|
|
IsChangingUserName = TRUE;
|
|
UserNameComboBox.Update(
|
|
-1,
|
|
L"",
|
|
IMAGE_USERNAME);
|
|
IsChangingUserName = FALSE;
|
|
|
|
IsChangingPassword = TRUE;
|
|
SetWindowText(PasswordControlWindow, NULL);
|
|
IsChangingPassword = FALSE;
|
|
|
|
OnUserNameSelectionChange();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
case CBN_SELCHANGE:
|
|
OnUserNameSelectionChange();
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case IDC_SAVE:
|
|
if (HIWORD(wParam) == BN_CLICKED)
|
|
{
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
}
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
if (FirstPaint && GetUpdateRect(Window, NULL, FALSE))
|
|
{
|
|
FirstPaint = FALSE;
|
|
|
|
if (ShowBalloonTip)
|
|
{
|
|
ShowBalloonTip = FALSE;
|
|
BalloonTip.Show();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_TIMER:
|
|
if ( wParam == CREDUI_HEARTBEAT_TIMER )
|
|
{
|
|
|
|
Heartbeats++;
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: thump thump\n",this->Window);
|
|
#endif
|
|
|
|
|
|
//
|
|
// If we've waited long enough,
|
|
// or all cards have been read,
|
|
// process the cards.
|
|
//
|
|
|
|
if ( Heartbeats > CREDUI_MAX_HEARTBEATS ||
|
|
( Heartbeats > 2 && SmartCardReadCount == 0 )) {
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: Heartbeat timeout\n",this->Window);
|
|
#endif
|
|
|
|
fputs( "\n", stdout );
|
|
KillTimer ( Window, CREDUI_HEARTBEAT_TIMER );
|
|
CmdlineSmartCardPrompt();
|
|
|
|
//
|
|
// If we're going to wait longer,
|
|
// let the user know we're making progress.
|
|
//
|
|
} else {
|
|
fputs( ".", stdout );
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
if (PasswordControlWindow != NULL)
|
|
{
|
|
SetWindowText(PasswordControlWindow, NULL);
|
|
DestroyWindow(PasswordControlWindow);
|
|
PasswordControlWindow = NULL;
|
|
}
|
|
|
|
if (PasswordStaticWindow != NULL)
|
|
{
|
|
DestroyWindow(PasswordStaticWindow);
|
|
PasswordStaticWindow = NULL;
|
|
}
|
|
|
|
if (ViewCertControlWindow != NULL)
|
|
{
|
|
DestroyWindow(ViewCertControlWindow);
|
|
ViewCertControlWindow = NULL;
|
|
}
|
|
|
|
if (UserNameControlWindow != NULL)
|
|
{
|
|
DestroyWindow(UserNameControlWindow);
|
|
UserNameControlWindow = NULL;
|
|
}
|
|
|
|
if (UserNameStaticWindow != NULL)
|
|
{
|
|
DestroyWindow(UserNameStaticWindow);
|
|
UserNameStaticWindow = NULL;
|
|
}
|
|
|
|
if (ScardUiHandle != NULL)
|
|
{
|
|
#ifdef SCARDREPORTS
|
|
CreduiDebugLog("CREDUI: Call to SCardUIExit\n");
|
|
#endif
|
|
SCardUIExit(ScardUiHandle);
|
|
ScardUiHandle = NULL;
|
|
}
|
|
|
|
if (UserNameCertHash != NULL)
|
|
{
|
|
delete [] UserNameCertHash;
|
|
UserNameCertHash = NULL;
|
|
}
|
|
|
|
if (CertCount > 0)
|
|
{
|
|
ASSERT(CertHashes != NULL);
|
|
|
|
delete [] CertHashes;
|
|
CertHashes = NULL;
|
|
CertCount = 0;
|
|
}
|
|
|
|
if ( InitialUserName != NULL ) {
|
|
delete InitialUserName;
|
|
InitialUserName = NULL;
|
|
}
|
|
|
|
// Only call CoUninitialize if we successfully initialized for STA:
|
|
|
|
if (IsAutoComplete)
|
|
{
|
|
CoUninitialize();
|
|
}
|
|
|
|
return 0;
|
|
|
|
case WM_NCDESTROY:
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
return DefWindowProc(Window, message, wParam, lParam);
|
|
}
|
|
|
|
|
|
BOOL CreduiCredentialControl::GetSmartCardInfo(
|
|
IN DWORD SmartCardIndex,
|
|
IN DWORD BufferLength,
|
|
OUT LPWSTR Buffer,
|
|
OUT BOOL *IsValid,
|
|
OUT CERT_ENUM **CertEnum OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Routine to get the smart card info for a smart card in the combo box
|
|
|
|
Arguments:
|
|
|
|
SmartCardIndex - Index of the smart card relative to SmartCardBaseInComboBox
|
|
|
|
BufferLength - Specifies the length of Buffer (in characters)
|
|
|
|
Buffer - Specifies the buffer to return the text for the smart card
|
|
|
|
IsValid - Return TRUE if the smartcard is valid
|
|
Returns FALSE otherwise
|
|
|
|
CertEnum - If specified, returns the description of the cert on the smartcard
|
|
This field is should be ignore if IsValid is returns false
|
|
|
|
Return Values:
|
|
|
|
Returns TRUE if Buffer and IsValid are filled in.
|
|
|
|
--*/
|
|
{
|
|
COMBOBOXEXITEM item;
|
|
|
|
//
|
|
// Get the item from the control
|
|
//
|
|
|
|
item.iItem = SmartCardBaseInComboBox + SmartCardIndex;
|
|
item.mask = CBEIF_IMAGE | CBEIF_TEXT;
|
|
item.pszText = Buffer;
|
|
item.cchTextMax = BufferLength;
|
|
|
|
if (!SendMessage(UserNameControlWindow,
|
|
CBEM_GETITEM,
|
|
0,
|
|
reinterpret_cast<LPARAM>(&item)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*IsValid = (item.iImage == IMAGE_SMART_CARD);
|
|
|
|
if ( CertEnum != NULL) {
|
|
if ( *IsValid ) {
|
|
|
|
*CertEnum = (CERT_ENUM *) SendMessage( UserNameControlWindow,
|
|
CB_GETITEMDATA, item.iItem, 0);
|
|
|
|
// NOTE: Consider more complete error handling here.
|
|
|
|
if ( *CertEnum == NULL) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
LPWSTR CreduiCredentialControl::MatchSmartCard(
|
|
IN DWORD SmartCardCount,
|
|
IN LPWSTR UserName,
|
|
OUT LPDWORD RetCertIndex,
|
|
OUT CERT_ENUM **RetCertEnum
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the smart card that matches UserName.
|
|
|
|
Arguments:
|
|
|
|
SmartCardCount - specifies the number of smart cards to search
|
|
|
|
UserName - specifies the user name to match
|
|
|
|
RetCertIndex - returns an index to the found smart card.
|
|
|
|
RetCertEnum - returns the description of the cert on the smartcard
|
|
|
|
Return Values:
|
|
|
|
Returns NULL if UserName matches one of the smart cards
|
|
|
|
On failure, returns a printf-style format string describing the error
|
|
|
|
--*/
|
|
{
|
|
WCHAR SmartCardText[CREDUI_MAX_USERNAME_LENGTH + 1];
|
|
DWORD i;
|
|
BOOL SmartCardValid;
|
|
CERT_ENUM *CertEnum;
|
|
CERT_ENUM *SavedCertEnum = NULL;
|
|
DWORD SavedCertIndex = 0;
|
|
|
|
|
|
//
|
|
// Loop through the list of smart cards seeing if we see a match
|
|
//
|
|
|
|
for ( i=0; i<SmartCardCount; i++ ) {
|
|
|
|
if ( !GetSmartCardInfo( i, CREDUI_MAX_USERNAME_LENGTH, SmartCardText, &SmartCardValid, &CertEnum ) ) {
|
|
//return CreduiStrings.NoUsernameMatch;
|
|
return (LPWSTR) IDS_NO_USERNAME_MATCH;
|
|
}
|
|
|
|
if ( !SmartCardValid ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// If the username is marshaled,
|
|
// compare the marshaled strings.
|
|
//
|
|
|
|
if ( LocalCredIsMarshaledCredentialW( UserName ) ) {
|
|
WCHAR szTestmarshall[CREDUI_MAX_USERNAME_LENGTH+1];
|
|
// see if this is the marshalled cred
|
|
if ( CredUIMarshallNode ( CertEnum, szTestmarshall ) )
|
|
{
|
|
if ( wcscmp ( szTestmarshall, UserName) == 0 ) {
|
|
*RetCertEnum = CertEnum;
|
|
*RetCertIndex = i;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the username is not marshalled,
|
|
// just match a substring of the name
|
|
//
|
|
|
|
} else if ( LookForUserNameMatch ( UserName, SmartCardText ) ) {
|
|
|
|
//
|
|
// If we already found a match,
|
|
// complain about the ambiguity.
|
|
//
|
|
|
|
if ( SavedCertEnum != NULL ) {
|
|
//return CreduiStrings.ManyUsernameMatch;
|
|
return (LPWSTR) IDS_MANY_USERNAME_MATCH;
|
|
}
|
|
|
|
SavedCertEnum = CertEnum;
|
|
SavedCertIndex = i;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If we didn't find a match,
|
|
// fail
|
|
//
|
|
|
|
if ( SavedCertEnum == NULL) {
|
|
//return CreduiStrings.NoUsernameMatch;
|
|
return (LPWSTR) IDS_NO_USERNAME_MATCH;
|
|
}
|
|
|
|
*RetCertEnum = SavedCertEnum;
|
|
*RetCertIndex = SavedCertIndex;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
void CreduiCredentialControl::CmdlineSmartCardPrompt()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Command line code to select a smartcard from the list of ones available.
|
|
|
|
Post a WM_QUIT message to terminate message processing. The status of the operation
|
|
is returned in wParam.
|
|
UserName and Password strings set in their respective controls.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
|
|
LONG ComboBoxItemCount;
|
|
DWORD SmartCardCount;
|
|
DWORD ValidSmartCardCount = 0;
|
|
DWORD InvalidSmartCardCount = 0;
|
|
DWORD KnownGoodCard = 0;
|
|
|
|
DWORD i;
|
|
DWORD_PTR rgarg[2]; // at most 2 substitution arguments
|
|
|
|
WCHAR szMsg[CREDUI_MAX_CMDLINE_MSG_LENGTH + 1];
|
|
WCHAR UserName[CREDUI_MAX_USERNAME_LENGTH + 1];
|
|
WCHAR Password[CREDUI_MAX_PASSWORD_LENGTH + 1];
|
|
|
|
WCHAR SmartCardText[CREDUI_MAX_USERNAME_LENGTH + 1];
|
|
BOOL SmartCardValid;
|
|
|
|
CERT_ENUM *SavedCertEnum = NULL;
|
|
DWORD SavedCertIndex = 0;
|
|
LPWSTR ErrorString = NULL;
|
|
|
|
//
|
|
// Compute the number of smart card entries
|
|
//
|
|
|
|
ComboBoxItemCount = (LONG) SendMessage(UserNameControlWindow, CB_GETCOUNT, 0, 0);
|
|
|
|
if ( ComboBoxItemCount == CB_ERR ||
|
|
ComboBoxItemCount <= SmartCardBaseInComboBox ) {
|
|
|
|
// Didn't find any smart card readers
|
|
szMsg[0] = 0;
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
CreduiInstance,
|
|
IDS_CHOOSE_A_CERT,
|
|
0,
|
|
szMsg,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
NULL);
|
|
CredPutStdout(szMsg);
|
|
WinStatus = ERROR_CANCELLED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
SmartCardCount = ComboBoxItemCount - SmartCardBaseInComboBox;
|
|
|
|
|
|
//
|
|
// Get a count of the number of valid and invalid smartcards
|
|
//
|
|
|
|
for ( i=0; i<SmartCardCount; i++ ) {
|
|
|
|
if ( !GetSmartCardInfo( i, CREDUI_MAX_USERNAME_LENGTH, SmartCardText, &SmartCardValid, NULL ) ) {
|
|
WinStatus = ERROR_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( SmartCardValid ) {
|
|
ValidSmartCardCount ++;
|
|
KnownGoodCard = i;
|
|
} else {
|
|
InvalidSmartCardCount ++;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Get the username passed into the API
|
|
//
|
|
// Can't do a GetWindowText( UserNameControlWindow ) since the cert control has
|
|
// a non-editable window so we can't set the window text
|
|
//
|
|
|
|
if ( InitialUserName != NULL) {
|
|
lstrcpyn( UserName, InitialUserName, CREDUI_MAX_USERNAME_LENGTH );
|
|
} else {
|
|
UserName[0] = '\0';
|
|
}
|
|
|
|
//
|
|
// If the caller passed a name into the API,
|
|
// check to see if the name matches one of the smart cards.
|
|
//
|
|
|
|
if ( UserName[0] != '\0' ) {
|
|
|
|
//
|
|
// Find the smartcard that matches the username
|
|
//
|
|
|
|
ErrorString = MatchSmartCard(
|
|
SmartCardCount,
|
|
UserName,
|
|
&SavedCertIndex,
|
|
&SavedCertEnum );
|
|
|
|
if ( ErrorString == NULL ) {
|
|
WinStatus = NO_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Report any errors to the user
|
|
//
|
|
|
|
if ( InvalidSmartCardCount ) {
|
|
|
|
szMsg[0] = 0;
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
CreduiInstance,
|
|
IDS_CMDLINE_ERRORS,
|
|
0,
|
|
szMsg,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
NULL);
|
|
CredPutStdout(szMsg);
|
|
|
|
for ( i=0; i<SmartCardCount; i++ ) {
|
|
|
|
if ( !GetSmartCardInfo( i, CREDUI_MAX_USERNAME_LENGTH, SmartCardText, &SmartCardValid, NULL ) ) {
|
|
WinStatus = ERROR_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !SmartCardValid ) {
|
|
// GetSmartCardInfo() fills SmartCardText, which may include user's name
|
|
CredPutStdout( SmartCardText );
|
|
//swprintf(szMsg,CreduiStrings.CmdLineError,i+1);
|
|
szMsg[0] = 0;
|
|
INT j = i+1;
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
Instance,
|
|
IDS_CMDLINE_ERROR,
|
|
0,
|
|
szMsg,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
(va_list *) &j);
|
|
CredPutStdout( szMsg);
|
|
}
|
|
|
|
}
|
|
CredPutStdout( L"\n" );
|
|
|
|
}
|
|
|
|
//
|
|
// If the caller passed a name into the API,
|
|
// simply report that we couldn't find the cert and return
|
|
//
|
|
|
|
if ( UserName[0] != '\0' ) {
|
|
|
|
// ErrorString is expected to be NoMatch or ManyMatch
|
|
//_snwprintf(szMsg,
|
|
// CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
// ErrorString,
|
|
// UserName);
|
|
// szMsg[0] = 0;
|
|
//szMsg[CREDUI_MAX_CMDLINE_MSG_LENGTH] = L'\0';
|
|
szMsg[0] = 0;
|
|
// Note that ErrorString returned from MatchSmartCard has type LPWSTR, but it is actually
|
|
// a message ID. We take the low word of the pointer as the ID.
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
CreduiInstance,
|
|
LOWORD(ErrorString),
|
|
0,
|
|
szMsg,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
(va_list *) UserName);
|
|
|
|
CredPutStdout( szMsg );
|
|
WinStatus = ERROR_CANCELLED;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// If there was only one smartcard and it was valid,
|
|
// use it
|
|
//
|
|
|
|
// if ( ValidSmartCardCount == 1 && InvalidSmartCardCount == 0 ) {
|
|
// gm: If list can only contain one item, use it.
|
|
if ( ValidSmartCardCount == 1 ) {
|
|
|
|
if ( !GetSmartCardInfo( KnownGoodCard, CREDUI_MAX_USERNAME_LENGTH, SmartCardText, &SmartCardValid, &SavedCertEnum ) ) {
|
|
WinStatus = ERROR_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
SavedCertIndex = KnownGoodCard;
|
|
WinStatus = NO_ERROR;
|
|
goto Cleanup;
|
|
|
|
//
|
|
// If there were valid smartcard,
|
|
// List the valid smartcards for the user
|
|
//
|
|
|
|
} else if ( ValidSmartCardCount ) {
|
|
|
|
//
|
|
// Tell user about all certs
|
|
//
|
|
|
|
szMsg[0] = 0;
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
CreduiInstance,
|
|
IDS_CHOOSE_A_CERT,
|
|
0,
|
|
szMsg,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
NULL);
|
|
CredPutStdout(szMsg);
|
|
|
|
for ( i=0; i<SmartCardCount; i++ ) {
|
|
|
|
if ( !GetSmartCardInfo( i, CREDUI_MAX_USERNAME_LENGTH, SmartCardText, &SmartCardValid, NULL ) ) {
|
|
WinStatus = ERROR_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( SmartCardValid ) {
|
|
//swprintf(szMsg,CreduiStrings.CmdLinePreamble,i+1,SmartCardText);
|
|
szMsg[0] = 0;
|
|
rgarg[0] = i+1;
|
|
rgarg[1] = (DWORD_PTR) SmartCardText;
|
|
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
Instance,
|
|
IDS_CMDLINE_PREAMBLE,
|
|
0,
|
|
szMsg,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
(va_list *) &rgarg);
|
|
CredPutStdout( szMsg );
|
|
}
|
|
|
|
}
|
|
CredPutStdout( L"\n" );
|
|
|
|
//
|
|
// Ask user to enter the reader number of one of the smartcards
|
|
//
|
|
|
|
//_snwprintf(szMsg,
|
|
// CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
// CreduiStrings.SCardPrompt,
|
|
// TargetName);
|
|
//szMsg[CREDUI_MAX_CMDLINE_MSG_LENGTH] = L'\0';
|
|
szMsg[0] = 0;
|
|
rgarg[0] = (DWORD_PTR)TargetName;
|
|
rgarg[1] = 0;
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
Instance,
|
|
IDS_SCARD_PROMPT,
|
|
0,
|
|
szMsg,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
(va_list *) rgarg);
|
|
CredPutStdout( szMsg );
|
|
|
|
CredGetStdin( UserName, CREDUI_MAX_USERNAME_LENGTH, TRUE );
|
|
|
|
if ( wcslen (UserName ) == 0 ) {
|
|
szMsg[0] = 0;
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
CreduiInstance,
|
|
IDS_NO_SCARD_ENTERED ,
|
|
0,
|
|
szMsg,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
NULL);
|
|
CredPutStdout(szMsg);
|
|
WinStatus = ERROR_CANCELLED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Find the smartcard that matches the username
|
|
//
|
|
INT iWhich = 0;
|
|
WCHAR *pc = NULL;
|
|
|
|
iWhich = wcstol(UserName,&pc,10);
|
|
if (pc == UserName) {
|
|
// Invalid if at least one char was not numeric
|
|
szMsg[0] = 0;
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
CreduiInstance,
|
|
IDS_READERINVALID,
|
|
0,
|
|
szMsg,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
NULL);
|
|
CredPutStdout(szMsg);
|
|
WinStatus = ERROR_CANCELLED;
|
|
goto Cleanup;
|
|
}
|
|
// convert 1 based UI number to 0 based internal index
|
|
if (iWhich > 0) iWhich -= 1;
|
|
if ( !GetSmartCardInfo( iWhich, CREDUI_MAX_USERNAME_LENGTH, SmartCardText, &SmartCardValid, &SavedCertEnum ) ) {
|
|
// Invalid if that indexed card did not read correctly
|
|
szMsg[0] = 0;
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
CreduiInstance,
|
|
IDS_READERINVALID,
|
|
0,
|
|
szMsg,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
NULL);
|
|
CredPutStdout(szMsg);
|
|
WinStatus = ERROR_CANCELLED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// At this point, a valid number was entered, and an attempt to read the card made
|
|
// GetSmartCardInfo() returned OK, but SmartCardValid may still be false
|
|
if (!SmartCardValid)
|
|
{
|
|
szMsg[0] = 0;
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
CreduiInstance,
|
|
IDS_READERINVALID,
|
|
0,
|
|
szMsg,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
NULL);
|
|
CredPutStdout(szMsg);
|
|
WinStatus = ERROR_CANCELLED;
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
SavedCertIndex = iWhich;
|
|
WinStatus = NO_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
WinStatus = ERROR_CANCELLED;
|
|
|
|
|
|
//
|
|
// Complete the operation.
|
|
//
|
|
// WinStatus is the status of the operation so far
|
|
// if NO_ERROR, SavedCertEnum is the description of the cert to use, and
|
|
// SavedCertIndex is the index to the selected cert.
|
|
//
|
|
Cleanup:
|
|
|
|
|
|
if ( WinStatus == NO_ERROR) {
|
|
|
|
if ( CredUIMarshallNode ( SavedCertEnum, UserName ) ) {
|
|
|
|
//
|
|
// Save the username
|
|
//
|
|
|
|
UserNameSelection = SmartCardBaseInComboBox + SavedCertIndex;
|
|
IsChangingUserName = TRUE;
|
|
SendMessage(UserNameControlWindow,
|
|
CB_SETCURSEL,
|
|
UserNameSelection,
|
|
0);
|
|
IsChangingUserName = FALSE;
|
|
|
|
|
|
|
|
//
|
|
// Prompt for the pin
|
|
//
|
|
|
|
//CredPutStdout( CreduiStrings.PinPrompt );
|
|
//swprintf(szMsg,CreduiStrings.CmdLineThisCard,SavedCertIndex + 1);
|
|
szMsg[0] = 0;
|
|
i = SavedCertIndex + 1;
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
Instance,
|
|
IDS_CMDLINE_THISCARD,
|
|
0,
|
|
szMsg,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
(va_list *) &i);
|
|
CredPutStdout(szMsg);
|
|
|
|
CredGetStdin( Password, CREDUI_MAX_PASSWORD_LENGTH, FALSE );
|
|
|
|
//
|
|
// Save the pin
|
|
//
|
|
|
|
if (!OnSetPassword( Password ) ) {
|
|
WinStatus = GetLastError();
|
|
|
|
CreduiDebugLog("CreduiCredentialControl::CmdlineSmartCardPrompt: "
|
|
"OnSetPassword failed: %u\n",
|
|
WinStatus );
|
|
}
|
|
|
|
|
|
//
|
|
// Prompt whether the save the cred or not
|
|
//
|
|
|
|
CmdlineSavePrompt();
|
|
|
|
|
|
} else {
|
|
WinStatus = GetLastError();
|
|
|
|
CreduiDebugLog("CreduiCredentialControl::CmdlineSmartCardPrompt: "
|
|
"CredMarshalCredential failed: %u\n",
|
|
WinStatus );
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Tell our parent window that we're done prompting
|
|
//
|
|
|
|
PostQuitMessage( WinStatus );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void CreduiCredentialControl::CmdlineSavePrompt()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Command line code to prompt for saving the credential
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Values:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
WCHAR szMsg[CREDUI_MAX_CMDLINE_MSG_LENGTH + 1];
|
|
WCHAR szY[CREDUI_MAX_CMDLINE_MSG_LENGTH + 1];
|
|
WCHAR szN[CREDUI_MAX_CMDLINE_MSG_LENGTH + 1];
|
|
|
|
//
|
|
// Only prompt if we've been asked to display the checkbox
|
|
//
|
|
|
|
while ( Style & CRS_SAVECHECK ) {
|
|
WCHAR szMsg[CREDUI_MAX_CMDLINE_MSG_LENGTH+1];
|
|
|
|
// Fetch the strings one by one from the messages, and cobble them together
|
|
WCHAR *rgsz[2];
|
|
szY[0] = 0;
|
|
szN[0] = 0;
|
|
rgsz[0] = szY;
|
|
rgsz[1] = szN;
|
|
szMsg[0] = 0;
|
|
// Fetch yes and no strings
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
CreduiInstance,
|
|
IDS_YES_TEXT,
|
|
0,
|
|
szY,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
NULL);
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
CreduiInstance,
|
|
IDS_NO_TEXT,
|
|
0,
|
|
szN,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
NULL);
|
|
// Arg substitute them into the prompt
|
|
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
CreduiInstance,
|
|
IDS_SAVE_PROMPT,
|
|
0,
|
|
szMsg,
|
|
CREDUI_MAX_CMDLINE_MSG_LENGTH,
|
|
(va_list *) rgsz);
|
|
|
|
szMsg[CREDUI_MAX_CMDLINE_MSG_LENGTH] = L'\0';
|
|
CredPutStdout( szMsg );
|
|
|
|
CredGetStdin( szMsg, CREDUI_MAX_CMDLINE_MSG_LENGTH, TRUE );
|
|
|
|
// if ( toupper(szMsg[0]) == toupper(CreduiStrings.YesText[0]) ) {
|
|
if ( toupper(szMsg[0]) == toupper(szY[0]) ) {
|
|
Credential_CheckSave( Window, TRUE );
|
|
break;
|
|
// } else if ( toupper(szMsg[0]) == toupper(CreduiStrings.NoText[0]) ) {
|
|
} else if ( toupper(szMsg[0]) == toupper(szN[0]) ) {
|
|
Credential_CheckSave( Window, FALSE );
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
UINT CreduiCredentialControl::MapID(UINT uiID) {
|
|
switch(uiID) {
|
|
|
|
case IDC_USERNAME:
|
|
return IDH_USERNAMEEDIT;
|
|
case IDC_PASSWORD:
|
|
return IDH_PASSWORDEDIT;
|
|
case IDC_SAVE:
|
|
return IDH_SAVECHECKBOX;
|
|
default:
|
|
return IDS_NOHELP;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
CreduiCredentialControl::OnHelpInfo(LPARAM lp) {
|
|
|
|
HELPINFO* pH;
|
|
INT iMapped;
|
|
pH = (HELPINFO *) lp;
|
|
HH_POPUP stPopUp;
|
|
RECT rcW;
|
|
UINT gID;
|
|
|
|
gID = pH->iCtrlId;
|
|
iMapped = MapID(gID);
|
|
|
|
if (iMapped == 0) return TRUE;
|
|
|
|
if (IDS_NOHELP != iMapped) {
|
|
|
|
memset(&stPopUp,0,sizeof(stPopUp));
|
|
stPopUp.cbStruct = sizeof(HH_POPUP);
|
|
stPopUp.hinst = Instance;
|
|
stPopUp.idString = iMapped;
|
|
stPopUp.pszText = NULL;
|
|
stPopUp.clrForeground = -1;
|
|
stPopUp.clrBackground = -1;
|
|
stPopUp.rcMargins.top = -1;
|
|
stPopUp.rcMargins.bottom = -1;
|
|
stPopUp.rcMargins.left = -1;
|
|
stPopUp.rcMargins.right = -1;
|
|
stPopUp.pszFont = NULL;
|
|
if (GetWindowRect((HWND)pH->hItemHandle,&rcW)) {
|
|
stPopUp.pt.x = (rcW.left + rcW.right) / 2;
|
|
stPopUp.pt.y = (rcW.top + rcW.bottom) / 2;
|
|
}
|
|
else stPopUp.pt = pH->MousePos;
|
|
HtmlHelp((HWND) pH->hItemHandle,NULL,HH_DISPLAY_TEXT_POPUP,(DWORD_PTR) &stPopUp);
|
|
}
|
|
return TRUE;
|
|
}
|