mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-07-04 17:39:57 +02:00
Compare commits
3 Commits
copilot/fi
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f9679b937d | ||
|
|
36300d3c75 | ||
|
|
1cde68ae04 |
@@ -18,13 +18,6 @@ public class ExtensionWrapper : IExtensionWrapper
|
||||
{
|
||||
private const int HResultRpcServerNotRunning = -2147023174;
|
||||
|
||||
// COM/WinRT HRESULT constants used during extension activation
|
||||
private const int HResultNoInterface = unchecked((int)0x80004002); // E_NOINTERFACE
|
||||
private const int HResultPathNotFound = unchecked((int)0x80070003); // E_PATH_NOT_FOUND (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND))
|
||||
|
||||
// IID_IUnknown - the base COM interface always accepted by any well-formed class factory
|
||||
private static readonly Guid IID_IUnknown = new("00000000-0000-0000-C000-000000000046");
|
||||
|
||||
private readonly string _appUserModelId;
|
||||
private readonly string _extensionId;
|
||||
|
||||
@@ -118,68 +111,28 @@ public class ExtensionWrapper : IExtensionWrapper
|
||||
var extensionPtr = (void*)nint.Zero;
|
||||
try
|
||||
{
|
||||
// First attempt: request IExtension directly.
|
||||
// On Windows 11 23H2 (Build 22631), CoCreateInstance with a custom
|
||||
// WinRT interface IID (like IExtension) may return E_NOINTERFACE because
|
||||
// the OS cannot marshal the interface cross-process without a registered
|
||||
// proxy/stub. Newer Windows builds (24H2+) handle this automatically via
|
||||
// WinRT metadata. In that case, we fall back to IID_IUnknown so the
|
||||
// server can return an IInspectable CCW, which is always marshalable.
|
||||
var extensionIid = typeof(IExtension).GUID;
|
||||
// -2147024809: E_INVALIDARG
|
||||
// -2147467262: E_NOINTERFACE
|
||||
// -2147024893: E_PATH_NOT_FOUND
|
||||
var guid = typeof(IExtension).GUID;
|
||||
|
||||
var hr = PInvoke.CoCreateInstance(Guid.Parse(ExtensionClassId), null, CLSCTX.CLSCTX_LOCAL_SERVER, extensionIid, out extensionPtr);
|
||||
var hr = PInvoke.CoCreateInstance(Guid.Parse(ExtensionClassId), null, CLSCTX.CLSCTX_LOCAL_SERVER, guid, out extensionPtr);
|
||||
|
||||
var usedIUnknownFallback = false;
|
||||
if (hr.Value == HResultNoInterface)
|
||||
{
|
||||
// On Windows 23H2, the OS may be unable to marshal the IExtension
|
||||
// WinRT interface cross-process. Retry with IID_IUnknown so the
|
||||
// server can return an IInspectable CCW (marshalable on all Windows
|
||||
// versions). We then QI for IExtension from the returned pointer.
|
||||
Logger.LogWarning($"CoCreateInstance for {ExtensionDisplayName} returned E_NOINTERFACE for IID_IExtension (hr=0x{hr.Value:X8}). " +
|
||||
$"Retrying with IID_IUnknown as a Windows 23H2 compatibility fallback.");
|
||||
|
||||
extensionPtr = (void*)nint.Zero;
|
||||
hr = PInvoke.CoCreateInstance(Guid.Parse(ExtensionClassId), null, CLSCTX.CLSCTX_LOCAL_SERVER, IID_IUnknown, out extensionPtr);
|
||||
usedIUnknownFallback = true;
|
||||
}
|
||||
|
||||
if (hr.Value == HResultPathNotFound)
|
||||
if (hr.Value == -2147024893)
|
||||
{
|
||||
Logger.LogError($"Failed to find {ExtensionDisplayName}: {hr}. It may have been uninstalled or deleted.");
|
||||
|
||||
// We don't really need to throw this exception.
|
||||
// We'll just return out nothing.
|
||||
return;
|
||||
}
|
||||
else if (hr.Value != 0)
|
||||
{
|
||||
// All other failures — log and bail out. Do NOT fall through to
|
||||
// MarshalInterface below, which would dereference a null pointer and
|
||||
// cause an access violation.
|
||||
Logger.LogError($"Failed to activate {ExtensionDisplayName}: hr=0x{hr.Value:X8}. " +
|
||||
$"On Windows 23H2 this may indicate that WinRT cross-process interface " +
|
||||
$"marshaling is unsupported for custom interfaces without a registered proxy/stub.");
|
||||
return;
|
||||
Logger.LogError($"Failed to find {ExtensionDisplayName}: {hr.Value}");
|
||||
}
|
||||
|
||||
if (usedIUnknownFallback)
|
||||
{
|
||||
// extensionPtr is an IUnknown/IInspectable cross-process proxy.
|
||||
// Wrap it as IInspectable and then try to QI for IExtension.
|
||||
// On Windows 23H2, this QI may fail (no proxy/stub for IExtension),
|
||||
// resulting in a null _extensionObject. On newer Windows the QI
|
||||
// should succeed via WinRT metadata-based marshaling.
|
||||
var inspectable = MarshalInspectable<object>.FromAbi((nint)extensionPtr);
|
||||
_extensionObject = inspectable as IExtension;
|
||||
if (_extensionObject == null)
|
||||
{
|
||||
Logger.LogError($"Extension {ExtensionDisplayName} does not expose IExtension across the COM process boundary. " +
|
||||
$"On Windows 23H2 (Build 22631) and earlier, custom WinRT interfaces cannot be marshaled " +
|
||||
$"cross-process without a registered proxy/stub. The extension will not be available.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_extensionObject = MarshalInterface<IExtension>.FromAbi((nint)extensionPtr);
|
||||
}
|
||||
// Marshal.ThrowExceptionForHR(hr);
|
||||
_extensionObject = MarshalInterface<IExtension>.FromAbi((nint)extensionPtr);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -456,18 +456,6 @@ public sealed partial class TopLevelCommandManager : ObservableObject,
|
||||
try
|
||||
{
|
||||
await startTask.WaitAsync(ExtensionStartTimeout, ct).ConfigureAwait(false);
|
||||
|
||||
// If the extension server failed to activate (e.g. E_NOINTERFACE on older Windows),
|
||||
// IsRunning() will be false and CommandProviderWrapper will throw. Check first so we
|
||||
// can log a more actionable message than the generic "You forgot to call StartExtensionAsync".
|
||||
if (!extension.IsRunning())
|
||||
{
|
||||
Logger.LogError($"Extension {extension.PackageFullName} did not activate after {sw.ElapsedMilliseconds} ms. " +
|
||||
$"This can happen on Windows 11 23H2 (Build 22631) and earlier due to WinRT cross-process " +
|
||||
$"interface marshaling limitations for custom interfaces. Check extension logs for HRESULT details.");
|
||||
return ExtensionStartResult.Failed(extension);
|
||||
}
|
||||
|
||||
Logger.LogInfo($"Started extension {extension.PackageFullName} in {sw.ElapsedMilliseconds} ms");
|
||||
return ExtensionStartResult.Started(extension, new CommandProviderWrapper(extension, _taskScheduler, _commandProviderCache));
|
||||
}
|
||||
|
||||
@@ -28,15 +28,6 @@ internal sealed partial class ExtensionInstanceManager : IClassFactory
|
||||
|
||||
private static readonly Guid IID_IUnknown = Guid.Parse("00000000-0000-0000-C000-000000000046");
|
||||
|
||||
// IInspectable is the WinRT base interface (always marshalable cross-process on all Windows versions).
|
||||
private static readonly Guid IID_IInspectable = Guid.Parse("AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90");
|
||||
|
||||
// IExtension's WinRT interface IID. On Windows 11 23H2 (Build 22631), CoCreateInstance may
|
||||
// invoke CreateInstance with this IID directly. We handle it by returning the IInspectable CCW,
|
||||
// which COM on all Windows versions can marshal cross-process. On 24H2+ the OS uses WinRT
|
||||
// metadata (winmd) to marshal custom interfaces transparently.
|
||||
private static readonly Guid IID_IExtension = typeof(IExtension).GUID;
|
||||
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
|
||||
private readonly Func<IExtension> _createExtension;
|
||||
@@ -69,12 +60,9 @@ internal sealed partial class ExtensionInstanceManager : IClassFactory
|
||||
Marshal.ThrowExceptionForHR(CLASS_E_NOAGGREGATION);
|
||||
}
|
||||
|
||||
if (riid == _clsid || riid == IID_IUnknown || riid == IID_IInspectable || riid == IID_IExtension)
|
||||
if (riid == _clsid || riid == IID_IUnknown)
|
||||
{
|
||||
// Create the instance of the .NET object and return it as IInspectable.
|
||||
// Returning IInspectable ensures the CCW is marshalable cross-process on all
|
||||
// Windows versions (including 23H2), since IInspectable has built-in OS support.
|
||||
// The client will resolve the specific WinRT interface (IExtension) via CsWinRT.
|
||||
// Create the instance of the .NET object
|
||||
var managed = _createExtension();
|
||||
var ins = MarshalInspectable<object>.FromManaged(managed);
|
||||
ppvObject = ins;
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
@@ -135,7 +136,18 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
private void Frame_NavigationFailed(object sender, NavigationFailedEventArgs e)
|
||||
{
|
||||
throw e.Exception;
|
||||
var sourcePage = e.SourcePageType?.FullName ?? "<unknown>";
|
||||
|
||||
if (e.Exception is null)
|
||||
{
|
||||
Logger.LogWarning($"Navigation to '{sourcePage}' failed without an exception.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogError($"Navigation to '{sourcePage}' failed.", e.Exception);
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void Frame_Navigated(object sender, NavigationEventArgs e)
|
||||
|
||||
Reference in New Issue
Block a user