From 3ed6df0effefda695d588f9830a29c6949d507a1 Mon Sep 17 00:00:00 2001 From: wj32 Date: Sun, 12 Jun 2011 10:29:58 +0000 Subject: [PATCH] ExtendedServices: added support for editing triggers git-svn-id: svn://svn.code.sf.net/p/processhacker/code@4229 21ef857c-d57f-4fe0-8362-d861dc6d29cd --- 2.x/trunk/CHANGELOG.txt | 1 + .../plugins/ExtendedServices/CHANGELOG.txt | 3 + .../ExtendedServices/ExtendedServices.rc | 65 +- .../ExtendedServices/ExtendedServices.vcxproj | 1 + .../ExtendedServices.vcxproj.filters | 3 + 2.x/trunk/plugins/ExtendedServices/extsrv.h | 34 + 2.x/trunk/plugins/ExtendedServices/other.c | 262 +-- 2.x/trunk/plugins/ExtendedServices/resource.h | 16 +- 2.x/trunk/plugins/ExtendedServices/trigger.c | 1605 +++++++++++++++++ 9 files changed, 1809 insertions(+), 181 deletions(-) create mode 100644 2.x/trunk/plugins/ExtendedServices/trigger.c 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; +}