#include #include "db.h" #include "resource.h" #define COMMENT_COLUMN_ID 1 typedef struct _PROCESS_EXTENSION { LIST_ENTRY ListEntry; BOOLEAN Valid; PPH_STRING Comment; } PROCESS_EXTENSION, *PPROCESS_EXTENSION; typedef struct _PROCESS_COMMENT_PAGE_CONTEXT { PPH_STRING OriginalComment; } PROCESS_COMMENT_PAGE_CONTEXT, *PPROCESS_COMMENT_PAGE_CONTEXT; typedef struct _SERVICE_EXTENSION { LIST_ENTRY ListEntry; BOOLEAN Valid; PPH_STRING Comment; } SERVICE_EXTENSION, *PSERVICE_EXTENSION; typedef struct _SERVICE_COMMENT_PAGE_CONTEXT { PPH_SERVICE_ITEM ServiceItem; PH_LAYOUT_MANAGER LayoutManager; } SERVICE_COMMENT_PAGE_CONTEXT, *PSERVICE_COMMENT_PAGE_CONTEXT; INT_PTR CALLBACK OptionsDlgProc( __in HWND hwndDlg, __in UINT uMsg, __in WPARAM wParam, __in LPARAM lParam ); INT_PTR CALLBACK ProcessCommentPageDlgProc( __in HWND hwndDlg, __in UINT uMsg, __in WPARAM wParam, __in LPARAM lParam ); INT_PTR CALLBACK ServiceCommentPageDlgProc( __in HWND hwndDlg, __in UINT uMsg, __in WPARAM wParam, __in LPARAM lParam ); PPH_PLUGIN PluginInstance; PH_CALLBACK_REGISTRATION PluginLoadCallbackRegistration; PH_CALLBACK_REGISTRATION PluginShowOptionsCallbackRegistration; PH_CALLBACK_REGISTRATION TreeNewMessageCallbackRegistration; PH_CALLBACK_REGISTRATION ProcessPropertiesInitializingCallbackRegistration; PH_CALLBACK_REGISTRATION ServicePropertiesInitializingCallbackRegistration; PH_CALLBACK_REGISTRATION ProcessTreeNewInitializingCallbackRegistration; PH_CALLBACK_REGISTRATION ServiceTreeNewInitializingCallbackRegistration; HWND ProcessTreeNewHandle; LIST_ENTRY ProcessListHead = { &ProcessListHead, &ProcessListHead }; PH_QUEUED_LOCK ProcessListLock = PH_QUEUED_LOCK_INIT; HWND ServiceTreeNewHandle; LIST_ENTRY ServiceListHead = { &ServiceListHead, &ServiceListHead }; PH_QUEUED_LOCK ServiceListLock = PH_QUEUED_LOCK_INIT; VOID NTAPI LoadCallback( __in_opt PVOID Parameter, __in_opt PVOID Context ) { PPH_STRING path; PPH_STRING realPath; InitializeDb(); path = PhGetStringSetting(L"ProcessHacker.UserNotes.DatabasePath"); realPath = PhExpandEnvironmentStrings(&path->sr); PhDereferenceObject(path); path = realPath; if (RtlDetermineDosPathNameType_U(path->Buffer) == RtlPathTypeRelative) { PPH_STRING directory; directory = PhGetApplicationDirectory(); realPath = PhConcatStringRef2(&directory->sr, &path->sr); PhDereferenceObject(directory); PhDereferenceObject(path); path = realPath; } SetDbPath(path); PhDereferenceObject(path); LoadDb(); } VOID NTAPI ShowOptionsCallback( __in_opt PVOID Parameter, __in_opt PVOID Context ) { DialogBox( PluginInstance->DllBase, MAKEINTRESOURCE(IDD_OPTIONS), (HWND)Parameter, OptionsDlgProc ); } VOID InvalidateProcessComments( VOID ) { PLIST_ENTRY listEntry; PhAcquireQueuedLockExclusive(&ProcessListLock); listEntry = ProcessListHead.Flink; while (listEntry != &ProcessListHead) { PPROCESS_EXTENSION extension; extension = CONTAINING_RECORD(listEntry, PROCESS_EXTENSION, ListEntry); extension->Valid = FALSE; listEntry = listEntry->Flink; } PhReleaseQueuedLockExclusive(&ProcessListLock); } VOID UpdateProcessComment( __in PPH_PROCESS_NODE Node, __in PPROCESS_EXTENSION Extension ) { if (!Extension->Valid) { PDB_OBJECT object; LockDb(); if (object = FindDbObject(FILE_TAG, &Node->ProcessItem->ProcessName->sr)) { PhSwapReference(&Extension->Comment, object->Comment); } else { PhSwapReference(&Extension->Comment, NULL); } UnlockDb(); Extension->Valid = TRUE; } } VOID InvalidateServiceComments( VOID ) { PLIST_ENTRY listEntry; PhAcquireQueuedLockExclusive(&ServiceListLock); listEntry = ServiceListHead.Flink; while (listEntry != &ServiceListHead) { PSERVICE_EXTENSION extension; extension = CONTAINING_RECORD(listEntry, SERVICE_EXTENSION, ListEntry); extension->Valid = FALSE; listEntry = listEntry->Flink; } PhReleaseQueuedLockExclusive(&ServiceListLock); } VOID UpdateServiceComment( __in PPH_SERVICE_NODE Node, __in PSERVICE_EXTENSION Extension ) { if (!Extension->Valid) { PDB_OBJECT object; LockDb(); if (object = FindDbObject(SERVICE_TAG, &Node->ServiceItem->Name->sr)) { PhSwapReference(&Extension->Comment, object->Comment); } else { PhSwapReference(&Extension->Comment, NULL); } UnlockDb(); Extension->Valid = TRUE; } } VOID TreeNewMessageCallback( __in_opt PVOID Parameter, __in_opt PVOID Context ) { PPH_PLUGIN_TREENEW_MESSAGE message = Parameter; switch (message->Message) { case TreeNewGetCellText: { PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1; if (message->TreeNewHandle == ProcessTreeNewHandle) { PPH_PROCESS_NODE node; node = (PPH_PROCESS_NODE)getCellText->Node; switch (message->SubId) { case COMMENT_COLUMN_ID: { PPROCESS_EXTENSION extension; extension = PhPluginGetObjectExtension(PluginInstance, node->ProcessItem, EmProcessItemType); UpdateProcessComment(node, extension); getCellText->Text = PhGetStringRef(extension->Comment); } break; } } else if (message->TreeNewHandle == ServiceTreeNewHandle) { PPH_SERVICE_NODE node; node = (PPH_SERVICE_NODE)getCellText->Node; switch (message->SubId) { case COMMENT_COLUMN_ID: { PSERVICE_EXTENSION extension; extension = PhPluginGetObjectExtension(PluginInstance, node->ServiceItem, EmServiceItemType); UpdateServiceComment(node, extension); getCellText->Text = PhGetStringRef(extension->Comment); } break; } } } break; } } LONG NTAPI ProcessCommentSortFunction( __in PVOID Node1, __in PVOID Node2, __in ULONG SubId, __in PVOID Context ) { PPH_PROCESS_NODE node1 = Node1; PPH_PROCESS_NODE node2 = Node2; PPROCESS_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ProcessItem, EmProcessItemType); PPROCESS_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ProcessItem, EmProcessItemType); UpdateProcessComment(node1, extension1); UpdateProcessComment(node2, extension2); return PhCompareStringWithNull(extension1->Comment, extension2->Comment, TRUE); } VOID ProcessPropertiesInitializingCallback( __in_opt PVOID Parameter, __in_opt PVOID Context ) { PPH_PLUGIN_PROCESS_PROPCONTEXT propContext = Parameter; PhAddProcessPropPage( propContext->PropContext, PhCreateProcessPropPageContextEx(PluginInstance->DllBase, MAKEINTRESOURCE(IDD_PROCCOMMENT), ProcessCommentPageDlgProc, NULL) ); } VOID ProcessTreeNewInitializingCallback( __in_opt PVOID Parameter, __in_opt PVOID Context ) { PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; PH_TREENEW_COLUMN column; ProcessTreeNewHandle = info->TreeNewHandle; memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); column.Text = L"Comment"; column.Width = 120; column.Alignment = PH_ALIGN_LEFT; PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COMMENT_COLUMN_ID, NULL, ProcessCommentSortFunction); } LONG NTAPI ServiceCommentSortFunction( __in PVOID Node1, __in PVOID Node2, __in ULONG SubId, __in PVOID Context ) { PPH_SERVICE_NODE node1 = Node1; PPH_SERVICE_NODE node2 = Node2; PSERVICE_EXTENSION extension1 = PhPluginGetObjectExtension(PluginInstance, node1->ServiceItem, EmServiceItemType); PSERVICE_EXTENSION extension2 = PhPluginGetObjectExtension(PluginInstance, node2->ServiceItem, EmServiceItemType); UpdateServiceComment(node1, extension1); UpdateServiceComment(node2, extension2); return PhCompareStringWithNull(extension1->Comment, extension2->Comment, TRUE); } VOID ServicePropertiesInitializingCallback( __in_opt PVOID Parameter, __in_opt PVOID Context ) { PPH_PLUGIN_OBJECT_PROPERTIES objectProperties = Parameter; PROPSHEETPAGE propSheetPage; if (objectProperties->NumberOfPages < objectProperties->MaximumNumberOfPages) { memset(&propSheetPage, 0, sizeof(PROPSHEETPAGE)); propSheetPage.dwSize = sizeof(PROPSHEETPAGE); propSheetPage.dwFlags = PSP_USETITLE; propSheetPage.hInstance = PluginInstance->DllBase; propSheetPage.pszTemplate = MAKEINTRESOURCE(IDD_SRVCOMMENT); propSheetPage.pszTitle = L"Comment"; propSheetPage.pfnDlgProc = ServiceCommentPageDlgProc; propSheetPage.lParam = (LPARAM)objectProperties->Parameter; objectProperties->Pages[objectProperties->NumberOfPages++] = CreatePropertySheetPage(&propSheetPage); } } VOID ServiceTreeNewInitializingCallback( __in_opt PVOID Parameter, __in_opt PVOID Context ) { PPH_PLUGIN_TREENEW_INFORMATION info = Parameter; PH_TREENEW_COLUMN column; ServiceTreeNewHandle = info->TreeNewHandle; memset(&column, 0, sizeof(PH_TREENEW_COLUMN)); column.Text = L"Comment"; column.Width = 120; column.Alignment = PH_ALIGN_LEFT; PhPluginAddTreeNewColumn(PluginInstance, info->CmData, &column, COMMENT_COLUMN_ID, NULL, ServiceCommentSortFunction); } VOID ProcessItemCreateCallback( __in PVOID Object, __in PH_EM_OBJECT_TYPE ObjectType, __in PVOID Extension ) { PPH_PROCESS_ITEM processItem = Object; PPROCESS_EXTENSION extension = Extension; memset(extension, 0, sizeof(PROCESS_EXTENSION)); PhAcquireQueuedLockExclusive(&ProcessListLock); InsertTailList(&ProcessListHead, &extension->ListEntry); PhReleaseQueuedLockExclusive(&ProcessListLock); } VOID ProcessItemDeleteCallback( __in PVOID Object, __in PH_EM_OBJECT_TYPE ObjectType, __in PVOID Extension ) { PPH_PROCESS_ITEM processItem = Object; PPROCESS_EXTENSION extension = Extension; PhSwapReference(&extension->Comment, NULL); PhAcquireQueuedLockExclusive(&ProcessListLock); RemoveEntryList(&extension->ListEntry); PhReleaseQueuedLockExclusive(&ProcessListLock); } VOID ServiceItemCreateCallback( __in PVOID Object, __in PH_EM_OBJECT_TYPE ObjectType, __in PVOID Extension ) { PPH_SERVICE_ITEM processItem = Object; PSERVICE_EXTENSION extension = Extension; memset(extension, 0, sizeof(SERVICE_EXTENSION)); PhAcquireQueuedLockExclusive(&ServiceListLock); InsertTailList(&ServiceListHead, &extension->ListEntry); PhReleaseQueuedLockExclusive(&ServiceListLock); } VOID ServiceItemDeleteCallback( __in PVOID Object, __in PH_EM_OBJECT_TYPE ObjectType, __in PVOID Extension ) { PPH_SERVICE_ITEM processItem = Object; PSERVICE_EXTENSION extension = Extension; PhSwapReference(&extension->Comment, NULL); PhAcquireQueuedLockExclusive(&ServiceListLock); RemoveEntryList(&extension->ListEntry); PhReleaseQueuedLockExclusive(&ServiceListLock); } LOGICAL DllMain( __in HINSTANCE Instance, __in ULONG Reason, __reserved PVOID Reserved ) { if (Reason == DLL_PROCESS_ATTACH) { PPH_PLUGIN_INFORMATION info; PluginInstance = PhRegisterPlugin(L"ProcessHacker.UserNotes", Instance, &info); if (!PluginInstance) return FALSE; info->DisplayName = L"User Notes"; info->Description = L"Allows the user to comments for processes and services."; info->Author = L"wj32"; info->HasOptions = TRUE; PhRegisterCallback(PhGetPluginCallback(PluginInstance, PluginCallbackLoad), LoadCallback, NULL, &PluginLoadCallbackRegistration); PhRegisterCallback(PhGetPluginCallback(PluginInstance, PluginCallbackShowOptions), ShowOptionsCallback, NULL, &PluginShowOptionsCallbackRegistration); PhRegisterCallback(PhGetPluginCallback(PluginInstance, PluginCallbackTreeNewMessage), TreeNewMessageCallback, NULL, &TreeNewMessageCallbackRegistration); PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackProcessPropertiesInitializing), ProcessPropertiesInitializingCallback, NULL, &ProcessPropertiesInitializingCallbackRegistration); PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackServicePropertiesInitializing), ServicePropertiesInitializingCallback, NULL, &ServicePropertiesInitializingCallbackRegistration); PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackProcessTreeNewInitializing), ProcessTreeNewInitializingCallback, NULL, &ProcessTreeNewInitializingCallbackRegistration); PhRegisterCallback(PhGetGeneralCallback(GeneralCallbackServiceTreeNewInitializing), ServiceTreeNewInitializingCallback, NULL, &ServiceTreeNewInitializingCallbackRegistration); PhPluginSetObjectExtension(PluginInstance, EmProcessItemType, sizeof(PROCESS_EXTENSION), ProcessItemCreateCallback, ProcessItemDeleteCallback); PhPluginSetObjectExtension(PluginInstance, EmServiceItemType, sizeof(SERVICE_EXTENSION), ServiceItemCreateCallback, ServiceItemDeleteCallback); { static PH_SETTING_CREATE settings[] = { { StringSettingType, L"ProcessHacker.UserNotes.DatabasePath", L"%APPDATA%\\Process Hacker 2\\usernotesdb.xml" } }; PhAddSettings(settings, sizeof(settings) / sizeof(PH_SETTING_CREATE)); } } return TRUE; } INT_PTR CALLBACK OptionsDlgProc( __in HWND hwndDlg, __in UINT uMsg, __in WPARAM wParam, __in LPARAM lParam ) { switch (uMsg) { case WM_INITDIALOG: { PPH_STRING path; path = PhGetStringSetting(L"ProcessHacker.UserNotes.DatabasePath"); SetDlgItemText(hwndDlg, IDC_DATABASE, path->Buffer); PhDereferenceObject(path); } break; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDCANCEL: EndDialog(hwndDlg, IDCANCEL); break; case IDOK: { PhSetStringSetting2(L"ProcessHacker.UserNotes.DatabasePath", &PHA_GET_DLGITEM_TEXT(hwndDlg, IDC_DATABASE)->sr); EndDialog(hwndDlg, IDOK); } break; case IDC_BROWSE: { static PH_FILETYPE_FILTER filters[] = { { L"XML files (*.xml)", L"*.xml" }, { L"All files (*.*)", L"*.*" } }; PVOID fileDialog; PPH_STRING fileName; fileDialog = PhCreateOpenFileDialog(); PhSetFileDialogFilter(fileDialog, filters, sizeof(filters) / sizeof(PH_FILETYPE_FILTER)); fileName = PhGetFileName(PHA_GET_DLGITEM_TEXT(hwndDlg, IDC_DATABASE)); PhSetFileDialogFileName(fileDialog, fileName->Buffer); PhDereferenceObject(fileName); if (PhShowFileDialog(hwndDlg, fileDialog)) { fileName = PhGetFileDialogFileName(fileDialog); SetDlgItemText(hwndDlg, IDC_DATABASE, fileName->Buffer); PhDereferenceObject(fileName); } PhFreeFileDialog(fileDialog); } break; } } break; } return FALSE; } INT_PTR CALLBACK ProcessCommentPageDlgProc( __in HWND hwndDlg, __in UINT uMsg, __in WPARAM wParam, __in LPARAM lParam ) { LPPROPSHEETPAGE propSheetPage; PPH_PROCESS_PROPPAGECONTEXT propPageContext; PPH_PROCESS_ITEM processItem; PPROCESS_COMMENT_PAGE_CONTEXT context; if (PhPropPageDlgProcHeader(hwndDlg, uMsg, lParam, &propSheetPage, &propPageContext, &processItem)) { context = propPageContext->Context; } else { return FALSE; } switch (uMsg) { case WM_INITDIALOG: { PDB_OBJECT object; PPH_STRING comment; context = PhAllocate(sizeof(PROCESS_COMMENT_PAGE_CONTEXT)); propPageContext->Context = context; // Load the comment. SendMessage(GetDlgItem(hwndDlg, IDC_COMMENT), EM_SETLIMITTEXT, UNICODE_STRING_MAX_CHARS, 0); LockDb(); if (object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) { comment = object->Comment; PhReferenceObject(comment); } else { comment = PhReferenceEmptyString(); } UnlockDb(); SetDlgItemText(hwndDlg, IDC_COMMENT, comment->Buffer); context->OriginalComment = comment; } break; case WM_DESTROY: { PDB_OBJECT object; PPH_STRING comment; comment = PhGetWindowText(GetDlgItem(hwndDlg, IDC_COMMENT)); LockDb(); if (comment->Length != 0) { CreateDbObject(FILE_TAG, &processItem->ProcessName->sr, comment); } else { if (object = FindDbObject(FILE_TAG, &processItem->ProcessName->sr)) DeleteDbObject(object); } UnlockDb(); PhDereferenceObject(comment); PhDereferenceObject(context->OriginalComment); PhFree(context); PhPropPageDlgProcDestroy(hwndDlg); SaveDb(); InvalidateProcessComments(); } break; case WM_SHOWWINDOW: { PPH_LAYOUT_ITEM dialogItem; if (dialogItem = PhBeginPropPageLayout(hwndDlg, propPageContext)) { PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_COMMENT), dialogItem, PH_ANCHOR_ALL); PhAddPropPageLayoutItem(hwndDlg, GetDlgItem(hwndDlg, IDC_REVERT), dialogItem, PH_ANCHOR_LEFT | PH_ANCHOR_BOTTOM); PhEndPropPageLayout(hwndDlg, propPageContext); } } break; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_COMMENT: { if (HIWORD(wParam) == EN_CHANGE) EnableWindow(GetDlgItem(hwndDlg, IDC_REVERT), TRUE); } break; case IDC_REVERT: { SetDlgItemText(hwndDlg, IDC_COMMENT, context->OriginalComment->Buffer); SendMessage(GetDlgItem(hwndDlg, IDC_COMMENT), EM_SETSEL, 0, -1); SetFocus(GetDlgItem(hwndDlg, IDC_COMMENT)); EnableWindow(GetDlgItem(hwndDlg, IDC_REVERT), FALSE); } break; } } break; } return FALSE; } INT_PTR CALLBACK ServiceCommentPageDlgProc( __in HWND hwndDlg, __in UINT uMsg, __in WPARAM wParam, __in LPARAM lParam ) { PSERVICE_COMMENT_PAGE_CONTEXT context; if (uMsg == WM_INITDIALOG) { context = PhAllocate(sizeof(SERVICE_COMMENT_PAGE_CONTEXT)); memset(context, 0, sizeof(SERVICE_COMMENT_PAGE_CONTEXT)); SetProp(hwndDlg, L"Context", (HANDLE)context); } else { context = (PSERVICE_COMMENT_PAGE_CONTEXT)GetProp(hwndDlg, L"Context"); if (uMsg == WM_DESTROY) RemoveProp(hwndDlg, L"Context"); } if (!context) return FALSE; switch (uMsg) { case WM_INITDIALOG: { LPPROPSHEETPAGE propSheetPage = (LPPROPSHEETPAGE)lParam; PPH_SERVICE_ITEM serviceItem = (PPH_SERVICE_ITEM)propSheetPage->lParam; PDB_OBJECT object; PPH_STRING comment; context->ServiceItem = serviceItem; PhInitializeLayoutManager(&context->LayoutManager, hwndDlg); PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_COMMENT), NULL, PH_ANCHOR_ALL); // Load the comment. SendMessage(GetDlgItem(hwndDlg, IDC_COMMENT), EM_SETLIMITTEXT, UNICODE_STRING_MAX_CHARS, 0); LockDb(); if (object = FindDbObject(SERVICE_TAG, &serviceItem->Name->sr)) { comment = object->Comment; PhReferenceObject(comment); } else { comment = PhReferenceEmptyString(); } UnlockDb(); SetDlgItemText(hwndDlg, IDC_COMMENT, comment->Buffer); PhDereferenceObject(comment); } break; case WM_DESTROY: { PhDeleteLayoutManager(&context->LayoutManager); PhFree(context); } break; case WM_SIZE: { PhLayoutManagerLayout(&context->LayoutManager); } break; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_COMMENT: { if (HIWORD(wParam) == EN_CHANGE) EnableWindow(GetDlgItem(hwndDlg, IDC_REVERT), TRUE); } 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: { PDB_OBJECT object; PPH_STRING comment; comment = PhGetWindowText(GetDlgItem(hwndDlg, IDC_COMMENT)); LockDb(); if (comment->Length != 0) { CreateDbObject(SERVICE_TAG, &context->ServiceItem->Name->sr, comment); } else { if (object = FindDbObject(SERVICE_TAG, &context->ServiceItem->Name->sr)) DeleteDbObject(object); } UnlockDb(); PhDereferenceObject(comment); SaveDb(); InvalidateServiceComments(); SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR); } return TRUE; } } break; } return FALSE; }