/* * Process Hacker - * plugin support * * Copyright (C) 2010-2015 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 #include #include #include #include typedef struct _PHP_PLUGIN_LOAD_ERROR { PPH_STRING FileName; PPH_STRING ErrorMessage; } PHP_PLUGIN_LOAD_ERROR, *PPHP_PLUGIN_LOAD_ERROR; typedef struct _PHP_PLUGIN_MENU_HOOK { PPH_PLUGIN Plugin; PVOID Context; } PHP_PLUGIN_MENU_HOOK, *PPHP_PLUGIN_MENU_HOOK; INT NTAPI PhpPluginsCompareFunction( _In_ PPH_AVL_LINKS Links1, _In_ PPH_AVL_LINKS Links2 ); BOOLEAN PhLoadPlugin( _In_ PPH_STRING FileName ); VOID PhpExecuteCallbackForAllPlugins( _In_ PH_PLUGIN_CALLBACK Callback, _In_ BOOLEAN StartupParameters ); PH_AVL_TREE PhPluginsByName = PH_AVL_TREE_INIT(PhpPluginsCompareFunction); static PH_CALLBACK GeneralCallbacks[GeneralCallbackMaximum]; static PPH_STRING PluginsDirectory; static PPH_LIST LoadErrors; static ULONG NextPluginId = IDPLUGINS + 1; VOID PhPluginsInitialization( VOID ) { ULONG i; for (i = 0; i < GeneralCallbackMaximum; i++) PhInitializeCallback(&GeneralCallbacks[i]); if (WindowsVersion <= WINDOWS_XP) { PH_STRINGREF extendedTools = PH_STRINGREF_INIT(L"ExtendedTools.dll"); // HACK and violation of abstraction. // Always disable ExtendedTools on XP to avoid the annoying error message. PhSetPluginDisabled(&extendedTools, TRUE); } } INT NTAPI PhpPluginsCompareFunction( _In_ PPH_AVL_LINKS Links1, _In_ PPH_AVL_LINKS Links2 ) { PPH_PLUGIN plugin1 = CONTAINING_RECORD(Links1, PH_PLUGIN, Links); PPH_PLUGIN plugin2 = CONTAINING_RECORD(Links2, PH_PLUGIN, Links); return PhCompareStringRef(&plugin1->Name, &plugin2->Name, FALSE); } BOOLEAN PhpLocateDisabledPlugin( _In_ PPH_STRING List, _In_ PPH_STRINGREF BaseName, _Out_opt_ PULONG FoundIndex ) { PH_STRINGREF namePart; PH_STRINGREF remainingPart; remainingPart = List->sr; while (remainingPart.Length != 0) { PhSplitStringRefAtChar(&remainingPart, '|', &namePart, &remainingPart); if (PhEqualStringRef(&namePart, BaseName, TRUE)) { if (FoundIndex) *FoundIndex = (ULONG)(namePart.Buffer - List->Buffer); return TRUE; } } return FALSE; } BOOLEAN PhIsPluginDisabled( _In_ PPH_STRINGREF BaseName ) { BOOLEAN found; PPH_STRING disabled; disabled = PhGetStringSetting(L"DisabledPlugins"); found = PhpLocateDisabledPlugin(disabled, BaseName, NULL); PhDereferenceObject(disabled); return found; } VOID PhSetPluginDisabled( _In_ PPH_STRINGREF BaseName, _In_ BOOLEAN Disable ) { BOOLEAN found; PPH_STRING disabled; ULONG foundIndex; PPH_STRING newDisabled; disabled = PhGetStringSetting(L"DisabledPlugins"); found = PhpLocateDisabledPlugin(disabled, BaseName, &foundIndex); if (Disable && !found) { // We need to add the plugin to the disabled list. if (disabled->Length != 0) { // We have other disabled plugins. Append a pipe character followed by the plugin name. newDisabled = PhCreateStringEx(NULL, disabled->Length + sizeof(WCHAR) + BaseName->Length); memcpy(newDisabled->Buffer, disabled->Buffer, disabled->Length); newDisabled->Buffer[disabled->Length / 2] = '|'; memcpy(&newDisabled->Buffer[disabled->Length / 2 + 1], BaseName->Buffer, BaseName->Length); PhSetStringSetting2(L"DisabledPlugins", &newDisabled->sr); PhDereferenceObject(newDisabled); } else { // This is the first disabled plugin. PhSetStringSetting2(L"DisabledPlugins", BaseName); } } else if (!Disable && found) { ULONG removeCount; // We need to remove the plugin from the disabled list. removeCount = (ULONG)BaseName->Length / 2; if (foundIndex + (ULONG)BaseName->Length / 2 < (ULONG)disabled->Length / 2) { // Remove the following pipe character as well. removeCount++; } else if (foundIndex != 0) { // Remove the preceding pipe character as well. foundIndex--; removeCount++; } newDisabled = PhCreateStringEx(NULL, disabled->Length - removeCount * sizeof(WCHAR)); memcpy(newDisabled->Buffer, disabled->Buffer, foundIndex * sizeof(WCHAR)); memcpy(&newDisabled->Buffer[foundIndex], &disabled->Buffer[foundIndex + removeCount], disabled->Length - removeCount * sizeof(WCHAR) - foundIndex * sizeof(WCHAR)); PhSetStringSetting2(L"DisabledPlugins", &newDisabled->sr); PhDereferenceObject(newDisabled); } PhDereferenceObject(disabled); } static BOOLEAN EnumPluginsDirectoryCallback( _In_ PFILE_DIRECTORY_INFORMATION Information, _In_opt_ PVOID Context ) { PH_STRINGREF baseName; PPH_STRING fileName; baseName.Buffer = Information->FileName; baseName.Length = Information->FileNameLength; if (PhEndsWithStringRef2(&baseName, L".dll", TRUE)) { if (!PhIsPluginDisabled(&baseName)) { fileName = PhCreateStringEx(NULL, PluginsDirectory->Length + Information->FileNameLength); memcpy(fileName->Buffer, PluginsDirectory->Buffer, PluginsDirectory->Length); memcpy(&fileName->Buffer[PluginsDirectory->Length / 2], Information->FileName, Information->FileNameLength); PhLoadPlugin(fileName); PhDereferenceObject(fileName); } } return TRUE; } /** * Loads plugins from the default plugins directory. */ VOID PhLoadPlugins( VOID ) { HANDLE pluginsDirectoryHandle; PPH_STRING pluginsDirectory; pluginsDirectory = PhGetStringSetting(L"PluginsDirectory"); if (RtlDetermineDosPathNameType_U(pluginsDirectory->Buffer) == RtlPathTypeRelative) { // Not absolute. Make sure it is. PluginsDirectory = PhConcatStrings(4, PhApplicationDirectory->Buffer, L"\\", pluginsDirectory->Buffer, L"\\"); PhDereferenceObject(pluginsDirectory); } else { PluginsDirectory = pluginsDirectory; } if (NT_SUCCESS(PhCreateFileWin32( &pluginsDirectoryHandle, PluginsDirectory->Buffer, FILE_GENERIC_READ, 0, FILE_SHARE_READ, FILE_OPEN, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT ))) { UNICODE_STRING pattern = RTL_CONSTANT_STRING(L"*.dll"); PhEnumDirectoryFile(pluginsDirectoryHandle, &pattern, EnumPluginsDirectoryCallback, NULL); NtClose(pluginsDirectoryHandle); } // Handle load errors. // In certain startup modes we want to ignore all plugin load errors. if (LoadErrors && LoadErrors->Count != 0 && !PhStartupParameters.PhSvc) { PH_STRING_BUILDER sb; ULONG i; PPHP_PLUGIN_LOAD_ERROR loadError; PPH_STRING baseName; PhInitializeStringBuilder(&sb, 100); PhAppendStringBuilder2(&sb, L"Unable to load the following plugin(s):\n\n"); for (i = 0; i < LoadErrors->Count; i++) { loadError = LoadErrors->Items[i]; baseName = PhGetBaseName(loadError->FileName); PhAppendFormatStringBuilder(&sb, L"%s: %s\n", baseName->Buffer, PhGetStringOrDefault(loadError->ErrorMessage, L"An unknown error occurred.")); PhDereferenceObject(baseName); } PhAppendStringBuilder2(&sb, L"\nDo you want to disable the above plugin(s)?"); if (PhShowMessage( NULL, MB_ICONERROR | MB_YESNO, sb.String->Buffer ) == IDYES) { ULONG i; for (i = 0; i < LoadErrors->Count; i++) { loadError = LoadErrors->Items[i]; baseName = PhGetBaseName(loadError->FileName); PhSetPluginDisabled(&baseName->sr, TRUE); PhDereferenceObject(baseName); } } PhDeleteStringBuilder(&sb); } // When we loaded settings before, we didn't know about plugin settings, so they // went into the ignored settings list. Now that they've had a chance to add // settings, we should scan the ignored settings list and move the settings to // the right places. if (PhSettingsFileName) PhConvertIgnoredSettings(); PhpExecuteCallbackForAllPlugins(PluginCallbackLoad, TRUE); } /** * Notifies all plugins that the program is shutting down. */ VOID PhUnloadPlugins( VOID ) { PhpExecuteCallbackForAllPlugins(PluginCallbackUnload, FALSE); } /** * Loads a plugin. * * \param FileName The full file name of the plugin. */ BOOLEAN PhLoadPlugin( _In_ PPH_STRING FileName ) { BOOLEAN success; PPH_STRING fileName; PPH_STRING errorMessage; fileName = PhGetFullPath(FileName->Buffer, NULL); if (!fileName) PhSetReference(&fileName, FileName); success = TRUE; if (!LoadLibrary(fileName->Buffer)) { success = FALSE; errorMessage = PhGetWin32Message(GetLastError()); } if (!success) { PPHP_PLUGIN_LOAD_ERROR loadError; loadError = PhAllocate(sizeof(PHP_PLUGIN_LOAD_ERROR)); PhSetReference(&loadError->FileName, fileName); PhSetReference(&loadError->ErrorMessage, errorMessage); if (!LoadErrors) LoadErrors = PhCreateList(2); PhAddItemList(LoadErrors, loadError); if (errorMessage) PhDereferenceObject(errorMessage); } PhDereferenceObject(fileName); return success; } VOID PhpExecuteCallbackForAllPlugins( _In_ PH_PLUGIN_CALLBACK Callback, _In_ BOOLEAN StartupParameters ) { PPH_AVL_LINKS links; for (links = PhMinimumElementAvlTree(&PhPluginsByName); links; links = PhSuccessorElementAvlTree(links)) { PPH_PLUGIN plugin = CONTAINING_RECORD(links, PH_PLUGIN, Links); PPH_LIST parameters = NULL; // Find relevant startup parameters for this plugin. if (StartupParameters && PhStartupParameters.PluginParameters) { ULONG i; for (i = 0; i < PhStartupParameters.PluginParameters->Count; i++) { PPH_STRING string = PhStartupParameters.PluginParameters->Items[i]; PH_STRINGREF pluginName; PH_STRINGREF parameter; if (PhSplitStringRefAtChar(&string->sr, ':', &pluginName, ¶meter) && PhEqualStringRef(&pluginName, &plugin->Name, FALSE) && parameter.Length != 0) { if (!parameters) parameters = PhCreateList(3); PhAddItemList(parameters, PhCreateString2(¶meter)); } } } PhInvokeCallback(PhGetPluginCallback(plugin, Callback), parameters); if (parameters) { PhDereferenceObjects(parameters->Items, parameters->Count); PhDereferenceObject(parameters); } } } BOOLEAN PhpValidatePluginName( _In_ PPH_STRINGREF Name ) { SIZE_T i; PWSTR buffer; SIZE_T count; buffer = Name->Buffer; count = Name->Length / sizeof(WCHAR); for (i = 0; i < count; i++) { if (!iswalnum(buffer[i]) && buffer[i] != ' ' && buffer[i] != '.' && buffer[i] != '_') { return FALSE; } } return TRUE; } /** * Registers a plugin with the host. * * \param Name A unique identifier for the plugin. The function fails * if another plugin has already been registered with the same name. The * name must only contain alphanumeric characters, spaces, dots and * underscores. * \param DllBase The base address of the plugin DLL. This is passed * to the DllMain function. * \param Information A variable which receives a pointer to the * plugin's additional information block. This should be filled in after * the function returns. * * \return A pointer to the plugin instance structure, or NULL if the * function failed. */ PPH_PLUGIN PhRegisterPlugin( _In_ PWSTR Name, _In_ PVOID DllBase, _Out_opt_ PPH_PLUGIN_INFORMATION *Information ) { PPH_PLUGIN plugin; PH_STRINGREF pluginName; PPH_AVL_LINKS existingLinks; ULONG i; PPH_STRING fileName; PhInitializeStringRefLongHint(&pluginName, Name); if (!PhpValidatePluginName(&pluginName)) return NULL; fileName = PhGetDllFileName(DllBase, NULL); if (!fileName) return NULL; plugin = PhAllocate(sizeof(PH_PLUGIN)); memset(plugin, 0, sizeof(PH_PLUGIN)); plugin->Name = pluginName; plugin->DllBase = DllBase; plugin->FileName = fileName; existingLinks = PhAddElementAvlTree(&PhPluginsByName, &plugin->Links); if (existingLinks) { // Another plugin has already been registered with the same name. PhFree(plugin); return NULL; } for (i = 0; i < PluginCallbackMaximum; i++) PhInitializeCallback(&plugin->Callbacks[i]); PhEmInitializeAppContext(&plugin->AppContext, &pluginName); if (Information) *Information = &plugin->Information; return plugin; } /** * Locates a plugin instance structure. * * \param Name The name of the plugin. * * \return A plugin instance structure, or NULL if the plugin was not found. */ PPH_PLUGIN PhFindPlugin( _In_ PWSTR Name ) { PH_STRINGREF name; PhInitializeStringRefLongHint(&name, Name); return PhFindPlugin2(&name); } /** * Locates a plugin instance structure. * * \param Name The name of the plugin. * * \return A plugin instance structure, or NULL if the plugin was not found. */ PPH_PLUGIN PhFindPlugin2( _In_ PPH_STRINGREF Name ) { PPH_AVL_LINKS links; PH_PLUGIN lookupPlugin; lookupPlugin.Name = *Name; links = PhFindElementAvlTree(&PhPluginsByName, &lookupPlugin.Links); if (links) return CONTAINING_RECORD(links, PH_PLUGIN, Links); else return NULL; } /** * Gets a pointer to a plugin's additional information block. * * \param Plugin The plugin instance structure. * * \return The plugin's additional information block. */ PPH_PLUGIN_INFORMATION PhGetPluginInformation( _In_ PPH_PLUGIN Plugin ) { return &Plugin->Information; } /** * Retrieves a pointer to a plugin callback. * * \param Plugin A plugin instance structure. * \param Callback The type of callback. * * \remarks The program invokes plugin callbacks for notifications * specific to a plugin. */ PPH_CALLBACK PhGetPluginCallback( _In_ PPH_PLUGIN Plugin, _In_ PH_PLUGIN_CALLBACK Callback ) { if (Callback >= PluginCallbackMaximum) PhRaiseStatus(STATUS_INVALID_PARAMETER_2); return &Plugin->Callbacks[Callback]; } /** * Retrieves a pointer to a general callback. * * \param Callback The type of callback. * * \remarks The program invokes general callbacks for system-wide * notifications. */ PPH_CALLBACK PhGetGeneralCallback( _In_ PH_GENERAL_CALLBACK Callback ) { if (Callback >= GeneralCallbackMaximum) PhRaiseStatus(STATUS_INVALID_PARAMETER_2); return &GeneralCallbacks[Callback]; } /** * Reserves unique GUI identifiers. * * \param Count The number of identifiers to reserve. * * \return The start of the reserved range. * * \remarks The identifiers reserved by this function are * guaranteed to be unique throughout the program. */ ULONG PhPluginReserveIds( _In_ ULONG Count ) { ULONG nextPluginId; nextPluginId = NextPluginId; NextPluginId += Count; return nextPluginId; } /** * Adds a menu item to the program's main menu. This function is * deprecated. Use \c GeneralCallbackMainMenuInitializing instead. * * \param Plugin A plugin instance structure. * \param Location A handle to the parent menu, or one of the following: * \li \c PH_MENU_ITEM_LOCATION_VIEW The "View" menu. * \li \c PH_MENU_ITEM_LOCATION_TOOLS The "Tools" menu. * \param InsertAfter The text of the menu item to insert the * new menu item after. The search is a case-insensitive prefix search * that ignores prefix characters (ampersands). * \param Id An identifier for the menu item. This should be unique * within the plugin. * \param Text The text of the menu item. * \param Context A user-defined value for the menu item. * * \return TRUE if the function succeeded, otherwise FALSE. * * \remarks The \ref PluginCallbackMenuItem callback is invoked when * the menu item is chosen, and the \ref PH_PLUGIN_MENU_ITEM structure * will contain the \a Id and \a Context values passed to this function. */ ULONG_PTR PhPluginAddMenuItem( _In_ PPH_PLUGIN Plugin, _In_ ULONG_PTR Location, _In_opt_ PWSTR InsertAfter, _In_ ULONG Id, _In_ PWSTR Text, _In_opt_ PVOID Context ) { PH_ADDMENUITEM addMenuItem; addMenuItem.Plugin = Plugin; addMenuItem.InsertAfter = InsertAfter; addMenuItem.Text = Text; addMenuItem.Context = Context; if (Location < 0x1000) { addMenuItem.Location = (ULONG)Location; } else { return 0; } addMenuItem.Flags = Id & PH_MENU_ITEM_VALID_FLAGS; Id &= ~PH_MENU_ITEM_VALID_FLAGS; addMenuItem.Id = Id; return ProcessHacker_AddMenuItem(PhMainWndHandle, &addMenuItem); } /** * Retrieves current system statistics. */ VOID PhPluginGetSystemStatistics( _Out_ PPH_PLUGIN_SYSTEM_STATISTICS Statistics ) { Statistics->Performance = &PhPerfInformation; Statistics->NumberOfProcesses = PhTotalProcesses; Statistics->NumberOfThreads = PhTotalThreads; Statistics->NumberOfHandles = PhTotalHandles; Statistics->CpuKernelUsage = PhCpuKernelUsage; Statistics->CpuUserUsage = PhCpuUserUsage; Statistics->IoReadDelta = PhIoReadDelta; Statistics->IoWriteDelta = PhIoWriteDelta; Statistics->IoOtherDelta = PhIoOtherDelta; Statistics->CommitPages = PhGetItemCircularBuffer_ULONG(&PhCommitHistory, 0); Statistics->PhysicalPages = PhGetItemCircularBuffer_ULONG(&PhPhysicalHistory, 0); Statistics->MaxCpuProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&PhMaxCpuHistory, 0)); Statistics->MaxIoProcessId = UlongToHandle(PhGetItemCircularBuffer_ULONG(&PhMaxIoHistory, 0)); Statistics->CpuKernelHistory = &PhCpuKernelHistory; Statistics->CpuUserHistory = &PhCpuUserHistory; Statistics->CpusKernelHistory = &PhCpusKernelHistory; Statistics->CpusUserHistory = &PhCpusUserHistory; Statistics->IoReadHistory = &PhIoReadHistory; Statistics->IoWriteHistory = &PhIoWriteHistory; Statistics->IoOtherHistory = &PhIoOtherHistory; Statistics->CommitHistory = &PhCommitHistory; Statistics->PhysicalHistory = &PhPhysicalHistory; Statistics->MaxCpuHistory = &PhMaxCpuHistory; Statistics->MaxIoHistory = &PhMaxIoHistory; #ifdef PH_RECORD_MAX_USAGE Statistics->MaxCpuUsageHistory = &PhMaxCpuUsageHistory; Statistics->MaxIoReadOtherHistory = &PhMaxIoReadOtherHistory; Statistics->MaxIoWriteHistory = &PhMaxIoWriteHistory; #else Statistics->MaxCpuUsageHistory = NULL; Statistics->MaxIoReadOtherHistory = NULL; Statistics->MaxIoWriteHistory = NULL; #endif } static VOID NTAPI PhpPluginEMenuItemDeleteFunction( _In_ PPH_EMENU_ITEM Item ) { PPH_PLUGIN_MENU_ITEM pluginMenuItem; pluginMenuItem = Item->Context; if (pluginMenuItem->DeleteFunction) pluginMenuItem->DeleteFunction(pluginMenuItem); PhFree(pluginMenuItem); } /** * Creates a menu item. * * \param Plugin A plugin instance structure. * \param Flags A combination of flags. * \param Id An identifier for the menu item. This should be unique * within the plugin. * \param Text The text of the menu item. * \param Context A user-defined value for the menu item. * * \return A menu item object. This can then be inserted into another * menu using PhInsertEMenuItem(). * * \remarks The \ref PluginCallbackMenuItem callback is invoked when * the menu item is chosen, and the \ref PH_PLUGIN_MENU_ITEM structure * will contain the \a Id and \a Context values passed to this function. */ PPH_EMENU_ITEM PhPluginCreateEMenuItem( _In_ PPH_PLUGIN Plugin, _In_ ULONG Flags, _In_ ULONG Id, _In_ PWSTR Text, _In_opt_ PVOID Context ) { PPH_EMENU_ITEM item; PPH_PLUGIN_MENU_ITEM pluginMenuItem; item = PhCreateEMenuItem(Flags, ID_PLUGIN_MENU_ITEM, Text, NULL, NULL); pluginMenuItem = PhAllocate(sizeof(PH_PLUGIN_MENU_ITEM)); memset(pluginMenuItem, 0, sizeof(PH_PLUGIN_MENU_ITEM)); pluginMenuItem->Plugin = Plugin; pluginMenuItem->Id = Id; pluginMenuItem->Context = Context; item->Context = pluginMenuItem; item->DeleteFunction = PhpPluginEMenuItemDeleteFunction; return item; } /** * Adds a menu hook. * * \param MenuInfo The plugin menu information structure. * \param Plugin A plugin instance structure. * \param Context A user-defined value that is later accessible from the callback. * * \remarks The \ref PluginCallbackMenuHook callback is invoked when any menu item * from the menu is chosen. */ BOOLEAN PhPluginAddMenuHook( _Inout_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, _In_ PPH_PLUGIN Plugin, _In_opt_ PVOID Context ) { PPHP_PLUGIN_MENU_HOOK hook; if (MenuInfo->Flags & PH_PLUGIN_MENU_DISALLOW_HOOKS) return FALSE; if (!MenuInfo->PluginHookList) MenuInfo->PluginHookList = PhAutoDereferenceObject(PhCreateList(2)); hook = PhAutoDereferenceObject(PhCreateAlloc(sizeof(PHP_PLUGIN_MENU_HOOK))); hook->Plugin = Plugin; hook->Context = Context; PhAddItemList(MenuInfo->PluginHookList, hook); return TRUE; } /** * Initializes a plugin menu information structure. * * \param MenuInfo The structure to initialize. * \param Menu The menu being shown. * \param OwnerWindow The window that owns the menu. * \param Flags Additional flags. * * \remarks This function is reserved for internal use. */ VOID PhPluginInitializeMenuInfo( _Out_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, _In_opt_ PPH_EMENU Menu, _In_ HWND OwnerWindow, _In_ ULONG Flags ) { memset(MenuInfo, 0, sizeof(PH_PLUGIN_MENU_INFORMATION)); MenuInfo->Menu = Menu; MenuInfo->OwnerWindow = OwnerWindow; MenuInfo->Flags = Flags; } /** * Triggers a plugin menu item. * * \param MenuInfo The plugin menu information structure. * \param Item The menu item chosen by the user. * * \remarks This function is reserved for internal use. */ BOOLEAN PhPluginTriggerEMenuItem( _In_ PPH_PLUGIN_MENU_INFORMATION MenuInfo, _In_ PPH_EMENU_ITEM Item ) { PPH_PLUGIN_MENU_ITEM pluginMenuItem; ULONG i; PPHP_PLUGIN_MENU_HOOK hook; PH_PLUGIN_MENU_HOOK_INFORMATION menuHookInfo; if (MenuInfo->PluginHookList) { for (i = 0; i < MenuInfo->PluginHookList->Count; i++) { hook = MenuInfo->PluginHookList->Items[i]; menuHookInfo.MenuInfo = MenuInfo; menuHookInfo.SelectedItem = Item; menuHookInfo.Context = hook->Context; menuHookInfo.Handled = FALSE; PhInvokeCallback(PhGetPluginCallback(hook->Plugin, PluginCallbackMenuHook), &menuHookInfo); if (menuHookInfo.Handled) return TRUE; } } if (Item->Id != ID_PLUGIN_MENU_ITEM) return FALSE; pluginMenuItem = Item->Context; pluginMenuItem->OwnerWindow = MenuInfo->OwnerWindow; PhInvokeCallback(PhGetPluginCallback(pluginMenuItem->Plugin, PluginCallbackMenuItem), pluginMenuItem); return TRUE; } /** * Adds a column to a tree new control. * * \param Plugin A plugin instance structure. * \param CmData The CmData value from the \ref PH_PLUGIN_TREENEW_INFORMATION * structure. * \param Column The column properties. * \param SubId An identifier for the column. This should be unique within the * plugin. * \param Context A user-defined value. * \param SortFunction The sort function for the column. */ BOOLEAN PhPluginAddTreeNewColumn( _In_ PPH_PLUGIN Plugin, _In_ PVOID CmData, _In_ PPH_TREENEW_COLUMN Column, _In_ ULONG SubId, _In_opt_ PVOID Context, _In_opt_ PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction ) { return !!PhCmCreateColumn( CmData, Column, Plugin, SubId, Context, SortFunction ); } /** * Sets the object extension size and callbacks for an object type. * * \param Plugin A plugin instance structure. * \param ObjectType The type of object for which the extension is being registered. * \param ExtensionSize The size of the extension, in bytes. * \param CreateCallback The object creation callback. * \param DeleteCallback The object deletion callback. */ VOID PhPluginSetObjectExtension( _In_ PPH_PLUGIN Plugin, _In_ PH_EM_OBJECT_TYPE ObjectType, _In_ ULONG ExtensionSize, _In_opt_ PPH_EM_OBJECT_CALLBACK CreateCallback, _In_opt_ PPH_EM_OBJECT_CALLBACK DeleteCallback ) { PhEmSetObjectExtension( &Plugin->AppContext, ObjectType, ExtensionSize, CreateCallback, DeleteCallback ); } /** * Gets the object extension for an object. * * \param Plugin A plugin instance structure. * \param Object The object. * \param ObjectType The type of object for which an extension has been registered. */ PVOID PhPluginGetObjectExtension( _In_ PPH_PLUGIN Plugin, _In_ PVOID Object, _In_ PH_EM_OBJECT_TYPE ObjectType ) { return PhEmGetObjectExtension( &Plugin->AppContext, ObjectType, Object ); } /** * Creates a notification icon. * * \param Plugin A plugin instance structure. * \param SubId An identifier for the column. This should be unique within the * plugin. * \param Context A user-defined value. * \param Text A string describing the notification icon. * \param Flags A combination of flags. * \li \c PH_NF_ICON_UNAVAILABLE The notification icon is currently unavailable. * \param RegistrationData A \ref PH_NF_ICON_REGISTRATION_DATA structure that * contains registration information. */ struct _PH_NF_ICON *PhPluginRegisterIcon( _In_ PPH_PLUGIN Plugin, _In_ ULONG SubId, _In_opt_ PVOID Context, _In_ PWSTR Text, _In_ ULONG Flags, _In_ struct _PH_NF_ICON_REGISTRATION_DATA *RegistrationData ) { return PhNfRegisterIcon( Plugin, SubId, Context, Text, Flags, RegistrationData->UpdateCallback, RegistrationData->MessageCallback ); } /** * Allows a plugin to receive all treenew messages, not just column-related ones. * * \param Plugin A plugin instance structure. * \param CmData The CmData value from the \ref PH_PLUGIN_TREENEW_INFORMATION * structure. */ VOID PhPluginEnableTreeNewNotify( _In_ PPH_PLUGIN Plugin, _In_ PVOID CmData ) { PhCmSetNotifyPlugin(CmData, Plugin); } BOOLEAN PhPluginQueryPhSvc( _Out_ PPH_PLUGIN_PHSVC_CLIENT Client ) { if (!PhSvcClServerProcessId) return FALSE; Client->ServerProcessId = PhSvcClServerProcessId; Client->FreeHeap = PhSvcpFreeHeap; Client->CreateString = PhSvcpCreateString; return TRUE; } NTSTATUS PhPluginCallPhSvc( _In_ PPH_PLUGIN Plugin, _In_ ULONG SubId, _In_reads_bytes_opt_(InLength) PVOID InBuffer, _In_ ULONG InLength, _Out_writes_bytes_opt_(OutLength) PVOID OutBuffer, _In_ ULONG OutLength ) { NTSTATUS status; PPH_STRING apiId; PH_FORMAT format[4]; PhInitFormatC(&format[0], '+'); PhInitFormatSR(&format[1], Plugin->Name); PhInitFormatC(&format[2], '+'); PhInitFormatU(&format[3], SubId); apiId = PhFormat(format, 4, 50); status = PhSvcCallPlugin(&apiId->sr, InBuffer, InLength, OutBuffer, OutLength); PhDereferenceObject(apiId); return status; }