diff --git a/PROGMGR2/dialog.c b/PROGMGR2/dialog.c new file mode 100644 index 0000000..2b3752c --- /dev/null +++ b/PROGMGR2/dialog.c @@ -0,0 +1,492 @@ +/* * * * * * * *\ + DIALOG.C - + Copyright (c) 2024 Vortesys, Brady McDermott + DESCRIPTION - + Program Manager's dialogs and related + common functions. + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Headers */ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include "progmgr.h" +#include "dialog.h" +#include "group.h" +#include "resource.h" + +/* Variables */ +WCHAR szDlgTitle[64]; + +/* Functions */ + +/* * * *\ + NewGroupDlgProc - + Dialog procedure for creating a group. + RETURNS - + TRUE if message is handled, FALSE otherwise. +\* * * */ +BOOL CALLBACK NewGroupDlgProc(HWND hWndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static GROUP grp = { 0 }; + BOOL bOKEnabled = FALSE; + WCHAR szBuffer[MAX_TITLE_LENGTH] = { TEXT("\0") }; + HICON hIconDef = NULL; + HICON hIconDlg = NULL; + + switch (message) + { + + case WM_INITDIALOG: + // TODO: + // Enable creation of common groups (enable the controls) + // if permissions are available. + // TODO: + // fix minor GDI font/region leak + + // Reset the necessary parts of the group structure + grp.dwSignature = GRP_SIGNATURE; + grp.wVersion = GRP_VERSION; + grp.wChecksum = 0; + grp.dwFlags = 0; + grp.cItemArray = 0; + // grp.pItemArray = 0; TODO: idk make sure this ain't all screwed up + + // Set the window title + LoadString(g_hAppInstance, IDS_DLT_GRP_NEW, szDlgTitle, ARRAYSIZE(szDlgTitle)); + SetWindowText(hWndDlg, szDlgTitle); + + // Populate the icon with the default path and index. + GetModuleFileName(g_hAppInstance, (LPWSTR)&grp.szIconPath, ARRAYSIZE(grp.szIconPath)); + grp.iIconIndex = IDI_PROGGRP - 1; + + // Get the default hIcon so we can delete it later + hIconDef = (HICON)SendDlgItemMessage(hWndDlg, IDD_STAT_ICON, STM_GETICON, 0, 0); + + // Set the icon in the dialog + hIconDlg = ExtractIcon(g_hAppInstance, (LPWSTR)&grp.szIconPath, grp.iIconIndex); + SendDlgItemMessage(hWndDlg, IDD_STAT_ICON, STM_SETICON, (WPARAM)hIconDlg, 0); + + // Set the maximum input length of the text boxes + SendDlgItemMessage(hWndDlg, IDD_NAME, EM_LIMITTEXT, MAX_TITLE_LENGTH, 0); + + // Disable the OK button since we're starting with no text in the box. + EnableWindow(GetDlgItem(hWndDlg, IDD_OK), bOKEnabled); + + // Enable the common group checkbox if perms are good + EnableWindow(GetDlgItem(hWndDlg, IDD_COMMGROUP), g_bPermAdmin); + + break; + + case WM_COMMAND: + + if (HIWORD(wParam) == EN_CHANGE) + { + if (LOWORD(wParam) == IDD_NAME) + { + // Name text control changed. See what's up... + bOKEnabled = GetDlgItemText(hWndDlg, IDD_NAME, (LPWSTR)&szBuffer, ARRAYSIZE(szBuffer)); + + // Enable or disable the OK button based on the information + EnableWindow(GetDlgItem(hWndDlg, IDD_OK), bOKEnabled); + } + } + + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + + case IDD_CHICON: + if (PickIconDlg(hWndDlg, (LPWSTR)&grp.szIconPath, ARRAYSIZE(grp.szIconPath), &grp.iIconIndex) == TRUE) + { + // Since we've got the new icon... + hIconDlg = ExtractIcon(g_hAppInstance, (LPWSTR)&grp.szIconPath, grp.iIconIndex); + SendDlgItemMessage(hWndDlg, IDD_STAT_ICON, STM_SETICON, (WPARAM)hIconDlg, 0); + } + + break; + + case IDD_OK: + // Check that all the applicable fields are filled out, + // and if not then set the focus to the offending field + if (!(bOKEnabled = GetDlgItemText(hWndDlg, IDD_NAME, (LPWSTR)&szBuffer, ARRAYSIZE(szBuffer)))) + SetFocus(GetDlgItem(hWndDlg, IDD_NAME)); + + // Enable or disable the OK button based on the information + EnableWindow(GetDlgItem(hWndDlg, IDD_OK), bOKEnabled); + + if (bOKEnabled) + { + // Set the name of the group + StringCchCopy(grp.szName, ARRAYSIZE(szBuffer), szBuffer); + + // Set the flags of the group + if (SendDlgItemMessage(hWndDlg, IDD_COMMGROUP, BM_GETCHECK, 0, 0) != BST_UNCHECKED) + grp.dwFlags = grp.dwFlags || GRP_FLAG_COMMON; + + // Set FILETIME + GetSystemTimeAsFileTime(&grp.ftLastWrite); + + // Set the rectangle of the group to be CW_USEDEFAULT + grp.rcGroup.left = grp.rcGroup.top = grp.rcGroup.right = grp.rcGroup.bottom = CW_USEDEFAULT; + + // Group's ready! + if (CreateGroup(&grp) != NULL) + { + EndDialog(hWndDlg, FALSE); + break; + } + + // Failure! + EndDialog(hWndDlg, FALSE); + break; + } + + break; + + case IDD_CANCEL: + EndDialog(hWndDlg, FALSE); + break; + + default: + break; + } + + default: + // Cleanup + if (hIconDef) + DestroyIcon(hIconDef); + if (hIconDlg) + DestroyIcon(hIconDlg); + + return FALSE; + } + + return TRUE; +} + +/* * * *\ + NewItemDlgProc - + Dialog procedure for creating or modifying + an item. + RETURNS - + TRUE if message is handled, FALSE otherwise. +\* * * */ +BOOL CALLBACK NewItemDlgProc(HWND hWndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static ITEM itm = { 0 }; + BOOL bOKEnabled = FALSE; + BOOL bWorkPath = FALSE; + WCHAR szBuffer[MAX_TITLE_LENGTH] = { TEXT("\0") }; + WCHAR szPathBuffer[MAX_PATH] = { TEXT("\0") }; + HICON hIconDef = NULL; + HICON hIconDlg = NULL; + + switch (message) + { + + case WM_INITDIALOG: + // TODO: + // require a valid group to be selected or else the dialog won't + // EVEN BE AVAILABLE!!! or let you press ok. + + // Reset the necessary parts of the item structure + itm.dwFlags = 0; + itm.uiHotkeyModifiers = 0; + itm.uiHotkeyVirtualKey = 0; + + // Set the window title + LoadString(g_hAppInstance, IDS_DLT_ITEM_NEW, szDlgTitle, ARRAYSIZE(szDlgTitle)); + SetWindowText(hWndDlg, szDlgTitle); + + // Populate the icon with the default path and index. + GetModuleFileName(NULL, (LPWSTR)&itm.szIconPath, MAX_PATH); + itm.iIconIndex = IDI_PROGITM - 1; + + // Get the default hIcon so we can delete it later + hIconDef = (HICON)SendDlgItemMessage(hWndDlg, IDD_STAT_ICON, STM_GETICON, 0, 0); + + // Set the icon in the dialog + hIconDlg = ExtractIcon(g_hAppInstance, (LPWSTR)&itm.szIconPath, itm.iIconIndex); + SendDlgItemMessage(hWndDlg, IDD_STAT_ICON, STM_SETICON, (WPARAM)hIconDlg, 0); + + // Set the maximum input length of the text boxes + SendDlgItemMessage(hWndDlg, IDD_NAME, EM_LIMITTEXT, MAX_TITLE_LENGTH, 0); + SendDlgItemMessage(hWndDlg, IDD_PATH, EM_LIMITTEXT, MAX_PATH, 0); + SendDlgItemMessage(hWndDlg, IDD_WORKPATH, EM_LIMITTEXT, MAX_PATH, 0); + + // Disable the OK button since we're starting with no text in the box. + EnableWindow(GetDlgItem(hWndDlg, IDD_OK), bOKEnabled); + + break; + + case WM_COMMAND: + if (HIWORD(wParam) == EN_CHANGE) + { + if ((LOWORD(wParam) == IDD_NAME) || (LOWORD(wParam) == IDD_PATH) || (LOWORD(wParam) == IDD_WORKPATH)) + { + // A control has changed. See what's up... + bOKEnabled = GetDlgItemText(hWndDlg, IDD_NAME, (LPWSTR)&szBuffer, ARRAYSIZE(szBuffer)); + bOKEnabled = bOKEnabled && GetDlgItemText(hWndDlg, IDD_PATH, (LPWSTR)&szPathBuffer, ARRAYSIZE(szPathBuffer)); + + if (bWorkPath) + bOKEnabled = bOKEnabled && GetDlgItemText(hWndDlg, IDD_WORKPATH, (LPWSTR)&szPathBuffer, ARRAYSIZE(szPathBuffer)); + + // Check that we have selected an MDI child window as well + if ((HWND)SendMessage(hWndMDIClient, WM_MDIGETACTIVE, 0, FALSE) == (HWND)NULL) + bOKEnabled = FALSE; + + // Enable or disable the relevant controls based on the information + EnableWindow(GetDlgItem(hWndDlg, IDD_OK), bOKEnabled); + } + } + + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + + case IDD_BROWSE: + { + OPENFILENAME ofn; + LPVOID lpData; + DWORD dwVerBuffer = 0; + UINT uiTitleLen = MAX_TITLE_LENGTH; + WCHAR szNameBuffer[MAX_TITLE_LENGTH] = { TEXT("\0") }; + WCHAR szFileBuffer[MAX_PATH] = { TEXT("\0") }; + + GetDlgItemText(hWndDlg, IDD_PATH, (LPWSTR)&szFileBuffer, ARRAYSIZE(szFileBuffer)); + + // Initialize the structure + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hWndDlg; + ofn.lpstrFilter = TEXT("Programs\0*.exe;*.bat;*.com;*.cmd;*.lnk\0All Files (*.*)\0*.*\0"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = (LPWSTR)&szFileBuffer; + // ofn.lpstrFile[0] = '\0'; + ofn.nMaxFile = ARRAYSIZE(szFileBuffer); + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.Flags = OFN_FILEMUSTEXIST; + + if (GetOpenFileName(&ofn) == TRUE) + SetDlgItemText(hWndDlg, IDD_PATH, (LPWSTR)&szFileBuffer); + else + break; + + // copy the filename to a new buffer for later + StringCchCopy(szNameBuffer, ARRAYSIZE(szNameBuffer), szFileBuffer); + + // let's retrieve the application's friendly name too + dwVerBuffer = GetFileVersionInfoSize((LPCWSTR)szFileBuffer, NULL); + // if (dwVerBuffer != 0) + if (NULL) + { + lpData = malloc(dwVerBuffer); + // TODO: totally redo this it doesn't work + + if (lpData != NULL) + GetFileVersionInfo((LPCWSTR)szFileBuffer, (DWORD)0, dwVerBuffer, lpData); + else + break; + + // TODO: this is hardcoded to english. make it dynamic later + // TODO: this is so busted lol, really make it dynamic so it works + VerQueryValue(lpData, + TEXT("\\StringFileInfo\\040904b0\\FileDescription"), + (LPVOID)&szNameBuffer, &uiTitleLen); // == 0 + + if (lpData) + free(lpData); + } + else + { + PathStripPath((LPWSTR)&szNameBuffer); + PathRemoveExtension((LPWSTR)&szNameBuffer); + } + + SetDlgItemText(hWndDlg, IDD_NAME, (LPWSTR)szNameBuffer); + + // let's get the icon now + if (ExtractIcon(g_hAppInstance, (LPWSTR)szFileBuffer, -1) > 0) + { + // file contains at least one icon, set the itm struct values + StringCchCopy(itm.szIconPath, ARRAYSIZE(itm.szIconPath), szFileBuffer); + itm.iIconIndex = 0; + + // update icon + hIconDlg = ExtractIcon(g_hAppInstance, (LPWSTR)itm.szIconPath, itm.iIconIndex); + SendDlgItemMessage(hWndDlg, IDD_STAT_ICON, STM_SETICON, (WPARAM)hIconDlg, 0); + } + + break; + } + + case IDD_BROWSE2: + { + OPENFILENAME ofn; + + GetDlgItemText(hWndDlg, IDD_WORKPATH, (LPWSTR)&szPathBuffer, ARRAYSIZE(szPathBuffer)); + + // Initialize the structure + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hWndDlg; + ofn.lpstrFilter = TEXT("Folders\0"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = (LPWSTR)&szPathBuffer; + // ofn.lpstrFile[0] = '\0'; + ofn.nMaxFile = ARRAYSIZE(szPathBuffer); + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.Flags = OFN_FILEMUSTEXIST; + + if (GetOpenFileName(&ofn) == TRUE) { + SetDlgItemText(hWndDlg, IDD_WORKPATH, (LPWSTR)&szPathBuffer); + } + + break; + } + + + case IDD_CHICON: + if (PickIconDlg(hWndDlg, (LPWSTR)&itm.szIconPath, ARRAYSIZE(itm.szIconPath), &itm.iIconIndex) == TRUE) + { + // Since we've got the new icon... + hIconDlg = ExtractIcon(g_hAppInstance, (LPWSTR)itm.szIconPath, itm.iIconIndex); + SendDlgItemMessage(hWndDlg, IDD_STAT_ICON, STM_SETICON, (WPARAM)hIconDlg, 0); + } + + break; + + case IDD_WORKDIR: + bWorkPath = IsDlgButtonChecked(hWndDlg, IDD_WORKDIR); + + if (bWorkPath) + { + GetDlgItemText(hWndDlg, IDD_PATH, (LPWSTR)&szPathBuffer, ARRAYSIZE(szPathBuffer)); + PathRemoveFileSpec((LPWSTR)&szPathBuffer); + SetDlgItemText(hWndDlg, IDD_WORKPATH, (LPWSTR)&szPathBuffer); + } + else + { + SetWindowText(GetDlgItem(hWndDlg, IDD_WORKPATH), 0); + } + + EnableWindow(GetDlgItem(hWndDlg, IDD_STAT_WORKDIR), bWorkPath); + EnableWindow(GetDlgItem(hWndDlg, IDD_WORKPATH), bWorkPath); + EnableWindow(GetDlgItem(hWndDlg, IDD_BROWSE2), bWorkPath); + + break; + + case IDD_OK: + // Check that all the applicable fields are filled out, + // and if not then set the focus to the offending field + if (!(bOKEnabled = GetDlgItemText(hWndDlg, IDD_NAME, (LPWSTR)&szBuffer, ARRAYSIZE(szBuffer)))) + SetFocus(GetDlgItem(hWndDlg, IDD_NAME)); + if (!(bOKEnabled = GetDlgItemText(hWndDlg, IDD_PATH, (LPWSTR)&szPathBuffer, ARRAYSIZE(szPathBuffer)))) + SetFocus(GetDlgItem(hWndDlg, IDD_PATH)); + if (bWorkPath) + { + if (!(bOKEnabled = GetDlgItemText(hWndDlg, IDD_WORKPATH, (LPWSTR)&szPathBuffer, ARRAYSIZE(szPathBuffer)))) + SetFocus(GetDlgItem(hWndDlg, IDD_WORKPATH)); + } + + // Enable or disable the OK button based on the information + EnableWindow(GetDlgItem(hWndDlg, IDD_OK), bOKEnabled); + + if (bOKEnabled) + { + // Set the name of the item + StringCchCopy(itm.szName, ARRAYSIZE(szBuffer), szBuffer); + + // And the paths... + GetDlgItemText(hWndDlg, IDD_PATH, (LPWSTR)&szPathBuffer, ARRAYSIZE(szPathBuffer)); + StringCchCopy(itm.szExecPath, ARRAYSIZE(szPathBuffer), szPathBuffer); + GetDlgItemText(hWndDlg, IDD_WORKPATH, (LPWSTR)&szPathBuffer, ARRAYSIZE(szPathBuffer)); + StringCchCopy(itm.szWorkPath, ARRAYSIZE(szPathBuffer), szPathBuffer); + + // Item's ready! + if (CreateItem((HWND)SendMessage(hWndMDIClient, WM_MDIGETACTIVE, 0, 0), &itm) != NULL) + { + EndDialog(hWndDlg, FALSE); + break; + } + + // Failure! + EndDialog(hWndDlg, FALSE); + break; + } + + break; + + case IDD_CANCEL: + EndDialog(hWndDlg, FALSE); + break; + + default: + break; + } + + default: + // Cleanup + if (hIconDef) + DestroyIcon(hIconDef); + if (hIconDlg) + DestroyIcon(hIconDlg); + + return FALSE; + } + + return TRUE; +} + +/* * * *\ + ShutdownDlgProc - + Dialog procedure for powering off the computer. + RETURNS - + TRUE if message is handled, FALSE otherwise. +\* * * */ +BOOL CALLBACK ShutdownDlgProc(HWND hWndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + HICON hIconDlg = NULL; + + switch (message) + { + + case WM_INITDIALOG: + break; + + case WM_COMMAND: + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + + case IDD_OK: + { + } + + case IDD_CANCEL: + EndDialog(hWndDlg, FALSE); + break; + + default: + break; + } + break; + + default: + // Cleanup + if (hIconDlg) + DestroyIcon(hIconDlg); + + return FALSE; + } + return TRUE; +} diff --git a/PROGMGR2/dialog.h b/PROGMGR2/dialog.h new file mode 100644 index 0000000..cc3a7a3 --- /dev/null +++ b/PROGMGR2/dialog.h @@ -0,0 +1,17 @@ +/* * * * * * * *\ + DIALOG.H - + Copyright (c) 2024 Vortesys, Brady McDermott + DESCRIPTION - + Dialog function prototypes and related + common dialog functions. + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Pragmas */ +#pragma once + +/* Function Prototypes */ +BOOL CALLBACK NewGroupDlgProc(HWND hWndDlg, UINT message, WPARAM wParam, LPARAM lParam); +BOOL CALLBACK NewItemDlgProc(HWND hWndDlg, UINT message, WPARAM wParam, LPARAM lParam); +BOOL CALLBACK ShutdownDlgProc(HWND hWndDlg, UINT message, WPARAM wParam, LPARAM lParam); diff --git a/PROGMGR2/group.c b/PROGMGR2/group.c new file mode 100644 index 0000000..3edf722 --- /dev/null +++ b/PROGMGR2/group.c @@ -0,0 +1,677 @@ +/* * * * * * * *\ + GROUP.C - + Copyright (c) 2024 Vortesys, Brady McDermott + DESCRIPTION - + Group window and program group/item functions. + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Headers */ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include "progmgr.h" +#include "group.h" +#include "resource.h" +#include "registry.h" + +/* Variables */ +WNDCLASSEX wcGrp; +WCHAR szGrpClass[16]; +HWND hWndMDIClient = NULL; +HWND hWndFoundGroup = NULL; + +/* Functions */ + +/* * * *\ + InitializeGroups - + Create the MDI Client window and initialize + the group window class. + RETURNS - + TRUE if groups are successfully created. + FALSE if otherwise. +\* * * */ +BOOL InitializeGroups(VOID) +{ + CLIENTCREATESTRUCT ccs = { 0 }; + WNDCLASSEX wce = { 0 }; + RECT rcFrame; + + // Create the MDI Client Window + ccs.hWindowMenu = GetSubMenu(GetMenu(g_hWndProgMgr), 2); + ccs.idFirstChild = IDM_WINDOW_CHILDSTART; + + GetClientRect(g_hWndProgMgr, &rcFrame); + + if ((hWndMDIClient = CreateWindowEx(WS_EX_COMPOSITED, + TEXT("MDIClient"), NULL, WS_CLIPCHILDREN | WS_CHILD | + WS_VSCROLL | WS_HSCROLL | WS_VISIBLE, + rcFrame.left, rcFrame.top, rcFrame.right, rcFrame.bottom, + g_hWndProgMgr, (HMENU)1, g_hAppInstance, (LPWSTR)&ccs)) == NULL) + { + return FALSE; + } + + // Load the group class string + LoadString(g_hAppInstance, IDS_GRPCLASS, + szGrpClass, ARRAYSIZE(szGrpClass)); + + // Register the group window class + wce.cbSize = sizeof(WNDCLASSEX); + wce.style = CS_DBLCLKS; + wce.lpfnWndProc = GroupWndProc; + wce.cbClsExtra = 0; + wce.cbWndExtra = 0; + wce.hInstance = g_hAppInstance; + wce.hIcon = g_hGroupIcon = LoadImage(g_hAppInstance, + MAKEINTRESOURCE(IDI_PROGGRP), IMAGE_ICON, + 0, 0, LR_DEFAULTSIZE | LR_SHARED); + wce.hCursor = LoadCursor(NULL, IDC_ARROW); + wce.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wce.lpszMenuName = NULL; + wce.lpszClassName = szGrpClass; + + if (!RegisterClassEx(&wce)) + return FALSE; + + // Now we can create the groups + if(LoadConfig(FALSE, FALSE, TRUE) != RCE_SUCCESS) + return FALSE; + + // TODO: if loading groups fails, throw an error + + return TRUE; +} + +/* * * *\ + CreateGroup - + Create an MDI window from a group structure + RETURNS - + Handle to the new group window + or NULL on failure +\* * * */ +HWND CreateGroup(_In_ PGROUP pg) +{ + MDICREATESTRUCT mcs = { NULL }; + PGROUP pGroup = NULL; + HICON hIconLarge = NULL; + HICON hIconSmall = NULL; + HICON hIconTemp = NULL; + HWND hWndGroup = NULL; + HIMAGELIST hImageList = NULL; + HWND hWndListView = NULL; + RECT rcGroupWindow = { 0 }; + LVCOLUMN lvc = { 0 }; + + if (pg == NULL) + return NULL; + + // error checking, if we get trash don't try to + // make a group out of it + // TODO: if a group is garbled, throw an error + // and ask the user if they want to delete it! + if (pg->dwSignature != GRP_SIGNATURE) + return NULL; + + // allocate memory for a new group + pGroup = (PGROUP)malloc(CalculateGroupMemory(pg, 1, 0)); + if (pGroup == NULL) + return NULL; + + // clean up the memory if it's valid + ZeroMemory(pGroup, sizeof(*pGroup)); + + // copy over the group structure + *pGroup = *pg; + + // TODO: get group minimized/maximized flags + + mcs.szClass = szGrpClass; + mcs.szTitle = pg->szName; + mcs.hOwner = g_hAppInstance; + if ((pg->rcGroup.left == CW_USEDEFAULT) & (pg->rcGroup.right == CW_USEDEFAULT)) + { + mcs.x = mcs.y = mcs.cx = mcs.cy = CW_USEDEFAULT; + } + mcs.style = WS_VISIBLE | WS_THICKFRAME | WS_CAPTION | WS_BORDER | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX; + // TODO: should I pass the pointer to the group through here + // or is it better and easier to just do it with GWLP_USERDATA? + mcs.lParam = (LPARAM)NULL; + + if ((hWndGroup = (HWND)SendMessage(hWndMDIClient, WM_MDICREATE, 0, + (LPARAM)(LPTSTR)&mcs)) == NULL) + return NULL; + + // Associate the group structure pointer to the group window + SetWindowLongPtr(hWndGroup, GWLP_USERDATA, (LONG_PTR)pGroup); + + // Resize the group + if (pg->wp.length == sizeof(WINDOWPLACEMENT)) + SetWindowPlacement(hWndGroup, &pg->wp); + + // Load the group icon + if (ExtractIconEx(pg->szIconPath, pg->iIconIndex, &hIconLarge, &hIconSmall, 1)) + { + if (hIconTemp = (HICON)SendMessage(hWndGroup, WM_SETICON, + ICON_SMALL, (LPARAM)hIconSmall)) + DestroyIcon(hIconTemp); + if (hIconTemp = (HICON)SendMessage(hWndGroup, WM_SETICON, + ICON_BIG, (LPARAM)hIconLarge)) + DestroyIcon(hIconTemp); + } + + // Get the group window rect + GetClientRect(hWndGroup, &rcGroupWindow); + + // Create the group window ListView control + if ((hWndListView = CreateWindowEx(WS_EX_LEFT, WC_LISTVIEW, + TEXT("ListView"), + WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | + LVS_ICON | LVS_SINGLESEL | LVS_AUTOARRANGE, + mcs.x, mcs.y, mcs.cx, mcs.cy, + hWndGroup, NULL, g_hAppInstance, NULL)) == NULL) + return NULL; + + // ((LVS_AUTOARRANGE & bAutoArrange) * LVS_AUTOARRANGE) + + // Resize it to fit the window + SetWindowPos(hWndListView, NULL, + rcGroupWindow.left, rcGroupWindow.top, + rcGroupWindow.right - rcGroupWindow.left, + rcGroupWindow.bottom - rcGroupWindow.top, SWP_NOZORDER); + + // Add the explorer style because it looks good + SetWindowTheme(hWndListView, TEXT("Explorer"), NULL); + + // Get this bad boy an image list + // TODO: if cxicon/cyicon change make sure we handle that + // don't require a program restart, DPI!!! + hImageList = ImageList_Create(GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON), ILC_COLOR32, 0, 1); + ListView_SetImageList(hWndListView, hImageList, LVSIL_NORMAL); + + // since the list is viewing we can load items if applicable + if (pGroup->cItemArray > 0) + LoadItems(hWndGroup); + + // TODO: make sure the groups delete their icons upon destruction! + // AND IMAGE LIST!!!!!!!!? i think it's nuked w/ listview though + return hWndGroup; +} + +/* * * *\ + RemoveGroup - + Removes and cleans up a group window + RETURNS - + TRUE if cleanup is successful, + FALSE otherwise. +\* * * */ +BOOL RemoveGroup(_In_ HWND hWndGroup, _In_ BOOL bEliminate) +{ + PGROUP pGroup = NULL; + BOOL bReturn = TRUE; + + // TODO: do i have to delete the titlebar icons? + + if (bEliminate == TRUE) + { + // TODO: remove group from registry + // if successful blah blah blah + bEliminate = TRUE; + } + + // get the group struct pointer + if ((pGroup = (PGROUP)GetWindowLongPtr(hWndGroup, GWLP_USERDATA)) == NULL) + bReturn = FALSE; + else + free(pGroup); + + // close the group window + SendMessage(hWndMDIClient, WM_MDIDESTROY, (WPARAM)hWndGroup, 0); + + return bReturn; +} + +/* * * *\ + CreateItem - + Creates a new program item + RETURNS - + Pointer to the item if successful + NULL otherwise +\* * * */ +PITEM CreateItem(_In_ HWND hWndGroup, _In_ PITEM pi) +{ + PGROUP pGroup = NULL; + PGROUP pNewGroup = NULL; + PITEM pItem = NULL; + HWND hWndListView = NULL; + HIMAGELIST hImageList = NULL; + HICON hIcon = NULL; + LVITEM lvi = { 0 }; + + // we actually just want the group pointer lol + pGroup = (PGROUP)GetWindowLongPtr(hWndGroup, GWLP_USERDATA); + + // return NULL if we can't get to the group or item + if (hWndGroup == NULL) + return NULL; + if (pGroup == NULL) + return NULL; + if (pi == NULL) + return NULL; + + // get the listview window + hWndListView = FindWindowEx(hWndGroup, NULL, WC_LISTVIEW, NULL); + if (hWndListView == NULL) + return NULL; + + // if we reallocate memory then send the new pointer in + pNewGroup = realloc(pGroup, CalculateGroupMemory(pGroup, 1, 0)); + if (pNewGroup != NULL) + { + pGroup = pNewGroup; + SetWindowLongPtr(hWndGroup, GWLP_USERDATA, (LONG_PTR)pGroup); + } + + // add the item + pItem = pGroup->pItemArray + pGroup->cItemArray; + *pItem = *pi; + + // increment the item counter + pGroup->cItemArray++; + + // then get the pointer to the group's image list + hImageList = ListView_GetImageList(hWndListView, LVSIL_NORMAL); + + // extract that icon son!! + hIcon = ExtractIcon(g_hAppInstance, (LPWSTR)pItem->szIconPath, pItem->iIconIndex); + + // populate the listview with the relevant information + lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; + lvi.iItem = pGroup->cItemArray; + lvi.iSubItem = 0; + lvi.pszText = pItem->szName; + lvi.cchTextMax = ARRAYSIZE(pItem->szName); + lvi.iImage = ImageList_AddIcon(hImageList, hIcon); + lvi.lParam = (LPARAM)pItem; + + // copy that bad boy into the listview + ListView_InsertItem(hWndListView, &lvi); + + // get that hicon outta here + DestroyIcon(hIcon); + + // this is all terrible i'm gonna rewrite this whole file + // why is the listview even touched here just... omg... + // BAD!! bad functions + + // TODO: fail if the listview item isn't added + return pItem; +} + +/* * * *\ + LoadItems - + Loads all of the items in a group structure + RETURNS - + TRUE if successful + FALSE otherwise +\* * * */ +BOOL LoadItems(_In_ HWND hWndGroup) +{ + PGROUP pGroup = NULL; + PGROUP pNewGroup = NULL; + PITEM pItem = NULL; + HWND hWndListView = NULL; + HIMAGELIST hImageList = NULL; + LVITEM lvi = { 0 }; + UINT cItemIndex = 0; + HICON hIcon = NULL; + + // return NULL if we can't get to the group + if (hWndGroup == NULL) + return FALSE; + + // retrieve the group pointer + pGroup = (PGROUP)GetWindowLongPtr(hWndGroup, GWLP_USERDATA); + if (pGroup == NULL) + return FALSE; + + // get the listview window + hWndListView = FindWindowEx(hWndGroup, NULL, WC_LISTVIEW, NULL); + if (hWndListView == NULL) + return FALSE; + + // make sure we have enough memory for the items + pNewGroup = realloc(pGroup, CalculateGroupMemory(pGroup, 0, 1)); + if (pNewGroup != NULL) + { + pGroup = pNewGroup; + SetWindowLongPtr(hWndGroup, GWLP_USERDATA, (LONG_PTR)pGroup); + } + + // then get the pointer to the group's image list + hImageList = ListView_GetImageList(hWndListView, LVSIL_NORMAL); + + while (pGroup->cItemArray > cItemIndex) + { + pItem = pGroup->pItemArray + cItemIndex; + + // extract that icon son!! + hIcon = ExtractIcon(g_hAppInstance, (LPWSTR)pItem->szIconPath, pItem->iIconIndex); + + // populate the listview with the relevant information + lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; + lvi.iItem = cItemIndex; + lvi.iSubItem = 0; + lvi.pszText = pItem->szName; + lvi.cchTextMax = ARRAYSIZE(pItem->szName); + lvi.iImage = ImageList_AddIcon(hImageList, hIcon); + lvi.lParam = (LPARAM)pItem; + + // copy that bad boy into the listview + ListView_InsertItem(hWndListView, &lvi); + + cItemIndex++; + } + + if (hIcon) + // get that hicon outta here + DestroyIcon(hIcon); + + // TODO: fail if the listview item isn't added + return TRUE; +} + +/* * * *\ + RemoveItem - + Removes a program item + RETURNS - + TRUE if item was successfully removed + FALSE otherwise +\* * * */ +BOOL RemoveItem(_In_ PITEM pi) +{ + // TODO: implement this + // return NULL if we can't get to the group + if (pi == NULL) + return FALSE; + + return FALSE; +} + +/* * * *\ + ExecuteItem - + Runs a program item + RETURNS - + TRUE if item was successfully ran + FALSE otherwise +\* * * */ +BOOL ExecuteItem(_In_ PITEM pi) +{ + // TODO: nCmdShow from program item flags + ShellExecute(g_hWndProgMgr, TEXT("open"), + pi->szExecPath, NULL, + (pi->szWorkPath != TEXT("")) ? pi->szWorkPath : NULL, + SW_NORMAL); + + // TODO: if item has run as admin as a flag, + // run it as an admin + + // TODO: make sure environment is + // refreshed before execution + return FALSE; +} + +/* * * *\ + UpdateGroup - + Updates group information to prepare + it for being saved. + ABSTRACT - + This function will update all of the + relevant structures in a group struct + in preparation for saving the group. + RETURNS - + Nothing. +\* * * */ +VOID UpdateGroup(_In_ PGROUP pg) +{ + DWORD dwFlags = 0; + HWND hWndGroup = NULL; + + // Set the important flags + pg->dwSignature = GRP_SIGNATURE; + pg->wVersion = GRP_VERSION; + + // Set the group checksum + pg->wChecksum = 1; // NOTE: implement this for real later lol + + // TODO: set name and group flags + // pg->dwFlags = GRP_FLAG_MAXIMIZED;// GetGroupFlags(pgw); + + // Set FILETIME + GetSystemTimeAsFileTime(&pg->ftLastWrite); + + if (hWndGroup = GetHwndFromPGroup(pg)) + { + // Get the group window rect and name + pg->wp.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(hWndGroup, &pg->wp); + GetWindowText(hWndGroup, pg->szName, ARRAYSIZE(pg->szName)); + } + + return; +} + +/* * * *\ + GetHwndFromPGroup - + In goes a PGROUP out comes a HWND + RETURNS - + HWND. +\* * * */ +HWND GetHwndFromPGroup(_In_ PGROUP pg) +{ + EnumChildWindows(g_hWndProgMgr, (WNDENUMPROC)GetHwndFromPGroupEnum, (LPARAM)pg); + + return hWndFoundGroup; +} + +/* * * *\ + GetHwndFromPGroupEnum - + Enum Function + RETURNS - + HWND. +\* * * */ +BOOL GetHwndFromPGroupEnum(_In_ HWND hwnd, _In_ LPARAM lParam) +{ + if (lParam == GetWindowLongPtr(hwnd, GWLP_USERDATA)) + { + hWndFoundGroup = hwnd; + return FALSE; + } + else + { + hWndFoundGroup = NULL; + return TRUE; + } +} + +/* * * *\ + VerifyGroup - + Verifies that a group contains the + correct information about itself. + ABSTRACT - + This function will check the checksum, + verify the number of items, check other + parts of the strucutre. If requested, + it will repair the group if it is damaged. + RETURNS - + TRUE if group appears to be fine + FALSE if the group is damaged +\* * * */ +BOOL VerifyGroup(_In_ PGROUP pg, _In_ BOOL bRepair) +{ + if (pg->dwSignature != GRP_SIGNATURE) + return FALSE; + + // if (pg->wVersion != GRP_VERSION) + // some sort of version difference handling + + if (pg->wChecksum == 1234) + // TODO: calculate checksum function + return FALSE; + + // TODO: use ftlastwrite to throw an error + // if system clock is in the future + + // calculate the size of the group based on item of numbers + + // TODO: change to return a dword error value + return TRUE; +} + +/* * * *\ + CalculateGroupMemory - + Calculates the memory needed by a group. + Takes a group structure pointer and the + number of desired items as input. + RETURNS - + Size in bytes that a group should take up. +\* * * */ +UINT CalculateGroupMemory(_In_ PGROUP pGroup, _In_ UINT cItems, _In_ BOOL bLean) +{ + UINT cbGroupSize = 0; + UINT cItemBlock = 0; + + // first add the size of a group strucutre + cbGroupSize += sizeof(GROUP); + + // calculate the total amount of items wanted + cItemBlock = pGroup->cItemArray + cItems; + + if (!bLean) + // round the amount of items to the nearest but highest ITEM_BATCH_COUNT + cItemBlock = ((cItemBlock + ITEM_BATCH_COUNT) / ITEM_BATCH_COUNT) * ITEM_BATCH_COUNT; + + // finally calculate the total group size + cbGroupSize += cItemBlock * sizeof(ITEM); + + return cbGroupSize; +} + +/* * * *\ + GroupWndProc - + Group window procedure. + RETURNS - + Zero if nothing, otherwise returns the good stuff. +\* * * */ +LRESULT CALLBACK GroupWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + + switch (message) + { + + case WM_CREATE: + { + CREATESTRUCT* pCreateStruct; + MDICREATESTRUCT* pMDICreateStruct; + + // code borrowed from winprog + pCreateStruct = (CREATESTRUCT*)lParam; + pMDICreateStruct = (MDICREATESTRUCT*)pCreateStruct->lpCreateParams; + + return 0; + } + + case WM_CONTEXTMENU: + { + POINT pt = { 0 }; + HMENU hMenu = NULL; + BOOL bPopup = FALSE; + + // LVHT_ONITEM + // LVHT_NOWHERE + + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + break; + } + + case WM_CLOSE: + return ShowWindow(hWnd, SW_MINIMIZE); + + case WM_NOTIFY: + { + LPNMHDR lpnmhdr = (LPNMHDR)lParam; + HWND hWndListView = NULL; + + // find the listview control + hWndListView = FindWindowEx(hWnd, NULL, WC_LISTVIEW, NULL); + if (hWndListView == NULL) + break; + + switch (lpnmhdr->code) + { + + case NM_DBLCLK: + { + LVITEM lvi = { 0 }; + PITEM pItem = NULL; + + // what info do we want? + lvi.mask = LVIF_PARAM; + lvi.iItem = ((LPNMITEMACTIVATE)lParam)->iItem; + + // get the listview item + ListView_GetItem(hWndListView, &lvi); + + // get the item pointer + pItem = (PITEM)lvi.lParam; + + // verify... + if (pItem) + { + // and execute! + ExecuteItem(pItem); + } + + break; + } + + } + return 0; + } + + case WM_SIZE: + { + HWND hWndListView = NULL; + RECT rcGroupWindow = { 0 }; + + // get the group window rect + GetClientRect(hWnd, &rcGroupWindow); + + // find the listview control + hWndListView = FindWindowEx(hWnd, NULL, WC_LISTVIEW, NULL); + if (hWndListView == NULL) + break; + + // resize it to fit the window + SetWindowPos(hWndListView, NULL, + rcGroupWindow.left, rcGroupWindow.top, + rcGroupWindow.right - rcGroupWindow.left, + rcGroupWindow.bottom - rcGroupWindow.top, SWP_NOZORDER); + + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + default: + return DefMDIChildProc(hWnd, message, wParam, lParam); + } + + return TRUE; +} diff --git a/PROGMGR2/group.h b/PROGMGR2/group.h new file mode 100644 index 0000000..0232720 --- /dev/null +++ b/PROGMGR2/group.h @@ -0,0 +1,92 @@ +/* * * * * * * *\ + GROUP.H - + Copyright (c) 2024 Vortesys, Brady McDermott + DESCRIPTION - + Program group/item structures, functions and definitons + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Pragmas */ +#pragma once + +/* Definitions */ +#define MAX_GROUPS 512 +#define MAX_ITEMS 512 +#define MAX_TITLE_LENGTH 255 // Same as the registry key name max length +#define ITEM_BATCH_COUNT 8 +// Group Format Definitions +#define GRP_SIGNATURE 0x47324D50L // PM2G +#define GRP_VERSION 2 // Increment for breaking changes, format version +// Group Flag Values (DWORD) +#define GRP_FLAG_COMMON 0x00000001 +#define GRP_FLAG_READONLY 0x00000002 +#define GRP_FLAG_MINIMIZED 0x00000004 +#define GRP_FLAG_MAXIMIZED 0x00000008 +// Item Flag Values (DWORD) +#define ITM_FLAG_HOTKEY 0x00000001 +#define ITM_FLAG_PRIVILEGE 0x00000002 +#define ITM_FLAG_MINIMIZED 0x00000004 +#define ITM_FLAG_MAXIMIZED 0x00000008 + +/* Structures */ +// Item Structure +typedef struct _ITEM { + // Item executable and name + WCHAR szName[MAX_TITLE_LENGTH + 1]; + WCHAR szExecPath[MAX_PATH + 1]; // Path of the executable + WCHAR szWorkPath[MAX_PATH + 1]; // Working directory + // Item flags + DWORD dwFlags; // Use with ITM_FLAG_* values. + UINT uiHotkeyModifiers; + UINT uiHotkeyVirtualKey; + // Icon + WCHAR szIconPath[MAX_PATH + 1]; + INT iIconIndex; +} ITEM, * PITEM; + +// Group format, .GRP +typedef struct _GROUP { + // Group file header + DWORD dwSignature; // Set to GRP_SIGNATURE + WORD wVersion; // Group format version + WORD wChecksum; + // Group information + WCHAR szName[MAX_TITLE_LENGTH + 1]; + DWORD dwFlags; // Use with GRP_FLAG_* values. + FILETIME ftLastWrite; + // Window information + RECT rcGroup; + WINDOWPLACEMENT wp; + // Icon + WCHAR szIconPath[MAX_PATH + 1]; + INT iIconIndex; + // Items + WORD cItemArray; // Number of items in pItemArray + ITEM pItemArray[1]; // Array of items +} GROUP, * PGROUP; + +/* Global Variables */ +extern HWND hWndMDIClient; + +/* Local Variables */ + +/* Function Prototypes */ +BOOL InitializeGroups(VOID); +// Group Management +HWND CreateGroup(_In_ PGROUP pg); +BOOL RemoveGroup(_In_ HWND hWndGroup, _In_ BOOL bEliminate); +// Item Management +PITEM CreateItem(_In_ HWND hWndGroup, _In_ PITEM pi); +BOOL LoadItems(_In_ HWND hWndGroup); +BOOL RemoveItem(_In_ PITEM pi); +BOOL ExecuteItem(_In_ PITEM pi); +// Save/Load helper functions +VOID UpdateGroup(_In_ PGROUP pg); +BOOL VerifyGroup(_In_ PGROUP pg, _In_ BOOL bRepair); +// Helper functions +UINT CalculateGroupMemory(_In_ PGROUP pGroup, _In_ UINT cItems, _In_ BOOL bLean); +HWND GetHwndFromPGroup(_In_ PGROUP pg); +BOOL GetHwndFromPGroupEnum(_In_ HWND hwnd, _In_ LPARAM lParam); +// Group Window +LRESULT CALLBACK GroupWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); diff --git a/PROGMGR2/grpupdat.h b/PROGMGR2/grpupdat.h new file mode 100644 index 0000000..e4fb1e2 --- /dev/null +++ b/PROGMGR2/grpupdat.h @@ -0,0 +1,38 @@ +/* * * * * * * *\ + GRPUPDAT.H - + Copyright (c) 2024 Vortesys, Brady McDermott + DESCRIPTION - + Header containing classic program group structures + in order to facilitate conversion to the new format. + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Pragmas */ +#pragma once + +/* Includes */ +#include + +/* Definitions */ +// Group signatures +#define OLDGRP_SIGNATURE 0x43554D50L // PMUC + +/* Structures */ +// Old group format, .GRP +typedef struct _OLDGROUP { + DWORD dwMagic; // Set to OLDGRP_SIGNATURE + WORD wCheckSum; // Seemingly unused + WORD cbGroup; // Size of group + RECT rcNormal; // Window rect + POINT ptMin; // Minimized window position + WORD nCmdShow; // Min/max state + WORD pName; // Group name + WORD cxIcon; // Icon width + WORD cyIcon; // Icon height + WORD wIconFormat; + WORD wReserved; // Unused + + WORD cItems; // Number of items + WORD rgiItems[1]; // Array of item offsets +} OLDGROUP, * POLDGROUP; diff --git a/PROGMGR2/icons/common.ico b/PROGMGR2/icons/common.ico new file mode 100644 index 0000000..530e4e9 Binary files /dev/null and b/PROGMGR2/icons/common.ico differ diff --git a/PROGMGR2/icons/misc/bkgsrc.pdn b/PROGMGR2/icons/misc/bkgsrc.pdn new file mode 100644 index 0000000..8007263 Binary files /dev/null and b/PROGMGR2/icons/misc/bkgsrc.pdn differ diff --git a/PROGMGR2/icons/misc/bkgsrctxt.pdn b/PROGMGR2/icons/misc/bkgsrctxt.pdn new file mode 100644 index 0000000..ee3de6e Binary files /dev/null and b/PROGMGR2/icons/misc/bkgsrctxt.pdn differ diff --git a/PROGMGR2/icons/misc/comms.ico b/PROGMGR2/icons/misc/comms.ico new file mode 100644 index 0000000..5ff14f3 Binary files /dev/null and b/PROGMGR2/icons/misc/comms.ico differ diff --git a/PROGMGR2/icons/misc/console.ico b/PROGMGR2/icons/misc/console.ico new file mode 100644 index 0000000..c3dff5b Binary files /dev/null and b/PROGMGR2/icons/misc/console.ico differ diff --git a/PROGMGR2/icons/misc/console.pdn b/PROGMGR2/icons/misc/console.pdn new file mode 100644 index 0000000..ed5e634 Binary files /dev/null and b/PROGMGR2/icons/misc/console.pdn differ diff --git a/PROGMGR2/icons/misc/data.ico b/PROGMGR2/icons/misc/data.ico new file mode 100644 index 0000000..ec213fe Binary files /dev/null and b/PROGMGR2/icons/misc/data.ico differ diff --git a/PROGMGR2/icons/misc/document.ico b/PROGMGR2/icons/misc/document.ico new file mode 100644 index 0000000..c4e9c3d Binary files /dev/null and b/PROGMGR2/icons/misc/document.ico differ diff --git a/PROGMGR2/icons/misc/msdos.ico b/PROGMGR2/icons/misc/msdos.ico new file mode 100644 index 0000000..6a8d6e5 Binary files /dev/null and b/PROGMGR2/icons/misc/msdos.ico differ diff --git a/PROGMGR2/icons/misc/pmcomm.pdn b/PROGMGR2/icons/misc/pmcomm.pdn new file mode 100644 index 0000000..a755cd0 Binary files /dev/null and b/PROGMGR2/icons/misc/pmcomm.pdn differ diff --git a/PROGMGR2/icons/misc/pmdata.pdn b/PROGMGR2/icons/misc/pmdata.pdn new file mode 100644 index 0000000..9544c0e Binary files /dev/null and b/PROGMGR2/icons/misc/pmdata.pdn differ diff --git a/PROGMGR2/icons/misc/pmlogo.pdn b/PROGMGR2/icons/misc/pmlogo.pdn new file mode 100644 index 0000000..8df28a8 Binary files /dev/null and b/PROGMGR2/icons/misc/pmlogo.pdn differ diff --git a/PROGMGR2/icons/misc/pmsheet.pdn b/PROGMGR2/icons/misc/pmsheet.pdn new file mode 100644 index 0000000..536c854 Binary files /dev/null and b/PROGMGR2/icons/misc/pmsheet.pdn differ diff --git a/PROGMGR2/icons/misc/pmword.pdn b/PROGMGR2/icons/misc/pmword.pdn new file mode 100644 index 0000000..142e612 Binary files /dev/null and b/PROGMGR2/icons/misc/pmword.pdn differ diff --git a/PROGMGR2/icons/misc/spsheet.ico b/PROGMGR2/icons/misc/spsheet.ico new file mode 100644 index 0000000..1f3b919 Binary files /dev/null and b/PROGMGR2/icons/misc/spsheet.ico differ diff --git a/PROGMGR2/icons/misc/superman.ico b/PROGMGR2/icons/misc/superman.ico new file mode 100644 index 0000000..8b32423 Binary files /dev/null and b/PROGMGR2/icons/misc/superman.ico differ diff --git a/PROGMGR2/icons/misc/wfdir.pdn b/PROGMGR2/icons/misc/wfdir.pdn new file mode 100644 index 0000000..c544c49 Binary files /dev/null and b/PROGMGR2/icons/misc/wfdir.pdn differ diff --git a/PROGMGR2/icons/misc/wftrdir.pdn b/PROGMGR2/icons/misc/wftrdir.pdn new file mode 100644 index 0000000..8319709 Binary files /dev/null and b/PROGMGR2/icons/misc/wftrdir.pdn differ diff --git a/PROGMGR2/icons/misc/wftree.pdn b/PROGMGR2/icons/misc/wftree.pdn new file mode 100644 index 0000000..909036b Binary files /dev/null and b/PROGMGR2/icons/misc/wftree.pdn differ diff --git a/PROGMGR2/icons/misc/winoldap.ico b/PROGMGR2/icons/misc/winoldap.ico new file mode 100644 index 0000000..53bc6bf Binary files /dev/null and b/PROGMGR2/icons/misc/winoldap.ico differ diff --git a/PROGMGR2/icons/misc/winoldap.pdn b/PROGMGR2/icons/misc/winoldap.pdn new file mode 100644 index 0000000..151d9d1 Binary files /dev/null and b/PROGMGR2/icons/misc/winoldap.pdn differ diff --git a/PROGMGR2/icons/personal.ico b/PROGMGR2/icons/personal.ico new file mode 100644 index 0000000..53d78de Binary files /dev/null and b/PROGMGR2/icons/personal.ico differ diff --git a/PROGMGR2/icons/pmgroup.ico b/PROGMGR2/icons/pmgroup.ico new file mode 100644 index 0000000..1ceac12 Binary files /dev/null and b/PROGMGR2/icons/pmgroup.ico differ diff --git a/PROGMGR2/icons/pmitem.ico b/PROGMGR2/icons/pmitem.ico new file mode 100644 index 0000000..41f92fc Binary files /dev/null and b/PROGMGR2/icons/pmitem.ico differ diff --git a/PROGMGR2/icons/progmgr.ico b/PROGMGR2/icons/progmgr.ico new file mode 100644 index 0000000..1cb5e7c Binary files /dev/null and b/PROGMGR2/icons/progmgr.ico differ diff --git a/PROGMGR2/icons/progmgrd.ico b/PROGMGR2/icons/progmgrd.ico new file mode 100644 index 0000000..77cd324 Binary files /dev/null and b/PROGMGR2/icons/progmgrd.ico differ diff --git a/PROGMGR2/icons/src/common.pdn b/PROGMGR2/icons/src/common.pdn new file mode 100644 index 0000000..39ceef9 Binary files /dev/null and b/PROGMGR2/icons/src/common.pdn differ diff --git a/PROGMGR2/icons/src/personal.pdn b/PROGMGR2/icons/src/personal.pdn new file mode 100644 index 0000000..60da523 Binary files /dev/null and b/PROGMGR2/icons/src/personal.pdn differ diff --git a/PROGMGR2/icons/src/pmgroup.pdn b/PROGMGR2/icons/src/pmgroup.pdn new file mode 100644 index 0000000..f19f7da Binary files /dev/null and b/PROGMGR2/icons/src/pmgroup.pdn differ diff --git a/PROGMGR2/icons/src/pmitem.pdn b/PROGMGR2/icons/src/pmitem.pdn new file mode 100644 index 0000000..7ac77f4 Binary files /dev/null and b/PROGMGR2/icons/src/pmitem.pdn differ diff --git a/PROGMGR2/icons/src/progmgr.ico b/PROGMGR2/icons/src/progmgr.ico new file mode 100644 index 0000000..1cb5e7c Binary files /dev/null and b/PROGMGR2/icons/src/progmgr.ico differ diff --git a/PROGMGR2/icons/src/progmgr.pdn b/PROGMGR2/icons/src/progmgr.pdn new file mode 100644 index 0000000..7503195 Binary files /dev/null and b/PROGMGR2/icons/src/progmgr.pdn differ diff --git a/PROGMGR2/icons/src/progmgrd.ico b/PROGMGR2/icons/src/progmgrd.ico new file mode 100644 index 0000000..77cd324 Binary files /dev/null and b/PROGMGR2/icons/src/progmgrd.ico differ diff --git a/PROGMGR2/icons/src/progmgrd.pdn b/PROGMGR2/icons/src/progmgrd.pdn new file mode 100644 index 0000000..c7891b5 Binary files /dev/null and b/PROGMGR2/icons/src/progmgrd.pdn differ diff --git a/PROGMGR2/lang/dlg_en-US.rc b/PROGMGR2/lang/dlg_en-US.rc new file mode 100644 index 0000000..2e391fb --- /dev/null +++ b/PROGMGR2/lang/dlg_en-US.rc @@ -0,0 +1,93 @@ +/* * * * * * * *\ + DLG_EN-US.RC - + Copyright (c) 2024 Vortesys, Brady McDermott + LANGUAGE - + English, US + CREDITS - + freedom7341 + DESCRIPTION - + This file contains the language-specific dialogs + for Program Manager. + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Necessary Evil */ +#define APSTUDIO_HIDDEN_SYMBOLS +#include +#define STRICT +#include +#undef STRICT +#include "resource.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +/* Dialogs */ +// Program Group +DLG_GROUP DIALOG 0, 0, 256, 96 +STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_POPUP +EXSTYLE WS_EX_WINDOWEDGE +FONT 8, "Ms Shell Dlg 2" +CAPTION "Program Group" +{ + EDITTEXT IDD_NAME, 56, 19, 108, 14, WS_TABSTOP | ES_AUTOHSCROLL, WS_EX_LEFT + AUTOCHECKBOX "Create group for all users", IDD_COMMGROUP, 14, 40, 94, 8, WS_TABSTOP | WS_DISABLED, WS_EX_LEFT + PUSHBUTTON "Change Icon...", IDD_CHICON, 185, 47, 55, 14, WS_TABSTOP, WS_EX_LEFT + PUSHBUTTON "OK", IDD_OK, 145, 75, 50, 14, WS_TABSTOP, WS_EX_LEFT + PUSHBUTTON "Cancel", IDD_CANCEL, 199, 75, 50, 14, WS_TABSTOP, WS_EX_LEFT + // The following controls shouldn't be directly interacted with. + CONTROL "", IDD_STAT_ICON, WC_STATIC, SS_ICON | SS_CENTERIMAGE, 202, 19, 21, 20, WS_EX_LEFT + LTEXT "Name:", 32, 16, 22, 22, 9, SS_LEFT, WS_EX_LEFT + GROUPBOX "Properties", 33, 7, 7, 162, 61, 0, WS_EX_LEFT + GROUPBOX "Icon", 34, 176, 7, 73, 61, 0, WS_EX_LEFT +} + +// Program Item +DLG_ITEM DIALOG 0, 0, 256, 167 +STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_POPUP +EXSTYLE WS_EX_WINDOWEDGE +FONT 8, "Ms Shell Dlg 2" +CAPTION "Program Item" +{ + EDITTEXT IDD_NAME, 56, 19, 108, 14, WS_TABSTOP | ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDD_PATH, 16, 47, 94, 14, WS_TABSTOP | ES_AUTOHSCROLL, WS_EX_LEFT + PUSHBUTTON "Browse...", IDD_BROWSE, 114, 47, 50, 14, WS_TABSTOP, WS_EX_LEFT + PUSHBUTTON "Change Icon...", IDD_CHICON, 185, 47, 55, 14, WS_TABSTOP, WS_EX_LEFT + AUTOCHECKBOX "Use different working directory", IDD_WORKDIR, 14, 87, 111, 8, WS_TABSTOP, WS_EX_LEFT + EDITTEXT IDD_WORKPATH, 80, 99, 106, 14, WS_TABSTOP | WS_DISABLED | ES_AUTOHSCROLL, WS_EX_LEFT + PUSHBUTTON "Browse...", IDD_BROWSE2, 190, 99, 50, 14, WS_TABSTOP | WS_DISABLED, WS_EX_LEFT + CONTROL "", IDD_HOTKEY, HOTKEY_CLASS, WS_TABSTOP, 56, 117, 184, 14, WS_EX_LEFT + PUSHBUTTON "OK", IDD_OK, 145, 146, 50, 14, WS_TABSTOP, WS_EX_LEFT + PUSHBUTTON "Cancel", IDD_CANCEL, 199, 146, 50, 14, WS_TABSTOP, WS_EX_LEFT + // The following controls shouldn't be directly interacted with. + CONTROL "", IDD_STAT_ICON, WC_STATIC, SS_ICON | SS_CENTERIMAGE, 202, 19, 21, 20, WS_EX_LEFT + LTEXT "Name:", 32, 16, 22, 22, 9, SS_LEFT, WS_EX_LEFT + LTEXT "Directory:", 34, 16, 37, 31, 9, SS_LEFT, WS_EX_LEFT + LTEXT "Hotkey:", 35, 16, 120, 26, 9, SS_LEFT, WS_EX_LEFT + LTEXT "Directory:", IDD_STAT_WORKDIR, 26, 102, 31, 9, WS_DISABLED | SS_LEFT, WS_EX_LEFT + GROUPBOX "Properties", 36, 7, 7, 162, 61, 0, WS_EX_LEFT + GROUPBOX "Icon", 37, 176, 7, 73, 61, 0, WS_EX_LEFT + GROUPBOX "Advanced", 38, 7, 75, 242, 64, 0, WS_EX_LEFT +} + +// Power and Shutdown +DLG_POWER DIALOG 0, 0, 256, 167 +STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_SYSMENU | WS_POPUP +EXSTYLE WS_EX_WINDOWEDGE +FONT 8, "Ms Shell Dlg 2" +CAPTION "Shut Down" +{ + EDITTEXT IDD_NAME, 56, 19, 108, 14, WS_TABSTOP | ES_AUTOHSCROLL, WS_EX_LEFT + EDITTEXT IDD_PATH, 16, 47, 94, 14, WS_TABSTOP | ES_AUTOHSCROLL, WS_EX_LEFT + PUSHBUTTON "Browse...", IDD_BROWSE, 114, 47, 50, 14, WS_TABSTOP, WS_EX_LEFT + PUSHBUTTON "Change Icon...", IDD_CHICON, 185, 47, 55, 14, WS_TABSTOP, WS_EX_LEFT + AUTOCHECKBOX "Use different working directory", IDD_WORKDIR, 14, 87, 111, 8, WS_TABSTOP, WS_EX_LEFT + EDITTEXT IDD_WORKPATH, 80, 99, 106, 14, WS_TABSTOP | WS_DISABLED | ES_AUTOHSCROLL, WS_EX_LEFT + PUSHBUTTON "Browse...", IDD_BROWSE2, 190, 99, 50, 14, WS_TABSTOP | WS_DISABLED, WS_EX_LEFT + CONTROL "", IDD_HOTKEY, HOTKEY_CLASS, WS_TABSTOP, 56, 117, 184, 14, WS_EX_LEFT + PUSHBUTTON "OK", IDD_OK, 145, 146, 50, 14, WS_TABSTOP, WS_EX_LEFT + PUSHBUTTON "Cancel", IDD_CANCEL, 199, 146, 50, 14, WS_TABSTOP, WS_EX_LEFT + // The following controls shouldn't be directly interacted with. + CONTROL "", IDD_STAT_ICON, WC_STATIC, SS_ICON | SS_CENTERIMAGE, 202, 19, 21, 20, WS_EX_LEFT + LTEXT "Name:", 32, 16, 22, 22, 9, SS_LEFT, WS_EX_LEFT + GROUPBOX "Properties", 36, 7, 7, 162, 61, 0, WS_EX_LEFT +} diff --git a/PROGMGR2/lang/res_en-US.rc b/PROGMGR2/lang/res_en-US.rc new file mode 100644 index 0000000..60ef863 --- /dev/null +++ b/PROGMGR2/lang/res_en-US.rc @@ -0,0 +1,160 @@ +/* * * * * * * *\ + RES_EN-US.RC - + Copyright (c) 2024 Vortesys, Brady McDermott + LANGUAGE - + English, US + CREDITS - + freedom7341 + DESCRIPTION - + This file contains the language-specific + resources for the Program Manager. + ** NO DEPENDENCIES ** + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Necessary Evil */ +#define APSTUDIO_HIDDEN_SYMBOLS +#include +#include "resource.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +/* Accelerator Table */ +IDA_ACCELS ACCELERATORS +{ + "N", IDM_FILE_NEW, VIRTKEY, CONTROL +} + +/* Menus and Commands */ +// Main Menu +IDM_MAIN MENU +{ + POPUP "&File" + { + POPUP "&New" + { + MENUITEM "Program &Group", IDM_FILE_NEW_GROUP + MENUITEM "Program &Item", IDM_FILE_NEW_ITEM + } + MENUITEM SEPARATOR + MENUITEM "&Open", IDM_FILE_OPEN + MENUITEM "&Move", IDM_FILE_MOVE + MENUITEM "&Copy", IDM_FILE_COPY + MENUITEM "&Delete", IDM_FILE_DELETE + MENUITEM "&Properties", IDM_FILE_PROPS + MENUITEM SEPARATOR + MENUITEM "&Run...", IDM_FILE_RUN + MENUITEM SEPARATOR + MENUITEM "E&xit", IDM_FILE_EXIT + } + POPUP "&Options" + { + MENUITEM "&Auto Arrange", IDM_OPTIONS_AUTOARRANGE + MENUITEM "&Minimize on Use", IDM_OPTIONS_MINONRUN + MENUITEM "Always &on Top", IDM_OPTIONS_TOPMOST + MENUITEM "Show &Name on Titlebar", IDM_OPTIONS_SHOWUSERNAME + MENUITEM "&Save Settings on Exit", IDM_OPTIONS_SAVESETTINGS + MENUITEM SEPARATOR + MENUITEM "Save Settings &Now", IDM_OPTIONS_SAVENOW + } + POPUP "&Window" + { + MENUITEM "&Cascade", IDM_WINDOW_CASCADE + MENUITEM "&Tile Vertically", IDM_WINDOW_TILE + MENUITEM "Tile &Horizontally", IDM_WINDOW_TILEHORIZONTALLY + MENUITEM "&Arrange Icons", IDM_WINDOW_ARRANGEICONS + } + POPUP "&Help" + { + MENUITEM "&View Internet Help", IDM_HELP_INDEX + MENUITEM SEPARATOR + MENUITEM "&About Program Manager II", IDM_HELP_ABOUT + } +} + +IDM_DESK MENU PRELOAD +{ + POPUP "Context Menu" + { + MENUITEM "&View", IDM_DESK_VIEW + MENUITEM "S&ort by", IDM_DESK_SORT + MENUITEM "&Run Program", IDM_FILE_RUN + MENUITEM "&Task Manager", IDM_TASKMGR + MENUITEM SEPARATOR + MENUITEM "S&creen Resolution", IDM_DESK_SCREENRES + MENUITEM "Pe&rsonalize", IDM_DESK_PERSONALIZE + } +} + + +// Context Menus +IDM_ITEM MENU PRELOAD // Program Item +{ + POPUP "Context Menu" + { + MENUITEM "&Open", IDM_FILE_OPEN + MENUITEM "&Move to", IDM_FILE_MOVE + MENUITEM "&Copy to", IDM_FILE_COPY + MENUITEM "&Delete", IDM_FILE_DELETE + MENUITEM SEPARATOR + MENUITEM "&Properties", IDM_FILE_PROPS + } +} +IDM_GROUP MENU PRELOAD // Program Group + { + POPUP "Context Menu" + { + MENUITEM "&New", IDM_FILE_NEW + // MENUITEM "&Delete", IDM_FILE_DELETE + // MENUITEM SEPARATOR + // MENUITEM "&Properties", IDM_FILE_PROPS + } +} + +/* String Table */ +STRINGTABLE +{ + // Application +#ifndef _DEBUG + IDS_APPTITLE, "Program Manager II" // 64 +#else + IDS_APPTITLE, "Program Manager II [DEBUG]" // 64 +#endif + IDS_PMCLASS, "progmgr" // 16 + IDS_DTCLASS, "progman" // 16 + IDS_GRPCLASS, "pmgroup" // 16 + IDS_WEBSITE, "https://Vortesys.github.io/progmgr/" // 64 + + // Menu commands + IDS_RUN "R&un..." + IDS_TASKMGR "&Task Manager" + IDS_EXIT "E&xit" + IDS_SHUTDOWN "S&hutdown..." + + // Dialog strings + IDS_DLG_OK "OK" + IDS_DLG_CANCEL "Cancel" + IDS_DLG_BROWSE "Browse..." + + // Icon section strings + IDS_DLG_ICON "Icon" + IDS_DLG_CHICON "Change Icon..." + + // Properties section strings + IDS_DLG_PROPS "Properties" + IDS_DLG_NAME "Name:" + IDS_DLG_DIRECTORY "Directory:" + IDS_DLG_COMMGRP "Create group for all users" + + // Advanced section strings + IDS_DLG_ADVANCED "Advanced" + IDS_DLG_WORKDIR "Use different working directory" + IDS_DLG_HOTKEY "Hotkey:" + + // Dialog title strings + IDS_DLT_GRP_PROPS "Group Properties" // 32 + IDS_DLT_GRP_NEW "New Group" // 16 + IDS_DLT_ITEM_PROPS "Item Properties" // 32 + IDS_DLT_ITEM_NEW "New Item" // 16 + IDS_DLT_POWER "Shut Down" // [Computer Name] 16 + 16 +} diff --git a/PROGMGR2/progmgr.c b/PROGMGR2/progmgr.c new file mode 100644 index 0000000..3ba8d95 --- /dev/null +++ b/PROGMGR2/progmgr.c @@ -0,0 +1,221 @@ +/* * * * * * * *\ + PROGMGR.C - + Copyright (c) 2024 Vortesys, Brady McDermott + DESCRIPTION - + Program Manager's main file, now with extra wWinMain! + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Headers */ +#define WIN32_LEAN_AND_MEAN +#define SECURITY_WIN32 +#include +#include "progmgr.h" +#include "group.h" +#include "resource.h" +#include "registry.h" + +/* Variables */ +// Global +#ifdef _DEBUG +BOOL g_bIsDebugBuild = TRUE; +#else +BOOL g_bIsDebugBuild = FALSE; +#endif +BOOL g_bIsDefaultShell = FALSE; +// Handles +HINSTANCE g_hAppInstance; +HWND g_hWndProgMgr = NULL; +// Icons +HICON g_hProgMgrIcon = NULL; +HICON g_hGroupIcon = NULL; +// Global Strings +WCHAR g_szAppTitle[64]; +WCHAR g_szProgMgr[] = TEXT("progmgr"); +WCHAR g_szWebsite[64]; +WCHAR g_szClass[16]; +// Permissions +BOOL g_bPermAdmin; // Has Administrator permissions +BOOL g_bPermGuest; // Has Guest permissions +BOOL g_bPermPower; // Has power option permissions + +/* Functions */ + +/* * * *\ + UpdatePermissions - + Update the permissions for the user based + on account settings. + RETURNS - + TRUE if permissions are found successfully. + FALSE if otherwise. +\* * * */ +BOOL UpdatePermissions(VOID) +{ + // SE_SHUTDOWN_NAME + g_bPermAdmin = FALSE; + g_bPermGuest = FALSE; + g_bPermPower = FALSE; + return FALSE; +} + +/* * * *\ + wWinMain - + Program Manager's entry point. +\* * * */ +int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) +{ + MSG msg = { 0 }; + HANDLE hAccel; + HMENU hMenu; + HMENU hSystemMenu; + WNDCLASS wc = { 0 }; + WCHAR szBuffer[MAX_PATH]; + RECT rcRoot; + POINT ptOffset = { 0 }; + + // Initialize the instance + g_hAppInstance = hInstance; + + // Create Strings + LoadString(g_hAppInstance, IDS_PMCLASS, g_szClass, ARRAYSIZE(g_szClass)); + LoadString(g_hAppInstance, IDS_APPTITLE, g_szAppTitle, ARRAYSIZE(g_szAppTitle)); + LoadString(g_hAppInstance, IDS_WEBSITE, g_szWebsite, ARRAYSIZE(g_szWebsite)); + + // Get Desktop background color + //CreateSolidBrush(GetBackgroundColor + + // Register the Frame Window Class + wc.lpfnWndProc = WndProc; + wc.hInstance = g_hAppInstance; + wc.hIcon = g_hProgMgrIcon = LoadImage(g_hAppInstance, MAKEINTRESOURCE(IDI_PROGMGR), IMAGE_ICON, + 0, 0, LR_DEFAULTSIZE | LR_SHARED); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + // wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND); + wc.hbrBackground = NULL; + wc.lpszMenuName = MAKEINTRESOURCE(IDM_MAIN); + wc.lpszClassName = g_szClass; + + if (!RegisterClass(&wc)) + return FALSE; + + // Load the Accelerator table + hAccel = LoadAccelerators(g_hAppInstance, MAKEINTRESOURCE(IDA_ACCELS)); + if (!hAccel) + return FALSE; + + // Perform Registry actions, close if registry is inaccessible. + if (!InitializeRegistryKeys()) + return FALSE; + + g_bIsDefaultShell = IsProgMgrDefaultShell(); + + // Load configuration, but don't load groups yet + if(LoadConfig(TRUE, TRUE, FALSE) != RCE_SUCCESS) + return FALSE; + + // Get size of the root HWND + GetWindowRect(GetDesktopWindow(), &rcRoot); + + // Get the initial window offset + SystemParametersInfo(SPI_ICONHORIZONTALSPACING, 0, &ptOffset.x, 0); + SystemParametersInfo(SPI_ICONVERTICALSPACING, 0, &ptOffset.y, 0); + + // Create main window with a default size + // TODO: i pulled 320x240 out of my ass, make this dynamic later + if ((g_hWndProgMgr = CreateWindowW(wc.lpszClassName, g_szAppTitle, + WS_OVERLAPPEDWINDOW | WS_VISIBLE, + rcRoot.left + ptOffset.x, rcRoot.top + ptOffset.y, + rcRoot.left + ptOffset.x + 320, rcRoot.top + ptOffset.y + 240, + 0, 0, g_hAppInstance, NULL)) == NULL) + return 2; + + // Set the window size from the registry, but only if the coords make sense + if ((rcMainWindow.left != rcMainWindow.right) && (rcMainWindow.top != rcMainWindow.bottom)) + SetWindowPos(g_hWndProgMgr, NULL, + rcMainWindow.left, rcMainWindow.top, + rcMainWindow.right - rcMainWindow.left, + rcMainWindow.bottom - rcMainWindow.top, SWP_NOZORDER); + + // Load the menus... + hMenu = GetMenu(g_hWndProgMgr); + hSystemMenu = GetSystemMenu(g_hWndProgMgr, FALSE); + + // Update relevant parts of the window + UpdateChecks(bAutoArrange, IDM_OPTIONS, IDM_OPTIONS_AUTOARRANGE); + UpdateChecks(bMinOnRun, IDM_OPTIONS, IDM_OPTIONS_MINONRUN); + UpdateChecks(bTopMost, IDM_OPTIONS, IDM_OPTIONS_TOPMOST); + UpdateChecks(bShowUsername, IDM_OPTIONS, IDM_OPTIONS_SHOWUSERNAME); + UpdateChecks(bSaveSettings, IDM_OPTIONS, IDM_OPTIONS_SAVESETTINGS); + + UpdateWindowTitle(); + + // Update settings based on their values + if (bTopMost) + SetWindowPos(g_hWndProgMgr, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + + if (g_bIsDefaultShell) + { + // Modify the context menus since we're the default shell + DeleteMenu(hSystemMenu, SC_CLOSE, MF_BYCOMMAND); + + LoadString(g_hAppInstance, IDS_SHUTDOWN, szBuffer, ARRAYSIZE(szBuffer)); + InsertMenu(hSystemMenu, 6, MF_BYPOSITION | MF_STRING, IDM_SHUTDOWN, szBuffer); + ModifyMenu(hMenu, IDM_FILE_EXIT, MF_BYCOMMAND | MF_STRING, IDM_SHUTDOWN, szBuffer); + + LoadString(g_hAppInstance, IDS_RUN, szBuffer, ARRAYSIZE(szBuffer)); + InsertMenu(hSystemMenu, 6, MF_BYPOSITION | MF_STRING, IDM_FILE_RUN, szBuffer); + + LoadString(g_hAppInstance, IDS_TASKMGR, szBuffer, ARRAYSIZE(szBuffer)); + InsertMenu(hSystemMenu, 6, MF_BYPOSITION | MF_STRING, IDM_TASKMGR, szBuffer); + + // Create the desktop window... + // CreateDesktopWindow(); + } + + // Create the frame window + if (!InitializeGroups()) + return FALSE; + + while (GetMessage(&msg, NULL, 0, 0) > 0) + { + if (!TranslateMDISysAccel(hWndMDIClient, &msg) && + !TranslateAccelerator(g_hWndProgMgr, hAccel, &msg)) + { + DispatchMessage(&msg); + } + } + + return 0; +} + + +/* + +// NOTE(u130b8): We used to compile Release builds with /NODEFAULTLIB +// but I've temporarily removed it so malloc and free works. + +// NOTE(u130b8): We're compiling without the C runtime by default in Release builds. +// But in Debug builds, we need the C runtime, otherwise the address sanitizer and +// MSVC debug tools break because they use the wWinMainCRTStartup entrypoint to initialize. + +#ifdef PROGMGR_RELEASE +#pragma function(memset) +void *memset(char* dst, int value, size_t count) { + while (count--) { *dst++ = value; } + return dst; +} + +#pragma function(memcpy) +void *memcpy(char *dst, const char *src, size_t count) { + while (count--) { *dst++ = *src++; } + return dst; +} + +void __stdcall wWinMainCRTStartup() { + int code = wWinMain(GetModuleHandle(0), 0, 0, 0); + ExitProcess(code); +} +#endif + +*/ \ No newline at end of file diff --git a/PROGMGR2/progmgr.exe.manifest b/PROGMGR2/progmgr.exe.manifest new file mode 100644 index 0000000..2720cbe --- /dev/null +++ b/PROGMGR2/progmgr.exe.manifest @@ -0,0 +1,28 @@ + + + + + True/PM + PerMonitorV2, PerMonitor + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PROGMGR2/progmgr.h b/PROGMGR2/progmgr.h new file mode 100644 index 0000000..4869e10 --- /dev/null +++ b/PROGMGR2/progmgr.h @@ -0,0 +1,61 @@ +/* * * * * * * *\ + PROGMGR.H - + Copyright (c) 2024 Vortesys, Brady McDermott + DESCRIPTION - + Program Manager's primary header file. + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Pragmas */ +#pragma once +#pragma comment(lib, "ComCtl32.lib") +#pragma comment(lib, "Shlwapi.lib") +#pragma comment(lib, "Shell32.lib") +#pragma comment(lib, "Secur32.lib") +#pragma comment(lib, "UxTheme.lib") +#pragma comment(lib, "Version.lib") +#pragma comment(lib, "Winmm.lib") + +/* Macros and Defines */ +#define GET_WM_COMMAND_ID(wParam, lParam) LOWORD(wParam) +// RunFileDlg flags +#define RFF_NOBROWSE 0x0001 // Removes the browse button +#define RFF_NODEFAULT 0x0002 // No default item selected +#define RFF_CALCDIRECTORY 0x0004 // Calculates the working directory from the file name +#define RFF_NOLABEL 0x0008 // Removes the edit box label +#define RFF_NOSEPARATEMEM 0x0020 // Removes the Separate Memory Space check box, NT only +// RunFileDlg notification return values +#define RF_OK 0x0000 // Allow the application to run +#define RF_CANCEL 0x0001 // Cancel the operation and close the dialog +#define RF_RETRY 0x0002 // Cancel the operation, but leave the dialog open + +/* Global Variables */ +extern BOOL g_bIsDefaultShell; +// Handles +extern HINSTANCE g_hAppInstance; +extern HWND g_hWndProgMgr; +// Icons +extern HICON g_hProgMgrIcon; +extern HICON g_hGroupIcon; +// Strings +extern WCHAR g_szAppTitle[64]; +extern WCHAR g_szProgMgr[]; +extern WCHAR g_szWebsite[64]; +extern WCHAR g_szClass[16]; +// Permissions +extern BOOL g_bPermAdmin; // Has Administrator permissions +extern BOOL g_bPermGuest; // Has Guest permissions +extern BOOL g_bPermPower; // Has power option permissions + +/* Function Prototypes */ +BOOL UpdatePermissions(VOID); +// SYSINT.C +BOOL RunFileDlg(HWND hWndOwner, HICON hIcon, LPWSTR lpszDir, LPWSTR lpszTitle, LPWSTR lpszDesc, DWORD dwFlags); +BOOL ExitWindowsDialog(HWND hWndOwner); +BOOL SetShellWindow(HWND hWndShell); +// WNDPROC.C +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK CmdProc(HWND hWnd, WPARAM wParam, LPARAM lParam); +VOID UpdateChecks(BOOL bVarMenu, UINT uSubMenu, UINT uID); +VOID UpdateWindowTitle(VOID); diff --git a/PROGMGR2/progmgr.vcxproj b/PROGMGR2/progmgr.vcxproj new file mode 100644 index 0000000..b141fbe --- /dev/null +++ b/PROGMGR2/progmgr.vcxproj @@ -0,0 +1,201 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {b76b940d-b61a-456e-8569-72f64cf91afb} + progmgr + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ + false + + + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ + false + + + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ + false + + + $(SolutionDir)bin\$(Platform)\$(Configuration)\ + $(SolutionDir)bin\$(Platform)\$(Configuration)\$(ProjectName)\ + false + + + + Level3 + true + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Windows + true + $(OutDir)$(TargetName)$(TargetExt) + 5.02 + $(IntDir)$(TargetName)$(TargetExt).manifest + + + _DEBUG;%(PreprocessorDefinitions) + + + + + Level3 + true + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreaded + + + Windows + true + true + true + $(OutDir)$(TargetName)$(TargetExt) + 5.02 + $(IntDir)$(TargetName)$(TargetExt).manifest + + + + + Level3 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + Windows + true + $(OutDir)$(TargetName)$(TargetExt) + 5.02 + $(IntDir)$(TargetName)$(TargetExt).manifest + + + _DEBUG;%(PreprocessorDefinitions) + + + + + Level3 + true + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreaded + + + Windows + true + true + true + $(OutDir)$(TargetName)$(TargetExt) + 5.02 + $(IntDir)$(TargetName)$(TargetExt).manifest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PROGMGR2/progmgr.vcxproj.filters b/PROGMGR2/progmgr.vcxproj.filters new file mode 100644 index 0000000..0b86c3d --- /dev/null +++ b/PROGMGR2/progmgr.vcxproj.filters @@ -0,0 +1,84 @@ + + + + + {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 + + + {71dbbd6d-c82a-4fac-a0ff-74be88190f82} + + + {2a11d118-5a5c-4681-86dc-31de4f0251ff} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + Resource Files\en-US + + + Resource Files\en-US + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/PROGMGR2/registry.c b/PROGMGR2/registry.c new file mode 100644 index 0000000..223cc6f --- /dev/null +++ b/PROGMGR2/registry.c @@ -0,0 +1,388 @@ +/* * * * * * * *\ + REGISTRY.C - + Copyright (c) 2024 Vortesys, Brady McDermott + DESCRIPTION - + Program Manager's registry and settings related functions. + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Headers */ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include "progmgr.h" +#include "group.h" +#include "registry.h" + +/* Variables */ +// Global HKEYs +HKEY hKeyProgramManager = NULL; +HKEY hKeyProgramGroups = NULL; +HKEY hKeySettings = NULL; +// Global Values +BOOL bSaveSettings; +BOOL bShowUsername; +BOOL bTopMost; +BOOL bMinOnRun; +BOOL bAutoArrange; +DWORD dwSettingsMask = { + PMS_SAVESETTINGS | + PMS_SHOWUSERNAME | + !PMS_TOPMOST | + !PMS_MINONRUN | + PMS_AUTOARRANGE +}; +RECT rcMainWindow; +// Registry Subkeys +PWSTR pszProgramGroups = TEXT("Program Groups"); +PWSTR pszSettings = TEXT("Settings"); +// Settings Subkeys +PWSTR pszSettingsWindow = TEXT("Window"); +PWSTR pszSettingsMask = TEXT("SettingsMask"); +// Permissions (Global) + +/* Functions */ + +/* * * *\ + InitializeRegistryKeys - + Takes the relevant registry keys and turns them + into valid and usable handles. + RETURNS - + TRUE if successful, FALSE if unsuccessful. +\* * * */ +BOOL InitializeRegistryKeys(VOID) +{ + if (!RegCreateKeyEx(HKEY_CURRENT_USER, PROGMGR_KEY, 0, g_szProgMgr, 0, + KEY_READ | KEY_WRITE, NULL, &hKeyProgramManager, NULL)) + { + // Create Program Groups and Settings keys + RegCreateKeyEx(hKeyProgramManager, pszProgramGroups, 0, g_szProgMgr, 0, + KEY_READ | KEY_WRITE, NULL, &hKeyProgramGroups, NULL); + RegCreateKeyEx(hKeyProgramManager, pszSettings, 0, g_szProgMgr, 0, + KEY_READ | KEY_WRITE, NULL, &hKeySettings, NULL); + + return TRUE; + } + return FALSE; +} + +/* * * *\ + IsProgMgrDefaultShell - + Detects if Program Manager is the default shell. + RETURNS - + TRUE if Program Manager is the default shell, + FALSE if otherwise or an error occurs. +\* * * */ +BOOL IsProgMgrDefaultShell(VOID) +{ + HKEY hKeyWinlogon; + WCHAR szShell[HKEYMAXLEN] = TEXT(""); + DWORD dwType; + DWORD dwBufferSize = sizeof(szShell); + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, WINLOGON_KEY, + 0, KEY_READ, &hKeyWinlogon) == ERROR_SUCCESS) + { + if (RegQueryValueEx(hKeyWinlogon, TEXT("Shell"), 0, &dwType, + (LPBYTE)szShell, &dwBufferSize) == ERROR_SUCCESS) + { + if (StrStr(szShell, g_szProgMgr)) + { + // ProgMgr detected >:) + RegCloseKey(hKeyWinlogon); + return TRUE; + } + else + { + // Inferior shell detected. + RegCloseKey(hKeyWinlogon); + return FALSE; + } + } + } + else + { + // Assume that we're the shell... just incase. + RegCloseKey(hKeyWinlogon); + return TRUE; + } + // No registry access. + return FALSE; +} + +/* * * *\ + RegistrySaveGroup - + Saves a group structure to registry. + RETURNS - + RCE_* configuration error value +\* * * */ +DWORD RegistrySaveGroup(_In_ PGROUP pg) +{ + DWORD dwConfigStatus = RCE_SUCCESS; + + // If the pointer is invalid then fail out + if (pg == NULL) + return RCE_FAILURE; + + // Save group + UpdateGroup(pg); + if (!RegSetValueEx(hKeyProgramGroups, pg->szName, 0, REG_BINARY, + (const BYTE*)pg, sizeof(*pg)) == ERROR_SUCCESS) + dwConfigStatus = dwConfigStatus && RCE_GROUPS; + + return dwConfigStatus; +} + +/* * * *\ + SaveConfig - + Finds and collapses all settings + and saves them to the registry. + RETURNS - + RCE_* configuration error value +\* * * */ +DWORD SaveConfig(_In_ BOOL bSettings, _In_ BOOL bPos, _In_ BOOL bGroups, _In_ BOOL bExit) +{ + DWORD dwConfigStatus = RCE_SUCCESS; + + if (bSettings) + { + // Shrink the settings into the bitmask and save it + // There's definitely a better way to do this but... + // TODO: do this more efficiently + dwSettingsMask = + (bAutoArrange && PMS_AUTOARRANGE) * PMS_AUTOARRANGE | + (bMinOnRun && PMS_MINONRUN) * PMS_MINONRUN | + (bTopMost && PMS_TOPMOST) * PMS_TOPMOST | + (bShowUsername && PMS_SHOWUSERNAME) * PMS_SHOWUSERNAME | + (bSaveSettings && PMS_SAVESETTINGS) * PMS_SAVESETTINGS; + + if (!RegSetValueEx(hKeySettings, pszSettingsMask, 0, REG_DWORD, + (const BYTE*)&dwSettingsMask, sizeof(dwSettingsMask)) == ERROR_SUCCESS) + dwConfigStatus = dwConfigStatus && RCE_SETTINGS; + } + + if (bPos) + { + WINDOWPLACEMENT wpProgMgr = { 0 }; + RECT rcWindow; + + // Get and save window position + wpProgMgr.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(g_hWndProgMgr, &wpProgMgr); + CopyRect(&rcWindow, &wpProgMgr.rcNormalPosition); + + if (!RegSetValueEx(hKeySettings, pszSettingsWindow, 0, REG_BINARY, + (const BYTE*)&rcWindow, sizeof(rcWindow)) == ERROR_SUCCESS) + dwConfigStatus = dwConfigStatus && RCE_POSITION; + } + + if (bGroups) + { + HWND hWndGroup = NULL; + + EnumChildWindows(hWndMDIClient, &SaveWindowEnumProc, (LPARAM)bExit); + } + + return dwConfigStatus; +} + +/* * * *\ + SaveWindowEnumProc() - + Procedure for enumeration + of group windows to save + or close them + NOTES - + lParam is a BOOL that determines + whether to close the group or not + RETURNS - + TRUE to continue + FALSE to stop +\* * * */ +BOOL CALLBACK SaveWindowEnumProc(HWND hWndGroup, LPARAM lParam) +{ + PGROUP pNewGroup = NULL; + PGROUP pGroup = NULL; + + if (hWndGroup == NULL) + return FALSE; + + pGroup = (PGROUP)GetWindowLongPtr(hWndGroup, GWLP_USERDATA); + + if (pGroup == NULL) + return FALSE; + + // lean it... + /*pNewGroup = realloc(pGroup, CalculateGroupMemory(pGroup, 1, 1)); + if (pNewGroup != NULL) + { + pGroup = pNewGroup; + SetWindowLongPtr(hWndGroup, GWLP_USERDATA, (LONG_PTR)pGroup); + }*/ + + // save it... + if (RegistrySaveGroup(pGroup) != RCE_SUCCESS) + return FALSE; + + // close it... + if ((BOOL)lParam == TRUE) + { + RemoveGroup(hWndGroup, FALSE); + return FALSE; + } + + return TRUE; +} + +/* * * *\ + LoadConfig() - + Finds all settings and retrieves them + from the registry. + RETURNS - + RCE_* configuration error value +\* * * */ +DWORD LoadConfig(_In_ BOOL bSettings, _In_ BOOL bPos, _In_ BOOL bGroups) +{ + DWORD dwConfigStatus = RCE_SUCCESS; + + if (bSettings) + { + DWORD dwBufferSize = sizeof(dwSettingsMask); + DWORD dwType = REG_DWORD; + + // Load settings bitmask + if (!RegQueryValueEx(hKeySettings, pszSettingsMask, 0, &dwType, + (LPBYTE)&dwSettingsMask, &dwBufferSize) == ERROR_SUCCESS) + dwConfigStatus = dwConfigStatus && RCE_SETTINGS; + + // Apply bitmask to booleans + bAutoArrange = (dwSettingsMask & PMS_AUTOARRANGE); + bMinOnRun = (dwSettingsMask & PMS_MINONRUN); + bTopMost = (dwSettingsMask & PMS_TOPMOST); + bSaveSettings = (dwSettingsMask & PMS_SAVESETTINGS); + bShowUsername = (dwSettingsMask & PMS_SHOWUSERNAME); + } + + if (bPos) + { + DWORD dwRectBufferSize = sizeof(rcMainWindow); + DWORD dwType = REG_BINARY; + + // Load window position + if (!RegQueryValueEx(hKeySettings, pszSettingsWindow, 0, &dwType, + (LPBYTE)&rcMainWindow, &dwRectBufferSize) == ERROR_SUCCESS) + dwConfigStatus = dwConfigStatus && RCE_POSITION; + } + + if (bGroups) + { + DWORD dwBufferSize = sizeof(dwSettingsMask); + DWORD dwType = REG_BINARY; + UINT cGroupVals = 0; + + OutputDebugString(TEXT("REGISTRY.C: Loading groups...\n")); + + // Get the num of group values, if we fail then ABORT! + if (!(RegQueryInfoKey(hKeyProgramGroups, NULL, NULL, NULL, + NULL, NULL, NULL, &cGroupVals, + NULL, NULL, NULL, NULL) == ERROR_SUCCESS)) + { + dwConfigStatus = dwConfigStatus && RCE_SETTINGS; + + // I can get away with returning here upon + // failure since this is the last one + OutputDebugString(TEXT("REGISTRY.C: Failed to get # of values!\n")); + return dwConfigStatus; + } + + // Enumerate through the registry values + if (cGroupVals) + { + DWORD i = 0; + + for (i = 0; i < cGroupVals; i++) + { + WCHAR szGroupValName[MAX_TITLE_LENGTH] = TEXT(""); + UINT cbGroupValName = MAX_TITLE_LENGTH; + UINT cbGroup = 0; + PGROUP pGroup = NULL; + LSTATUS error = ERROR_SUCCESS; + + // TODO: figure out where i'm really going to store the + // group name, if not in the group structure then in + // the name of the registry key (val 3 here) + + // get the size of the group + error = RegEnumValue(hKeyProgramGroups, i, (LPWSTR)&szGroupValName, + &cbGroupValName, NULL, NULL, NULL, &cbGroup); + + if (error == ERROR_NO_MORE_ITEMS) + break; + else if (error != ERROR_SUCCESS) + { + OutputDebugString(TEXT("REGISTRY.C: RegEnumValue failure.\n")); + break; + } + + + // allocate memory for the group + pGroup = malloc(cbGroup); + + if (pGroup == NULL) + { + dwConfigStatus = dwConfigStatus && RCE_GROUPS; + return dwConfigStatus; + } + + // retrieve the group + error = RegGetValue(hKeyProgramGroups, NULL, szGroupValName, RRF_RT_REG_BINARY, NULL, pGroup, &cbGroup); + + if (error == ERROR_FILE_NOT_FOUND) + { + OutputDebugString(TEXT("REGISTRY.C: Group not found!\n")); + + dwConfigStatus = dwConfigStatus && RCE_GROUPS; + } + if (error == ERROR_MORE_DATA) + { + OutputDebugString(TEXT("REGISTRY.C: Buffer too small!\n")); + + dwConfigStatus = dwConfigStatus && RCE_GROUPS; + } + else if (error != ERROR_SUCCESS) + { + OutputDebugString(TEXT("REGISTRY.C: Group retrieval failed!\n")); + + dwConfigStatus = dwConfigStatus && RCE_GROUPS; + } + else + OutputDebugString(TEXT("REGISTRY.C: Group retrieved.\n")); + + // create the group + if (pGroup) + { + if (pGroup->dwSignature == GRP_SIGNATURE) + { + CreateGroup(pGroup); + OutputDebugString(TEXT("REGISTRY.C: Group created.\n")); + } + else + { + OutputDebugString(TEXT("REGISTRY.C: Group creation aborted.\n")); + } + + // free memory + free(pGroup); + } + else + { + dwConfigStatus = dwConfigStatus && RCE_GROUPS; + OutputDebugString(TEXT("REGISTRY.C: Failed to create group!\n")); + } + } + } + } + + return dwConfigStatus; +} diff --git a/PROGMGR2/registry.h b/PROGMGR2/registry.h new file mode 100644 index 0000000..5af71c0 --- /dev/null +++ b/PROGMGR2/registry.h @@ -0,0 +1,55 @@ +/* * * * * * * *\ + REGISTRY.H - + Copyright (c) 2024 Vortesys, Brady McDermott + DESCRIPTION - + Program Manager's header file for registry related functions. + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Pragmas */ +#pragma once + +/* Defines */ +#define HKEYMAXLEN 261 +// Key Paths +#define WINLOGON_KEY TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon") +// #define PROGMAN_KEY TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Program Manager") +// #define WINDOWS_KEY TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows") +#define PROGMGR_KEY TEXT("Software\\Vortesys\\Program Manager II") +// Registry configuration error values (DWORD) +#define RCE_SUCCESS 0x0000000 +#define RCE_FAILURE 0x0000001 +#define RCE_SETTINGS 0x0000002 +#define RCE_POSITION 0x0000004 +#define RCE_GROUPS 0x0000008 +// Settings values (DWORD) +#define PMS_AUTOARRANGE 0x00000001 +#define PMS_MINONRUN 0x00000002 +#define PMS_TOPMOST 0x00000004 +#define PMS_SHOWUSERNAME 0x00000008 +#define PMS_SAVESETTINGS 0x00000010 + +/* Global Variables */ +// HKEYs +extern HKEY hKeyProgramManager; +extern HKEY hkeyProgramGroups; +extern HKEY hKeySettings; +// Settings +extern BOOL bAutoArrange; +extern BOOL bMinOnRun; +extern BOOL bTopMost; +extern BOOL bShowUsername; +extern BOOL bSaveSettings; +extern RECT rcMainWindow; + +/* Function Prototypes */ +BOOL InitializeRegistryKeys(VOID); +BOOL IsProgMgrDefaultShell(VOID); +// Groups +DWORD RegistrySaveGroup(_In_ PGROUP pg); +// Settings +DWORD SaveConfig(_In_ BOOL bSettings, _In_ BOOL bPos, _In_ BOOL bGroups, _In_ BOOL bExit); +DWORD LoadConfig(_In_ BOOL bSettings, _In_ BOOL bPos, _In_ BOOL bGroups); +// Miscellaneous +BOOL CALLBACK SaveWindowEnumProc(HWND hWndGroup, LPARAM lParam); diff --git a/PROGMGR2/resource.h b/PROGMGR2/resource.h new file mode 100644 index 0000000..3811e32 --- /dev/null +++ b/PROGMGR2/resource.h @@ -0,0 +1,134 @@ +/* * * * * * * *\ + RESOURCE.H - + Copyright (c) 2024 Vortesys, Brady McDermott + DESCRIPTION - + Program Manager's resource collection. + ** NO DEPENDENCIES ** + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Pragmas */ +#ifndef RC_INVOKED +#pragma once +#endif + +/* Icon Library */ +// Primary, 1-9 +#define IDI_PROGMGR 1 +#define IDI_PROGGRP 2 +#define IDI_PROGITM 3 +#define IDI_COMMON 4 +#define IDI_PERSONAL 5 +// Secondary, 10-19 +// #define IDI_COMMS 10 +// #define IDI_CONSOLE 11 +// #define IDI_DOCUMENT 12 +// #define IDI_SPREADSHEET 13 +// Tertiary, 20+ +// #define IDI_SUPERMAN 20 + +/* Menus and Commands */ +// Context Menus and Accelerators +#define IDM_MAIN 100 +#define IDM_ITEM 101 +#define IDM_GROUP 102 +#define IDA_ACCELS 103 +// File, 110-119 +#define IDM_FILE 110 +#define IDM_FILE_NEW 111 +#define IDM_FILE_NEW_GROUP 112 +#define IDM_FILE_NEW_ITEM 113 +#define IDM_FILE_OPEN 114 +#define IDM_FILE_MOVE 115 +#define IDM_FILE_COPY 116 +#define IDM_FILE_DELETE 117 +#define IDM_FILE_PROPS 118 +#define IDM_FILE_RUN 119 +#define IDM_FILE_EXIT 120 +// Options, 130-149 +#define IDM_OPTIONS 130 +#define IDM_OPTIONS_AUTOARRANGE 131 +#define IDM_OPTIONS_MINONRUN 132 +#define IDM_OPTIONS_TOPMOST 133 +#define IDM_OPTIONS_SHOWUSERNAME 134 +#define IDM_OPTIONS_SAVESETTINGS 135 +#define IDM_OPTIONS_SAVENOW 136 +// Window, 150-169, 5000+ +#define IDM_WINDOW 150 +#define IDM_WINDOW_CASCADE 151 +#define IDM_WINDOW_TILE 152 +#define IDM_WINDOW_TILEHORIZONTALLY 153 +#define IDM_WINDOW_ARRANGEICONS 154 +#define IDM_WINDOW_CHILDSTART 5000 +// Help, 170-179 +#define IDM_HELP 170 +#define IDM_HELP_INDEX 171 +#define IDM_HELP_ABOUT 172 +// Miscellaneous, 180-199 +#define IDM_SHUTDOWN 180 +#define IDM_TASKMGR 181 +// Desktop Menus, 200-299 +#define IDM_DESK 200 +#define IDM_DESK_VIEW 201 +#define IDM_DESK_SORT 202 +#define IDM_DESK_SCREENRES 203 +#define IDM_DESK_PERSONALIZE 204 + +/* Dialogs */ +#define DLG_GROUP 1 +#define DLG_ITEM 2 +#define DLG_POWER 3 + +/* Dialog Controls */ +// Buttons +#define IDD_OK 1 +#define IDD_CANCEL 2 +#define IDD_BROWSE 3 +#define IDD_BROWSE2 4 +#define IDD_CHICON 5 +// Radio Buttons +#define IDD_WORKDIR 100 +#define IDD_COMMGROUP 101 +// Input Controls +#define IDD_NAME 200 +#define IDD_PATH 201 +#define IDD_WORKPATH 202 +#define IDD_HOTKEY 203 +// Static Controls +#define IDD_STAT_ICON 300 +#define IDD_STAT_WORKDIR 301 + +/* String Table */ +#define IDS_APPTITLE 1 +#define IDS_PMCLASS 2 +#define IDS_DTCLASS 3 +#define IDS_GRPCLASS 4 +#define IDS_WEBSITE 5 +// Menu Commands +#define IDS_RUN 100 +#define IDS_TASKMGR 101 +#define IDS_EXIT 102 +#define IDS_SHUTDOWN 103 +// General Dialog +#define IDS_DLG_OK 200 +#define IDS_DLG_CANCEL 201 +#define IDS_DLG_BROWSE 202 +// Icon Section +#define IDS_DLG_ICON 203 +#define IDS_DLG_CHICON 204 +// Properties Section +#define IDS_DLG_PROPS 205 +#define IDS_DLG_NAME 206 +#define IDS_DLG_DIRECTORY 207 +#define IDS_DLG_COMMGRP 208 +// Advanced Section +#define IDS_DLG_ADVANCED 209 +#define IDS_DLG_WORKDIR 210 +#define IDS_DLG_HOTKEY 211 +// Dialog Title +#define IDS_DLT_GRP_PROPS 300 +#define IDS_DLT_GRP_NEW 301 +#define IDS_DLT_ITEM_PROPS 302 +#define IDS_DLT_ITEM_NEW 303 +#define IDS_DLT_POWER 304 diff --git a/PROGMGR2/resource.rc b/PROGMGR2/resource.rc new file mode 100644 index 0000000..9090652 --- /dev/null +++ b/PROGMGR2/resource.rc @@ -0,0 +1,78 @@ +/* * * * * * * *\ + RESOURCE.RC - + Copyright (c) 2024 Vortesys, Brady McDermott + DESCRIPTION - + This file contains Program Manager's resources. + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Necessary Evil */ +#define APSTUDIO_HIDDEN_SYMBOLS +#include +#include "resource.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +/* Language-based Resources */ +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +#pragma code_page(65001) // UTF-8 +#include "lang\res_en-US.rc" // English (US) +#include "lang\dlg_en-US.rc" // English (US) + +/* Icon Library */ +// Primary +#ifdef NDEBUG +IDI_PROGMGR ICON icons\progmgr.ico +#else +IDI_PROGMGR ICON icons\progmgrd.ico +#endif +IDI_PROGGRP ICON icons\pmgroup.ico +IDI_PROGITM ICON icons\pmitem.ico +IDI_COMMON ICON icons\common.ico +IDI_PERSONAL ICON icons\personal.ico +// Secondary +// IDI_COMMS ICON icons\comms.ico +// IDI_CONSOLE ICON icons\console.ico +// IDI_DOCUMENT ICON icons\document.ico +// IDI_SPREADSHEET ICON icons\spsheet.ico +// Tertiary +// IDI_SUPERMAN ICON icons\misc\superman.ico + +/* Manifest */ +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "progmgr.exe.manifest" + +/* Version Information */ +#define APSTUDIO_HIDDEN_SYMBOLS +#include "version.inc" +#undef APSTUDIO_HIDDEN_SYMBOLS + +// NOTE(u130b8): Example of using PROGMGR_GIT_HASH in the resource file +#ifdef PROGMGR_GIT_HASH +#undef VER_PRODUCTVERSION_STR +#define VER_PRODUCTVERSION_STR "git-" PROGMGR_GIT_HASH +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VER_FILEVERSION + PRODUCTVERSION VER_PRODUCTVERSION + FILEFLAGSMASK 0x3fL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", VER_FILEDESCRIPTION_STR "\0" + VALUE "FileVersion", VER_FILEVERSION_STR "\0" + VALUE "InternalName", VER_INTERNALNAME_STR "\0" + VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR "\0" + VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR "\0" + VALUE "ProductName", VER_PRODUCTNAME_STR + VALUE "ProductVersion", VER_PRODUCTVERSION_STR "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 1033, 1200 + END +END diff --git a/PROGMGR2/sysint.c b/PROGMGR2/sysint.c new file mode 100644 index 0000000..090d8c1 --- /dev/null +++ b/PROGMGR2/sysint.c @@ -0,0 +1,88 @@ +/* * * * * * * *\ + SYSINT.C - + Copyright (c) 2024 Vortesys, Brady McDermott + DESCRIPTION - + Program Manager's system export functions. + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Headers */ +#define WIN32_LEAN_AND_MEAN +#include +#include "progmgr.h" + +/* Functions */ + +/* * * *\ + RunFileDlg - + Produces a Run dialog window. + RETURNS - + True if successful, false if unsuccessful. +\* * * */ +BOOL RunFileDlg(HWND hWndOwner, + HICON hIcon, + LPWSTR lpszDir, + LPWSTR lpszTitle, + LPWSTR lpszDesc, + DWORD dwFlags) +{ + HMODULE hLib = LoadLibrary(TEXT("shell32.dll")); + + if (hLib) + { + FARPROC fLib = GetProcAddress(hLib, MAKEINTRESOURCEA(61)); + if (fLib(hWndOwner, hIcon, lpszDir, lpszTitle, lpszDesc, dwFlags)) + { + FreeLibrary(hLib); + return TRUE; + } + } + + return FALSE; +} + +/* * * *\ + ExitWindowsDialog - + Produces a Shutdown dialog window. + RETURNS - + True if successful, false if unsuccessful. +\* * * */ +BOOL ExitWindowsDialog(HWND hWndOwner) +{ + HMODULE hLib = LoadLibrary(TEXT("shell32.dll")); + + if (hLib) + { + FARPROC fLib = GetProcAddress(hLib, (LPCSTR)60); + if (fLib(hWndOwner)) + { + FreeLibrary(hLib); + return TRUE; + } + } + + return FALSE; +} + +/* * * *\ + SetShellWindow - + Sets a window as the shell window. + RETURNS - + True if successful, false if unsuccessful. +\* * * */ +BOOL SetShellWindow(HWND hWndShell) +{ + HMODULE hLib = GetModuleHandle(TEXT("user32.dll")); + + if (hLib) + { + FARPROC fLib = GetProcAddress(hLib, "SetShellWindow"); + if (fLib(hWndShell)) + { + return TRUE; + } + } + + return FALSE; +} diff --git a/PROGMGR2/version.inc b/PROGMGR2/version.inc new file mode 100644 index 0000000..40b4bdb Binary files /dev/null and b/PROGMGR2/version.inc differ diff --git a/PROGMGR2/wndproc.c b/PROGMGR2/wndproc.c new file mode 100644 index 0000000..6ffb155 --- /dev/null +++ b/PROGMGR2/wndproc.c @@ -0,0 +1,243 @@ +/* * * * * * * *\ + WNDPROC.C - + Copyright (c) 2024 Vortesys, Brady McDermott + DESCRIPTION - + Program Manager's window procedure. + LICENSE INFORMATION - + MIT License, see LICENSE.txt in the root folder +\* * * * * * * */ + +/* Headers */ +#define WIN32_LEAN_AND_MEAN +#define SECURITY_WIN32 +#include +#include +#include +#include +#include +#include +#include "progmgr.h" +#include "dialog.h" +#include "group.h" +#include "resource.h" +#include "registry.h" + +/* Functions */ + +/* * * *\ + WndProc - + Program Manager's window procedure. + RETURNS - + Zero if nothing, otherwise returns the good stuff. +\* * * */ +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + + case WM_CREATE: + { + // Play the logon sound. (But only if we're the default shell) + if (g_bIsDefaultShell) + PlaySound(TEXT("WindowsLogon"), NULL, SND_ALIAS | SND_ASYNC); + + return TRUE; + } + + case WM_SYSCOMMAND: + { + if ((wParam >= IDM_MAIN) && (wParam <= IDM_TASKMGR)) + { + if (wParam == IDM_TASKMGR) + { + ShellExecute(g_hWndProgMgr, TEXT("open"), TEXT("TASKMGR.EXE"), NULL, NULL, SW_NORMAL); + return 0; + } + + if (CmdProc(hWnd, wParam, lParam)) + return 0; + } + + return DefFrameProc(hWnd, hWndMDIClient, message, wParam, lParam); + } + + case WM_COMMAND: + if (CmdProc(hWnd, wParam, lParam)) + return 0; + + return DefFrameProc(hWnd, hWndMDIClient, message, wParam, lParam); + + case WM_CLOSE: + case WM_ENDSESSION: + if (bSaveSettings) + SaveConfig(TRUE, TRUE, TRUE, TRUE); + + if (g_bIsDefaultShell && (GetShellWindow() != NULL)) + SetShellWindow(0); + + PostQuitMessage(0); + break; + + default: + return DefFrameProc(hWnd, hWndMDIClient, message, wParam, lParam); + } + return 0; + +} + +/* * * *\ + CmdProc - + Program Manager's syscommand procedure. + RETURNS - + Zero if nothing, otherwise returns the good stuff. +\* * * */ +LRESULT CALLBACK CmdProc(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + switch (GET_WM_COMMAND_ID(wParam, lParam)) + { + + case IDM_SHUTDOWN: + DialogBox(g_hAppInstance, MAKEINTRESOURCE(DLG_POWER), hWnd, (DLGPROC)ShutdownDlgProc); + break; + + case IDM_FILE_NEW_GROUP: + DialogBox(g_hAppInstance, MAKEINTRESOURCE(DLG_GROUP), hWnd, (DLGPROC)NewGroupDlgProc); + break; + + case IDM_FILE_NEW_ITEM: + DialogBox(g_hAppInstance, MAKEINTRESOURCE(DLG_ITEM), hWnd, (DLGPROC)NewItemDlgProc); + break; + + case IDM_FILE_RUN: + RunFileDlg(hWnd, NULL, NULL, NULL, NULL, RFF_CALCDIRECTORY); + break; + + case IDM_FILE_EXIT: + PostQuitMessage(0); + break; + + case IDM_OPTIONS_AUTOARRANGE: + bAutoArrange = !bAutoArrange; + UpdateChecks(bAutoArrange, IDM_OPTIONS, IDM_OPTIONS_AUTOARRANGE); + goto SaveConfig; + + case IDM_OPTIONS_MINONRUN: + bMinOnRun = !bMinOnRun; + UpdateChecks(bMinOnRun, IDM_OPTIONS, IDM_OPTIONS_MINONRUN); + goto SaveConfig; + + case IDM_OPTIONS_TOPMOST: + bTopMost = !bTopMost; + SetWindowPos(g_hWndProgMgr, bTopMost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + UpdateChecks(bTopMost, IDM_OPTIONS, IDM_OPTIONS_TOPMOST); + goto SaveConfig; + + case IDM_OPTIONS_SHOWUSERNAME: + bShowUsername = !bShowUsername; + UpdateWindowTitle(); + UpdateChecks(bShowUsername, IDM_OPTIONS, IDM_OPTIONS_SHOWUSERNAME); + goto SaveConfig; + + case IDM_OPTIONS_SAVESETTINGS: + bSaveSettings = !bSaveSettings; + UpdateChecks(bSaveSettings, IDM_OPTIONS, IDM_OPTIONS_SAVESETTINGS); + goto SaveConfig; + + case IDM_OPTIONS_SAVENOW: + SaveConfig: + SaveConfig(TRUE, TRUE, TRUE, FALSE); + break; + + case IDM_WINDOW_CASCADE: + SendMessage(hWndMDIClient, WM_MDICASCADE, 0, 0); + break; + + case IDM_WINDOW_TILE: + SendMessage(hWndMDIClient, WM_MDITILE, MDITILE_VERTICAL, 0); + break; + + case IDM_WINDOW_TILEHORIZONTALLY: + SendMessage(hWndMDIClient, WM_MDITILE, MDITILE_HORIZONTAL, 0); + break; + + case IDM_WINDOW_ARRANGEICONS: + SendMessage(hWndMDIClient, WM_MDIICONARRANGE, 0, 0); + break; + + case IDM_HELP_INDEX: + ShellExecute(NULL, TEXT("open"), g_szWebsite, NULL, NULL, SW_SHOWNORMAL); + break; + + case IDM_HELP_ABOUT: + { + WCHAR szTitle[40]; + + LoadString(g_hAppInstance, IDS_APPTITLE, szTitle, ARRAYSIZE(szTitle)); + ShellAbout(g_hWndProgMgr, szTitle, NULL, g_hProgMgrIcon); + break; + } + + default: + if (GET_WM_COMMAND_ID(wParam, lParam) >= IDM_WINDOW_CHILDSTART) + { + DefFrameProc(hWnd, hWndMDIClient, 0, wParam, lParam); + } + else + { + HWND hChild = (HWND)SendMessage(hWndMDIClient, WM_MDIGETACTIVE, 0, 0); + if (hChild) + { + SendMessage(hChild, WM_COMMAND, wParam, lParam); + } + } + return FALSE; + } + + return TRUE; +} + +/* * * *\ + UpdateChecks - + Updated checkmarks for a menu based on input + RETURNS - + Nothing! +\* * * */ +VOID UpdateChecks(BOOL bVarMenu, UINT uSubMenu, UINT uID) +{ + HMENU hMenu; + + hMenu = GetMenu(g_hWndProgMgr); + CheckMenuItem(hMenu, uID, (WORD)(bVarMenu ? MF_CHECKED : MF_UNCHECKED)); + + return; +} + +/* * * *\ + UpdateWindowTitle - + Updates Window title based on settings... and + current foreground group window if applicable? + RETURNS - + Nothing! +\* * * */ +VOID UpdateWindowTitle(VOID) +{ + WCHAR szUsername[UNLEN + 1] = TEXT(""); + DWORD dwUsernameLen = UNLEN; + WCHAR szWindowTitle[UNLEN + ARRAYSIZE(g_szAppTitle) + 4] = TEXT(""); + + // Get user and domain name + GetUserNameEx(NameSamCompatible, szUsername, &dwUsernameLen); + + // Add username to window title if settings permit + StringCchCopy(szWindowTitle, ARRAYSIZE(g_szAppTitle), g_szAppTitle); + + if (bShowUsername) + { + StringCchCat(szWindowTitle, ARRAYSIZE(szWindowTitle), TEXT(" - ")); + StringCchCat(szWindowTitle, ARRAYSIZE(szWindowTitle), szUsername); + } + + SetWindowText(g_hWndProgMgr, (LPCWSTR)&szWindowTitle); + + return; +}