2025-05-13 19:49:49 +03:00

752 lines
27 KiB
C

/*
* Process Hacker Extra Plugins -
* Plugin Manager
*
* Copyright (C) 2016 dmex
*
* 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 "main.h"
BOOLEAN WordMatchStringRef(
_In_ PWCT_CONTEXT Context,
_In_ PPH_STRINGREF Text
)
{
PH_STRINGREF part;
PH_STRINGREF remainingPart;
remainingPart = Context->SearchboxText->sr;
while (remainingPart.Length)
{
PhSplitStringRefAtChar(&remainingPart, '|', &part, &remainingPart);
if (part.Length)
{
if (PhFindStringInStringRef(Text, &part, TRUE) != -1)
return TRUE;
}
}
return FALSE;
}
BOOLEAN WordMatchStringZ(
_In_ PWCT_CONTEXT Context,
_In_ PWSTR Text
)
{
PH_STRINGREF text;
PhInitializeStringRef(&text, Text);
return WordMatchStringRef(Context, &text);
}
VOID UpdateTreeView(
_In_ PWCT_CONTEXT Context
)
{
//PhSwapReference(&Context->TreeText, PhCreateString(L"Loading Plugins..."));
//TreeNew_SetEmptyText(Context->TreeNewHandle, &Context->TreeText->sr, 0);
PhApplyTreeNewFilters(Context->TreeFilter);
TreeNew_AutoSizeColumn(Context->TreeNewHandle, TREE_COLUMN_ITEM_NAME, TN_AUTOSIZE_REMAINING_SPACE);
}
VOID UpdateMenuView(_In_ PWCT_CONTEXT Context, UINT Id)
{
HWND active = Context->PluginMenuActive;
Context->PluginMenuActiveId = Id;
Context->PluginMenuActive = GetDlgItem(Context->DialogHandle, Id);
RedrawWindow(active, NULL, NULL, RDW_ERASENOW | RDW_INVALIDATE | RDW_FRAME);
}
LRESULT DrawButton(
_In_ PWCT_CONTEXT Context,
_In_ LPARAM lParam
)
{
LPNMTVCUSTOMDRAW drawInfo = (LPNMTVCUSTOMDRAW)lParam;
BOOLEAN isGrayed = (drawInfo->nmcd.uItemState & CDIS_GRAYED) == CDIS_GRAYED;
BOOLEAN isChecked = (drawInfo->nmcd.uItemState & CDIS_CHECKED) == CDIS_CHECKED;
BOOLEAN isDisabled = (drawInfo->nmcd.uItemState & CDIS_DISABLED) == CDIS_DISABLED;
BOOLEAN isSelected = (drawInfo->nmcd.uItemState & CDIS_SELECTED) == CDIS_SELECTED;
BOOLEAN isHighlighted = (drawInfo->nmcd.uItemState & CDIS_HOT) == CDIS_HOT;
BOOLEAN isFocused = (drawInfo->nmcd.uItemState & CDIS_FOCUS) == CDIS_FOCUS;
if (drawInfo->nmcd.dwDrawStage == CDDS_PREPAINT)
{
HPEN PenActive = CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
HPEN PenNormal = CreatePen(PS_SOLID, 1, RGB(0, 0xff, 0));
HBRUSH BrushNormal = GetSysColorBrush(COLOR_3DFACE);
HBRUSH BrushSelected = CreateSolidBrush(RGB(0xff, 0xff, 0xff));
HBRUSH BrushPushed = CreateSolidBrush(RGB(153, 209, 255));
PPH_STRING windowText = PH_AUTO(PhGetWindowText(drawInfo->nmcd.hdr.hwndFrom));
SetBkMode(drawInfo->nmcd.hdc, TRANSPARENT);
if (isHighlighted || Context->PluginMenuActiveId == drawInfo->nmcd.hdr.idFrom)
{
FillRect(drawInfo->nmcd.hdc, &drawInfo->nmcd.rc, BrushNormal);
SelectObject(drawInfo->nmcd.hdc, PenActive);
SelectObject(drawInfo->nmcd.hdc, Context->BoldFontHandle);
}
else if (isSelected)
{
FillRect(drawInfo->nmcd.hdc, &drawInfo->nmcd.rc, BrushPushed);
SelectObject(drawInfo->nmcd.hdc, PenActive);
SelectObject(drawInfo->nmcd.hdc, Context->BoldFontHandle);
}
else
{
FillRect(drawInfo->nmcd.hdc, &drawInfo->nmcd.rc, BrushNormal);
}
DrawText(
drawInfo->nmcd.hdc,
windowText->Buffer,
(ULONG)windowText->Length / 2,
&drawInfo->nmcd.rc,
DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE
);
MoveToEx(drawInfo->nmcd.hdc, drawInfo->nmcd.rc.left, drawInfo->nmcd.rc.bottom, 0);
LineTo(drawInfo->nmcd.hdc, drawInfo->nmcd.rc.right, drawInfo->nmcd.rc.bottom);
DeletePen(PenNormal);
DeletePen(PenActive);
DeleteBrush(BrushNormal);
DeleteBrush(BrushSelected);
DeleteBrush(BrushPushed);
return CDRF_SKIPDEFAULT;
}
return CDRF_DODEFAULT;
}
BOOLEAN ProcessTreeFilterCallback(
_In_ PPH_TREENEW_NODE Node,
_In_opt_ PVOID Context
)
{
PWCT_CONTEXT context = Context;
PPLUGIN_NODE node = (PPLUGIN_NODE)Node;
// Hide plugins from different tabs
if (node->State != context->Type)
return FALSE;
// Hide uninstalled plugins from the local tab
//if (node->Type == PLUGIN_VIEW_LOCAL && node->State == PLUGIN_STATE_UNINSTALLED)
// return FALSE;
//// Hide installed plugins from the remote tab
//if (node->Type == PLUGIN_VIEW_REMOTE && node->State == PLUGIN_STATE_INSTALLED)
// return FALSE;
if (PhIsNullOrEmptyString(context->SearchboxText))
return TRUE;
if (!PhIsNullOrEmptyString(node->InternalName))
{
if (WordMatchStringRef(context, &node->InternalName->sr))
return TRUE;
}
if (!PhIsNullOrEmptyString(node->Id))
{
if (WordMatchStringRef(context, &node->Id->sr))
return TRUE;
}
if (!PhIsNullOrEmptyString(node->Name))
{
if (WordMatchStringRef(context, &node->Name->sr))
return TRUE;
}
if (!PhIsNullOrEmptyString(node->Version))
{
if (WordMatchStringRef(context, &node->Version->sr))
return TRUE;
}
if (!PhIsNullOrEmptyString(node->Author))
{
if (WordMatchStringRef(context, &node->Author->sr))
return TRUE;
}
if (!PhIsNullOrEmptyString(node->Description))
{
if (WordMatchStringRef(context, &node->Description->sr))
return TRUE;
}
return FALSE;
}
INT_PTR CALLBACK CloudPluginsDlgProc(
_In_ HWND hwndDlg,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
PWCT_CONTEXT context = NULL;
if (uMsg == WM_INITDIALOG)
{
context = (PWCT_CONTEXT)PhAllocate(sizeof(WCT_CONTEXT));
memset(context, 0, sizeof(WCT_CONTEXT));
SetProp(hwndDlg, L"Context", (HANDLE)context);
}
else
{
context = (PWCT_CONTEXT)GetProp(hwndDlg, L"Context");
if (uMsg == WM_DESTROY)
{
PhSaveWindowPlacementToSetting(SETTING_NAME_WINDOW_POSITION, SETTING_NAME_WINDOW_SIZE, hwndDlg);
PhDeleteLayoutManager(&context->LayoutManager);
PhUnregisterDialog(hwndDlg);
RemoveProp(hwndDlg, L"Context");
PhFree(context);
PostQuitMessage(0);
}
}
if (context == NULL)
return FALSE;
switch (uMsg)
{
case WM_INITDIALOG:
{
LOGFONT logFont;
context->DialogHandle = hwndDlg;
context->PluginMenuActiveId = IDC_INSTALLED;
context->PluginMenuActive = GetDlgItem(hwndDlg, IDC_INSTALLED);
context->TreeNewHandle = GetDlgItem(hwndDlg, IDC_PLUGINTREE);
context->SearchHandle = GetDlgItem(hwndDlg, IDC_SEARCHBOX);
context->SearchboxText = PhReferenceEmptyString();
CreateSearchControl(hwndDlg, context->SearchHandle, ID_SEARCH_CLEAR);
if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &logFont, 0))
{
context->NormalFontHandle = CreateFont(
-PhMultiplyDivideSigned(-14, PhGlobalDpi, 72),
0,
0,
0,
FW_NORMAL,
FALSE,
FALSE,
FALSE,
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY | ANTIALIASED_QUALITY,
DEFAULT_PITCH,
logFont.lfFaceName
);
context->BoldFontHandle = CreateFont(
-PhMultiplyDivideSigned(-16, PhGlobalDpi, 72),
0,
0,
0,
FW_BOLD,
FALSE,
FALSE,
FALSE,
ANSI_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY | ANTIALIASED_QUALITY,
DEFAULT_PITCH,
logFont.lfFaceName
);
}
PhCenterWindow(hwndDlg, PhMainWndHandle);
WtcInitializeWindowTree(hwndDlg, context->TreeNewHandle, &context->TreeContext);
PhAddTreeNewFilter(context->TreeFilter = WtcGetTreeListFilterSupport(), (PPH_TN_FILTER_FUNCTION)ProcessTreeFilterCallback, context);
PhInitializeLayoutManager(&context->LayoutManager, hwndDlg);
PhAddLayoutItem(&context->LayoutManager, context->TreeNewHandle, NULL, PH_ANCHOR_ALL);
PhAddLayoutItem(&context->LayoutManager, context->SearchHandle, NULL, PH_ANCHOR_TOP | PH_ANCHOR_RIGHT);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_CLEANUP), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDC_DISABLED), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_LEFT);
PhAddLayoutItem(&context->LayoutManager, GetDlgItem(hwndDlg, IDOK), NULL, PH_ANCHOR_BOTTOM | PH_ANCHOR_RIGHT);
PhLoadWindowPlacementFromSetting(SETTING_NAME_WINDOW_POSITION, SETTING_NAME_WINDOW_SIZE, hwndDlg);
EnumerateLoadedPlugins(context);
SetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED), PhGetString(PhaFormatString(L"Disabled Plugins (%lu)", PhDisabledPluginsCount())));
PhQueueItemWorkQueue(PhGetGlobalWorkQueue(), QueryPluginsCallbackThread, context);
UpdateTreeView(context);
SendMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_INSTALLED), TRUE);
}
break;
case WM_SIZE:
PhLayoutManagerLayout(&context->LayoutManager);
TreeNew_AutoSizeColumn(context->TreeNewHandle, TREE_COLUMN_ITEM_NAME, TN_AUTOSIZE_REMAINING_SPACE);
break;
case WM_COMMAND:
{
switch (GET_WM_COMMAND_CMD(wParam, lParam))
{
case EN_CHANGE:
{
PPH_STRING newSearchboxText;
newSearchboxText = PH_AUTO(PhGetWindowText(context->SearchHandle));
if (!PhEqualString(context->SearchboxText, newSearchboxText, FALSE))
{
// Cache the current search text for our callback.
PhSwapReference(&context->SearchboxText, newSearchboxText);
if (!PhIsNullOrEmptyString(context->SearchboxText))
{
// Expand the nodes to ensure that they will be visible to the user.
}
PhApplyTreeNewFilters(context->TreeFilter);
}
}
break;
}
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case ID_SEARCH_CLEAR:
{
if (context->SearchHandle)
{
SetFocus(context->SearchHandle);
Static_SetText(context->SearchHandle, L"");
UpdateTreeView(context);
}
}
break;
case IDC_INSTALLED:
{
context->Type = PLUGIN_STATE_LOCAL;
UpdateMenuView(context, IDC_INSTALLED);
UpdateTreeView(context);
}
break;
case IDC_BROWSE:
{
context->Type = PLUGIN_STATE_REMOTE;
UpdateMenuView(context, IDC_BROWSE);
UpdateTreeView(context);
}
break;
case IDC_UPDATES:
{
context->Type = PLUGIN_STATE_UPDATE;
UpdateMenuView(context, IDC_UPDATES);
UpdateTreeView(context);
}
break;
case WM_ACTION:
{
PPLUGIN_NODE selectedNode;
if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
{
if (selectedNode->State == PLUGIN_STATE_LOCAL)
{
if (selectedNode->PluginInstance && selectedNode->PluginInstance->Information.HasOptions)
{
PhInvokeCallback(PhGetPluginCallback((PPH_PLUGIN)selectedNode->PluginInstance, PluginCallbackShowOptions), hwndDlg);
}
}
else if (selectedNode->State == PLUGIN_STATE_REMOTE)
{
if (StartInitialCheck(hwndDlg, selectedNode, PLUGIN_ACTION_INSTALL))
{
}
}
else if (selectedNode->State == PLUGIN_STATE_UPDATE)
{
if (StartInitialCheck(hwndDlg, selectedNode, PLUGIN_ACTION_INSTALL))
{
}
}
}
}
break;
case ID_WCTSHOWCONTEXTMENU:
{
POINT cursorPos;
PPH_EMENU menu;
PPH_EMENU_ITEM selectedItem;
PPLUGIN_NODE selectedNode;
GetCursorPos(&cursorPos);
if (!(selectedNode = WeGetSelectedWindowNode(&context->TreeContext)))
break;
menu = PhCreateEMenu();
if (selectedNode->State == PLUGIN_STATE_LOCAL)
{
HICON shieldIcon;
shieldIcon = PhLoadIcon(NULL, IDI_SHIELD, PH_LOAD_ICON_SIZE_SMALL | PH_LOAD_ICON_STRICT, 0, 0);
selectedItem = PhCreateEMenuItem(0, ID_MENU_UNINSTALL, L"Uninstall", NULL, NULL);
if (shieldIcon)
{
selectedItem->Bitmap = PhIconToBitmap(
shieldIcon,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON)
);
DestroyIcon(shieldIcon);
}
PhInsertEMenuItem(menu, selectedItem, -1);
PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1);
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_MENU_DISABLE, L"Disable", NULL, NULL), -1);
PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1);
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_MENU_PROPERTIES, L"Options", NULL, NULL), -1);
if (!selectedNode->PluginInstance || !selectedNode->PluginInstance->Information.HasOptions)
{
PhSetFlagsEMenuItem(menu, ID_MENU_PROPERTIES, PH_EMENU_DISABLED, PH_EMENU_DISABLED);
}
if (!PhGetOwnTokenAttributes().Elevated)
{
PhSetFlagsEMenuItem(menu, ID_MENU_UNINSTALL, PH_EMENU_DISABLED, PH_EMENU_DISABLED);
}
}
else
{
HICON shieldIcon;
shieldIcon = PhLoadIcon(NULL, IDI_SHIELD, PH_LOAD_ICON_SIZE_SMALL | PH_LOAD_ICON_STRICT, 0, 0);
selectedItem = PhCreateEMenuItem(0, ID_MENU_INSTALL, PhaFormatString(L"Install %s plugin", PhGetStringOrEmpty(selectedNode->Name))->Buffer, NULL, NULL);
if (shieldIcon)
{
selectedItem->Bitmap = PhIconToBitmap(
shieldIcon,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON)
);
DestroyIcon(shieldIcon);
}
PhInsertEMenuItem(menu, selectedItem, -1);
PhInsertEMenuItem(menu, PhCreateEMenuItem(PH_EMENU_SEPARATOR, 0, NULL, NULL, NULL), -1);
PhInsertEMenuItem(menu, PhCreateEMenuItem(0, ID_MENU_DISABLE, L"Disable", NULL, NULL), -1);
if (!PhGetOwnTokenAttributes().Elevated)
{
PhSetFlagsEMenuItem(menu, ID_MENU_INSTALL, PH_EMENU_DISABLED, PH_EMENU_DISABLED);
}
}
selectedItem = PhShowEMenu(
menu,
hwndDlg,
PH_EMENU_SHOW_LEFTRIGHT,
PH_ALIGN_LEFT | PH_ALIGN_TOP,
cursorPos.x,
cursorPos.y
);
if (selectedItem && selectedItem->Id != -1)
{
switch (selectedItem->Id)
{
case ID_MENU_INSTALL:
{
if (!PhGetOwnTokenAttributes().Elevated)
{
//SHELLEXECUTEINFO info = { sizeof(SHELLEXECUTEINFO) };
//info.lpFile = L"ProcessHacker.exe";
//info.lpParameters = L"-plugin dmex.ExtraPlugins:INSTALL -plugin dmex.ExtraPlugins:hex64value";
//info.lpVerb = L"runas";
//info.nShow = SW_SHOW;
//info.hwnd = hwndDlg;
//if (!ShellExecuteEx(&info))
//{
//}
}
else
{
PPLUGIN_NODE selectedNode;
PPLUGIN_NODE existingNode;
if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
{
if (selectedNode->State == PLUGIN_STATE_LOCAL)
{
if (existingNode = FindTreeNode(
&context->TreeContext,
PLUGIN_STATE_REMOTE,
selectedNode->InternalName
))
{
if (StartInitialCheck(hwndDlg, existingNode, PLUGIN_ACTION_INSTALL))
{
}
}
UpdateTreeView(context);
}
else
{
if (StartInitialCheck(hwndDlg, selectedNode, PLUGIN_ACTION_INSTALL))
{
}
if (existingNode = FindTreeNode(
&context->TreeContext,
PLUGIN_STATE_REMOTE,
selectedNode->InternalName
))
{
selectedNode->State = PLUGIN_STATE_RESTART;
}
UpdateTreeView(context);
}
}
}
}
break;
case ID_MENU_UNINSTALL:
{
if (!PhGetOwnTokenAttributes().Elevated)
{
//SHELLEXECUTEINFO info = { sizeof(SHELLEXECUTEINFO) };
//info.lpFile = L"ProcessHacker.exe";
//info.lpParameters = L"-plugin dmex.ExtraPlugins:UNINSTALL -plugin dmex.ExtraPlugins:hex64value";
//info.lpVerb = L"runas";
//info.nShow = SW_SHOW;
//info.hwnd = hwndDlg;
//
//if (!ShellExecuteEx(&info))
//{
//
//}
}
else
{
PPLUGIN_NODE selectedNode;
PPLUGIN_NODE existingNode;
if (selectedNode = WeGetSelectedWindowNode(&context->TreeContext))
{
if (StartInitialCheck(hwndDlg, selectedNode, PLUGIN_ACTION_UNINSTALL))
{
if (existingNode = FindTreeNode(
&context->TreeContext,
PLUGIN_STATE_LOCAL,
selectedNode->InternalName
))
{
existingNode->State = PLUGIN_STATE_RESTART;
}
}
UpdateTreeView(context);
}
}
}
break;
case ID_MENU_DISABLE:
{
PPH_STRING baseName;
baseName = PhGetBaseName(selectedNode->FileName);
PhSetPluginDisabled(&baseName->sr, TRUE);
selectedNode->State = PLUGIN_STATE_RESTART;
UpdateTreeView(context);
SetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED),
PhGetString(PhaFormatString(L"Disabled Plugins (%lu)", PhDisabledPluginsCount()))
);
}
break;
case ID_MENU_PROPERTIES:
{
if (selectedNode->PluginInstance)
{
PhInvokeCallback(PhGetPluginCallback((PPH_PLUGIN)selectedNode->PluginInstance, PluginCallbackShowOptions), hwndDlg);
}
}
break;
}
}
}
break;
case IDCANCEL:
case IDOK:
{
//ShowUpdateDialog(hwndDlg, PLUGIN_ACTION_RESTART);
DestroyWindow(hwndDlg);
}
break;
case IDC_DISABLED:
{
ShowDisabledPluginsDialog(hwndDlg);
SetWindowText(GetDlgItem(hwndDlg, IDC_DISABLED),
PhGetString(PhaFormatString(L"Disabled Plugins (%lu)", PhDisabledPluginsCount()))
);
}
break;
}
}
break;
case WM_NOTIFY:
{
LPNMHDR header = (LPNMHDR)lParam;
switch (header->code)
{
case NM_CUSTOMDRAW:
{
if (header->idFrom == IDC_INSTALLED || header->idFrom == IDC_BROWSE || header->idFrom == IDC_UPDATES)
{
SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, DrawButton(context, lParam));
return TRUE;
}
}
break;
}
}
break;
case ID_UPDATE_ADD:
{
PPLUGIN_NODE entry = (PPLUGIN_NODE)lParam;
CloudAddChildWindowNode(&context->TreeContext, entry);
UpdateTreeView(context);
}
break;
case ID_UPDATE_COUNT:
{
ULONG count = 0;
for (ULONG i = 0; i < context->TreeContext.NodeList->Count; i++)
{
PPLUGIN_NODE windowNode = context->TreeContext.NodeList->Items[i];
if (windowNode->State == PLUGIN_STATE_UPDATE)
{
count++;
}
}
SetWindowText(GetDlgItem(hwndDlg, IDC_UPDATES), PhGetString(PhaFormatString(L"Updates (%lu)", count)));
}
break;
}
return FALSE;
}
NTSTATUS CloudDialogThread(
_In_ PVOID Parameter
)
{
BOOL result;
MSG message;
HWND cloudDialogHandle;
PH_AUTO_POOL autoPool;
PhInitializeAutoPool(&autoPool);
cloudDialogHandle = CreateDialog(
PluginInstance->DllBase,
MAKEINTRESOURCE(IDD_PLUGINS_DIALOG),
NULL,
CloudPluginsDlgProc
);
if (IsIconic(cloudDialogHandle))
ShowWindow(cloudDialogHandle, SW_RESTORE);
else
ShowWindow(cloudDialogHandle, SW_SHOW);
SetForegroundWindow(cloudDialogHandle);
while (result = GetMessage(&message, NULL, 0, 0))
{
if (result == -1)
break;
if (!IsDialogMessage(cloudDialogHandle, &message))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
PhDrainAutoPool(&autoPool);
}
PhDeleteAutoPool(&autoPool);
return STATUS_SUCCESS;
}
VOID ShowPluginManagerDialog(
VOID
)
{
HANDLE threadHandle;
if (threadHandle = PhCreateThread(0, CloudDialogThread, NULL))
{
NtClose(threadHandle);
}
}