diff --git a/2.x/trunk/CHANGELOG.txt b/2.x/trunk/CHANGELOG.txt
index dd5cc42ed..d32063f4f 100644
--- a/2.x/trunk/CHANGELOG.txt
+++ b/2.x/trunk/CHANGELOG.txt
@@ -2,6 +2,7 @@ Process Hacker
2.17
* NEW/IMPROVED:
+ * Updated ExtendedServices plugin
* Updated WindowExplorer plugin
* FIXED:
diff --git a/2.x/trunk/plugins/ExtendedServices/CHANGELOG.txt b/2.x/trunk/plugins/ExtendedServices/CHANGELOG.txt
index 5e04b5b2f..f30aa969c 100644
--- a/2.x/trunk/plugins/ExtendedServices/CHANGELOG.txt
+++ b/2.x/trunk/plugins/ExtendedServices/CHANGELOG.txt
@@ -1,3 +1,6 @@
+1.5
+ * Added support for editing triggers
+
1.4
* Improved Services submenu when there is only one service
diff --git a/2.x/trunk/plugins/ExtendedServices/ExtendedServices.rc b/2.x/trunk/plugins/ExtendedServices/ExtendedServices.rc
index edab2e004..221a436cc 100644
--- a/2.x/trunk/plugins/ExtendedServices/ExtendedServices.rc
+++ b/2.x/trunk/plugins/ExtendedServices/ExtendedServices.rc
@@ -51,8 +51,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,4,0,0
- PRODUCTVERSION 1,4,0,0
+ FILEVERSION 1,5,0,0
+ PRODUCTVERSION 1,5,0,0
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -69,12 +69,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "wj32"
VALUE "FileDescription", "Extended Services for Process Hacker"
- VALUE "FileVersion", "1.4"
+ VALUE "FileVersion", "1.5"
VALUE "InternalName", "ExtendedServices"
VALUE "LegalCopyright", "Licensed under the GNU GPL, v3."
VALUE "OriginalFilename", "ExtendedServices.dll"
VALUE "ProductName", "Extended Services for Process Hacker"
- VALUE "ProductVersion", "1.4"
+ VALUE "ProductVersion", "1.5"
END
END
BLOCK "VarFileInfo"
@@ -159,7 +159,7 @@ BEGIN
CONTROL "",IDC_PROGRESS,"msctls_progress32",WS_BORDER,7,21,190,14
END
-IDD_SRVOTHER DIALOGEX 0, 0, 282, 183
+IDD_SRVOTHER DIALOGEX 0, 0, 282, 204
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Other"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
@@ -170,7 +170,10 @@ BEGIN
LTEXT "Required privileges:",IDC_STATIC,7,21,64,8
CONTROL "",IDC_PRIVILEGES,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,32,268,59
LTEXT "Triggers:",IDC_TRIGGERS_LABEL,7,95,30,8
- CONTROL "",IDC_TRIGGERS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,106,268,70
+ CONTROL "",IDC_TRIGGERS,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,7,106,268,73
+ PUSHBUTTON "New...",IDC_NEW,117,183,50,14
+ PUSHBUTTON "Edit...",IDC_EDIT,171,183,50,14
+ PUSHBUTTON "Delete",IDC_DELETE,225,183,50,14
END
IDD_OPTIONS DIALOGEX 0, 0, 215, 54
@@ -184,6 +187,38 @@ BEGIN
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,7,142,10
END
+IDD_SRVTRIGGER DIALOGEX 0, 0, 330, 196
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Trigger"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,219,175,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,273,175,50,14
+ LTEXT "Type:",IDC_STATIC,7,9,20,8
+ COMBOBOX IDC_TYPE,52,7,271,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Subtype:",IDC_STATIC,7,26,30,8
+ COMBOBOX IDC_SUBTYPE,52,24,271,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ EDITTEXT IDC_SUBTYPECUSTOM,52,41,271,12,ES_AUTOHSCROLL
+ LTEXT "Action:",IDC_STATIC,7,59,24,8
+ COMBOBOX IDC_ACTION,52,58,72,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Data items:",IDC_STATIC,7,76,38,8
+ CONTROL "",IDC_LIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,52,79,217,90
+ PUSHBUTTON "New...",IDC_NEW,273,79,50,14
+ PUSHBUTTON "Edit...",IDC_EDIT,273,96,50,14
+ PUSHBUTTON "Delete",IDC_DELETE,273,113,50,14
+END
+
+IDD_VALUE DIALOGEX 0, 0, 267, 167
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Edit Data"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,156,146,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,210,146,50,14
+ LTEXT "Values (one per line):",IDC_STATIC,7,7,69,8
+ EDITTEXT IDC_VALUES,7,18,253,124,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL
+END
+
/////////////////////////////////////////////////////////////////////////////
//
@@ -238,7 +273,7 @@ BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 275
TOPMARGIN, 7
- BOTTOMMARGIN, 176
+ BOTTOMMARGIN, 197
END
IDD_OPTIONS, DIALOG
@@ -248,6 +283,22 @@ BEGIN
TOPMARGIN, 7
BOTTOMMARGIN, 47
END
+
+ IDD_SRVTRIGGER, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 323
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 189
+ END
+
+ IDD_VALUE, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 260
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 160
+ END
END
#endif // APSTUDIO_INVOKED
diff --git a/2.x/trunk/plugins/ExtendedServices/ExtendedServices.vcxproj b/2.x/trunk/plugins/ExtendedServices/ExtendedServices.vcxproj
index 6557fb1f2..6c597692c 100644
--- a/2.x/trunk/plugins/ExtendedServices/ExtendedServices.vcxproj
+++ b/2.x/trunk/plugins/ExtendedServices/ExtendedServices.vcxproj
@@ -182,6 +182,7 @@
+
diff --git a/2.x/trunk/plugins/ExtendedServices/ExtendedServices.vcxproj.filters b/2.x/trunk/plugins/ExtendedServices/ExtendedServices.vcxproj.filters
index 8ba2397f0..d23ac0663 100644
--- a/2.x/trunk/plugins/ExtendedServices/ExtendedServices.vcxproj.filters
+++ b/2.x/trunk/plugins/ExtendedServices/ExtendedServices.vcxproj.filters
@@ -33,6 +33,9 @@
Source Files
+
+ Source Files
+
diff --git a/2.x/trunk/plugins/ExtendedServices/extsrv.h b/2.x/trunk/plugins/ExtendedServices/extsrv.h
index 395509b85..6e3f2af84 100644
--- a/2.x/trunk/plugins/ExtendedServices/extsrv.h
+++ b/2.x/trunk/plugins/ExtendedServices/extsrv.h
@@ -71,4 +71,38 @@ VOID EsRestartServiceWithProgress(
__in SC_HANDLE ServiceHandle
);
+// trigger
+
+struct _ES_TRIGGER_CONTEXT;
+
+struct _ES_TRIGGER_CONTEXT *EsCreateServiceTriggerContext(
+ __in PPH_SERVICE_ITEM ServiceItem,
+ __in HWND WindowHandle,
+ __in HWND TriggersLv
+ );
+
+VOID EsDestroyServiceTriggerContext(
+ __in struct _ES_TRIGGER_CONTEXT *Context
+ );
+
+VOID EsLoadServiceTriggerInfo(
+ __in struct _ES_TRIGGER_CONTEXT *Context,
+ __in SC_HANDLE ServiceHandle
+ );
+
+BOOLEAN EsSaveServiceTriggerInfo(
+ __in struct _ES_TRIGGER_CONTEXT *Context,
+ __out PULONG Win32Result
+ );
+
+#define ES_TRIGGER_EVENT_NEW 1
+#define ES_TRIGGER_EVENT_EDIT 2
+#define ES_TRIGGER_EVENT_DELETE 3
+#define ES_TRIGGER_EVENT_SELECTIONCHANGED 4
+
+VOID EsHandleEventServiceTrigger(
+ __in struct _ES_TRIGGER_CONTEXT *Context,
+ __in ULONG Event
+ );
+
#endif
diff --git a/2.x/trunk/plugins/ExtendedServices/other.c b/2.x/trunk/plugins/ExtendedServices/other.c
index 4611827ed..64b0a0383 100644
--- a/2.x/trunk/plugins/ExtendedServices/other.c
+++ b/2.x/trunk/plugins/ExtendedServices/other.c
@@ -31,6 +31,7 @@ typedef struct _SERVICE_OTHER_CONTEXT
HWND PrivilegesLv;
HWND TriggersLv;
+ struct _ES_TRIGGER_CONTEXT *TriggerContext;
} SERVICE_OTHER_CONTEXT, *PSERVICE_OTHER_CONTEXT;
NTSTATUS EspLoadOtherInfo(
@@ -42,9 +43,7 @@ NTSTATUS EspLoadOtherInfo(
SC_HANDLE serviceHandle;
SERVICE_PRESHUTDOWN_INFO preshutdownInfo;
LPSERVICE_REQUIRED_PRIVILEGES_INFO requiredPrivileges;
- PSERVICE_TRIGGER_INFO triggerInfo;
ULONG returnLength;
- ULONG i;
if (!(serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_QUERY_CONFIG)))
return NTSTATUS_FROM_WIN32(GetLastError());
@@ -103,171 +102,9 @@ NTSTATUS EspLoadOtherInfo(
// Triggers
- if (WindowsVersion >= WINDOWS_7 && (triggerInfo = PhQueryServiceVariableSize(serviceHandle, SERVICE_CONFIG_TRIGGER_INFO)))
+ if (Context->TriggerContext)
{
- for (i = 0; i < triggerInfo->cTriggers; i++)
- {
- static GUID networkManagerFirstIpAddressArrivalGuid = { 0x4f27f2de, 0x14e2, 0x430b, { 0xa5, 0x49, 0x7c, 0xd4, 0x8c, 0xbc, 0x82, 0x45 } };
- static GUID networkManagerLastIpAddressRemovalGuid = { 0xcc4ba62a, 0x162e, 0x4648, { 0x84, 0x7a, 0xb6, 0xbd, 0xf9, 0x93, 0xe3, 0x35 } };
- static GUID domainJoinGuid = { 0x1ce20aba, 0x9851, 0x4421, { 0x94, 0x30, 0x1d, 0xde, 0xb7, 0x66, 0xe8, 0x09 } };
- static GUID domainLeaveGuid = { 0xddaf516e, 0x58c2, 0x4866, { 0x95, 0x74, 0xc3, 0xb6, 0x15, 0xd4, 0x2e, 0xa1 } };
- static GUID firewallPortOpenGuid = { 0xb7569e07, 0x8421, 0x4ee0, { 0xad, 0x10, 0x86, 0x91, 0x5a, 0xfd, 0xad, 0x09 } };
- static GUID firewallPortCloseGuid = { 0xa144ed38, 0x8e12, 0x4de4, { 0x9d, 0x96, 0xe6, 0x47, 0x40, 0xb1, 0xa5, 0x24 } };
- static GUID machinePolicyPresentGuid = { 0x659fcae6, 0x5bdb, 0x4da9, { 0xb1, 0xff, 0xca, 0x2a, 0x17, 0x8d, 0x46, 0xe0 } };
- static GUID userPolicyPresentGuid = { 0x54fb46c8, 0xf089, 0x464c, { 0xb1, 0xfd, 0x59, 0xd1, 0xb6, 0x2c, 0x3b, 0x50 } };
-
- PSERVICE_TRIGGER trigger = &triggerInfo->pTriggers[i];
- PPH_STRING stringUsed = NULL;
- PWSTR triggerString;
- PWSTR actionString;
- INT lvItemIndex;
-
- switch (trigger->dwTriggerType)
- {
- case SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL:
- {
- PPH_STRING guidString;
-
- if (!trigger->pTriggerSubtype)
- {
- triggerString = L"Device interface arrival";
- }
- else
- {
- guidString = PhFormatGuid(trigger->pTriggerSubtype);
- stringUsed = PhConcatStrings2(L"Device interface arrival: ", guidString->Buffer);
- triggerString = stringUsed->Buffer;
- }
- }
- break;
- case SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY:
- if (!trigger->pTriggerSubtype)
- triggerString = L"IP address";
- else if (memcmp(trigger->pTriggerSubtype, &networkManagerFirstIpAddressArrivalGuid, sizeof(GUID)) == 0)
- triggerString = L"IP address: First arrival";
- else if (memcmp(trigger->pTriggerSubtype, &networkManagerLastIpAddressRemovalGuid, sizeof(GUID)) == 0)
- triggerString = L"IP address: Last removal";
- else
- triggerString = L"IP address: Unknown";
- break;
- case SERVICE_TRIGGER_TYPE_DOMAIN_JOIN:
- if (!trigger->pTriggerSubtype)
- triggerString = L"Domain";
- else if (memcmp(trigger->pTriggerSubtype, &domainJoinGuid, sizeof(GUID)) == 0)
- triggerString = L"Domain: Join";
- else if (memcmp(trigger->pTriggerSubtype, &domainLeaveGuid, sizeof(GUID)) == 0)
- triggerString = L"Domain: Leave";
- else
- triggerString = L"Domain: Unknown";
- break;
- case SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT:
- if (!trigger->pTriggerSubtype)
- triggerString = L"Firewall port";
- else if (memcmp(trigger->pTriggerSubtype, &firewallPortOpenGuid, sizeof(GUID)) == 0)
- triggerString = L"Firewall port: Open";
- else if (memcmp(trigger->pTriggerSubtype, &firewallPortCloseGuid, sizeof(GUID)) == 0)
- triggerString = L"Firewall port: Close";
- else
- triggerString = L"Firewall port: Unknown";
- break;
- case SERVICE_TRIGGER_TYPE_GROUP_POLICY:
- if (!trigger->pTriggerSubtype)
- triggerString = L"Group policy change";
- else if (memcmp(trigger->pTriggerSubtype, &machinePolicyPresentGuid, sizeof(GUID)) == 0)
- triggerString = L"Group policy change: Machine";
- else if (memcmp(trigger->pTriggerSubtype, &userPolicyPresentGuid, sizeof(GUID)) == 0)
- triggerString = L"Group policy change: User";
- else
- triggerString = L"Group policy change: Unknown";
- break;
- case SERVICE_TRIGGER_TYPE_CUSTOM:
- {
- PPH_STRING guidString;
- PPH_STRING keyName;
- HKEY keyHandle;
- PPH_STRING publisherName = NULL;
-
- if (trigger->pTriggerSubtype)
- {
- // Try to lookup the publisher name from the GUID.
- // Copied from ProcessHacker\hndlinfo.c.
-
- guidString = PhFormatGuid(trigger->pTriggerSubtype);
-
- keyName = PhConcatStrings2(
- L"Software\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\",
- guidString->Buffer
- );
-
- if (RegOpenKeyEx(
- HKEY_LOCAL_MACHINE,
- keyName->Buffer,
- 0,
- KEY_READ,
- &keyHandle
- ) == ERROR_SUCCESS)
- {
- publisherName = PhQueryRegistryString(keyHandle, NULL);
-
- if (publisherName && publisherName->Length == 0)
- {
- PhDereferenceObject(publisherName);
- publisherName = NULL;
- }
-
- RegCloseKey(keyHandle);
- }
-
- PhDereferenceObject(keyName);
-
- if (publisherName)
- {
- stringUsed = PhFormatString(L"Custom: %s", publisherName->Buffer);
- PhDereferenceObject(publisherName);
- }
- else
- {
- stringUsed = PhFormatString(L"Custom: %s", guidString->Buffer);
- }
-
- PhDereferenceObject(guidString);
-
- triggerString = stringUsed->Buffer;
- }
- else
- {
- triggerString = L"Custom";
- }
- }
- break;
- default:
- triggerString = L"Unknown";
- break;
- }
-
- switch (trigger->dwAction)
- {
- case SERVICE_TRIGGER_ACTION_SERVICE_START:
- actionString = L"Start";
- break;
- case SERVICE_TRIGGER_ACTION_SERVICE_STOP:
- actionString = L"Stop";
- break;
- default:
- actionString = L"Unknown";
- break;
- }
-
- lvItemIndex = PhAddListViewItem(Context->TriggersLv, MAXINT, triggerString, NULL);
- PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString);
-
- if (stringUsed)
- PhDereferenceObject(stringUsed);
- }
-
- ExtendedListView_SortItems(Context->TriggersLv);
-
- PhFree(triggerInfo);
+ EsLoadServiceTriggerInfo(Context->TriggerContext, serviceHandle);
}
CloseServiceHandle(serviceHandle);
@@ -322,16 +159,22 @@ INT_PTR CALLBACK EspServiceOtherDlgProc(
PhSetExtendedListView(privilegesLv);
context->TriggersLv = triggersLv = GetDlgItem(hwndDlg, IDC_TRIGGERS);
- PhSetListViewStyle(triggersLv, FALSE, TRUE);
- PhSetControlTheme(triggersLv, L"explorer");
- PhAddListViewColumn(triggersLv, 0, 0, 0, LVCFMT_LEFT, 300, L"Trigger");
- PhAddListViewColumn(triggersLv, 1, 1, 1, LVCFMT_LEFT, 60, L"Action");
- PhSetExtendedListView(triggersLv);
- if (WindowsVersion < WINDOWS_7)
+ if (WindowsVersion >= WINDOWS_7)
+ {
+ context->TriggerContext = EsCreateServiceTriggerContext(
+ context->ServiceItem,
+ hwndDlg,
+ triggersLv
+ );
+ }
+ else
{
EnableWindow(GetDlgItem(hwndDlg, IDC_TRIGGERS_LABEL), FALSE);
EnableWindow(triggersLv, FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_NEW), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE);
}
status = EspLoadOtherInfo(hwndDlg, context);
@@ -345,9 +188,84 @@ INT_PTR CALLBACK EspServiceOtherDlgProc(
break;
case WM_DESTROY:
{
+ if (context->TriggerContext)
+ EsDestroyServiceTriggerContext(context->TriggerContext);
+
PhFree(context);
}
break;
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_NEW:
+ if (context->TriggerContext)
+ EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_NEW);
+ break;
+ case IDC_EDIT:
+ if (context->TriggerContext)
+ EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_EDIT);
+ break;
+ case IDC_DELETE:
+ if (context->TriggerContext)
+ EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_DELETE);
+ break;
+ }
+ }
+ break;
+ case WM_NOTIFY:
+ {
+ LPNMHDR header = (LPNMHDR)lParam;
+
+ switch (header->code)
+ {
+ case PSN_KILLACTIVE:
+ {
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, FALSE);
+ }
+ return TRUE;
+ case PSN_APPLY:
+ {
+ ULONG win32Result;
+
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
+
+ if (context->TriggerContext)
+ {
+ if (!EsSaveServiceTriggerInfo(context->TriggerContext, &win32Result))
+ {
+ if (PhShowMessage(
+ hwndDlg,
+ MB_ICONERROR | MB_RETRYCANCEL,
+ L"Unable to change service trigger information: %s",
+ ((PPH_STRING)PHA_DEREFERENCE(PhGetWin32Message(win32Result)))->Buffer
+ ) == IDRETRY)
+ {
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID);
+ }
+ }
+ }
+ }
+ return TRUE;
+ case LVN_ITEMCHANGED:
+ {
+ if (header->hwndFrom == context->TriggersLv && context->TriggerContext)
+ {
+ EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_SELECTIONCHANGED);
+ }
+ }
+ break;
+ case NM_DBLCLK:
+ {
+ if (header->hwndFrom == context->TriggersLv && context->TriggerContext)
+ {
+ EsHandleEventServiceTrigger(context->TriggerContext, ES_TRIGGER_EVENT_EDIT);
+ }
+ }
+ break;
+ }
+ }
+ break;
}
return FALSE;
diff --git a/2.x/trunk/plugins/ExtendedServices/resource.h b/2.x/trunk/plugins/ExtendedServices/resource.h
index a47dadc1c..ac9252b48 100644
--- a/2.x/trunk/plugins/ExtendedServices/resource.h
+++ b/2.x/trunk/plugins/ExtendedServices/resource.h
@@ -16,6 +16,8 @@
#define IDD_SRVOTHER 106
#define ID_SERVICE_PAUSE 106
#define IDD_OPTIONS 107
+#define IDD_SRVTRIGGER 108
+#define IDD_VALUE 109
#define IDC_SERVICES_LAYOUT 1001
#define IDC_MESSAGE 1002
#define IDC_FIRSTFAILURE 1003
@@ -42,14 +44,24 @@
#define IDC_TRIGGERS 1026
#define IDC_TRIGGERS_LABEL 1027
#define IDC_ENABLESERVICESMENU 1028
+#define IDC_TYPE 1029
+#define IDC_SUBTYPE 1030
+#define IDC_SUBTYPECUSTOM 1031
+#define IDC_ACTION 1032
+#define IDC_LIST1 1033
+#define IDC_LIST 1033
+#define IDC_NEW 1034
+#define IDC_EDIT 1035
+#define IDC_DELETE 1037
+#define IDC_VALUES 1038
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 108
+#define _APS_NEXT_RESOURCE_VALUE 110
#define _APS_NEXT_COMMAND_VALUE 40001
-#define _APS_NEXT_CONTROL_VALUE 1029
+#define _APS_NEXT_CONTROL_VALUE 1042
#define _APS_NEXT_SYMED_VALUE 107
#endif
#endif
diff --git a/2.x/trunk/plugins/ExtendedServices/trigger.c b/2.x/trunk/plugins/ExtendedServices/trigger.c
new file mode 100644
index 000000000..636277a57
--- /dev/null
+++ b/2.x/trunk/plugins/ExtendedServices/trigger.c
@@ -0,0 +1,1605 @@
+/*
+ * Process Hacker Extended Services -
+ * trigger editor
+ *
+ * Copyright (C) 2011 wj32
+ *
+ * This file is part of Process Hacker.
+ *
+ * Process Hacker is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Process Hacker is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Process Hacker. If not, see .
+ */
+
+#include
+#include
+#include "extsrv.h"
+#include "resource.h"
+
+typedef struct _ES_TRIGGER_DATA
+{
+ ULONG Type;
+ union
+ {
+ PPH_STRING String;
+ struct
+ {
+ PVOID Binary;
+ ULONG BinaryLength;
+ };
+ };
+} ES_TRIGGER_DATA, *PES_TRIGGER_DATA;
+
+typedef struct _ES_TRIGGER_INFO
+{
+ ULONG Type;
+ PGUID Subtype;
+ ULONG Action;
+ PPH_LIST DataList;
+ GUID SubtypeBuffer;
+} ES_TRIGGER_INFO, *PES_TRIGGER_INFO;
+
+typedef struct _ES_TRIGGER_CONTEXT
+{
+ PPH_SERVICE_ITEM ServiceItem;
+ HWND WindowHandle;
+ HWND TriggersLv;
+ BOOLEAN Dirty;
+ ULONG InitialNumberOfTriggers;
+ PPH_LIST InfoList;
+
+ // Trigger dialog box
+ PES_TRIGGER_INFO EditingInfo;
+ ULONG LastSelectedType;
+ PPH_STRING LastCustomSubType;
+
+ // Value dialog box
+ PPH_STRING EditingValue;
+} ES_TRIGGER_CONTEXT, *PES_TRIGGER_CONTEXT;
+
+typedef struct _TYPE_ENTRY
+{
+ ULONG Type;
+ PWSTR Name;
+} TYPE_ENTRY, PTYPE_ENTRY;
+
+typedef struct _SUBTYPE_ENTRY
+{
+ ULONG Type;
+ PGUID Guid;
+ PWSTR Name;
+} SUBTYPE_ENTRY, PSUBTYPE_ENTRY;
+
+typedef struct _ETW_PUBLISHER_ENTRY
+{
+ PPH_STRING PublisherName;
+ GUID Guid;
+} ETW_PUBLISHER_ENTRY, *PETW_PUBLISHER_ENTRY;
+
+INT_PTR CALLBACK EspServiceTriggerDlgProc(
+ __in HWND hwndDlg,
+ __in UINT uMsg,
+ __in WPARAM wParam,
+ __in LPARAM lParam
+ );
+
+INT_PTR CALLBACK ValueDlgProc(
+ __in HWND hwndDlg,
+ __in UINT uMsg,
+ __in WPARAM wParam,
+ __in LPARAM lParam
+ );
+
+static GUID NetworkManagerFirstIpAddressArrivalGuid = { 0x4f27f2de, 0x14e2, 0x430b, { 0xa5, 0x49, 0x7c, 0xd4, 0x8c, 0xbc, 0x82, 0x45 } };
+static GUID NetworkManagerLastIpAddressRemovalGuid = { 0xcc4ba62a, 0x162e, 0x4648, { 0x84, 0x7a, 0xb6, 0xbd, 0xf9, 0x93, 0xe3, 0x35 } };
+static GUID DomainJoinGuid = { 0x1ce20aba, 0x9851, 0x4421, { 0x94, 0x30, 0x1d, 0xde, 0xb7, 0x66, 0xe8, 0x09 } };
+static GUID DomainLeaveGuid = { 0xddaf516e, 0x58c2, 0x4866, { 0x95, 0x74, 0xc3, 0xb6, 0x15, 0xd4, 0x2e, 0xa1 } };
+static GUID FirewallPortOpenGuid = { 0xb7569e07, 0x8421, 0x4ee0, { 0xad, 0x10, 0x86, 0x91, 0x5a, 0xfd, 0xad, 0x09 } };
+static GUID FirewallPortCloseGuid = { 0xa144ed38, 0x8e12, 0x4de4, { 0x9d, 0x96, 0xe6, 0x47, 0x40, 0xb1, 0xa5, 0x24 } };
+static GUID MachinePolicyPresentGuid = { 0x659fcae6, 0x5bdb, 0x4da9, { 0xb1, 0xff, 0xca, 0x2a, 0x17, 0x8d, 0x46, 0xe0 } };
+static GUID UserPolicyPresentGuid = { 0x54fb46c8, 0xf089, 0x464c, { 0xb1, 0xfd, 0x59, 0xd1, 0xb6, 0x2c, 0x3b, 0x50 } };
+static GUID SubTypeUnknownGuid; // dummy
+
+static TYPE_ENTRY TypeEntries[] =
+{
+ { SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL, L"Device interface arrival" },
+ { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, L"IP address availability" },
+ { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, L"Domain join" },
+ { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, L"Firewall port event" },
+ { SERVICE_TRIGGER_TYPE_GROUP_POLICY, L"Group policy" },
+ { SERVICE_TRIGGER_TYPE_CUSTOM, L"Custom" }
+};
+
+static SUBTYPE_ENTRY SubTypeEntries[] =
+{
+ { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, NULL, L"IP address" },
+ { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &NetworkManagerFirstIpAddressArrivalGuid, L"IP address: First arrival" },
+ { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &NetworkManagerLastIpAddressRemovalGuid, L"IP address: Last removal" },
+ { SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY, &SubTypeUnknownGuid, L"IP address: Unknown" },
+ { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, NULL, L"Domain" },
+ { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &DomainJoinGuid, L"Domain: Join" },
+ { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &DomainLeaveGuid, L"Domain: Leave" },
+ { SERVICE_TRIGGER_TYPE_DOMAIN_JOIN, &SubTypeUnknownGuid, L"Domain: Unknown" },
+ { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, NULL, L"Firewall port" },
+ { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &FirewallPortOpenGuid, L"Firewall port: Open" },
+ { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &FirewallPortCloseGuid, L"Firewall port: Close" },
+ { SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT, &SubTypeUnknownGuid, L"Firewall port: Unknown" },
+ { SERVICE_TRIGGER_TYPE_GROUP_POLICY, NULL, L"Group policy change" },
+ { SERVICE_TRIGGER_TYPE_GROUP_POLICY, &MachinePolicyPresentGuid, L"Group policy change: Machine" },
+ { SERVICE_TRIGGER_TYPE_GROUP_POLICY, &UserPolicyPresentGuid, L"Group policy change: User" },
+ { SERVICE_TRIGGER_TYPE_GROUP_POLICY, &SubTypeUnknownGuid, L"Group policy change: Unknown" }
+};
+
+static PH_STRINGREF PublishersKeyName = PH_STRINGREF_INIT(L"Software\\Microsoft\\Windows\\CurrentVersion\\WINEVT\\Publishers\\");
+
+PES_TRIGGER_DATA EspCreateTriggerData(
+ __in_opt PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM DataItem
+ )
+{
+ PES_TRIGGER_DATA data;
+
+ data = PhAllocate(sizeof(ES_TRIGGER_DATA));
+ memset(data, 0, sizeof(ES_TRIGGER_DATA));
+
+ if (DataItem)
+ {
+ data->Type = DataItem->dwDataType;
+
+ if (data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
+ {
+ if (DataItem->pData && DataItem->cbData >= 2)
+ data->String = PhCreateStringEx((PWSTR)DataItem->pData, DataItem->cbData - 2); // exclude final null terminator
+ else
+ data->String = PhReferenceEmptyString();
+ }
+ else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY)
+ {
+ data->BinaryLength = DataItem->cbData;
+ data->Binary = PhAllocateCopy(DataItem->pData, DataItem->cbData);
+ }
+ }
+
+ return data;
+}
+
+PES_TRIGGER_DATA EspCloneTriggerData(
+ __in PES_TRIGGER_DATA Data
+ )
+{
+ PES_TRIGGER_DATA newData;
+
+ newData = PhAllocateCopy(Data, sizeof(ES_TRIGGER_DATA));
+
+ if (newData->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
+ {
+ if (newData->String)
+ newData->String = PhDuplicateString(newData->String);
+ }
+ else if (newData->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY)
+ {
+ if (newData->Binary)
+ newData->Binary = PhAllocateCopy(newData->Binary, newData->BinaryLength);
+ }
+
+ return newData;
+}
+
+VOID EspDestroyTriggerData(
+ __in PES_TRIGGER_DATA Data
+ )
+{
+ if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
+ {
+ if (Data->String)
+ PhDereferenceObject(Data->String);
+ }
+ else if (Data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY)
+ {
+ if (Data->Binary)
+ PhFree(Data->Binary);
+ }
+
+ PhFree(Data);
+}
+
+PES_TRIGGER_INFO EspCreateTriggerInfo(
+ __in_opt PSERVICE_TRIGGER Trigger
+ )
+{
+ PES_TRIGGER_INFO info;
+
+ info = PhAllocate(sizeof(ES_TRIGGER_INFO));
+ memset(info, 0, sizeof(ES_TRIGGER_INFO));
+
+ if (Trigger)
+ {
+ info->Type = Trigger->dwTriggerType;
+
+ if (Trigger->pTriggerSubtype)
+ {
+ info->SubtypeBuffer = *Trigger->pTriggerSubtype;
+ info->Subtype = &info->SubtypeBuffer;
+ }
+
+ info->Action = Trigger->dwAction;
+
+ if (
+ info->Type == SERVICE_TRIGGER_TYPE_CUSTOM ||
+ info->Type == SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL ||
+ info->Type == SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT
+ )
+ {
+ ULONG i;
+
+ info->DataList = PhCreateList(Trigger->cDataItems);
+
+ for (i = 0; i < Trigger->cDataItems; i++)
+ {
+ PhAddItemList(info->DataList, EspCreateTriggerData(&Trigger->pDataItems[i]));
+ }
+ }
+ }
+
+ return info;
+}
+
+PES_TRIGGER_INFO EspCloneTriggerInfo(
+ __in PES_TRIGGER_INFO Info
+ )
+{
+ PES_TRIGGER_INFO newInfo;
+
+ newInfo = PhAllocateCopy(Info, sizeof(ES_TRIGGER_INFO));
+
+ if (newInfo->Subtype == &Info->SubtypeBuffer)
+ newInfo->Subtype = &newInfo->SubtypeBuffer;
+
+ if (newInfo->DataList)
+ {
+ ULONG i;
+
+ newInfo->DataList = PhCreateList(Info->DataList->AllocatedCount);
+ newInfo->DataList->Count = Info->DataList->Count;
+
+ for (i = 0; i < Info->DataList->Count; i++)
+ newInfo->DataList->Items[i] = EspCloneTriggerData(Info->DataList->Items[i]);
+ }
+
+ return newInfo;
+}
+
+VOID EspDestroyTriggerInfo(
+ __in PES_TRIGGER_INFO Info
+ )
+{
+ if (Info->DataList)
+ {
+ ULONG i;
+
+ for (i = 0; i < Info->DataList->Count; i++)
+ {
+ EspDestroyTriggerData(Info->DataList->Items[i]);
+ }
+ }
+
+ PhFree(Info);
+}
+
+VOID EspClearTriggerInfoList(
+ __in PPH_LIST List
+ )
+{
+ ULONG i;
+
+ for (i = 0; i < List->Count; i++)
+ {
+ EspDestroyTriggerInfo(List->Items[i]);
+ }
+
+ PhClearList(List);
+}
+
+struct _ES_TRIGGER_CONTEXT *EsCreateServiceTriggerContext(
+ __in PPH_SERVICE_ITEM ServiceItem,
+ __in HWND WindowHandle,
+ __in HWND TriggersLv
+ )
+{
+ PES_TRIGGER_CONTEXT context;
+
+ context = PhAllocate(sizeof(ES_TRIGGER_CONTEXT));
+ memset(context, 0, sizeof(ES_TRIGGER_CONTEXT));
+ context->ServiceItem = ServiceItem;
+ context->WindowHandle = WindowHandle;
+ context->TriggersLv = TriggersLv;
+ context->InfoList = PhCreateList(4);
+
+ PhSetListViewStyle(TriggersLv, FALSE, TRUE);
+ PhSetControlTheme(TriggersLv, L"explorer");
+ PhAddListViewColumn(TriggersLv, 0, 0, 0, LVCFMT_LEFT, 300, L"Trigger");
+ PhAddListViewColumn(TriggersLv, 1, 1, 1, LVCFMT_LEFT, 60, L"Action");
+ PhSetExtendedListView(TriggersLv);
+
+ EnableWindow(GetDlgItem(WindowHandle, IDC_EDIT), FALSE);
+ EnableWindow(GetDlgItem(WindowHandle, IDC_DELETE), FALSE);
+
+ return context;
+}
+
+VOID EsDestroyServiceTriggerContext(
+ __in struct _ES_TRIGGER_CONTEXT *Context
+ )
+{
+ ULONG i;
+
+ for (i = 0; i < Context->InfoList->Count; i++)
+ {
+ EspDestroyTriggerInfo(Context->InfoList->Items[i]);
+ }
+
+ PhDereferenceObject(Context->InfoList);
+ PhFree(Context);
+}
+
+PPH_STRING EspLookupEtwPublisherName(
+ __in PGUID Guid
+ )
+{
+ PPH_STRING guidString;
+ PPH_STRING keyName;
+ HANDLE keyHandle;
+ PPH_STRING publisherName = NULL;
+
+ // Copied from ProcessHacker\hndlinfo.c.
+
+ guidString = PhFormatGuid(Guid);
+
+ keyName = PhConcatStringRef2(&PublishersKeyName, &guidString->sr);
+
+ if (NT_SUCCESS(PhOpenKey(
+ &keyHandle,
+ KEY_READ,
+ PH_KEY_LOCAL_MACHINE,
+ &keyName->sr,
+ 0
+ )))
+ {
+ publisherName = PhQueryRegistryString(keyHandle, NULL);
+
+ if (publisherName && publisherName->Length == 0)
+ {
+ PhDereferenceObject(publisherName);
+ publisherName = NULL;
+ }
+
+ NtClose(keyHandle);
+ }
+
+ PhDereferenceObject(keyName);
+
+ if (publisherName)
+ {
+ PhDereferenceObject(guidString);
+ return publisherName;
+ }
+ else
+ {
+ return guidString;
+ }
+}
+
+BOOLEAN EspEnumerateEtwPublishers(
+ __out PETW_PUBLISHER_ENTRY *Entries,
+ __out PULONG NumberOfEntries
+ )
+{
+ NTSTATUS status;
+ HANDLE publishersKeyHandle;
+ ULONG index;
+ PKEY_BASIC_INFORMATION buffer;
+ ULONG bufferSize;
+ PETW_PUBLISHER_ENTRY entries;
+ ULONG numberOfEntries;
+ ULONG allocatedEntries;
+
+ if (!NT_SUCCESS(PhOpenKey(
+ &publishersKeyHandle,
+ KEY_READ,
+ PH_KEY_LOCAL_MACHINE,
+ &PublishersKeyName,
+ 0
+ )))
+ {
+ return FALSE;
+ }
+
+ numberOfEntries = 0;
+ allocatedEntries = 256;
+ entries = PhAllocate(allocatedEntries * sizeof(ETW_PUBLISHER_ENTRY));
+
+ index = 0;
+ bufferSize = 0x100;
+ buffer = PhAllocate(0x100);
+
+ while (TRUE)
+ {
+ status = NtEnumerateKey(
+ publishersKeyHandle,
+ index,
+ KeyBasicInformation,
+ buffer,
+ bufferSize,
+ &bufferSize
+ );
+
+ if (NT_SUCCESS(status))
+ {
+ PH_STRINGREF name;
+ HANDLE keyHandle;
+ GUID guid;
+ PPH_STRING publisherName;
+
+ name.Buffer = buffer->Name;
+ name.Length = (USHORT)buffer->NameLength;
+
+ // Make sure this is a valid publisher key.
+ if (NT_SUCCESS(RtlGUIDFromString(&name.us, &guid)))
+ {
+ if (NT_SUCCESS(PhOpenKey(
+ &keyHandle,
+ KEY_READ,
+ publishersKeyHandle,
+ &name,
+ 0
+ )))
+ {
+ publisherName = PhQueryRegistryString(keyHandle, NULL);
+
+ if (publisherName)
+ {
+ if (publisherName->Length != 0)
+ {
+ PETW_PUBLISHER_ENTRY entry;
+
+ if (numberOfEntries == allocatedEntries)
+ {
+ allocatedEntries *= 2;
+ entries = PhReAllocate(entries, allocatedEntries * sizeof(ETW_PUBLISHER_ENTRY));
+ }
+
+ entry = &entries[numberOfEntries++];
+ entry->PublisherName = publisherName;
+ entry->Guid = guid;
+ }
+ else
+ {
+ PhDereferenceObject(publisherName);
+ }
+ }
+
+ NtClose(keyHandle);
+ }
+ }
+
+ index++;
+ }
+ else if (status == STATUS_NO_MORE_ENTRIES)
+ {
+ break;
+ }
+ else if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL)
+ {
+ PhFree(buffer);
+ buffer = PhAllocate(bufferSize);
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ PhFree(buffer);
+ NtClose(publishersKeyHandle);
+
+ *Entries = entries;
+ *NumberOfEntries = numberOfEntries;
+
+ return TRUE;
+}
+
+BOOLEAN EspLookupEtwPublisherGuid(
+ __in PPH_STRINGREF PublisherName,
+ __out PGUID Guid
+ )
+{
+ BOOLEAN result;
+ PETW_PUBLISHER_ENTRY entries;
+ ULONG numberOfEntries;
+ ULONG i;
+
+ if (!EspEnumerateEtwPublishers(&entries, &numberOfEntries))
+ return FALSE;
+
+ result = FALSE;
+
+ for (i = 0; i < numberOfEntries; i++)
+ {
+ if (!result && PhEqualStringRef(&entries[i].PublisherName->sr, PublisherName, TRUE))
+ {
+ *Guid = entries[i].Guid;
+ result = TRUE;
+ }
+
+ PhDereferenceObject(entries[i].PublisherName);
+ }
+
+ PhFree(entries);
+
+ return result;
+}
+
+VOID EspFormatTriggerInfo(
+ __in PES_TRIGGER_INFO Info,
+ __out PWSTR *TriggerString,
+ __out PWSTR *ActionString,
+ __out PPH_STRING *StringUsed
+ )
+{
+ PPH_STRING stringUsed = NULL;
+ PWSTR triggerString;
+ PWSTR actionString;
+ ULONG i;
+ BOOLEAN typeFound;
+ BOOLEAN subTypeFound;
+
+ switch (Info->Type)
+ {
+ case SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL:
+ {
+ PPH_STRING guidString;
+
+ if (!Info->Subtype)
+ {
+ triggerString = L"Device interface arrival";
+ }
+ else
+ {
+ guidString = PhFormatGuid(Info->Subtype);
+ stringUsed = PhConcatStrings2(L"Device interface arrival: ", guidString->Buffer);
+ triggerString = stringUsed->Buffer;
+ }
+ }
+ break;
+ case SERVICE_TRIGGER_TYPE_CUSTOM:
+ {
+ if (Info->Subtype)
+ {
+ PPH_STRING publisherName;
+
+ // Try to lookup the publisher name from the GUID.
+ publisherName = EspLookupEtwPublisherName(Info->Subtype);
+ stringUsed = PhConcatStrings2(L"Custom: ", publisherName->Buffer);
+ PhDereferenceObject(publisherName);
+ triggerString = stringUsed->Buffer;
+ }
+ else
+ {
+ triggerString = L"Custom";
+ }
+ }
+ break;
+ default:
+ {
+ typeFound = FALSE;
+ subTypeFound = FALSE;
+
+ for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++)
+ {
+ if (SubTypeEntries[i].Type == Info->Type)
+ {
+ typeFound = TRUE;
+
+ if (!Info->Subtype && !SubTypeEntries[i].Guid)
+ {
+ subTypeFound = TRUE;
+ triggerString = SubTypeEntries[i].Name;
+ break;
+ }
+ else if (Info->Subtype && SubTypeEntries[i].Guid && memcmp(Info->Subtype, SubTypeEntries[i].Guid, sizeof(GUID)) == 0)
+ {
+ subTypeFound = TRUE;
+ triggerString = SubTypeEntries[i].Name;
+ break;
+ }
+ else if (!subTypeFound && SubTypeEntries[i].Guid == &SubTypeUnknownGuid)
+ {
+ triggerString = SubTypeEntries[i].Name;
+ break;
+ }
+ }
+ }
+
+ if (!typeFound)
+ {
+ triggerString = L"Unknown";
+ }
+ }
+ break;
+ }
+
+ switch (Info->Action)
+ {
+ case SERVICE_TRIGGER_ACTION_SERVICE_START:
+ actionString = L"Start";
+ break;
+ case SERVICE_TRIGGER_ACTION_SERVICE_STOP:
+ actionString = L"Stop";
+ break;
+ default:
+ actionString = L"Unknown";
+ break;
+ }
+
+ *TriggerString = triggerString;
+ *ActionString = actionString;
+ *StringUsed = stringUsed;
+}
+
+VOID EsLoadServiceTriggerInfo(
+ __in struct _ES_TRIGGER_CONTEXT *Context,
+ __in SC_HANDLE ServiceHandle
+ )
+{
+ PSERVICE_TRIGGER_INFO triggerInfo;
+ ULONG i;
+
+ EspClearTriggerInfoList(Context->InfoList);
+
+ if (triggerInfo = PhQueryServiceVariableSize(ServiceHandle, SERVICE_CONFIG_TRIGGER_INFO))
+ {
+ for (i = 0; i < triggerInfo->cTriggers; i++)
+ {
+ PSERVICE_TRIGGER trigger = &triggerInfo->pTriggers[i];
+ PES_TRIGGER_INFO info;
+ PWSTR triggerString;
+ PWSTR actionString;
+ PPH_STRING stringUsed;
+ INT lvItemIndex;
+
+ info = EspCreateTriggerInfo(trigger);
+ PhAddItemList(Context->InfoList, info);
+
+ EspFormatTriggerInfo(info, &triggerString, &actionString, &stringUsed);
+
+ lvItemIndex = PhAddListViewItem(Context->TriggersLv, MAXINT, triggerString, info);
+ PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString);
+
+ if (stringUsed)
+ PhDereferenceObject(stringUsed);
+ }
+
+ Context->InitialNumberOfTriggers = triggerInfo->cTriggers;
+
+ ExtendedListView_SortItems(Context->TriggersLv);
+
+ PhFree(triggerInfo);
+ }
+}
+
+BOOLEAN EsSaveServiceTriggerInfo(
+ __in struct _ES_TRIGGER_CONTEXT *Context,
+ __out PULONG Win32Result
+ )
+{
+ BOOLEAN result = TRUE;
+ PH_AUTO_POOL autoPool;
+ SC_HANDLE serviceHandle;
+ SERVICE_TRIGGER_INFO triggerInfo;
+ ULONG i;
+ ULONG j;
+
+ if (!Context->Dirty)
+ return TRUE;
+
+ // Do not try to change trigger information if we didn't have any triggers before and we don't
+ // have any now. ChangeServiceConfig2 returns an error in this situation.
+ if (Context->InitialNumberOfTriggers == 0 && Context->InfoList->Count == 0)
+ return TRUE;
+
+ if (!(serviceHandle = PhOpenService(Context->ServiceItem->Name->Buffer, SERVICE_CHANGE_CONFIG)))
+ {
+ *Win32Result = GetLastError();
+ return FALSE;
+ }
+
+ PhInitializeAutoPool(&autoPool);
+
+ memset(&triggerInfo, 0, sizeof(SERVICE_TRIGGER_INFO));
+ triggerInfo.cTriggers = Context->InfoList->Count;
+
+ // pTriggers needs to be NULL when there are no triggers.
+ if (Context->InfoList->Count != 0)
+ {
+ PhCreateAlloc(&triggerInfo.pTriggers, Context->InfoList->Count * sizeof(SERVICE_TRIGGER));
+ memset(triggerInfo.pTriggers, 0, Context->InfoList->Count * sizeof(SERVICE_TRIGGER));
+
+ for (i = 0; i < Context->InfoList->Count; i++)
+ {
+ PSERVICE_TRIGGER trigger = &triggerInfo.pTriggers[i];
+ PES_TRIGGER_INFO info = Context->InfoList->Items[i];
+
+ trigger->dwTriggerType = info->Type;
+ trigger->dwAction = info->Action;
+ trigger->pTriggerSubtype = info->Subtype;
+
+ if (info->DataList && info->DataList->Count != 0)
+ {
+ trigger->cDataItems = info->DataList->Count;
+ PhCreateAlloc(&trigger->pDataItems, info->DataList->Count * sizeof(SERVICE_TRIGGER_SPECIFIC_DATA_ITEM));
+
+ for (j = 0; j < info->DataList->Count; j++)
+ {
+ PSERVICE_TRIGGER_SPECIFIC_DATA_ITEM dataItem = &trigger->pDataItems[j];
+ PES_TRIGGER_DATA data = info->DataList->Items[j];
+
+ dataItem->dwDataType = data->Type;
+
+ if (data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
+ {
+ dataItem->cbData = data->String->Length + 2; // include null terminator
+ dataItem->pData = (PBYTE)data->String->Buffer;
+ }
+ else if (data->Type == SERVICE_TRIGGER_DATA_TYPE_BINARY)
+ {
+ dataItem->cbData = data->BinaryLength;
+ dataItem->pData = data->Binary;
+ }
+ }
+ }
+ }
+ }
+
+ if (!ChangeServiceConfig2(serviceHandle, SERVICE_CONFIG_TRIGGER_INFO, &triggerInfo))
+ {
+ result = FALSE;
+ *Win32Result = GetLastError();
+ }
+
+ PhDeleteAutoPool(&autoPool);
+
+ CloseServiceHandle(serviceHandle);
+
+ return result;
+}
+
+LOGICAL EspSetListViewItemParam(
+ __in HWND ListViewHandle,
+ __in INT Index,
+ __in PVOID Param
+ )
+{
+ LVITEM item;
+
+ item.mask = LVIF_PARAM;
+ item.iItem = Index;
+ item.iSubItem = 0;
+ item.lParam = (LPARAM)Param;
+
+ return ListView_SetItem(ListViewHandle, &item);
+}
+
+VOID EsHandleEventServiceTrigger(
+ __in struct _ES_TRIGGER_CONTEXT *Context,
+ __in ULONG Event
+ )
+{
+ switch (Event)
+ {
+ case ES_TRIGGER_EVENT_NEW:
+ {
+ Context->EditingInfo = EspCreateTriggerInfo(NULL);
+ Context->EditingInfo->Type = SERVICE_TRIGGER_TYPE_IP_ADDRESS_AVAILABILITY;
+ Context->EditingInfo->SubtypeBuffer = NetworkManagerFirstIpAddressArrivalGuid;
+ Context->EditingInfo->Subtype = &Context->EditingInfo->SubtypeBuffer;
+ Context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_START;
+
+ if (DialogBoxParam(
+ PluginInstance->DllBase,
+ MAKEINTRESOURCE(IDD_SRVTRIGGER),
+ Context->WindowHandle,
+ EspServiceTriggerDlgProc,
+ (LPARAM)Context
+ ) == IDOK)
+ {
+ PWSTR triggerString;
+ PWSTR actionString;
+ PPH_STRING stringUsed;
+ INT lvItemIndex;
+
+ Context->Dirty = TRUE;
+ PhAddItemList(Context->InfoList, Context->EditingInfo);
+
+ EspFormatTriggerInfo(Context->EditingInfo, &triggerString, &actionString, &stringUsed);
+
+ lvItemIndex = PhAddListViewItem(Context->TriggersLv, MAXINT, triggerString, Context->EditingInfo);
+ PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString);
+
+ if (stringUsed)
+ PhDereferenceObject(stringUsed);
+ }
+ else
+ {
+ EspDestroyTriggerInfo(Context->EditingInfo);
+ }
+
+ Context->EditingInfo = NULL;
+ }
+ break;
+ case ES_TRIGGER_EVENT_EDIT:
+ {
+ INT lvItemIndex;
+ PES_TRIGGER_INFO info;
+ ULONG index;
+
+ lvItemIndex = ListView_GetNextItem(Context->TriggersLv, -1, LVNI_SELECTED);
+
+ if (lvItemIndex != -1 && PhGetListViewItemParam(Context->TriggersLv, lvItemIndex, (PPVOID)&info))
+ {
+ index = PhFindItemList(Context->InfoList, info);
+
+ if (index != -1)
+ {
+ Context->EditingInfo = EspCloneTriggerInfo(info);
+
+ if (DialogBoxParam(
+ PluginInstance->DllBase,
+ MAKEINTRESOURCE(IDD_SRVTRIGGER),
+ Context->WindowHandle,
+ EspServiceTriggerDlgProc,
+ (LPARAM)Context
+ ) == IDOK)
+ {
+ PWSTR triggerString;
+ PWSTR actionString;
+ PPH_STRING stringUsed;
+
+ Context->Dirty = TRUE;
+ EspDestroyTriggerInfo(Context->InfoList->Items[index]);
+ Context->InfoList->Items[index] = Context->EditingInfo;
+
+ EspFormatTriggerInfo(Context->EditingInfo, &triggerString, &actionString, &stringUsed);
+ PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 0, triggerString);
+ PhSetListViewSubItem(Context->TriggersLv, lvItemIndex, 1, actionString);
+
+ if (stringUsed)
+ PhDereferenceObject(stringUsed);
+
+ EspSetListViewItemParam(Context->TriggersLv, lvItemIndex, Context->EditingInfo);
+ }
+ else
+ {
+ EspDestroyTriggerInfo(Context->EditingInfo);
+ }
+
+ Context->EditingInfo = NULL;
+ }
+ }
+ }
+ break;
+ case ES_TRIGGER_EVENT_DELETE:
+ {
+ INT lvItemIndex;
+ PES_TRIGGER_INFO info;
+ ULONG index;
+
+ lvItemIndex = ListView_GetNextItem(Context->TriggersLv, -1, LVNI_SELECTED);
+
+ if (lvItemIndex != -1 && PhGetListViewItemParam(Context->TriggersLv, lvItemIndex, (PPVOID)&info))
+ {
+ index = PhFindItemList(Context->InfoList, info);
+
+ if (index != -1)
+ {
+ EspDestroyTriggerInfo(info);
+ PhRemoveItemList(Context->InfoList, index);
+ PhRemoveListViewItem(Context->TriggersLv, lvItemIndex);
+ }
+ }
+
+ Context->Dirty = TRUE;
+ }
+ break;
+ case ES_TRIGGER_EVENT_SELECTIONCHANGED:
+ {
+ ULONG selectedCount;
+
+ selectedCount = ListView_GetSelectedCount(Context->TriggersLv);
+
+ EnableWindow(GetDlgItem(Context->WindowHandle, IDC_EDIT), selectedCount == 1);
+ EnableWindow(GetDlgItem(Context->WindowHandle, IDC_DELETE), selectedCount == 1);
+ }
+ break;
+ }
+}
+
+static ULONG EspTriggerTypeStringToInteger(
+ __in PWSTR String
+ )
+{
+ ULONG i;
+
+ for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++)
+ {
+ if (PhEqualStringZ(TypeEntries[i].Name, String, FALSE))
+ return TypeEntries[i].Type;
+ }
+
+ return 0;
+}
+
+static int __cdecl EtwPublisherByNameCompareFunction(
+ __in const void *elem1,
+ __in const void *elem2
+ )
+{
+ PETW_PUBLISHER_ENTRY entry1 = (PETW_PUBLISHER_ENTRY)elem1;
+ PETW_PUBLISHER_ENTRY entry2 = (PETW_PUBLISHER_ENTRY)elem2;
+
+ return PhCompareString(entry1->PublisherName, entry2->PublisherName, TRUE);
+}
+
+static VOID EspFixServiceTriggerControls(
+ __in HWND hwndDlg,
+ __in PES_TRIGGER_CONTEXT Context
+ )
+{
+ HWND typeComboBox;
+ HWND subTypeComboBox;
+ ULONG i;
+ PPH_STRING selectedTypeString;
+ ULONG type;
+ PPH_STRING selectedSubTypeString;
+
+ typeComboBox = GetDlgItem(hwndDlg, IDC_TYPE);
+ subTypeComboBox = GetDlgItem(hwndDlg, IDC_SUBTYPE);
+
+ selectedTypeString = PhGetWindowText(typeComboBox);
+ type = EspTriggerTypeStringToInteger(selectedTypeString->Buffer);
+ PhDereferenceObject(selectedTypeString);
+
+ if (Context->LastSelectedType != type)
+ {
+ // Change the contents of the subtype combo box based on the type.
+
+ ComboBox_ResetContent(subTypeComboBox);
+
+ switch (type)
+ {
+ case SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL:
+ {
+ ComboBox_AddString(subTypeComboBox, L"Custom");
+ }
+ break;
+ case SERVICE_TRIGGER_TYPE_CUSTOM:
+ {
+ PETW_PUBLISHER_ENTRY entries;
+ ULONG numberOfEntries;
+ ULONG i;
+
+ ComboBox_AddString(subTypeComboBox, L"Custom");
+
+ // Display a list of publishers.
+ if (EspEnumerateEtwPublishers(&entries, &numberOfEntries))
+ {
+ // Sort the list by name.
+ qsort(entries, numberOfEntries, sizeof(ETW_PUBLISHER_ENTRY), EtwPublisherByNameCompareFunction);
+
+ for (i = 0; i < numberOfEntries; i++)
+ {
+ ComboBox_AddString(subTypeComboBox, entries[i].PublisherName->Buffer);
+ }
+
+ PhFree(entries);
+ }
+ }
+ break;
+ default:
+ for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++)
+ {
+ if (SubTypeEntries[i].Type == type && SubTypeEntries[i].Guid && SubTypeEntries[i].Guid != &SubTypeUnknownGuid)
+ {
+ ComboBox_AddString(subTypeComboBox, SubTypeEntries[i].Name);
+ }
+ }
+ break;
+ }
+
+ ComboBox_SetCurSel(subTypeComboBox, 0);
+
+ Context->LastSelectedType = type;
+ }
+
+ selectedSubTypeString = PhGetWindowText(subTypeComboBox);
+
+ if (PhEqualString2(selectedSubTypeString, L"Custom", FALSE))
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM), TRUE);
+ SetDlgItemText(hwndDlg, IDC_SUBTYPECUSTOM, Context->LastCustomSubType->Buffer);
+ }
+ else
+ {
+ if (IsWindowEnabled(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM)))
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM), FALSE);
+ PhSwapReference2(&Context->LastCustomSubType, PhGetWindowText(GetDlgItem(hwndDlg, IDC_SUBTYPECUSTOM)));
+ SetDlgItemText(hwndDlg, IDC_SUBTYPECUSTOM, L"");
+ }
+ }
+
+ PhDereferenceObject(selectedSubTypeString);
+}
+
+PPH_STRING EspConvertNullsToNewLines(
+ __in PPH_STRING String
+ )
+{
+ PH_STRING_BUILDER sb;
+ ULONG i;
+
+ PhInitializeStringBuilder(&sb, String->Length);
+
+ for (i = 0; i < (ULONG)String->Length / 2; i++)
+ {
+ if (String->Buffer[i] == 0)
+ {
+ PhAppendStringBuilderEx(&sb, L"\r\n", 4);
+ continue;
+ }
+
+ PhAppendCharStringBuilder(&sb, String->Buffer[i]);
+ }
+
+ return PhFinalStringBuilderString(&sb);
+}
+
+PPH_STRING EspConvertNewLinesToNulls(
+ __in PPH_STRING String
+ )
+{
+ PPH_STRING text;
+ ULONG count;
+ ULONG i;
+
+ text = PhCreateStringEx(NULL, String->Length + 2); // plus one character for an extra null terminator (see below)
+ text->Length = 0;
+ count = 0;
+
+ for (i = 0; i < (ULONG)String->Length / 2; i++)
+ {
+ // Lines are terminated by "\r\n".
+ if (String->Buffer[i] == '\r')
+ {
+ continue;
+ }
+
+ if (String->Buffer[i] == '\n')
+ {
+ text->Buffer[count++] = 0;
+ continue;
+ }
+
+ text->Buffer[count++] = String->Buffer[i];
+ }
+
+ if (count != 0)
+ {
+ // Make sure we have an extra null terminator at the end, as required of multistrings.
+ if (text->Buffer[count - 1] != 0)
+ text->Buffer[count++] = 0;
+ }
+
+ text->Length = (USHORT)(count * 2);
+
+ return text;
+}
+
+PPH_STRING EspConvertNullsToSpaces(
+ __in PPH_STRING String
+ )
+{
+ PPH_STRING text;
+ ULONG j;
+
+ text = PhDuplicateString(String);
+
+ for (j = 0; j < (ULONG)text->Length / 2; j++)
+ {
+ if (text->Buffer[j] == 0)
+ text->Buffer[j] = ' ';
+ }
+
+ return text;
+}
+
+INT_PTR CALLBACK EspServiceTriggerDlgProc(
+ __in HWND hwndDlg,
+ __in UINT uMsg,
+ __in WPARAM wParam,
+ __in LPARAM lParam
+ )
+{
+ PES_TRIGGER_CONTEXT context;
+
+ if (uMsg == WM_INITDIALOG)
+ {
+ context = (PES_TRIGGER_CONTEXT)lParam;
+ SetProp(hwndDlg, L"Context", (HANDLE)context);
+ }
+ else
+ {
+ context = (PES_TRIGGER_CONTEXT)GetProp(hwndDlg, L"Context");
+
+ if (uMsg == WM_DESTROY)
+ RemoveProp(hwndDlg, L"Context");
+ }
+
+ if (!context)
+ return FALSE;
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ HWND typeComboBox;
+ HWND actionComboBox;
+ HWND lvHandle;
+ ULONG i;
+
+ context->LastSelectedType = 0;
+
+ if (context->EditingInfo->Subtype)
+ context->LastCustomSubType = PhFormatGuid(context->EditingInfo->Subtype);
+ else
+ context->LastCustomSubType = PhReferenceEmptyString();
+
+ typeComboBox = GetDlgItem(hwndDlg, IDC_TYPE);
+ actionComboBox = GetDlgItem(hwndDlg, IDC_ACTION);
+
+ for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++)
+ {
+ ComboBox_AddString(typeComboBox, TypeEntries[i].Name);
+
+ if (TypeEntries[i].Type == context->EditingInfo->Type)
+ {
+ ComboBox_SelectString(typeComboBox, -1, TypeEntries[i].Name);
+ }
+ }
+
+ ComboBox_AddString(actionComboBox, L"Start");
+ ComboBox_AddString(actionComboBox, L"Stop");
+ ComboBox_SetCurSel(actionComboBox, context->EditingInfo->Action == SERVICE_TRIGGER_ACTION_SERVICE_START ? 0 : 1);
+
+ EspFixServiceTriggerControls(hwndDlg, context);
+
+ if (context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM)
+ {
+ for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++)
+ {
+ if (
+ SubTypeEntries[i].Type == context->EditingInfo->Type &&
+ SubTypeEntries[i].Guid &&
+ context->EditingInfo->Subtype &&
+ memcmp(SubTypeEntries[i].Guid, context->EditingInfo->Subtype, sizeof(GUID)) == 0
+ )
+ {
+ ComboBox_SelectString(GetDlgItem(hwndDlg, IDC_SUBTYPE), -1, SubTypeEntries[i].Name);
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (context->EditingInfo->Subtype)
+ {
+ PPH_STRING publisherName;
+
+ // Try to select the publisher name in the subtype list.
+ publisherName = EspLookupEtwPublisherName(context->EditingInfo->Subtype);
+ ComboBox_SelectString(GetDlgItem(hwndDlg, IDC_SUBTYPE), -1, publisherName->Buffer);
+ PhDereferenceObject(publisherName);
+ }
+ }
+
+ // Call a second time since the state of the custom subtype text box may have changed.
+ EspFixServiceTriggerControls(hwndDlg, context);
+
+ lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
+ PhSetListViewStyle(lvHandle, FALSE, TRUE);
+ PhSetControlTheme(lvHandle, L"explorer");
+ PhAddListViewColumn(lvHandle, 0, 0, 0, LVCFMT_LEFT, 280, L"Data");
+
+ if (context->EditingInfo->DataList)
+ {
+ for (i = 0; i < context->EditingInfo->DataList->Count; i++)
+ {
+ PES_TRIGGER_DATA data;
+ PPH_STRING text;
+ INT lvItemIndex;
+
+ data = context->EditingInfo->DataList->Items[i];
+
+ if (data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING)
+ {
+ // Prepare the text for display by replacing null characters with spaces.
+ text = EspConvertNullsToSpaces(data->String);
+ }
+ else
+ {
+ text = PhCreateString(L"(binary data)");
+ }
+
+ lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, text->Buffer, data);
+ PhDereferenceObject(text);
+ }
+ }
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE);
+ }
+ break;
+ case WM_DESTROY:
+ {
+ PhSwapReference(&context->LastCustomSubType, NULL);
+ }
+ break;
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDC_TYPE:
+ if (HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ EspFixServiceTriggerControls(hwndDlg, context);
+ }
+ break;
+ case IDC_SUBTYPE:
+ if (HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ EspFixServiceTriggerControls(hwndDlg, context);
+ }
+ break;
+ case IDC_NEW:
+ {
+ HWND lvHandle;
+
+ lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
+ context->EditingValue = PhReferenceEmptyString();
+
+ if (DialogBoxParam(
+ PluginInstance->DllBase,
+ MAKEINTRESOURCE(IDD_VALUE),
+ hwndDlg,
+ ValueDlgProc,
+ (LPARAM)context
+ ) == IDOK)
+ {
+ PES_TRIGGER_DATA data;
+ PPH_STRING text;
+ INT lvItemIndex;
+
+ data = EspCreateTriggerData(NULL);
+ data->Type = SERVICE_TRIGGER_DATA_TYPE_STRING;
+ data->String = EspConvertNewLinesToNulls(context->EditingValue);
+
+ if (!context->EditingInfo->DataList)
+ context->EditingInfo->DataList = PhCreateList(4);
+
+ PhAddItemList(context->EditingInfo->DataList, data);
+
+ text = EspConvertNullsToSpaces(data->String);
+ lvItemIndex = PhAddListViewItem(lvHandle, MAXINT, text->Buffer, data);
+ PhDereferenceObject(text);
+ }
+
+ PhSwapReference(&context->EditingValue, NULL);
+ }
+ break;
+ case IDC_EDIT:
+ {
+ HWND lvHandle;
+ INT lvItemIndex;
+ PES_TRIGGER_DATA data;
+ ULONG index;
+
+ lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
+ lvItemIndex = ListView_GetNextItem(lvHandle, -1, LVNI_SELECTED);
+
+ if (
+ lvItemIndex != -1 && PhGetListViewItemParam(lvHandle, lvItemIndex, (PPVOID)&data) &&
+ data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING // editing binary values is not supported
+ )
+ {
+ index = PhFindItemList(context->EditingInfo->DataList, data);
+
+ if (index != -1)
+ {
+ context->EditingValue = EspConvertNullsToNewLines(data->String);
+
+ if (DialogBoxParam(
+ PluginInstance->DllBase,
+ MAKEINTRESOURCE(IDD_VALUE),
+ hwndDlg,
+ ValueDlgProc,
+ (LPARAM)context
+ ) == IDOK)
+ {
+ PPH_STRING text;
+
+ PhSwapReference2(&data->String, EspConvertNewLinesToNulls(context->EditingValue));
+
+ text = EspConvertNullsToSpaces(data->String);
+ PhSetListViewSubItem(lvHandle, lvItemIndex, 0, text->Buffer);
+ }
+
+ PhSwapReference(&context->EditingValue, NULL);
+ }
+ }
+ }
+ break;
+ case IDC_DELETE:
+ {
+ HWND lvHandle;
+ INT lvItemIndex;
+ PES_TRIGGER_DATA data;
+ ULONG index;
+
+ lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
+ lvItemIndex = ListView_GetNextItem(lvHandle, -1, LVNI_SELECTED);
+
+ if (lvItemIndex != -1 && PhGetListViewItemParam(lvHandle, lvItemIndex, (PPVOID)&data))
+ {
+ index = PhFindItemList(context->EditingInfo->DataList, data);
+
+ if (index != -1)
+ {
+ EspDestroyTriggerData(data);
+ PhRemoveItemList(context->EditingInfo->DataList, index);
+ PhRemoveListViewItem(lvHandle, lvItemIndex);
+ }
+ }
+ }
+ break;
+ case IDCANCEL:
+ EndDialog(hwndDlg, IDCANCEL);
+ break;
+ case IDOK:
+ {
+ PH_AUTO_POOL autoPool;
+ PPH_STRING typeString;
+ PPH_STRING subTypeString;
+ PPH_STRING customSubTypeString;
+ PPH_STRING actionString;
+ ULONG i;
+
+ PhInitializeAutoPool(&autoPool);
+
+ typeString = PHA_GET_DLGITEM_TEXT(hwndDlg, IDC_TYPE);
+ subTypeString = PHA_GET_DLGITEM_TEXT(hwndDlg, IDC_SUBTYPE);
+ customSubTypeString = PHA_GET_DLGITEM_TEXT(hwndDlg, IDC_SUBTYPECUSTOM);
+ actionString = PHA_GET_DLGITEM_TEXT(hwndDlg, IDC_ACTION);
+
+ for (i = 0; i < sizeof(TypeEntries) / sizeof(TYPE_ENTRY); i++)
+ {
+ if (PhEqualStringZ(TypeEntries[i].Name, typeString->Buffer, FALSE))
+ {
+ context->EditingInfo->Type = TypeEntries[i].Type;
+ break;
+ }
+ }
+
+ if (!PhEqualString2(subTypeString, L"Custom", FALSE))
+ {
+ if (context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM)
+ {
+ for (i = 0; i < sizeof(SubTypeEntries) / sizeof(SUBTYPE_ENTRY); i++)
+ {
+ if (
+ SubTypeEntries[i].Type == context->EditingInfo->Type &&
+ PhEqualString2(subTypeString, SubTypeEntries[i].Name, FALSE)
+ )
+ {
+ context->EditingInfo->SubtypeBuffer = *SubTypeEntries[i].Guid;
+ context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer;
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (!EspLookupEtwPublisherGuid(&subTypeString->sr, &context->EditingInfo->SubtypeBuffer))
+ {
+ PhShowError(hwndDlg, L"Unable to find the ETW publisher GUID.");
+ goto DoNotClose;
+ }
+
+ context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer;
+ }
+ }
+ else
+ {
+ UNICODE_STRING guidString;
+
+ guidString = customSubTypeString->us;
+
+ // Trim whitespace.
+
+ while (guidString.Length != 0 && *guidString.Buffer == ' ')
+ {
+ guidString.Buffer++;
+ guidString.Length -= 2;
+ }
+
+ while (guidString.Length != 0 && guidString.Buffer[guidString.Length / 2 - 1] == ' ')
+ {
+ guidString.Length -= 2;
+ }
+
+ if (NT_SUCCESS(RtlGUIDFromString(&guidString, &context->EditingInfo->SubtypeBuffer)))
+ {
+ context->EditingInfo->Subtype = &context->EditingInfo->SubtypeBuffer;
+ }
+ else
+ {
+ PhShowError(hwndDlg, L"The custom subtype is invalid. Please ensure that the string is a valid GUID: \"{x-x-x-x-x}\".");
+ goto DoNotClose;
+ }
+ }
+
+ if (PhEqualString2(actionString, L"Start", FALSE))
+ context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_START;
+ else
+ context->EditingInfo->Action = SERVICE_TRIGGER_ACTION_SERVICE_STOP;
+
+ if (
+ context->EditingInfo->DataList &&
+ context->EditingInfo->DataList->Count != 0 &&
+ context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_DEVICE_INTERFACE_ARRIVAL &&
+ context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_FIREWALL_PORT_EVENT &&
+ context->EditingInfo->Type != SERVICE_TRIGGER_TYPE_CUSTOM
+ )
+ {
+ // This trigger has data items, but the trigger type doesn't allow them.
+ if (PhShowMessage(
+ hwndDlg,
+ MB_OKCANCEL | MB_ICONWARNING,
+ L"The trigger type \"%s\" does not allow data items to be configured. "
+ L"If you continue, they will be removed.",
+ typeString->Buffer
+ ) != IDOK)
+ {
+ goto DoNotClose;
+ }
+
+ for (i = 0; i < context->EditingInfo->DataList->Count; i++)
+ {
+ EspDestroyTriggerData(context->EditingInfo->DataList->Items[i]);
+ }
+
+ PhSwapReference(&context->EditingInfo->DataList, NULL);
+ }
+
+ EndDialog(hwndDlg, IDOK);
+
+DoNotClose:
+ PhDeleteAutoPool(&autoPool);
+ }
+ break;
+ }
+ }
+ break;
+ case WM_NOTIFY:
+ {
+ LPNMHDR header = (LPNMHDR)lParam;
+ HWND lvHandle;
+
+ lvHandle = GetDlgItem(hwndDlg, IDC_LIST);
+
+ switch (header->code)
+ {
+ case LVN_ITEMCHANGED:
+ {
+ if (header->hwndFrom == lvHandle)
+ {
+ if (ListView_GetSelectedCount(lvHandle) == 1)
+ {
+ PES_TRIGGER_DATA data = PhGetSelectedListViewItemParam(GetDlgItem(hwndDlg, IDC_LIST));
+
+ // Editing binary data is not supported.
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), data && data->Type == SERVICE_TRIGGER_DATA_TYPE_STRING);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), TRUE);
+ }
+ else
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_EDIT), FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DELETE), FALSE);
+ }
+ }
+ }
+ break;
+ case NM_DBLCLK:
+ {
+ if (header->hwndFrom == lvHandle)
+ {
+ SendMessage(hwndDlg, WM_COMMAND, IDC_EDIT, 0);
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
+static INT_PTR CALLBACK ValueDlgProc(
+ __in HWND hwndDlg,
+ __in UINT uMsg,
+ __in WPARAM wParam,
+ __in LPARAM lParam
+ )
+{
+ PES_TRIGGER_CONTEXT context;
+
+ if (uMsg == WM_INITDIALOG)
+ {
+ context = (PES_TRIGGER_CONTEXT)lParam;
+ SetProp(hwndDlg, L"Context", (HANDLE)context);
+ }
+ else
+ {
+ context = (PES_TRIGGER_CONTEXT)GetProp(hwndDlg, L"Context");
+
+ if (uMsg == WM_DESTROY)
+ RemoveProp(hwndDlg, L"Context");
+ }
+
+ if (!context)
+ return FALSE;
+
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ SetDlgItemText(hwndDlg, IDC_VALUES, context->EditingValue->Buffer);
+ SetFocus(GetDlgItem(hwndDlg, IDC_VALUES));
+ Edit_SetSel(GetDlgItem(hwndDlg, IDC_VALUES), 0, -1);
+ }
+ break;
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDCANCEL:
+ EndDialog(hwndDlg, IDCANCEL);
+ break;
+ case IDOK:
+ PhSwapReference2(&context->EditingValue, PhGetWindowText(GetDlgItem(hwndDlg, IDC_VALUES)));
+ EndDialog(hwndDlg, IDOK);
+ break;
+ }
+ }
+ break;
+ }
+
+ return FALSE;
+}