From cdbe54e60542231f832d59389381bf9b56b710be Mon Sep 17 00:00:00 2001 From: Mounir IDRASSI Date: Fri, 30 Dec 2016 12:17:09 +0100 Subject: Windows: Implement Secure Desktop for password entry. Add option and command line switch to activate it. --- src/Common/Dlgcode.c | 176 +++++++++++++++++++++++++++++++++++++++++++ src/Common/Dlgcode.h | 4 + src/Common/Language.xml | 1 + src/ExpandVolume/WinMain.cpp | 4 +- src/Mount/Mount.c | 32 +++++++- src/Mount/Mount.rc | 26 ++++--- src/Mount/Resource.h | 3 +- 7 files changed, 231 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/Common/Dlgcode.c b/src/Common/Dlgcode.c index 001d7102..3fc5c06a 100644 --- a/src/Common/Dlgcode.c +++ b/src/Common/Dlgcode.c @@ -26,6 +26,11 @@ #include #include #include +#ifdef TCMOUNT +#include +#include +#include +#endif #include "Resource.h" @@ -108,6 +113,9 @@ BOOL bShowDisconnectedNetworkDrives = FALSE; BOOL bHideWaitingDialog = FALSE; BOOL bCmdHideWaitingDialog = FALSE; BOOL bCmdHideWaitingDialogValid = FALSE; +BOOL bUseSecureDesktop = FALSE; +BOOL bCmdUseSecureDesktop = FALSE; +BOOL bCmdUseSecureDesktopValid = FALSE; BOOL bStartOnLogon = FALSE; BOOL bMountDevicesOnLogon = FALSE; BOOL bMountFavoritesOnLogon = FALSE; @@ -12225,3 +12233,171 @@ BOOL DeleteDirectory (const wchar_t* szDirName) } return bStatus; } + +#ifdef TCMOUNT +/*********************************************************************/ + +static BOOL GenerateRandomString (HWND hwndDlg, LPTSTR szName, DWORD maxCharsCount) +{ + BOOL bRet = FALSE; + if (Randinit () != ERR_SUCCESS) + { + handleError (hwndDlg, (CryptoAPILastError == ERROR_SUCCESS)? ERR_RAND_INIT_FAILED : ERR_CAPI_INIT_FAILED, SRC_POS); + } + else + { + BYTE* indexes = (BYTE*) malloc (maxCharsCount + 1); + bRet = RandgetBytesFull (hwndDlg, indexes, maxCharsCount + 1, TRUE, TRUE); + if (bRet) + { + static LPCTSTR chars = _T("0123456789@#$%^&_-*abcdefghijklmnopqrstuvwxyz"); + DWORD i, charsLen = (DWORD) _tcslen (chars); + DWORD effectiveLen = (indexes[0] % (64 - 16)) + 16; // random length between 16 to 64 + effectiveLen = (effectiveLen > maxCharsCount)? maxCharsCount : effectiveLen; + + for (i = 0; i < effectiveLen; i++) + { + szName[i] = chars[indexes[i + 1] % charsLen]; + } + + szName[effectiveLen] = 0; + } + burn (indexes, maxCharsCount + 1); + free (indexes); + } + + return bRet; +} + +typedef struct +{ + HDESK hDesk; + HINSTANCE hInstance; + LPCWSTR lpTemplateName; + DLGPROC lpDialogFunc; + LPARAM dwInitParam; + INT_PTR retValue; +} SecureDesktopThreadParam; + +static DWORD WINAPI SecureDesktopThread(LPVOID lpThreadParameter) +{ + SecureDesktopThreadParam* pParam = (SecureDesktopThreadParam*) lpThreadParameter; + + SetThreadDesktop (pParam->hDesk); + SwitchDesktop (pParam->hDesk); + + pParam->retValue = DialogBoxParamW (pParam->hInstance, pParam->lpTemplateName, + NULL, pParam->lpDialogFunc, pParam->dwInitParam); + + return 0; +} + +static void GetCtfMonProcessIdList (map& processIdList) +{ + HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + PROCESSENTRY32 pEntry; + BOOL hRes; + + pEntry.dwSize = sizeof (pEntry); + processIdList.clear(); + hRes = Process32First(hSnapShot, &pEntry); + while (hRes) + { + LPTSTR szFileName = PathFindFileName (pEntry.szExeFile); + if (_wcsicmp(szFileName, L"ctfmon.exe") == 0) + { + processIdList[pEntry.th32ProcessID] = TRUE; + } + hRes = Process32Next(hSnapShot, &pEntry); + } + CloseHandle(hSnapShot); +} + +static void KillProcess (DWORD dwProcessId) +{ + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, 0, dwProcessId); + if (hProcess != NULL) + { + TerminateProcess(hProcess, (UINT) -1); + CloseHandle(hProcess); + } +} + +INT_PTR SecureDesktopDialogBoxParam( + HINSTANCE hInstance, + LPCWSTR lpTemplateName, + HWND hWndParent, + DLGPROC lpDialogFunc, + LPARAM dwInitParam) +{ + TCHAR szDesktopName[65] = {0}; + BOOL bSuccess = FALSE; + INT_PTR retValue = 0; + BOOL bEffectiveUseSecureDesktop = bCmdUseSecureDesktopValid? bCmdUseSecureDesktop : bUseSecureDesktop; + + if (bEffectiveUseSecureDesktop && GenerateRandomString (hWndParent, szDesktopName, 64)) + { + map ctfmonBeforeList, ctfmonAfterList; + DWORD desktopAccess = DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | DESKTOP_READOBJECTS | DESKTOP_SWITCHDESKTOP | DESKTOP_WRITEOBJECTS; + HDESK hSecureDesk; + + // get the initial list of ctfmon.exe processes before creating new desktop + GetCtfMonProcessIdList (ctfmonBeforeList); + + hSecureDesk = CreateDesktop (szDesktopName, NULL, NULL, 0, desktopAccess, NULL); + if (hSecureDesk) + { + HDESK hOriginalDesk = GetThreadDesktop (GetCurrentThreadId ()); + SecureDesktopThreadParam param; + + param.hDesk = hSecureDesk; + param.hInstance = hInstance; + param.lpTemplateName = lpTemplateName; + param.lpDialogFunc = lpDialogFunc; + param.dwInitParam = dwInitParam; + param.retValue = 0; + + HANDLE hThread = ::CreateThread (NULL, 0, SecureDesktopThread, (LPVOID) ¶m, 0, NULL); + if (hThread) + { + WaitForSingleObject (hThread, INFINITE); + CloseHandle (hThread); + + SwitchDesktop (hOriginalDesk); + SetThreadDesktop (hOriginalDesk); + + retValue = param.retValue; + bSuccess = TRUE; + } + + CloseDesktop (hSecureDesk); + + // get the new list of ctfmon.exe processes in order to find the ID of the + // ctfmon.exe instance that corresponds to the desktop we create so that + // we can kill it, otherwise it would remain running + GetCtfMonProcessIdList (ctfmonAfterList); + + for (map::iterator It = ctfmonAfterList.begin(); + It != ctfmonAfterList.end(); It++) + { + if (ctfmonBeforeList[It->first] != TRUE) + { + // Kill process + KillProcess (It->first); + } + } + } + + burn (szDesktopName, sizeof (szDesktopName)); + } + + if (!bSuccess) + { + // fallback to displaying in normal desktop + retValue = DialogBoxParamW (hInstance, lpTemplateName, hWndParent, lpDialogFunc, dwInitParam); + } + + return retValue; +} + +#endif diff --git a/src/Common/Dlgcode.h b/src/Common/Dlgcode.h index 86afbe0f..a1930f67 100644 --- a/src/Common/Dlgcode.h +++ b/src/Common/Dlgcode.h @@ -121,6 +121,9 @@ extern BOOL bShowDisconnectedNetworkDrives; extern BOOL bHideWaitingDialog; extern BOOL bCmdHideWaitingDialog; extern BOOL bCmdHideWaitingDialogValid; +extern BOOL bUseSecureDesktop; +extern BOOL bCmdUseSecureDesktop; +extern BOOL bCmdUseSecureDesktopValid; extern BOOL bStartOnLogon; extern BOOL bMountDevicesOnLogon; extern BOOL bMountFavoritesOnLogon; @@ -514,6 +517,7 @@ BOOL LaunchElevatedProcess (HWND hwndDlg, const wchar_t* szModPath, const wchar_ BOOL GetFreeDriveLetter(WCHAR* pCh); BOOL RaisePrivileges(void); BOOL DeleteDirectory (const wchar_t* szDirName); +INT_PTR SecureDesktopDialogBoxParam (HINSTANCE, LPCWSTR, HWND, DLGPROC, LPARAM); #ifdef __cplusplus } diff --git a/src/Common/Language.xml b/src/Common/Language.xml index 1addd375..6c5697b4 100644 --- a/src/Common/Language.xml +++ b/src/Common/Language.xml @@ -1413,6 +1413,7 @@ Cannot verify that the Rescue Disk has been correctly extracted.\n\nIf you have extracted the Rescue Disk, please eject and reinsert the USB stick; then click Next to try again. If this does not help, please try another USB stick and/or another ZIP software.\n\nIf you have not extracted the Rescue Disk yet, please do so, and then click Next.\n\nIf you attempted to verify a VeraCrypt Rescue Disk created before you started this wizard, please note that such Rescue Disk cannot be used, because it was created for a different master key. You need to extract the newly generated Rescue Disk ZIP image. Cannot verify that the Rescue Disk has been correctly extracted.\n\nIf you have extracted the Rescue Disk image to a USB stick, please eject it and reinsert it; then try again. If this does not help, please try other ZIP software and/or medium.\n\nIf you attempted to verify a VeraCrypt Rescue Disk created for a different master key, password, salt, etc., please note that such Rescue Disk will always fail this verification. To create a new Rescue Disk fully compatible with your current configuration, select 'System' > 'Create Rescue Disk'. The Rescue Disk image has been created and stored in this file:\n%s\n\nNow you need to extract the Rescue Disk image to a USB stick that is formatted as FAT/FAT32.\n\nIMPORTANT: Note that the zip file must be extracted directly to the root of the USB stick. For example, if the drive letter of the USB stick is E: then extracting the zip file should create a folder E:\\EFI on the USB stick.\n\nAfter you create the Rescue Disk, select 'System' > 'Verify Rescue Disk' to verify that it has been correctly created. + Use Secure Desktop for password entry diff --git a/src/ExpandVolume/WinMain.cpp b/src/ExpandVolume/WinMain.cpp index 3172a45f..5ab9718c 100644 --- a/src/ExpandVolume/WinMain.cpp +++ b/src/ExpandVolume/WinMain.cpp @@ -284,6 +284,7 @@ void LoadSettings (HWND hwndDlg) bPreserveTimestamp = defaultMountOptions.PreserveTimestamp = ConfigReadInt ("PreserveTimestamps", TRUE); bShowDisconnectedNetworkDrives = ConfigReadInt ("ShowDisconnectedNetworkDrives", FALSE); bHideWaitingDialog = ConfigReadInt ("HideWaitingDialog", FALSE); + bUseSecureDesktop = ConfigReadInt ("UseSecureDesktop", FALSE); defaultMountOptions.Removable = ConfigReadInt ("MountVolumesRemovable", FALSE); defaultMountOptions.ReadOnly = ConfigReadInt ("MountVolumesReadOnly", FALSE); defaultMountOptions.ProtectHiddenVolume = FALSE; @@ -815,7 +816,7 @@ int ExtcvAskVolumePassword (HWND hwndDlg, const wchar_t* fileName, Password *pas StringCbCopyW (PasswordDlgVolume, sizeof(PasswordDlgVolume), fileName); - result = DialogBoxParamW (hInst, + result = SecureDesktopDialogBoxParam (hInst, MAKEINTRESOURCEW (IDD_PASSWORD_DLG), hwndDlg, (DLGPROC) ExtcvPasswordDlgProc, (LPARAM) &dlgParam); @@ -883,6 +884,7 @@ BOOL CALLBACK MainDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPa bPreserveTimestamp = defaultMountOptions.PreserveTimestamp = TRUE; bShowDisconnectedNetworkDrives = FALSE; bHideWaitingDialog = FALSE; + bUseSecureDesktop = FALSE; if (UsePreferences) { diff --git a/src/Mount/Mount.c b/src/Mount/Mount.c index 12ee7985..28a43acf 100644 --- a/src/Mount/Mount.c +++ b/src/Mount/Mount.c @@ -721,6 +721,8 @@ void LoadSettingsAndCheckModified (HWND hwndDlg, BOOL bOnlyCheckModified, BOOL* ConfigReadCompareInt ("HideWaitingDialog", FALSE, &bHideWaitingDialog, bOnlyCheckModified, pbSettingsModified); + ConfigReadCompareInt ("UseSecureDesktop", FALSE, &bUseSecureDesktop, bOnlyCheckModified, pbSettingsModified); + ConfigReadCompareInt ("MountVolumesRemovable", FALSE, &defaultMountOptions.Removable, bOnlyCheckModified, pbSettingsModified); ConfigReadCompareInt ("MountVolumesReadOnly", FALSE, &defaultMountOptions.ReadOnly, bOnlyCheckModified, pbSettingsModified); @@ -878,6 +880,7 @@ void SaveSettings (HWND hwndDlg) ConfigWriteInt ("PreserveTimestamps", defaultMountOptions.PreserveTimestamp); ConfigWriteInt ("ShowDisconnectedNetworkDrives",bShowDisconnectedNetworkDrives); ConfigWriteInt ("HideWaitingDialog", bHideWaitingDialog); + ConfigWriteInt ("UseSecureDesktop", bUseSecureDesktop); ConfigWriteInt ("EnableBackgroundTask", bEnableBkgTask); ConfigWriteInt ("CloseBackgroundTaskOnNoVolumes", bCloseBkgTaskWhenNoVolumes); @@ -3132,6 +3135,9 @@ BOOL CALLBACK PreferencesDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM SendMessage (GetDlgItem (hwndDlg, IDC_HIDE_WAITING_DIALOG), BM_SETCHECK, bHideWaitingDialog ? BST_CHECKED:BST_UNCHECKED, 0); + SendMessage (GetDlgItem (hwndDlg, IDC_SECURE_DESKTOP_PASSWORD_ENTRY), BM_SETCHECK, + bUseSecureDesktop ? BST_CHECKED:BST_UNCHECKED, 0); + SendMessage (GetDlgItem (hwndDlg, IDC_PREF_TEMP_CACHE_ON_MULTIPLE_MOUNT), BM_SETCHECK, bCacheDuringMultipleMount ? BST_CHECKED:BST_UNCHECKED, 0); @@ -3247,6 +3253,7 @@ BOOL CALLBACK PreferencesDlgProc (HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM bPreserveTimestamp = defaultMountOptions.PreserveTimestamp = IsButtonChecked (GetDlgItem (hwndDlg, IDC_PRESERVE_TIMESTAMPS)); bShowDisconnectedNetworkDrives = IsButtonChecked (GetDlgItem (hwndDlg, IDC_SHOW_DISCONNECTED_NETWORK_DRIVES)); bHideWaitingDialog = IsButtonChecked (GetDlgItem (hwndDlg, IDC_HIDE_WAITING_DIALOG)); + bUseSecureDesktop = IsButtonChecked (GetDlgItem (hwndDlg, IDC_SECURE_DESKTOP_PASSWORD_ENTRY)); bCacheDuringMultipleMount = IsButtonChecked (GetDlgItem (hwndDlg, IDC_PREF_TEMP_CACHE_ON_MULTIPLE_MOUNT)); bWipeCacheOnExit = IsButtonChecked (GetDlgItem (hwndDlg, IDC_PREF_WIPE_CACHE_ON_EXIT)); bWipeCacheOnAutoDismount = IsButtonChecked (GetDlgItem (hwndDlg, IDC_PREF_WIPE_CACHE_ON_AUTODISMOUNT)); @@ -4537,7 +4544,7 @@ static int AskVolumePassword (HWND hwndDlg, Password *password, int *pkcs5, int dlgParam.pim = pim; dlgParam.truecryptMode = truecryptMode; - result = DialogBoxParamW (hInst, + result = SecureDesktopDialogBoxParam (hInst, MAKEINTRESOURCEW (IDD_PASSWORD_DLG), hwndDlg, (DLGPROC) PasswordDlgProc, (LPARAM) &dlgParam); @@ -6440,6 +6447,7 @@ BOOL CALLBACK MainDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPa bPreserveTimestamp = defaultMountOptions.PreserveTimestamp = TRUE; bShowDisconnectedNetworkDrives = FALSE; bHideWaitingDialog = FALSE; + bUseSecureDesktop = FALSE; ResetWrongPwdRetryCount (); @@ -8449,6 +8457,7 @@ void ExtractCommandLine (HWND hwndDlg, wchar_t *lpszCommandLine) OptionPim, OptionTryEmptyPassword, OptionNoWaitDlg, + OptionSecureDesktop, }; argument args[]= @@ -8476,6 +8485,7 @@ void ExtractCommandLine (HWND hwndDlg, wchar_t *lpszCommandLine) { CommandWipeCache, L"/wipecache", L"/w", FALSE }, { OptionTryEmptyPassword, L"/tryemptypass", NULL, FALSE }, { OptionNoWaitDlg, L"/nowaitdlg", NULL, FALSE }, + { OptionSecureDesktop, L"/secureDesktop", NULL, FALSE }, }; argumentspec as; @@ -8547,6 +8557,25 @@ void ExtractCommandLine (HWND hwndDlg, wchar_t *lpszCommandLine) } break; + case OptionSecureDesktop: + { + wchar_t szTmp[16] = {0}; + bCmdUseSecureDesktop = TRUE; + bCmdUseSecureDesktopValid = TRUE; + + if (HAS_ARGUMENT == GetArgumentValue (lpszCommandLineArgs, &i, nNoCommandLineArgs, + szTmp, ARRAYSIZE (szTmp))) + { + if (!_wcsicmp(szTmp,L"n") || !_wcsicmp(szTmp,L"no")) + bCmdUseSecureDesktop = FALSE; + else if (!_wcsicmp(szTmp,L"y") || !_wcsicmp(szTmp,L"yes")) + bCmdUseSecureDesktop = TRUE; + else + AbortProcess ("COMMAND_LINE_ERROR"); + } + } + break; + case OptionCache: { wchar_t szTmp[16] = {0}; @@ -8972,6 +9001,7 @@ static BOOL StartSystemFavoritesService () DeviceChangeBroadcastDisabled = TRUE; bShowDisconnectedNetworkDrives = TRUE; bHideWaitingDialog = TRUE; + bUseSecureDesktop = FALSE; InitOSVersionInfo(); diff --git a/src/Mount/Mount.rc b/src/Mount/Mount.rc index 03496871..2f96bff7 100644 --- a/src/Mount/Mount.rc +++ b/src/Mount/Mount.rc @@ -41,7 +41,7 @@ IDR_MOUNT_TLB TYPELIB "Mount.tlb" // Dialog // -IDD_PREFERENCES_DLG DIALOGEX 0, 0, 336, 333 +IDD_PREFERENCES_DLG DIALOGEX 0, 0, 336, 340 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "VeraCrypt - Preferences" FONT 8, "MS Shell Dlg", 400, 0, 0x1 @@ -78,28 +78,30 @@ BEGIN CONTROL "Make disconnected network drives available for mounting",IDC_SHOW_DISCONNECTED_NETWORK_DRIVES, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,208,316,10 CONTROL "Cache passwords in driver memory",IDC_PREF_CACHE_PASSWORDS, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,254,146,11 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,263,146,11 CONTROL "Wipe cached passwords on exit",IDC_PREF_WIPE_CACHE_ON_EXIT, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,162,254,165,11 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,162,263,165,11 CONTROL "Temporarily cache password during ""Mount Favorite Volumes"" operations",IDC_PREF_TEMP_CACHE_ON_MULTIPLE_MOUNT, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,268,294,11 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,277,294,11 CONTROL "Wipe cached passwords on auto-dismount",IDC_PREF_WIPE_CACHE_ON_AUTODISMOUNT, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,282,296,11 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,291,296,11 CONTROL "Include PIM when caching a password",IDC_PREF_CACHE_PIM, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,296,296,10 - PUSHBUTTON "More Settings...",IDC_MORE_SETTINGS,5,317,85,14 - DEFPUSHBUTTON "OK",IDOK,225,317,50,14 - PUSHBUTTON "Cancel",IDCANCEL,281,317,50,14 - GROUPBOX "Windows",IDT_WINDOWS_RELATED_SETTING,4,160,328,76 + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,305,296,10 + PUSHBUTTON "More Settings...",IDC_MORE_SETTINGS,5,324,85,14 + DEFPUSHBUTTON "OK",IDOK,225,324,50,14 + PUSHBUTTON "Cancel",IDCANCEL,281,324,50,14 + GROUPBOX "Windows",IDT_WINDOWS_RELATED_SETTING,4,160,328,87 GROUPBOX "Default Mount Options",IDT_DEFAULT_MOUNT_OPTIONS,4,3,328,26 GROUPBOX "VeraCrypt Background Task",IDT_TASKBAR_ICON,4,33,328,26 GROUPBOX "Auto-Dismount",IDT_AUTO_DISMOUNT,4,94,328,62 LTEXT "minutes",IDT_MINUTES,289,129,39,10 LTEXT "Dismount all when:",IDT_AUTO_DISMOUNT_ON,9,104,71,20 - GROUPBOX "Password Cache",IDT_PW_CACHE_OPTIONS,4,243,328,68 + GROUPBOX "Password Cache",IDT_PW_CACHE_OPTIONS,4,252,328,68 GROUPBOX "Actions to perform upon logon to Windows",IDT_LOGON,4,63,328,28 CONTROL "Don't show wait message dialog when performing operations",IDC_HIDE_WAITING_DIALOG, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,220,316,10 + CONTROL "Use Secure Desktop for password entry",IDC_SECURE_DESKTOP_PASSWORD_ENTRY, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,232,316,10 END IDD_VOLUME_PROPERTIES DIALOGEX 60, 30, 284, 224 @@ -421,7 +423,7 @@ BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 329 TOPMARGIN, 7 - BOTTOMMARGIN, 331 + BOTTOMMARGIN, 338 END IDD_VOLUME_PROPERTIES, DIALOG diff --git a/src/Mount/Resource.h b/src/Mount/Resource.h index 12860915..0c863386 100644 --- a/src/Mount/Resource.h +++ b/src/Mount/Resource.h @@ -183,6 +183,7 @@ #define IDC_DISABLE_BOOT_LOADER_PIM_PROMPT 1160 #define IDC_HIDE_WAITING_DIALOG 1161 #define IDC_DISABLE_BOOT_LOADER_HASH_PROMPT 1162 +#define IDC_SECURE_DESKTOP_PASSWORD_ENTRY 1163 #define IDM_HELP 40001 #define IDM_ABOUT 40002 #define IDM_UNMOUNT_VOLUME 40003 @@ -259,7 +260,7 @@ #define _APS_NO_MFC 1 #define _APS_NEXT_RESOURCE_VALUE 120 #define _APS_NEXT_COMMAND_VALUE 40069 -#define _APS_NEXT_CONTROL_VALUE 1163 +#define _APS_NEXT_CONTROL_VALUE 1164 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif -- cgit v1.2.3