Skip to content

Commit

Permalink
Add CSV export
Browse files Browse the repository at this point in the history
  • Loading branch information
learn-more committed Sep 28, 2023
1 parent 76a321d commit 095704e
Show file tree
Hide file tree
Showing 15 changed files with 187 additions and 2 deletions.
Binary file added docs/WindowsHookEx-ExportCSV.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions docs/assets/css/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@
width: 120px !important;
}

sup
{
font-size: 75%;
vertical-align: super;
}
8 changes: 7 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ Additionally, when working on [ReactOS](https://reactos.org) it is a good tool t
* Hook messages received, with decoded arguments
* All received events include the process name and Thread ID on which they are received!
* Show processes where the hook dll is loaded
* Allows exporting the recorded hooks to CSV [^1]
* An Atom viewer, showing [Global and User Atoms](https://docs.microsoft.com/en-us/windows/win32/dataxchg/about-atom-tables).
* Highlight Added Atoms (green)
* Highlight Removed Atoms (red)
* Works on Windows XP / Server 2003 and newer

[^1]: New in version 1.8.0.


## Screenshots
Expand All @@ -56,7 +58,11 @@ Available options:


Hook types:
![Hook types](WindowsHookEx-HookTypes.png)
![Hook types](WindowsHookEx-HookTypes.png)


CSV Export:
![CSV Export](WindowsHookEx-ExportCSV.png)


Atom viewer:
Expand Down
2 changes: 1 addition & 1 deletion src/CAboutWindow.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//
//
// WindowsHookEx - Test the behavior of the api SetWindowsHookEx
// Copyright (c) 2020 Mark Jansen
// UI Framework: Wizard-2020 Example from https://building.enlyze.com/posts/writing-win32-apps-like-its-2020-part-1
Expand Down
52 changes: 52 additions & 0 deletions src/CHookOutputPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@

#include "Wizard-2020.h"
#include <cassert>
#include <algorithm>
#include "HookDll/Shared.h"

#define IDC_MENU_CLEAR_LIST 600
#define IDC_MENU_EXPORT_CSV 601


const static int g_ColumnWidths[] = {
Expand Down Expand Up @@ -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);
}

Expand All @@ -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;
Expand Down Expand Up @@ -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());
}
}
}
1 change: 1 addition & 0 deletions src/CHookOutputPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
2 changes: 2 additions & 0 deletions src/WindowsHookEx.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@
<ClCompile Include="CMainWindow.cpp" />
<ClCompile Include="CHookOutputPage.cpp" />
<ClCompile Include="AlternateCallbacks.cpp" />
<ClCompile Include="csv_export.cpp" />
<ClCompile Include="Wizard-2020.cpp" />
<ClCompile Include="utils.cpp" />
</ItemGroup>
Expand All @@ -239,6 +240,7 @@
<ClInclude Include="CMainWindow.h" />
<ClInclude Include="CPage.h" />
<ClInclude Include="CHookOutputPage.h" />
<ClInclude Include="csv_export.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="version.h" />
<ClInclude Include="Wizard-2020.h" />
Expand Down
6 changes: 6 additions & 0 deletions src/WindowsHookEx.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
<ClCompile Include="AlternateCallbacks.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="csv_export.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="targetver.h">
Expand Down Expand Up @@ -76,6 +79,9 @@
<ClInclude Include="version.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="csv_export.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="lang\en-US.rc">
Expand Down
1 change: 1 addition & 0 deletions src/Wizard-2020.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include "unique_resource.h"
#include "win32_wrappers.h"
#include "csv_export.h"

#include "resource.h"
#include "utils.h"
Expand Down
80 changes: 80 additions & 0 deletions src/csv_export.cpp
Original file line number Diff line number Diff line change
@@ -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");
}
}
22 changes: 22 additions & 0 deletions src/csv_export.h
Original file line number Diff line number Diff line change
@@ -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);

Binary file modified src/lang/en-US.rc
Binary file not shown.
3 changes: 3 additions & 0 deletions src/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

6 changes: 6 additions & 0 deletions src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
1 change: 1 addition & 0 deletions src/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Gdiplus::Bitmap> LoadPNGAsGdiplusBitmap(HINSTANCE hInstance, UINT uID);
Expand Down

0 comments on commit 095704e

Please sign in to comment.