/*
* 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;
}