297 lines
7.6 KiB
C
297 lines
7.6 KiB
C
/*
|
|
* Process Hacker .NET Tools -
|
|
* thread list extensions
|
|
*
|
|
* Copyright (C) 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "dn.h"
|
|
#include "clrsup.h"
|
|
|
|
VOID NTAPI ThreadsContextCreateCallback(
|
|
_In_ PVOID Object,
|
|
_In_ PH_EM_OBJECT_TYPE ObjectType,
|
|
_In_ PVOID Extension
|
|
);
|
|
|
|
VOID NTAPI ThreadsContextDeleteCallback(
|
|
_In_ PVOID Object,
|
|
_In_ PH_EM_OBJECT_TYPE ObjectType,
|
|
_In_ PVOID Extension
|
|
);
|
|
|
|
VOID ThreadTreeNewMessage(
|
|
_In_ PVOID Parameter
|
|
);
|
|
|
|
LONG ThreadTreeNewSortFunction(
|
|
_In_ PVOID Node1,
|
|
_In_ PVOID Node2,
|
|
_In_ ULONG SubId,
|
|
_In_ PVOID Context
|
|
);
|
|
|
|
#define THREAD_TREE_CONTEXT_TYPE 1
|
|
|
|
typedef struct _THREAD_TREE_CONTEXT
|
|
{
|
|
ULONG Type;
|
|
HANDLE ProcessId;
|
|
PH_CALLBACK_REGISTRATION AddedCallbackRegistration;
|
|
PCLR_PROCESS_SUPPORT Support;
|
|
} THREAD_TREE_CONTEXT, *PTHREAD_TREE_CONTEXT;
|
|
|
|
static PPH_HASHTABLE ContextHashtable;
|
|
static PH_QUEUED_LOCK ContextHashtableLock = PH_QUEUED_LOCK_INIT;
|
|
static PH_INITONCE ContextHashtableInitOnce = PH_INITONCE_INIT;
|
|
|
|
VOID InitializeTreeNewObjectExtensions(
|
|
VOID
|
|
)
|
|
{
|
|
PhPluginSetObjectExtension(
|
|
PluginInstance,
|
|
EmThreadsContextType,
|
|
sizeof(THREAD_TREE_CONTEXT),
|
|
ThreadsContextCreateCallback,
|
|
ThreadsContextDeleteCallback
|
|
);
|
|
}
|
|
|
|
VOID AddTreeNewColumn(
|
|
_In_ PPH_PLUGIN_TREENEW_INFORMATION TreeNewInfo,
|
|
_In_ PVOID Context,
|
|
_In_ ULONG SubId,
|
|
_In_ BOOLEAN Visible,
|
|
_In_ PWSTR Text,
|
|
_In_ ULONG Width,
|
|
_In_ ULONG Alignment,
|
|
_In_ ULONG TextFlags,
|
|
_In_ BOOLEAN SortDescending,
|
|
_In_ PPH_PLUGIN_TREENEW_SORT_FUNCTION SortFunction
|
|
)
|
|
{
|
|
PH_TREENEW_COLUMN column;
|
|
|
|
memset(&column, 0, sizeof(PH_TREENEW_COLUMN));
|
|
column.Visible = Visible;
|
|
column.SortDescending = SortDescending;
|
|
column.Text = Text;
|
|
column.Width = Width;
|
|
column.Alignment = Alignment;
|
|
column.DisplayIndex = TreeNew_GetVisibleColumnCount(TreeNewInfo->TreeNewHandle);
|
|
column.TextFlags = TextFlags;
|
|
|
|
PhPluginAddTreeNewColumn(
|
|
PluginInstance,
|
|
TreeNewInfo->CmData,
|
|
&column,
|
|
SubId,
|
|
Context,
|
|
SortFunction
|
|
);
|
|
}
|
|
|
|
VOID DispatchTreeNewMessage(
|
|
_In_ PVOID Parameter
|
|
)
|
|
{
|
|
PPH_PLUGIN_TREENEW_MESSAGE message = Parameter;
|
|
|
|
switch (*(PULONG)message->Context)
|
|
{
|
|
case THREAD_TREE_CONTEXT_TYPE:
|
|
ThreadTreeNewMessage(Parameter);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static VOID ThreadAddedHandler(
|
|
_In_opt_ PVOID Parameter,
|
|
_In_opt_ PVOID Context
|
|
)
|
|
{
|
|
PPH_THREAD_ITEM threadItem = Parameter;
|
|
PDN_THREAD_ITEM dnThread;
|
|
PTHREAD_TREE_CONTEXT context = Context;
|
|
|
|
dnThread = PhPluginGetObjectExtension(PluginInstance, threadItem, EmThreadItemType);
|
|
memset(dnThread, 0, sizeof(DN_THREAD_ITEM));
|
|
dnThread->ThreadItem = threadItem;
|
|
}
|
|
|
|
VOID NTAPI ThreadsContextCreateCallback(
|
|
_In_ PVOID Object,
|
|
_In_ PH_EM_OBJECT_TYPE ObjectType,
|
|
_In_ PVOID Extension
|
|
)
|
|
{
|
|
PPH_THREADS_CONTEXT threadsContext = Object;
|
|
PTHREAD_TREE_CONTEXT context = Extension;
|
|
|
|
memset(context, 0, sizeof(THREAD_TREE_CONTEXT));
|
|
context->Type = THREAD_TREE_CONTEXT_TYPE;
|
|
context->ProcessId = threadsContext->Provider->ProcessId;
|
|
|
|
PhRegisterCallback(
|
|
&threadsContext->Provider->ThreadAddedEvent,
|
|
ThreadAddedHandler,
|
|
context,
|
|
&context->AddedCallbackRegistration
|
|
);
|
|
}
|
|
|
|
VOID NTAPI ThreadsContextDeleteCallback(
|
|
_In_ PVOID Object,
|
|
_In_ PH_EM_OBJECT_TYPE ObjectType,
|
|
_In_ PVOID Extension
|
|
)
|
|
{
|
|
PPH_THREADS_CONTEXT threadsContext = Object;
|
|
PTHREAD_TREE_CONTEXT context = Extension;
|
|
|
|
PhUnregisterCallback(
|
|
&threadsContext->Provider->ThreadAddedEvent,
|
|
&context->AddedCallbackRegistration
|
|
);
|
|
|
|
if (context->Support)
|
|
FreeClrProcessSupport(context->Support);
|
|
}
|
|
|
|
VOID ThreadTreeNewInitializing(
|
|
_In_ PVOID Parameter
|
|
)
|
|
{
|
|
PPH_PLUGIN_TREENEW_INFORMATION info = Parameter;
|
|
PPH_THREADS_CONTEXT threadsContext;
|
|
PTHREAD_TREE_CONTEXT context;
|
|
BOOLEAN isDotNet;
|
|
|
|
threadsContext = info->SystemContext;
|
|
context = PhPluginGetObjectExtension(PluginInstance, threadsContext, EmThreadsContextType);
|
|
|
|
if (NT_SUCCESS(PhGetProcessIsDotNet(threadsContext->Provider->ProcessId, &isDotNet)) && isDotNet)
|
|
{
|
|
PCLR_PROCESS_SUPPORT support;
|
|
|
|
support = CreateClrProcessSupport(threadsContext->Provider->ProcessId);
|
|
|
|
if (!support)
|
|
return;
|
|
|
|
context->Support = support;
|
|
|
|
AddTreeNewColumn(info, context, DNTHTNC_APPDOMAIN, TRUE, L"AppDomain", 120, PH_ALIGN_LEFT, 0, FALSE, ThreadTreeNewSortFunction);
|
|
}
|
|
}
|
|
|
|
VOID ThreadTreeNewUninitializing(
|
|
_In_ PVOID Parameter
|
|
)
|
|
{
|
|
NOTHING;
|
|
}
|
|
|
|
VOID UpdateThreadClrData(
|
|
_In_ PTHREAD_TREE_CONTEXT Context,
|
|
_Inout_ PDN_THREAD_ITEM DnThread
|
|
)
|
|
{
|
|
if (!DnThread->ClrDataValid)
|
|
{
|
|
IXCLRDataProcess *process;
|
|
IXCLRDataTask *task;
|
|
IXCLRDataAppDomain *appDomain;
|
|
|
|
if (Context->Support)
|
|
process = Context->Support->DataProcess;
|
|
else
|
|
return;
|
|
|
|
if (SUCCEEDED(IXCLRDataProcess_GetTaskByOSThreadID(process, HandleToUlong(DnThread->ThreadItem->ThreadId), &task)))
|
|
{
|
|
if (SUCCEEDED(IXCLRDataTask_GetCurrentAppDomain(task, &appDomain)))
|
|
{
|
|
DnThread->AppDomainText = GetNameXClrDataAppDomain(appDomain);
|
|
IXCLRDataAppDomain_Release(appDomain);
|
|
}
|
|
|
|
IXCLRDataTask_Release(task);
|
|
}
|
|
|
|
DnThread->ClrDataValid = TRUE;
|
|
}
|
|
}
|
|
|
|
VOID ThreadTreeNewMessage(
|
|
_In_ PVOID Parameter
|
|
)
|
|
{
|
|
PPH_PLUGIN_TREENEW_MESSAGE message = Parameter;
|
|
PTHREAD_TREE_CONTEXT context = message->Context;
|
|
|
|
if (message->Message == TreeNewGetCellText)
|
|
{
|
|
PPH_TREENEW_GET_CELL_TEXT getCellText = message->Parameter1;
|
|
PPH_THREAD_NODE threadNode = (PPH_THREAD_NODE)getCellText->Node;
|
|
PDN_THREAD_ITEM dnThread;
|
|
|
|
dnThread = PhPluginGetObjectExtension(PluginInstance, threadNode->ThreadItem, EmThreadItemType);
|
|
|
|
switch (message->SubId)
|
|
{
|
|
case DNTHTNC_APPDOMAIN:
|
|
UpdateThreadClrData(context, dnThread);
|
|
getCellText->Text = PhGetStringRef(dnThread->AppDomainText);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
LONG ThreadTreeNewSortFunction(
|
|
_In_ PVOID Node1,
|
|
_In_ PVOID Node2,
|
|
_In_ ULONG SubId,
|
|
_In_ PVOID Context
|
|
)
|
|
{
|
|
PTHREAD_TREE_CONTEXT context = Context;
|
|
LONG result;
|
|
PPH_THREAD_NODE node1 = Node1;
|
|
PPH_THREAD_NODE node2 = Node2;
|
|
PDN_THREAD_ITEM dnThread1;
|
|
PDN_THREAD_ITEM dnThread2;
|
|
|
|
dnThread1 = PhPluginGetObjectExtension(PluginInstance, node1->ThreadItem, EmThreadItemType);
|
|
dnThread2 = PhPluginGetObjectExtension(PluginInstance, node2->ThreadItem, EmThreadItemType);
|
|
|
|
result = 0;
|
|
|
|
switch (SubId)
|
|
{
|
|
case DNTHTNC_APPDOMAIN:
|
|
UpdateThreadClrData(context, dnThread1);
|
|
UpdateThreadClrData(context, dnThread2);
|
|
result = PhCompareStringWithNull(dnThread1->AppDomainText, dnThread2->AppDomainText, TRUE);
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|