mirror of
https://github.com/BeneficialCode/WinArk
synced 2026-08-09 12:00:31 +00:00
523 lines
16 KiB
C++
523 lines
16 KiB
C++
#include "stdafx.h"
|
|
#include "KernelNotifyTable.h"
|
|
#include "DriverHelper.h"
|
|
#include <SymbolManager.h>
|
|
#include "Helpers.h"
|
|
#include <filesystem>
|
|
#include "SymbolHelper.h"
|
|
#include "ClipboardHelper.h"
|
|
|
|
CKernelNotifyTable::CKernelNotifyTable(BarInfo& bars, TableInfo& table)
|
|
:CTable(bars, table) {
|
|
SetTableWindowInfo(bars.nbar);
|
|
Refresh();
|
|
}
|
|
|
|
LRESULT CKernelNotifyTable::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CKernelNotifyTable::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lparam, BOOL& /*bHandled*/) {
|
|
|
|
return 0;
|
|
}
|
|
LRESULT CKernelNotifyTable::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
PaintTable(m_hWnd);
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CKernelNotifyTable::OnHScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
Tablefunction(m_hWnd, uMsg, wParam, lParam);
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CKernelNotifyTable::OnVScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
Tablefunction(m_hWnd, uMsg, wParam, lParam);
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CKernelNotifyTable::OnUserVabs(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
Tablefunction(m_hWnd, uMsg, wParam, lParam);
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CKernelNotifyTable::OnUserVrel(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
Tablefunction(m_hWnd, uMsg, wParam, lParam);
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CKernelNotifyTable::OnUserChgs(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
Tablefunction(m_hWnd, uMsg, wParam, lParam);
|
|
return 0;
|
|
}
|
|
|
|
LRESULT CKernelNotifyTable::OnMouseWheel(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
|
|
return Tablefunction(m_hWnd, WM_VSCROLL, zDelta >= 0 ? 0 : 1, wParam);
|
|
}
|
|
LRESULT CKernelNotifyTable::OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
return Tablefunction(m_hWnd, uMsg, wParam, lParam);
|
|
}
|
|
LRESULT CKernelNotifyTable::OnLBtnDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
return Tablefunction(m_hWnd, uMsg, wParam, lParam);
|
|
}
|
|
LRESULT CKernelNotifyTable::OnLBtnUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
return Tablefunction(m_hWnd, uMsg, wParam, lParam);
|
|
}
|
|
LRESULT CKernelNotifyTable::OnRBtnDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
CMenu menu;
|
|
CMenuHandle hSubMenu;
|
|
menu.LoadMenu(IDR_CONTEXT);
|
|
hSubMenu = menu.GetSubMenu(8);
|
|
POINT pt;
|
|
::GetCursorPos(&pt);
|
|
int selected = m_Table.data.selected;
|
|
ATLASSERT(selected >= 0);
|
|
|
|
bool show = Tablefunction(m_hWnd, uMsg, wParam, lParam);
|
|
if (show) {
|
|
auto id = (UINT)TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_hWnd, nullptr);
|
|
if (id) {
|
|
PostMessage(WM_COMMAND, id);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
LRESULT CKernelNotifyTable::OnUserSts(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
return Tablefunction(m_hWnd, uMsg, wParam, lParam);
|
|
}
|
|
LRESULT CKernelNotifyTable::OnWindowPosChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
return Tablefunction(m_hWnd, uMsg, wParam, lParam);
|
|
}
|
|
LRESULT CKernelNotifyTable::OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
return Tablefunction(m_hWnd, uMsg, wParam, lParam);
|
|
}
|
|
LRESULT CKernelNotifyTable::OnSysKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/) {
|
|
return Tablefunction(m_hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
int CKernelNotifyTable::ParseTableEntry(CString& s, char& mask, int& select, CallbackInfo& info, int column) {
|
|
// 回调函数地址 回调类型 所在模块 文件厂商
|
|
switch (column)
|
|
{
|
|
case 0:
|
|
s.Format(L"0x%p", info.Routine);
|
|
break;
|
|
case 1:
|
|
{
|
|
switch (info.Type)
|
|
{
|
|
case CallbackType::CreateProcessNotify:
|
|
s = L"CreateProcess";
|
|
break;
|
|
case CallbackType::CreateThreadNotify:
|
|
s = L"CreateThread";
|
|
break;
|
|
|
|
case CallbackType::LoadImageNotify:
|
|
s = L"LoadImage";
|
|
break;
|
|
|
|
case CallbackType::ProcessObPostOperationNotify:
|
|
s = L"Process ObPostOperation";
|
|
break;
|
|
|
|
case CallbackType::ProcessObPreOperationNotify:
|
|
s = L"Process ObPreOperation";
|
|
break;
|
|
|
|
case CallbackType::ThreadObPostOperationNotify:
|
|
s = L"Thread ObPostOperation";
|
|
break;
|
|
|
|
case CallbackType::ThreadObPreOperationNotify:
|
|
s = L"Thread ObPreOperation";
|
|
break;
|
|
|
|
case CallbackType::RegistryNotify:
|
|
s = L"Registry Notify";
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case 2:
|
|
s = info.Company.c_str();
|
|
break;
|
|
case 3:
|
|
s = Helpers::StringToWstring(info.Module).c_str();
|
|
break;
|
|
}
|
|
|
|
return s.GetLength();
|
|
}
|
|
|
|
bool CKernelNotifyTable::CompareItems(const CallbackInfo& s1, const CallbackInfo& s2, int col, bool asc) {
|
|
|
|
return false;
|
|
}
|
|
|
|
void CKernelNotifyTable::Refresh() {
|
|
#ifdef _WIN64
|
|
static ULONG Max = 64;
|
|
#else
|
|
static ULONG Max = 8;
|
|
#endif
|
|
|
|
ULONG count;
|
|
ProcessNotifyCountData data;
|
|
|
|
data.pCount = (PULONG)SymbolHelper::GetKernelSymbolAddressFromName("PspCreateProcessNotifyRoutineCount");
|
|
data.pExCount = (PULONG)SymbolHelper::GetKernelSymbolAddressFromName("PspCreateProcessNotifyRoutineExCount");
|
|
count = DriverHelper::GetProcessNotifyCount(&data);
|
|
NotifyInfo info;
|
|
info.Count = count;
|
|
info.pRoutine = (void*)SymbolHelper::GetKernelSymbolAddressFromName("PspCreateProcessNotifyRoutine");
|
|
|
|
m_Table.data.n = 0;
|
|
m_Table.data.info.clear();
|
|
|
|
// Enum CreateProcessNotify
|
|
if (count > 0) {
|
|
SIZE_T size = Max * sizeof(void*) + sizeof(ULONG);
|
|
wil::unique_virtualalloc_ptr<> buffer(::VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE));
|
|
|
|
KernelCallbackInfo* p = (KernelCallbackInfo*)buffer.get();
|
|
if (p != nullptr) {
|
|
p->Count = count + 1;
|
|
DriverHelper::EnumProcessNotify(&info, p);
|
|
for (int i = 0; i < count; i++) {
|
|
CallbackInfo info;
|
|
info.Routine = p->Address[i];
|
|
info.Type = CallbackType::CreateProcessNotify;
|
|
info.Module = Helpers::GetKernelModuleByAddress((ULONG_PTR)info.Routine);
|
|
std::wstring path = Helpers::StringToWstring(info.Module);
|
|
info.Company = GetCompanyName(path);
|
|
m_Table.data.info.push_back(std::move(info));
|
|
}
|
|
}
|
|
}
|
|
|
|
ULONG offset = SymbolHelper::GetKernelStructMemberOffset("_OBJECT_TYPE", "CallbackList");
|
|
KernelNotifyInfo notifyInfo;
|
|
notifyInfo.Type = NotifyType::ProcessObjectNotify;
|
|
notifyInfo.Offset = offset;
|
|
count = DriverHelper::GetObCallbackCount(¬ifyInfo);
|
|
if (count > 0) {
|
|
SIZE_T size = Max * sizeof(ObCallbackInfo);
|
|
wil::unique_virtualalloc_ptr<> buffer(::VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE));
|
|
|
|
ObCallbackInfo* p = (ObCallbackInfo*)buffer.get();
|
|
if (p != nullptr) {
|
|
DriverHelper::EnumObCallbackNotify(¬ifyInfo, p, size);
|
|
for (int i = 0; i < count; i++) {
|
|
if (p[i].PostOperation) {
|
|
CallbackInfo info;
|
|
info.Routine = p[i].PostOperation;
|
|
info.Type = CallbackType::ProcessObPostOperationNotify;
|
|
info.Module = Helpers::GetKernelModuleByAddress((ULONG_PTR)info.Routine);
|
|
std::wstring path = Helpers::StringToWstring(info.Module);
|
|
info.Company = GetCompanyName(path);
|
|
info.Address = p[i].RegistrationHandle;
|
|
m_Table.data.info.push_back(std::move(info));
|
|
}
|
|
if (p[i].PreOperation) {
|
|
CallbackInfo info;
|
|
info.Routine = p[i].PreOperation;
|
|
info.Type = CallbackType::ProcessObPreOperationNotify;
|
|
info.Module = Helpers::GetKernelModuleByAddress((ULONG_PTR)info.Routine);
|
|
std::wstring path = Helpers::StringToWstring(info.Module);
|
|
info.Company = GetCompanyName(path);
|
|
info.Address = p[i].RegistrationHandle;
|
|
m_Table.data.info.push_back(std::move(info));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
notifyInfo.Type = NotifyType::ThreadObjectNotify;
|
|
count = DriverHelper::GetObCallbackCount(¬ifyInfo);
|
|
if (count > 0) {
|
|
SIZE_T size = Max * sizeof(ObCallbackInfo);
|
|
wil::unique_virtualalloc_ptr<> buffer(::VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE));
|
|
ObCallbackInfo* p = (ObCallbackInfo*)buffer.get();
|
|
if (p != nullptr) {
|
|
DriverHelper::EnumObCallbackNotify(¬ifyInfo, p, size);
|
|
for (int i = 0; i < count; i++) {
|
|
if (p[i].PostOperation) {
|
|
CallbackInfo info;
|
|
info.Routine = p[i].PostOperation;
|
|
info.Type = CallbackType::ThreadObPostOperationNotify;
|
|
info.Module = Helpers::GetKernelModuleByAddress((ULONG_PTR)info.Routine);
|
|
std::wstring path = Helpers::StringToWstring(info.Module);
|
|
info.Company = GetCompanyName(path);
|
|
info.Address = p[i].RegistrationHandle;
|
|
m_Table.data.info.push_back(std::move(info));
|
|
}
|
|
if (p[i].PreOperation) {
|
|
CallbackInfo info;
|
|
info.Routine = p[i].PreOperation;
|
|
info.Type = CallbackType::ThreadObPreOperationNotify;
|
|
info.Module = Helpers::GetKernelModuleByAddress((ULONG_PTR)info.Routine);
|
|
std::wstring path = Helpers::StringToWstring(info.Module);
|
|
info.Company = GetCompanyName(path);
|
|
info.Address = p[i].RegistrationHandle;
|
|
m_Table.data.info.push_back(std::move(info));
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
count = 0;
|
|
ThreadNotifyCountData threadData;
|
|
threadData.pCount = 0;
|
|
threadData.pNonSystemCount = 0;
|
|
threadData.pCount = (PULONG)SymbolHelper::GetKernelSymbolAddressFromName("PspCreateThreadNotifyRoutineCount");
|
|
threadData.pNonSystemCount = (PULONG)SymbolHelper::GetKernelSymbolAddressFromName("PspCreateThreadNotifyRoutineNonSystemCount");
|
|
|
|
count = DriverHelper::GetThreadNotifyCount(&threadData);
|
|
if (count > 0) {
|
|
info.Count = count;
|
|
info.pRoutine = (void*)SymbolHelper::GetKernelSymbolAddressFromName("PspCreateThreadNotifyRoutine");
|
|
SIZE_T size = Max * sizeof(void*) + sizeof(ULONG);
|
|
wil::unique_virtualalloc_ptr<> buffer(::VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE));
|
|
KernelCallbackInfo* p = (KernelCallbackInfo*)buffer.get();
|
|
if (p != nullptr) {
|
|
p->Count = count + 1;
|
|
DriverHelper::EnumThreadNotify(&info, p);
|
|
for (int i = 0; i < count; i++) {
|
|
CallbackInfo item;
|
|
item.Routine = p->Address[i];
|
|
item.Type = CallbackType::CreateThreadNotify;
|
|
item.Module = Helpers::GetKernelModuleByAddress((ULONG_PTR)item.Routine);
|
|
std::wstring path = Helpers::StringToWstring(item.Module);
|
|
item.Company = GetCompanyName(path);
|
|
m_Table.data.info.push_back(std::move(item));
|
|
}
|
|
}
|
|
}
|
|
|
|
count = 0;
|
|
PULONG pCount = (PULONG)SymbolHelper::GetKernelSymbolAddressFromName("PspLoadImageNotifyRoutineCount");
|
|
|
|
count = DriverHelper::GetImageNotifyCount(&pCount);
|
|
if (count > 0) {
|
|
info.Count = count;
|
|
info.pRoutine = (void*)SymbolHelper::GetKernelSymbolAddressFromName("PspLoadImageNotifyRoutine");
|
|
|
|
SIZE_T size = Max * sizeof(void*) + sizeof(ULONG);
|
|
wil::unique_virtualalloc_ptr<> buffer(::VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE));
|
|
KernelCallbackInfo* p = (KernelCallbackInfo*)buffer.get();
|
|
if (p != nullptr) {
|
|
p->Count = count + 1;
|
|
DriverHelper::EnumImageLoadNotify(&info, p);
|
|
for (int i = 0; i < count; i++) {
|
|
CallbackInfo info;
|
|
info.Routine = p->Address[i];
|
|
info.Type = CallbackType::LoadImageNotify;
|
|
info.Module = Helpers::GetKernelModuleByAddress((ULONG_PTR)info.Routine);
|
|
std::wstring path = Helpers::StringToWstring(info.Module);
|
|
info.Company = GetCompanyName(path);
|
|
m_Table.data.info.push_back(std::move(info));
|
|
}
|
|
}
|
|
}
|
|
|
|
pCount = (PULONG)SymbolHelper::GetKernelSymbolAddressFromName("CmpCallbackCount");
|
|
count = DriverHelper::GetCmCallbackCount(&pCount);
|
|
if (count > 0) {
|
|
SIZE_T size = Max * sizeof(CmCallbackInfo);
|
|
wil::unique_virtualalloc_ptr<> buffer(::VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE));
|
|
CmCallbackInfo* p = (CmCallbackInfo*)buffer.get();
|
|
if (p != nullptr) {
|
|
PVOID callbackListHead = (void*)SymbolHelper::GetKernelSymbolAddressFromName("CallbackListHead");
|
|
|
|
DriverHelper::EnumCmCallbackNotify(callbackListHead, p, size);
|
|
/*symbol = handler.GetSymbolFromName("CmpPreloadedHivesList");
|
|
PVOID pCmpPreloadedHivesList = (PVOID)symbol->GetSymbolInfo()->Address;*/
|
|
for (int i = 0; i < count; ++i) {
|
|
CallbackInfo info;
|
|
info.Routine = p[i].Address;
|
|
info.Module = Helpers::GetKernelModuleByAddress((ULONG_PTR)info.Routine);
|
|
info.Type = CallbackType::RegistryNotify;
|
|
std::wstring path = Helpers::StringToWstring(info.Module);
|
|
info.Company = GetCompanyName(path);
|
|
info.Cookie = p[i].Cookie;
|
|
m_Table.data.info.push_back(std::move(info));
|
|
}
|
|
}
|
|
}
|
|
|
|
m_Table.data.n = m_Table.data.info.size();
|
|
}
|
|
|
|
std::wstring CKernelNotifyTable::GetCompanyName(std::wstring path) {
|
|
BYTE buffer[1 << 12];
|
|
WCHAR* companyName = nullptr;
|
|
CString filePath = path.c_str();
|
|
if (filePath.Mid(1, 2) == "??")
|
|
filePath = filePath.Mid(4);
|
|
if (::GetFileVersionInfo(filePath, 0, sizeof(buffer), buffer)) {
|
|
WORD* langAndCodePage;
|
|
UINT len;
|
|
if (::VerQueryValue(buffer, L"\\VarFileInfo\\Translation", (void**)&langAndCodePage, &len)) {
|
|
CString text;
|
|
text.Format(L"\\StringFileInfo\\%04x%04x\\CompanyName", langAndCodePage[0], langAndCodePage[1]);
|
|
|
|
if (::VerQueryValue(buffer, text, (void**)&companyName, &len))
|
|
return companyName;
|
|
}
|
|
}
|
|
return L"";
|
|
}
|
|
|
|
LRESULT CKernelNotifyTable::OnRefresh(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
|
|
Refresh();
|
|
Invalidate(True);
|
|
return TRUE;
|
|
}
|
|
|
|
LRESULT CKernelNotifyTable::OnRemove(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
|
|
int selected = m_Table.data.selected;
|
|
ATLASSERT(selected >= 0);
|
|
auto& p = m_Table.data.info[selected];
|
|
|
|
CString text;
|
|
text.Format(L"移除回调:%p?", p.Routine);
|
|
if (AtlMessageBox(*this, (PCWSTR)text, IDS_TITLE, MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON2) == IDCANCEL)
|
|
return 0;
|
|
|
|
NotifyData data;
|
|
data.Address = p.Routine;
|
|
|
|
switch (p.Type)
|
|
{
|
|
case CallbackType::CreateProcessNotify:
|
|
data.Type = NotifyType::CreateProcessNotify;
|
|
break;
|
|
|
|
case CallbackType::CreateThreadNotify:
|
|
data.Type = NotifyType::CreateThreadNotify;
|
|
break;
|
|
|
|
case CallbackType::LoadImageNotify:
|
|
data.Type = NotifyType::LoadImageNotify;
|
|
break;
|
|
|
|
case CallbackType::ProcessObPostOperationNotify:
|
|
case CallbackType::ProcessObPreOperationNotify:
|
|
data.Type = NotifyType::ProcessObjectNotify;
|
|
data.Address = p.Address;
|
|
break;
|
|
case CallbackType::ThreadObPostOperationNotify:
|
|
case CallbackType::ThreadObPreOperationNotify:
|
|
data.Type = NotifyType::ThreadObjectNotify;
|
|
data.Address = p.Address;
|
|
break;
|
|
|
|
case CallbackType::RegistryNotify:
|
|
data.Type = NotifyType::RegistryNotify;
|
|
data.Cookie = p.Cookie;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
BOOL ok = false;
|
|
|
|
ok = DriverHelper::RemoveNotify(&data);
|
|
if (!ok)
|
|
AtlMessageBox(*this, L"Failed to remove notify", IDS_TITLE, MB_ICONERROR);
|
|
else
|
|
Refresh();
|
|
Invalidate(True);
|
|
return TRUE;
|
|
}
|
|
|
|
std::wstring CKernelNotifyTable::GetSingleNotifyInfo(CallbackInfo& info) {
|
|
CString text;
|
|
CString s;
|
|
|
|
s.Format(L"0x%p", info.Routine);
|
|
s += L"\t";
|
|
text += s;
|
|
|
|
switch (info.Type)
|
|
{
|
|
case CallbackType::CreateProcessNotify:
|
|
s = L"CreateProcess";
|
|
break;
|
|
case CallbackType::CreateThreadNotify:
|
|
s = L"CreateThread";
|
|
break;
|
|
|
|
case CallbackType::LoadImageNotify:
|
|
s = L"LoadImage";
|
|
break;
|
|
|
|
case CallbackType::ProcessObPostOperationNotify:
|
|
s = L"Process ObPostOperation";
|
|
break;
|
|
|
|
case CallbackType::ProcessObPreOperationNotify:
|
|
s = L"Process ObPreOperation";
|
|
break;
|
|
|
|
case CallbackType::ThreadObPostOperationNotify:
|
|
s = L"Thread ObPostOperation";
|
|
break;
|
|
|
|
case CallbackType::ThreadObPreOperationNotify:
|
|
s = L"Thread ObPreOperation";
|
|
break;
|
|
|
|
case CallbackType::RegistryNotify:
|
|
s = L"Registry Notify";
|
|
break;
|
|
}
|
|
s += L"\t";
|
|
text += s;
|
|
|
|
s = info.Company.c_str();
|
|
s += L"\t";
|
|
text += s;
|
|
|
|
s = Helpers::StringToWstring(info.Module).c_str();
|
|
s += L"\t";
|
|
text += s;
|
|
|
|
text += L"\r\n";
|
|
|
|
return text.GetString();
|
|
}
|
|
|
|
LRESULT CKernelNotifyTable::OnNotifyCopy(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
|
|
int selected = m_Table.data.selected;
|
|
ATLASSERT(selected >= 0);
|
|
auto& info = m_Table.data.info[selected];
|
|
|
|
std::wstring text = GetSingleNotifyInfo(info);
|
|
ClipboardHelper::CopyText(m_hWnd, text.c_str());
|
|
return 0;
|
|
}
|
|
|
|
|
|
LRESULT CKernelNotifyTable::OnNotifyExport(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
|
|
CSimpleFileDialog dlg(FALSE, nullptr, L"*.txt",
|
|
OFN_EXPLORER | OFN_ENABLESIZING | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY,
|
|
L"文本文档 (*.txt)\0*.txt\0所有文件\0*.*\0", m_hWnd);
|
|
if (dlg.DoModal() == IDOK) {
|
|
auto hFile = ::CreateFile(dlg.m_szFileName, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
return FALSE;
|
|
for (int i = 0; i < m_Table.data.n; ++i) {
|
|
auto& info = m_Table.data.info[i];
|
|
std::wstring text = GetSingleNotifyInfo(info);
|
|
Helpers::WriteString(hFile, text);
|
|
}
|
|
::CloseHandle(hFile);
|
|
}
|
|
return TRUE;
|
|
} |