From 095704eb3f874a31590a02e1fe7be9b7c7bec8ed Mon Sep 17 00:00:00 2001 From: Mark Jansen Date: Thu, 28 Sep 2023 22:14:54 +0200 Subject: [PATCH] Add CSV export --- docs/WindowsHookEx-ExportCSV.png | Bin 0 -> 22707 bytes docs/assets/css/style.scss | 5 ++ docs/index.md | 8 ++- src/CAboutWindow.cpp | 2 +- src/CHookOutputPage.cpp | 52 +++++++++++++++++++ src/CHookOutputPage.h | 1 + src/WindowsHookEx.vcxproj | 2 + src/WindowsHookEx.vcxproj.filters | 6 +++ src/Wizard-2020.h | 1 + src/csv_export.cpp | 80 ++++++++++++++++++++++++++++++ src/csv_export.h | 22 ++++++++ src/lang/en-US.rc | Bin 2142 -> 4662 bytes src/resource.h | 3 ++ src/utils.cpp | 6 +++ src/utils.h | 1 + 15 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 docs/WindowsHookEx-ExportCSV.png create mode 100644 src/csv_export.cpp create mode 100644 src/csv_export.h diff --git a/docs/WindowsHookEx-ExportCSV.png b/docs/WindowsHookEx-ExportCSV.png new file mode 100644 index 0000000000000000000000000000000000000000..6a4c8087a2c03eb29c250e9166f9f78a5507ac7b GIT binary patch literal 22707 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 Uskc@DmIi@XncJFGp1T?MUu-F| +#include #include "HookDll/Shared.h" #define IDC_MENU_CLEAR_LIST 600 +#define IDC_MENU_EXPORT_CSV 601 const static int g_ColumnWidths[] = { @@ -193,8 +195,10 @@ void CHookOutputPage::UpdateMenu(HMENU hMenu) { std::wstring clearText = LoadStringAsWstr(m_pMainWindow->GetHInstance(), IDS_MENU_CLEAR_LIST); + std::wstring exportText = LoadStringAsWstr(m_pMainWindow->GetHInstance(), IDS_MENU_EXPORT_CSV); AppendMenuW(hMenu, MF_STRING, IDC_MENU_CLEAR_LIST, clearText.c_str()); + AppendMenuW(hMenu, MF_STRING, IDC_MENU_EXPORT_CSV, exportText.c_str()); AppendMenuW(hMenu, MF_SEPARATOR, 0, nullptr); } @@ -206,6 +210,9 @@ CHookOutputPage::_OnCommand(WPARAM wParam) case IDC_MENU_CLEAR_LIST: ListView_DeleteAllItems(m_hList); break; + case IDC_MENU_EXPORT_CSV: + _OnExportCsv(); + break; } return 0; @@ -295,3 +302,48 @@ CHookOutputPage::OnTimer(WPARAM wParam) return 0; } + +void +CHookOutputPage::_OnExportCsv() +{ + std::wstring wstrSaveFilter = LoadStringAsWstr(m_pMainWindow->GetHInstance(), IDS_SAVE_FILTER); + std::replace(wstrSaveFilter.begin(), wstrSaveFilter.end(), L'|', L'\0'); + + std::wstring wstrSaveTitle = LoadStringAsWstr(m_pMainWindow->GetHInstance(), IDS_SAVE_TITLE); + + + std::wstring wstrFileToSave = std::wstring(MAX_PATH, L'\0'); + + OPENFILENAMEW ofn = {}; + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = m_hWnd; + ofn.lpstrFilter = wstrSaveFilter.c_str(); + ofn.lpstrFile = wstrFileToSave.data(); + ofn.nMaxFile = MAX_PATH; + ofn.lpstrTitle = wstrSaveTitle.c_str(); + ofn.lpstrDefExt = L".csv"; + + if (GetSaveFileNameW(&ofn)) + { + auto pos = wstrFileToSave.find(L'\0'); + if (pos != std::wstring::npos) + wstrFileToSave.resize(pos); + + auto hFile = make_unique_handle(CreateFileW(wstrFileToSave.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr)); + if (hFile.get() == INVALID_HANDLE_VALUE) + { + ErrorBox(L"Unable to open file.\r\nError: " + std::to_wstring(GetLastError())); + return; + } + try + { + export_csv(hFile.get(), m_hList); + } + catch (const export_error& err) + { + std::string tmpA = err.what(); + std::wstring tmpW(tmpA.begin(), tmpA.end()); + ErrorBox(tmpW.c_str()); + } + } +} diff --git a/src/CHookOutputPage.h b/src/CHookOutputPage.h index 638c049..badbbcb 100644 --- a/src/CHookOutputPage.h +++ b/src/CHookOutputPage.h @@ -42,5 +42,6 @@ class CHookOutputPage : public CPage LRESULT _OnDestroy(); LRESULT _OnCommand(WPARAM wParam); LRESULT _OnSize(); + void _OnExportCsv(); static LRESULT CALLBACK _WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); }; diff --git a/src/WindowsHookEx.vcxproj b/src/WindowsHookEx.vcxproj index 11caa33..f1eaf79 100644 --- a/src/WindowsHookEx.vcxproj +++ b/src/WindowsHookEx.vcxproj @@ -229,6 +229,7 @@ + @@ -239,6 +240,7 @@ + diff --git a/src/WindowsHookEx.vcxproj.filters b/src/WindowsHookEx.vcxproj.filters index 3482cd4..5c16472 100644 --- a/src/WindowsHookEx.vcxproj.filters +++ b/src/WindowsHookEx.vcxproj.filters @@ -38,6 +38,9 @@ Source Files + + Source Files + @@ -76,6 +79,9 @@ Header Files + + Header Files + diff --git a/src/Wizard-2020.h b/src/Wizard-2020.h index b4ebc92..dd6a50d 100644 --- a/src/Wizard-2020.h +++ b/src/Wizard-2020.h @@ -30,6 +30,7 @@ #include "unique_resource.h" #include "win32_wrappers.h" +#include "csv_export.h" #include "resource.h" #include "utils.h" diff --git a/src/csv_export.cpp b/src/csv_export.cpp new file mode 100644 index 0000000..92809fd --- /dev/null +++ b/src/csv_export.cpp @@ -0,0 +1,80 @@ +// +// WindowsHookEx - Test the behavior of the api SetWindowsHookEx +// Copyright (c) 2023 Mark Jansen +// UI Framework: Wizard-2020 Example from https://building.enlyze.com/posts/writing-win32-apps-like-its-2020-part-1 +// Copyright (c) 2020 Colin Finck, ENLYZE GmbH +// SPDX-License-Identifier: MIT +// + +#include "Wizard-2020.h" + +static void +write(HANDLE hFile, const char* text) +{ + DWORD cbWritten; + DWORD cbLen = (DWORD)strlen(text); + if (!WriteFile(hFile, text, cbLen, &cbWritten, nullptr)) + throw export_error("Unable to write data.\r\nError: " + std::to_string(GetLastError())); + if (cbWritten != cbLen) + throw export_error("Could not write all data."); +} + +inline static BOOL +ListView_GetColumnA(HWND hwnd, int columnCount, LVCOLUMNA& column) +{ + return (BOOL)SendMessageA(hwnd, LVM_GETCOLUMNA, (WPARAM)columnCount, (LPARAM)&column); +} + +inline static BOOL +ListView_GetItemA(HWND hwnd, LV_ITEMA& item) +{ + return (BOOL)SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); +} + +void +export_csv(HANDLE hFile, HWND hList) +{ + LVCOLUMNA column = { LVCF_TEXT }; + std::string Buffer(MAX_PATH, '\0'); + column.pszText = Buffer.data(); + column.cchTextMax = (int)Buffer.size(); + int columnCount = 0; + + write(hFile, "sep=,\r\n"); // Tell excel that we use a 'comma' as delimiter + for (; ListView_GetColumnA(hList, columnCount, column); columnCount++) + { + // Each cell in the header is quoted, so we don't have to check for comma's in the data + if (!columnCount) + write(hFile, "\""); + else + write(hFile, "\",\""); + write(hFile, Buffer.c_str()); + } + write(hFile, "\"\r\n"); + + const int itemCount = ListView_GetItemCount(hList); + Buffer.resize(512); + for (int index = 0; index < itemCount; index++) + { + LV_ITEMA item = {}; + for (int col = 0; col < columnCount; ++col) + { + item.iItem = index; + item.iSubItem = col; + item.mask = LVIF_TEXT; + item.pszText = Buffer.data(); + item.cchTextMax = (int)Buffer.size(); + if (ListView_GetItemA(hList, item)) + { + // Each cell in the data is quoted, so we don't have to check for comma's in the data + if (!col) + write(hFile, "\""); + else + write(hFile, "\",\""); + + write(hFile, Buffer.c_str()); + } + } + write(hFile, "\"\r\n"); + } +} diff --git a/src/csv_export.h b/src/csv_export.h new file mode 100644 index 0000000..dcb0653 --- /dev/null +++ b/src/csv_export.h @@ -0,0 +1,22 @@ +// +// WindowsHookEx - Test the behavior of the api SetWindowsHookEx +// Copyright (c) 2023 Mark Jansen +// UI Framework: Wizard-2020 Example from https://building.enlyze.com/posts/writing-win32-apps-like-its-2020-part-1 +// Copyright (c) 2020 Colin Finck, ENLYZE GmbH +// SPDX-License-Identifier: MIT +// + +#pragma once + +class export_error : public std::runtime_error +{ +public: + export_error(const std::string& error) + :runtime_error(error) + { + } +}; + +void +export_csv(HANDLE hFile, HWND hList); + diff --git a/src/lang/en-US.rc b/src/lang/en-US.rc index d343fe0cb01c44b61e7401ba3af4e1fe61a181d8..8a615531f2a7c9408085ddee738e9b64000cf1ac 100644 GIT binary patch literal 4662 zcmcJTYi}Ay6o${|O8pP3l`6$miQDE|RaNo@6B=wHz)9LKD{P2kg%|`lwxj%^{>wh^ z8ODq8EZ|;*U}ooX&U@aOb6Iw6*S@uH_}#V4-rA*I*}%qjV>5e6JGPHjw_E0Fe0w%z zd`@p}ALwUx#_tE~+h=x;ZOX{l-Y?DfZNf}uUDnOl$S*M*AYo#6$hx!(WJ}vKJGUL$ zpY2Eg))u`Nj2)wW3d+oWr*{RuA-|=(Q)@DJ#G1Z+V(!Lqezad0mAzkl{{Z`<^vo^R z>WPimkzvu4U7zR)&z$iY`^DLrT_a-zXJYQcp1bxD_`lG54sPw+Wyjc=o6)~<+vbZM zHJ-Ki1^R1XnYi>CGB2@e$k-(sXG{2MtdPxf#(r9|bQ|U}(<;fna#qxyqvr>n>ULr+ z`@{aEmstbcXWW63$*#S&LwkqT7W>cLQn6iw;ekKNruTe@pcB8x%=GRhNRqxS+e1^s zPH7viD~=S7p}~qKH|C|I>auSCe$VQPg4wQ1mEzquFlW+5Rg}((4 zDHy5KKjgQwVoi>HLr!zZ9aOCB^HtQja!T#$d=<=e7(GDRL#+3nw*dj|pwp(32z@AYB*Aab_cyt2R4rf}> zzU8U#+uBHAmuEG}FIQ-sI+7B0bUz=n9z1v7HT6ADi!FItd0V7ane@UfP!(}Q3=y7kO=)jn zS7#(!6~}ZI!kxa|k@3feJ);`S@TJ-jpj1#;!_DGckz zl-!@ghVJ$mt#V#Gh2S6OoS}Rpolv7v48bO&hZH4MK zW%KGhxGFb1>t=pfWSST`jZ+m5yM4dCxX|RU@h}#}!PlktmwdG!Anb8&d82{;>e?NsLP(&_x_*2jQ>k^?Ca9gSDrQ@cuKa^$v?Dm ixfWE#H(k_BrQfKU3U!&@A(a2iSSfGP8`hz}cENYd z=BjS^?21Zd|9VFpxT4@CjCq;u9)JY?AAb^<>{rtUA1y{-ZJ%chW!qlPO2yZl$rqS= zu?F;e{hsdxlf;=2&_+sviSI?z2Z)lPxsWE221oJI3nsBM^hv)rBoi;__W&Pn6s1GQ zooT!4v3`Rg&30Cc4#?NI3#r#EeSYxSK)X)h62BX7xrL~i0Umn+hHwX*lv^Y>(BWOt zC?uR2$TwU&3lNnybOf!aa1p1|Fw(DZmM)f|+XikWHA6)WVI@Vz@`Gf6^{89!k-cfVR*dGl z?JmoCQ5dQ>NSk7v%!4p%4_g?@=eg z4f+40K9v7XKK7QF{%dCbn|#eBj=e^GMvG#Fou@;<^T-aW38FpTraIHui+y6`=4_rz z6n-&F`i#FRuIoOl9iD&x)jd0n1fH4N8okn>fz^IHn?dPGeo(yN>Vzal0N9In^drp) z%WwZvU+Vr%J+l~E=az0_Q)a`jCti!WSXN9|g*kc};=q}AV8IITy10=(@x!6xH`C<8 zc>P+FLaeCSDM>_@-B`>QHlpm})=himycj>Yn+h=^^+OlcbnXDl7g$P{9c}jYJRs5S zEWEl|=#fyH)e}##8N2fiR=mZAG+V4&%9*~oBMk9gFb*${-+wKyTnhu+406{e&N9Wz V?shigZNjL8g4gHSOoCCr_YZuRAMyYI diff --git a/src/resource.h b/src/resource.h index d4c19e2..40d20b0 100644 --- a/src/resource.h +++ b/src/resource.h @@ -51,4 +51,7 @@ #define IDS_HOOK 1502 #define IDS_INFO 1503 #define IDS_MENU_CLEAR_LIST 1504 +#define IDS_MENU_EXPORT_CSV 1505 +#define IDS_SAVE_FILTER 1506 +#define IDS_SAVE_TITLE 1507 diff --git a/src/utils.cpp b/src/utils.cpp index 8fb6208..fc8d552 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -14,6 +14,12 @@ typedef HRESULT (WINAPI *PGetDpiForMonitor)(HMONITOR hmonitor, int dpiType, UINT #define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 #endif +void +ErrorBox(const std::wstring& wstrMessage) +{ + MessageBoxW(nullptr, wstrMessage.c_str(), wszAppName, MB_ICONERROR); +} + WORD GetWindowDPI(HWND hWnd) { diff --git a/src/utils.h b/src/utils.h index fc569e3..d3e8200 100644 --- a/src/utils.h +++ b/src/utils.h @@ -33,6 +33,7 @@ InstanceFromWndProc(HWND hWnd, UINT uMsg, LPARAM lParam) } // utils.cpp +void ErrorBox(const std::wstring& wstrMessage); WORD GetWindowDPI(HWND hWnd); std::wstring LoadStringAsWstr(HINSTANCE hInstance, UINT uID); std::unique_ptr LoadPNGAsGdiplusBitmap(HINSTANCE hInstance, UINT uID);