refactored layers

bug fixes
This commit is contained in:
Pavel Yosifovich
2022-12-11 12:06:15 -05:00
parent 93d4f4f178
commit d5f90a7dcb
22 changed files with 307 additions and 281 deletions
+22
View File
@@ -54,3 +54,25 @@ struct WFPCalloutEnumerator : WFPEnumerator<
WFPEnumerator(hEngine, ::FwpmCalloutCreateEnumHandle, ::FwpmCalloutDestroyEnumHandle, ::FwpmCalloutEnum) {
}
};
struct WFPLayerEnumerator : WFPEnumerator<
decltype(&::FwpmLayerCreateEnumHandle),
decltype(&::FwpmLayerDestroyEnumHandle),
decltype(&::FwpmLayerEnum),
FWPM_LAYER> {
explicit WFPLayerEnumerator(HANDLE hEngine) :
WFPEnumerator(hEngine, ::FwpmLayerCreateEnumHandle, ::FwpmLayerDestroyEnumHandle, ::FwpmLayerEnum) {
}
};
struct WFPSubLayerEnumerator : WFPEnumerator<
decltype(&::FwpmSubLayerCreateEnumHandle),
decltype(&::FwpmSubLayerDestroyEnumHandle),
decltype(&::FwpmSubLayerEnum),
FWPM_SUBLAYER> {
explicit WFPSubLayerEnumerator(HANDLE hEngine) :
WFPEnumerator(hEngine, ::FwpmSubLayerCreateEnumHandle, ::FwpmSubLayerDestroyEnumHandle, ::FwpmSubLayerEnum) {
}
};
+9 -49
View File
@@ -109,15 +109,10 @@ bool WFPEngine::DeleteFilter(UINT64 id) {
return m_LastError == ERROR_SUCCESS;
}
std::optional<WFPLayerInfo> WFPEngine::GetLayerByKey(GUID const& key) const {
FWPM_LAYER* layer;
m_LastError = FwpmLayerGetByKey(m_hEngine, &key, &layer);
if (m_LastError != ERROR_SUCCESS)
return {};
auto info = InitLayer(layer, true);
FwpmFreeMemory((void**)&layer);
return info;
WFPObject<FWPM_LAYER> WFPEngine::GetLayerByKey(GUID const& key) const {
FWPM_LAYER* layer = nullptr;
FwpmLayerGetByKey(m_hEngine, &key, &layer);
return WFPObject<FWPM_LAYER>(layer);
}
std::optional<WFPLayerInfo> WFPEngine::GetLayerById(UINT16 id) const {
@@ -177,39 +172,6 @@ WFPConnectionInfo WFPEngine::InitConnection(FWPM_CONNECTION* p, bool includeData
return ci;
}
std::wstring WFPEngine::PoorParseMUIString(std::wstring const& path) {
static std::unordered_map<std::wstring, std::wstring> cache;
if (path[0] != L'@')
return path;
if (auto it = cache.find(path); it != cache.end())
return it->second;
auto comma = path.find(L',', 1);
if (comma == std::wstring::npos)
return path;
auto dllname = path.substr(1, comma - 1);
auto hDll = ::LoadLibraryEx(dllname.c_str(), nullptr, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
if (!hDll)
return path;
CString result;
auto id = _wtoi(path.substr(comma + 1).c_str());
if (id < 0)
id = -id;
result.LoadStringW(hDll, id);
::FreeLibrary(hDll);
if (!result.IsEmpty()) {
cache.insert({ path, (PCWSTR)result });
return (PCWSTR)result;
}
if (dllname.substr(dllname.rfind(L'.')) != L".mui")
return PoorParseMUIString(L"@c:\\Windows\\System32\\en-US\\" + dllname + L".mui" + path.substr(comma));
return path;
}
std::wstring WFPEngine::ParseMUIString(PCWSTR input) {
if (input == nullptr)
return L"";
@@ -244,12 +206,10 @@ std::vector<WFPProviderContextInfo> WFPEngine::EnumProviderContexts(bool include
return info;
}
std::optional<WFPCalloutInfo> WFPEngine::GetCalloutByKey(GUID const& key) const {
FWPM_CALLOUT* co;
WFPObject<FWPM_CALLOUT> WFPEngine::GetCalloutByKey(GUID const& key) const {
FWPM_CALLOUT* co = nullptr;
FwpmCalloutGetByKey(m_hEngine, &key, &co);
auto info = InitCallout(co, true);
FwpmFreeMemory((void**)&co);
return info;
return WFPObject(co);
}
uint32_t WFPEngine::GetFilterCount(GUID const& layer) const {
@@ -260,7 +220,7 @@ uint32_t WFPEngine::GetFilterCount(GUID const& layer) const {
FWPM_FILTER** filters;
UINT32 count;
uint32_t total = 0;
m_LastError = FwpmFilterEnum(m_hEngine, hEnum, 4096, &filters, &count);
m_LastError = FwpmFilterEnum(m_hEngine, hEnum, 8192, &filters, &count);
if (m_LastError == ERROR_SUCCESS) {
for (UINT32 i = 0; i < count; i++) {
auto filter = filters[i];
@@ -281,7 +241,7 @@ uint32_t WFPEngine::GetCalloutCount(GUID const& layer) const {
FWPM_CALLOUT** callouts;
UINT32 count;
uint32_t total = 0;
m_LastError = FwpmCalloutEnum(m_hEngine, hEnum, 256, &callouts, &count);
m_LastError = FwpmCalloutEnum(m_hEngine, hEnum, 1024, &callouts, &count);
if (m_LastError == ERROR_SUCCESS) {
for (UINT32 i = 0; i < count; i++) {
auto c = callouts[i];
+2 -107
View File
@@ -618,40 +618,6 @@ public:
std::vector<WFPConnectionInfo> EnumConnections(bool includeData = false);
std::vector<WFPSystemPortByType> EnumSystemPorts();
template<typename TSession = WFPSessionInfo> requires std::is_base_of_v<WFPSessionInfo, TSession>
std::vector<TSession> EnumSessions() const {
HANDLE hEnum;
std::vector<TSession> info;
m_LastError = FwpmSessionCreateEnumHandle(m_hEngine, nullptr, &hEnum);
if (m_LastError != ERROR_SUCCESS)
return info;
UINT32 count;
FWPM_SESSION** sessions;
if ((m_LastError = FwpmSessionEnum(m_hEngine, hEnum, 128, &sessions, &count)) == ERROR_SUCCESS) {
info.reserve(count);
for (UINT32 i = 0; i < count; i++) {
auto session = sessions[i];
TSession si;
si.Name = ParseMUIString(session->displayData.name);
si.Desc = ParseMUIString(session->displayData.description);
si.SessionKey = session->sessionKey;
si.ProcessId = session->processId;
si.UserName = session->username;
si.Flags = static_cast<WFPSessionFlags>(session->flags);
auto len = ::GetLengthSid(session->sid);
si.Sid.resize(len);
::CopySid(len, (PSID)si.Sid.data(), session->sid);
si.KernelMode = session->kernelMode;
info.emplace_back(std::move(si));
}
FwpmFreeMemory((void**)&sessions);
m_LastError = FwpmSessionDestroyEnumHandle(m_hEngine, hEnum);
}
return info;
}
template<typename TFilter = WFPFilterInfo> requires std::is_base_of_v<WFPFilterInfo, TFilter>
std::vector<TFilter> EnumFilters(GUID const& layer, bool full = false) const {
if (layer == GUID_NULL)
@@ -754,58 +720,6 @@ public:
std::vector<WFPProviderContextInfo> EnumProviderContexts(bool includeData = false) const;
template<typename TCallout = WFPCalloutInfo> requires std::is_base_of_v<WFPCalloutInfo, TCallout>
std::vector<TCallout> EnumCallouts() const {
HANDLE hEnum;
std::vector<TCallout> info;
m_LastError = FwpmCalloutCreateEnumHandle(m_hEngine, nullptr, &hEnum);
if (m_LastError)
return info;
FWPM_CALLOUT** callouts;
UINT32 count;
m_LastError = FwpmCalloutEnum(m_hEngine, hEnum, 256, &callouts, &count);
if (m_LastError == ERROR_SUCCESS) {
info.reserve(count);
for (UINT32 i = 0; i < count; i++) {
auto c = callouts[i];
auto ci = InitCallout(c);
info.emplace_back(std::move(ci));
}
FwpmFreeMemory((void**)&callouts);
}
m_LastError = FwpmCalloutDestroyEnumHandle(m_hEngine, hEnum);
return info;
}
template<typename TCallout = WFPCalloutInfo> requires std::is_base_of_v<WFPCalloutInfo, TCallout>
std::vector<TCallout> EnumCallouts(GUID const& layer) const {
if (layer == GUID_NULL)
return EnumCallouts<TCallout>();
HANDLE hEnum;
std::vector<TCallout> info;
m_LastError = FwpmCalloutCreateEnumHandle(m_hEngine, nullptr, &hEnum);
if (m_LastError)
return info;
FWPM_CALLOUT** callouts;
UINT32 count;
m_LastError = FwpmCalloutEnum(m_hEngine, hEnum, 256, &callouts, &count);
if (m_LastError == ERROR_SUCCESS) {
info.reserve(count);
for (UINT32 i = 0; i < count; i++) {
auto c = callouts[i];
if (c->applicableLayer == layer) {
auto ci = InitCallout(c);
info.emplace_back(std::move(ci));
}
}
FwpmFreeMemory((void**)&callouts);
}
m_LastError = FwpmCalloutDestroyEnumHandle(m_hEngine, hEnum);
return info;
}
//
// providers API
//
@@ -822,7 +736,7 @@ public:
//
// layer API
//
std::optional<WFPLayerInfo> GetLayerByKey(GUID const& key) const;
WFPObject<FWPM_LAYER> GetLayerByKey(GUID const& key) const;
std::optional<WFPLayerInfo> GetLayerById(UINT16 id) const;
//
@@ -832,7 +746,7 @@ public:
std::optional<WFPSubLayerInfo> GetSublayerById(UINT16 id) const;
std::optional<WFPCalloutInfo> GetCalloutByKey(GUID const& key) const;
WFPObject<FWPM_CALLOUT> GetCalloutByKey(GUID const& key) const;
private:
//
@@ -918,25 +832,6 @@ private:
return li;
}
template<typename TCallout = WFPCalloutInfo> requires std::is_base_of_v<WFPCalloutInfo, TCallout>
static TCallout InitCallout(FWPM_CALLOUT* c, bool full = false) {
TCallout ci;
ci.Name = ParseMUIString(c->displayData.name);
ci.Desc = ParseMUIString(c->displayData.description);
ci.ProviderKey = c->providerKey ? *c->providerKey : GUID_NULL;
ci.Flags = static_cast<WFPCalloutFlags>(c->flags);
ci.CalloutKey = c->calloutKey;
ci.ApplicableLayer = c->applicableLayer;
ci.ProviderDataSize = c->providerData.size;
if (full && ci.ProviderDataSize) {
ci.ProviderData.resize(ci.ProviderDataSize);
memcpy(ci.ProviderData.data(), c->providerData.data, ci.ProviderDataSize);
}
ci.CalloutId = c->calloutId;
ci.ApplicableLayer = c->applicableLayer;
return ci;
}
static std::wstring PoorParseMUIString(std::wstring const& path);
HANDLE m_hEngine{ nullptr };
+96 -6
View File
@@ -1,5 +1,70 @@
#pragma once
template<typename TItem, typename T = TItem*>
class WFPObjectVector {
public:
WFPObjectVector() = default;
WFPObjectVector(std::vector<T>&& v, TItem** items) : m_Vec(std::move(v)), m_pItems(items) {}
WFPObjectVector(WFPObjectVector const&) = delete;
WFPObjectVector(WFPObjectVector&& other) : m_Vec(std::move(other.m_Vec)), m_pItems(other.m_pItems) {
other.m_pItems = nullptr;
}
WFPObjectVector& operator=(WFPObjectVector const&) = delete;
WFPObjectVector& operator=(WFPObjectVector&& other) {
if (this != &other) {
if(m_pItems)
::FwpmFreeMemory((void**)&m_pItems);
m_Vec = std::move(other.m_Vec);
m_pItems = other.m_pItems;
other.m_pItems = nullptr;
}
return *this;
}
~WFPObjectVector() {
if (m_pItems)
::FwpmFreeMemory((void**)&m_pItems);
}
auto begin() {
return m_Vec.begin();
}
auto end() {
return m_Vec.end();
}
auto data() {
return m_Vec.data();
}
auto const begin() const {
return m_Vec.begin();
}
auto const end() const {
return m_Vec.end();
}
auto size() const {
return m_Vec.size();
}
auto& operator[](size_t index) {
return m_Vec[index];
}
auto const& operator[](size_t index) const {
return m_Vec[index];
}
private:
std::vector<T> m_Vec;
TItem** m_pItems{ nullptr };
};
template<typename Create, typename Destroy, typename Enum, typename TItem>
class WFPEnumerator abstract {
public:
@@ -17,7 +82,7 @@ public:
return m_LastError;
}
std::vector<TItem*> Next(uint32_t next = 1024) {
WFPObjectVector<TItem> Next(uint32_t next = 1024) {
if (m_hEnum == nullptr) {
m_LastError = m_create(m_hEngine, nullptr, &m_hEnum);
if (m_LastError != ERROR_SUCCESS)
@@ -30,15 +95,40 @@ public:
m_LastError = m_enum(m_hEngine, m_hEnum, next, &pItems, &count);
if (ERROR_SUCCESS == m_LastError) {
items.reserve(count);
for (UINT i = 0; i < count; i++)
for (UINT32 i = 0; i < count; i++)
items.push_back(pItems[i]);
return items;
return WFPObjectVector<TItem>(std::move(items), pItems);
}
return items;
return WFPObjectVector<TItem>();
}
template<typename T, typename Pred>
WFPObjectVector<TItem, T> NextFiltered(Pred&& pred, uint32_t next = 1024) {
if (m_hEnum == nullptr) {
m_LastError = m_create(m_hEngine, nullptr, &m_hEnum);
if (m_LastError != ERROR_SUCCESS)
return {};
}
TItem** pItems;
UINT32 count;
std::vector<T> items;
m_LastError = m_enum(m_hEngine, m_hEnum, next, &pItems, &count);
if (ERROR_SUCCESS == m_LastError) {
items.reserve(count);
for (UINT32 i = 0; i < count; i++) {
if (pred(pItems[i])) {
T item;
item.Data = pItems[i];
items.push_back(std::move(item));
}
}
}
return WFPObjectVector<TItem, T>(std::move(items), pItems);
}
template<typename T>
std::vector<T> Next(uint32_t next = 1024) {
WFPObjectVector<TItem, T> Next(uint32_t next = 1024) {
if (m_hEnum == nullptr) {
m_LastError = m_create(m_hEngine, nullptr, &m_hEnum);
if (m_LastError != ERROR_SUCCESS)
@@ -57,7 +147,7 @@ public:
items.push_back(std::move(item));
}
}
return items;
return WFPObjectVector<TItem, T>(std::move(items), pItems);
}
protected:
+6 -7
View File
@@ -5,7 +5,7 @@
#include "resource.h"
#include <ranges>
CCalloutsView::CCalloutsView(IMainFrame* frame, WFPEngine& engine) : CFrameView(frame), m_Engine(engine), m_Enum(engine.Handle()) {
CCalloutsView::CCalloutsView(IMainFrame* frame, WFPEngine& engine) : CFrameView(frame), m_Engine(engine) {
}
LRESULT CCalloutsView::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) {
@@ -41,13 +41,12 @@ void CCalloutsView::SetLayer(GUID const& layer) {
}
void CCalloutsView::Refresh() {
m_Enum.Close();
WFPCalloutEnumerator cenum(m_Engine.Handle());
if (m_LayerKey != GUID_NULL) {
auto callouts = m_Enum.Next<CalloutInfo>(2048) | std::views::filter([&](auto& c) { return c.Data->applicableLayer == m_LayerKey; });
m_Callouts.assign(callouts.begin(), callouts.end());
m_Callouts = cenum.NextFiltered<CalloutInfo>([&](auto c) { return c->applicableLayer == m_LayerKey; }, 2048);
}
else {
m_Callouts = m_Enum.Next<CalloutInfo>(2048);
m_Callouts = cenum.Next<CalloutInfo>(2048);
}
Sort(m_List);
m_List.SetItemCountEx((int)m_Callouts.size(), LVSICF_NOSCROLL);
@@ -61,8 +60,8 @@ CString CCalloutsView::GetColumnText(HWND, int row, int col) {
case ColumnType::Layer:
if (info.Layer.IsEmpty()) {
auto layer = m_Engine.GetLayerByKey(info.Data->applicableLayer);
if (layer && !layer->Name.empty()) {
info.Layer = layer->Name.c_str();
if (layer && layer->displayData.name) {
info.Layer = layer->displayData.name;
}
else {
info.Layer = StringHelper::GuidToString(info.Data->applicableLayer);
+1 -2
View File
@@ -52,7 +52,6 @@ private:
WFPEngine& m_Engine;
CListViewCtrl m_List;
std::vector<CalloutInfo> m_Callouts;
WFPObjectVector<FWPM_CALLOUT, CalloutInfo> m_Callouts;
GUID m_LayerKey{ GUID_NULL };
WFPCalloutEnumerator m_Enum;
};
+3 -3
View File
@@ -49,7 +49,7 @@ CString CFiltersView::GetColumnText(HWND, int row, int col) {
else {
auto callout = m_Engine.GetCalloutByKey(info.Action.CalloutKey);
if (callout)
info.FilterAction = callout->Name.c_str();
info.FilterAction = callout->displayData.name;
}
}
}
@@ -84,8 +84,8 @@ CString const& CFiltersView::GetProviderName(FilterInfo& info) const {
CString const& CFiltersView::GetLayerName(FilterInfo& info) const {
if (info.Layer.IsEmpty() && info.LayerKey != GUID_NULL) {
auto layer = m_Engine.GetLayerByKey(info.LayerKey);
if (layer && !layer.value().Name.empty() && layer.value().Name[0] != L'@')
info.Layer = layer.value().Name.c_str();
if (layer && layer->displayData.name && layer->displayData.name[0] != L'@')
info.Layer = layer->displayData.name;
else
info.Layer = StringHelper::GuidToString(info.LayerKey);
}
+7 -5
View File
@@ -7,6 +7,7 @@
#include "CalloutsView.h"
#include "FiltersView.h"
#include "AppSettings.h"
#include <Enumerators.h>
CHierarchyView::CHierarchyView(IMainFrame* frame, WFPEngine& engine) : CFrameView(frame), m_Engine(engine) {
}
@@ -69,7 +70,8 @@ void CHierarchyView::BuildTree() {
auto hLayers = InsertTreeItem(m_Tree, L"Layers", 1, TreeItemType::Layers, TVI_ROOT);
auto filters = m_Engine.EnumFilters();
auto callouts = m_Engine.EnumCallouts();
WFPCalloutEnumerator cenum(m_Engine.Handle());
auto callouts = cenum.Next(1024);
m_LayersMap.clear();
m_FiltersMap.clear();
m_CalloutsMap.clear();
@@ -95,16 +97,16 @@ void CHierarchyView::BuildTree() {
}
}
{
auto view = callouts | views::filter([&](auto& c) { return c.ApplicableLayer == layer.LayerKey; });
auto view = callouts | views::filter([&](auto& c) { return c->applicableLayer == layer.LayerKey; });
if (!view.empty()) {
auto hCallouts = InsertTreeItem(m_Tree, L"Callouts", 2, TreeItemType::Callouts, hLayer, TVI_LAST);
uint32_t count = 0;
for (auto& v : view) {
CString name = v.Name.c_str();
CString name = v->displayData.name;
if (name[0] != L'{')
name += L" " + StringHelper::GuidToString(v.CalloutKey);
name += L" " + StringHelper::GuidToString(v->calloutKey);
auto hCallout = InsertTreeItem(m_Tree, name, 2, TreeItemType::Callout, hCallouts, TVI_SORT);
m_CalloutsMap.insert({ hCallout, v.CalloutKey });
m_CalloutsMap.insert({ hCallout, v->calloutKey });
count++;
}
m_Tree.SetItemText(hCallouts, std::format(L"Callouts ({})", count).c_str());
+12 -10
View File
@@ -4,16 +4,16 @@
#include "StringHelper.h"
#include <SortHelper.h>
CLayerFieldsPage::CLayerFieldsPage(WFPEngine& engine, WFPLayerInfo const& layer) : m_Engine(engine), m_Layer(layer) {
CLayerFieldsPage::CLayerFieldsPage(WFPEngine& engine, FWPM_LAYER* layer) : m_Engine(engine), m_Layer(layer) {
}
CString CLayerFieldsPage::GetColumnText(HWND, int row, int col) const {
auto& field = m_Fields[row];
auto const& field = m_Fields[row];
switch (col) {
case 0: return StringHelper::WFPConditionFieldKeyToString(field.FieldKey);
case 1: return StringHelper::WFPFieldTypeToString(field.Type);
case 2: return StringHelper::WFPDataTypeToString(field.DataType);
case 0: return StringHelper::WFPConditionFieldKeyToString(*field.fieldKey);
case 1: return StringHelper::WFPFieldTypeToString(field.type);
case 2: return StringHelper::WFPDataTypeToString((WFPDataType)field.dataType);
}
return CString();
}
@@ -21,9 +21,9 @@ CString CLayerFieldsPage::GetColumnText(HWND, int row, int col) const {
void CLayerFieldsPage::DoSort(SortInfo const* si) {
auto compare = [&](auto& f1, auto& f2) {
switch (si->SortColumn) {
case 0: return SortHelper::Sort(StringHelper::WFPConditionFieldKeyToString(f1.FieldKey), StringHelper::WFPConditionFieldKeyToString(f2.FieldKey), si->SortAscending);
case 1: return SortHelper::Sort(f1.Type, f2.Type, si->SortAscending);
case 2: return SortHelper::Sort(StringHelper::WFPDataTypeToString(f1.DataType), StringHelper::WFPDataTypeToString(f2.DataType), si->SortAscending);
case 0: return SortHelper::Sort(StringHelper::WFPConditionFieldKeyToString(*f1.fieldKey), StringHelper::WFPConditionFieldKeyToString(*f2.fieldKey), si->SortAscending);
case 1: return SortHelper::Sort(f1.type, f2.type, si->SortAscending);
case 2: return SortHelper::Sort(StringHelper::WFPDataTypeToString((WFPDataType)f1.dataType), StringHelper::WFPDataTypeToString((WFPDataType)f2.dataType), si->SortAscending);
}
return false;
};
@@ -35,8 +35,10 @@ LRESULT CLayerFieldsPage::OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) {
m_List.Attach(GetDlgItem(IDC_LIST));
m_List.SetExtendedListViewStyle(LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT);
m_Fields = m_Engine.GetLayerByKey(m_Layer.LayerKey)->Fields;
m_List.SetItemCount(m_Layer.NumFields);
auto layer = m_Engine.GetLayerByKey(m_Layer->layerKey);
m_Fields.resize(layer->numFields);
memcpy(m_Fields.data(), layer->field, sizeof(FWPM_FIELD) * layer->numFields);
m_List.SetItemCount(m_Layer->numFields);
auto cm = GetColumnManager(m_List);
cm->AddColumn(L"Field", 0, 230);
+3 -4
View File
@@ -5,7 +5,6 @@
#include <VirtualListView.h>
class WFPEngine;
struct WFPLayerInfo;
struct WFPFieldInfo;
class CLayerFieldsPage :
@@ -16,7 +15,7 @@ class CLayerFieldsPage :
public:
enum { IDD = IDD_LAYERFIELDS };
CLayerFieldsPage(WFPEngine& engine, WFPLayerInfo const& layer);
CLayerFieldsPage(WFPEngine& engine, FWPM_LAYER* layer);
CString GetColumnText(HWND, int row, int col) const;
void DoSort(SortInfo const* si);
@@ -37,7 +36,7 @@ private:
private:
WFPEngine& m_Engine;
WFPLayerInfo const& m_Layer;
std::vector<WFPFieldInfo> m_Fields;
FWPM_LAYER* m_Layer;
std::vector<FWPM_FIELD> m_Fields;
CListViewCtrl m_List;
};
+11 -11
View File
@@ -4,24 +4,24 @@
#include "StringHelper.h"
#include "WFPHelper.h"
CLayerGeneralPage::CLayerGeneralPage(WFPEngine& engine, WFPLayerInfo const& layer) : m_Engine(engine), m_Layer(layer) {
CLayerGeneralPage::CLayerGeneralPage(WFPEngine& engine, FWPM_LAYER* layer) : m_Engine(engine), m_Layer(layer) {
}
LRESULT CLayerGeneralPage::OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) {
SetDlgItemText(IDC_NAME, m_Layer.Name.c_str());
SetDlgItemText(IDC_DESC, m_Layer.Desc.c_str());
SetDlgItemText(IDC_KEY, StringHelper::GuidToString(m_Layer.LayerKey));
SetDlgItemInt(IDC_FIELDS, m_Layer.NumFields);
SetDlgItemInt(IDC_ID, m_Layer.LayerId);
auto flags = StringHelper::WFPLayerFlagsToString(m_Layer.Flags);
SetDlgItemText(IDC_NAME, StringHelper::ParseMUIString(m_Layer->displayData.name));
SetDlgItemText(IDC_DESC, StringHelper::ParseMUIString(m_Layer->displayData.description));
SetDlgItemText(IDC_KEY, StringHelper::GuidToString(m_Layer->layerKey));
SetDlgItemInt(IDC_FIELDS, m_Layer->numFields);
SetDlgItemInt(IDC_ID, m_Layer->layerId);
auto flags = StringHelper::WFPLayerFlagsToString(m_Layer->flags);
if (!flags.IsEmpty())
flags = std::format(L"0x{:X} ({})", (UINT32)m_Layer.Flags, (PCWSTR)flags).c_str();
flags = std::format(L"0x{:X} ({})", m_Layer->flags, (PCWSTR)flags).c_str();
else
flags = L"(None)";
SetDlgItemText(IDC_FLAGS, flags);
SetDlgItemText(IDC_SUBLAYER, WFPHelper::GetSublayerName(m_Engine, m_Layer.DefaultSubLayerKey));
SetDlgItemInt(IDC_FILTERS, m_Engine.GetFilterCount(m_Layer.LayerKey), false);
SetDlgItemInt(IDC_CALLOUTS, m_Engine.GetCalloutCount(m_Layer.LayerKey), false);
SetDlgItemText(IDC_SUBLAYER, WFPHelper::GetSublayerName(m_Engine, m_Layer->defaultSubLayerKey));
SetDlgItemInt(IDC_FILTERS, m_Engine.GetFilterCount(m_Layer->layerKey), false);
SetDlgItemInt(IDC_CALLOUTS, m_Engine.GetCalloutCount(m_Layer->layerKey), false);
AddIconToButton(IDC_SUBLAYER_PROP, IDI_SUBLAYER);
+2 -2
View File
@@ -12,7 +12,7 @@ class CLayerGeneralPage :
public:
enum { IDD = IDD_LAYERINFO };
CLayerGeneralPage(WFPEngine& engine, WFPLayerInfo const& layer);
CLayerGeneralPage(WFPEngine& engine, FWPM_LAYER* layer);
BEGIN_MSG_MAP(CLayerGeneralPage)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
@@ -28,5 +28,5 @@ private:
private:
WFPEngine& m_Engine;
WFPLayerInfo const& m_Layer;
FWPM_LAYER* m_Layer;
};
+45 -29
View File
@@ -4,6 +4,7 @@
#include <SortHelper.h>
#include "WFPHelper.h"
#include "LayerGeneralPage.h"
#include <Enumerators.h>
CLayersView::CLayersView(IMainFrame* frame, WFPEngine& engine) : CFrameView(frame), m_Engine(engine) {
}
@@ -49,46 +50,48 @@ LRESULT CLayersView::OnRefresh(WORD, WORD, HWND, BOOL&) {
LRESULT CLayersView::OnProperties(WORD, WORD, HWND, BOOL&) {
auto& layer = m_Layers[m_List.GetSelectedIndex()];
WFPHelper::ShowLayerProperties(m_Engine, layer);
WFPHelper::ShowLayerProperties(m_Engine, layer.Data);
return 0;
}
void CLayersView::Refresh() {
m_Layers = m_Engine.EnumLayers<LayerInfo>();
WFPLayerEnumerator enumerator(m_Engine.Handle());
m_Layers = enumerator.Next<LayerInfo>();
Sort(m_List);
m_List.SetItemCountEx((int)m_Layers.size(), LVSICF_NOSCROLL);
Frame()->SetStatusText(4, std::format(L"{} Layers", m_Layers.size()).c_str());
}
CString CLayersView::GetColumnText(HWND, int row, int col) {
auto& info = m_Layers[row];
auto& layer = m_Layers[row];
auto info = layer.Data;
switch (GetColumnManager(m_List)->GetColumnTag<ColumnType>(col)) {
case ColumnType::Key: return StringHelper::GuidToString(info.LayerKey);
case ColumnType::Key: return StringHelper::GuidToString(info->layerKey);
case ColumnType::DefaultSubLayer:
if (info.DefaultSublayer.IsEmpty()) {
if (info.DefaultSubLayerKey != GUID_NULL) {
auto sl = m_Engine.GetSublayerByKey(info.DefaultSubLayerKey);
if (layer.DefaultSublayer.IsEmpty()) {
if (info->defaultSubLayerKey != GUID_NULL) {
auto sl = m_Engine.GetSublayerByKey(info->defaultSubLayerKey);
if (sl && !sl.value().Name.empty())
info.DefaultSublayer = sl.value().Name.c_str();
layer.DefaultSublayer = sl.value().Name.c_str();
else
info.DefaultSublayer = StringHelper::GuidToString(info.DefaultSubLayerKey);
layer.DefaultSublayer = StringHelper::GuidToString(info->defaultSubLayerKey);
}
}
return info.DefaultSublayer;
return layer.DefaultSublayer;
case ColumnType::Filters: return std::to_wstring(info.FilterCount).c_str();
case ColumnType::Callouts: return std::to_wstring(info.CalloutCount).c_str();
case ColumnType::Filters: return std::to_wstring(layer.FilterCount).c_str();
case ColumnType::Callouts: return std::to_wstring(layer.CalloutCount).c_str();
case ColumnType::Name: return info.Name.c_str();
case ColumnType::Desc: return info.Desc.c_str();
case ColumnType::Name: return layer.Name();
case ColumnType::Desc: return layer.Desc();
case ColumnType::Flags:
if (info.Flags == WFPLayerFlags::None)
if (info->flags == 0)
return L"0";
return std::format(L"0x{:X} ({})", (UINT32)info.Flags,
(PCWSTR)StringHelper::WFPLayerFlagsToString(info.Flags)).c_str();
return std::format(L"0x{:X} ({})", info->flags,
(PCWSTR)StringHelper::WFPLayerFlagsToString(info->flags)).c_str();
case ColumnType::Fields: return std::to_wstring(info.NumFields).c_str();
case ColumnType::Id: return std::to_wstring(info.LayerId).c_str();
case ColumnType::Fields: return std::to_wstring(info->numFields).c_str();
case ColumnType::Id: return std::to_wstring(info->layerId).c_str();
}
return CString();
}
@@ -99,15 +102,16 @@ void CLayersView::DoSort(SortInfo const* si) {
auto col = GetColumnManager(m_List)->GetColumnTag<ColumnType>(si->SortColumn);
auto asc = si->SortAscending;
auto compare = [&](auto& l1, auto& l2) {
auto compare = [&](auto& layer1, auto& layer2) {
auto l1 = layer1.Data, l2 = layer2.Data;
switch (col) {
case ColumnType::Key: return SortHelper::Sort(StringHelper::GuidToString(l1.LayerKey), StringHelper::GuidToString(l2.LayerKey), asc);
case ColumnType::Name: return SortHelper::Sort(l1.Name, l2.Name, asc);
case ColumnType::Desc: return SortHelper::Sort(l1.Desc, l2.Desc, asc);
case ColumnType::Flags: return SortHelper::Sort(l1.Flags, l2.Flags, asc);
case ColumnType::Fields: return SortHelper::Sort(l1.NumFields, l2.NumFields, asc);
case ColumnType::DefaultSubLayer: return SortHelper::Sort(l1.DefaultSublayer, l2.DefaultSublayer, asc);
case ColumnType::Id: return SortHelper::Sort(l1.LayerId, l2.LayerId, asc);
case ColumnType::Key: return SortHelper::Sort(StringHelper::GuidToString(l1->layerKey), StringHelper::GuidToString(l2->layerKey), asc);
case ColumnType::Name: return SortHelper::Sort(layer1.Name(), layer2.Name(), asc);
case ColumnType::Desc: return SortHelper::Sort(layer1.Desc(), layer2.Desc(), asc);
case ColumnType::Flags: return SortHelper::Sort(l1->flags, l2->flags, asc);
case ColumnType::Fields: return SortHelper::Sort(l1->numFields, l2->numFields, asc);
case ColumnType::DefaultSubLayer: return SortHelper::Sort(layer1.DefaultSublayer, layer2.DefaultSublayer, asc);
case ColumnType::Id: return SortHelper::Sort(l1->layerId, l2->layerId, asc);
}
return false;
};
@@ -129,13 +133,13 @@ void CLayersView::UpdateUI() {
int CLayersView::GetFilterCount(LayerInfo& layer) const {
if (layer.FilterCount < 0)
layer.FilterCount = m_Engine.GetFilterCount(layer.LayerKey);
layer.FilterCount = m_Engine.GetFilterCount(layer.Data->layerKey);
return layer.FilterCount;
}
int CLayersView::GetCalloutCount(LayerInfo& layer) const {
if (layer.CalloutCount < 0)
layer.CalloutCount = m_Engine.GetCalloutCount(layer.LayerKey);
layer.CalloutCount = m_Engine.GetCalloutCount(layer.Data->layerKey);
return layer.CalloutCount;
}
@@ -148,3 +152,15 @@ bool CLayersView::OnDoubleClickList(HWND, int row, int col, POINT const& pt) {
LRESULT result;
return ProcessWindowMessage(m_hWnd, WM_COMMAND, ID_EDIT_PROPERTIES, 0, result, 1);
}
CString const& CLayersView::LayerInfo::Name() const {
if (m_Name.IsEmpty())
m_Name = StringHelper::ParseMUIString(Data->displayData.name);
return m_Name;
}
CString const& CLayersView::LayerInfo::Desc() const {
if (m_Desc.IsEmpty())
m_Desc = StringHelper::ParseMUIString(Data->displayData.description);
return m_Desc;
}
+9 -2
View File
@@ -5,6 +5,7 @@
#include "Interfaces.h"
#include <WFPEngine.h>
#include "resource.h"
#include <WFPEnumerator.h>
class WFPEngine;
@@ -39,10 +40,16 @@ public:
// LRESULT NotifyHandler(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
private:
struct LayerInfo : WFPLayerInfo {
struct LayerInfo {
FWPM_LAYER* Data;
CString DefaultSublayer;
int FilterCount;
int CalloutCount;
CString const& Name() const;
CString const& Desc() const;
private:
mutable CString m_Name, m_Desc;
};
void UpdateUI();
@@ -61,5 +68,5 @@ private:
WFPEngine& m_Engine;
CListViewCtrl m_List;
std::vector<LayerInfo> m_Layers;
WFPObjectVector<FWPM_LAYER, LayerInfo> m_Layers;
};
+24 -10
View File
@@ -4,7 +4,7 @@
#include <SortHelper.h>
#include "resource.h"
CProvidersView::CProvidersView(IMainFrame* frame, WFPEngine& engine) : CFrameView(frame), m_Engine(engine), m_Enum(engine.Handle()) {
CProvidersView::CProvidersView(IMainFrame* frame, WFPEngine& engine) : CFrameView(frame), m_Engine(engine) {
}
LRESULT CProvidersView::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) {
@@ -37,19 +37,20 @@ LRESULT CProvidersView::OnRefresh(WORD, WORD, HWND, BOOL&) {
}
void CProvidersView::Refresh() {
m_Enum.Close();
m_Providers = m_Enum.Next(256);
WFPProviderEnumerator penum(m_Engine.Handle());
m_Providers = penum.Next<ProviderInfo>(256);
Sort(m_List);
m_List.SetItemCountEx((int)m_Providers.size(), LVSICF_NOSCROLL);
Frame()->SetStatusText(2, std::format(L"{} Providers", m_Providers.size()).c_str());
}
CString CProvidersView::GetColumnText(HWND, int row, int col) {
auto& info = m_Providers[row];
auto& pi = m_Providers[row];
auto info = pi.Data;
switch (GetColumnManager(m_List)->GetColumnTag<ColumnType>(col)) {
case ColumnType::Key: return StringHelper::GuidToString(info->providerKey);
case ColumnType::Name: return info->displayData.name;
case ColumnType::Desc: return info->displayData.description;
case ColumnType::Name: return pi.Name();
case ColumnType::Desc: return pi.Desc();
case ColumnType::ProviderData: return info->providerData.size == 0 ? L"" : std::format(L"{} Bytes", info->providerData.size).c_str();
case ColumnType::Flags:
if (info->flags == 0)
@@ -64,11 +65,12 @@ void CProvidersView::DoSort(SortInfo const* si) {
auto col = GetColumnManager(m_List)->GetColumnTag<ColumnType>(si->SortColumn);
auto asc = si->SortAscending;
auto compare = [&](auto& p1, auto& p2) {
auto compare = [&](auto& pr1, auto& pr2) {
auto p1 = pr1.Data, p2 = pr2.Data;
switch (col) {
case ColumnType::Key: return SortHelper::Sort(StringHelper::GuidToString(p1->providerKey), StringHelper::GuidToString(p2->providerKey), asc);
case ColumnType::Name: return SortHelper::Sort(p1->displayData.name, p2->displayData.name, asc);
case ColumnType::Desc: return SortHelper::Sort(p1->displayData.description, p2->displayData.description, asc);
case ColumnType::Name: return SortHelper::Sort(pr1.Name(), pr2.Name(), asc);
case ColumnType::Desc: return SortHelper::Sort(pr1.Desc(), pr2.Desc(), asc);
case ColumnType::Flags: return SortHelper::Sort(p1->flags, p2->flags, asc);
case ColumnType::ServiceName: return SortHelper::Sort(p1->serviceName, p2->serviceName, asc);
case ColumnType::ProviderData: return SortHelper::Sort(p1->providerData.size, p2->providerData.size, asc);
@@ -83,5 +85,17 @@ int CProvidersView::GetSaveColumnRange(HWND, int&) const {
}
int CProvidersView::GetRowImage(HWND, int row, int col) const {
return m_Providers[row]->flags & FWPM_PROVIDER_FLAG_PERSISTENT ? 1 : 0;
return m_Providers[row].Data->flags & FWPM_PROVIDER_FLAG_PERSISTENT ? 1 : 0;
}
CString const& CProvidersView::ProviderInfo::Name() const {
if (m_Name.IsEmpty())
m_Name = StringHelper::ParseMUIString(Data->displayData.name);
return m_Name;
}
CString const& CProvidersView::ProviderInfo::Desc() const {
if (m_Desc.IsEmpty())
m_Desc = StringHelper::ParseMUIString(Data->displayData.description);
return m_Desc;
}
+9 -2
View File
@@ -38,6 +38,14 @@ private:
enum class ColumnType {
Key, Name, Desc, Flags, ServiceName, ProviderData,
};
struct ProviderInfo {
FWPM_PROVIDER* Data;
CString const& Name() const;
CString const& Desc() const;
private:
mutable CString m_Name, m_Desc;
};
LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
LRESULT OnRefresh(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/);
@@ -45,6 +53,5 @@ private:
WFPEngine& m_Engine;
CListViewCtrl m_List;
std::vector<FWPM_PROVIDER*> m_Providers;
WFPProviderEnumerator m_Enum;
WFPObjectVector<FWPM_PROVIDER, ProviderInfo> m_Providers;
};
+1 -1
View File
@@ -49,6 +49,6 @@ private:
WFPEngine& m_Engine;
CListViewCtrl m_List;
std::vector<SessionInfo> m_Sessions;
WFPObjectVector<FWPM_SESSION, SessionInfo> m_Sessions;
WFPSessionEnumerator m_Enum;
};
+22 -10
View File
@@ -5,6 +5,18 @@
#pragma comment(lib, "ntdll")
CString StringHelper::ParseMUIString(PCWSTR input) {
if (input == nullptr)
return L"";
if (*input && input[0] == L'@') {
WCHAR result[256];
if (::SHLoadIndirectString(input, result, _countof(result), nullptr) == S_OK)
return result;
}
return input;
}
CString StringHelper::GuidToString(GUID const& guid) {
WCHAR sguid[64];
return ::StringFromGUID2(guid, sguid, _countof(sguid)) ? sguid : L"";
@@ -88,15 +100,15 @@ CString StringHelper::WFPFilterFlagsToString(WFPFilterFlags flags) {
return result;
}
CString StringHelper::WFPLayerFlagsToString(WFPLayerFlags flags) {
CString StringHelper::WFPLayerFlagsToString(DWORD flags) {
static const struct {
WFPLayerFlags flag;
DWORD flag;
PCWSTR text;
} data[] = {
{ WFPLayerFlags::Kernel, L"Kernel" },
{ WFPLayerFlags::BuiltIn, L"Builtin" },
{ WFPLayerFlags::ClassifyMostly, L"Class Modify" },
{ WFPLayerFlags::Buffered, L"Buffered" },
{ FWPM_LAYER_FLAG_KERNEL, L"Kernel" },
{ FWPM_LAYER_FLAG_BUILTIN, L"Builtin" },
{ FWPM_LAYER_FLAG_CLASSIFY_MOSTLY, L"Class Mostly" },
{ FWPM_LAYER_FLAG_BUFFERED, L"Buffered" },
};
return FlagsToString(flags, data);
}
@@ -390,11 +402,11 @@ CString StringHelper::FormatSID(PSID const sid) {
return L"";
}
PCWSTR StringHelper::WFPFieldTypeToString(WFPFieldType type) {
PCWSTR StringHelper::WFPFieldTypeToString(DWORD type) {
switch (type) {
case WFPFieldType::RawData: return L"Raw Data";
case WFPFieldType::IpAddress: return L"IP Address";
case WFPFieldType::Flags: return L"Flags";
case FWPM_FIELD_RAW_DATA: return L"Raw Data";
case FWPM_FIELD_IP_ADDRESS: return L"IP Address";
case FWPM_FIELD_FLAGS: return L"Flags";
}
ATLASSERT(false);
return L"";
+3 -3
View File
@@ -12,13 +12,13 @@ enum class WFPProviderContextType;
enum class WFPActionType;
enum class WFPMatchType;
enum class WFPDataType;
enum class WFPFieldType;
struct StringHelper abstract final {
static CString GuidToString(GUID const& guid);
static CString ParseMUIString(PCWSTR input);
static CString WFPValueToString(WFPValue const& value, bool hex = false, bool full = false);
static CString WFPFilterFlagsToString(WFPFilterFlags flags);
static CString WFPLayerFlagsToString(WFPLayerFlags flags);
static CString WFPLayerFlagsToString(DWORD flags);
static CString WFPSessionFlagsToString(WFPSessionFlags flags);
static CString WFPProviderFlagsToString(DWORD flags);
static CString WFPSubLayerFlagsToString(WFPSubLayerFlags flags);
@@ -31,7 +31,7 @@ struct StringHelper abstract final {
static CString WFPConditionFieldKeyToString(GUID const& key);
static CString FormatBinary(BYTE const* buffer, ULONG size, int lineSize = 16);
static CString FormatSID(PSID const sid);
static PCWSTR WFPFieldTypeToString(WFPFieldType type);
static PCWSTR WFPFieldTypeToString(DWORD type);
template<typename TFlags, typename TValue>
static CString FlagsToString(TValue value, TFlags const& data) {
+6 -5
View File
@@ -6,6 +6,7 @@
#include "LayerFieldsPage.h"
#include "FilterGeneralPage.h"
#include "FilterConditionsPage.h"
#include "LayersView.h"
CString WFPHelper::GetProviderName(WFPEngine const& engine, GUID const& key) {
if (key != GUID_NULL) {
@@ -30,8 +31,8 @@ CString WFPHelper::GetFilterName(WFPEngine const& engine, GUID const& key) {
CString WFPHelper::GetLayerName(WFPEngine const& engine, GUID const& key) {
if (key != GUID_NULL) {
auto layer = engine.GetLayerByKey(key);
if (layer && !layer->Name.empty() && layer->Name[0] != L'@')
return layer->Name.c_str();
if (layer && layer->displayData.name && layer->displayData.name[0] != L'@')
return layer->displayData.name;
return StringHelper::GuidToString(key);
}
return L"";
@@ -47,8 +48,8 @@ CString WFPHelper::GetSublayerName(WFPEngine const& engine, GUID const& key) {
return L"";
}
int WFPHelper::ShowLayerProperties(WFPEngine& engine, WFPLayerInfo const& layer) {
auto name = L"Layer Properties (" + GetLayerName(engine, layer.LayerKey) + L")";
int WFPHelper::ShowLayerProperties(WFPEngine& engine, FWPM_LAYER* layer) {
auto name = L"Layer Properties (" + GetLayerName(engine, layer->layerKey) + L")";
CPropertySheet sheet((PCWSTR)name);
sheet.m_psh.dwFlags |= PSH_NOAPPLYNOW | PSH_USEICONID | PSH_NOCONTEXTHELP | PSH_RESIZABLE;
sheet.m_psh.pszIcon = MAKEINTRESOURCE(IDI_LAYERS);
@@ -58,7 +59,7 @@ int WFPHelper::ShowLayerProperties(WFPEngine& engine, WFPLayerInfo const& layer)
sheet.AddPage(general);
CLayerFieldsPage fields(engine, layer);
if (layer.NumFields > 0) {
if (layer->numFields > 0) {
fields.m_psp.dwFlags |= PSP_USEICONID;
fields.m_psp.pszIcon = MAKEINTRESOURCE(IDI_FIELD);
sheet.AddPage(fields);
+2 -3
View File
@@ -1,9 +1,8 @@
#pragma once
class WFPEngine;
struct WFPLayerInfo;
struct WFPFilterInfo;
struct WFPProviderInfo;
struct WFPSubLayerInfo;
struct WFPHelper abstract final {
@@ -11,7 +10,7 @@ struct WFPHelper abstract final {
static CString GetFilterName(WFPEngine const& engine, GUID const& key);
static CString GetLayerName(WFPEngine const& engine, GUID const& key);
static CString GetSublayerName(WFPEngine const& engine, GUID const& key);
static int ShowLayerProperties(WFPEngine& engine, WFPLayerInfo const& layer);
static int ShowLayerProperties(WFPEngine& engine, FWPM_LAYER* layer);
static int ShowFilterProperties(WFPEngine& engine, WFPFilterInfo const& filter);
static int ShowSublayerProperties(WFPEngine& engine, WFPSubLayerInfo& sublayer);
static int ShowProviderProperties(WFPEngine& engine, FWPM_PROVIDER* provider);
+12 -10
View File
@@ -13,7 +13,9 @@ std::wstring GuidToString(GUID const& guid) {
return ::StringFromGUID2(guid, sguid, _countof(sguid)) ? sguid : L"";
}
void DisplaySessions(std::vector<WFPSessionInfo> const& sessions) {
void DisplaySessions(WFPEngine& engine) {
WFPSessionEnumerator enumerator(engine.Handle());
auto sessions = enumerator.Next(256);
printf("Total sessions: %u\n", (UINT32)sessions.size());
int n = printf("%-39s %-6s %-28s %6s %-28s %s\n",
"Key", "PID", "User name", "Flags", "Name", "Description");
@@ -21,12 +23,12 @@ void DisplaySessions(std::vector<WFPSessionInfo> const& sessions) {
for (auto& session : sessions) {
printf("%ws %6u %-28ws %6X %-28ws %ws\n",
GuidToString(session.SessionKey).c_str(),
session.ProcessId,
session.UserName.c_str(),
session.Flags,
session.Name.c_str(),
session.Desc.c_str());
GuidToString(session->sessionKey).c_str(),
session->processId,
session->username,
session->flags,
session->displayData.name,
session->displayData.description);
}
}
@@ -110,11 +112,11 @@ int main(int argc, const char* argv[]) {
switch (argv[1][0]) {
case 's': case 'S':
DisplaySessions(engine.EnumSessions());
DisplaySessions(engine);
break;
case 'p': case 'P':
// DisplayProviders(engine.EnumProviders());
//DisplayProviders(engine);
break;
case 'l': case 'L':
@@ -126,7 +128,7 @@ int main(int argc, const char* argv[]) {
break;
case 'c': case 'C':
DisplayCallouts(engine.EnumCallouts());
//DisplayCallouts(engine.EnumCallouts());
break;
default: