Compare commits
1 Commits
main
...
procedures
Author | SHA1 | Date | |
---|---|---|---|
|
5b4d4b17ed |
200
gdk/win32/apisets.c
Normal file
200
gdk/win32/apisets.c
Normal file
@@ -0,0 +1,200 @@
|
||||
/* GTK - The GIMP Toolkit
|
||||
*
|
||||
* Copyright (C) 2024 the GTK team
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "apisets.h"
|
||||
#include "procedures.h"
|
||||
|
||||
#include "gdkprivate-win32.h"
|
||||
|
||||
static struct api_set
|
||||
api_sets[] = {
|
||||
# define API_SET(id, name, os_version) \
|
||||
{name, L"" name, NULL, FALSE, os_version},
|
||||
API_SETS
|
||||
# undef API_SET
|
||||
};
|
||||
|
||||
static struct module
|
||||
modules[] = {
|
||||
# define MODULE(id, name, folder, flags) \
|
||||
{L"" name, folder, flags, NULL, FALSE},
|
||||
MODULES
|
||||
# undef MODULE
|
||||
};
|
||||
|
||||
static void
|
||||
load_api_set_internal (struct api_set *api_set)
|
||||
{
|
||||
if (ptrIsApiSetImplemented)
|
||||
if (!ptrIsApiSetImplemented (api_set->name_narrow))
|
||||
return;
|
||||
|
||||
/* Use LOAD_LIBRARY_SEARCH_SYSTEM32 for security (if IsApiSetImplemented
|
||||
* is not present)
|
||||
*
|
||||
* If we can't use IsApiSetImplemented, we should try loading the api set
|
||||
* directly. However, if the OS is too old and the api set is unknown,
|
||||
* LoadLibrary will look for a corresponding DLL file in the search paths.
|
||||
* Here we use LOAD_LIBRARY_SEARCH_SYSTEM32 to restrict search to a safe
|
||||
* folder. */
|
||||
const DWORD flags = LOAD_LIBRARY_SEARCH_SYSTEM32;
|
||||
|
||||
api_set->module_handle = LoadLibraryEx (api_set->name_wide, NULL, flags);
|
||||
if (api_set->module_handle == NULL)
|
||||
{
|
||||
DWORD code = GetLastError ();
|
||||
if (code == ERROR_INVALID_PARAMETER)
|
||||
{
|
||||
/* LOAD_LIBRARY_SEARCH_SYSTEM32 is supported on Windows Vista/7
|
||||
* only with update KB2533623 installed. If we can't use that
|
||||
* flag, it's best to return early and rely on classic modules.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
else if (code == ERROR_MOD_NOT_FOUND)
|
||||
{
|
||||
if (strncmp (api_set->name_narrow, "api", strlen ("api")) == 0 &&
|
||||
api_set->os_version <= gdk_win32_get_os_version ())
|
||||
{
|
||||
g_message ("%s missing\n", api_set->name_narrow);
|
||||
}
|
||||
}
|
||||
else
|
||||
WIN32_API_FAILED ("LoadLibraryEx");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
load_api_set (struct api_set *api_set)
|
||||
{
|
||||
g_assert (api_set->module_handle == NULL);
|
||||
g_assert (api_set->checked == false);
|
||||
|
||||
load_api_set_internal (api_set);
|
||||
|
||||
api_set->checked = true;
|
||||
}
|
||||
|
||||
/** gdk_win32_get_api_set:
|
||||
*/
|
||||
struct api_set *
|
||||
gdk_win32_get_api_set (int api_set_id)
|
||||
{
|
||||
if (api_set_id >= 0 && api_set_id < API_SET_COUNT)
|
||||
{
|
||||
struct api_set *api_set = &api_sets[api_set_id];
|
||||
|
||||
if (!api_set->checked)
|
||||
load_api_set (api_set);
|
||||
|
||||
return api_set;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
load_module_internal (struct module *module)
|
||||
{
|
||||
if (module->flags & MODULE_FLAG_PACKAGED)
|
||||
{
|
||||
if (gdk_win32_check_app_packaged ())
|
||||
{
|
||||
g_assert (ptrLoadPackagedLibrary);
|
||||
|
||||
module->module_handle = ptrLoadPackagedLibrary (module->name, 0);
|
||||
if (module->module_handle == NULL)
|
||||
{
|
||||
if (GetLastError () != ERROR_MOD_NOT_FOUND)
|
||||
WIN32_API_FAILED ("LoadPackagedLibrary");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO
|
||||
*
|
||||
* https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/framework-packages/use-the-dynamic-dependency-api
|
||||
* https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/use-windows-app-sdk-run-time
|
||||
* https://github.com/microsoft/WindowsAppSDK/issues/89
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
switch (module->folder)
|
||||
{
|
||||
case FOLDER_APP:
|
||||
if (can_use_app_folder < 0)
|
||||
can_use_app_folder = check_can_use_app_folder ();
|
||||
|
||||
if (can_use_app_folder)
|
||||
{
|
||||
module->module_handle = gdk_win32_load_library_from_app_folder (module->name);
|
||||
break;
|
||||
}
|
||||
|
||||
G_GNUC_FALLTHROUGH;
|
||||
case FOLDER_SYSTEM32:
|
||||
module->module_handle = gdk_win32_load_library_from_system32 (module->name, false, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
load_module (struct module *module)
|
||||
{
|
||||
g_assert (module->module_handle == NULL);
|
||||
g_assert (module->checked == false);
|
||||
|
||||
load_module_internal (module);
|
||||
|
||||
module->checked = true;
|
||||
}
|
||||
|
||||
/** gdk_win32_get_module:
|
||||
*/
|
||||
struct module *
|
||||
gdk_win32_get_module (int module_id)
|
||||
{
|
||||
if (module_id >= 0 && module_id < MODULE_COUNT)
|
||||
{
|
||||
struct module *module = &modules[module_id];
|
||||
|
||||
if (!module->checked)
|
||||
load_module (module);
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// Check Dynamic-link library redirection
|
||||
// Check .exe.manifest file alongside exe
|
||||
// PSP_USEFUSIONCONTEXT
|
||||
// Manifest and WinRT activatable classes?
|
||||
// Check if GetProcAddress works in app container (do we really need a delay-load table for it to work?)
|
||||
// (can it really access the calling module?)
|
||||
|
||||
// Is it possible to generate a synthetic delay-load import table?
|
||||
|
||||
|
87
gdk/win32/apisets.h
Normal file
87
gdk/win32/apisets.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/* GTK - The GIMP Toolkit
|
||||
*
|
||||
* Copyright (C) 2024 the GTK team
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gdkmain-win32.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#undef API_SET
|
||||
|
||||
/* Api sets whose name starts with 'api' can be removed from the table once
|
||||
* support for older OSes is dropped. On the countrary, api sets whose name
|
||||
* starts with 'ext' must remain in the table because they are not present
|
||||
* on all Windows editions (Core, Desktop, Hololens, etc.) */
|
||||
|
||||
#define API_SETS \
|
||||
API_SET (API_CORE_APIQUERY_2 , "api-ms-win-core-apiquery-l2-1-0.dll" , OSVersionWindows10) \
|
||||
API_SET (API_APPMODEL_RUNTIME_1 , "api-ms-win-appmodel-runtime-l1-1-0.dll" , OSVersionWindows8 ) \
|
||||
API_SET (API_CORE_LIBRARYLOADER_2 , "api-ms-win-core-libraryloader-l2-1-0.dll" , OSVersionWindows8 ) \
|
||||
|
||||
#define MODULES \
|
||||
MODULE (MODULE_KERNEL32 , "kernel32.dll" , FOLDER_SYSTEM32 , 0 ) \
|
||||
MODULE (MODULE_USER32 , "user32.dll" , FOLDER_SYSTEM32 , 0 ) \
|
||||
|
||||
#define API_SET(id, name, os_version) id,
|
||||
enum ApiSets {
|
||||
API_SETS
|
||||
|
||||
API_SET_COUNT
|
||||
};
|
||||
#undef API_SET
|
||||
|
||||
#define MODULE(id, name, folder, flags) id,
|
||||
enum Modules {
|
||||
MODULES
|
||||
|
||||
MODULE_COUNT
|
||||
};
|
||||
#undef MODULE
|
||||
|
||||
struct api_set {
|
||||
const char *name_narrow;
|
||||
const wchar_t *name_wide;
|
||||
HMODULE module_handle;
|
||||
bool checked;
|
||||
OSVersion os_version;
|
||||
};
|
||||
|
||||
struct api_set * gdk_win32_get_api_set (int api_set_id);
|
||||
|
||||
enum ModuleFolder {
|
||||
FOLDER_SYSTEM32,
|
||||
FOLDER_APP,
|
||||
};
|
||||
|
||||
#define MODULE_FLAG_PACKAGED (1U << 0)
|
||||
#define MODULE_FLAG_DIRECT (1U << 1)
|
||||
|
||||
struct module {
|
||||
const wchar_t *name;
|
||||
enum ModuleFolder folder;
|
||||
unsigned int flags;
|
||||
HMODULE module_handle;
|
||||
bool checked;
|
||||
};
|
||||
|
||||
struct module * gdk_win32_get_module (int module_id);
|
||||
|
@@ -38,6 +38,14 @@
|
||||
#include <windows.h>
|
||||
#include <wintab.h>
|
||||
#include <imm.h>
|
||||
#include <shlwapi.h> /* for DLLVERSIONINFO */
|
||||
|
||||
#include "gdkmain-win32.h"
|
||||
#include "apisets.h"
|
||||
#include "procedures.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Whether GDK initialized COM */
|
||||
gboolean
|
||||
@@ -106,6 +114,361 @@ gdk_win32_ensure_ole (void)
|
||||
return ole_initialized;
|
||||
}
|
||||
|
||||
struct invoke_context
|
||||
{
|
||||
void (*callback)(void*);
|
||||
void *user_data;
|
||||
va_list setup_funcs;
|
||||
};
|
||||
|
||||
typedef void (*invoke_setup_func_t)(invoke_context_t *);
|
||||
|
||||
#define gdk_win32_invoke_finish(context) \
|
||||
do \
|
||||
{ \
|
||||
context->callback (context->user_data); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define gdk_win32_invoke_continue(context) \
|
||||
do \
|
||||
{ \
|
||||
invoke_setup_func_t setup_func; \
|
||||
setup_func = va_arg ((context)->setup_funcs, invoke_setup_func_t); \
|
||||
\
|
||||
if (setup_func) \
|
||||
setup_func (context); \
|
||||
else \
|
||||
gdk_win32_invoke_finish (context); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/** gdk_win32_invoke_callback:
|
||||
*
|
||||
* Invoke a callback with a list of prepare functions setting
|
||||
* up the necessary context (thread error mode, activation context,
|
||||
* etc.)
|
||||
*
|
||||
* Prepare functions are called recursively, so it's possible to
|
||||
* use Structured Exception Handling for cleanup even in case of
|
||||
* exceptions.
|
||||
*
|
||||
* Note: diverging control-flow via signals / structured exceptions
|
||||
* is not supported, but it's good practice to cleanup some things
|
||||
* on exceptions for the sake of error handling or debugging.
|
||||
*/
|
||||
void
|
||||
gdk_win32_invoke_callback (void (*callback)(void *arg),
|
||||
void *user_data,
|
||||
...)
|
||||
{
|
||||
invoke_context_t context = {
|
||||
callback, user_data,
|
||||
};
|
||||
|
||||
va_start (context.setup_funcs, user_data);
|
||||
gdk_win32_invoke_continue ((&context));
|
||||
va_end (context.setup_funcs);
|
||||
}
|
||||
|
||||
/** gdk_win32_with_loader_error_mode:
|
||||
*
|
||||
* Setup function that sets the error mode to prevent interactive
|
||||
* mesage boxes from the loader. To be used with gdk_win32_invoke_callback()
|
||||
*/
|
||||
void
|
||||
gdk_win32_with_loader_error_mode (invoke_context_t *context)
|
||||
{
|
||||
/* It seems that both SEM_NOOPENFILEERRORBOX, SEM_FAILCRITICALERRORS
|
||||
* are relevant for DLL loading:
|
||||
*
|
||||
* -> https://devblogs.microsoft.com/oldnewthing/20240208-00/?p=109374
|
||||
* -> https://www.betaarchive.com/wiki/index.php?title=Microsoft_KB_Archive/111610
|
||||
*
|
||||
* Now, it could be that SEM_NOOPENFILEERRORBOX is not needed anymore
|
||||
* on modern Windows, but let's just be safe */
|
||||
const DWORD error_mode = SEM_FAILCRITICALERRORS |
|
||||
SEM_NOOPENFILEERRORBOX;
|
||||
const DWORD original_error_mode = GetThreadErrorMode ();
|
||||
|
||||
SEH_TRY
|
||||
{
|
||||
SetThreadErrorMode (original_error_mode | error_mode, NULL);
|
||||
gdk_win32_invoke_continue (context);
|
||||
}
|
||||
SEH_FINALLY
|
||||
{
|
||||
SetThreadErrorMode (original_error_mode, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static HANDLE
|
||||
get_activation_context (void)
|
||||
{
|
||||
static HANDLE handle_activation_context = NULL;
|
||||
static GOnce once = G_ONCE_INIT;
|
||||
|
||||
if (g_once_init_enter (&handle_activation_context))
|
||||
{
|
||||
ACTCTX actctx = {0};
|
||||
HANDLE handle;
|
||||
|
||||
actctx.cbSize = sizeof (actctx);
|
||||
actctx.dwFlags = ACTCTX_FLAG_HMODULE_VALID |
|
||||
ACTCTX_FLAG_RESOURCE_NAME_VALID;
|
||||
actctx.hModule = this_module_handle ();
|
||||
#ifndef GTK_STATIC_COMPILATION
|
||||
actctx.lpResourceName = MAKEINTRESOURCE (ISOLATIONAWARE_MANIFEST_RESOURCE_ID);
|
||||
#else
|
||||
actctx.lpResourceName = L"GTK_MANIFEST";
|
||||
#endif
|
||||
|
||||
handle = CreateActCtx (&actctx);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
WIN32_API_FAILED ("CreateActCtx");
|
||||
|
||||
g_once_init_leave (&handle_activation_context, handle);
|
||||
}
|
||||
|
||||
return handle_activation_context;
|
||||
}
|
||||
|
||||
/** gdk_win32_with_activation_context:
|
||||
*
|
||||
* Setup function that activates the GTK's activation context.
|
||||
* To be used with gdk_win32_invoke_callback().
|
||||
*/
|
||||
void
|
||||
gdk_win32_with_activation_context (invoke_context_t *context)
|
||||
{
|
||||
HANDLE handle_activation_context = get_activation_context ();
|
||||
bool activated = false;
|
||||
ULONG_PTR cookie = 0;
|
||||
|
||||
SEH_TRY
|
||||
{
|
||||
if (handle_activation_context != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (!ActivateActCtx (handle_activation_context, &cookie))
|
||||
WIN32_API_FAILED ("ActivateActCtx");
|
||||
else
|
||||
activated = true;
|
||||
}
|
||||
|
||||
gdk_win32_invoke_continue (context);
|
||||
}
|
||||
SEH_FINALLY
|
||||
{
|
||||
if (activated)
|
||||
{
|
||||
if (!DeactivateActCtx (0, cookie) &&
|
||||
!SEH_ABNORMAL_TERMINATION ())
|
||||
{
|
||||
WIN32_API_FAILED ("DeactivateActCtx");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ISOLATION_AWARE_ENABLED
|
||||
# ifndef GTK_STATIC_COMPILATION
|
||||
# warning "Defining ISOLATION_AWARE_ENABLED is not needed. GTK implements manual activation context handling"
|
||||
# else
|
||||
# error "Cannot build with ISOLATION_AWARE_ENABLED. GTK implements manual activation context handling"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* TODO:
|
||||
*
|
||||
* Free the activation context on unload (ReleaseActCtx)
|
||||
* Perhaps call ZombifyActCtx if a debugger is present
|
||||
*/
|
||||
|
||||
|
||||
/** gdk_win32_check_app_packaged:
|
||||
*
|
||||
* Check if the app is running with identity.
|
||||
*
|
||||
* Even if the app was installed with package identity (e.g with MSIX),
|
||||
* example using MSIX), it may still be launched in classic mode;
|
||||
* that happens, for example, if the user launches the exe from
|
||||
* File Explorer, or when using CreateProcess(). Package identity
|
||||
* is applied only when the app is activated by the system.
|
||||
*/
|
||||
bool
|
||||
gdk_win32_check_app_packaged (void)
|
||||
{
|
||||
UINT32 wchars_count = 0;
|
||||
LONG code;
|
||||
|
||||
if (!ptrGetCurrentPackageFullName)
|
||||
return false;
|
||||
|
||||
code = ptrGetCurrentPackageFullName (&wchars_count, NULL);
|
||||
if (code == APPMODEL_ERROR_NO_PACKAGE)
|
||||
return false;
|
||||
else if (code == ERROR_SUCCESS || code == ERROR_INSUFFICIENT_BUFFER)
|
||||
return true;
|
||||
|
||||
WIN32_API_FAILED_WITH_CODE ("GetCurrentPackageFullName", code);
|
||||
return false;
|
||||
}
|
||||
|
||||
/** gdk_win32_check_app_container:
|
||||
*
|
||||
* Check if running sandboxed in an app container
|
||||
*/
|
||||
bool
|
||||
gdk_win32_check_app_container (void)
|
||||
{
|
||||
HANDLE token_handle = NULL;
|
||||
DWORD is_app_container = 0;
|
||||
DWORD size = sizeof (is_app_container);
|
||||
bool result = false;
|
||||
|
||||
/* Since Windows 8: use GetCurrentProcessToken() and remove the call to CloseHandle() */
|
||||
|
||||
if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &token_handle))
|
||||
{
|
||||
WIN32_API_FAILED ("OpenProcessToken");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetTokenInformation (token_handle, TokenIsAppContainer, &is_app_container, size, &size))
|
||||
WIN32_API_FAILED ("GetTokenInformation");
|
||||
else if (size > 0)
|
||||
result = !!is_app_container;
|
||||
|
||||
CloseHandle (token_handle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** gdk_win32_check_high_integrity:
|
||||
*
|
||||
* Check if the app is running with high integrity
|
||||
*
|
||||
* Code based on:
|
||||
* https://devblogs.microsoft.com/oldnewthing/20221017-00/?p=107291
|
||||
* https://github.com/microsoft/WindowsAppSDK/blob/main/dev/Common/Security.IntegrityLevel.h
|
||||
*/
|
||||
bool
|
||||
gdk_win32_check_high_integrity (void)
|
||||
{
|
||||
HANDLE token_handle = NULL;
|
||||
TOKEN_MANDATORY_LABEL integrity_level;
|
||||
DWORD size = sizeof (integrity_level);
|
||||
bool result = false;
|
||||
|
||||
/* Since Windows 8: use GetCurrentProcessToken() and remove the call to CloseHandle() */
|
||||
|
||||
if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &token_handle))
|
||||
{
|
||||
WIN32_API_FAILED ("OpenProcessToken");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset (&integrity_level, 0, sizeof (integrity_level));
|
||||
|
||||
if (!GetTokenInformation (token_handle, TokenIntegrityLevel, &integrity_level, size, &size))
|
||||
WIN32_API_FAILED ("GetTokenInformation");
|
||||
else if (size > 0 && IsValidSid (integrity_level.Label.Sid))
|
||||
{
|
||||
UCHAR subauthority_count = *GetSidSubAuthorityCount (integrity_level.Label.Sid);
|
||||
if (subauthority_count > 0)
|
||||
{
|
||||
DWORD level = *GetSidSubAuthority (integrity_level.Label.Sid, subauthority_count - 1);
|
||||
result = (level >= SECURITY_MANDATORY_HIGH_RID);
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle (token_handle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** gdk_win32_check_manually_elevated:
|
||||
*
|
||||
* Code based on:
|
||||
* https://devblogs.microsoft.com/oldnewthing/20241003-00/?p=110336
|
||||
*/
|
||||
bool
|
||||
gdk_win32_check_manually_elevated (void)
|
||||
{
|
||||
HANDLE token_handle = NULL;
|
||||
TOKEN_ELEVATION_TYPE elevation_type = TokenElevationTypeDefault;
|
||||
DWORD size = sizeof (elevation_type);
|
||||
bool result = false;
|
||||
|
||||
/* Since Windows 8: use GetCurrentProcessToken() and remove the call to CloseHandle() */
|
||||
|
||||
if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &token_handle))
|
||||
{
|
||||
WIN32_API_FAILED ("OpenProcessToken");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetTokenInformation (token_handle, TokenElevationType, &elevation_type, size, &size))
|
||||
WIN32_API_FAILED ("GetTokenInformation");
|
||||
else if (size > 0)
|
||||
result = (elevation_type == TokenElevationTypeFull);
|
||||
|
||||
CloseHandle (token_handle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
get_comctl32_version (HMODULE module_handle)
|
||||
{
|
||||
DLLGETVERSIONPROC ptrDllGetVersion;
|
||||
DLLVERSIONINFO info = {0};
|
||||
HRESULT hr = E_FAIL;
|
||||
int result = 0;
|
||||
|
||||
ptrDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress (module_handle, "DllGetVersion");
|
||||
if (!ptrDllGetVersion)
|
||||
return 1;
|
||||
|
||||
// TODO: Add support for DLLVERSIONINFO2
|
||||
// TODO: This won't work in app containers, probably
|
||||
|
||||
info.cbSize = sizeof (info);
|
||||
hr = ptrDllGetVersion (&info);
|
||||
if (FAILED (hr))
|
||||
HR_LOG ("DllGetVersion", hr);
|
||||
else
|
||||
result = ;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** gdk_win32_get_comctl32_version:
|
||||
*
|
||||
* Get the major version of the Common Controls library. Note that
|
||||
* common controls may not be present on modern Windows editions
|
||||
* (Core, Hololens, etc.)
|
||||
*
|
||||
* Returns: TODO
|
||||
*/
|
||||
int
|
||||
gdk_win32_get_comctl32_version (void)
|
||||
{
|
||||
struct api_set *api_set;
|
||||
struct module *module;
|
||||
HMODULE module_handle;
|
||||
|
||||
api_set *api_set = gdk_win32_get_api_set (API_SET_EXT_WIN_SHELL_INIT);
|
||||
if (api_set)
|
||||
return get_comctl32_version (api_set->module_handle);
|
||||
|
||||
module = gdk_win32_get_module (MODULE_COMCTL32);
|
||||
if (module)
|
||||
return get_comctl32_version (module->module_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
_gdk_win32_api_failed (const char *where,
|
||||
const char *api)
|
||||
|
42
gdk/win32/gdkmain-win32.h
Normal file
42
gdk/win32/gdkmain-win32.h
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum {
|
||||
OSVersionWindows7,
|
||||
OSVersionWindows8,
|
||||
OSVersionWindows8_1,
|
||||
OSVersionWindows10,
|
||||
OSVersionWindows11,
|
||||
} OSVersion;
|
||||
|
||||
OSVersion
|
||||
gdk_win32_get_os_version (void);
|
||||
|
||||
void
|
||||
gdk_win32_invoke_callback (void (*callback)(void *arg),
|
||||
void *user_data,
|
||||
...);
|
||||
|
||||
struct invoke_context;
|
||||
typedef struct invoke_context invoke_context_t;
|
||||
|
||||
void
|
||||
gdk_win32_with_loader_error_mode (invoke_context_t *context);
|
||||
|
||||
void
|
||||
gdk_win32_with_activation_context (invoke_context_t *context);
|
||||
|
||||
bool
|
||||
gdk_win32_check_app_packaged (void);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define SEH_TRY __try
|
||||
#define SEH_FINALLY __finally
|
||||
#define SEH_ABNORMAL_TERMINATION() AbnormalTermination()
|
||||
#else
|
||||
#define SEH_TRY
|
||||
#define SEH_FINALLY
|
||||
#define SEH_ABNORMAL_TERMINATION() FALSE
|
||||
#endif
|
@@ -288,4 +288,3 @@ this_module (void)
|
||||
{
|
||||
return (HMODULE) &__ImageBase;
|
||||
}
|
||||
|
||||
|
@@ -13,6 +13,7 @@ gdk_win32_public_sources = files([
|
||||
])
|
||||
|
||||
gdk_win32_sources = gdk_win32_public_sources + files([
|
||||
'apisets.c',
|
||||
'gdkcairocontext-win32.c',
|
||||
'gdkclipboard-win32.c',
|
||||
'gdkclipdrop-win32.c',
|
||||
@@ -34,6 +35,7 @@ gdk_win32_sources = gdk_win32_public_sources + files([
|
||||
'gdkwin32cursor.h',
|
||||
'gdkwin32display.h',
|
||||
'gdkwin32keys.h',
|
||||
'procedures.c',
|
||||
])
|
||||
|
||||
gdk_win32_public_headers = files([
|
||||
|
56
gdk/win32/procedures-delayload.c
Normal file
56
gdk/win32/procedures-delayload.c
Normal file
@@ -0,0 +1,56 @@
|
||||
/* GTK - The GIMP Toolkit
|
||||
* Copyright (C) 2024 the GTK team
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "procedures.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <apiquery2.h>
|
||||
#include <appmodel.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* This file is used to generate entries in the delayload import table for
|
||||
* all the optional procedures used by GTK. That's needed for appcontainer
|
||||
* environments, where procedures can be obtained dynamically only when
|
||||
* present in the delayload import table (even though we don't use delay
|
||||
* loading)
|
||||
*/
|
||||
|
||||
/* This file must be compiled with OneCoreUAP_apiset.lib
|
||||
* and WINAPI_PARTITION_DESKTOP
|
||||
*/
|
||||
|
||||
# if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
# error "Invalid WINAPI_FAMILY_PARTITION, WINAPI_PARTITION_DESKTOP is required"
|
||||
# endif
|
||||
|
||||
static void
|
||||
api_entries_dummy_ctor (void)
|
||||
{
|
||||
uintptr_t dummy_value =
|
||||
# define PROCEDURE(name, api_set_id, module_id, os_version) \
|
||||
((uintptr_t) (void*) name) +
|
||||
PROCEDURES
|
||||
# endif
|
||||
0;
|
||||
|
||||
EncodePointer ((void*)dummy_value);
|
||||
}
|
||||
|
||||
G_DEFINE_CONSTRUCTOR (api_entries_dummy_ctor)
|
||||
|
147
gdk/win32/procedures.c
Normal file
147
gdk/win32/procedures.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/* GTK - The GIMP Toolkit
|
||||
*
|
||||
* Copyright (C) 2024 the GTK team
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "apisets.h"
|
||||
#include "procedures.h"
|
||||
#include "gdkmain-win32.h"
|
||||
#include "gdkprivate-win32.h"
|
||||
|
||||
# define PROCEDURE(name, api_set_id, module_id, os_version) \
|
||||
name##_t ptr##name;
|
||||
PROCEDURES
|
||||
# undef PROCEDURE
|
||||
|
||||
struct procedure {
|
||||
void **pp;
|
||||
char *name;
|
||||
int api_set_id;
|
||||
int module_id;
|
||||
OSVersion os_version;
|
||||
};
|
||||
|
||||
static struct procedure
|
||||
procedures[] = {
|
||||
# define PROCEDURE(name, api_set_id, module_id, os_version) \
|
||||
{(void**)&ptr##name, #name, api_set_id, module_id, os_version},
|
||||
PROCEDURES
|
||||
# undef PROCEDURE
|
||||
};
|
||||
|
||||
static void *
|
||||
load_common (HMODULE module_handle,
|
||||
const char *type,
|
||||
const wchar_t *m_name,
|
||||
const char *name,
|
||||
OSVersion os_version)
|
||||
{
|
||||
void *proc = GetProcAddress (module_handle, name);
|
||||
if (proc == NULL)
|
||||
{
|
||||
DWORD code = GetLastError ();
|
||||
|
||||
if (code != ERROR_PROC_NOT_FOUND)
|
||||
WIN32_API_FAILED ("GetProcAddress");
|
||||
else if (gdk_win32_get_os_version () >= os_version)
|
||||
g_warning ("Could not find procedure %s in %s %S",
|
||||
name, type, m_name);
|
||||
}
|
||||
|
||||
return proc;
|
||||
}
|
||||
|
||||
static bool
|
||||
load_by_api_set (struct procedure *procedure)
|
||||
{
|
||||
struct api_set *api_set = gdk_win32_get_api_set (procedure->api_set_id);
|
||||
|
||||
if (api_set->module_handle)
|
||||
*procedure->pp = load_common (api_set->module_handle,
|
||||
"api set",
|
||||
api_set->name_wide,
|
||||
procedure->name,
|
||||
procedure->os_version);
|
||||
|
||||
return *procedure->pp != NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
load_by_module (struct procedure *procedure)
|
||||
{
|
||||
struct module *module = gdk_win32_get_module (procedure->module_id);
|
||||
|
||||
if (module->module_handle)
|
||||
*procedure->pp = load_common (module->module_handle,
|
||||
"module",
|
||||
module->name,
|
||||
procedure->name,
|
||||
procedure->os_version);
|
||||
|
||||
return *procedure->pp != NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
static void
|
||||
load_by_package (struct procedure *procedure)
|
||||
{
|
||||
struct package *package = gdk_win32_get_package (procedure->package_id);
|
||||
|
||||
if (package->module_handle)
|
||||
*procedure->pp = load_common (package->module_handle,
|
||||
"package",
|
||||
package->name,
|
||||
procedure->name,
|
||||
procedure->os_version);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void
|
||||
procedures_load_internal (void *)
|
||||
{
|
||||
for (size_t i = 0; i < G_N_ELEMENTS (procedures); i++)
|
||||
{
|
||||
struct procedure *procedure = &procedures[i];
|
||||
|
||||
g_assert (*procedure->pp == NULL);
|
||||
|
||||
load_by_api_set (procedure) ||
|
||||
load_by_module (procedure) ||
|
||||
load_by_package (procedure);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gdk_win32_procedures_load (void)
|
||||
{
|
||||
gdk_win32_invoke_callback (procedures_load_internal,
|
||||
NULL,
|
||||
gdk_win32_with_loader_error_mode,
|
||||
gdk_win32_with_activation_context,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gdk_win32_procedures_unload (void)
|
||||
{
|
||||
for (size_t i = 0; i < G_N_ELEMENTS (procedures); i++)
|
||||
*procedures[i].pp = NULL;
|
||||
}
|
||||
|
46
gdk/win32/procedures.h
Normal file
46
gdk/win32/procedures.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/* GTK - The GIMP Toolkit
|
||||
*
|
||||
* Copyright (C) 2024 the GTK team
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
typedef BOOL
|
||||
(APIENTRY *IsApiSetImplemented_t) (PCSTR Contract);
|
||||
|
||||
typedef LONG
|
||||
(WINAPI *GetCurrentPackageFullName_t) (UINT32 packageFullNameLength,
|
||||
PWSTR packageFullName);
|
||||
|
||||
typedef HMODULE
|
||||
(WINAPI *LoadPackagedLibrary_t) (LPCWSTR lpwLibFileName,
|
||||
DWORD Reserved);
|
||||
|
||||
#define PROCEDURES \
|
||||
PROCEDURE (IsApiSetImplemented , API_CORE_APIQUERY_2 , -1 , -1 ) \
|
||||
PROCEDURE (GetCurrentPackageFullName , API_APPMODEL_RUNTIME_1 , MODULE_KERNEL32 , OSVersionWindows8) \
|
||||
PROCEDURE (LoadPackagedLibrary , API_CORE_LIBRARYLOADER_2 , MODULE_KERNEL32 , OSVersionWindows8) \
|
||||
|
||||
#define PROCEDURE(name, api_set_id, module_id, os_version) \
|
||||
extern name##_t ptr##name;
|
||||
PROCEDURES
|
||||
#undef PROCEDURE
|
||||
|
||||
void gdk_win32_procedures_load (void);
|
||||
void gdk_win32_procedures_unload (void);
|
||||
|
Reference in New Issue
Block a user