mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
refactor: use ComPtr pattern for MSIX to avoid exception handling (#49688)
* Revert "fix: fix Windows MSIX release build errors (#49613)"
This reverts commit 4b5d5f9dd5.
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
* refactor: use WRL ComPtr pattern for MSIX to avoid exception handling
The MSIX auto-updater code was using C++/WinRT (winrt::* namespace), which requires exception handling (/EHsc). Mixing exception and non-exception handling code in the same binary is problematic at runtime. This commit refactors electron_api_msix_updater.cc to use an upstream Chromium pattern and eliminates the need for special exception handling build flags
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
* build: import correct packages
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
* build: consolidate IPackage declarations
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
* refactor: use IPackageManager/IPackageManager5/IPackageManager9 and IPackage/IPackage2/IPackage4/IPackage6 interfaces as needed for different API methods.
Also consolidates duplicate completion handler logic, fixes a bug in
RegisterRestartOnUpdate where the command line string could go out of
scope, and removes unused includes.
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
---------
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Keeley Hammond <khammond@slack-corp.com>
This commit is contained in:
32
BUILD.gn
32
BUILD.gn
@@ -420,37 +420,6 @@ action("electron_generate_node_defines") {
|
||||
args = [ rebase_path(target_gen_dir) ] + rebase_path(inputs)
|
||||
}
|
||||
|
||||
# MSIX updater needs to be in a separate source_set because it uses C++/WinRT
|
||||
# headers that require exceptions to be enabled.
|
||||
source_set("electron_msix_updater") {
|
||||
sources = [
|
||||
"shell/browser/api/electron_api_msix_updater.cc",
|
||||
"shell/browser/api/electron_api_msix_updater.h",
|
||||
]
|
||||
|
||||
configs += [ "//third_party/electron_node:node_external_config" ]
|
||||
|
||||
public_configs = [ ":electron_lib_config" ]
|
||||
|
||||
if (is_win) {
|
||||
cflags_cc = [
|
||||
"/EHsc", # Enable C++ exceptions for C++/WinRT
|
||||
"-Wno-c++98-compat-extra-semi", #Suppress C++98 compatibility warnings
|
||||
]
|
||||
|
||||
include_dirs = [ "//third_party/nearby/src/internal/platform/implementation/windows/generated" ]
|
||||
}
|
||||
|
||||
deps = [
|
||||
"//base",
|
||||
"//content/public/browser",
|
||||
"//gin",
|
||||
"//third_party/electron_node/deps/simdjson",
|
||||
"//third_party/electron_node/deps/uv",
|
||||
"//v8",
|
||||
]
|
||||
}
|
||||
|
||||
source_set("electron_lib") {
|
||||
configs += [
|
||||
"//v8:external_startup_data",
|
||||
@@ -466,7 +435,6 @@ source_set("electron_lib") {
|
||||
":electron_fuses",
|
||||
":electron_generate_node_defines",
|
||||
":electron_js2c",
|
||||
":electron_msix_updater",
|
||||
":electron_version_header",
|
||||
":resources",
|
||||
"buildflags",
|
||||
|
||||
@@ -277,6 +277,8 @@ filenames = {
|
||||
"shell/browser/api/electron_api_in_app_purchase.h",
|
||||
"shell/browser/api/electron_api_menu.cc",
|
||||
"shell/browser/api/electron_api_menu.h",
|
||||
"shell/browser/api/electron_api_msix_updater.cc",
|
||||
"shell/browser/api/electron_api_msix_updater.h",
|
||||
"shell/browser/api/electron_api_native_theme.cc",
|
||||
"shell/browser/api/electron_api_native_theme.h",
|
||||
"shell/browser/api/electron_api_net_log.cc",
|
||||
|
||||
@@ -11,14 +11,9 @@
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/task/single_thread_task_runner.h"
|
||||
#include "base/task/task_traits.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "content/public/browser/browser_task_traits.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "shell/browser/browser.h"
|
||||
#include "shell/browser/javascript_environment.h"
|
||||
#include "shell/browser/native_window.h"
|
||||
#include "shell/browser/window_list.h"
|
||||
#include "shell/common/gin_helper/dictionary.h"
|
||||
#include "shell/common/gin_helper/error_thrower.h"
|
||||
#include "shell/common/gin_helper/promise.h"
|
||||
@@ -33,16 +28,10 @@
|
||||
#include <windows.foundation.metadata.h>
|
||||
#include <windows.h>
|
||||
#include <windows.management.deployment.h>
|
||||
// Use pre-generated C++/WinRT headers from //third_party/nearby instead of the
|
||||
// SDK's cppwinrt headers, which are missing implementation files.
|
||||
#include "third_party/nearby/src/internal/platform/implementation/windows/generated/winrt/Windows.ApplicationModel.h"
|
||||
#include "third_party/nearby/src/internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h"
|
||||
#include "third_party/nearby/src/internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Metadata.h"
|
||||
#include "third_party/nearby/src/internal/platform/implementation/windows/generated/winrt/Windows.Foundation.h"
|
||||
#include "third_party/nearby/src/internal/platform/implementation/windows/generated/winrt/Windows.Management.Deployment.h"
|
||||
#include "third_party/nearby/src/internal/platform/implementation/windows/generated/winrt/base.h"
|
||||
#include <wrl.h>
|
||||
|
||||
#include "base/win/scoped_com_initializer.h"
|
||||
#include "base/win/core_winrt_util.h"
|
||||
#include "base/win/scoped_hstring.h"
|
||||
#endif
|
||||
|
||||
namespace electron {
|
||||
@@ -55,6 +44,53 @@ const bool debug_msix_updater =
|
||||
namespace {
|
||||
|
||||
#if BUILDFLAG(IS_WIN)
|
||||
|
||||
// Type aliases for cleaner code
|
||||
using ABI::Windows::ApplicationModel::IAppInstallerInfo;
|
||||
using ABI::Windows::ApplicationModel::IPackage;
|
||||
using ABI::Windows::ApplicationModel::IPackage2;
|
||||
using ABI::Windows::ApplicationModel::IPackage4;
|
||||
using ABI::Windows::ApplicationModel::IPackage6;
|
||||
using ABI::Windows::ApplicationModel::IPackageId;
|
||||
using ABI::Windows::ApplicationModel::IPackageStatics;
|
||||
using ABI::Windows::ApplicationModel::PackageSignatureKind;
|
||||
using ABI::Windows::ApplicationModel::PackageSignatureKind_Developer;
|
||||
using ABI::Windows::ApplicationModel::PackageSignatureKind_Enterprise;
|
||||
using ABI::Windows::ApplicationModel::PackageSignatureKind_None;
|
||||
using ABI::Windows::ApplicationModel::PackageSignatureKind_Store;
|
||||
using ABI::Windows::ApplicationModel::PackageSignatureKind_System;
|
||||
using ABI::Windows::Foundation::AsyncStatus;
|
||||
using ABI::Windows::Foundation::IAsyncInfo;
|
||||
using ABI::Windows::Foundation::IUriRuntimeClass;
|
||||
using ABI::Windows::Foundation::IUriRuntimeClassFactory;
|
||||
using ABI::Windows::Foundation::Metadata::IApiInformationStatics;
|
||||
using ABI::Windows::Management::Deployment::DeploymentOptions;
|
||||
using ABI::Windows::Management::Deployment::
|
||||
DeploymentOptions_ForceApplicationShutdown;
|
||||
using ABI::Windows::Management::Deployment::
|
||||
DeploymentOptions_ForceTargetApplicationShutdown;
|
||||
using ABI::Windows::Management::Deployment::
|
||||
DeploymentOptions_ForceUpdateFromAnyVersion;
|
||||
using ABI::Windows::Management::Deployment::DeploymentOptions_None;
|
||||
using ABI::Windows::Management::Deployment::IAddPackageOptions;
|
||||
using ABI::Windows::Management::Deployment::IDeploymentResult;
|
||||
using ABI::Windows::Management::Deployment::IPackageManager;
|
||||
using ABI::Windows::Management::Deployment::IPackageManager5;
|
||||
using ABI::Windows::Management::Deployment::IPackageManager9;
|
||||
using Microsoft::WRL::Callback;
|
||||
using Microsoft::WRL::ComPtr;
|
||||
|
||||
// Type alias for deployment async operation
|
||||
// AddPackageByUriAsync returns IAsyncOperationWithProgress<DeploymentResult*,
|
||||
// DeploymentProgress>
|
||||
using DeploymentAsyncOp = ABI::Windows::Foundation::IAsyncOperationWithProgress<
|
||||
ABI::Windows::Management::Deployment::DeploymentResult*,
|
||||
ABI::Windows::Management::Deployment::DeploymentProgress>;
|
||||
using DeploymentCompletedHandler =
|
||||
ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<
|
||||
ABI::Windows::Management::Deployment::DeploymentResult*,
|
||||
ABI::Windows::Management::Deployment::DeploymentProgress>;
|
||||
|
||||
// Helper function for debug logging
|
||||
void DebugLog(std::string_view log_msg) {
|
||||
if (electron::debug_msix_updater)
|
||||
@@ -84,32 +120,274 @@ struct RegisterPackageOptions {
|
||||
bool force_update_from_any_version = false;
|
||||
};
|
||||
|
||||
// Helper: Create PackageManager using RoActivateInstance
|
||||
//
|
||||
// Note on COM interface versioning: In COM/WinRT, each interface version
|
||||
// (IPackageManager, IPackageManager5, IPackageManager9, etc.) is a separate
|
||||
// interface that must be queried independently. Unlike C++ inheritance,
|
||||
// IPackageManager9 does NOT inherit methods from IPackageManager5 or the base
|
||||
// IPackageManager. Each version only contains the methods that were newly
|
||||
// added in that version. To call methods from different versions, you must
|
||||
// QueryInterface (or ComPtr::As) for each specific interface version needed.
|
||||
HRESULT CreatePackageManager(ComPtr<IPackageManager>* package_manager) {
|
||||
base::win::ScopedHString class_id = base::win::ScopedHString::Create(
|
||||
RuntimeClass_Windows_Management_Deployment_PackageManager);
|
||||
if (!class_id.is_valid()) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
ComPtr<IInspectable> inspectable;
|
||||
HRESULT hr = base::win::RoActivateInstance(class_id.get(), &inspectable);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return inspectable.As(package_manager);
|
||||
}
|
||||
|
||||
// Helper: Create URI using IUriRuntimeClassFactory
|
||||
HRESULT CreateUri(const std::wstring& uri_string,
|
||||
ComPtr<IUriRuntimeClass>* uri) {
|
||||
ComPtr<IUriRuntimeClassFactory> uri_factory;
|
||||
HRESULT hr =
|
||||
base::win::GetActivationFactory<IUriRuntimeClassFactory,
|
||||
RuntimeClass_Windows_Foundation_Uri>(
|
||||
&uri_factory);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
base::win::ScopedHString uri_hstring =
|
||||
base::win::ScopedHString::Create(uri_string);
|
||||
if (!uri_hstring.is_valid()) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
return uri_factory->CreateUri(uri_hstring.get(), uri->GetAddressOf());
|
||||
}
|
||||
|
||||
// Helper: Create and configure AddPackageOptions
|
||||
HRESULT CreateAddPackageOptions(const UpdateMsixOptions& opts,
|
||||
ComPtr<IAddPackageOptions>* package_options) {
|
||||
base::win::ScopedHString class_id = base::win::ScopedHString::Create(
|
||||
RuntimeClass_Windows_Management_Deployment_AddPackageOptions);
|
||||
if (!class_id.is_valid()) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
ComPtr<IInspectable> inspectable;
|
||||
HRESULT hr = base::win::RoActivateInstance(class_id.get(), &inspectable);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = inspectable.As(package_options);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Configure options using ABI interface methods
|
||||
(*package_options)
|
||||
->put_DeferRegistrationWhenPackagesAreInUse(opts.defer_registration);
|
||||
(*package_options)->put_DeveloperMode(opts.developer_mode);
|
||||
(*package_options)->put_ForceAppShutdown(opts.force_shutdown);
|
||||
(*package_options)->put_ForceTargetAppShutdown(opts.force_target_shutdown);
|
||||
(*package_options)
|
||||
->put_ForceUpdateFromAnyVersion(opts.force_update_from_any_version);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Helper: Check if API contract is present
|
||||
HRESULT CheckApiContractPresent(UINT16 version, boolean* is_present) {
|
||||
ComPtr<IApiInformationStatics> api_info;
|
||||
HRESULT hr = base::win::GetActivationFactory<
|
||||
IApiInformationStatics,
|
||||
RuntimeClass_Windows_Foundation_Metadata_ApiInformation>(&api_info);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
base::win::ScopedHString contract_name = base::win::ScopedHString::Create(
|
||||
L"Windows.Foundation.UniversalApiContract");
|
||||
if (!contract_name.is_valid()) {
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
return api_info->IsApiContractPresentByMajor(contract_name.get(), version,
|
||||
is_present);
|
||||
}
|
||||
|
||||
// Helper: Get current package using IPackageStatics
|
||||
HRESULT GetCurrentPackage(ComPtr<IPackage>* package) {
|
||||
ComPtr<IPackageStatics> package_statics;
|
||||
HRESULT hr = base::win::GetActivationFactory<
|
||||
IPackageStatics, RuntimeClass_Windows_ApplicationModel_Package>(
|
||||
&package_statics);
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return package_statics->get_Current(package->GetAddressOf());
|
||||
}
|
||||
|
||||
// Structure to hold callback data for async operations
|
||||
struct DeploymentCallbackData {
|
||||
scoped_refptr<base::SingleThreadTaskRunner> reply_runner;
|
||||
gin_helper::Promise<void> promise;
|
||||
bool fire_and_forget;
|
||||
ComPtr<DeploymentAsyncOp> async_op; // Keep async_op alive
|
||||
std::string operation_name; // "Deployment" or "Registration" for logs
|
||||
};
|
||||
|
||||
// Handler for deployment/registration completion
|
||||
void OnDeploymentCompleted(std::unique_ptr<DeploymentCallbackData> data,
|
||||
DeploymentAsyncOp* async_op,
|
||||
AsyncStatus status) {
|
||||
std::string error;
|
||||
const std::string& op_name = data->operation_name;
|
||||
|
||||
if (data->fire_and_forget) {
|
||||
std::ostringstream oss;
|
||||
oss << op_name
|
||||
<< " initiated. Force shutdown or target shutdown requested. "
|
||||
"Good bye!";
|
||||
DebugLog(oss.str());
|
||||
// Don't wait for result in fire-and-forget mode
|
||||
data->reply_runner->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise) { promise.Resolve(); },
|
||||
std::move(data->promise)));
|
||||
return;
|
||||
}
|
||||
|
||||
if (status == AsyncStatus::Error) {
|
||||
ComPtr<IDeploymentResult> result;
|
||||
HRESULT hr = async_op->GetResults(&result);
|
||||
if (SUCCEEDED(hr) && result) {
|
||||
HSTRING error_text_hstring;
|
||||
hr = result->get_ErrorText(&error_text_hstring);
|
||||
if (SUCCEEDED(hr)) {
|
||||
base::win::ScopedHString scoped_error(error_text_hstring);
|
||||
error = scoped_error.GetAsUTF8();
|
||||
}
|
||||
|
||||
ComPtr<IAsyncInfo> async_info;
|
||||
hr = async_op->QueryInterface(IID_PPV_ARGS(&async_info));
|
||||
if (SUCCEEDED(hr)) {
|
||||
HRESULT error_code;
|
||||
hr = async_info->get_ErrorCode(&error_code);
|
||||
if (SUCCEEDED(hr)) {
|
||||
error += " (" + std::to_string(static_cast<int>(error_code)) + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error.empty()) {
|
||||
error = op_name + " failed with unknown error";
|
||||
}
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << op_name << " failed: " << error;
|
||||
DebugLog(oss.str());
|
||||
}
|
||||
} else if (status == AsyncStatus::Canceled) {
|
||||
std::ostringstream oss;
|
||||
oss << op_name << " canceled";
|
||||
DebugLog(oss.str());
|
||||
error = op_name + " canceled";
|
||||
} else if (status == AsyncStatus::Completed) {
|
||||
std::ostringstream oss;
|
||||
oss << "MSIX " << op_name << " completed.";
|
||||
DebugLog(oss.str());
|
||||
} else {
|
||||
error = op_name + " status unknown";
|
||||
std::ostringstream oss;
|
||||
oss << op_name << " status unknown";
|
||||
DebugLog(oss.str());
|
||||
}
|
||||
|
||||
// Post result back to UI thread
|
||||
data->reply_runner->PostTask(
|
||||
FROM_HERE, base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise, std::string error) {
|
||||
if (error.empty()) {
|
||||
promise.Resolve();
|
||||
} else {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
}
|
||||
},
|
||||
std::move(data->promise), std::move(error)));
|
||||
}
|
||||
|
||||
// Performs MSIX update on IO thread
|
||||
void DoUpdateMsix(const std::string& package_uri,
|
||||
UpdateMsixOptions opts,
|
||||
scoped_refptr<base::SingleThreadTaskRunner> reply_runner,
|
||||
gin_helper::Promise<void> promise) {
|
||||
DebugLog("DoUpdateMsix: Starting");
|
||||
|
||||
using winrt::Windows::Foundation::AsyncStatus;
|
||||
using winrt::Windows::Foundation::Uri;
|
||||
using winrt::Windows::Management::Deployment::AddPackageOptions;
|
||||
using winrt::Windows::Management::Deployment::DeploymentResult;
|
||||
using winrt::Windows::Management::Deployment::PackageManager;
|
||||
|
||||
std::string error;
|
||||
std::wstring packageUriString =
|
||||
std::wstring(package_uri.begin(), package_uri.end());
|
||||
Uri uri{packageUriString};
|
||||
PackageManager packageManager;
|
||||
AddPackageOptions packageOptions;
|
||||
|
||||
// Use the pre-parsed options
|
||||
packageOptions.DeferRegistrationWhenPackagesAreInUse(opts.defer_registration);
|
||||
packageOptions.DeveloperMode(opts.developer_mode);
|
||||
packageOptions.ForceAppShutdown(opts.force_shutdown);
|
||||
packageOptions.ForceTargetAppShutdown(opts.force_target_shutdown);
|
||||
packageOptions.ForceUpdateFromAnyVersion(opts.force_update_from_any_version);
|
||||
// Create PackageManager
|
||||
ComPtr<IPackageManager> package_manager;
|
||||
HRESULT hr = CreatePackageManager(&package_manager);
|
||||
if (FAILED(hr)) {
|
||||
error = "Failed to create PackageManager";
|
||||
reply_runner->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise, std::string error) {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
},
|
||||
std::move(promise), std::move(error)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get IPackageManager9 for AddPackageByUriAsync
|
||||
ComPtr<IPackageManager9> package_manager9;
|
||||
hr = package_manager.As(&package_manager9);
|
||||
if (FAILED(hr)) {
|
||||
error = "Failed to get IPackageManager9 interface";
|
||||
reply_runner->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise, std::string error) {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
},
|
||||
std::move(promise), std::move(error)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create URI
|
||||
std::wstring uri_wstring = base::UTF8ToWide(package_uri);
|
||||
ComPtr<IUriRuntimeClass> uri;
|
||||
hr = CreateUri(uri_wstring, &uri);
|
||||
if (FAILED(hr)) {
|
||||
error = "Failed to create URI";
|
||||
reply_runner->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise, std::string error) {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
},
|
||||
std::move(promise), std::move(error)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create AddPackageOptions
|
||||
ComPtr<IAddPackageOptions> package_options;
|
||||
hr = CreateAddPackageOptions(opts, &package_options);
|
||||
if (FAILED(hr)) {
|
||||
error = "Failed to create AddPackageOptions";
|
||||
reply_runner->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise, std::string error) {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
},
|
||||
std::move(promise), std::move(error)));
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::ostringstream oss;
|
||||
@@ -127,63 +405,54 @@ void DoUpdateMsix(const std::string& package_uri,
|
||||
DebugLog(oss.str());
|
||||
}
|
||||
|
||||
auto deploymentOperation =
|
||||
packageManager.AddPackageByUriAsync(uri, packageOptions);
|
||||
|
||||
if (!deploymentOperation) {
|
||||
DebugLog("Deployment operation is null");
|
||||
// Start async operation
|
||||
ComPtr<DeploymentAsyncOp> async_op;
|
||||
hr = package_manager9->AddPackageByUriAsync(uri.Get(), package_options.Get(),
|
||||
&async_op);
|
||||
if (FAILED(hr) || !async_op) {
|
||||
DebugLog("AddPackageByUriAsync failed or returned null");
|
||||
error =
|
||||
"Deployment is NULL. See "
|
||||
"http://go.microsoft.com/fwlink/?LinkId=235160 for diagnosing.";
|
||||
} else {
|
||||
if (!opts.force_shutdown && !opts.force_target_shutdown) {
|
||||
DebugLog("Waiting for deployment...");
|
||||
deploymentOperation.get();
|
||||
DebugLog("Deployment finished.");
|
||||
|
||||
if (deploymentOperation.Status() == AsyncStatus::Error) {
|
||||
auto deploymentResult{deploymentOperation.GetResults()};
|
||||
std::string errorText = winrt::to_string(deploymentResult.ErrorText());
|
||||
std::string errorCode =
|
||||
std::to_string(static_cast<int>(deploymentOperation.ErrorCode()));
|
||||
error = errorText + " (" + errorCode + ")";
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Deployment failed: " << error;
|
||||
DebugLog(oss.str());
|
||||
}
|
||||
} else if (deploymentOperation.Status() == AsyncStatus::Canceled) {
|
||||
DebugLog("Deployment canceled");
|
||||
error = "Deployment canceled";
|
||||
} else if (deploymentOperation.Status() == AsyncStatus::Completed) {
|
||||
DebugLog("MSIX Deployment completed.");
|
||||
} else {
|
||||
error = "Deployment status unknown";
|
||||
DebugLog("Deployment status unknown");
|
||||
}
|
||||
} else {
|
||||
// At this point, we can not await the deployment because we require a
|
||||
// shutdown of the app to continue, so we do a fire and forget. When the
|
||||
// deployment process tries ot shutdown the app, the process waits for us
|
||||
// to finish here. But to finish we need to shutdow. That leads to a 30s
|
||||
// dealock, till we forcefully get shutdown by the OS.
|
||||
DebugLog(
|
||||
"Deployment initiated. Force shutdown or target shutdown requested. "
|
||||
"Good bye!");
|
||||
}
|
||||
reply_runner->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise, std::string error) {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
},
|
||||
std::move(promise), std::move(error)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Post result back
|
||||
reply_runner->PostTask(
|
||||
FROM_HERE, base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise, std::string error) {
|
||||
if (error.empty()) {
|
||||
promise.Resolve();
|
||||
} else {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
}
|
||||
},
|
||||
std::move(promise), error));
|
||||
// Set up callback data
|
||||
auto callback_data = std::make_unique<DeploymentCallbackData>();
|
||||
callback_data->reply_runner = reply_runner;
|
||||
callback_data->promise = std::move(promise);
|
||||
callback_data->fire_and_forget =
|
||||
opts.force_shutdown || opts.force_target_shutdown;
|
||||
callback_data->async_op = async_op; // Keep async_op alive
|
||||
callback_data->operation_name = "Deployment";
|
||||
|
||||
// Register completion handler
|
||||
DeploymentCallbackData* raw_data = callback_data.get();
|
||||
hr = async_op->put_Completed(
|
||||
Callback<DeploymentCompletedHandler>([data = std::move(callback_data)](
|
||||
DeploymentAsyncOp* op,
|
||||
AsyncStatus status) mutable {
|
||||
OnDeploymentCompleted(std::move(data), op, status);
|
||||
return S_OK;
|
||||
}).Get());
|
||||
|
||||
if (FAILED(hr)) {
|
||||
DebugLog("Failed to register completion handler");
|
||||
raw_data->reply_runner->PostTask(
|
||||
FROM_HERE, base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise) {
|
||||
promise.RejectWithErrorMessage(
|
||||
"Failed to register completion handler");
|
||||
},
|
||||
std::move(raw_data->promise)));
|
||||
}
|
||||
}
|
||||
|
||||
// Performs package registration on IO thread
|
||||
@@ -192,31 +461,67 @@ void DoRegisterPackage(const std::string& family_name,
|
||||
scoped_refptr<base::SingleThreadTaskRunner> reply_runner,
|
||||
gin_helper::Promise<void> promise) {
|
||||
DebugLog("DoRegisterPackage: Starting");
|
||||
|
||||
using winrt::Windows::Foundation::AsyncStatus;
|
||||
using winrt::Windows::Foundation::Collections::IIterable;
|
||||
using winrt::Windows::Management::Deployment::DeploymentOptions;
|
||||
using winrt::Windows::Management::Deployment::PackageManager;
|
||||
|
||||
std::string error;
|
||||
auto familyNameH = winrt::to_hstring(family_name);
|
||||
PackageManager packageManager;
|
||||
DeploymentOptions deploymentOptions = DeploymentOptions::None;
|
||||
|
||||
// Use the pre-parsed options (no V8 access needed)
|
||||
// Create PackageManager
|
||||
ComPtr<IPackageManager> package_manager;
|
||||
HRESULT hr = CreatePackageManager(&package_manager);
|
||||
if (FAILED(hr)) {
|
||||
error = "Failed to create PackageManager";
|
||||
reply_runner->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise, std::string error) {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
},
|
||||
std::move(promise), std::move(error)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get IPackageManager5 for RegisterPackageByFamilyNameAsync
|
||||
ComPtr<IPackageManager5> package_manager5;
|
||||
hr = package_manager.As(&package_manager5);
|
||||
if (FAILED(hr)) {
|
||||
error = "Failed to get IPackageManager5 interface";
|
||||
reply_runner->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise, std::string error) {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
},
|
||||
std::move(promise), std::move(error)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Build DeploymentOptions flags
|
||||
DeploymentOptions deployment_options = DeploymentOptions_None;
|
||||
if (opts.force_shutdown) {
|
||||
deploymentOptions |= DeploymentOptions::ForceApplicationShutdown;
|
||||
deployment_options = static_cast<DeploymentOptions>(
|
||||
deployment_options | DeploymentOptions_ForceApplicationShutdown);
|
||||
}
|
||||
if (opts.force_target_shutdown) {
|
||||
deploymentOptions |= DeploymentOptions::ForceTargetApplicationShutdown;
|
||||
deployment_options = static_cast<DeploymentOptions>(
|
||||
deployment_options | DeploymentOptions_ForceTargetApplicationShutdown);
|
||||
}
|
||||
if (opts.force_update_from_any_version) {
|
||||
deploymentOptions |= DeploymentOptions::ForceUpdateFromAnyVersion;
|
||||
deployment_options = static_cast<DeploymentOptions>(
|
||||
deployment_options | DeploymentOptions_ForceUpdateFromAnyVersion);
|
||||
}
|
||||
|
||||
// Create empty collections for dependency and optional packages
|
||||
IIterable<winrt::hstring> emptyDependencies{nullptr};
|
||||
IIterable<winrt::hstring> emptyOptional{nullptr};
|
||||
// Create HSTRING for family name
|
||||
base::win::ScopedHString family_name_hstring =
|
||||
base::win::ScopedHString::Create(family_name);
|
||||
if (!family_name_hstring.is_valid()) {
|
||||
error = "Failed to create family name string";
|
||||
reply_runner->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise, std::string error) {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
},
|
||||
std::move(promise), std::move(error)));
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::ostringstream oss;
|
||||
@@ -233,63 +538,59 @@ void DoRegisterPackage(const std::string& family_name,
|
||||
DebugLog(oss.str());
|
||||
}
|
||||
|
||||
auto deploymentOperation = packageManager.RegisterPackageByFamilyNameAsync(
|
||||
familyNameH, emptyDependencies, deploymentOptions, nullptr,
|
||||
emptyOptional);
|
||||
// RegisterPackageByFamilyNameAndOptionalPackagesAsync (ABI name)
|
||||
ComPtr<DeploymentAsyncOp> async_op;
|
||||
hr = package_manager5->RegisterPackageByFamilyNameAndOptionalPackagesAsync(
|
||||
family_name_hstring.get(),
|
||||
nullptr, // dependencyPackageFamilyNames
|
||||
deployment_options,
|
||||
nullptr, // appDataVolume
|
||||
nullptr, // optionalPackageFamilyNames
|
||||
&async_op);
|
||||
|
||||
if (!deploymentOperation) {
|
||||
if (FAILED(hr) || !async_op) {
|
||||
error =
|
||||
"Deployment is NULL. See "
|
||||
"http://go.microsoft.com/fwlink/?LinkId=235160 for diagnosing.";
|
||||
} else {
|
||||
if (!opts.force_shutdown && !opts.force_target_shutdown) {
|
||||
DebugLog("Waiting for registration...");
|
||||
deploymentOperation.get();
|
||||
DebugLog("Registration finished.");
|
||||
|
||||
if (deploymentOperation.Status() == AsyncStatus::Error) {
|
||||
auto deploymentResult{deploymentOperation.GetResults()};
|
||||
std::string errorText = winrt::to_string(deploymentResult.ErrorText());
|
||||
std::string errorCode =
|
||||
std::to_string(static_cast<int>(deploymentOperation.ErrorCode()));
|
||||
error = errorText + " (" + errorCode + ")";
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "Registration failed: " << error;
|
||||
DebugLog(oss.str());
|
||||
}
|
||||
} else if (deploymentOperation.Status() == AsyncStatus::Canceled) {
|
||||
DebugLog("Registration canceled");
|
||||
error = "Registration canceled";
|
||||
} else if (deploymentOperation.Status() == AsyncStatus::Completed) {
|
||||
DebugLog("MSIX Registration completed.");
|
||||
} else {
|
||||
error = "Registration status unknown";
|
||||
DebugLog("Registration status unknown");
|
||||
}
|
||||
} else {
|
||||
// At this point, we can not await the registration because we require a
|
||||
// shutdown of the app to continue, so we do a fire and forget. When the
|
||||
// registration process tries ot shutdown the app, the process waits for
|
||||
// us to finish here. But to finish we need to shutdown. That leads to a
|
||||
// 30s dealock, till we forcefully get shutdown by the OS.
|
||||
DebugLog(
|
||||
"Registration initiated. Force shutdown or target shutdown "
|
||||
"requested. Good bye!");
|
||||
}
|
||||
reply_runner->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise, std::string error) {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
},
|
||||
std::move(promise), std::move(error)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Post result back to UI thread
|
||||
reply_runner->PostTask(
|
||||
FROM_HERE, base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise, std::string error) {
|
||||
if (error.empty()) {
|
||||
promise.Resolve();
|
||||
} else {
|
||||
promise.RejectWithErrorMessage(error);
|
||||
}
|
||||
},
|
||||
std::move(promise), error));
|
||||
// Set up callback data
|
||||
auto callback_data = std::make_unique<DeploymentCallbackData>();
|
||||
callback_data->reply_runner = reply_runner;
|
||||
callback_data->promise = std::move(promise);
|
||||
callback_data->fire_and_forget =
|
||||
opts.force_shutdown || opts.force_target_shutdown;
|
||||
callback_data->async_op = async_op; // Keep async_op alive
|
||||
callback_data->operation_name = "Registration";
|
||||
|
||||
// Register completion handler
|
||||
DeploymentCallbackData* raw_data = callback_data.get();
|
||||
hr = async_op->put_Completed(
|
||||
Callback<DeploymentCompletedHandler>([data = std::move(callback_data)](
|
||||
DeploymentAsyncOp* op,
|
||||
AsyncStatus status) mutable {
|
||||
OnDeploymentCompleted(std::move(data), op, status);
|
||||
return S_OK;
|
||||
}).Get());
|
||||
|
||||
if (FAILED(hr)) {
|
||||
DebugLog("Failed to register completion handler");
|
||||
raw_data->reply_runner->PostTask(
|
||||
FROM_HERE, base::BindOnce(
|
||||
[](gin_helper::Promise<void> promise) {
|
||||
promise.RejectWithErrorMessage(
|
||||
"Failed to register completion handler");
|
||||
},
|
||||
std::move(raw_data->promise)));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -307,6 +608,16 @@ v8::Local<v8::Promise> UpdateMsix(const std::string& package_uri,
|
||||
return handle;
|
||||
}
|
||||
|
||||
// Check for required API contract (IPackageManager9 requires v10)
|
||||
boolean is_api_present = FALSE;
|
||||
if (FAILED(CheckApiContractPresent(10, &is_api_present)) || !is_api_present) {
|
||||
DebugLog("UpdateMsix: Required Windows API contract not present");
|
||||
promise.RejectWithErrorMessage(
|
||||
"This Windows version does not support MSIX updates via this API. "
|
||||
"Windows 10 version 2004 or later is required.");
|
||||
return handle;
|
||||
}
|
||||
|
||||
// Parse options on UI thread (where V8 is available)
|
||||
UpdateMsixOptions opts;
|
||||
options.Get("deferRegistration", &opts.defer_registration);
|
||||
@@ -349,6 +660,16 @@ v8::Local<v8::Promise> RegisterPackage(const std::string& family_name,
|
||||
return handle;
|
||||
}
|
||||
|
||||
// Check for required API contract (IPackageManager5 requires v3)
|
||||
boolean is_api_present = FALSE;
|
||||
if (FAILED(CheckApiContractPresent(3, &is_api_present)) || !is_api_present) {
|
||||
DebugLog("RegisterPackage: Required Windows API contract not present");
|
||||
promise.RejectWithErrorMessage(
|
||||
"This Windows version does not support package registration via this "
|
||||
"API. Windows 10 version 1607 or later is required.");
|
||||
return handle;
|
||||
}
|
||||
|
||||
// Parse options on UI thread (where V8 is available)
|
||||
RegisterPackageOptions opts;
|
||||
options.Get("forceShutdown", &opts.force_shutdown);
|
||||
@@ -384,32 +705,30 @@ bool RegisterRestartOnUpdate(const std::string& command_line) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const wchar_t* commandLine = nullptr;
|
||||
// Flags: RESTART_NO_CRASH | RESTART_NO_HANG | RESTART_NO_REBOOT
|
||||
// This means: only restart on updates (RESTART_NO_PATCH is NOT set)
|
||||
const DWORD dwFlags = 1 | 2 | 8; // 11
|
||||
|
||||
// Convert command line to wide string (keep in scope for API call)
|
||||
std::wstring command_line_wide;
|
||||
const wchar_t* command_line_ptr = nullptr;
|
||||
if (!command_line.empty()) {
|
||||
std::wstring commandLineW =
|
||||
std::wstring(command_line.begin(), command_line.end());
|
||||
commandLine = commandLineW.c_str();
|
||||
command_line_wide = base::UTF8ToWide(command_line);
|
||||
command_line_ptr = command_line_wide.c_str();
|
||||
}
|
||||
|
||||
HRESULT hr = RegisterApplicationRestart(commandLine, dwFlags);
|
||||
HRESULT hr = RegisterApplicationRestart(command_line_ptr, dwFlags);
|
||||
if (FAILED(hr)) {
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "RegisterApplicationRestart failed with error code: " << hr;
|
||||
DebugLog(oss.str());
|
||||
}
|
||||
std::ostringstream oss;
|
||||
oss << "RegisterApplicationRestart failed with error code: " << hr;
|
||||
DebugLog(oss.str());
|
||||
return false;
|
||||
}
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "RegisterApplicationRestart succeeded"
|
||||
<< (command_line.empty() ? "" : " with command line");
|
||||
DebugLog(oss.str());
|
||||
}
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "RegisterApplicationRestart succeeded"
|
||||
<< (command_line.empty() ? "" : " with command line");
|
||||
DebugLog(oss.str());
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
@@ -434,57 +753,119 @@ v8::Local<v8::Value> GetPackageInfo() {
|
||||
gin_helper::Dictionary result(isolate, v8::Object::New(isolate));
|
||||
|
||||
// Check API contract version (Windows 10 version 1703 or later)
|
||||
if (winrt::Windows::Foundation::Metadata::ApiInformation::
|
||||
IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 7)) {
|
||||
using winrt::Windows::ApplicationModel::Package;
|
||||
using winrt::Windows::ApplicationModel::PackageSignatureKind;
|
||||
Package package = Package::Current();
|
||||
boolean is_present = FALSE;
|
||||
HRESULT hr = CheckApiContractPresent(7, &is_present);
|
||||
if (SUCCEEDED(hr) && is_present) {
|
||||
ComPtr<IPackage> package;
|
||||
hr = GetCurrentPackage(&package);
|
||||
if (SUCCEEDED(hr) && package) {
|
||||
// Query all needed package interface versions upfront.
|
||||
// Note: Like IPackageManager, each IPackage version (IPackage2,
|
||||
// IPackage4, IPackage6) is a separate COM interface. IPackage6 does NOT
|
||||
// inherit methods from earlier versions. We must query each version
|
||||
// separately to access its specific methods:
|
||||
// - IPackage2: get_IsDevelopmentMode
|
||||
// - IPackage4: get_SignatureKind
|
||||
// - IPackage6: GetAppInstallerInfo
|
||||
ComPtr<IPackage2> package2;
|
||||
ComPtr<IPackage4> package4;
|
||||
ComPtr<IPackage6> package6;
|
||||
package.As(&package2);
|
||||
package.As(&package4);
|
||||
package.As(&package6);
|
||||
|
||||
// Get package ID and family name
|
||||
std::string packageId = winrt::to_string(package.Id().FullName());
|
||||
std::string familyName = winrt::to_string(package.Id().FamilyName());
|
||||
// Get package ID (from base IPackage)
|
||||
ComPtr<IPackageId> package_id;
|
||||
hr = package->get_Id(&package_id);
|
||||
if (SUCCEEDED(hr) && package_id) {
|
||||
// Get FullName
|
||||
HSTRING full_name;
|
||||
hr = package_id->get_FullName(&full_name);
|
||||
if (SUCCEEDED(hr)) {
|
||||
base::win::ScopedHString scoped_name(full_name);
|
||||
result.Set("id", scoped_name.GetAsUTF8());
|
||||
}
|
||||
|
||||
result.Set("id", packageId);
|
||||
result.Set("familyName", familyName);
|
||||
result.Set("developmentMode", package.IsDevelopmentMode());
|
||||
// Get FamilyName
|
||||
HSTRING family_name;
|
||||
hr = package_id->get_FamilyName(&family_name);
|
||||
if (SUCCEEDED(hr)) {
|
||||
base::win::ScopedHString scoped_name(family_name);
|
||||
result.Set("familyName", scoped_name.GetAsUTF8());
|
||||
}
|
||||
|
||||
// Get package version
|
||||
auto packageVersion = package.Id().Version();
|
||||
std::string version = std::to_string(packageVersion.Major) + "." +
|
||||
std::to_string(packageVersion.Minor) + "." +
|
||||
std::to_string(packageVersion.Build) + "." +
|
||||
std::to_string(packageVersion.Revision);
|
||||
result.Set("version", version);
|
||||
// Get Version
|
||||
ABI::Windows::ApplicationModel::PackageVersion pkg_version;
|
||||
hr = package_id->get_Version(&pkg_version);
|
||||
if (SUCCEEDED(hr)) {
|
||||
std::string version = std::to_string(pkg_version.Major) + "." +
|
||||
std::to_string(pkg_version.Minor) + "." +
|
||||
std::to_string(pkg_version.Build) + "." +
|
||||
std::to_string(pkg_version.Revision);
|
||||
result.Set("version", version);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert signature kind to string
|
||||
std::string signatureKind;
|
||||
switch (package.SignatureKind()) {
|
||||
case PackageSignatureKind::Developer:
|
||||
signatureKind = "developer";
|
||||
break;
|
||||
case PackageSignatureKind::Enterprise:
|
||||
signatureKind = "enterprise";
|
||||
break;
|
||||
case PackageSignatureKind::None:
|
||||
signatureKind = "none";
|
||||
break;
|
||||
case PackageSignatureKind::Store:
|
||||
signatureKind = "store";
|
||||
break;
|
||||
case PackageSignatureKind::System:
|
||||
signatureKind = "system";
|
||||
break;
|
||||
default:
|
||||
signatureKind = "none";
|
||||
break;
|
||||
}
|
||||
result.Set("signatureKind", signatureKind);
|
||||
// Get IsDevelopmentMode (from IPackage2)
|
||||
if (package2) {
|
||||
boolean is_dev_mode = FALSE;
|
||||
hr = package2->get_IsDevelopmentMode(&is_dev_mode);
|
||||
result.Set("developmentMode", SUCCEEDED(hr) && is_dev_mode != FALSE);
|
||||
} else {
|
||||
result.Set("developmentMode", false);
|
||||
}
|
||||
|
||||
// Get app installer info if available
|
||||
auto appInstallerInfo = package.GetAppInstallerInfo();
|
||||
if (appInstallerInfo != nullptr) {
|
||||
std::string uriStr = winrt::to_string(appInstallerInfo.Uri().ToString());
|
||||
result.Set("appInstallerUri", uriStr);
|
||||
// Get SignatureKind (from IPackage4)
|
||||
if (package4) {
|
||||
PackageSignatureKind sig_kind;
|
||||
hr = package4->get_SignatureKind(&sig_kind);
|
||||
if (SUCCEEDED(hr)) {
|
||||
std::string signature_kind;
|
||||
switch (sig_kind) {
|
||||
case PackageSignatureKind_Developer:
|
||||
signature_kind = "developer";
|
||||
break;
|
||||
case PackageSignatureKind_Enterprise:
|
||||
signature_kind = "enterprise";
|
||||
break;
|
||||
case PackageSignatureKind_None:
|
||||
signature_kind = "none";
|
||||
break;
|
||||
case PackageSignatureKind_Store:
|
||||
signature_kind = "store";
|
||||
break;
|
||||
case PackageSignatureKind_System:
|
||||
signature_kind = "system";
|
||||
break;
|
||||
default:
|
||||
signature_kind = "none";
|
||||
break;
|
||||
}
|
||||
result.Set("signatureKind", signature_kind);
|
||||
} else {
|
||||
result.Set("signatureKind", "none");
|
||||
}
|
||||
} else {
|
||||
result.Set("signatureKind", "none");
|
||||
}
|
||||
|
||||
// Get AppInstallerInfo (from IPackage6)
|
||||
if (package6) {
|
||||
ComPtr<IAppInstallerInfo> app_installer_info;
|
||||
hr = package6->GetAppInstallerInfo(&app_installer_info);
|
||||
if (SUCCEEDED(hr) && app_installer_info) {
|
||||
ComPtr<IUriRuntimeClass> uri;
|
||||
hr = app_installer_info->get_Uri(&uri);
|
||||
if (SUCCEEDED(hr) && uri) {
|
||||
HSTRING uri_string;
|
||||
hr = uri->get_AbsoluteUri(&uri_string);
|
||||
if (SUCCEEDED(hr)) {
|
||||
base::win::ScopedHString scoped_uri(uri_string);
|
||||
result.Set("appInstallerUri", scoped_uri.GetAsUTF8());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Windows version doesn't meet minimum API requirements
|
||||
|
||||
Reference in New Issue
Block a user