Merge pull request #6525 from nextcloud/feature/msi-customaction-close-shellextensions
Allow installation to close shell extension DLLs via the custom action. Disable reboot prompt in case of the version with this change was previously already installed.
This commit is contained in:
commit
a5b65d1cb3
|
@ -23,8 +23,13 @@ set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
|||
|
||||
include(${CMAKE_SOURCE_DIR}/NEXTCLOUD.cmake)
|
||||
|
||||
set ( NCCONTEXTMENU_SHELLEXT_WINDOW_CLASS_NAME "${APPLICATION_SHORTNAME}_Shxt_CntMenuHndlr_WndClass" )
|
||||
set ( NCOVERLAYS_SHELLEXT_WINDOW_CLASS_NAME "${APPLICATION_SHORTNAME}_Shxt_Ovs_WndClass" )
|
||||
|
||||
# CfAPI Shell Extensions
|
||||
set( CFAPI_SHELL_EXTENSIONS_LIB_NAME CfApiShellExtensions )
|
||||
|
||||
set ( CFAPI_SHELLEXT_WINDOW_CLASS_NAME "${APPLICATION_SHORTNAME}_${CFAPI_SHELL_EXTENSIONS_LIB_NAME}_WndClass" )
|
||||
|
||||
set( CFAPI_SHELLEXT_APPID_REG "{E314A650-DCA4-416E-974E-18EA37C213EA}")
|
||||
set( CFAPI_SHELLEXT_APPID_DISPLAY_NAME "${APPLICATION_NAME} CfApi Shell Extensions" )
|
||||
|
|
|
@ -60,18 +60,33 @@
|
|||
<FileSearch Id="LegacyUninstallFileName" Name="Uninstall.exe"/>
|
||||
</RegistrySearch>
|
||||
</Property>
|
||||
|
||||
<Property Id="IS_PREV_VERSION_SHELL_EXT_CLOSE_SUPPORTED">
|
||||
<RegistrySearch Id="RegIsPrevVersionShellExtCloseSupported" Type="raw" Root="HKLM" Key="Software\$(var.AppVendor)\$(var.AppName)" Name="isShellExtCloseSupported" Win64="$(var.PlatformWin64)" />
|
||||
</Property>
|
||||
|
||||
<!-- Property to disable update checks -->
|
||||
<Property Id="SKIPAUTOUPDATE" Value="0" />
|
||||
|
||||
<!-- Quit / restart application -->
|
||||
<util:RestartResource ProcessName="$(var.AppExe)" />
|
||||
|
||||
<Property Id="WNDCLASSNAMETOCLOSE" Value="$(var.CfApiShellextWndClassName)" />
|
||||
<CustomAction Id="SetCfApiWindowClassName" Property="WNDCLASSNAMETOCLOSE" Value="$(var.CfApiShellextWndClassName)" />
|
||||
<CustomAction Id="SetNCContextMenuWindowClassName" Property="WNDCLASSNAMETOCLOSE" Value="$(var.NCContextMenuShellextWndClassName)" />
|
||||
<CustomAction Id="SetNCOverlaysWindowClassName" Property="WNDCLASSNAMETOCLOSE" Value="$(var.NCOverlaysShellextWndClassName)" />
|
||||
|
||||
<!-- Helper DLL Custom Actions -->
|
||||
<!-- Helper DLL Custom Actions -->
|
||||
<SetProperty Id="ExecNsisUninstaller" Value=""$(var.AppShortName)" "[NSIS_UNINSTALLEXE]"" Before="ExecNsisUninstaller" Sequence="execute" />
|
||||
<SetProperty Id="RemoveNavigationPaneEntries" Value=""$(var.AppName)"" Before="RemoveNavigationPaneEntries" Sequence="execute" />
|
||||
|
||||
<InstallExecuteSequence>
|
||||
<Custom Action="SetCfApiWindowClassName" Before="InstallValidate"/>
|
||||
<Custom Action="CloseCfApiShellExtension" After="SetCfApiWindowClassName" />
|
||||
<Custom Action="SetNCContextMenuWindowClassName" After="CloseCfApiShellExtension"/>
|
||||
<Custom Action="CloseNCContextMenuShellExtension" After="SetNCContextMenuWindowClassName" />
|
||||
<Custom Action="SetNCOverlaysWindowClassName" After="CloseNCContextMenuShellExtension"/>
|
||||
<Custom Action="CloseNCOverlaysShellExtension" After="SetNCOverlaysWindowClassName" />
|
||||
<!-- Install: Remove previous NSIS installation, if detected -->
|
||||
<Custom Action="ExecNsisUninstaller" Before="ProcessComponents">NSIS_UNINSTALLEXE AND NOT Installed</Custom>
|
||||
|
||||
|
@ -80,9 +95,8 @@
|
|||
|
||||
<!-- Uninstall: Cleanup the Registry -->
|
||||
<Custom Action="RegistryCleanupCustomAction" After="RemoveFiles">(NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")</Custom>
|
||||
|
||||
<!-- Schedule Reboot for the Shell Extensions (in silent installation mode only, or if SCHEDULE_REBOOT argument is set-->
|
||||
<ScheduleReboot After="InstallFinalize">(SCHEDULE_REBOOT=1) OR NOT (UILevel=2)</ScheduleReboot>
|
||||
<!-- Schedule Reboot for the Shell Extensions (only if SCHEDULE_REBOOT argument is set or if th version is less than 3.12.2 -->
|
||||
<ScheduleReboot After="InstallFinalize">(SCHEDULE_REBOOT=1) OR ((NOT (UILevel=2)) AND (NOT IS_PREV_VERSION_SHELL_EXT_CLOSE_SUPPORTED))</ScheduleReboot>
|
||||
</InstallExecuteSequence>
|
||||
|
||||
<!-- "Add or Remove" Programs Entries -->
|
||||
|
@ -184,6 +198,11 @@
|
|||
<RegistryValue Type="string" Name="InstallerProductCode" Value="[ProductCode]" />
|
||||
</RegistryKey>
|
||||
</Component>
|
||||
<Component Id="RegistryMiscInfo" Guid="*" Win64="$(var.PlatformWin64)">
|
||||
<RegistryKey Root="HKLM" Key="Software\$(var.AppVendor)\$(var.AppName)">
|
||||
<RegistryValue Type="integer" Name="isShellExtCloseSupported" Value="1" />
|
||||
</RegistryKey>
|
||||
</Component>
|
||||
|
||||
<!-- Platform bitness-dependent settings -->
|
||||
<Component Id="RegistryDefaultSettings" Guid="*" Win64="$(var.PlatformWin64)">
|
||||
|
@ -213,6 +232,7 @@
|
|||
<ComponentGroupRef Id="ClientFiles" />
|
||||
|
||||
<ComponentRef Id="RegistryVersionInfo" />
|
||||
<ComponentRef Id="RegistryMiscInfo" />
|
||||
<ComponentRef Id="RegistryDefaultSettings" />
|
||||
<ComponentRef Id="RegistryUriHandler" />
|
||||
|
||||
|
|
|
@ -31,6 +31,10 @@
|
|||
|
||||
<?define AppCommandOpenUrlScheme = "@APPLICATION_URI_HANDLER_SCHEME@" ?>
|
||||
|
||||
<?define CfApiShellextWndClassName = "@CFAPI_SHELLEXT_WINDOW_CLASS_NAME@" ?>
|
||||
<?define NCOverlaysShellextWndClassName = "@NCOVERLAYS_SHELLEXT_WINDOW_CLASS_NAME@" ?>
|
||||
<?define NCContextMenuShellextWndClassName = "@NCCONTEXTMENU_SHELLEXT_WINDOW_CLASS_NAME@" ?>
|
||||
|
||||
<!-- Custom license: To use it, also remove the "Skip the license page" stuff in the <UI> section
|
||||
and uncomment <WixVariable Id="WixUILicenseRtf"...
|
||||
<?define AppLicenseRtf = "path\License.rtf" ?>
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
*/
|
||||
|
||||
#include "NCMsiHelper.h"
|
||||
#include <MsiQuery.h>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* Sets up logging for MSIs and then calls the appropriate custom action with argc/argv parameters.
|
||||
|
@ -94,6 +96,71 @@ UINT __stdcall RemoveNavigationPaneEntries(MSIHANDLE hInstall)
|
|||
return CustomActionArgcArgv(hInstall, DoRemoveNavigationPaneEntries, "RemoveNavigationPaneEntries");
|
||||
}
|
||||
|
||||
UINT LogMsiInfoMessage(MSIHANDLE hInstall, const TCHAR *format, ...)
|
||||
{
|
||||
TCHAR szFormatted[MAX_PATH];
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vswprintf(szFormatted, MAX_PATH, format, args);
|
||||
va_end(args);
|
||||
|
||||
PMSIHANDLE hRecord = ::MsiCreateRecord(1);
|
||||
::MsiRecordSetString(hRecord, 0, szFormatted);
|
||||
|
||||
// we are always logging a message as info, as error will bring a popup that we don't want, just logs
|
||||
return MsiProcessMessage(hInstall, INSTALLMESSAGE_INFO, hRecord);
|
||||
}
|
||||
|
||||
UINT __stdcall CloseWindowByClassName(MSIHANDLE hInstall)
|
||||
{
|
||||
const auto windowClassPropertyName = _T("WNDCLASSNAMETOCLOSE");
|
||||
DWORD windowClassNameSize = 0;
|
||||
if (MsiGetProperty(hInstall, windowClassPropertyName, _T(""), &windowClassNameSize) != ERROR_MORE_DATA) {
|
||||
LogMsiInfoMessage(hInstall,
|
||||
_T("ERROR: Custom action CloseWindowByClassName. MsiGetProperty failed for windowClassPropertyName: %s"),
|
||||
windowClassPropertyName);
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
if (windowClassNameSize <= 0) {
|
||||
LogMsiInfoMessage(hInstall, _T("ERROR: Custom action CloseWindowByClassName. classNameSize is <= 0!"));
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
++windowClassNameSize;
|
||||
|
||||
std::vector<TCHAR> windowClassNameValue(windowClassNameSize, 0);
|
||||
std::vector<char> vec;
|
||||
const auto getPropertyRes = MsiGetProperty(hInstall, windowClassPropertyName, windowClassNameValue.data(), &windowClassNameSize);
|
||||
if (getPropertyRes != ERROR_SUCCESS) {
|
||||
LogMsiInfoMessage(hInstall, _T("ERROR: Custom action CloseWindowByClassName. MsiGetProperty failed for windowClassPropertyName: %s with code: %d"),
|
||||
windowClassNameValue.data(),
|
||||
getPropertyRes);
|
||||
return getPropertyRes;
|
||||
}
|
||||
|
||||
if (windowClassNameSize <= 0) {
|
||||
LogMsiInfoMessage(hInstall, _T("ERROR: Custom action CloseWindowByClassName. Final classNameSize is <= 0!"));
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
LogMsiInfoMessage(hInstall, _T("Custom action CloseWindowByClassName is running for windowClassNameValue: %s"), windowClassNameValue.data());
|
||||
|
||||
const auto windowToCloseHandle = FindWindow(windowClassNameValue.data(), NULL);
|
||||
if (windowToCloseHandle == NULL) {
|
||||
LogMsiInfoMessage(hInstall, _T("WARNING: Custom action CloseWindowByClassName. windowToCloseHandle is NULL."));
|
||||
// FindWindow will return NULL if the window is not currently running, so not an error
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
LogMsiInfoMessage(hInstall, _T("Custom action CloseWindowByClassName. Sending WM_CLOSE message to windowClassNameValue: %s"), windowClassNameValue.data());
|
||||
|
||||
SendMessage(windowToCloseHandle, WM_CLOSE, 0, 0);
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* DllMain - Initialize and cleanup WiX custom action utils.
|
||||
*/
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
EXPORTS
|
||||
CloseWindowByClassName
|
||||
ExecNsisUninstaller
|
||||
RemoveNavigationPaneEntries
|
||||
|
|
|
@ -39,5 +39,24 @@
|
|||
Execute="deferred"
|
||||
Impersonate="yes" />
|
||||
|
||||
<CustomAction Id="CloseCfApiShellExtension"
|
||||
Return="ignore"
|
||||
BinaryKey="NCMsiHelper"
|
||||
DllEntry="CloseWindowByClassName"
|
||||
Execute="immediate"
|
||||
Impersonate="yes" />
|
||||
<CustomAction Id="CloseNCContextMenuShellExtension"
|
||||
Return="ignore"
|
||||
BinaryKey="NCMsiHelper"
|
||||
DllEntry="CloseWindowByClassName"
|
||||
Execute="immediate"
|
||||
Impersonate="yes" />
|
||||
<CustomAction Id="CloseNCOverlaysShellExtension"
|
||||
Return="ignore"
|
||||
BinaryKey="NCMsiHelper"
|
||||
DllEntry="CloseWindowByClassName"
|
||||
Execute="immediate"
|
||||
Impersonate="yes" />
|
||||
|
||||
</Fragment>
|
||||
</Wix>
|
||||
|
|
|
@ -48,6 +48,10 @@
|
|||
|
||||
#cmakedefine BUILD_UPDATER "@BUILD_UPDATER@"
|
||||
|
||||
#cmakedefine CFAPI_SHELLEXT_WINDOW_CLASS_NAME "@CFAPI_SHELLEXT_WINDOW_CLASS_NAME@"
|
||||
#cmakedefine NCCONTEXTMENU_SHELLEXT_WINDOW_CLASS_NAME "@NCCONTEXTMENU_SHELLEXT_WINDOW_CLASS_NAME@"
|
||||
#cmakedefine NCOVERLAYS_SHELLEXT_WINDOW_CLASS_NAME "@NCOVERLAYS_SHELLEXT_WINDOW_CLASS_NAME@"
|
||||
|
||||
#cmakedefine CFAPI_SHELLEXT_APPID_REG "@CFAPI_SHELLEXT_APPID_REG@"
|
||||
#cmakedefine CFAPI_SHELLEXT_APPID_DISPLAY_NAME "@CFAPI_SHELLEXT_APPID_DISPLAY_NAME@"
|
||||
|
||||
|
|
|
@ -21,6 +21,11 @@
|
|||
HINSTANCE g_hInst = nullptr;
|
||||
long g_cDllRef = 0;
|
||||
|
||||
HWND hHiddenWnd = nullptr;
|
||||
DWORD WINAPI MessageLoopThread(LPVOID lpParameter);
|
||||
LRESULT CALLBACK HiddenWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
void CreateHiddenWindowAndLaunchMessageLoop();
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
|
||||
{
|
||||
switch (dwReason)
|
||||
|
@ -30,6 +35,7 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
|
|||
// path of the DLL to register the component.
|
||||
g_hInst = hModule;
|
||||
DisableThreadLibraryCalls(hModule);
|
||||
CreateHiddenWindowAndLaunchMessageLoop();
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
|
@ -122,3 +128,63 @@ STDAPI DllUnregisterServer(void)
|
|||
|
||||
return hr;
|
||||
}
|
||||
|
||||
void CreateHiddenWindowAndLaunchMessageLoop()
|
||||
{
|
||||
const WNDCLASSEX hiddenWindowClass{sizeof(WNDCLASSEX),
|
||||
CS_CLASSDC,
|
||||
HiddenWndProc,
|
||||
0L,
|
||||
0L,
|
||||
GetModuleHandle(NULL),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NCCONTEXTMENU_SHELLEXT_WINDOW_CLASS_NAME,
|
||||
NULL};
|
||||
|
||||
RegisterClassEx(&hiddenWindowClass);
|
||||
|
||||
hHiddenWnd = CreateWindow(hiddenWindowClass.lpszClassName,
|
||||
L"",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
NULL,
|
||||
NULL,
|
||||
hiddenWindowClass.hInstance,
|
||||
NULL);
|
||||
|
||||
ShowWindow(hHiddenWnd, SW_HIDE);
|
||||
UpdateWindow(hHiddenWnd);
|
||||
|
||||
const auto hMessageLoopThread = CreateThread(NULL, 0, MessageLoopThread, NULL, 0, NULL);
|
||||
if (hMessageLoopThread) {
|
||||
CloseHandle(hMessageLoopThread);
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WINAPI MessageLoopThread(LPVOID lpParameter)
|
||||
{
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, NULL, 0, 0)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK HiddenWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (msg) {
|
||||
case WM_CLOSE:
|
||||
FreeLibrary(g_hInst);
|
||||
break;
|
||||
default:
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,11 @@ HINSTANCE instanceHandle = nullptr;
|
|||
|
||||
long dllReferenceCount = 0;
|
||||
|
||||
HWND hHiddenWnd = nullptr;
|
||||
DWORD WINAPI MessageLoopThread(LPVOID lpParameter);
|
||||
LRESULT CALLBACK HiddenWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
void CreateHiddenWindowAndLaunchMessageLoop();
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
|
||||
{
|
||||
switch (dwReason)
|
||||
|
@ -27,6 +32,7 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved)
|
|||
case DLL_PROCESS_ATTACH:
|
||||
instanceHandle = hModule;
|
||||
DisableThreadLibraryCalls(hModule);
|
||||
CreateHiddenWindowAndLaunchMessageLoop();
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
|
@ -175,3 +181,63 @@ STDAPI DllUnregisterServer(void)
|
|||
|
||||
return hResult;
|
||||
}
|
||||
|
||||
void CreateHiddenWindowAndLaunchMessageLoop()
|
||||
{
|
||||
const WNDCLASSEX hiddenWindowClass{sizeof(WNDCLASSEX),
|
||||
CS_CLASSDC,
|
||||
HiddenWndProc,
|
||||
0L,
|
||||
0L,
|
||||
GetModuleHandle(NULL),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NCOVERLAYS_SHELLEXT_WINDOW_CLASS_NAME,
|
||||
NULL};
|
||||
|
||||
RegisterClassEx(&hiddenWindowClass);
|
||||
|
||||
hHiddenWnd = CreateWindow(hiddenWindowClass.lpszClassName,
|
||||
L"",
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
NULL,
|
||||
NULL,
|
||||
hiddenWindowClass.hInstance,
|
||||
NULL);
|
||||
|
||||
ShowWindow(hHiddenWnd, SW_HIDE);
|
||||
UpdateWindow(hHiddenWnd);
|
||||
|
||||
const auto hMessageLoopThread = CreateThread(NULL, 0, MessageLoopThread, NULL, 0, NULL);
|
||||
if (hMessageLoopThread) {
|
||||
CloseHandle(hMessageLoopThread);
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WINAPI MessageLoopThread(LPVOID lpParameter)
|
||||
{
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, NULL, 0, 0)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK HiddenWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (msg) {
|
||||
case WM_CLOSE:
|
||||
FreeLibrary(instanceHandle);
|
||||
break;
|
||||
default:
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
#define OVERLAY_GUID_SYNC L"@WIN_SHELLEXT_OVERLAY_GUID_SYNC@"
|
||||
#define OVERLAY_GUID_WARNING L"@WIN_SHELLEXT_OVERLAY_GUID_WARNING@"
|
||||
|
||||
#cmakedefine NCCONTEXTMENU_SHELLEXT_WINDOW_CLASS_NAME L"@NCCONTEXTMENU_SHELLEXT_WINDOW_CLASS_NAME@"
|
||||
#cmakedefine NCOVERLAYS_SHELLEXT_WINDOW_CLASS_NAME L"@NCOVERLAYS_SHELLEXT_WINDOW_CLASS_NAME@"
|
||||
|
||||
//
|
||||
// Preceding spaces are intended, two spaces to put us ahead of the competition :/
|
||||
//
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "customstateprovider.h"
|
||||
#include "thumbnailprovider.h"
|
||||
#include <comdef.h>
|
||||
#include <tchar.h>
|
||||
|
||||
long dllReferenceCount = 0;
|
||||
long dllObjectsCount = 0;
|
||||
|
@ -25,6 +26,11 @@ HINSTANCE instanceHandle = nullptr;
|
|||
HRESULT CustomStateProvider_CreateInstance(REFIID riid, void **ppv);
|
||||
HRESULT ThumbnailProvider_CreateInstance(REFIID riid, void **ppv);
|
||||
|
||||
HWND hHiddenWnd = nullptr;
|
||||
DWORD WINAPI MessageLoopThread(LPVOID lpParameter);
|
||||
LRESULT CALLBACK HiddenWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
void CreateHiddenWindowAndLaunchMessageLoop();
|
||||
|
||||
const VfsShellExtensions::ClassObjectInit listClassesSupported[] = {
|
||||
{&__uuidof(winrt::CfApiShellExtensions::implementation::CustomStateProvider), CustomStateProvider_CreateInstance},
|
||||
{&__uuidof(VfsShellExtensions::ThumbnailProvider), ThumbnailProvider_CreateInstance}
|
||||
|
@ -38,6 +44,8 @@ STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, void *)
|
|||
::GetModuleFileName(instanceHandle, dllFilePath, _MAX_PATH);
|
||||
winrt::CfApiShellExtensions::implementation::CustomStateProvider::setDllFilePath(dllFilePath);
|
||||
DisableThreadLibraryCalls(hInstance);
|
||||
|
||||
CreateHiddenWindowAndLaunchMessageLoop();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
@ -73,3 +81,66 @@ HRESULT ThumbnailProvider_CreateInstance(REFIID riid, void **ppv)
|
|||
thumbnailProvider->Release();
|
||||
return hresult;
|
||||
}
|
||||
|
||||
void CreateHiddenWindowAndLaunchMessageLoop()
|
||||
{
|
||||
const WNDCLASSEX hiddenWindowClass {
|
||||
sizeof(WNDCLASSEX),
|
||||
CS_CLASSDC,
|
||||
HiddenWndProc,
|
||||
0L,
|
||||
0L,
|
||||
GetModuleHandle(NULL),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
_T(CFAPI_SHELLEXT_WINDOW_CLASS_NAME),
|
||||
NULL
|
||||
};
|
||||
|
||||
RegisterClassEx(&hiddenWindowClass);
|
||||
|
||||
hHiddenWnd = CreateWindow(
|
||||
hiddenWindowClass.lpszClassName,
|
||||
_T(""),
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
NULL,
|
||||
NULL,
|
||||
hiddenWindowClass.hInstance,
|
||||
NULL);
|
||||
|
||||
ShowWindow(hHiddenWnd, SW_HIDE);
|
||||
UpdateWindow(hHiddenWnd);
|
||||
|
||||
const auto hMessageLoopThread = CreateThread(NULL, 0, MessageLoopThread, NULL, 0, NULL);
|
||||
if (hMessageLoopThread) {
|
||||
CloseHandle(hMessageLoopThread);
|
||||
}
|
||||
}
|
||||
|
||||
DWORD WINAPI MessageLoopThread(LPVOID lpParameter)
|
||||
{
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, NULL, 0, 0)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LRESULT CALLBACK HiddenWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (msg) {
|
||||
case WM_CLOSE:
|
||||
FreeLibrary(instanceHandle);
|
||||
break;
|
||||
default:
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue