From 1ae0c3717ef0b08067c85d05b6055e2a522822ec Mon Sep 17 00:00:00 2001 From: Anton-V-K <20116984+Anton-V-K@users.noreply.github.com> Date: Wed, 3 May 2023 01:09:38 +0300 Subject: [PATCH 1/3] Logon Hours Manager prototype --- .editorconfig | 10 + .gitignore | 3 + ClassicParentalControl.sln | 17 +- Common/Common.vcxitems | 23 ++ {LogonHoursService => Common}/Logger.h | 0 Common/LogonHours.cpp | 202 ++++++++++++++++++ Common/LogonHours.h | 50 +++++ Common/WeekHours.h | 8 + LogonHoursManager/LogonHoursManager.vcxproj | 159 ++++++++++++++ .../LogonHoursManager.vcxproj.filters | 67 ++++++ LogonHoursManager/MainDialog.cpp | 186 ++++++++++++++++ LogonHoursManager/MainDialog.h | 82 +++++++ LogonHoursManager/MainModel.cpp | 82 +++++++ LogonHoursManager/MainModel.h | 25 +++ LogonHoursManager/WinDataExchangeEx.h | 121 +++++++++++ LogonHoursManager/WinMain.cpp | 202 ++++++++++++++++++ LogonHoursManager/app.ico | Bin 0 -> 46227 bytes LogonHoursManager/app.manifest | 22 ++ LogonHoursManager/app.rc | Bin 0 -> 8892 bytes LogonHoursManager/framework.h | 2 + LogonHoursManager/packages.config | 5 + LogonHoursManager/resource.h | 28 +++ LogonHoursManager/stdafx.cpp | 3 + LogonHoursManager/stdafx.h | 40 ++++ LogonHoursManager/targetver.h | 6 + LogonHoursService/LogonHoursService.vcxproj | 13 +- LogonHoursService/WorkerThread.cpp | 104 +-------- _props/Cxx.props | 7 + 28 files changed, 1358 insertions(+), 109 deletions(-) create mode 100644 .editorconfig create mode 100644 Common/Common.vcxitems rename {LogonHoursService => Common}/Logger.h (100%) create mode 100644 Common/LogonHours.cpp create mode 100644 Common/LogonHours.h create mode 100644 Common/WeekHours.h create mode 100644 LogonHoursManager/LogonHoursManager.vcxproj create mode 100644 LogonHoursManager/LogonHoursManager.vcxproj.filters create mode 100644 LogonHoursManager/MainDialog.cpp create mode 100644 LogonHoursManager/MainDialog.h create mode 100644 LogonHoursManager/MainModel.cpp create mode 100644 LogonHoursManager/MainModel.h create mode 100644 LogonHoursManager/WinDataExchangeEx.h create mode 100644 LogonHoursManager/WinMain.cpp create mode 100644 LogonHoursManager/app.ico create mode 100644 LogonHoursManager/app.manifest create mode 100644 LogonHoursManager/app.rc create mode 100644 LogonHoursManager/framework.h create mode 100644 LogonHoursManager/packages.config create mode 100644 LogonHoursManager/resource.h create mode 100644 LogonHoursManager/stdafx.cpp create mode 100644 LogonHoursManager/stdafx.h create mode 100644 LogonHoursManager/targetver.h diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3c51781 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +# To learn more about .editorconfig see https://aka.ms/editorconfigdocs + +# All files +[*] +indent_size = 4 +indent_style = space +tab_width = 4 + +[*.{xml,xsd,xsl}] +indent_size = 2 diff --git a/.gitignore b/.gitignore index 6e30c21..d456880 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ /packages /_crashes /_releases + +*.vcxproj.user +*.aps diff --git a/ClassicParentalControl.sln b/ClassicParentalControl.sln index 276f50a..490116e 100644 --- a/ClassicParentalControl.sln +++ b/ClassicParentalControl.sln @@ -1,11 +1,20 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.779 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.33130.400 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LogonHoursService", "LogonHoursService\LogonHoursService.vcxproj", "{D6E78055-7BC4-4301-BDFC-C4DDA35E959C}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LogonHoursManager", "LogonHoursManager\LogonHoursManager.vcxproj", "{1FB09AD5-5BEA-427A-9862-6884A60AA97A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "Common\Common.vcxitems", "{2665D681-30A9-4760-9648-25AB585A19B3}" +EndProject Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + Common\Common.vcxitems*{1fb09ad5-5bea-427a-9862-6884a60aa97a}*SharedItemsImports = 4 + Common\Common.vcxitems*{2665d681-30a9-4760-9648-25ab585a19b3}*SharedItemsImports = 9 + Common\Common.vcxitems*{d6e78055-7bc4-4301-bdfc-c4dda35e959c}*SharedItemsImports = 4 + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 @@ -15,6 +24,10 @@ Global {D6E78055-7BC4-4301-BDFC-C4DDA35E959C}.Debug|x86.Build.0 = Debug|Win32 {D6E78055-7BC4-4301-BDFC-C4DDA35E959C}.Release|x86.ActiveCfg = Release|Win32 {D6E78055-7BC4-4301-BDFC-C4DDA35E959C}.Release|x86.Build.0 = Release|Win32 + {1FB09AD5-5BEA-427A-9862-6884A60AA97A}.Debug|x86.ActiveCfg = Debug|Win32 + {1FB09AD5-5BEA-427A-9862-6884A60AA97A}.Debug|x86.Build.0 = Debug|Win32 + {1FB09AD5-5BEA-427A-9862-6884A60AA97A}.Release|x86.ActiveCfg = Release|Win32 + {1FB09AD5-5BEA-427A-9862-6884A60AA97A}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Common/Common.vcxitems b/Common/Common.vcxitems new file mode 100644 index 0000000..51ab425 --- /dev/null +++ b/Common/Common.vcxitems @@ -0,0 +1,23 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {2665d681-30a9-4760-9648-25ab585a19b3} + + + + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + + + + + + + + + + + \ No newline at end of file diff --git a/LogonHoursService/Logger.h b/Common/Logger.h similarity index 100% rename from LogonHoursService/Logger.h rename to Common/Logger.h diff --git a/Common/LogonHours.cpp b/Common/LogonHours.cpp new file mode 100644 index 0000000..9500c1b --- /dev/null +++ b/Common/LogonHours.cpp @@ -0,0 +1,202 @@ +#include "stdafx.h" +#include "LogonHours.h" + +#include "Logger.h" + +#pragma comment(lib, "Netapi32.lib") // NetUserGetInfo(), etc. + +EDayOfWeek& operator ++(EDayOfWeek& day) +{ + if (day < EDayOfWeek::LAST) + { + int value = static_cast(day); + day = static_cast(++value); + return day; + } + LOG_ERROR(__func__) << "Cannot increment " << int(day); + return day; +} + +LogonHours::LogonHours() +{ +} + +LogonHours::LogonHours(PBYTE usri2_logon_hours) +{ + Init(usri2_logon_hours); +} + +bool LogonHours::ApplyTo(const char* username) const +{ + const std::wstring wusername(username, username + strlen(username)); + return ApplyTo(wusername.c_str()); +} + +bool LogonHours::ApplyTo(const wchar_t* username) const +{ + // Each bit in the string represents a unique hour in the week, in Greenwich Mean Time (GMT). + // The first bit (bit 0, word 0) is Sunday, 0:00 to 0:59; the second bit (bit 1, word 0) is Sunday, 1:00 to 1:59; and so on. + // (c) https://learn.microsoft.com/en-us/windows/win32/api/lmaccess/ns-lmaccess-user_info_1020 + BYTE usri2_logon_hours[21]; + // By default no restrictions + memset(usri2_logon_hours, 0xFF, _countof(usri2_logon_hours)); + if (!m_all) + { + int pos = 0; // in the array + int bit = 0; // bit index + const BYTE mask = 0b00000001; + for (int day = FIRST; day <= LAST; ++day) + { + for (int hour = 0; hour < 24; ++hour) + { + if (!m_data[day][hour]) + usri2_logon_hours[pos] &= ~(mask << bit); // clear bit + ++bit; + if (bit >= 8) + { + bit = 0; + ++pos; // next chunk + } + } + } + } + USER_INFO_1020 info; + DWORD parm_err; + info.usri1020_units_per_week = UNITS_PER_WEEK; // NetUserAdd and NetUserSetInfo functions ignore this member + info.usri1020_logon_hours = usri2_logon_hours; + if (NetUserSetInfo(NULL, username, 1020, reinterpret_cast(&info), &parm_err) != NERR_Success) + return false; + return true; +} + +void LogonHours::Init(PBYTE usri2_logon_hours) +{ + m_all = true; + int day = Sun; + int hour = 0; // 0:00 + for (int p = 0; p < 21; ++p) + { + const BYTE chunk = usri2_logon_hours[p]; + BYTE mask = 0b00000001; + for (int bit = 0; bit < 8; ++bit) + { + m_data[day][hour] = (chunk & mask) != 0; + if (!m_data[day][hour]) + m_all = false; + ++hour; + mask <<= 1; + if (hour >= 24) + { + ++day; + hour = 0; + } + } + } +} + +bool LogonHours::InitFrom(const char* username) +{ + const std::wstring wusername(username, username + strlen(username)); + return InitFrom(wusername.c_str()); +} + +bool LogonHours::InitFrom(const wchar_t* username) +{ + LPBYTE bufptr; + // Refer to https://docs.microsoft.com/en-us/windows/win32/api/lmaccess/nf-lmaccess-netusergetinfo + const NET_API_STATUS result = NetUserGetInfo(NULL, username, 2, &bufptr); + if (result != NERR_Success) + return false; + + const USER_INFO_2* const userinfo2 = reinterpret_cast(bufptr); + Init(userinfo2->usri2_logon_hours); + + NetApiBufferFree(bufptr); + + return true; +} + +bool LogonHours::Allowed(EDayOfWeek day, WORD hour) const +{ + if (Sun <= day && day <= Sat && 0 <= hour && hour <= 23) + return m_data[day][hour]; + return false; +} + +bool LogonHours::Allow(EDayOfWeek day, WORD hour, bool allow) +{ + if (Sun <= day && day <= Sat && 0 <= hour && hour <= 23) + if (m_data[day][hour] != allow) + { + m_data[day][hour] = allow; + if (!allow && m_all) + m_all = false; + return true; + } + return false; +} + +void LogonHours::Get(WeekHours& week, bool utc) const +{ + int pos = utc ? 0 : getSun0(); + for (int d = 0; d < 7; ++d) + { + for (WORD h = 0; h < 24; ++h) + { + week.array[pos] = m_data[d][h]; + ++pos; + if (pos >= sizeof(week.array)) + pos = 0; + } + } +} + +void LogonHours::Set(const WeekHours& week, bool utc) +{ + int pos = utc ? 0 : getSun0(); + for (int d = 0; d < 7; ++d) + { + for (WORD h = 0; h < 24; ++h) + { + m_data[d][h] = week.array[pos]; + ++pos; + if (pos >= sizeof(week.array)) + pos = 0; + } + } +} + +long LogonHours::SecondsLeft(EDayOfWeek day, WORD hour, WORD minute, WORD second) const +{ + if (All()) + return -1; + if (!Allowed(day, hour)) + return 0; + long left = (60 - second - 1) + 60 * (60 - minute - 1); // the rest of current hour is allowed + WORD hour_start = hour + 1; // for the current day - start with the next hour + EDayOfWeek d = day; + for (int i = 0; i < 7; ++i) // we'll check all week days starting from the current one + { + for (WORD h = hour_start; h < 24; ++h) // check hours until end of day + { + if (Allowed(day, h)) + left += 3600; + else + return left; + } + hour_start = 0; // for the rest of days + // next day + if (d == EDayOfWeek::LAST) + d = EDayOfWeek::FIRST; + else + ++d; + } + return left; +} + +int LogonHours::getSun0() +{ + TIME_ZONE_INFORMATION tz = { 0 }; + GetTimeZoneInformation(&tz); + return (tz.Bias < 0) ? (-tz.Bias / 60) : (7 * 24 - tz.Bias / 60); +} diff --git a/Common/LogonHours.h b/Common/LogonHours.h new file mode 100644 index 0000000..9c3c90a --- /dev/null +++ b/Common/LogonHours.h @@ -0,0 +1,50 @@ +#pragma once + +#include "WeekHours.h" + +enum EDayOfWeek +{ + // NOTE: the order can't be changed - it matches the logon hours bit-array + Sun, Mon, Tue, Wed, Thu, Fri, Sat, + FIRST = Sun, + LAST = Sat, +}; +EDayOfWeek& operator ++(EDayOfWeek& day); + +class LogonHours +{ +public: + LogonHours(); + LogonHours(PBYTE usri2_logon_hours); + + bool ApplyTo(const char* username) const; + bool ApplyTo(const wchar_t* username) const; + + void Init(PBYTE usri2_logon_hours); + + bool InitFrom(const char* username); + bool InitFrom(const wchar_t* username); + + bool All() const { return m_all; } + bool Allowed(EDayOfWeek day, WORD hour) const; + + bool Allow(EDayOfWeek day, WORD hour, bool allow); + + void Get(WeekHours& week, bool utc = true) const; + void Set(const WeekHours& week, bool utc = true); + + // @return seconds left (for the given time) until the allowed logon period expires, or -1 if no restrictions are set + // @note 0 (zero) is returned if logon isn't allowed during given time + long SecondsLeft(EDayOfWeek day, WORD hour, WORD minute, WORD second) const; + +protected: + /// + /// Calculates cell index which corresponds to Sun 0:00 UTC in local time zone. + /// + /// Zero-based index of cell in WeekHours.array + static int getSun0(); + +private: + bool m_all{ true }; // true if no logon restrictions + bool m_data[7][24]; +}; diff --git a/Common/WeekHours.h b/Common/WeekHours.h new file mode 100644 index 0000000..81a6f83 --- /dev/null +++ b/Common/WeekHours.h @@ -0,0 +1,8 @@ +#pragma once + +union WeekHours +{ + bool array [7 * 24]; + bool table [7] [24]; +}; + diff --git a/LogonHoursManager/LogonHoursManager.vcxproj b/LogonHoursManager/LogonHoursManager.vcxproj new file mode 100644 index 0000000..cccc939 --- /dev/null +++ b/LogonHoursManager/LogonHoursManager.vcxproj @@ -0,0 +1,159 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {1fb09ad5-5bea-427a-9862-6884a60aa97a} + LogonHoursManager + 10.0 + + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + Use + + + Windows + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + Use + + + Windows + true + true + true + + + + + Level3 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + Use + + + Windows + true + + + + + Level3 + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + Use + + + Windows + true + true + true + + + + + + + + + + + + + + + + Create + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/LogonHoursManager/LogonHoursManager.vcxproj.filters b/LogonHoursManager/LogonHoursManager.vcxproj.filters new file mode 100644 index 0000000..74e1671 --- /dev/null +++ b/LogonHoursManager/LogonHoursManager.vcxproj.filters @@ -0,0 +1,67 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Resource Files + + + + + + + + + \ No newline at end of file diff --git a/LogonHoursManager/MainDialog.cpp b/LogonHoursManager/MainDialog.cpp new file mode 100644 index 0000000..6505b48 --- /dev/null +++ b/LogonHoursManager/MainDialog.cpp @@ -0,0 +1,186 @@ +#include "stdafx.h" +#include "MainDialog.h" + +#include "MainModel.h" + +CMainDialog::CMainDialog(MainModel& model) + :m_model(model) +{ +} + +CMainDialog::~CMainDialog() +{ +} + +LRESULT CMainDialog::OnClose(UINT, int, HWND) +{ + EndDialog(0); + return 0; +} + +void CMainDialog::OnDataExchangeError(UINT, BOOL) +{ +} + +LRESULT CMainDialog::OnInitDialog(HWND, LPARAM) +{ + HICON hIcon = ::LoadIcon(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDI_APP)); + SetIcon(hIcon); + + m_ctrlToolTip.Create(m_hWnd); // , rcDefault, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, WS_EX_TOPMOST); + ATLASSERT(m_ctrlToolTip.IsWindow()); + + const auto& wndGroup = GetDlgItem(IDC_GROUP_HOURS); + RECT rcGroup; + wndGroup.GetWindowRect(&rcGroup); + // MapDialogRect(&rcGroup); + + const auto units = GetDialogBaseUnits(); + const auto xunits = LOWORD(units); + const auto yunits = HIWORD(units); + + UINT xshift = 30; // width of left-most column with day names + const UINT yshift = 4; + UINT width = (rcGroup.right - rcGroup.left - xshift - 10) / 24; // width of each column with a checkbox + UINT height = (rcGroup.bottom - rcGroup.top - 12 - yshift) / 8; // height of each row (applies to header as well) + // starting ID for checkboxes: Sun 0:00-1:00, Sun 1:00-2:00, ..., Sun 23:00-24:00, Mon 0:00-1:00, etc. + UINT id = IDC_HOURS_FIRST; + // Columns labels + for (WORD hour = 0; hour < 24; ++hour) + { + UINT x = rcGroup.left + xshift + hour * width; + UINT y = rcGroup.top + yshift - height * 3 / 4; + //if (hour % 2 == 0) + { + wchar_t header[12]; + swprintf_s(header, L"%02d", hour); + const auto hwndLabel = CreateWindow(_T("Static"), header, WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE | SS_LEFT, + x, y, width, height, m_hWnd, HMENU(IDC_STATIC), NULL, NULL); + SendMessage(hwndLabel, WM_SETFONT, WPARAM(GetFont()), 0); + } + } + static const wchar_t* namesShort[] + = { L"SUN", L"MON", L"TUE", L"WED", L"THU", L"FRI", L"SAT" }; + static const wchar_t* namesLong[] + = { L"Sunday", L"Monday", L"Tuesday", L"Wednesday", L"Thursday", L"Friday", L"Saturday" }; + // Row labels and check boxes + for (WORD day = 0; day < 7; ++day) + { + const UINT x = rcGroup.left; + const UINT y = rcGroup.top + yshift + height * day + height / 5; + + const auto hwndLabel = CreateWindow(_T("Static"), namesShort[day], WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE | SS_LEFT, + x, y, xshift, height, m_hWnd, HMENU(IDC_STATIC), NULL, NULL); + SendMessage(hwndLabel, WM_SETFONT, WPARAM(GetFont()), 0); + + // TODO? CToolInfo ti(TTF_SUBCLASS, hwndLabel, 0, NULL, LPTSTR(namesLong[day])); + // TODO? ATLASSERT(m_ctrlToolTip.AddTool(ti)); + + for (WORD hour = 0; hour < 24; ++hour) + { + const UINT x = rcGroup.left + xshift + hour * width; + const auto hwndCheckBox = CreateWindow(_T("Button"), NULL, WS_CHILD | WS_VISIBLE | WS_EX_NOPARENTNOTIFY | BS_CHECKBOX | BS_PUSHLIKE, + x, y, width, height, m_hWnd, HMENU(id), NULL, NULL); +#if 1 + wchar_t tttext[42]; + swprintf_s(tttext, L"%s %02d:00-%02d:00", namesLong[day], hour, hour + 1); + CToolInfo ti(TTF_SUBCLASS, hwndCheckBox, 0, NULL, tttext); + ATLASSERT(m_ctrlToolTip.AddTool(ti)); + // m_ctrlToolTip.AddTool(hwndCheckBox, tttext); // , & rcTool, TTF_IDISHWND); +#else + RECT rcTool; + rcTool.left = x; + rcTool.top = y; + rcTool.right = x + width; + rcTool.bottom = y + height; + TOOLINFOW toolinfo = { 0 }; + toolinfo.cbSize = sizeof(toolinfo); // TTTOOLINFO_V1_SIZE; + toolinfo.hwnd = m_hWnd; + toolinfo.rect = rcTool; + // toolinfo.uFlags = TTF_IDISHWND; + toolinfo.uId = id; + toolinfo.lpszText = LPTSTR(L"Ýòî ïîëå äëÿ ââîäà òåêñòà"); + ::SendMessageW(m_ctrlToolTip, TTM_ADDTOOL, 0, LPARAM(&toolinfo)); +#endif + + ++id; + } + } + + m_ctrlToolTip.Activate(TRUE); + + FetchData(); + + DoDataExchange(DDX_LOAD); + + UIAddChildWindowContainer(m_hWnd); + UpdateUI(true); + + return 0; +} +/*DEL? +LRESULT CMainDialog::OnHourChanged(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) +{ + if (wNotifyCode == BN_CLICKED) + { + // UpdateData(); + // DisplayGUID(); + if (IDC_HOURS_FIRST <= wID && wID <= IDC_HOURS_LAST) + { + const auto span = wID - IDC_HOURS_FIRST; + const auto day = span / 24; + const auto hour = span % 24; + const auto check = Button_GetCheck(hWndCtl); + m_model.getHours().Allow(EDayOfWeek(day), hour, check == BST_CHECKED); + // TODO m_model.getHours().ApplyTo(m_model.getUser()); + } + } + return 0; +} +*/ +LRESULT CMainDialog::OnUserChanged(UINT, int, HWND) +{ + UpdateUI(); + m_model.selectUser(m_userName); + FetchData(); + DoDataExchange(DDX_LOAD); + return 0; +} + +void CMainDialog::FetchData() +{ + wcscpy_s(m_userName, m_model.getUser()); + m_users = m_model.getUsers(); + m_model.getHours().Get(m_week, false); +} + +void CMainDialog::UpdateUI(bool saved) +{ + const auto isValid = saved || DoDataExchange(DDX_SAVE); + + for (UINT id = IDC_HOURS_FIRST; id <= IDC_HOURS_LAST; ++id) + UIEnable(id, isValid && m_userName[0] != 0); + + if (isValid) + { + +/*DEL + UIEnable(IDC_EXP, TRUE); + UIEnable(IDC_LN, m_number > 0); + UIEnable(IDC_SQRT, m_number >= 0); + UIEnable(IDC_FACT, m_number <= 100 && m_number > 0 && ((double)(unsigned long)m_number == m_number)); +*/ + } + else + { +/*DEL + UIEnable(IDC_EXP, FALSE); + UIEnable(IDC_LN, FALSE); + UIEnable(IDC_SQRT, FALSE); + UIEnable(IDC_FACT, FALSE); +*/ + } + + UIUpdateChildWindows(); +} + diff --git a/LogonHoursManager/MainDialog.h b/LogonHoursManager/MainDialog.h new file mode 100644 index 0000000..5dd34ab --- /dev/null +++ b/LogonHoursManager/MainDialog.h @@ -0,0 +1,82 @@ +#pragma once +#include // MSG_WM_INITDIALOG, etc. +#include // CToolTipCtrl +#include // CUpdateUI +#include + +#include + +#include + +#include "WinDataExchangeEx.h" + +#include "resource.h" + +class MainModel; + +class CMainDialog + : public CDialogImpl + , public CUpdateUI + , public CWinDataExchangeEx +{ + typedef CMainDialog Self; + +private: + MainModel& m_model; + + CToolTipCtrl m_ctrlToolTip; + + CComboBox m_comboUserName; + TCHAR m_userName[256] = _T(""); + std::vector m_users; + + WeekHours m_week; + +public: + enum EID + { + IDD = IDD_MAIN, + + IDC_HOURS_FIRST = IDC_GROUP_HOURS + 1, + IDC_HOURS_LAST = IDC_HOURS_FIRST + 7 * 24, + IDC_HOURS_COUNT = IDC_HOURS_LAST - IDC_HOURS_FIRST, + }; + + BEGIN_MSG_MAP(Self) + MSG_WM_INITDIALOG(OnInitDialog) + // COMMAND_RANGE_HANDLER(IDC_HOURS_FIRST, IDC_HOURS_LAST, OnHourChanged) + COMMAND_HANDLER_EX(IDC_COMBO_USER, CBN_SELENDOK, OnUserChanged) + COMMAND_HANDLER_EX(IDCANCEL, BN_CLICKED, OnClose) + END_MSG_MAP() + + BEGIN_DDX_MAP(Self) + DDX_CONTROL_HANDLE(IDC_COMBO_USER, m_comboUserName) + DDX_COMBO_LIST_SOURCE(IDC_COMBO_USER, m_users) + DDX_COMBO_TEXT(IDC_COMBO_USER, m_userName) + DDX_CHECK_ARRAY(IDC_HOURS_FIRST, IDC_HOURS_LAST, m_week.array) + END_DDX_MAP() + + BEGIN_UPDATE_UI_MAP(Self) + UPDATE_ELEMENT(IDC_COMBO_USER, UPDUI_CHILDWINDOW) + UPDATE_ELEMENT(IDOK, UPDUI_CHILDWINDOW) + END_UPDATE_UI_MAP() + + CMainDialog(MainModel& model); + ~CMainDialog(); + +public: + LRESULT OnClose(UINT, int, HWND); + + void OnDataExchangeError(UINT, BOOL); + + LRESULT OnInitDialog(HWND, LPARAM); + +private: + LRESULT OnHourChanged(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled); + + LRESULT OnUserChanged(UINT, int, HWND); + + void FetchData(); + + void UpdateUI(bool saved = false); +}; diff --git a/LogonHoursManager/MainModel.cpp b/LogonHoursManager/MainModel.cpp new file mode 100644 index 0000000..cf0c8bf --- /dev/null +++ b/LogonHoursManager/MainModel.cpp @@ -0,0 +1,82 @@ +#include "stdafx.h" +#include "MainModel.h" + +struct MainModel::Data +{ + // username => logon hours + std::map hours; + + std::wstring user; // active user +}; + +MainModel::MainModel() + : m_(new Data) +{ + fetchUsers(); + selectUser(nullptr); +} + +MainModel::~MainModel() +{ + delete m_; +} + +const LogonHours& MainModel::getHours() const +{ + return m_->hours[m_->user]; +} + +LogonHours& MainModel::getHours() +{ + return m_->hours[m_->user]; +} + +const wchar_t* MainModel::getUser() const +{ + return m_->user.c_str(); +} + +std::vector MainModel::getUsers() const +{ + std::vector users; + for (const auto& it : m_->hours) + { + users.push_back(it.first); + } + return users; +} + +void MainModel::selectUser(const wchar_t* user) +{ + if (user) + { + m_->user = user; + } + else + { + TCHAR username[256]; + DWORD len = _countof(username); + if (GetUserName(username, &len) == 0) + LOG_ERROR(__func__) << "GetUserName() failed with error " << GetLastError(); + else + m_->user = username; + } + + if (!m_->user.empty()) + m_->hours[m_->user].InitFrom(m_->user.c_str()); +} + +void MainModel::fetchUsers() +{ + LPBYTE buffer; + DWORD entriesread, totalentries; + if (NetUserEnum(NULL, 1, FILTER_NORMAL_ACCOUNT, &buffer, MAX_PREFERRED_LENGTH, &entriesread, &totalentries, NULL) != NERR_Success) + return; + const auto* const userinfo = reinterpret_cast(buffer); + for (DWORD i = 0; i < entriesread; ++i) + { + if ((userinfo[i].usri1_flags & USER_PRIV_MASK) == USER_PRIV_USER) + m_->hours[userinfo[i].usri1_name] = LogonHours(); + } + NetApiBufferFree(buffer); +} diff --git a/LogonHoursManager/MainModel.h b/LogonHoursManager/MainModel.h new file mode 100644 index 0000000..f5b66a1 --- /dev/null +++ b/LogonHoursManager/MainModel.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +class MainModel +{ +public: + MainModel(); + ~MainModel(); + + const LogonHours& getHours() const; + LogonHours& getHours(); + + const wchar_t* getUser() const; + std::vector getUsers() const; + + void selectUser(const wchar_t* user); + +protected: + void fetchUsers(); + +private: + struct Data; + Data* const m_; +}; diff --git a/LogonHoursManager/WinDataExchangeEx.h b/LogonHoursManager/WinDataExchangeEx.h new file mode 100644 index 0000000..e5d77da --- /dev/null +++ b/LogonHoursManager/WinDataExchangeEx.h @@ -0,0 +1,121 @@ +#pragma once + +#include +#include + +template +class CWinDataExchangeEx : public CWinDataExchange +{ + typedef CWinDataExchange Base_; + +public: + BOOL DDX_Combo_Text(UINT nID, LPTSTR lpstrText, int cbSize, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) + { + T* const pT = static_cast(this); + BOOL bSuccess = TRUE; + + HWND hWndCtrl = pT->GetDlgItem(nID); + if (bSave) + { + int nRetLen = ::GetWindowText(hWndCtrl, lpstrText, cbSize / sizeof(TCHAR)); + if (nRetLen < ::GetWindowTextLength(hWndCtrl)) + bSuccess = FALSE; + } + else + { + ATLASSERT(!bValidate || (lstrlen(lpstrText) <= nLength)); + // bSuccess = pT->SetDlgItemText(nID, lpstrText); // doesn't work for combo boxes and similar controls + // SetWindowText() won't help either + bSuccess = ComboBox_SelectString(hWndCtrl, -1, lpstrText) != CB_ERR; + } + + if (!bSuccess) + { + pT->OnDataExchangeError(nID, bSave); + } + else if (bSave && bValidate) // validation + { + ATLASSERT(nLength > 0); + if (lstrlen(lpstrText) > nLength) + { + typename Base_::_XData data = { Base_::ddxDataText }; + data.textData.nLength = lstrlen(lpstrText); + data.textData.nMaxLength = nLength; + pT->OnDataValidateError(nID, bSave, data); + bSuccess = FALSE; + } + } + return bSuccess; + } + + template + BOOL DDX_Combo_List(UINT nID, Container& list, BOOL bSave, BOOL bValidate = FALSE, int nLength = 0) + { + T* const pT = static_cast(this); + BOOL bSuccess = TRUE; + + HWND hWndCtrl = pT->GetDlgItem(nID); + if (bSave) + { + const int count = ComboBox_GetCount(hWndCtrl); + list.clear(); + for (int i = 0; i < count; ++i) + { + TCHAR str[256] = { 0 }; + ComboBox_GetLBText(hWndCtrl, i, str); + list.push_back(str); + } + } + else + { + ComboBox_ResetContent(hWndCtrl); + for (const auto& str : list) + { + ComboBox_AddString(hWndCtrl, str.c_str()); + } + } + + if (!bSuccess) + { + pT->OnDataExchangeError(nID, bSave); + } + else if (bSave && bValidate) // validation + { +/*TODO? + ATLASSERT(nLength > 0); + if (strText.GetLength() > nLength) + { + _XData data = { ddxDataText }; + data.textData.nLength = strText.GetLength(); + data.textData.nMaxLength = nLength; + pT->OnDataValidateError(nID, bSave, data); + bSuccess = FALSE; + } +*/ + } + return bSuccess; + } +}; + +#define DDX_CHECK_ARRAY(nFirstID, nLastID, arr) \ + static_assert(_countof(arr) == nLastID - nFirstID, "The elements range should match the array's size"); \ + if (nFirstID <= nCtlID && nCtlID <= nLastID) \ + DDX_Check(nCtlID, arr[nCtlID - nFirstID], bSaveAndValidate); \ + if (nCtlID == (UINT)-1) \ + for (UINT id = nFirstID; id <= nLastID; ++id) \ + DDX_Check(id, arr[id - nFirstID], bSaveAndValidate); + +#define DDX_COMBO_LIST_SOURCE(nID, var) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + { \ + if (!bSaveAndValidate) \ + if(!DDX_Combo_List(nID, var, bSaveAndValidate)) \ + return FALSE; \ + } + +#define DDX_COMBO_TEXT(nID, var) \ + if((nCtlID == (UINT)-1) || (nCtlID == nID)) \ + { \ + if(!DDX_Combo_Text(nID, var, sizeof(var), bSaveAndValidate)) \ + return FALSE; \ + } diff --git a/LogonHoursManager/WinMain.cpp b/LogonHoursManager/WinMain.cpp new file mode 100644 index 0000000..a6a91a0 --- /dev/null +++ b/LogonHoursManager/WinMain.cpp @@ -0,0 +1,202 @@ +#include "stdafx.h" + +#include + +#include "MainDialog.h" +#include "MainModel.h" + +#if 1 +CAppModule _Module; + +int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance + , _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) +{ + _Module.Init(NULL, hInstance); + + MainModel model; + + CMainDialog dlg(model); + dlg.DoModal(); + + _Module.Term(); + + return 0; +} +#else + +#define MAX_LOADSTRING 100 + +// Global Variables: +HINSTANCE hInst; // current instance +WCHAR szTitle[MAX_LOADSTRING]; // The title bar text +WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name + +// Forward declarations of functions included in this code module: +ATOM MyRegisterClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); + +int APIENTRY wWinMain(_In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPWSTR lpCmdLine, + _In_ int nCmdShow) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + + // TODO: Place code here. + + // Initialize global strings + LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadStringW(hInstance, IDS_WINDOW_CLASS, szWindowClass, MAX_LOADSTRING); + MyRegisterClass(hInstance); + + // Perform application initialization: + if (!InitInstance (hInstance, nCmdShow)) + { + return FALSE; + } + + HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_ACCELERATORS)); + + MSG msg; + + // Main message loop: + while (GetMessage(&msg, nullptr, 0, 0)) + { + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return (int) msg.wParam; +} + + + +// +// FUNCTION: MyRegisterClass() +// +// PURPOSE: Registers the window class. +// +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASSEXW wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APP)); + wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = MAKEINTRESOURCEW(IDM_APP); + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APP)); + + return RegisterClassExW(&wcex); +} + +// +// FUNCTION: InitInstance(HINSTANCE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + hInst = hInstance; // Store instance handle in our global variable + + HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); + + if (!hWnd) + { + return FALSE; + } + + ShowWindow(hWnd, nCmdShow); + UpdateWindow(hWnd); + + return TRUE; +} + +// +// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) +// +// PURPOSE: Processes messages for the main window. +// +// WM_COMMAND - process the application menu +// WM_PAINT - Paint the main window +// WM_DESTROY - post a quit message and return +// +// +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_COMMAND: + { + int wmId = LOWORD(wParam); + // Parse the menu selections: + switch (wmId) + { + case IDM_ABOUT: + DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); + break; + case IDM_EXIT: + DestroyWindow(hWnd); + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + } + break; + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + // TODO: Add any drawing code that uses hdc here... + EndPaint(hWnd, &ps); + } + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +// Message handler for about box. +INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + case WM_INITDIALOG: + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + return (INT_PTR)FALSE; +} + +#endif diff --git a/LogonHoursManager/app.ico b/LogonHoursManager/app.ico new file mode 100644 index 0000000000000000000000000000000000000000..b3ec03bd617f32e58128fa977fd6ac9605124f4b GIT binary patch literal 46227 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 + + + + true + PerMonitorV2 + + + + + + + + + + diff --git a/LogonHoursManager/app.rc b/LogonHoursManager/app.rc new file mode 100644 index 0000000000000000000000000000000000000000..7178f2f1be50f152f3bd360a0046f2a9bb259f05 GIT binary patch literal 8892 zcmdT}`)}e#5T2ha^?z_3>2#+?Nr2=ol}@b)P=P0;$QZT+2Z2Wr$x_Mrb=l+fc@GB|W^AWW9cJ0o`x;vN_}WA78|@p;jNg#o zCDh&=xu*TPI*zo9(vX(4{%f*_mMWfoe)6z-mviU$ zKn|goX&rZ=rF*%9{3B?GdT2xHbNp8zbqR7(ANy#}<2P-QSsQdQ1NGlU?^%tK<|(xI z2^7njb{hc$14YD0{s653Z0(j?QyFexwQe-tW~24ft#|}U$|^%W)q=Cg`KTD|MbdKa zpEG#M8)rcc__GtTon1^MI!-%}dNx%p-%S z?+7CUjZ@5XjF|y?KHwsPt`#Y_%1xl}Nxm$`-k}d8+7Kg+?;+mF2_rc9;TTfw;bLC= z8hyRP-J&u%t^f;+!VUCUbX9;hX1YcF{CgP5v=PY=XI9ti>c%cdfO&UKwK|@`1~phq zXs{Of73JH*^C9$G*HNyM1~k}!k5J#Cyn#pG!akp)s>6GwkM*xUOGPtrO)2ez)?c7C z0wsFsGM7uitWKKb-p4aeSRVG4X$g+NeGkYr>w2%{C3piI{sz>J6}=n4=*E*}aS4iA zd7Wg+5KBWDUmFUG@N`zW#EVtI_qe+hE)jK6QcRXKJ5+(gCUn~d@|uXvth&;_kV3lM zLiuH`EoSvUAA?2kQ3BQ~5mt-HCzH4fkyYm*`Q>JO-IyrD=NaSgQGKK#p{z*YdW2rV z`#BK6xKEuQYrbd8mRiF8bMhg2e~+4;RJrQ8K!4T-WxR2nvyZj|P^jb1y2HQJ&{kIJ z2Z}pJtK|0s%zchF(z(FTzMU!6WNaZtYb}9V1G8|=#q1M*F$x|J$?0_85M$%0QdK=M zGahNAVLq-we|`M8z8pkRES76m^%9~NIc*{D)Bl)J%lQ8iVKk||EyUrRYAxND7G+T) zy9o`~z&W!=ti^S_n{U$|^k~vr$86h=|9QNv!)jZ~fk$I{m#x{nW%3@vwnWf8&CsJy zRY$Beth&e{MsKMO(mC4(CozAZxC9;^P}Oj?{{?no z&GQ!VkyS9|&r4w;XL)(A{5fYy4!EX0&|J;RcTfF?G1Tjy5YL|;XW%m>&0*Gc*n?Ks zQ7181dn}Peo8bZql)Q=tzM6wDf+p$ za;CD)dUlN2AmPv4Q+mEfZ^L%Wc=ckm6r=k7>l9}4Kq~FXs7o})s7a!cHDfvs&0|8K zHLm~68@Yz#`mhSBdD+FaXcpG@wtMB7!u`?i?&WbmTko;LSO@>~_7D2we?c`^g6}Y6 zFsjkt&7#~dXm z9*Lp?F2){4okB{KA&0BE>PoJ-oJ+>Qeb~2xI;D<(X2WBZ%6qdX_vgAx?J07vgF2;e zkSgcMo+~SlTfC0f9A;-mH|7(Wcu{*)J^ZHVnVJ@Dmy?h4x zYg2xO%^Z8w2m7aMGS+EwN872{zQj}Z&q^E;ja;Jgy~N68a^EFXJ5Qf;g{Y3xPfSA4 z$fCoYL_0^x?(>|pm29fhCv|D(Ez68fJA=s{KmXKX-nmQ`%`B`Gz7Z?S&hs)61a&jt{Ll;$%`{^ABQW`xF%%AuH(n5w3!;GvN7>8XIZO_7FyVU%SK+u n&}vHP-1+^vr}~Uh^H23%kE^K + + + + \ No newline at end of file diff --git a/LogonHoursManager/resource.h b/LogonHoursManager/resource.h new file mode 100644 index 0000000..c6cac94 --- /dev/null +++ b/LogonHoursManager/resource.h @@ -0,0 +1,28 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by app.rc +// +#define IDD_MAIN 102 +#define IDS_APP_TITLE 103 +#define IDD_ABOUTBOX 103 +#define IDM_ABOUT 104 +#define IDS_WINDOW_CLASS 104 +#define IDM_EXIT 105 +#define IDI_APP 107 +#define IDC_ACCELERATORS 108 +#define IDM_APP 109 +#define IDR_MAINFRAME 128 +#define IDC_COMBO_USER 1000 +#define IDC_GROUP_HOURS 1002 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 130 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1004 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/LogonHoursManager/stdafx.cpp b/LogonHoursManager/stdafx.cpp new file mode 100644 index 0000000..6acde0d --- /dev/null +++ b/LogonHoursManager/stdafx.cpp @@ -0,0 +1,3 @@ +#include "stdafx.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/LogonHoursManager/stdafx.h b/LogonHoursManager/stdafx.h new file mode 100644 index 0000000..8c6ad1f --- /dev/null +++ b/LogonHoursManager/stdafx.h @@ -0,0 +1,40 @@ +// Files listed below are compiled only once, improving build performance for future builds. +// This also affects IntelliSense performance, including code completion and many code browsing features. +// However, files listed here are ALL re-compiled if any one of them is updated between builds. +// Do not add files here that you will be updating frequently as this negates the performance advantage. + +#ifndef STDAFX_H_LogonHoursManager_ +#define STDAFX_H_LogonHoursManager_ + +#include "targetver.h" +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include + +#include // NetUserGetInfo(), etc. + +// C RunTime Header Files +#include +#include +#include +#include + +// STL +#include + +// ATL +#include + +// WTL +#include +extern CAppModule _Module; +#include // MSG_WM_INITDIALOG, etc. +#include // CToolTipCtrl +// #define _ATL_USE_DDX_FLOAT +#include +#include // CUpdateUI +#include + +#include + +#endif diff --git a/LogonHoursManager/targetver.h b/LogonHoursManager/targetver.h new file mode 100644 index 0000000..5a9c7ff --- /dev/null +++ b/LogonHoursManager/targetver.h @@ -0,0 +1,6 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. +#include diff --git a/LogonHoursService/LogonHoursService.vcxproj b/LogonHoursService/LogonHoursService.vcxproj index 0cd1db4..eb98f3e 100644 --- a/LogonHoursService/LogonHoursService.vcxproj +++ b/LogonHoursService/LogonHoursService.vcxproj @@ -30,20 +30,20 @@ - - + + - + <_ProjectFileVersion>15.0.28307.799 - + true - + false @@ -77,8 +77,7 @@ - Create - Create + Create diff --git a/LogonHoursService/WorkerThread.cpp b/LogonHoursService/WorkerThread.cpp index e989540..6b7bd20 100644 --- a/LogonHoursService/WorkerThread.cpp +++ b/LogonHoursService/WorkerThread.cpp @@ -1,5 +1,7 @@ #include "stdafx.h" +#include + #include "config.h" #include "Logger.h" #include "WorkerThread.h" @@ -28,98 +30,6 @@ LPSTR W2A(LPCWSTR lpwszStrIn) WorkerData g_WorkerData; -enum EDayOfWeek -{ - Sun, Mon, Tue, Wed, Thu, Fri, Sat, - FIRST = Sun, - LAST = Sat, -}; -EDayOfWeek& operator ++(EDayOfWeek& day) -{ - if (day < EDayOfWeek::LAST) - { - int value = static_cast(day); - day = static_cast(++value); - return day; - } - LOG_ERROR(__func__) << "Cannot increment " << int(day); - return day; -} - -class LogonHours -{ -public: - LogonHours(PBYTE usri2_logon_hours) - { - Init(usri2_logon_hours); - } - void Init(PBYTE usri2_logon_hours) - { - m_all = true; - int day = Sun; - int hour = 0; // 0:00 - for (int p = 0; p < 21; ++p) - { - const BYTE chunk = usri2_logon_hours[p]; - BYTE mask = 0b00000001; - for (int bit = 0; bit < 8; ++bit) - { - m_data[day][hour] = (chunk & mask) != 0; - if (!m_data[day][hour]) - m_all = false; - ++hour; - mask <<= 1; - if (hour >= 24) - { - ++day; - hour = 0; - } - } - } - } - - bool All() const { return m_all; } - bool Allowed(EDayOfWeek day, WORD hour) const - { - if (Sun <= day && day <= Sat && 0 <= hour && hour <= 23) - return m_data[day][hour]; - return false; - } - // @return seconds left (for the given time) until the allowed logon period expires, or -1 if no restrictions are set - // @note 0 (zero) is returned if logon isn't allowed during given time - long SecondsLeft(EDayOfWeek day, WORD hour, WORD minute, WORD second) const - { - if (All()) - return -1; - if (!Allowed(day, hour)) - return 0; - long left = (60 - second - 1) + 60 * (60 - minute - 1); // the rest of current hour is allowed - WORD hour_start = hour + 1; // for the current day - start with the next hour - EDayOfWeek d = day; - for (int i = 0; i < 7; ++i) // we'll check all week days starting from the current one - { - for (WORD h = hour_start; h < 24; ++h) // check hours until end of day - { - if (Allowed(day, h)) - left += 3600; - else - return left; - } - hour_start = 0; // for the rest of days - // next day - if (d == EDayOfWeek::LAST) - d = EDayOfWeek::FIRST; - else - ++d; - } - return left; - } - -private: - bool m_all; // true if no logon restrictions - bool m_data[7][24]; -}; - void seconds2time(long seconds, long* days, WORD* hours, WORD* mins, WORD* secs) { if (days) @@ -199,15 +109,9 @@ DWORD WINAPI WorkerThread(LPVOID lpData) { const auto& wusername = session.user; - LPBYTE bufptr; - // Refer to https://docs.microsoft.com/en-us/windows/win32/api/lmaccess/nf-lmaccess-netusergetinfo - const NET_API_STATUS result = NetUserGetInfo(NULL, wusername.c_str(), 2, &bufptr); - if (result == NERR_Success) + LogonHours hours; + if (hours.InitFrom(wusername.c_str())) { - const USER_INFO_2* const userinfo2 = reinterpret_cast(bufptr); - const LogonHours hours(userinfo2->usri2_logon_hours); - NetApiBufferFree(bufptr); - if (!hours.All()) { const std::unique_ptr username(W2A(wusername.c_str())); diff --git a/_props/Cxx.props b/_props/Cxx.props index b0b1095..e814c35 100644 --- a/_props/Cxx.props +++ b/_props/Cxx.props @@ -6,6 +6,13 @@ Unicode + + + + $(MSBuildThisFileDirectory).. + + + EnableFastChecks From 06316e8131b3361dd3a09554e8d1921d7a52a4f6 Mon Sep 17 00:00:00 2001 From: Anton-V-K <20116984+Anton-V-K@users.noreply.github.com> Date: Wed, 3 May 2023 14:32:30 +0300 Subject: [PATCH 2/3] actions upgrade --- .github/workflows/msbuild.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/msbuild.yml b/.github/workflows/msbuild.yml index 29b6ace..6a33d3d 100644 --- a/.github/workflows/msbuild.yml +++ b/.github/workflows/msbuild.yml @@ -16,10 +16,10 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Add MSBuild to PATH - uses: microsoft/setup-msbuild@v1.0.2 + uses: microsoft/setup-msbuild@v1.1 - name: Restore NuGet packages working-directory: ${{env.GITHUB_WORKSPACE}} From e8ce988bb49397a7d22a6f3d91d27f4bc3478bdd Mon Sep 17 00:00:00 2001 From: Anton-V-K <20116984+Anton-V-K@users.noreply.github.com> Date: Wed, 10 May 2023 02:06:48 +0300 Subject: [PATCH 3/3] v1.1.0-alpha --- .gitignore | 2 +- Common/Common.vcxitems | 4 + Common/Logger.cpp | 105 +++++++++++ Common/Logger.h | 2 + Common/LogonHours.cpp | 3 + Common/REPEAT.h | 177 ++++++++++++++++++ {LogonHoursService => Common}/version.h | 6 +- LogonHoursManager/LogonHoursManager.vcxproj | 7 +- .../LogonHoursManager.vcxproj.filters | 3 + LogonHoursManager/MainDialog.cpp | 130 ++++++++++--- LogonHoursManager/MainDialog.h | 18 +- LogonHoursManager/MainModel.cpp | 29 +++ LogonHoursManager/MainModel.h | 2 + LogonHoursManager/UIEx.h | 9 + LogonHoursManager/WinDataExchangeEx.h | 2 +- LogonHoursManager/WinMain.cpp | 2 + LogonHoursManager/stdafx.h | 7 + LogonHoursService/LogonHoursService.rc | 4 +- LogonHoursService/LogonHoursService.vcxproj | 6 - LogonHoursService/main.cpp | 90 +-------- README.md | 41 +++- _props/Cxx.props | 6 + _props/Linker.props | 21 ++- _props/RC.props | 10 + _props/TARGET_EXE.props | 1 + 25 files changed, 539 insertions(+), 148 deletions(-) create mode 100644 Common/Logger.cpp create mode 100644 Common/REPEAT.h rename {LogonHoursService => Common}/version.h (62%) create mode 100644 LogonHoursManager/UIEx.h create mode 100644 _props/RC.props diff --git a/.gitignore b/.gitignore index d456880..d9a1d01 100644 --- a/.gitignore +++ b/.gitignore @@ -38,5 +38,5 @@ /_crashes /_releases -*.vcxproj.user *.aps +*.vcxproj.user diff --git a/Common/Common.vcxitems b/Common/Common.vcxitems index 51ab425..72701ee 100644 --- a/Common/Common.vcxitems +++ b/Common/Common.vcxitems @@ -14,10 +14,14 @@ + + + + \ No newline at end of file diff --git a/Common/Logger.cpp b/Common/Logger.cpp new file mode 100644 index 0000000..aaa3067 --- /dev/null +++ b/Common/Logger.cpp @@ -0,0 +1,105 @@ +#include "stdafx.h" +#include "Logger.h" + +#include +#include +#include +#include + +#include "version.h" + +#pragma comment(lib, "ws2_32.lib") // Needed for log4cpp if PropertyConfigurator is used + +void LogMain(const char* appname) +{ + log4cpp::Category& root = log4cpp::Category::getRoot(); + + //////////////////////////////////////// + // Configure log4cpp + //////////////////////////////////////// + char config_path[_MAX_PATH] = { 0 }; + GetModuleFileNameA(NULL, config_path, _countof(config_path)); + strcat_s(config_path, ".log4cpp"); + try + { + log4cpp::PropertyConfigurator::configure(config_path); + } + catch (const log4cpp::ConfigureFailure& /*ex*/) + { + config_path[0] = 0; + + // Default configuration for log4cpp +#ifdef _DEBUG + root.setPriority(log4cpp::Priority::DEBUG); +#else + root.setPriority(log4cpp::Priority::INFO); +#endif + + } + + const log4cpp::AppenderSet& set = root.getAllAppenders(); + if (set.empty()) // if no appenders are specified in the config file ... + { + char log_path_format[MAX_PATH]; + ExpandEnvironmentStringsA("%TEMP%\\%s.log", log_path_format, _countof(log_path_format)); + + char log_path[MAX_PATH]; + if (appname) + sprintf_s(log_path, log_path_format, appname); + else + { + char module_path[_MAX_PATH] = { 0 }; + GetModuleFileNameA(NULL, module_path, _countof(module_path)); + + char filename[MAX_PATH] = { 0 }; + _splitpath_s(module_path, NULL, 0, NULL, 0, filename, _countof(filename), NULL, 0); + sprintf_s(log_path, log_path_format, filename); + } + +#if 1 + if (log4cpp::Appender* const appender = new log4cpp::DailyRollingFileAppender("logfile", log_path, 14)) +#else + if (log4cpp::Appender* const appender = new log4cpp::RollingFileAppender("logfile", log_path, 10*1024*1024, 10)) +//#else + if (log4cpp::Appender* const appender = new log4cpp::FileAppender("logfile", log_path)) +#endif + { + log4cpp::PatternLayout* const layout = new log4cpp::PatternLayout(); + // Refer to http://www.cplusplus.com/reference/ctime/strftime/ for format specification + layout->setConversionPattern("%d{%Y.%m.%d %H:%M:%S,%l} [%p] [%t] %c: %m%n"); + appender->setLayout(layout); + root.addAppender(appender); + } + if (log4cpp::Appender* const appender = new log4cpp::Win32DebugAppender("debugger")) + { + log4cpp::PatternLayout* const layout = new log4cpp::PatternLayout(); + // Refer to http://www.cplusplus.com/reference/ctime/strftime/ for format specification + layout->setConversionPattern("%d{%H:%M:%S,%l} [%p] [%t] %c: %m%n"); + appender->setLayout(layout); + root.addAppender(appender); + } + } + + LOG_INFO(__func__) << "========================================"; + LOG_INFO(__func__) << "Log initialized successfully"; + if (*config_path) + LOG_INFO(__func__) << "Loaded " << config_path; + +#ifndef _CONSOLE + LOG_INFO(__func__) << "Length of command line: " << _tcslen(GetCommandLine()); +#endif + + LOG_INFO(__func__) << "Version: " << VERSION_STRING; + LOG_INFO(__func__) << "Build : " << __DATE__ << ' ' << __TIME__; +#if defined(_MSC_FULL_VER) + LOG_INFO(__func__) << "_MSC_FULL_VER: " + << _MSC_FULL_VER / 10000000 << '.' + << (_MSC_FULL_VER % 10000000) / 100000 << '.' + << _MSC_FULL_VER % 100000; +#elif defined(_MSC_VER) + LOG_INFO(__func__) << "_MSC_VER: " + << _MSC_VER / 100 << '.' + << _MSC_VER % 100; +#endif + +} diff --git a/Common/Logger.h b/Common/Logger.h index 6128d78..f57981f 100644 --- a/Common/Logger.h +++ b/Common/Logger.h @@ -49,3 +49,5 @@ inline log4cpp::Category& Logger(const void* p = NULL) { return log4cpp::Category::getRoot(); } + +void LogMain(const char* appname = nullptr); diff --git a/Common/LogonHours.cpp b/Common/LogonHours.cpp index 9500c1b..413d027 100644 --- a/Common/LogonHours.cpp +++ b/Common/LogonHours.cpp @@ -65,7 +65,10 @@ bool LogonHours::ApplyTo(const wchar_t* username) const info.usri1020_units_per_week = UNITS_PER_WEEK; // NetUserAdd and NetUserSetInfo functions ignore this member info.usri1020_logon_hours = usri2_logon_hours; if (NetUserSetInfo(NULL, username, 1020, reinterpret_cast(&info), &parm_err) != NERR_Success) + { + LOG_ERROR(__func__) << "NetUserSetInfo() failed with error " << GetLastError(); return false; + } return true; } diff --git a/Common/REPEAT.h b/Common/REPEAT.h new file mode 100644 index 0000000..60cf78a --- /dev/null +++ b/Common/REPEAT.h @@ -0,0 +1,177 @@ +#pragma once + +// The approach was taken from https://dev.to/sgf4/repeat-macro-in-c-2hh0 + + +#define REPEAT_1(FN, START) FN(START+0) +#define REPEAT_2(FN, START) REPEAT_1(FN, START) FN(START+1) +#define REPEAT_3(FN, START) REPEAT_2(FN, START) FN(START+2) +#define REPEAT_4(FN, START) REPEAT_3(FN, START) FN(START+3) +#define REPEAT_5(FN, START) REPEAT_4(FN, START) FN(START+4) +#define REPEAT_6(FN, START) REPEAT_5(FN, START) FN(START+5) +#define REPEAT_7(FN, START) REPEAT_6(FN, START) FN(START+6) +#define REPEAT_8(FN, START) REPEAT_7(FN, START) FN(START+7) +#define REPEAT_9(FN, START) REPEAT_8(FN, START) FN(START+8) +#define REPEAT_10(FN, START) REPEAT_9(FN, START) FN(START+9) +#define REPEAT_11(FN, START) REPEAT_10(FN, START) FN(START+10) +#define REPEAT_12(FN, START) REPEAT_11(FN, START) FN(START+11) +#define REPEAT_13(FN, START) REPEAT_12(FN, START) FN(START+12) +#define REPEAT_14(FN, START) REPEAT_13(FN, START) FN(START+13) +#define REPEAT_15(FN, START) REPEAT_14(FN, START) FN(START+14) +#define REPEAT_16(FN, START) REPEAT_15(FN, START) FN(START+15) +#define REPEAT_17(FN, START) REPEAT_16(FN, START) FN(START+16) +#define REPEAT_18(FN, START) REPEAT_17(FN, START) FN(START+17) +#define REPEAT_19(FN, START) REPEAT_18(FN, START) FN(START+18) +#define REPEAT_20(FN, START) REPEAT_19(FN, START) FN(START+19) +#define REPEAT_21(FN, START) REPEAT_20(FN, START) FN(START+20) +#define REPEAT_22(FN, START) REPEAT_21(FN, START) FN(START+21) +#define REPEAT_23(FN, START) REPEAT_22(FN, START) FN(START+22) +#define REPEAT_24(FN, START) REPEAT_23(FN, START) FN(START+23) +#define REPEAT_25(FN, START) REPEAT_24(FN, START) FN(START+24) +#define REPEAT_26(FN, START) REPEAT_25(FN, START) FN(START+25) +#define REPEAT_27(FN, START) REPEAT_26(FN, START) FN(START+26) +#define REPEAT_28(FN, START) REPEAT_27(FN, START) FN(START+27) +#define REPEAT_29(FN, START) REPEAT_28(FN, START) FN(START+28) +#define REPEAT_30(FN, START) REPEAT_29(FN, START) FN(START+29) +#define REPEAT_31(FN, START) REPEAT_30(FN, START) FN(START+30) +#define REPEAT_32(FN, START) REPEAT_31(FN, START) FN(START+31) +#define REPEAT_33(FN, START) REPEAT_32(FN, START) FN(START+32) +#define REPEAT_34(FN, START) REPEAT_33(FN, START) FN(START+33) +#define REPEAT_35(FN, START) REPEAT_34(FN, START) FN(START+34) +#define REPEAT_36(FN, START) REPEAT_35(FN, START) FN(START+35) +#define REPEAT_37(FN, START) REPEAT_36(FN, START) FN(START+36) +#define REPEAT_38(FN, START) REPEAT_37(FN, START) FN(START+37) +#define REPEAT_39(FN, START) REPEAT_38(FN, START) FN(START+38) +#define REPEAT_40(FN, START) REPEAT_39(FN, START) FN(START+39) +#define REPEAT_41(FN, START) REPEAT_40(FN, START) FN(START+40) +#define REPEAT_42(FN, START) REPEAT_41(FN, START) FN(START+41) +#define REPEAT_43(FN, START) REPEAT_42(FN, START) FN(START+42) +#define REPEAT_44(FN, START) REPEAT_43(FN, START) FN(START+43) +#define REPEAT_45(FN, START) REPEAT_44(FN, START) FN(START+44) +#define REPEAT_46(FN, START) REPEAT_45(FN, START) FN(START+45) +#define REPEAT_47(FN, START) REPEAT_46(FN, START) FN(START+46) +#define REPEAT_48(FN, START) REPEAT_47(FN, START) FN(START+47) +#define REPEAT_49(FN, START) REPEAT_48(FN, START) FN(START+48) +#define REPEAT_50(FN, START) REPEAT_49(FN, START) FN(START+49) +#define REPEAT_51(FN, START) REPEAT_50(FN, START) FN(START+50) +#define REPEAT_52(FN, START) REPEAT_51(FN, START) FN(START+51) +#define REPEAT_53(FN, START) REPEAT_52(FN, START) FN(START+52) +#define REPEAT_54(FN, START) REPEAT_53(FN, START) FN(START+53) +#define REPEAT_55(FN, START) REPEAT_54(FN, START) FN(START+54) +#define REPEAT_56(FN, START) REPEAT_55(FN, START) FN(START+55) +#define REPEAT_57(FN, START) REPEAT_56(FN, START) FN(START+56) +#define REPEAT_58(FN, START) REPEAT_57(FN, START) FN(START+57) +#define REPEAT_59(FN, START) REPEAT_58(FN, START) FN(START+58) +#define REPEAT_60(FN, START) REPEAT_59(FN, START) FN(START+59) +#define REPEAT_61(FN, START) REPEAT_60(FN, START) FN(START+60) +#define REPEAT_62(FN, START) REPEAT_61(FN, START) FN(START+61) +#define REPEAT_63(FN, START) REPEAT_62(FN, START) FN(START+62) +#define REPEAT_64(FN, START) REPEAT_63(FN, START) FN(START+63) +#define REPEAT_65(FN, START) REPEAT_64(FN, START) FN(START+64) +#define REPEAT_66(FN, START) REPEAT_65(FN, START) FN(START+65) +#define REPEAT_67(FN, START) REPEAT_66(FN, START) FN(START+66) +#define REPEAT_68(FN, START) REPEAT_67(FN, START) FN(START+67) +#define REPEAT_69(FN, START) REPEAT_68(FN, START) FN(START+68) +#define REPEAT_70(FN, START) REPEAT_69(FN, START) FN(START+69) +#define REPEAT_71(FN, START) REPEAT_70(FN, START) FN(START+70) +#define REPEAT_72(FN, START) REPEAT_71(FN, START) FN(START+71) +#define REPEAT_73(FN, START) REPEAT_72(FN, START) FN(START+72) +#define REPEAT_74(FN, START) REPEAT_73(FN, START) FN(START+73) +#define REPEAT_75(FN, START) REPEAT_74(FN, START) FN(START+74) +#define REPEAT_76(FN, START) REPEAT_75(FN, START) FN(START+75) +#define REPEAT_77(FN, START) REPEAT_76(FN, START) FN(START+76) +#define REPEAT_78(FN, START) REPEAT_77(FN, START) FN(START+77) +#define REPEAT_79(FN, START) REPEAT_78(FN, START) FN(START+78) +#define REPEAT_80(FN, START) REPEAT_79(FN, START) FN(START+79) +#define REPEAT_81(FN, START) REPEAT_80(FN, START) FN(START+80) +#define REPEAT_82(FN, START) REPEAT_81(FN, START) FN(START+81) +#define REPEAT_83(FN, START) REPEAT_82(FN, START) FN(START+82) +#define REPEAT_84(FN, START) REPEAT_83(FN, START) FN(START+83) +#define REPEAT_85(FN, START) REPEAT_84(FN, START) FN(START+84) +#define REPEAT_86(FN, START) REPEAT_85(FN, START) FN(START+85) +#define REPEAT_87(FN, START) REPEAT_86(FN, START) FN(START+86) +#define REPEAT_88(FN, START) REPEAT_87(FN, START) FN(START+87) +#define REPEAT_89(FN, START) REPEAT_88(FN, START) FN(START+88) +#define REPEAT_90(FN, START) REPEAT_89(FN, START) FN(START+89) +#define REPEAT_91(FN, START) REPEAT_90(FN, START) FN(START+90) +#define REPEAT_92(FN, START) REPEAT_91(FN, START) FN(START+91) +#define REPEAT_93(FN, START) REPEAT_92(FN, START) FN(START+92) +#define REPEAT_94(FN, START) REPEAT_93(FN, START) FN(START+93) +#define REPEAT_95(FN, START) REPEAT_94(FN, START) FN(START+94) +#define REPEAT_96(FN, START) REPEAT_95(FN, START) FN(START+95) +#define REPEAT_97(FN, START) REPEAT_96(FN, START) FN(START+96) +#define REPEAT_98(FN, START) REPEAT_97(FN, START) FN(START+97) +#define REPEAT_99(FN, START) REPEAT_98(FN, START) FN(START+98) +#define REPEAT_100(FN, START) REPEAT_99(FN, START) FN(START+99) +#define REPEAT_101(FN, START) REPEAT_100(FN, START) FN(START+100) +#define REPEAT_102(FN, START) REPEAT_101(FN, START) FN(START+101) +#define REPEAT_103(FN, START) REPEAT_102(FN, START) FN(START+102) +#define REPEAT_104(FN, START) REPEAT_103(FN, START) FN(START+103) +#define REPEAT_105(FN, START) REPEAT_104(FN, START) FN(START+104) +#define REPEAT_106(FN, START) REPEAT_105(FN, START) FN(START+105) +#define REPEAT_107(FN, START) REPEAT_106(FN, START) FN(START+106) +#define REPEAT_108(FN, START) REPEAT_107(FN, START) FN(START+107) +#define REPEAT_109(FN, START) REPEAT_108(FN, START) FN(START+108) +#define REPEAT_110(FN, START) REPEAT_109(FN, START) FN(START+109) +#define REPEAT_111(FN, START) REPEAT_110(FN, START) FN(START+110) +#define REPEAT_112(FN, START) REPEAT_111(FN, START) FN(START+111) +#define REPEAT_113(FN, START) REPEAT_112(FN, START) FN(START+112) +#define REPEAT_114(FN, START) REPEAT_113(FN, START) FN(START+113) +#define REPEAT_115(FN, START) REPEAT_114(FN, START) FN(START+114) +#define REPEAT_116(FN, START) REPEAT_115(FN, START) FN(START+115) +#define REPEAT_117(FN, START) REPEAT_116(FN, START) FN(START+116) +#define REPEAT_118(FN, START) REPEAT_117(FN, START) FN(START+117) +#define REPEAT_119(FN, START) REPEAT_118(FN, START) FN(START+118) +#define REPEAT_120(FN, START) REPEAT_119(FN, START) FN(START+119) +#define REPEAT_121(FN, START) REPEAT_120(FN, START) FN(START+120) +#define REPEAT_122(FN, START) REPEAT_121(FN, START) FN(START+121) +#define REPEAT_123(FN, START) REPEAT_122(FN, START) FN(START+122) +#define REPEAT_124(FN, START) REPEAT_123(FN, START) FN(START+123) +#define REPEAT_125(FN, START) REPEAT_124(FN, START) FN(START+124) +#define REPEAT_126(FN, START) REPEAT_125(FN, START) FN(START+125) +#define REPEAT_127(FN, START) REPEAT_126(FN, START) FN(START+126) +#define REPEAT_128(FN, START) REPEAT_127(FN, START) FN(START+127) +#define REPEAT_129(FN, START) REPEAT_128(FN, START) FN(START+128) +#define REPEAT_130(FN, START) REPEAT_129(FN, START) FN(START+129) +#define REPEAT_131(FN, START) REPEAT_130(FN, START) FN(START+130) +#define REPEAT_132(FN, START) REPEAT_131(FN, START) FN(START+131) +#define REPEAT_133(FN, START) REPEAT_132(FN, START) FN(START+132) +#define REPEAT_134(FN, START) REPEAT_133(FN, START) FN(START+133) +#define REPEAT_135(FN, START) REPEAT_134(FN, START) FN(START+134) +#define REPEAT_136(FN, START) REPEAT_135(FN, START) FN(START+135) +#define REPEAT_137(FN, START) REPEAT_136(FN, START) FN(START+136) +#define REPEAT_138(FN, START) REPEAT_137(FN, START) FN(START+137) +#define REPEAT_139(FN, START) REPEAT_138(FN, START) FN(START+138) +#define REPEAT_140(FN, START) REPEAT_139(FN, START) FN(START+139) +#define REPEAT_141(FN, START) REPEAT_140(FN, START) FN(START+140) +#define REPEAT_142(FN, START) REPEAT_141(FN, START) FN(START+141) +#define REPEAT_143(FN, START) REPEAT_142(FN, START) FN(START+142) +#define REPEAT_144(FN, START) REPEAT_143(FN, START) FN(START+143) +#define REPEAT_145(FN, START) REPEAT_144(FN, START) FN(START+144) +#define REPEAT_146(FN, START) REPEAT_145(FN, START) FN(START+145) +#define REPEAT_147(FN, START) REPEAT_146(FN, START) FN(START+146) +#define REPEAT_148(FN, START) REPEAT_147(FN, START) FN(START+147) +#define REPEAT_149(FN, START) REPEAT_148(FN, START) FN(START+148) +#define REPEAT_150(FN, START) REPEAT_149(FN, START) FN(START+149) +#define REPEAT_151(FN, START) REPEAT_150(FN, START) FN(START+150) +#define REPEAT_152(FN, START) REPEAT_151(FN, START) FN(START+151) +#define REPEAT_153(FN, START) REPEAT_152(FN, START) FN(START+152) +#define REPEAT_154(FN, START) REPEAT_153(FN, START) FN(START+153) +#define REPEAT_155(FN, START) REPEAT_154(FN, START) FN(START+154) +#define REPEAT_156(FN, START) REPEAT_155(FN, START) FN(START+155) +#define REPEAT_157(FN, START) REPEAT_156(FN, START) FN(START+156) +#define REPEAT_158(FN, START) REPEAT_157(FN, START) FN(START+157) +#define REPEAT_159(FN, START) REPEAT_158(FN, START) FN(START+158) +#define REPEAT_160(FN, START) REPEAT_159(FN, START) FN(START+159) +#define REPEAT_161(FN, START) REPEAT_160(FN, START) FN(START+160) +#define REPEAT_162(FN, START) REPEAT_161(FN, START) FN(START+161) +#define REPEAT_163(FN, START) REPEAT_162(FN, START) FN(START+162) +#define REPEAT_164(FN, START) REPEAT_163(FN, START) FN(START+163) +#define REPEAT_165(FN, START) REPEAT_164(FN, START) FN(START+164) +#define REPEAT_166(FN, START) REPEAT_165(FN, START) FN(START+165) +#define REPEAT_167(FN, START) REPEAT_166(FN, START) FN(START+166) +#define REPEAT_168(FN, START) REPEAT_167(FN, START) FN(START+167) + +#define REPEAT_N(FN, N, START) REPEAT_##N(FN, START) +// #define EVAL(x) #x +// TODO? #define REPEAT(FN, N1, N2) REPEAT_N(FN, EVAL(N2-N1), N1) diff --git a/LogonHoursService/version.h b/Common/version.h similarity index 62% rename from LogonHoursService/version.h rename to Common/version.h index 2d6c3a5..cffd4bd 100644 --- a/LogonHoursService/version.h +++ b/Common/version.h @@ -8,9 +8,9 @@ #endif #define VERSION_MAJOR 1 -#define VERSION_MINOR 0 -#define VERSION_REV 3 +#define VERSION_MINOR 1 +#define VERSION_REV 0 -#define VERSION_STRING "1.0.3 Alpha" VERSION_CONFIG +#define VERSION_STRING "1.1.0 Alpha" VERSION_CONFIG #endif diff --git a/LogonHoursManager/LogonHoursManager.vcxproj b/LogonHoursManager/LogonHoursManager.vcxproj index cccc939..0473486 100644 --- a/LogonHoursManager/LogonHoursManager.vcxproj +++ b/LogonHoursManager/LogonHoursManager.vcxproj @@ -50,12 +50,6 @@ - - true - - - false - Level3 @@ -117,6 +111,7 @@ + diff --git a/LogonHoursManager/LogonHoursManager.vcxproj.filters b/LogonHoursManager/LogonHoursManager.vcxproj.filters index 74e1671..1bb7466 100644 --- a/LogonHoursManager/LogonHoursManager.vcxproj.filters +++ b/LogonHoursManager/LogonHoursManager.vcxproj.filters @@ -33,6 +33,9 @@ Header Files + + Header Files + diff --git a/LogonHoursManager/MainDialog.cpp b/LogonHoursManager/MainDialog.cpp index 6505b48..fb8106a 100644 --- a/LogonHoursManager/MainDialog.cpp +++ b/LogonHoursManager/MainDialog.cpp @@ -14,6 +14,23 @@ CMainDialog::~CMainDialog() LRESULT CMainDialog::OnClose(UINT, int, HWND) { + if (m_week_modified) + { + TCHAR message[1024]; + _stprintf_s(message, _T("Allowed Logon Hours for the user '%s' were modified.\nDo you want to apply the changes?"), m_userName); + const auto answer = MessageBox(message, _T("Confirm the changes"), MB_ICONQUESTION | MB_YESNOCANCEL); + switch (answer) + { + case IDCANCEL: + return 0; + case IDYES: + ApplyChanges(true); + break; + case IDNO: + // discard changes + break; + } + } EndDialog(0); return 0; } @@ -27,6 +44,12 @@ LRESULT CMainDialog::OnInitDialog(HWND, LPARAM) HICON hIcon = ::LoadIcon(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDI_APP)); SetIcon(hIcon); + if (!m_model.isElevated()) + { + SetDlgItemText(IDOK, _T("Restart")); + SendDlgItemMessage(IDOK, BCM_SETSHIELD, 0, TRUE); + } + m_ctrlToolTip.Create(m_hWnd); // , rcDefault, NULL, WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP, WS_EX_TOPMOST); ATLASSERT(m_ctrlToolTip.IsWindow()); @@ -111,47 +134,109 @@ LRESULT CMainDialog::OnInitDialog(HWND, LPARAM) FetchData(); - DoDataExchange(DDX_LOAD); - UIAddChildWindowContainer(m_hWnd); UpdateUI(true); return 0; } -/*DEL? + +LRESULT CMainDialog::OnOK(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& bHandled) +{ + if (m_model.isElevated()) + { + ApplyChanges(false); + } + else + { + // TODO move to another class? + + TCHAR filepath[MAX_PATH]; + GetModuleFileName(NULL, filepath, sizeof(filepath)); + + TCHAR curdir[MAX_PATH]; + GetCurrentDirectory(_countof(curdir), curdir); + + SHELLEXECUTEINFO shex = { 0 }; + + shex.cbSize = sizeof(shex); + shex.fMask = SEE_MASK_UNICODE | SEE_MASK_NOZONECHECKS | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC; + shex.lpVerb = L"runas"; + shex.nShow = SW_SHOW; + shex.lpFile = filepath; + // TODO pass currently selected user + shex.lpParameters = GetCommandLine(); + shex.lpDirectory = curdir; + + if (ShellExecuteEx(&shex)) + { + EndDialog(0); + } + else + LOG_ERROR(__func__) << "ShellExecuteEx() failed with error " << GetLastError(); + } + // bHandled = TRUE; + return 0; +} + LRESULT CMainDialog::OnHourChanged(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) { if (wNotifyCode == BN_CLICKED) { - // UpdateData(); - // DisplayGUID(); - if (IDC_HOURS_FIRST <= wID && wID <= IDC_HOURS_LAST) + if (m_model.isElevated()) { - const auto span = wID - IDC_HOURS_FIRST; - const auto day = span / 24; - const auto hour = span % 24; - const auto check = Button_GetCheck(hWndCtl); - m_model.getHours().Allow(EDayOfWeek(day), hour, check == BST_CHECKED); - // TODO m_model.getHours().ApplyTo(m_model.getUser()); + m_week_modified = true; + UpdateUI(); + } + else + { + bHandled = FALSE; + } } return 0; } -*/ + LRESULT CMainDialog::OnUserChanged(UINT, int, HWND) { + if (m_week_modified) + { + TCHAR message[1024]; + _stprintf_s(message, _T("Allowed Logon Hours for the user '%s' were modified.\nDo you want to apply the changes?"), m_userName); + if (MessageBox(message, _T("Confirm the changes"), MB_ICONQUESTION | MB_YESNO) == IDYES) + { + ApplyChanges(true); + } + } UpdateUI(); m_model.selectUser(m_userName); FetchData(); - DoDataExchange(DDX_LOAD); + UpdateUI(true); return 0; } +void CMainDialog::ApplyChanges(bool prev_user) +{ + const std::wstring userName = prev_user ? m_userName : L""; // m_userName will be changed during DDX + DoDataExchange(DDX_SAVE); + m_model.getHours().Set(m_week, false); + if (m_model.getHours().ApplyTo(userName.empty() ? m_userName : userName.c_str())) + { + m_week_modified = false; + } + else + { + MessageBox(_T("Cannot apply these changes!"), _T("Error"), MB_ICONERROR | MB_OK); + } + UpdateUI(false); +} + void CMainDialog::FetchData() { wcscpy_s(m_userName, m_model.getUser()); m_users = m_model.getUsers(); m_model.getHours().Get(m_week, false); + m_week_modified = false; + DoDataExchange(DDX_LOAD); } void CMainDialog::UpdateUI(bool saved) @@ -159,26 +244,15 @@ void CMainDialog::UpdateUI(bool saved) const auto isValid = saved || DoDataExchange(DDX_SAVE); for (UINT id = IDC_HOURS_FIRST; id <= IDC_HOURS_LAST; ++id) - UIEnable(id, isValid && m_userName[0] != 0); + UIEnable(id, isValid && m_userName[0] != 0 && m_model.isElevated()); if (isValid) { - -/*DEL - UIEnable(IDC_EXP, TRUE); - UIEnable(IDC_LN, m_number > 0); - UIEnable(IDC_SQRT, m_number >= 0); - UIEnable(IDC_FACT, m_number <= 100 && m_number > 0 && ((double)(unsigned long)m_number == m_number)); -*/ + UIEnable(IDOK, !m_model.isElevated() || m_week_modified); } else { -/*DEL - UIEnable(IDC_EXP, FALSE); - UIEnable(IDC_LN, FALSE); - UIEnable(IDC_SQRT, FALSE); - UIEnable(IDC_FACT, FALSE); -*/ + UIEnable(IDOK, FALSE); } UIUpdateChildWindows(); diff --git a/LogonHoursManager/MainDialog.h b/LogonHoursManager/MainDialog.h index 5dd34ab..d77aee3 100644 --- a/LogonHoursManager/MainDialog.h +++ b/LogonHoursManager/MainDialog.h @@ -9,6 +9,7 @@ #include #include "WinDataExchangeEx.h" +#include "UIEx.h" #include "resource.h" @@ -30,7 +31,11 @@ class CMainDialog TCHAR m_userName[256] = _T(""); std::vector m_users; + /// + /// Temporary data while the user is editing it + /// WeekHours m_week; + bool m_week_modified{ false }; public: enum EID @@ -38,13 +43,14 @@ class CMainDialog IDD = IDD_MAIN, IDC_HOURS_FIRST = IDC_GROUP_HOURS + 1, - IDC_HOURS_LAST = IDC_HOURS_FIRST + 7 * 24, + IDC_HOURS_LAST = IDC_HOURS_FIRST + 7 * 24 - 1, IDC_HOURS_COUNT = IDC_HOURS_LAST - IDC_HOURS_FIRST, }; BEGIN_MSG_MAP(Self) MSG_WM_INITDIALOG(OnInitDialog) - // COMMAND_RANGE_HANDLER(IDC_HOURS_FIRST, IDC_HOURS_LAST, OnHourChanged) + COMMAND_ID_HANDLER(IDOK, OnOK) + COMMAND_RANGE_HANDLER(IDC_HOURS_FIRST, IDC_HOURS_LAST, OnHourChanged) COMMAND_HANDLER_EX(IDC_COMBO_USER, CBN_SELENDOK, OnUserChanged) COMMAND_HANDLER_EX(IDCANCEL, BN_CLICKED, OnClose) END_MSG_MAP() @@ -59,6 +65,9 @@ class CMainDialog BEGIN_UPDATE_UI_MAP(Self) UPDATE_ELEMENT(IDC_COMBO_USER, UPDUI_CHILDWINDOW) UPDATE_ELEMENT(IDOK, UPDUI_CHILDWINDOW) + UPDATE_CHILDWINDOWS(IDC_HOURS_FIRST, IDC_HOURS_LAST, 168) // 7 * 24 + // TODO? UPDATE_ELEMENT(IDC_HOURS_FIRST, UPDUI_CHILDWINDOW) + // TODO? UPDATE_ELEMENT(IDC_HOURS_LAST, UPDUI_CHILDWINDOW) END_UPDATE_UI_MAP() CMainDialog(MainModel& model); @@ -72,10 +81,15 @@ class CMainDialog LRESULT OnInitDialog(HWND, LPARAM); private: + LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/); + LRESULT OnHourChanged(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled); LRESULT OnUserChanged(UINT, int, HWND); +private: + void ApplyChanges(bool prev_user); + void FetchData(); void UpdateUI(bool saved = false); diff --git a/LogonHoursManager/MainModel.cpp b/LogonHoursManager/MainModel.cpp index cf0c8bf..3c13f59 100644 --- a/LogonHoursManager/MainModel.cpp +++ b/LogonHoursManager/MainModel.cpp @@ -46,6 +46,35 @@ std::vector MainModel::getUsers() const return users; } +bool MainModel::isElevated() const +{ + static bool init_ok{ false }; + static TOKEN_ELEVATION_TYPE type; + if (!init_ok) + { + init_ok = true; + HANDLE hProcess; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hProcess)) + { + DWORD size; + if (GetTokenInformation(hProcess, TokenElevationType, &type, sizeof(type), &size)) + { + if (size == sizeof(type)) + { + LOG_DEBUG(__func__) << "GetTokenInformation: TOKEN_ELEVATION_TYPE = " << int(type); + } + } + else + LOG_ERROR(__func__) << "GetTokenInformation(..., TokenElevationType, ...) failed with error " << GetLastError(); + CloseHandle(hProcess); + } + else + LOG_ERROR(__func__) << "OpenProcessToken(GetCurrentProcess(), TOKEN_READ, ...) failed with error " << GetLastError(); + } + + return type == TokenElevationTypeFull; +} + void MainModel::selectUser(const wchar_t* user) { if (user) diff --git a/LogonHoursManager/MainModel.h b/LogonHoursManager/MainModel.h index f5b66a1..7bb4a78 100644 --- a/LogonHoursManager/MainModel.h +++ b/LogonHoursManager/MainModel.h @@ -14,6 +14,8 @@ class MainModel const wchar_t* getUser() const; std::vector getUsers() const; + bool isElevated() const; + void selectUser(const wchar_t* user); protected: diff --git a/LogonHoursManager/UIEx.h b/LogonHoursManager/UIEx.h new file mode 100644 index 0000000..1ca4a70 --- /dev/null +++ b/LogonHoursManager/UIEx.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#define UPDATE_CHILDWINDOW(nID) \ + { nID, UPDUI_CHILDWINDOW }, + +#define UPDATE_CHILDWINDOWS(nStartID, nEndId, count) \ + REPEAT_N(UPDATE_CHILDWINDOW, count, nStartID) diff --git a/LogonHoursManager/WinDataExchangeEx.h b/LogonHoursManager/WinDataExchangeEx.h index e5d77da..9c714bd 100644 --- a/LogonHoursManager/WinDataExchangeEx.h +++ b/LogonHoursManager/WinDataExchangeEx.h @@ -98,7 +98,7 @@ class CWinDataExchangeEx : public CWinDataExchange }; #define DDX_CHECK_ARRAY(nFirstID, nLastID, arr) \ - static_assert(_countof(arr) == nLastID - nFirstID, "The elements range should match the array's size"); \ + static_assert(_countof(arr) == nLastID - nFirstID + 1, "The elements range should match the array's size"); \ if (nFirstID <= nCtlID && nCtlID <= nLastID) \ DDX_Check(nCtlID, arr[nCtlID - nFirstID], bSaveAndValidate); \ if (nCtlID == (UINT)-1) \ diff --git a/LogonHoursManager/WinMain.cpp b/LogonHoursManager/WinMain.cpp index a6a91a0..16d4c31 100644 --- a/LogonHoursManager/WinMain.cpp +++ b/LogonHoursManager/WinMain.cpp @@ -11,6 +11,8 @@ CAppModule _Module; int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance , _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { + LogMain(); + _Module.Init(NULL, hInstance); MainModel model; diff --git a/LogonHoursManager/stdafx.h b/LogonHoursManager/stdafx.h index 8c6ad1f..d55856d 100644 --- a/LogonHoursManager/stdafx.h +++ b/LogonHoursManager/stdafx.h @@ -12,6 +12,8 @@ #include #include // NetUserGetInfo(), etc. +#include // IsUserAnAdmin(), etc. +#include // C RunTime Header Files #include @@ -35,6 +37,11 @@ extern CAppModule _Module; #include // CUpdateUI #include +#include +#include +#include +#include + #include #endif diff --git a/LogonHoursService/LogonHoursService.rc b/LogonHoursService/LogonHoursService.rc index 4fa8c80..1465378 100644 --- a/LogonHoursService/LogonHoursService.rc +++ b/LogonHoursService/LogonHoursService.rc @@ -10,7 +10,7 @@ #include #include "config.h" -#include "version.h" +#include "Common/version.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS @@ -37,7 +37,7 @@ BEGIN "#include \r\n" "\r\n" "#include ""config.h""\r\n" - "#include ""version.h""\r\n" + "#include ""Common/version.h""\r\n" "\0" END diff --git a/LogonHoursService/LogonHoursService.vcxproj b/LogonHoursService/LogonHoursService.vcxproj index eb98f3e..648d720 100644 --- a/LogonHoursService/LogonHoursService.vcxproj +++ b/LogonHoursService/LogonHoursService.vcxproj @@ -40,12 +40,6 @@ <_ProjectFileVersion>15.0.28307.799 - - true - - - false - _CONSOLE;WIN32;_DEBUG;%(PreprocessorDefinitions) diff --git a/LogonHoursService/main.cpp b/LogonHoursService/main.cpp index 0fa7aad..f620671 100644 --- a/LogonHoursService/main.cpp +++ b/LogonHoursService/main.cpp @@ -6,10 +6,6 @@ #include "ServiceManager.h" #include "WorkerThread.h" -#include "version.h" - -#pragma comment(lib, "ws2_32.lib") // Needed for log4cpp if PropertyConfigurator is used - void Main() { LOG_DEBUG(__func__) << "Entry"; @@ -56,91 +52,7 @@ int APIENTRY _tWinMain(HINSTANCE hInstance, { OutputDebugString(_T(SERVICE_NAME) _T(": Start\n")); - log4cpp::Category& root = log4cpp::Category::getRoot(); - - //////////////////////////////////////// - // Configigure log4cpp - //////////////////////////////////////// - char config_path[_MAX_PATH] = { 0 }; - GetModuleFileNameA(NULL, config_path, _countof(config_path)); - strcat_s(config_path, ".log4cpp"); - try - { - log4cpp::PropertyConfigurator::configure(config_path); - } - catch (const log4cpp::ConfigureFailure& /*ex*/) - { - config_path[0] = 0; -/* -#ifdef _CONSOLE - std::cerr - << ex.what() - << " [log4cpp::ConfigureFailure catched] while reading " - << file_log4cpp_init - << std::endl; -#endif -*/ - - // Default configuration for log4cpp -#ifdef _DEBUG - root.setPriority(log4cpp::Priority::DEBUG); -#else - root.setPriority(log4cpp::Priority::INFO); -#endif - - } - - const log4cpp::AppenderSet& set = root.getAllAppenders(); - if (set.empty()) // if no appenders are specified in the config file ... - { - char log_path[MAX_PATH]; - ExpandEnvironmentStringsA("%TEMP%\\" SERVICE_NAME ".log", log_path, _countof(log_path)); -#if 1 - if (log4cpp::Appender* const appender = new log4cpp::DailyRollingFileAppender("logfile", log_path, 14)) -#else - if (log4cpp::Appender* const appender = new log4cpp::RollingFileAppender("logfile", log_path, 10*1024*1024, 10)) -//#else - if (log4cpp::Appender* const appender = new log4cpp::FileAppender("logfile", log_path)) -#endif - { - log4cpp::PatternLayout* const layout = new log4cpp::PatternLayout(); - // Refer to http://www.cplusplus.com/reference/ctime/strftime/ for format specification - layout->setConversionPattern("%d{%Y.%m.%d %H:%M:%S,%l} [%p] [%t] %c: %m%n"); - appender->setLayout(layout); - root.addAppender(appender); - } - if (log4cpp::Appender* const appender = new log4cpp::Win32DebugAppender("debugger")) - { - log4cpp::PatternLayout* const layout = new log4cpp::PatternLayout(); - // Refer to http://www.cplusplus.com/reference/ctime/strftime/ for format specification - layout->setConversionPattern("%d{%H:%M:%S,%l} [%p] [%t] %c: %m%n"); - appender->setLayout(layout); - root.addAppender(appender); - } - } - - LOG_INFO(__func__) << "========================================"; - LOG_INFO(__func__) << "Log initialized successfully"; - if (*config_path) - LOG_INFO(__func__) << "Loaded " << config_path; - -#ifndef _CONSOLE - LOG_INFO(__func__) << "Length of command line: " << _tcslen(lpCmdLine); -#endif - - LOG_INFO(__func__) << "Version: " << VERSION_STRING; - LOG_INFO(__func__) << "Build : " << __DATE__ << ' ' << __TIME__; -#if defined(_MSC_FULL_VER) - LOG_INFO(__func__) << "_MSC_FULL_VER: " - << _MSC_FULL_VER / 10000000 << '.' - << (_MSC_FULL_VER % 10000000) / 100000 << '.' - << _MSC_FULL_VER % 100000; -#elif defined(_MSC_VER) - LOG_INFO(__func__) << "_MSC_VER: " - << _MSC_VER / 100 << '.' - << _MSC_VER % 100; -#endif - + LogMain(SERVICE_NAME); #ifdef _CONSOLE if (argc > 1) diff --git a/README.md b/README.md index de9a200..32889b8 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,38 @@ [![MSBuild](https://github.com/Anton-V-K/ClassicParentalControl/actions/workflows/msbuild.yml/badge.svg)](actions/workflows/msbuild.yml) + # Classic Parental Control (for Windows) The project provides utility which allows you to establish/maintain Parental Control for Local Accounts in Windows 10. -- `LogonHoursService` monitors allowed logon hours (which you can set with a command like `net user USERNAME /time:M-F,10-18`) and locks the session once the time is over + +- `LogonHoursManager` is a GUI application to easily change to change the allowed logon hours +- `LogonHoursService` it a service which monitors the allowed logon hours and locks the session once the time is over + +The allowed logon hours are the ones you can set with a command like `net user USERNAME /time:M-F,10-18`. ## Compilation -You need VS2019 with vc142 toolkit to build the solution. -The project can also be built with older versions of VS after tuning the properties: +You need VS2019 with v142 toolkit to build the solution. + +The project can also be built with other versions of VisualStuido after tuning the properties: 1. Copy `_props\user\_Platform.props.IN` to `_props\user\_Platform.props` + 2. Adjust `PlatformToolset` to specify available/desired toolset: - ``` - - v141 - - ``` + + ``` + + v141 + + ``` ## Installation + +### Manager + +The application doesn't require any special installation. + +### Service + 1. Copy all binaries from the archive into a directory with read-only access to Everyone, so only Administrators can remove or update them (if needed). 2. Run `LogonHoursService` from command line (with any permissions) for testing purposes, close its console window after ~5 seconds and examine the log in `%TEMP%\LogonHoursService.log`. The log should start with something like this: `2021.11.28 02:31:03,322 [INFO] [2672] wmain: ========================================` @@ -50,6 +65,12 @@ After local dumps are enabled, you will be able to find the dump under `%LOCALAP If you're running the service under `Local System` account, its dumps are generated under `%windir%\System32\config\systemprofile\AppData\Local\CrashDumps` or `%windir%\SysWOW64\config\systemprofile\AppData\Local\CrashDumps` (for 32-bit services on 64-bit system) +### Missing `clang_rt.asan_dbg_dynamic-i386.dll` in Debug configuration + +Both projects have now Address Sanitizer enabled in Debug configuration (refer to `true` in `\_props\Cxx.props`), so when running them not from VisualStudio IDE the system may complain about missing `clang_rt.asan_dbg_dynamic-i386.dll` (for Win32 platform). + +It is enough to copy the required DLL from VisualStudio platform toolset directory (something like `C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\Hostx86\x86`) to the directory with the executable. + ## Supported Windows versions This project is intended to be useful in Windows 10, where you cannot easily establish parental control for Local Accounts. @@ -61,6 +82,10 @@ Though the built-in classic parental control is available in Windows 7/8.1, you ## History +### 1.1.0 Alpha (10.05.2023) + +- [x] `LogonHoursManager` is available + ### 1.0.3 Alpha (28.01.2022) - [x] `DailyRollingFileAppender` is used instead of `RollingFileAppender` diff --git a/_props/Cxx.props b/_props/Cxx.props index e814c35..b4d472b 100644 --- a/_props/Cxx.props +++ b/_props/Cxx.props @@ -1,12 +1,17 @@ + Unicode + + true + + $(MSBuildThisFileDirectory).. @@ -24,6 +29,7 @@ + ProgramDatabase diff --git a/_props/Linker.props b/_props/Linker.props index c6d7acb..129cff4 100644 --- a/_props/Linker.props +++ b/_props/Linker.props @@ -1,15 +1,32 @@ - + + Windows + + /ignore:4099 %(AdditionalOptions) + + + + + MachineX86 + - Windows MachineX64 + + + false + + + + false + + diff --git a/_props/RC.props b/_props/RC.props new file mode 100644 index 0000000..22169f6 --- /dev/null +++ b/_props/RC.props @@ -0,0 +1,10 @@ + + + + + + $(MSBuildThisFileDirectory).. + + + + diff --git a/_props/TARGET_EXE.props b/_props/TARGET_EXE.props index 1ce2aa4..84aeaca 100644 --- a/_props/TARGET_EXE.props +++ b/_props/TARGET_EXE.props @@ -4,4 +4,5 @@ +