From 3529151141afc4e31ac8d55aa8225add0385b74c Mon Sep 17 00:00:00 2001 From: Mark Jansen Date: Sat, 13 Dec 2025 02:13:12 +0100 Subject: [PATCH] [MSCUTILS] Add very basic certmgr --- base/applications/mscutils/CMakeLists.txt | 1 + base/applications/mscutils/certmgr/CCert.hpp | 85 +++++++++ .../mscutils/certmgr/CMakeLists.txt | 19 ++ .../mscutils/certmgr/CSortableHeader.hpp | 105 +++++++++++ .../mscutils/certmgr/CSplitter.hpp | 117 ++++++++++++ base/applications/mscutils/certmgr/CStore.hpp | 93 ++++++++++ .../mscutils/certmgr/CStoreList.cpp | 49 ++++++ .../mscutils/certmgr/CStoreList.hpp | 51 ++++++ .../applications/mscutils/certmgr/certmgr.cpp | 166 ++++++++++++++++++ base/applications/mscutils/certmgr/certmgr.h | 62 +++++++ base/applications/mscutils/certmgr/certmgr.rc | 40 +++++ base/applications/mscutils/certmgr/resource.h | 16 ++ .../mscutils/certmgr/resources/certmgr.ico | Bin 0 -> 9062 bytes 13 files changed, 804 insertions(+) create mode 100644 base/applications/mscutils/certmgr/CCert.hpp create mode 100644 base/applications/mscutils/certmgr/CMakeLists.txt create mode 100644 base/applications/mscutils/certmgr/CSortableHeader.hpp create mode 100644 base/applications/mscutils/certmgr/CSplitter.hpp create mode 100644 base/applications/mscutils/certmgr/CStore.hpp create mode 100644 base/applications/mscutils/certmgr/CStoreList.cpp create mode 100644 base/applications/mscutils/certmgr/CStoreList.hpp create mode 100644 base/applications/mscutils/certmgr/certmgr.cpp create mode 100644 base/applications/mscutils/certmgr/certmgr.h create mode 100644 base/applications/mscutils/certmgr/certmgr.rc create mode 100644 base/applications/mscutils/certmgr/resource.h create mode 100644 base/applications/mscutils/certmgr/resources/certmgr.ico diff --git a/base/applications/mscutils/CMakeLists.txt b/base/applications/mscutils/CMakeLists.txt index cd3e5511e7c..f6928976b23 100644 --- a/base/applications/mscutils/CMakeLists.txt +++ b/base/applications/mscutils/CMakeLists.txt @@ -1,4 +1,5 @@ +add_subdirectory(certmgr) add_subdirectory(devmgmt) add_subdirectory(eventvwr) add_subdirectory(servman) \ No newline at end of file diff --git a/base/applications/mscutils/certmgr/CCert.hpp b/base/applications/mscutils/certmgr/CCert.hpp new file mode 100644 index 00000000000..97f60912a9e --- /dev/null +++ b/base/applications/mscutils/certmgr/CCert.hpp @@ -0,0 +1,85 @@ +/* + * PROJECT: ReactOS Certificate Manager + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: CCert definition + * COPYRIGHT: Copyright 2025 Mark Jansen + */ + +class CCert +{ + private: + PCCERT_CONTEXT m_CertContext = NULL; + CAtlStringW m_SubjectName; + CAtlStringW m_IssuerName; + CAtlStringW m_FriendlyName; + + void + GetNameString(DWORD dwFlags, CAtlStringW &out) + { + DWORD length = CertGetNameStringW(m_CertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, dwFlags, NULL, NULL, 0); + if (length > 1) + { + WCHAR *buffer = out.GetBuffer(length); + CertGetNameStringW(m_CertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, dwFlags, NULL, buffer, length); + out.ReleaseBuffer(); + } + } + + public: + explicit CCert(_In_ PCCERT_CONTEXT certContext) + { + m_CertContext = CertDuplicateCertificateContext(certContext); + Init(); + } + ~CCert() + { + if (m_CertContext) + { + CertFreeCertificateContext(m_CertContext); + } + } + + const CAtlStringW & + GetSubjectName() const + { + return m_SubjectName; + } + + const CAtlStringW & + GetIssuerName() const + { + return m_IssuerName; + } + + FILETIME + GetNotAfter() const + { + return (m_CertContext && m_CertContext->pCertInfo) ? m_CertContext->pCertInfo->NotAfter : FILETIME{}; + } + + const CAtlStringW & + GetFriendlyName() const + { + return m_FriendlyName; + } + + const PCCERT_CONTEXT + GetCertContext() const + { + return m_CertContext; + } + + void + Init() + { + GetNameString(0, m_SubjectName); + GetNameString(CERT_NAME_ISSUER_FLAG, m_IssuerName); + DWORD length = 0; + if (CertGetCertificateContextProperty(m_CertContext, CERT_FRIENDLY_NAME_PROP_ID, NULL, &length) && length > 0) + { + WCHAR *buffer = m_FriendlyName.GetBuffer(length / sizeof(WCHAR)); + CertGetCertificateContextProperty(m_CertContext, CERT_FRIENDLY_NAME_PROP_ID, buffer, &length); + m_FriendlyName.ReleaseBuffer(); + } + } +}; diff --git a/base/applications/mscutils/certmgr/CMakeLists.txt b/base/applications/mscutils/certmgr/CMakeLists.txt new file mode 100644 index 00000000000..0ce160db56b --- /dev/null +++ b/base/applications/mscutils/certmgr/CMakeLists.txt @@ -0,0 +1,19 @@ + +add_executable(certmgr + certmgr.cpp + certmgr.h + certmgr.rc + resource.h + + CCert.hpp + CStore.hpp + CStoreList.cpp + CStoreList.hpp + CSplitter.hpp + CSortableHeader.hpp +) +set_module_type(certmgr win32gui UNICODE) +target_link_libraries(certmgr uuid cpprt atl_classes) +add_importlibs(certmgr cryptui crypt32 ole32 user32 msvcrt kernel32 ntdll) +add_dependencies(certmgr psdk) +add_cd_file(TARGET certmgr DESTINATION reactos/system32 FOR all) diff --git a/base/applications/mscutils/certmgr/CSortableHeader.hpp b/base/applications/mscutils/certmgr/CSortableHeader.hpp new file mode 100644 index 00000000000..6145c58d7d8 --- /dev/null +++ b/base/applications/mscutils/certmgr/CSortableHeader.hpp @@ -0,0 +1,105 @@ +/* + * PROJECT: ReactOS Certificate Manager + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: CSortableHeader definition + * COPYRIGHT: Copyright 2025 Mark Jansen + */ + +struct CSortableHeader +{ + private: + int m_LastHeaderID = 0; + bool m_Ascending = true; + + public: + struct SortContext + { + CSortableHeader *hdr; + CListView *lvw; + INT iSubItem; + }; + + static INT CALLBACK + s_CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) + { + SortContext *ctx = ((SortContext *)lParamSort); + return ctx->hdr->CompareFunc(lParam1, lParam2, ctx); + } + + INT + CompareFunc(LPARAM lParam1, LPARAM lParam2, SortContext *ctx) + { + const INT iSubItem = ctx->iSubItem; +#define MAX_STR_LEN 256 + + CStringW Item1, Item2; + LVFINDINFOW IndexInfo; + INT Index; + + IndexInfo.flags = LVFI_PARAM; + + IndexInfo.lParam = lParam1; + Index = ctx->lvw->FindItem(-1, &IndexInfo); + ctx->lvw->GetItemText(Index, iSubItem, Item1.GetBuffer(MAX_STR_LEN), MAX_STR_LEN); + Item1.ReleaseBuffer(); + + IndexInfo.lParam = lParam2; + Index = ctx->lvw->FindItem(-1, &IndexInfo); + ctx->lvw->GetItemText(Index, iSubItem, Item2.GetBuffer(MAX_STR_LEN), MAX_STR_LEN); + Item2.ReleaseBuffer(); + + return m_Ascending ? Item1.Compare(Item2) : Item2.Compare(Item1); + } + + void + ColumnClick(CListView &listView, LPNMLISTVIEW pnmv) + { + if ((listView.GetWindowLongPtr(GWL_STYLE) & ~LVS_NOSORTHEADER) == 0) + return; + + ApplySorting(listView, pnmv->iSubItem); + + /* Save new values */ + m_LastHeaderID = pnmv->iSubItem; + m_Ascending = !m_Ascending; + } + + void + ReapplySorting(CListView &listView) + { + if (m_LastHeaderID != -1) + { + ApplySorting(listView, m_LastHeaderID); + } + } + + void + ApplySorting(CListView &listView, INT nHeaderID) + { + HWND hHeader = (HWND)listView.SendMessage(LVM_GETHEADER, 0, 0); + HDITEMW hColumn; + ZeroMemory(&hColumn, sizeof(hColumn)); + + /* If the sorting column changed, remove the sorting style from the old column */ + if ((m_LastHeaderID != -1) && (m_LastHeaderID != nHeaderID)) + { + m_Ascending = TRUE; // also reset sorting method to ascending + hColumn.mask = HDI_FORMAT; + Header_GetItem(hHeader, m_LastHeaderID, &hColumn); + hColumn.fmt &= ~(HDF_SORTUP | HDF_SORTDOWN); + Header_SetItem(hHeader, m_LastHeaderID, &hColumn); + } + + /* Set the sorting style to the new column */ + hColumn.mask = HDI_FORMAT; + Header_GetItem(hHeader, nHeaderID, &hColumn); + + hColumn.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP); + hColumn.fmt |= (m_Ascending ? HDF_SORTUP : HDF_SORTDOWN); + Header_SetItem(hHeader, nHeaderID, &hColumn); + + /* Sort the list, using the current values of nHeaderID and bIsAscending */ + SortContext ctx = {this, &listView, nHeaderID}; + listView.SortItems(s_CompareFunc, &ctx); + } +}; diff --git a/base/applications/mscutils/certmgr/CSplitter.hpp b/base/applications/mscutils/certmgr/CSplitter.hpp new file mode 100644 index 00000000000..6e2aa05edb5 --- /dev/null +++ b/base/applications/mscutils/certmgr/CSplitter.hpp @@ -0,0 +1,117 @@ +/* + * PROJECT: ReactOS Certificate Manager + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: CSortableHeader definition + * COPYRIGHT: Copyright 2025 Mark Jansen + */ + +struct CSplitter +{ + private: + CWindow m_Parent; + CWindow m_Left; + CWindow m_Right; + + int m_splitterPos = 280; + const int m_splitterSize = 4; + + public: + void + Init(CWindow parent, CWindow left, CWindow right) + { + m_Parent = parent; + m_Left = left; + m_Right = right; + } + + void + ResizeWnd(int width, int height) + { + RECT rc; + m_Parent.GetClientRect(&rc); + + HDWP hdwp = BeginDeferWindowPos(2); + + if (hdwp) + hdwp = m_Left.DeferWindowPos( + hdwp, NULL, 0, 0, m_splitterPos - m_splitterSize / 2, height, SWP_NOZORDER | SWP_NOACTIVATE); + if (hdwp) + hdwp = m_Right.DeferWindowPos( + hdwp, NULL, m_splitterPos + m_splitterSize / 2, 0, width - (m_splitterPos + m_splitterSize / 2), height, + SWP_NOZORDER | SWP_NOACTIVATE); + if (hdwp) + EndDeferWindowPos(hdwp); + } + + void + DragSplitter(INT x) + { + m_splitterPos = max(x, 120); + RECT rc; + m_Parent.GetClientRect(&rc); + ResizeWnd(rc.right - rc.left, rc.bottom - rc.top); + } + + LRESULT + OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) + { + ResizeWnd(LOWORD(lParam), HIWORD(lParam)); + return TRUE; + } + + LRESULT + OnSetCursor(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) + { + if (LOWORD(lParam) == HTCLIENT) + { + POINT pt; + GetCursorPos(&pt); + m_Parent.ScreenToClient(&pt); + if (pt.x >= m_splitterPos - m_splitterSize / 2 && pt.x <= m_splitterPos + m_splitterSize / 2) + { + SetCursor(LoadCursor(NULL, IDC_SIZEWE)); + bHandled = TRUE; + return TRUE; + } + } + bHandled = FALSE; + return FALSE; + } + + LRESULT + OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) + { + INT x = GET_X_LPARAM(lParam); + + if (x >= m_splitterPos - m_splitterSize / 2 && x <= m_splitterPos + m_splitterSize / 2) + { + m_Parent.SetCapture(); + + bHandled = TRUE; + return TRUE; + } + + return TRUE; + } + + LRESULT + OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) + { + if (GetCapture() == m_Parent.m_hWnd) + { + DragSplitter(GET_X_LPARAM(lParam)); + } + return TRUE; + } + + LRESULT + OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) + { + if (GetCapture() == m_Parent.m_hWnd) + { + DragSplitter(GET_X_LPARAM(lParam)); + ReleaseCapture(); + } + return TRUE; + } +}; diff --git a/base/applications/mscutils/certmgr/CStore.hpp b/base/applications/mscutils/certmgr/CStore.hpp new file mode 100644 index 00000000000..452021943aa --- /dev/null +++ b/base/applications/mscutils/certmgr/CStore.hpp @@ -0,0 +1,93 @@ +/* + * PROJECT: ReactOS Certificate Manager + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: CStore definition + * COPYRIGHT: Copyright 2025 Mark Jansen + */ + +class CStore +{ + private: + CAtlStringW m_CryptName; + DWORD m_dwFlags; + CAtlStringW m_StoreName; + HCERTSTORE m_hStore = NULL; + CAtlList m_Certificates; + bool m_Expanded = false; + + HCERTSTORE + StoreHandle() + { + if (!m_hStore) + { + m_hStore = CertOpenStore( + CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, NULL, + CERT_STORE_SET_LOCALIZED_NAME_FLAG | m_dwFlags | CERT_STORE_MAXIMUM_ALLOWED_FLAG, m_CryptName); + } + return m_hStore; + } + + public: + CStore(_In_ LPCWSTR cryptName, DWORD dwFlags) : m_CryptName(cryptName), m_dwFlags(dwFlags) + { + if (auto localized = CryptFindLocalizedName(cryptName)) + { + m_StoreName = localized; + } + else + { + m_StoreName = cryptName; + } + } + ~CStore() + { + if (m_hStore) + { + CertCloseStore(m_hStore, 0); + } + } + + LPCWSTR + GetStoreName() const + { + return m_StoreName; + } + + void + Expand() + { + if (m_Expanded) + return; + m_Expanded = true; + // EnumCertificateRevocationLists() + // EnumCertificateTrustLists() + EnumCertificates(); + } + + void + EnumCertificates() + { + auto handle = StoreHandle(); + if (!handle) + return; + PCCERT_CONTEXT pContext = NULL; + while ((pContext = CertEnumCertificatesInStore(handle, pContext))) + { + CCert *newCert = new CCert(pContext); + m_Certificates.AddTail(newCert); + } + } + + template + void + ForEach(Fn callback) + { + Expand(); + + for (POSITION it = m_Certificates.GetHeadPosition(); it; m_Certificates.GetNext(it)) + { + auto current = m_Certificates.GetAt(it); + callback(current); + } + } +}; diff --git a/base/applications/mscutils/certmgr/CStoreList.cpp b/base/applications/mscutils/certmgr/CStoreList.cpp new file mode 100644 index 00000000000..4ff9e7d8b97 --- /dev/null +++ b/base/applications/mscutils/certmgr/CStoreList.cpp @@ -0,0 +1,49 @@ +/* + * PROJECT: ReactOS Certificate Manager + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: CStoreList implementation + * COPYRIGHT: Copyright 2025 Mark Jansen + */ + +#include "certmgr.h" + +CStoreList::CStoreList(StoreType type) : m_Type(type) +{ +} + +DWORD +CStoreList::StoreTypeFlags() const +{ + switch (m_Type) + { + case StoreType::User: + return CERT_SYSTEM_STORE_CURRENT_USER; + case StoreType::Service: + return CERT_SYSTEM_STORE_SERVICES; + case StoreType::Computer: + return CERT_SYSTEM_STORE_LOCAL_MACHINE; + default: + return 0; + } +} + +BOOL CALLBACK +CStoreList::s_StoreCallback( + const void *pvSystemStore, + DWORD dwFlags, + PCERT_SYSTEM_STORE_INFO pStoreInfo, + void *pvReserved, + void *pvArg) +{ + CStoreList *storeList = static_cast(pvArg); + const wchar_t *storeName = static_cast(pvSystemStore); + CStore *newStore = new CStore(storeName, storeList->StoreTypeFlags()); + storeList->m_Stores.AddTail(newStore); + return TRUE; // Continue enumeration +} + +void +CStoreList::LoadStores() +{ + CertEnumSystemStore(StoreTypeFlags(), NULL, this, s_StoreCallback); +} diff --git a/base/applications/mscutils/certmgr/CStoreList.hpp b/base/applications/mscutils/certmgr/CStoreList.hpp new file mode 100644 index 00000000000..da371f47a4c --- /dev/null +++ b/base/applications/mscutils/certmgr/CStoreList.hpp @@ -0,0 +1,51 @@ +/* + * PROJECT: ReactOS Certificate Manager + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: CStoreList definition + * COPYRIGHT: Copyright 2025 Mark Jansen + */ + +enum class StoreType +{ + User, + Service, + Computer, +}; + +class CStoreList +{ + private: + CAtlList m_Stores; + StoreType m_Type; + + static BOOL CALLBACK + s_StoreCallback( + const void *pvSystemStore, + DWORD dwFlags, + PCERT_SYSTEM_STORE_INFO pStoreInfo, + void *pvReserved, + void *pvArg); + + public: + explicit CStoreList(StoreType type); + + void + LoadStores(); + DWORD + StoreTypeFlags() const; + + template + void + ForEach(Fn callback) + { + if (m_Stores.IsEmpty()) + LoadStores(); + + for (POSITION it = m_Stores.GetHeadPosition(); it; m_Stores.GetNext(it)) + { + CStore *current = m_Stores.GetAt(it); + + callback(current); + } + } +}; diff --git a/base/applications/mscutils/certmgr/certmgr.cpp b/base/applications/mscutils/certmgr/certmgr.cpp new file mode 100644 index 00000000000..a84c6d4481d --- /dev/null +++ b/base/applications/mscutils/certmgr/certmgr.cpp @@ -0,0 +1,166 @@ +/* + * PROJECT: ReactOS Certificate Manager + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: + * COPYRIGHT: Copyright 2025 Mark Jansen + */ + +#include "certmgr.h" + +struct CCertMgrProperties : public CDialogImpl +{ + enum + { + IDD = IDD_CERTMGR_MAIN + }; + CTreeView m_StoreTree; + CListView m_CertList; + CSplitter m_Splitter; + CSortableHeader m_SortableHeader; + + CStoreList m_UserStoreList; + CStoreList m_ComputerStoreList; + + CCertMgrProperties() : m_UserStoreList(StoreType::User), m_ComputerStoreList(StoreType::Computer) + { + + } + + LRESULT + OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled) + { + HICON hIcon = (HICON)::LoadImageW(_AtlBaseModule.GetResourceInstance(), MAKEINTRESOURCEW(IDI_CERTMGR), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); + SetIcon(hIcon, TRUE); + SetIcon(hIcon, FALSE); + + m_StoreTree.Attach(GetDlgItem(IDC_STORE_TREE)); + m_CertList.Attach(GetDlgItem(IDC_CERT_LIST)); + m_Splitter.Init(m_hWnd, m_StoreTree.m_hWnd, m_CertList.m_hWnd); + + ListView_SetExtendedListViewStyleEx(m_CertList, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); + + auto certRoot = m_StoreTree.AddItem(NULL, (LPWSTR)L"Certificates", 0, 0, 0); + auto userRoot = m_StoreTree.AddItem(certRoot, (LPWSTR)L"User Store", 0, 0, 0); + m_UserStoreList.ForEach( + [&](const CStore *current) + {m_StoreTree.AddItem(userRoot, (LPWSTR)current->GetStoreName(), 0, 0, (LPARAM)current); }); + auto computerRoot = m_StoreTree.AddItem(certRoot, (LPWSTR)L"Computer Store", 0, 0, 0); + m_ComputerStoreList.ForEach( + [&](const CStore *current) + { m_StoreTree.AddItem(computerRoot, (LPWSTR)current->GetStoreName(), 0, 0, (LPARAM)current); }); + + m_StoreTree.Expand(certRoot, TVE_EXPAND); + + m_CertList.InsertColumn(0, (LPWSTR)L"Issued To", LVCFMT_LEFT, 200); + m_CertList.InsertColumn(1, (LPWSTR)L"Issued By", LVCFMT_LEFT, 200); + m_CertList.InsertColumn(2, (LPWSTR)L"Expiration Date", LVCFMT_LEFT, 100); + m_CertList.InsertColumn(3, (LPWSTR)L"Friendly Name", LVCFMT_LEFT, 200); + + return TRUE; + } + + LRESULT + OnEndDialog(WORD, WORD wID, HWND, BOOL &) + { + EndDialog(wID); + return 0L; + } + + LRESULT + OnStoreChanged(int idCtrl, LPNMHDR pnmh, BOOL &bHandled) + { + if (idCtrl == IDC_STORE_TREE) + { + LPNMTREEVIEW pntv = (LPNMTREEVIEW)pnmh; + CStore *store = (CStore *)pntv->itemNew.lParam; + if (store) + { + m_CertList.DeleteAllItems(); + store->Expand(); + store->ForEach( + [&](const CCert *current) + { + LV_ITEM item = {}; + item.mask = LVIF_TEXT | LVIF_PARAM; + item.lParam = (LPARAM)current; + item.pszText = (LPWSTR)(LPCWSTR)current->GetSubjectName(); + item.iItem = m_CertList.GetItemCount(); + item.iItem = m_CertList.InsertItem(&item); + m_CertList.SetItemText(item.iItem, 1, (LPCWSTR)current->GetIssuerName()); + FILETIME notAfter = current->GetNotAfter(); + SYSTEMTIME st; + FileTimeToSystemTime(¬After, &st); + WCHAR dateStr[100]; + GetDateFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, dateStr, _countof(dateStr)); + m_CertList.SetItemText(item.iItem, 2, dateStr); + m_CertList.SetItemText(item.iItem, 3, (LPCWSTR)current->GetFriendlyName()); + }); + m_SortableHeader.ReapplySorting(m_CertList); + } + } + return 0L; + } + + LRESULT + OnCertDoubleClicked(int idCtrl, LPNMHDR pnmh, BOOL &bHandled) + { + if (idCtrl == IDC_CERT_LIST) + { + auto lpnmitem = (LPNMITEMACTIVATE)pnmh; + if (lpnmitem->iItem < 0) + return 0L; + + const CCert *current = (const CCert *)m_CertList.GetItemData(lpnmitem->iItem); + + CRYPTUI_VIEWCERTIFICATE_STRUCTW CertViewInfo = {0}; + CertViewInfo.dwSize = sizeof(CertViewInfo); + CertViewInfo.pCertContext = current->GetCertContext(); + CertViewInfo.hwndParent = m_hWnd; + CryptUIDlgViewCertificateW(&CertViewInfo, NULL); + } + return 0L; + } + + LRESULT + OnHeaderClicked(int idCtrl, LPNMHDR pnmh, BOOL &bHandled) + { + auto pnmv = (LPNMLISTVIEW)pnmh; + m_SortableHeader.ColumnClick(m_CertList, pnmv); + return 0L; + } + + BEGIN_MSG_MAP(CCertMgrProperties) + MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) + COMMAND_ID_HANDLER(IDCANCEL, OnEndDialog) + NOTIFY_HANDLER(IDC_STORE_TREE, TVN_SELCHANGED, OnStoreChanged) + NOTIFY_HANDLER(IDC_CERT_LIST, NM_DBLCLK, OnCertDoubleClicked) + NOTIFY_HANDLER(IDC_CERT_LIST, LVN_COLUMNCLICK, OnHeaderClicked) + + MESSAGE_HANDLER(WM_SIZE, m_Splitter.OnSize) + MESSAGE_HANDLER(WM_SETCURSOR, m_Splitter.OnSetCursor) + MESSAGE_HANDLER(WM_LBUTTONDOWN, m_Splitter.OnLButtonDown) + MESSAGE_HANDLER(WM_MOUSEMOVE, m_Splitter.OnMouseMove) + MESSAGE_HANDLER(WM_LBUTTONUP, m_Splitter.OnLButtonUp) + END_MSG_MAP() +}; + +class CCertMgrModule : public ATL::CAtlExeModuleT +{ + public: + HRESULT + Run(_In_ int nShowCmd) throw() + { + CStringW Title(MAKEINTRESOURCEW(IDS_PROPERTIES_MAIN_TITLE)); + CCertMgrProperties certMgr; + certMgr.DoModal(); + return S_OK; + } +}; + +CCertMgrModule _AtlModule; + +extern "C" int WINAPI +wWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPWSTR /*lpCmdLine*/, int nShowCmd) +{ + return _AtlModule.WinMain(nShowCmd); +} diff --git a/base/applications/mscutils/certmgr/certmgr.h b/base/applications/mscutils/certmgr/certmgr.h new file mode 100644 index 00000000000..2a553560437 --- /dev/null +++ b/base/applications/mscutils/certmgr/certmgr.h @@ -0,0 +1,62 @@ +/* + * PROJECT: ReactOS Certificate Manager + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Main header file + * COPYRIGHT: Copyright 2025 Mark Jansen + */ + +#pragma once + +#ifndef STRICT +#define STRICT +#endif + +#define _ATL_APARTMENT_THREADED +#define _ATL_NO_AUTOMATIC_NAMESPACE +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit +#define ATL_NO_ASSERT_ON_DESTROY_NONEXISTENT_WINDOW + +#define _FORCENAMELESSUNION + +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include + +using namespace ATL; + +#define NDEBUG +#include +#include +#include + +template class CLocalPtr + : public CHeapPtr +{ +public: + CLocalPtr() throw() + { + } + + explicit CLocalPtr(_In_ T* pData) throw() : + CHeapPtr(pData) + { + } +}; + +#include "resource.h" +#include "CCert.hpp" +#include "CStore.hpp" +#include "CStoreList.hpp" +#include "CSplitter.hpp" +#include "CSortableHeader.hpp" diff --git a/base/applications/mscutils/certmgr/certmgr.rc b/base/applications/mscutils/certmgr/certmgr.rc new file mode 100644 index 00000000000..619060732ae --- /dev/null +++ b/base/applications/mscutils/certmgr/certmgr.rc @@ -0,0 +1,40 @@ +/* + * PROJECT: ReactOS Certificate Manager + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Resources + * COPYRIGHT: Copyright 2025 Mark Jansen + */ + +#include +#include +#include +#include "resource.h" + +#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Certificate Manager" +#define REACTOS_STR_INTERNAL_NAME "certmgr" +#define REACTOS_STR_ORIGINAL_FILENAME "certmgr.exe" +#include + +#include + +IDI_CERTMGR ICON "resources/certmgr.ico" + +#pragma code_page(65001) + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +STRINGTABLE +BEGIN + IDS_PROPERTIES_MAIN_TITLE "Certificate manager" +END + +IDD_CERTMGR_MAIN DIALOGEX 0, 0, 522, 273 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +EXSTYLE WS_EX_CLIENTEDGE | WS_EX_LEFTSCROLLBAR | WS_EX_APPWINDOW +CAPTION "Certificate manager" +FONT 8, "MS Shell Dlg", 400, 0, 0x0 +BEGIN + CONTROL "",IDC_STORE_TREE,"SysTreeView32",TVS_HASBUTTONS | LVS_SORTASCENDING | TVS_HASLINES | TVS_SHOWSELALWAYS | WS_BORDER | WS_HSCROLL | WS_TABSTOP,7,7,158,259 + CONTROL "",IDC_CERT_LIST,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,170,7,345,259 +END + diff --git a/base/applications/mscutils/certmgr/resource.h b/base/applications/mscutils/certmgr/resource.h new file mode 100644 index 00000000000..7d7ae4ca62e --- /dev/null +++ b/base/applications/mscutils/certmgr/resource.h @@ -0,0 +1,16 @@ +/* + * PROJECT: ReactOS Certificate Manager + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Resource definitions + * COPYRIGHT: Copyright 2025 Mark Jansen + */ + +#define IDC_STATIC -1 + +#define IDI_CERTMGR 100 + +#define IDD_CERTMGR_MAIN 200 +#define IDC_STORE_TREE 201 +#define IDC_CERT_LIST 202 + +#define IDS_PROPERTIES_MAIN_TITLE 1000 diff --git a/base/applications/mscutils/certmgr/resources/certmgr.ico b/base/applications/mscutils/certmgr/resources/certmgr.ico new file mode 100644 index 0000000000000000000000000000000000000000..0582af55bb5c87372a4593177203335b075fb935 GIT binary patch literal 9062 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