Compare commits

..

4 Commits

Author SHA1 Message Date
Shawn Yuan
12534a986f update
Signed-off-by: Shawn Yuan <shuaiyuan@microsoft.com>
2025-06-03 12:40:10 +08:00
Shawn Yuan
8916442406 update
Signed-off-by: Shawn Yuan <shuaiyuan@microsoft.com>
2025-06-03 11:57:51 +08:00
Shawn Yuan
2ef0262897 update
Signed-off-by: Shawn Yuan <shuaiyuan@microsoft.com>
2025-06-03 11:53:51 +08:00
Shawn Yuan
7a0ebbd21e allow pipeline to use unstable winappsdk.
Signed-off-by: Shawn Yuan <shuaiyuan@microsoft.com>
2025-06-03 10:57:42 +08:00
95 changed files with 2102 additions and 1695 deletions

View File

@@ -127,8 +127,6 @@ Naro
nathancartlidge
Nemeth
nielslaute
Noraa
noraajunker
oldnewthing
onegreatworld
palenshus

View File

@@ -14,7 +14,6 @@ AColumn
acrt
ACTIVATEAPP
activationaction
ACTIVATEOPTIONS
ACVS
adaptivecards
ADate
@@ -1589,7 +1588,6 @@ steamapps
STGC
STGM
STGMEDIUM
STGMREAD
STICKYKEYS
sticpl
storelogo
@@ -1694,7 +1692,6 @@ TLayout
tlb
tlbimp
tlc
TGM
TNP
Toolhelp
toolkitconverters
@@ -1998,15 +1995,3 @@ culori
Evercoder
LCh
CIELCh
CLSCTXINPROCALL
IIDI
irow
lcid
OTHERUNZOOM
OTHERZOOM
PARENTCLOSING
PARENTOPENING
ppwsz
rguid
SCROLLCHILDREN
VARTYPE

View File

@@ -37,6 +37,10 @@ parameters:
- name: useExperimentalVersion
type: boolean
default: false
- name: useUnstableWinAppSDK
type: boolean
displayName: "Allow using unstable WinAppSDK package (even if build failed)"
default: false
extends:
template: templates/pipeline-ci-build.yml
@@ -48,3 +52,4 @@ extends:
useLatestWinAppSDK: ${{ parameters.useLatestWinAppSDK }}
winAppSDKVersionNumber: ${{ parameters.winAppSDKVersionNumber }}
useExperimentalVersion: ${{ parameters.useExperimentalVersion }}
useUnstableWinAppSDK: ${{ parameters.useUnstableWinAppSDK }}

View File

@@ -68,6 +68,9 @@ parameters:
- name: useExperimentalVersion
type: boolean
default: false
- name: useUnstableWinAppSDK
type: boolean
default: false
- name: csProjectsToPublish
type: object
default:
@@ -204,6 +207,7 @@ jobs:
parameters:
versionNumber: ${{ parameters.winAppSDKVersionNumber }}
useExperimentalVersion: ${{ parameters.useExperimentalVersion }}
useUnstableWinAppSDK: ${{ parameters.useUnstableWinAppSDK }}
- ${{ if eq(parameters.useLatestWinAppSDK, false)}}:
- template: .\steps-restore-nuget.yml

View File

@@ -34,6 +34,9 @@ parameters:
- name: useExperimentalVersion
type: boolean
default: false
- name: useUnstableWinAppSDK
type: boolean
default: false
stages:
- ${{ each platform in parameters.buildPlatforms }}:
@@ -58,6 +61,7 @@ stages:
runTests: ${{ parameters.runTests }}
useVSPreview: ${{ parameters.useVSPreview }}
useLatestWinAppSDK: ${{ parameters.useLatestWinAppSDK }}
useUnstableWinAppSDK: ${{ parameters.useUnstableWinAppSDK }}
${{ if eq(parameters.useLatestWinAppSDK, true) }}:
winAppSDKVersionNumber: ${{ parameters.winAppSDKVersionNumber }}
useExperimentalVersion: ${{ parameters.useExperimentalVersion }}

View File

@@ -5,6 +5,9 @@ parameters:
- name: useExperimentalVersion
type: boolean
default: false
- name: useUnstableWinAppSDK
type: boolean
default: false
steps:
- task: NuGetAuthenticate@1
@@ -28,6 +31,8 @@ steps:
buildType: 'specific'
project: '55e8140e-57ac-4e5f-8f9c-c7c15b51929d'
definition: '104083'
allowPartiallySucceededBuilds: ${{ parameters.useUnstableWinAppSDK }}
allowFailedBuilds: ${{ parameters.useUnstableWinAppSDK }}
buildVersionToDownload: 'latestFromBranch'
branchName: 'refs/heads/release/${{ parameters.versionNumber }}-stable'
artifactName: 'WindowsAppSDK_Nuget_And_MSIX'

View File

@@ -6,8 +6,8 @@ Names are in alphabetical order based on first name.
## High impact community members
### [@Noraa-Junker](https://github.com/Noraa-Junker) - [Noraa Junker](https://noraajunker.ch)
Noraa has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Noraa was the primary person for helping build the File Explorer preview pane handler for developer files.
### [@Aaron-Junker](https://github.com/Aaron-Junker) - [Aaron Junker](https://aaron-junker.github.io)
Aaron has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Aaron was the primary person for helping build the File Explorer preview pane handler for developer files.
### [@cgaarden](https://github.com/cgaarden) - [Christian Gaarden Gaardmark](https://www.onegreatworld.com)
Christian contributed New+ utility

View File

@@ -7,6 +7,6 @@
<CsWinRTAotWarningLevel>2</CsWinRTAotWarningLevel>
<!-- Suppress DynamicallyAccessedMemberTypes.PublicParameterlessConstructor in fallback code path of Windows SDK projection -->
<WarningsNotAsErrors>IL2081;CsWinRT1028;$(WarningsNotAsErrors)</WarningsNotAsErrors>
<WarningsNotAsErrors>IL2081;$(WarningsNotAsErrors)</WarningsNotAsErrors>
</PropertyGroup>
</Project>

View File

@@ -71,7 +71,7 @@ public class AllAppsSettings : JsonSettingsManager
internal static string SettingsJsonPath()
{
var directory = Utilities.BaseSettingsPath("Microsoft.CmdPal");
string directory = Utilities.BaseSettingsPath("Microsoft.CmdPal");
Directory.CreateDirectory(directory);
// now, the state is just next to the exe

View File

@@ -8,12 +8,7 @@ using System.Threading.Tasks;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Apps.Programs;
using Microsoft.CmdPal.Ext.Apps.Properties;
using Microsoft.CmdPal.Ext.Apps.Utils;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Services.Maps;
using Windows.Win32;
using Windows.Win32.System.Com;
using Windows.Win32.UI.Shell;
using WyHash;
namespace Microsoft.CmdPal.Ext.Apps;
@@ -32,31 +27,26 @@ internal sealed partial class AppCommand : InvokableCommand
internal static async Task StartApp(string aumid)
{
var appManager = new ApplicationActivationManager();
const ActivateOptions noFlags = ActivateOptions.None;
await Task.Run(() =>
{
unsafe
try
{
IApplicationActivationManager* appManager = null;
try
{
PInvoke.CoCreateInstance(typeof(ApplicationActivationManager).GUID, null, CLSCTX.CLSCTX_INPROC_SERVER, out appManager).ThrowOnFailure();
using var handle = new SafeComHandle((IntPtr)appManager);
appManager->ActivateApplication(
aumid,
string.Empty,
ACTIVATEOPTIONS.AO_NONE,
out var unusedPid);
}
catch (System.Exception ex)
{
Logger.LogError(ex.Message);
}
appManager.ActivateApplication(aumid, /*queryArguments*/ string.Empty, noFlags, out var unusedPid);
}
catch (System.Exception ex)
{
Logger.LogError(ex.Message);
}
}).ConfigureAwait(false);
}
internal static async Task StartExe(string path)
{
var appManager = new ApplicationActivationManager();
// const ActivateOptions noFlags = ActivateOptions.None;
await Task.Run(() =>
{
try

View File

@@ -1,13 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
<PropertyGroup>
<RootNamespace>Microsoft.CmdPal.Ext.Apps</RootNamespace>
<Nullable>enable</Nullable>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<DisableRuntimeMarshalling>true</DisableRuntimeMarshalling>
</PropertyGroup>
<ItemGroup>
@@ -51,9 +49,4 @@
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="NativeMethods.txt" />
<AdditionalFiles Include="NativeMethods.json" />
</ItemGroup>
</Project>

View File

@@ -1,7 +0,0 @@
{
"$schema": "https://aka.ms/CsWin32.schema.json",
"allowMarshaling": false,
"comInterop": {
"preserveSigMethods": [ "*" ]
}
}

View File

@@ -1,20 +1,19 @@
IStream
GetPhysicallyInstalledSystemMemory
GlobalMemoryStatusEx
GetSystemInfo
CoCreateInstance
IApplicationActivationManager
ApplicationActivationManager
SetForegroundWindow
IsIconic
RegisterHotKey
SetWindowLongPtr
CallWindowProc
ShowWindow
SetForegroundWindow
SetFocus
SetActiveWindow
MonitorFromWindow
GetMonitorInfo
SHCreateStreamOnFileEx
SHCreateItemFromParsingName
IShellItem
ISequentialStream
SHLoadIndirectString
IAppxFactory
AppxFactory
IAppxManifestReader
IAppxManifestApplicationsEnumerator
IAppxManifestApplication
IAppxManifestProperties
IShellLinkW
ShellLink
IPersistFile
CoTaskMemFree
IUnknown
CoAllowSetForegroundWindow
SHCreateStreamOnFileEx
SHLoadIndirectString

View File

@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
namespace Microsoft.CmdPal.Ext.Apps.Programs;
// Reference : https://stackoverflow.com/questions/32122679/getting-icon-of-modern-windows-app-from-a-desktop-application
[Guid("5842a140-ff9f-4166-8f5c-62f5b7b0c781")]
[ComImport]
public class AppxFactory
{
}

View File

@@ -2,75 +2,42 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Apps.Utils;
using Microsoft.UI.Xaml.Controls;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Storage.Packaging.Appx;
using System.Runtime.InteropServices;
using Windows.Win32.System.Com;
using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
namespace Microsoft.CmdPal.Ext.Apps.Programs;
public static class AppxPackageHelper
{
internal static unsafe List<IntPtr> GetAppsFromManifest(IStream* stream)
private static readonly IAppxFactory AppxFactory = (IAppxFactory)new AppxFactory();
// This function returns a list of attributes of applications
internal static IEnumerable<IAppxManifestApplication> GetAppsFromManifest(IStream stream)
{
PInvoke.CoCreateInstance(typeof(AppxFactory).GUID, null, CLSCTX.CLSCTX_INPROC_SERVER, out IAppxFactory* appxFactory).ThrowOnFailure();
using var handle = new SafeComHandle((IntPtr)appxFactory);
var reader = AppxFactory.CreateManifestReader(stream);
var manifestApps = reader.GetApplications();
IAppxManifestReader* reader = null;
IAppxManifestApplicationsEnumerator* manifestApps = null;
var result = new List<IntPtr>();
appxFactory->CreateManifestReader(stream, &reader);
using var readerHandle = new SafeComHandle((IntPtr)reader);
reader->GetApplications(&manifestApps);
using var manifestAppsHandle = new SafeComHandle((IntPtr)manifestApps);
while (true)
while (manifestApps.GetHasCurrent())
{
manifestApps->GetHasCurrent(out var hasCurrent);
if (hasCurrent == false)
var manifestApp = manifestApps.GetCurrent();
var hr = manifestApp.GetStringValue("AppListEntry", out var appListEntry);
_ = CheckHRAndReturnOrThrow(hr, appListEntry);
if (appListEntry != "none")
{
break;
yield return manifestApp;
}
IAppxManifestApplication* manifestApp = null;
manifestApps.MoveNext();
}
}
try
{
manifestApps->GetCurrent(&manifestApp).ThrowOnFailure();
var hr = manifestApp->GetStringValue("AppListEntry", out var appListEntryPtr);
var appListEntry = ComFreeHelper.GetStringAndFree(hr, appListEntryPtr);
if (appListEntry != "none")
{
result.Add((IntPtr)manifestApp);
}
else if (manifestApp != null)
{
manifestApp->Release();
}
}
catch (Exception ex)
{
if (manifestApp != null)
{
manifestApp->Release();
}
Logger.LogError($"Failed to get current application from manifest: {ex.Message}");
}
manifestApps->MoveNext(out var hasNext);
if (hasNext == false)
{
break;
}
internal static T CheckHRAndReturnOrThrow<T>(HRESULT hr, T result)
{
if (hr != HRESULT.S_OK)
{
Marshal.ThrowExceptionForHR((int)hr);
}
return result;

View File

@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Microsoft.CmdPal.Ext.Apps.Programs;
// Reference : https://github.com/MicrosoftEdge/edge-launcher/blob/108e63df0b4cb5cd9d5e45aa7a264690851ec51d/MIcrosoftEdgeLauncherCsharp/Program.cs
[Flags]
public enum ActivateOptions
{
None = 0x00000000,
DesignMode = 0x00000001,
NoErrorUI = 0x00000002,
NoSplashScreen = 0x00000004,
}
// ApplicationActivationManager
[ComImport]
[Guid("2e941141-7f97-4756-ba1d-9decde894a3d")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IApplicationActivationManager
{
IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId);
IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId);
IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId);
}
// Application Activation Manager Class
[ComImport]
[Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")]
public class ApplicationActivationManager : IApplicationActivationManager
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)/*, PreserveSig*/]
public extern IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public extern IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public extern IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId);
}

View File

@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using Windows.Win32.System.Com;
namespace Microsoft.CmdPal.Ext.Apps.Programs;
[Guid("BEB94909-E451-438B-B5A7-D79E767B75D8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAppxFactory
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Implements COM Interface")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Implements COM Interface")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")]
void _VtblGap0_2(); // skip 2 methods
internal IAppxManifestReader CreateManifestReader(IStream inputStream);
}

View File

@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
namespace Microsoft.CmdPal.Ext.Apps.Programs;
[Guid("5DA89BF4-3773-46BE-B650-7E744863B7E8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAppxManifestApplication
{
[PreserveSig]
HRESULT GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value);
[PreserveSig]
HRESULT GetAppUserModelId([MarshalAs(UnmanagedType.LPWStr)] out string value);
}

View File

@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Microsoft.CmdPal.Ext.Apps.Programs;
[Guid("9EB8A55A-F04B-4D0D-808D-686185D4847A")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAppxManifestApplicationsEnumerator
{
IAppxManifestApplication GetCurrent();
bool GetHasCurrent();
bool MoveNext();
}

View File

@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
namespace Microsoft.CmdPal.Ext.Apps.Programs;
[Guid("03FAF64D-F26F-4B2C-AAF7-8FE7789B8BCA")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAppxManifestProperties
{
[PreserveSig]
HRESULT GetBoolValue([MarshalAs(UnmanagedType.LPWStr)] string name, out bool value);
[PreserveSig]
HRESULT GetStringValue([MarshalAs(UnmanagedType.LPWStr)] string name, [MarshalAs(UnmanagedType.LPWStr)] out string value);
}

View File

@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Microsoft.CmdPal.Ext.Apps.Programs;
[Guid("4E1BD148-55A0-4480-A3D1-15544710637C")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAppxManifestReader
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Implements COM Interface")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Implements COM Interface")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")]
void _VtblGap0_1(); // skip 1 method
IAppxManifestProperties GetProperties();
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Implements COM Interface")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Implements COM Interface")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")]
void _VtblGap1_5(); // skip 5 methods
IAppxManifestApplicationsEnumerator GetApplications();
}

View File

@@ -5,7 +5,6 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
@@ -17,7 +16,7 @@ namespace Microsoft.CmdPal.Ext.Apps.Programs;
/// <summary>
/// Provides access to NTFS reparse points in .Net.
/// </summary>
public static partial class ReparsePoint
public static class ReparsePoint
{
#pragma warning disable SA1310 // Field names should not contain underscore
@@ -37,7 +36,7 @@ public static partial class ReparsePoint
#pragma warning restore SA1310 // Field names should not contain underscore
[Flags]
internal enum FileAccessType : uint
private enum FileAccessType : uint
{
DELETE = 0x00010000,
READ_CONTROL = 0x00020000,
@@ -101,7 +100,7 @@ public static partial class ReparsePoint
}
[Flags]
internal enum FileShareType : uint
private enum FileShareType : uint
{
None = 0x00000000,
Read = 0x00000001,
@@ -109,7 +108,7 @@ public static partial class ReparsePoint
Delete = 0x00000004,
}
internal enum CreationDisposition : uint
private enum CreationDisposition : uint
{
New = 1,
CreateAlways = 2,
@@ -119,7 +118,7 @@ public static partial class ReparsePoint
}
[Flags]
internal enum FileAttributes : uint
private enum FileAttributes : uint
{
Readonly = 0x00000001,
Hidden = 0x00000002,
@@ -196,9 +195,8 @@ public static partial class ReparsePoint
public AppExecutionAliasReparseTagBufferLayoutVersion Version;
}
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool DeviceIoControl(
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
IntPtr inBuffer,
@@ -208,8 +206,8 @@ public static partial class ReparsePoint
out int pBytesReturned,
IntPtr lpOverlapped);
[LibraryImport("kernel32.dll", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
internal static partial int CreateFile(
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr CreateFile(
string lpFileName,
FileAccessType dwDesiredAccess,
FileShareType dwShareMode,
@@ -286,18 +284,15 @@ public static partial class ReparsePoint
ThrowLastWin32Error("Unable to get information about reparse point.");
}
unsafe
AppExecutionAliasReparseTagHeader aliasReparseHeader = Marshal.PtrToStructure<AppExecutionAliasReparseTagHeader>(outBuffer);
if (aliasReparseHeader.ReparseTag == IO_REPARSE_TAG_APPEXECLINK)
{
var aliasReparseHeader = Unsafe.Read<AppExecutionAliasReparseTagHeader>((void*)outBuffer);
var metadata = AppExecutionAliasMetadata.FromPersistedRepresentationIntPtr(
outBuffer,
aliasReparseHeader.Version);
if (aliasReparseHeader.ReparseTag == IO_REPARSE_TAG_APPEXECLINK)
{
var metadata = AppExecutionAliasMetadata.FromPersistedRepresentationIntPtr(
outBuffer,
aliasReparseHeader.Version);
return metadata.ExePath;
}
return metadata.ExePath;
}
return null;
@@ -324,65 +319,61 @@ public static partial class ReparsePoint
public static AppExecutionAliasMetadata FromPersistedRepresentationIntPtr(IntPtr reparseDataBufferPtr, AppExecutionAliasReparseTagBufferLayoutVersion version)
{
unsafe
var dataOffset = Marshal.SizeOf<AppExecutionAliasReparseTagHeader>();
var dataBufferPtr = reparseDataBufferPtr + dataOffset;
string? packageFullName = null;
string? packageFamilyName = null;
string? aumid = null;
string? exePath = null;
VerifyVersion(version);
switch (version)
{
var dataOffset = Unsafe.SizeOf<AppExecutionAliasReparseTagHeader>();
case AppExecutionAliasReparseTagBufferLayoutVersion.Initial:
packageFullName = Marshal.PtrToStringUni(dataBufferPtr);
if (packageFullName is not null)
{
dataBufferPtr += Encoding.Unicode.GetByteCount(packageFullName) + Encoding.Unicode.GetByteCount("\0");
aumid = Marshal.PtrToStringUni(dataBufferPtr);
var dataBufferPtr = reparseDataBufferPtr + dataOffset;
string? packageFullName = null;
string? packageFamilyName = null;
string? aumid = null;
string? exePath = null;
VerifyVersion(version);
switch (version)
{
case AppExecutionAliasReparseTagBufferLayoutVersion.Initial:
packageFullName = Marshal.PtrToStringUni(dataBufferPtr);
if (packageFullName is not null)
if (aumid is not null)
{
dataBufferPtr += Encoding.Unicode.GetByteCount(packageFullName) + Encoding.Unicode.GetByteCount("\0");
aumid = Marshal.PtrToStringUni(dataBufferPtr);
if (aumid is not null)
{
dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0");
exePath = Marshal.PtrToStringUni(dataBufferPtr);
}
dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0");
exePath = Marshal.PtrToStringUni(dataBufferPtr);
}
}
break;
break;
case AppExecutionAliasReparseTagBufferLayoutVersion.PackageFamilyName:
case AppExecutionAliasReparseTagBufferLayoutVersion.MultiAppTypeSupport:
packageFamilyName = Marshal.PtrToStringUni(dataBufferPtr);
case AppExecutionAliasReparseTagBufferLayoutVersion.PackageFamilyName:
case AppExecutionAliasReparseTagBufferLayoutVersion.MultiAppTypeSupport:
packageFamilyName = Marshal.PtrToStringUni(dataBufferPtr);
if (packageFamilyName is not null)
if (packageFamilyName is not null)
{
dataBufferPtr += Encoding.Unicode.GetByteCount(packageFamilyName) + Encoding.Unicode.GetByteCount("\0");
aumid = Marshal.PtrToStringUni(dataBufferPtr);
if (aumid is not null)
{
dataBufferPtr += Encoding.Unicode.GetByteCount(packageFamilyName) + Encoding.Unicode.GetByteCount("\0");
aumid = Marshal.PtrToStringUni(dataBufferPtr);
dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0");
if (aumid is not null)
{
dataBufferPtr += Encoding.Unicode.GetByteCount(aumid) + Encoding.Unicode.GetByteCount("\0");
exePath = Marshal.PtrToStringUni(dataBufferPtr);
}
exePath = Marshal.PtrToStringUni(dataBufferPtr);
}
}
break;
}
return new AppExecutionAliasMetadata
{
PackageFullName = packageFullName ?? string.Empty,
PackageFamilyName = packageFamilyName ?? string.Empty,
Aumid = aumid ?? string.Empty,
ExePath = exePath ?? string.Empty,
};
break;
}
return new AppExecutionAliasMetadata
{
PackageFullName = packageFullName ?? string.Empty,
PackageFamilyName = packageFamilyName ?? string.Empty,
Aumid = aumid ?? string.Empty,
ExePath = exePath ?? string.Empty,
};
}
private static void VerifyVersion(AppExecutionAliasReparseTagBufferLayoutVersion version)

View File

@@ -3,19 +3,17 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Abstractions;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Apps.Utils;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Storage.Packaging.Appx;
using Windows.Win32.System.Com;
using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
namespace Microsoft.CmdPal.Ext.Apps.Programs;
@@ -57,7 +55,7 @@ public partial class UWP
FamilyName = package.FamilyName;
}
public unsafe void InitializeAppInfo(string installedLocation)
public void InitializeAppInfo(string installedLocation)
{
Location = installedLocation;
LocationLocalized = ShellLocalization.Instance.GetLocalizedPath(installedLocation);
@@ -67,31 +65,26 @@ public partial class UWP
InitPackageVersion(namespaces);
const uint noAttribute = 0x80;
const uint STGMREAD = 0x00000000;
try
{
IStream* stream = null;
PInvoke.SHCreateStreamOnFileEx(path, STGMREAD, noAttribute, false, null, &stream).ThrowOnFailure();
using var streamHandle = new SafeComHandle((IntPtr)stream);
Apps = AppxPackageHelper.GetAppsFromManifest(stream).Select(appInManifest =>
{
using var appHandle = new SafeComHandle(appInManifest);
return new UWPApplication((IAppxManifestApplication*)appInManifest, this);
}).Where(a =>
var access = (uint)STGM.READ;
var hResult = PInvoke.SHCreateStreamOnFileEx(path, access, noAttribute, false, null, out IStream stream);
// S_OK
if (hResult == 0)
{
Apps = AppxPackageHelper.GetAppsFromManifest(stream).Select(appInManifest => new UWPApplication(appInManifest, this)).Where(a =>
{
var valid =
!string.IsNullOrEmpty(a.UserModelId) &&
!string.IsNullOrEmpty(a.DisplayName) &&
a.AppListEntry != "none";
!string.IsNullOrEmpty(a.UserModelId) &&
!string.IsNullOrEmpty(a.DisplayName) &&
a.AppListEntry != "none";
return valid;
}).ToList();
}
catch (Exception ex)
else
{
Apps = Array.Empty<UWPApplication>();
Logger.LogError($"Failed to initialize UWP app info for {Name} ({FullName}): {ex.Message}");
return;
}
}
@@ -130,36 +123,35 @@ public partial class UWP
{
var windows10 = new Version(10, 0);
var support = Environment.OSVersion.Version.Major >= windows10.Major;
if (support)
{
var applications = CurrentUserPackages().AsParallel().SelectMany(p =>
{
UWP u;
try
{
u = new UWP(p);
u.InitializeAppInfo(p.InstalledLocation);
}
catch (Exception ex)
{
Logger.LogError(ex.Message);
return Array.Empty<UWPApplication>();
}
if (!support)
return u.Apps;
});
var updatedListWithoutDisabledApps = applications
.Where(t1 => AllAppsSettings.Instance.DisabledProgramSources.All(x => x.UniqueIdentifier != t1.UniqueIdentifier))
.Select(x => x);
return updatedListWithoutDisabledApps.ToArray();
}
else
{
return Array.Empty<UWPApplication>();
}
var appsBag = new ConcurrentBag<UWPApplication>();
Parallel.ForEach(CurrentUserPackages(), p =>
{
try
{
var u = new UWP(p);
u.InitializeAppInfo(p.InstalledLocation);
foreach (var app in u.Apps)
{
if (AllAppsSettings.Instance.DisabledProgramSources.All(x => x.UniqueIdentifier != app.UniqueIdentifier))
{
appsBag.Add(app);
}
}
}
catch (Exception ex)
{
Logger.LogError(ex.Message);
}
});
return appsBag.ToArray();
}
private static IEnumerable<IPackage> CurrentUserPackages()

View File

@@ -6,7 +6,6 @@ using System;
using System.Collections.Generic;
using System.IO.Abstractions;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
using ManagedCommon;
@@ -14,9 +13,7 @@ using Microsoft.CmdPal.Ext.Apps.Commands;
using Microsoft.CmdPal.Ext.Apps.Properties;
using Microsoft.CmdPal.Ext.Apps.Utils;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.Storage.Packaging.Appx;
using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
using PackageVersion = Microsoft.CmdPal.Ext.Apps.Programs.UWP.PackageVersion;
using Theme = Microsoft.CmdPal.Ext.Apps.Utils.Theme;
@@ -100,27 +97,27 @@ public class UWPApplication : IProgram
return commands;
}
internal unsafe UWPApplication(IAppxManifestApplication* manifestApp, UWP package)
public UWPApplication(IAppxManifestApplication manifestApp, UWP package)
{
ArgumentNullException.ThrowIfNull(manifestApp);
var hr = manifestApp->GetAppUserModelId(out var tmpUserModelIdPtr);
UserModelId = ComFreeHelper.GetStringAndFree(hr, tmpUserModelIdPtr);
var hr = manifestApp.GetAppUserModelId(out var tmpUserModelId);
UserModelId = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpUserModelId);
manifestApp->GetAppUserModelId(out var tmpUniqueIdentifierPtr);
UniqueIdentifier = ComFreeHelper.GetStringAndFree(hr, tmpUniqueIdentifierPtr);
hr = manifestApp.GetAppUserModelId(out var tmpUniqueIdentifier);
UniqueIdentifier = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpUniqueIdentifier);
manifestApp->GetStringValue("DisplayName", out var tmpDisplayNamePtr);
DisplayName = ComFreeHelper.GetStringAndFree(hr, tmpDisplayNamePtr);
hr = manifestApp.GetStringValue("DisplayName", out var tmpDisplayName);
DisplayName = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpDisplayName);
manifestApp->GetStringValue("Description", out var tmpDescriptionPtr);
Description = ComFreeHelper.GetStringAndFree(hr, tmpDescriptionPtr);
hr = manifestApp.GetStringValue("Description", out var tmpDescription);
Description = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpDescription);
manifestApp->GetStringValue("BackgroundColor", out var tmpBackgroundColorPtr);
BackgroundColor = ComFreeHelper.GetStringAndFree(hr, tmpBackgroundColorPtr);
hr = manifestApp.GetStringValue("BackgroundColor", out var tmpBackgroundColor);
BackgroundColor = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpBackgroundColor);
manifestApp->GetStringValue("EntryPoint", out var tmpEntryPointPtr);
EntryPoint = ComFreeHelper.GetStringAndFree(hr, tmpEntryPointPtr);
hr = manifestApp.GetStringValue("EntryPoint", out var tmpEntryPoint);
EntryPoint = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, tmpEntryPoint);
Package = package ?? throw new ArgumentNullException(nameof(package));
@@ -169,7 +166,7 @@ public class UWPApplication : IProgram
return false;
}
internal unsafe string ResourceFromPri(string packageFullName, string resourceReference)
internal string ResourceFromPri(string packageFullName, string resourceReference)
{
const string prefix = "ms-resource:";
@@ -203,8 +200,30 @@ public class UWPApplication : IProgram
parsedFallback = prefix + "///" + key;
}
if (string.IsNullOrEmpty(parsedFallback))
var outBuffer = new StringBuilder(128);
var source = $"@{{{packageFullName}? {parsed}}}";
var capacity = (uint)outBuffer.Capacity;
var hResult = SHLoadIndirectString(source, outBuffer, capacity, IntPtr.Zero);
if (hResult != HRESULT.S_OK)
{
if (!string.IsNullOrEmpty(parsedFallback))
{
var sourceFallback = $"@{{{packageFullName}? {parsedFallback}}}";
hResult = SHLoadIndirectString(sourceFallback, outBuffer, capacity, IntPtr.Zero);
if (hResult == HRESULT.S_OK)
{
var loaded = outBuffer.ToString();
if (!string.IsNullOrEmpty(loaded))
{
return loaded;
}
else
{
return string.Empty;
}
}
}
// https://github.com/Wox-launcher/Wox/issues/964
// known hresult 2147942522:
// 'Microsoft Corporation' violates pattern constraint of '\bms-resource:.{1,256}'.
@@ -213,40 +232,17 @@ public class UWPApplication : IProgram
// Microsoft.BingFoodAndDrink_3.0.4.336_x64__8wekyb3d8bbwe: ms-resource:AppDescription
return string.Empty;
}
var capacity = 1024U;
PWSTR outBuffer = new PWSTR((char*)(void*)Marshal.AllocHGlobal((int)capacity * sizeof(char)));
var source = $"@{{{packageFullName}? {parsed}}}";
void* reserved = null;
try
else
{
PInvoke.SHLoadIndirectString(source, outBuffer, capacity, ref reserved).ThrowOnFailure();
var loaded = outBuffer.ToString();
return string.IsNullOrEmpty(loaded) ? string.Empty : loaded;
}
catch (Exception)
{
try
if (!string.IsNullOrEmpty(loaded))
{
var sourceFallback = $"@{{{packageFullName}?{parsedFallback}}}";
PInvoke.SHLoadIndirectString(sourceFallback, outBuffer, capacity, ref reserved).ThrowOnFailure();
var loaded = outBuffer.ToString();
return string.IsNullOrEmpty(loaded) ? string.Empty : loaded;
return loaded;
}
catch (Exception)
else
{
// ProgramLogger.Exception($"Unable to load resource {resourceReference} from {packageFullName}", new InvalidOperationException(), GetType(), packageFullName);
return string.Empty;
}
finally
{
}
}
finally
{
Marshal.FreeHGlobal((IntPtr)outBuffer.Value);
}
}
else
@@ -262,12 +258,13 @@ public class UWPApplication : IProgram
{ PackageVersion.Windows8, "SmallLogo" },
};
internal unsafe string LogoUriFromManifest(IAppxManifestApplication* app)
internal string LogoUriFromManifest(IAppxManifestApplication app)
{
if (_logoKeyFromVersion.TryGetValue(Package.Version, out var key))
{
var hr = app->GetStringValue(key, out var logoUriFromAppPtr);
return ComFreeHelper.GetStringAndFree(hr, logoUriFromAppPtr);
var hr = app.GetStringValue(key, out var logoUriFromApp);
_ = AppxPackageHelper.CheckHRAndReturnOrThrow(hr, logoUriFromApp);
return logoUriFromApp;
}
else
{
@@ -352,7 +349,7 @@ public class UWPApplication : IProgram
var prefix = path.Substring(0, end);
var paths = new List<string> { };
const int appIconSize = 36;
var targetSizes = new List<int> { 16, 24, 30, 36, 44, 60, 72, 96, 128, 180, 256 };
var targetSizes = new List<int> { 16, 24, 30, 36, 44, 60, 72, 96, 128, 180, 256 }.AsParallel();
var pathFactorPairs = new Dictionary<string, int>();
foreach (var factor in targetSizes)

View File

@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Diagnostics;
@@ -842,69 +841,18 @@ public class Win32Program : IProgram
var disabledProgramsList = settings.DisabledProgramSources;
// Get all paths but exclude all normal .Executables
var pathBag = new ConcurrentBag<string>();
paths.UnionWith(sources
.AsParallel()
.SelectMany(source => source.IsEnabled ? source.GetPaths() : Enumerable.Empty<string>())
.Where(programPath => disabledProgramsList.All(x => x.UniqueIdentifier != programPath))
.Where(path => !ExecutableApplicationExtensions.Contains(Extension(path))));
runCommandPaths.UnionWith(runCommandSources
.AsParallel()
.SelectMany(source => source.IsEnabled ? source.GetPaths() : Enumerable.Empty<string>())
.Where(programPath => disabledProgramsList.All(x => x.UniqueIdentifier != programPath)));
Parallel.ForEach(sources, source =>
{
if (!source.IsEnabled)
{
return;
}
foreach (var path in source.GetPaths())
{
if (disabledProgramsList.All(x => x.UniqueIdentifier != path) &&
!ExecutableApplicationExtensions.Contains(Extension(path)))
{
pathBag.Add(path);
}
}
});
paths.UnionWith(pathBag);
var runCommandPathBag = new ConcurrentBag<string>();
Parallel.ForEach(runCommandSources, source =>
{
if (!source.IsEnabled)
{
return;
}
foreach (var path in source.GetPaths())
{
if (disabledProgramsList.All(x => x.UniqueIdentifier != path))
{
runCommandPathBag.Add(path);
}
}
});
runCommandPaths.UnionWith(runCommandPathBag);
var programsList = new ConcurrentBag<Win32Program>();
Parallel.ForEach(paths, source =>
{
var program = GetProgramFromPath(source);
if (program != null)
{
programsList.Add(program);
}
});
var runCommandProgramsList = new ConcurrentBag<Win32Program>();
Parallel.ForEach(runCommandPaths, source =>
{
var program = GetRunCommandProgramFromPath(source);
if (program != null)
{
runCommandProgramsList.Add(program);
}
});
var programs = programsList.ToList();
var runCommandPrograms = runCommandProgramsList.ToList();
var programs = paths.AsParallel().Select(source => GetProgramFromPath(source));
var runCommandPrograms = runCommandPaths.AsParallel().Select(source => GetRunCommandProgramFromPath(source));
return DeduplicatePrograms(programs.Concat(runCommandPrograms).Where(program => program?.Valid == true));
}

View File

@@ -69,6 +69,11 @@ public class ListRepository<T> : IRepository<T>, IEnumerable<T>
}
}
public ParallelQuery<T> AsParallel()
{
return _items.Values.AsParallel();
}
public bool Contains(T item)
{
if (item is not null)

View File

@@ -6,8 +6,8 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Abstractions;
using System.Threading.Tasks;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Apps.Programs;
@@ -15,9 +15,11 @@ using Win32Program = Microsoft.CmdPal.Ext.Apps.Programs.Win32Program;
namespace Microsoft.CmdPal.Ext.Apps.Storage;
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
internal sealed partial class Win32ProgramRepository : ListRepository<Programs.Win32Program>, IProgramRepository
{
private static readonly IFileSystem FileSystem = new FileSystem();
private static readonly IPath Path = FileSystem.Path;
private const string LnkExtension = ".lnk";
private const string UrlExtension = ".url";

View File

@@ -1,35 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.System.Com;
namespace Microsoft.CmdPal.Ext.Apps.Utils;
public static class ComFreeHelper
{
internal static unsafe string GetStringAndFree(HRESULT hr, PWSTR ptr)
{
hr.ThrowOnFailure();
try
{
return ptr.ToString();
}
finally
{
PInvoke.CoTaskMemFree(ptr);
}
}
public static unsafe void ComObjectRelease<T>(T* comPtr)
where T : unmanaged
{
if (comPtr != null)
{
((IUnknown*)comPtr)->Release();
}
}
}

View File

@@ -0,0 +1,157 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using System.Text;
namespace Microsoft.CmdPal.Ext.Apps.Utils;
[SuppressMessage("Interoperability", "CA1401:P/Invokes should not be visible", Justification = "We want plugins to share this NativeMethods class, instead of each one creating its own.")]
public sealed class Native
{
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, int cchOutBuf, nint ppvReserved);
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string path, nint pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem);
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern HRESULT SHCreateStreamOnFileEx(string fileName, STGM grfMode, uint attributes, bool create, System.Runtime.InteropServices.ComTypes.IStream reserved, out System.Runtime.InteropServices.ComTypes.IStream stream);
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern HRESULT SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, nint ppvReserved);
public enum HRESULT : uint
{
/// <summary>
/// Operation successful.
/// </summary>
S_OK = 0x00000000,
/// <summary>
/// Operation successful. (negative condition/no operation)
/// </summary>
S_FALSE = 0x00000001,
/// <summary>
/// Not implemented.
/// </summary>
E_NOTIMPL = 0x80004001,
/// <summary>
/// No such interface supported.
/// </summary>
E_NOINTERFACE = 0x80004002,
/// <summary>
/// Pointer that is not valid.
/// </summary>
E_POINTER = 0x80004003,
/// <summary>
/// Operation aborted.
/// </summary>
E_ABORT = 0x80004004,
/// <summary>
/// Unspecified failure.
/// </summary>
E_FAIL = 0x80004005,
/// <summary>
/// Unexpected failure.
/// </summary>
E_UNEXPECTED = 0x8000FFFF,
/// <summary>
/// General access denied error.
/// </summary>
E_ACCESSDENIED = 0x80070005,
/// <summary>
/// Handle that is not valid.
/// </summary>
E_HANDLE = 0x80070006,
/// <summary>
/// Failed to allocate necessary memory.
/// </summary>
E_OUTOFMEMORY = 0x8007000E,
/// <summary>
/// One or more arguments are not valid.
/// </summary>
E_INVALIDARG = 0x80070057,
/// <summary>
/// The operation was canceled by the user. (Error source 7 means Win32.)
/// </summary>
/// <SeeAlso href="https://learn.microsoft.com/windows/win32/debug/system-error-codes--1000-1299-"/>
/// <SeeAlso href="https://en.wikipedia.org/wiki/HRESULT"/>
E_CANCELLED = 0x800704C7,
}
public static class ShellItemTypeConstants
{
/// <summary>
/// Guid for type IShellItem.
/// </summary>
public static readonly Guid ShellItemGuid = new("43826d1e-e718-42ee-bc55-a1e261c37bfe");
/// <summary>
/// Guid for type IShellItem2.
/// </summary>
public static readonly Guid ShellItem2Guid = new("7E9FB0D3-919F-4307-AB2E-9B1860310C93");
}
/// <summary>
/// The following are ShellItem DisplayName types.
/// </summary>
[Flags]
public enum SIGDN : uint
{
NORMALDISPLAY = 0,
PARENTRELATIVEPARSING = 0x80018001,
PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
DESKTOPABSOLUTEPARSING = 0x80028000,
PARENTRELATIVEEDITING = 0x80031001,
DESKTOPABSOLUTEEDITING = 0x8004c000,
FILESYSPATH = 0x80058000,
URL = 0x80068000,
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
public interface IShellItem
{
void BindToHandler(
nint pbc,
[MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
[MarshalAs(UnmanagedType.LPStruct)] Guid riid,
out nint ppv);
void GetParent(out IShellItem ppsi);
void GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
void Compare(IShellItem psi, uint hint, out int piOrder);
}
/// <summary>
/// <see href="https://learn.microsoft.com/windows/win32/stg/stgm-constants">see all STGM values</see>
/// </summary>
[Flags]
public enum STGM : long
{
READ = 0x00000000L,
WRITE = 0x00000001L,
READWRITE = 0x00000002L,
CREATE = 0x00001000L,
}
}

View File

@@ -1,30 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Microsoft.CmdPal.Ext.Apps.Utils;
public partial class SafeComHandle : SafeHandle
{
public SafeComHandle()
: base(IntPtr.Zero, ownsHandle: true)
{
}
public SafeComHandle(IntPtr handle)
: base(IntPtr.Zero, ownsHandle: true)
{
SetHandle(handle);
}
public override bool IsInvalid => handle == IntPtr.Zero;
protected override bool ReleaseHandle()
{
var count = Marshal.Release(handle);
return true;
}
}

View File

@@ -3,17 +3,124 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using ManagedCommon;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.System.Com;
using Windows.Win32.UI.Shell;
namespace Microsoft.CmdPal.Ext.Apps.Utils;
public class ShellLinkHelper : IShellLinkHelper
{
[Flags]
private enum SLGP_FLAGS
{
SLGP_SHORTPATH = 0x1,
SLGP_UNCPRIORITY = 0x2,
SLGP_RAWPATH = 0x4,
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching COM")]
private struct WIN32_FIND_DATAW
{
public uint dwFileAttributes;
public long ftCreationTime;
public long ftLastAccessTime;
public long ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
[Flags]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Implements COM Interface")]
public enum SLR_FLAGS
{
SLR_NO_UI = 0x1,
SLR_ANY_MATCH = 0x2,
SLR_UPDATE = 0x4,
SLR_NOUPDATE = 0x8,
SLR_NOSEARCH = 0x10,
SLR_NOTRACK = 0x20,
SLR_NOLINKINFO = 0x40,
SLR_INVOKE_MSI = 0x80,
}
// Reference : http://www.pinvoke.net/default.aspx/Interfaces.IShellLinkW
// The IShellLink interface allows Shell links to be created, modified, and resolved
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214F9-0000-0000-C000-000000000046")]
private interface IShellLinkW
{
/// <summary>Retrieves the path and file name of a Shell link object</summary>
void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATAW pfd, SLGP_FLAGS fFlags);
/// <summary>Retrieves the list of item identifiers for a Shell link object</summary>
void GetIDList(out nint ppidl);
/// <summary>Sets the pointer to an item identifier list (PIDL) for a Shell link object.</summary>
void SetIDList(nint pidl);
/// <summary>Retrieves the description string for a Shell link object</summary>
void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName);
/// <summary>Sets the description for a Shell link object. The description can be any application-defined string</summary>
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
/// <summary>Retrieves the name of the working directory for a Shell link object</summary>
void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);
/// <summary>Sets the name of the working directory for a Shell link object</summary>
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
/// <summary>Retrieves the command-line arguments associated with a Shell link object</summary>
void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);
/// <summary>Sets the command-line arguments for a Shell link object</summary>
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
/// <summary>Retrieves the hot key for a Shell link object</summary>
void GetHotkey(out short pwHotkey);
/// <summary>Sets a hot key for a Shell link object</summary>
void SetHotkey(short wHotkey);
/// <summary>Retrieves the show command for a Shell link object</summary>
void GetShowCmd(out int piShowCmd);
/// <summary>Sets the show command for a Shell link object. The show command sets the initial show state of the window.</summary>
void SetShowCmd(int iShowCmd);
/// <summary>Retrieves the location (path and index) of the icon for a Shell link object</summary>
void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon);
/// <summary>Sets the location (path and index) of the icon for a Shell link object</summary>
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
/// <summary>Sets the relative path to the Shell link object</summary>
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved);
/// <summary>Attempts to find the target of a Shell link, even if it has been moved or renamed</summary>
void Resolve(ref nint hwnd, SLR_FLAGS fFlags);
/// <summary>Sets the path and file name of a Shell link object</summary>
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
[ComImport]
[Guid("00021401-0000-0000-C000-000000000046")]
private class ShellLink
{
}
// Contains the description of the app
public string Description { get; set; } = string.Empty;
@@ -23,63 +130,60 @@ public class ShellLinkHelper : IShellLinkHelper
public bool HasArguments { get; set; }
// Retrieve the target path using Shell Link
public unsafe string RetrieveTargetPath(string path)
public string RetrieveTargetPath(string path)
{
var target = string.Empty;
const int MAX_PATH = 260;
IShellLinkW* link = null;
var link = new ShellLink();
const int STGM_READ = 0;
PInvoke.CoCreateInstance(typeof(ShellLink).GUID, null, CLSCTX.CLSCTX_INPROC_SERVER, out link).ThrowOnFailure();
using var linkHandle = new SafeComHandle((IntPtr)link);
const int STGMREAD = 0;
IPersistFile* persistFile = null;
Guid iid = typeof(IPersistFile).GUID;
((IUnknown*)link)->QueryInterface(&iid, (void**)&persistFile);
if (persistFile != null)
try
{
using var persistFileHandle = new SafeComHandle((IntPtr)persistFile);
try
{
persistFile->Load(path, STGMREAD);
}
catch (System.IO.FileNotFoundException)
{
// Log.Exception($"Failed to load {path}, {e.Message}", e, GetType());
return string.Empty;
}
((IPersistFile)link).Load(path, STGM_READ);
}
catch (System.IO.FileNotFoundException ex)
{
Logger.LogError(ex.Message);
return string.Empty;
}
var hwnd = HWND.Null;
const uint SLR_NO_UI = 0x1;
link->Resolve(hwnd, SLR_NO_UI);
var hwnd = default(nint);
((IShellLinkW)link).Resolve(ref hwnd, 0);
var buffer = stackalloc char[MAX_PATH];
const int MAX_PATH = 260;
var buffer = new StringBuilder(MAX_PATH);
var hr = link->GetPath((PWSTR)buffer, MAX_PATH, null, 0x1);
target = hr.Succeeded ? new string(buffer) : string.Empty;
var data = default(WIN32_FIND_DATAW);
((IShellLinkW)link).GetPath(buffer, buffer.Capacity, ref data, SLGP_FLAGS.SLGP_SHORTPATH);
var target = buffer.ToString();
// To set the app description
if (!string.IsNullOrEmpty(target))
{
var descBuffer = stackalloc char[MAX_PATH];
var desHr = link->GetDescription(descBuffer, MAX_PATH);
Description = desHr.Succeeded ? new string(descBuffer) : string.Empty;
buffer = new StringBuilder(MAX_PATH);
try
{
((IShellLinkW)link).GetDescription(buffer, MAX_PATH);
Description = buffer.ToString();
}
catch (Exception ex)
{
Logger.LogError(ex.Message);
Description = string.Empty;
}
var argsBuffer = stackalloc char[MAX_PATH];
var argHr = link->GetArguments(argsBuffer, MAX_PATH);
Arguments = argHr.Succeeded ? new string(argsBuffer) : string.Empty;
var argumentBuffer = new StringBuilder(MAX_PATH);
((IShellLinkW)link).GetArguments(argumentBuffer, argumentBuffer.Capacity);
Arguments = argumentBuffer.ToString();
// Set variable to true if the program takes in any arguments
if (Arguments.Length != 0)
if (argumentBuffer.Length != 0)
{
HasArguments = true;
}
}
// To release unmanaged memory
Marshal.ReleaseComObject(link);
return target;
}
}

View File

@@ -4,8 +4,7 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using Windows.Win32;
using Windows.Win32.UI.Shell;
using static Microsoft.CmdPal.Ext.Apps.Utils.Native;
namespace Microsoft.CmdPal.Ext.Apps.Utils;
@@ -24,7 +23,7 @@ public class ShellLocalization
/// </summary>
/// <param name="path">Path to the shell item (e. g. shortcut 'File Explorer.lnk').</param>
/// <returns>The localized name as string or <see cref="string.Empty"/>.</returns>
public unsafe string GetLocalizedName(string path)
public string GetLocalizedName(string path)
{
var lowerInvariantPath = path.ToLowerInvariant();
@@ -34,29 +33,18 @@ public class ShellLocalization
return value;
}
void* shellItemPtrVoid = null;
try
{
var retCode = PInvoke.SHCreateItemFromParsingName(path, null, typeof(IShellItem).GUID, out shellItemPtrVoid).ThrowOnFailure();
using var shellItemHandle = new SafeComHandle((IntPtr)shellItemPtrVoid);
IShellItem* shellItemPtr = (IShellItem*)shellItemPtrVoid;
var hr = shellItemPtr->GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out var filenamePtr);
var filename = ComFreeHelper.GetStringAndFree(hr, filenamePtr);
if (filename == null)
{
return string.Empty;
}
_ = _localizationCache.TryAdd(lowerInvariantPath, filename);
return filename;
}
catch (Exception)
var shellItemType = ShellItemTypeConstants.ShellItemGuid;
var retCode = SHCreateItemFromParsingName(path, nint.Zero, ref shellItemType, out var shellItem);
if (retCode != 0)
{
return string.Empty;
}
shellItem.GetDisplayName(SIGDN.NORMALDISPLAY, out var filename);
_ = _localizationCache.TryAdd(lowerInvariantPath, filename);
return filename;
}
/// <summary>

View File

@@ -2,10 +2,10 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.CmdPal.Ext.Bookmarks.Helpers;
using Microsoft.CmdPal.Ext.Bookmarks.Models;
using Microsoft.CmdPal.Ext.Bookmarks.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Foundation;
@@ -23,7 +23,6 @@ internal sealed partial class AddBookmarkForm : FormContent
_bookmark = bookmark;
var name = _bookmark?.Name ?? string.Empty;
var url = _bookmark?.Bookmark ?? string.Empty;
TemplateJson = $$"""
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
@@ -34,25 +33,25 @@ internal sealed partial class AddBookmarkForm : FormContent
"type": "Input.Text",
"style": "text",
"id": "name",
"label": {{JsonSerializer.Serialize(Resources.bookmarks_form_name_label, BookmarkSerializationContext.Default.String)}},
"label": "{{Resources.bookmarks_form_name_label}}",
"value": {{JsonSerializer.Serialize(name, BookmarkSerializationContext.Default.String)}},
"isRequired": true,
"errorMessage": "{{JsonSerializer.Serialize(Resources.bookmarks_form_name_required, BookmarkSerializationContext.Default.String)}}"
"errorMessage": "{{Resources.bookmarks_form_name_required}}"
},
{
"type": "Input.Text",
"style": "text",
"id": "bookmark",
"value": {{JsonSerializer.Serialize(url, BookmarkSerializationContext.Default.String)}},
"label": {{JsonSerializer.Serialize(Resources.bookmarks_form_bookmark_label, BookmarkSerializationContext.Default.String)}},
"label": "{{Resources.bookmarks_form_bookmark_label}}",
"isRequired": true,
"errorMessage": "{{JsonSerializer.Serialize(Resources.bookmarks_form_bookmark_required, BookmarkSerializationContext.Default.String)}}"
"errorMessage": "{{Resources.bookmarks_form_bookmark_required}}"
}
],
"actions": [
{
"type": "Action.Submit",
"title": {{JsonSerializer.Serialize(Resources.bookmarks_form_save, BookmarkSerializationContext.Default.String)}},
"title": "{{Resources.bookmarks_form_save}}",
"data": {
"name": "name",
"bookmark": "bookmark"
@@ -74,11 +73,33 @@ internal sealed partial class AddBookmarkForm : FormContent
// get the name and url out of the values
var formName = formInput["name"] ?? string.Empty;
var formBookmark = formInput["bookmark"] ?? string.Empty;
var hasPlaceholder = formBookmark.ToString().Contains('{') && formBookmark.ToString().Contains('}');
// Determine the type of the bookmark
string bookmarkType;
if (formBookmark.ToString().StartsWith("http://", StringComparison.OrdinalIgnoreCase) || formBookmark.ToString().StartsWith("https://", StringComparison.OrdinalIgnoreCase))
{
bookmarkType = "web";
}
else if (File.Exists(formBookmark.ToString()))
{
bookmarkType = "file";
}
else if (Directory.Exists(formBookmark.ToString()))
{
bookmarkType = "folder";
}
else
{
// Default to web if we can't determine the type
bookmarkType = "web";
}
var updated = _bookmark ?? new BookmarkData();
updated.Name = formName.ToString();
updated.Bookmark = formBookmark.ToString();
updated.Type = BookmarkTypeHelper.GetBookmarkTypeFromValue(formBookmark.ToString());
updated.Type = bookmarkType;
AddedCommand?.Invoke(this, updated);
return CommandResult.GoHome();

View File

@@ -2,7 +2,6 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.Ext.Bookmarks.Models;
using Microsoft.CmdPal.Ext.Bookmarks.Properties;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;

View File

@@ -4,7 +4,7 @@
using System.Text.Json.Serialization;
namespace Microsoft.CmdPal.Ext.Bookmarks.Models;
namespace Microsoft.CmdPal.Ext.Bookmarks;
public class BookmarkData
{
@@ -12,8 +12,7 @@ public class BookmarkData
public string Bookmark { get; set; } = string.Empty;
[JsonConverter(typeof(JsonStringEnumConverter<BookmarkType>))]
public BookmarkType Type { get; set; } = BookmarkType.Web;
public string Type { get; set; } = string.Empty;
[JsonIgnore]
public bool IsPlaceholder => Bookmark.Contains('{') && Bookmark.Contains('}');

View File

@@ -7,14 +7,12 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Bookmarks.Command;
using Microsoft.CmdPal.Ext.Bookmarks.Models;
using Microsoft.CmdPal.Ext.Bookmarks.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.System;
namespace Microsoft.CmdPal.Ext.Bookmarks;
@@ -26,14 +24,10 @@ internal sealed partial class BookmarkPlaceholderForm : FormContent
private readonly string _bookmark = string.Empty;
private BookmarkType _bookmarkType;
// TODO pass in an array of placeholders
public BookmarkPlaceholderForm(string name, string url, BookmarkType bookmarkType)
public BookmarkPlaceholderForm(string name, string url, string type)
{
_bookmark = url;
_bookmarkType = bookmarkType;
var r = new Regex(Regex.Escape("{") + "(.*?)" + Regex.Escape("}"));
var matches = r.Matches(url);
_placeholderNames = matches.Select(m => m.Groups[1].Value).ToList();
@@ -44,10 +38,10 @@ internal sealed partial class BookmarkPlaceholderForm : FormContent
{
"type": "Input.Text",
"style": "text",
"id": "{{JsonSerializer.Serialize(p, BookmarkSerializationContext.Default.String)}}",
"label": "{{JsonSerializer.Serialize(p, BookmarkSerializationContext.Default.String)}}",
"id": "{{p}}",
"label": "{{p}}",
"isRequired": true,
"errorMessage": "{{JsonSerializer.Serialize(errorMessage, BookmarkSerializationContext.Default.String)}}"
"errorMessage": "{{errorMessage}}"
}
""";
}).ToList();
@@ -65,7 +59,7 @@ internal sealed partial class BookmarkPlaceholderForm : FormContent
"actions": [
{
"type": "Action.Submit",
"title": {{JsonSerializer.Serialize(Resources.bookmarks_form_open, BookmarkSerializationContext.Default.String)}},
"title": "{{Resources.bookmarks_form_open}}",
"data": {
"placeholder": "placeholder"
}
@@ -96,7 +90,15 @@ internal sealed partial class BookmarkPlaceholderForm : FormContent
try
{
return ShellCommand.Invoke(_bookmarkType, target);
var uri = UrlCommand.GetUri(target);
if (uri != null)
{
_ = Launcher.LaunchUriAsync(uri);
}
else
{
// throw new UriFormatException("The provided URL is not valid.");
}
}
catch (Exception ex)
{

View File

@@ -2,8 +2,6 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CmdPal.Ext.Bookmarks.Helpers;
using Microsoft.CmdPal.Ext.Bookmarks.Models;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
@@ -20,10 +18,10 @@ internal sealed partial class BookmarkPlaceholderPage : ContentPage
{
}
public BookmarkPlaceholderPage(string name, string url, BookmarkType type)
public BookmarkPlaceholderPage(string name, string url, string type)
{
Name = name;
Icon = IconHelper.CreateIcon(url, type, true);
Icon = new IconInfo(UrlCommand.IconFromUrl(url, type));
_bookmarkPlaceholder = new BookmarkPlaceholderForm(name, url, type);
}
}

View File

@@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
using Microsoft.CmdPal.Ext.Bookmarks.Models;
namespace Microsoft.CmdPal.Ext.Bookmarks;

View File

@@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Microsoft.CmdPal.Ext.Bookmarks.Models;
namespace Microsoft.CmdPal.Ext.Bookmarks;

View File

@@ -8,14 +8,10 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Bookmarks.Helpers;
using Microsoft.CmdPal.Ext.Bookmarks.Models;
using Microsoft.CmdPal.Ext.Bookmarks.Properties;
using Microsoft.CmdPal.Ext.Indexer;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Microsoft.Diagnostics.Utilities;
using Windows.Foundation;
namespace Microsoft.CmdPal.Ext.Bookmarks;
@@ -27,6 +23,10 @@ public partial class BookmarksCommandProvider : CommandProvider
private Bookmarks? _bookmarks;
public static IconInfo DeleteIcon { get; private set; } = new("\uE74D"); // Delete
public static IconInfo EditIcon { get; private set; } = new("\uE70F"); // Edit
public BookmarksCommandProvider()
{
Id = "Bookmarks";
@@ -109,23 +109,57 @@ public partial class BookmarksCommandProvider : CommandProvider
private CommandItem BookmarkToCommandItem(BookmarkData bookmark)
{
var deleteAction = () =>
ICommand command = bookmark.IsPlaceholder ?
new BookmarkPlaceholderPage(bookmark) :
new UrlCommand(bookmark);
var listItem = new CommandItem(command) { Icon = command.Icon };
List<CommandContextItem> contextMenu = [];
// Add commands for folder types
if (command is UrlCommand urlCommand)
{
if (_bookmarks != null)
if (urlCommand.Type == "folder")
{
ExtensionHost.LogMessage($"Deleting bookmark ({bookmark.Name},{bookmark.Bookmark})");
contextMenu.Add(
new CommandContextItem(new DirectoryPage(urlCommand.Url)));
_bookmarks.Data.Remove(bookmark);
SaveAndUpdateCommands();
contextMenu.Add(
new CommandContextItem(new OpenInTerminalCommand(urlCommand.Url)));
}
};
if (bookmark.IsPlaceholder)
{
return CreatePlaceholderCommand(bookmark, Edit_AddedCommand, deleteAction);
listItem.Subtitle = urlCommand.Url;
}
return CreateShellCommand(bookmark, Edit_AddedCommand, deleteAction);
var edit = new AddBookmarkPage(bookmark) { Icon = EditIcon };
edit.AddedCommand += Edit_AddedCommand;
contextMenu.Add(new CommandContextItem(edit));
var delete = new CommandContextItem(
title: Resources.bookmarks_delete_title,
name: Resources.bookmarks_delete_name,
action: () =>
{
if (_bookmarks != null)
{
ExtensionHost.LogMessage($"Deleting bookmark ({bookmark.Name},{bookmark.Bookmark})");
_bookmarks.Data.Remove(bookmark);
SaveAndUpdateCommands();
}
},
result: CommandResult.KeepOpen())
{
IsCritical = true,
Icon = DeleteIcon,
};
contextMenu.Add(delete);
listItem.MoreCommands = contextMenu.ToArray();
return listItem;
}
public override ICommandItem[] TopLevelCommands()
@@ -146,64 +180,4 @@ public partial class BookmarksCommandProvider : CommandProvider
// now, the state is just next to the exe
return System.IO.Path.Combine(directory, "bookmarks.json");
}
private static CommandItem CreatePlaceholderCommand(BookmarkData bookmark, TypedEventHandler<object, BookmarkData> addBookmarkFunc, Action deleteAction)
{
var command = new BookmarkPlaceholderPage(bookmark);
var listItem = new CommandItem(command) { Icon = command.Icon };
List<CommandContextItem> contextMenu = [];
var edit = new AddBookmarkPage(bookmark) { Icon = IconHelper.EditIcon };
edit.AddedCommand += addBookmarkFunc;
contextMenu.Add(new CommandContextItem(edit));
var delete = new CommandContextItem(
title: Resources.bookmarks_delete_title,
name: Resources.bookmarks_delete_name,
action: deleteAction,
result: CommandResult.KeepOpen())
{
IsCritical = true,
Icon = IconHelper.DeleteIcon,
};
contextMenu.Add(delete);
listItem.MoreCommands = contextMenu.ToArray();
return listItem;
}
private static CommandItem CreateShellCommand(BookmarkData bookmark, TypedEventHandler<object, BookmarkData> addBookmarkFunc, Action deleteAction)
{
var invokableCommand = new Command.ShellCommand(bookmark);
var listItem = new CommandItem(invokableCommand) { Icon = invokableCommand.Icon };
List<CommandContextItem> contextMenu = [];
if (bookmark.Type == BookmarkType.Folder)
{
contextMenu.Add(
new CommandContextItem(new DirectoryPage(bookmark.Bookmark)));
contextMenu.Add(
new CommandContextItem(new OpenInTerminalCommand(bookmark.Bookmark)));
}
listItem.Subtitle = invokableCommand.BookmarkData.Bookmark;
var edit = new AddBookmarkPage(bookmark) { Icon = IconHelper.EditIcon };
edit.AddedCommand += addBookmarkFunc;
contextMenu.Add(new CommandContextItem(edit));
var delete = new CommandContextItem(
title: Resources.bookmarks_delete_title,
name: Resources.bookmarks_delete_name,
action: deleteAction,
result: CommandResult.KeepOpen())
{
IsCritical = true,
Icon = IconHelper.DeleteIcon,
};
contextMenu.Add(delete);
listItem.MoreCommands = contextMenu.ToArray();
return listItem;
}
}

View File

@@ -1,96 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Data;
using Microsoft.CmdPal.Ext.Bookmarks.Helpers;
using Microsoft.CmdPal.Ext.Bookmarks.Models;
using Microsoft.CmdPal.Ext.Bookmarks.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.Bookmarks.Command;
public sealed partial class ShellCommand : InvokableCommand
{
public BookmarkData BookmarkData { get; }
public ShellCommand(BookmarkData data)
{
BookmarkData = data;
Name = data.Name;
Icon = IconHelper.CreateIcon(data.Bookmark, data.Type, false);
}
public override CommandResult Invoke()
{
return ShellCommand.Invoke(BookmarkData.Type, BookmarkData.Bookmark);
}
public static CommandResult Invoke(BookmarkType bookmarkType, string bookmarkValue)
{
if (bookmarkType == BookmarkType.Web)
{
var uri = OpenInShellHelper.GetUri(bookmarkValue);
if (uri != null)
{
if (!OpenInShellHelper.OpenInShell(uri.ToString(), null, null, OpenInShellHelper.ShellRunAsType.None, false, out var errMsg))
{
ExtensionHost.LogMessage($"Failed to open {bookmarkValue} in shell. Ex: {errMsg}");
return CommandResult.ShowToast(new ToastArgs() { Message = Resources.bookmarks_command_invoke_failed_message });
}
return CommandResult.Dismiss();
}
else
{
return CommandResult.ShowToast(new ToastArgs() { Message = Resources.bookmarks_command_invoke_failed_message });
}
}
// if it's a file or folder bookmark, call them directly.
if (bookmarkType == BookmarkType.File || bookmarkType == BookmarkType.Folder)
{
if (!OpenInShellHelper.OpenInShell(bookmarkValue, null, null, OpenInShellHelper.ShellRunAsType.None, false, out var errMsg))
{
ExtensionHost.LogMessage($"Failed to open {bookmarkValue} in shell. Ex: {errMsg}");
return CommandResult.ShowToast(new ToastArgs() { Message = Resources.bookmarks_command_invoke_failed_message });
}
return CommandResult.Dismiss();
}
// We assume all command bookmarks will follow the same format.
// For example: "python test.py" or "pwsh test.ps1"
// So, we can split the command and get the first part as the command name.
var splittedBookmarkValue = bookmarkValue.Split(" ");
if (splittedBookmarkValue.Length == 0)
{
ExtensionHost.LogMessage($"Failed to open {bookmarkValue} in shell. Empty bookmark value.");
return CommandResult.ShowToast(new ToastArgs() { Message = Resources.bookmarks_command_invoke_failed_message });
}
if (splittedBookmarkValue.Length == 1)
{
// directly call. Because it maybe a command with no args. eg: haproxy.exe or cmd.exe
if (!OpenInShellHelper.OpenInShell(splittedBookmarkValue[0], null, null, OpenInShellHelper.ShellRunAsType.None, false, out var errMsg))
{
ExtensionHost.LogMessage($"Failed to open {bookmarkValue} in shell. Ex: {errMsg}");
return CommandResult.ShowToast(new ToastArgs() { Message = Resources.bookmarks_command_invoke_failed_message });
}
return CommandResult.Dismiss();
}
// args = without the first part and join with space
var args = splittedBookmarkValue[1..];
if (!OpenInShellHelper.OpenInShell(splittedBookmarkValue[0], string.Join(" ", args), null, OpenInShellHelper.ShellRunAsType.None, false, out var errorMessage))
{
ExtensionHost.LogMessage($"Failed to open {bookmarkValue} in shell. Ex: {errorMessage}");
return CommandResult.ShowToast(new ToastArgs() { Message = Resources.bookmarks_command_invoke_failed_message });
}
return CommandResult.Dismiss();
}
}

View File

@@ -1,44 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.CmdPal.Ext.Bookmarks.Models;
namespace Microsoft.CmdPal.Ext.Bookmarks.Helpers;
public static partial class BookmarkTypeHelper
{
/*
* Summary:
* If it's a valid uri, we assume it's a Web Link.
* Otherwise, we check if it's a existing folder or file.
* By default, we assume it's a command type.
*/
public static BookmarkType GetBookmarkTypeFromValue(string bookmark)
{
// judge if the bookmark is a url
var uri = OpenInShellHelper.GetUri(bookmark);
if (uri?.Scheme == Uri.UriSchemeHttp || uri?.Scheme == Uri.UriSchemeHttps)
{
return BookmarkType.Web;
}
// judge if the bookmark is a existing folder
if (System.IO.Directory.Exists(bookmark))
{
return BookmarkType.Folder;
}
// ok, fine. Actually, it's also have the possibility to be a shell command.
// Such as 'test.cmd' or 'test.ps1'. Try to catch this case.
if (System.IO.File.Exists(bookmark))
{
return BookmarkType.File;
}
// by default. we assume it's a command type
return BookmarkType.Command;
}
}

View File

@@ -1,95 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.CmdPal.Ext.Bookmarks.Models;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Storage.Streams;
namespace Microsoft.CmdPal.Ext.Bookmarks.Helpers;
public static class IconHelper
{
public static IconInfo DeleteIcon { get; private set; } = new("\uE74D"); // Delete
public static IconInfo EditIcon { get; private set; } = new("\uE70F"); // Edit
public static IconInfo UrlIcon { get; private set; } = new("🔗"); // Web
public static IconInfo FolderIcon { get; private set; } = new("📁"); // Folder
public static IconInfo FileIcon { get; private set; } = new("📄"); // File
public static IconInfo CommandIcon { get; private set; } = new("\uE756"); // Command
public static IconInfo GetIconByType(BookmarkType type)
{
return type switch
{
BookmarkType.Web => UrlIcon,
BookmarkType.Folder => FolderIcon,
BookmarkType.File => FileIcon,
BookmarkType.Command => CommandIcon,
_ => UrlIcon, // Default icon
};
}
public static IconInfo CreateIcon(string bookmark, BookmarkType bookmarkType, bool isPlaceholder)
{
// In some case, we want to use placeholder, but we can still get the favicon.
// eg: "https://google.com?q={query}"
if (bookmarkType == BookmarkType.Web)
{
// Get the base url up to the first placeholder
var placeholderIndex = bookmark.IndexOf('{');
var baseString = placeholderIndex > 0 ? bookmark.Substring(0, placeholderIndex) : bookmark;
try
{
var uri = OpenInShellHelper.GetUri(baseString);
if (uri != null)
{
var hostname = uri.Host;
var faviconUrl = $"{uri.Scheme}://{hostname}/favicon.ico";
return new IconInfo(faviconUrl);
}
}
catch (UriFormatException ex)
{
ExtensionHost.LogMessage(new LogMessage() { Message = $"Create Icon failed. {ex}: {ex.Message}" });
}
}
if (isPlaceholder)
{
// If it's a placeholder bookmark, we don't need to get the icon.
// Just use the default icon.
return GetIconByType(bookmarkType);
}
if (bookmarkType == BookmarkType.File || bookmarkType == BookmarkType.Folder)
{
// try to get the file icon first. If not, use the default file icon.
try
{
// To be honest, I don't like to block thread.
// We need to refactor in the future.
// But now, it's ok. Only image file will trigger the async path.
var stream = ThumbnailHelper.GetThumbnail(bookmark).Result;
if (stream != null)
{
var data = new IconData(RandomAccessStreamReference.CreateFromStream(stream));
return new IconInfo(data, data);
}
}
catch
{
ExtensionHost.LogMessage(new LogMessage() { Message = $"Create Icon failed. {bookmarkType}: {bookmark}" });
}
}
// If we can't get the icon, just use the default icon.
return GetIconByType(bookmarkType);
}
}

View File

@@ -1,68 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Diagnostics;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.Bookmarks.Helpers;
// TODO: ok fine, we have two OpenInShellHelper classes, one in CmdPal and one in System.
// We should probably merge them into one. and move it to a common location.
public static partial class OpenInShellHelper
{
public static bool OpenInShell(string path, string? arguments, string? workingDir, ShellRunAsType runAs, bool runWithHiddenWindow, out string errorMessage)
{
errorMessage = string.Empty;
using var process = new Process();
process.StartInfo.FileName = path;
process.StartInfo.WorkingDirectory = string.IsNullOrWhiteSpace(workingDir) ? string.Empty : workingDir;
process.StartInfo.Arguments = string.IsNullOrWhiteSpace(arguments) ? string.Empty : arguments;
process.StartInfo.WindowStyle = runWithHiddenWindow ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Normal;
process.StartInfo.UseShellExecute = true;
if (runAs == ShellRunAsType.Administrator)
{
process.StartInfo.Verb = "RunAs";
}
else if (runAs == ShellRunAsType.OtherUser)
{
process.StartInfo.Verb = "RunAsUser";
}
try
{
process.Start();
return true;
}
catch (Win32Exception ex)
{
ExtensionHost.LogMessage(new LogMessage() { Message = $"Unable to open {path}: {ex.Message}" });
errorMessage = ex.Message;
return false;
}
}
public static Uri? GetUri(string url)
{
Uri? uri;
if (!Uri.TryCreate(url, UriKind.Absolute, out uri))
{
if (!Uri.TryCreate("https://" + url, UriKind.Absolute, out uri))
{
return null;
}
}
return uri;
}
public enum ShellRunAsType
{
None,
Administrator,
OtherUser,
}
}

View File

@@ -1,13 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CmdPal.Ext.Bookmarks.Models;
public enum BookmarkType
{
Web,
File,
Folder,
Command,
}

View File

@@ -4,7 +4,6 @@
using System;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Bookmarks.Helpers;
using Microsoft.CmdPal.Ext.Bookmarks.Properties;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
@@ -18,7 +17,6 @@ internal sealed partial class OpenInTerminalCommand : InvokableCommand
public OpenInTerminalCommand(string folder)
{
Name = Resources.bookmarks_open_in_terminal_name;
Icon = IconHelper.CommandIcon;
_folder = folder;
}
@@ -37,7 +35,7 @@ internal sealed partial class OpenInTerminalCommand : InvokableCommand
}
catch (Exception ex)
{
ExtensionHost.LogMessage(new LogMessage() { Message = $"Error launching Windows Terminal: {ex.Message}" });
Logger.LogError(ex.Message);
}
return CommandResult.Dismiss();

View File

@@ -78,15 +78,6 @@ namespace Microsoft.CmdPal.Ext.Bookmarks.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Unable to call command. Please check your configuration..
/// </summary>
public static string bookmarks_command_invoke_failed_message {
get {
return ResourceManager.GetString("bookmarks_command_invoke_failed_message", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Delete.
/// </summary>

View File

@@ -161,7 +161,4 @@
<value>{0} is required</value>
<comment>{0} will be replaced by a parameter name provided by the user</comment>
</data>
<data name="bookmarks_command_invoke_failed_message" xml:space="preserve">
<value>Unable to call command. Please check your configuration.</value>
</data>
</root>

View File

@@ -0,0 +1,99 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using ManagedCommon;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.System;
namespace Microsoft.CmdPal.Ext.Bookmarks;
public partial class UrlCommand : InvokableCommand
{
public string Type { get; }
public string Url { get; }
public UrlCommand(BookmarkData data)
: this(data.Name, data.Bookmark, data.Type)
{
}
public UrlCommand(string name, string url, string type)
{
Name = name;
Type = type;
Url = url;
Icon = new IconInfo(IconFromUrl(Url, type));
}
public override CommandResult Invoke()
{
var target = Url;
try
{
var uri = GetUri(target);
if (uri != null)
{
_ = Launcher.LaunchUriAsync(uri);
}
else
{
// throw new UriFormatException("The provided URL is not valid.");
}
}
catch (Exception ex)
{
Logger.LogError(ex.Message);
}
return CommandResult.Dismiss();
}
internal static Uri? GetUri(string url)
{
Uri? uri;
if (!Uri.TryCreate(url, UriKind.Absolute, out uri))
{
if (!Uri.TryCreate("https://" + url, UriKind.Absolute, out uri))
{
return null;
}
}
return uri;
}
internal static string IconFromUrl(string url, string type)
{
switch (type)
{
case "file":
return "📄";
case "folder":
return "📁";
case "web":
default:
// Get the base url up to the first placeholder
var placeholderIndex = url.IndexOf('{');
var baseString = placeholderIndex > 0 ? url.Substring(0, placeholderIndex) : url;
try
{
var uri = GetUri(baseString);
if (uri != null)
{
var hostname = uri.Host;
var faviconUrl = $"{uri.Scheme}://{hostname}/favicon.ico";
return faviconUrl;
}
}
catch (UriFormatException ex)
{
Logger.LogError(ex.Message);
}
return "🔗";
}
}
}

View File

@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Indexer.Data;
@@ -14,7 +13,6 @@ using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Shell;
using Windows.Win32.UI.WindowsAndMessaging;
using static Microsoft.CmdPal.Ext.Indexer.Native.NativeMethods;
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
@@ -29,16 +27,19 @@ internal sealed partial class OpenPropertiesCommand : InvokableCommand
try
{
var filenamePCWSTR = new PCWSTR((char*)filenamePtr);
var propertiesPCWSTR = new PCWSTR((char*)propertiesPtr);
var info = new SHELLEXECUTEINFOW
{
cbSize = (uint)sizeof(SHELLEXECUTEINFOW),
lpVerb = propertiesPtr,
lpFile = filenamePtr,
cbSize = (uint)Marshal.SizeOf<SHELLEXECUTEINFOW>(),
lpVerb = propertiesPCWSTR,
lpFile = filenamePCWSTR,
nShow = (int)SHOW_WINDOW_CMD.SW_SHOW,
fMask = NativeHelpers.SEEMASKINVOKEIDLIST,
};
return ShellExecuteEx(ref info);
return PInvoke.ShellExecuteEx(ref info);
}
finally
{

View File

@@ -2,8 +2,6 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.CmdPal.Ext.Indexer.Data;
using Microsoft.CmdPal.Ext.Indexer.Native;
@@ -13,7 +11,6 @@ using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.UI.Shell;
using Windows.Win32.UI.WindowsAndMessaging;
using static Microsoft.CmdPal.Ext.Indexer.Native.NativeMethods;
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
@@ -28,16 +25,19 @@ internal sealed partial class OpenWithCommand : InvokableCommand
try
{
var filenamePCWSTR = new PCWSTR((char*)filenamePtr);
var verbPCWSTR = new PCWSTR((char*)verbPtr);
var info = new SHELLEXECUTEINFOW
{
cbSize = (uint)sizeof(SHELLEXECUTEINFOW),
lpVerb = verbPtr,
lpFile = filenamePtr,
cbSize = (uint)Marshal.SizeOf<SHELLEXECUTEINFOW>(),
lpVerb = verbPCWSTR,
lpFile = filenamePCWSTR,
nShow = (int)SHOW_WINDOW_CMD.SW_SHOWNORMAL,
fMask = NativeHelpers.SEEMASKINVOKEIDLIST,
};
return ShellExecuteEx(ref info);
return PInvoke.ShellExecuteEx(ref info);
}
finally
{

View File

@@ -3,17 +3,17 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
using Microsoft.CmdPal.Ext.Indexer.Native;
using Windows.Win32;
using Windows.Win32.System.Com;
using Windows.Win32.System.Search;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer;
internal static class DataSourceManager
{
private static readonly Guid CLSIDCollatorDataSource = new("9E175B8B-F52A-11D8-B9A5-505054503030");
private static IDBInitialize _dataSource;
public static IDBInitialize GetDataSource()
@@ -28,36 +28,22 @@ internal static class DataSourceManager
private static bool InitializeDataSource()
{
var riid = typeof(IDBInitialize).GUID;
var hr = NativeMethods.CoCreateInstance(ref Unsafe.AsRef(in NativeHelpers.CsWin32GUID.CLSIDCollatorDataSource), IntPtr.Zero, NativeHelpers.CLSCTXINPROCALL, ref riid, out var dataSourceObjPtr);
var hr = PInvoke.CoCreateInstance(CLSIDCollatorDataSource, null, CLSCTX.CLSCTX_INPROC_SERVER, typeof(IDBInitialize).GUID, out var dataSourceObj);
if (hr != 0)
{
Logger.LogError("CoCreateInstance failed: " + hr);
return false;
}
if (dataSourceObjPtr == IntPtr.Zero)
{
Logger.LogError("CoCreateInstance failed: dataSourceObjPtr is null");
return false;
}
var comWrappers = new StrategyBasedComWrappers();
_dataSource = (IDBInitialize)comWrappers.GetOrCreateObjectForComInstance(dataSourceObjPtr, CreateObjectFlags.None);
if (_dataSource == null)
if (dataSourceObj == null)
{
Logger.LogError("CoCreateInstance failed: dataSourceObj is null");
return false;
}
_dataSource = (IDBInitialize)dataSourceObj;
_dataSource.Initialize();
if (dataSourceObjPtr != IntPtr.Zero)
{
Marshal.Release(dataSourceObjPtr);
}
return true;
}
}

View File

@@ -3,8 +3,6 @@
// See the LICENSE file in the project root for more information.
using System.Runtime.InteropServices;
using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
using Microsoft.CmdPal.Ext.Indexer.Native;
using Windows.Win32.Storage.IndexServer;
using Windows.Win32.System.Com.StructuredStorage;
@@ -18,6 +16,6 @@ internal struct DBPROP
public uint dwOptions;
public uint dwStatus;
public DBID colid;
public PropVariant vValue;
public PROPVARIANT vValue;
#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter
}

View File

@@ -4,38 +4,40 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB;
[ComImport]
[Guid("0c733a7c-2a1c-11ce-ade5-00aa0044773d")]
[GeneratedComInterface]
public partial interface IRowset
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IRowset
{
void AddRefRows(
[PreserveSig]
int AddRefRows(
uint cRows,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] rghRows,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] rgRefCounts,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgRowStatus);
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] rghRows,
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] rgRefCounts,
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgRowStatus);
void GetData(
[PreserveSig]
int GetData(
IntPtr hRow,
IntPtr hAccessor,
IntPtr pData);
void GetNextRows(
[PreserveSig]
int GetNextRows(
IntPtr hReserved,
long lRowsOffset,
long cRows,
out uint pcRowsObtained,
out IntPtr prghRows);
void ReleaseRows(
[PreserveSig]
int ReleaseRows(
uint cRows,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] rghRows,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] rghRows,
IntPtr rgRowOptions,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] rgRefCounts,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgRowStatus);
void RestartPosition(nuint hReserved);
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] uint[] rgRefCounts,
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgRowStatus);
}

View File

@@ -4,29 +4,29 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB;
[ComImport]
[Guid("0C733A55-2A1C-11CE-ADE5-00AA0044773D")]
[GeneratedComInterface]
public partial interface IRowsetInfo
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IRowsetInfo
{
[PreserveSig]
int GetProperties(
uint cPropertyIDSets,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] DBPROPIDSET[] rgPropertyIDSets,
[In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] DBPROPIDSET[] rgPropertyIDSets,
out ulong pcPropertySets,
out IntPtr prgPropertySets);
[PreserveSig]
int GetReferencedRowset(
uint iOrdinal,
ref Guid riid,
[MarshalAs(UnmanagedType.Interface)] out object ppReferencedRowset);
[In] ref Guid riid,
[Out, MarshalAs(UnmanagedType.Interface)] out object ppReferencedRowset);
[PreserveSig]
int GetSpecification(
ref Guid riid,
[MarshalAs(UnmanagedType.Interface)] out object ppSpecification);
[In] ref Guid riid,
[Out, MarshalAs(UnmanagedType.Interface)] out object ppSpecification);
}

View File

@@ -8,10 +8,12 @@ using System.Runtime.InteropServices;
using System.Threading;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB;
using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
using Microsoft.CmdPal.Ext.Indexer.Indexer.Utils;
using Microsoft.CmdPal.Ext.Indexer.Native;
using static Microsoft.CmdPal.Ext.Indexer.Native.NativeHelpers;
using Windows.Win32;
using Windows.Win32.System.Com;
using Windows.Win32.System.Search;
using Windows.Win32.UI.Shell.PropertiesSystem;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer;
@@ -118,6 +120,11 @@ internal sealed partial class SearchQuery : IDisposable
// We need to generate a search query string with the search text the user entered above
if (currentRowset != null)
{
if (reuseRowset != null)
{
Marshal.ReleaseComObject(reuseRowset);
}
// We have a previous rowset, this means the user is typing and we should store this
// recapture the where ID from this so the next ExecuteSync call will be faster
reuseRowset = currentRowset;
@@ -141,12 +148,13 @@ internal sealed partial class SearchQuery : IDisposable
private bool HandleRow(IGetRow getRow, nuint rowHandle)
{
object propertyStorePtr = null;
try
{
var riid = CsWin32GUID.PropertyStore;
getRow.GetRowFromHROW(null, rowHandle, ref riid, out var propertyStore);
getRow.GetRowFromHROW(null, rowHandle, typeof(IPropertyStore).GUID, out propertyStorePtr);
var propertyStore = (IPropertyStore)propertyStorePtr;
if (propertyStore == null)
{
Logger.LogError("Failed to get IPropertyStore interface");
@@ -168,6 +176,14 @@ internal sealed partial class SearchQuery : IDisposable
Logger.LogError("Error handling row", ex);
return false;
}
finally
{
// Ensure the COM object is released if not returned
if (propertyStorePtr != null)
{
Marshal.ReleaseComObject(propertyStorePtr);
}
}
}
public bool FetchRows(int offset, int limit)
@@ -178,17 +194,16 @@ internal sealed partial class SearchQuery : IDisposable
return false;
}
IGetRow getRow = null;
try
{
getRow = (IGetRow)currentRowset;
}
catch (Exception)
if (currentRowset is not IGetRow)
{
Logger.LogInfo("Reset the current rowset");
ExecuteSyncInternal();
getRow = (IGetRow)currentRowset;
}
if (currentRowset is not IGetRow getRow)
{
Logger.LogError("Rowset does not support IGetRow interface");
return false;
}
uint rowCountReturned;
@@ -196,7 +211,12 @@ internal sealed partial class SearchQuery : IDisposable
try
{
currentRowset.GetNextRows(IntPtr.Zero, offset, limit, out rowCountReturned, out prghRows);
var res = currentRowset.GetNextRows(IntPtr.Zero, offset, limit, out rowCountReturned, out prghRows);
if (res < 0)
{
Logger.LogError($"Error fetching rows: {res}");
return false;
}
if (rowCountReturned == 0)
{
@@ -217,7 +237,11 @@ internal sealed partial class SearchQuery : IDisposable
}
}
currentRowset.ReleaseRows(rowCountReturned, rowHandles, IntPtr.Zero, null, null);
res = currentRowset.ReleaseRows(rowCountReturned, rowHandles, IntPtr.Zero, null, null);
if (res != 0)
{
Logger.LogError($"Error releasing rows: {res}");
}
Marshal.FreeCoTaskMem(prghRows);
prghRows = IntPtr.Zero;
@@ -244,6 +268,11 @@ internal sealed partial class SearchQuery : IDisposable
var rowset = ExecuteCommand(queryStr);
if (rowset != null)
{
if (reuseRowset != null)
{
Marshal.ReleaseComObject(reuseRowset);
}
reuseRowset = rowset;
reuseWhereID = GetReuseWhereId(reuseRowset);
}
@@ -251,50 +280,110 @@ internal sealed partial class SearchQuery : IDisposable
private unsafe IRowset ExecuteCommand(string queryStr)
{
if (string.IsNullOrEmpty(queryStr))
{
return null;
}
object sessionPtr = null;
object commandPtr = null;
try
{
var session = (IDBCreateSession)DataSourceManager.GetDataSource();
var guid = typeof(IDBCreateCommand).GUID;
session.CreateSession(IntPtr.Zero, ref guid, out var ppDBSession);
if (ppDBSession == null)
session.CreateSession(null, typeof(IDBCreateCommand).GUID, out sessionPtr);
if (sessionPtr == null)
{
Logger.LogError("CreateSession failed");
return null;
}
var createCommand = (IDBCreateCommand)ppDBSession;
guid = typeof(ICommandText).GUID;
createCommand.CreateCommand(IntPtr.Zero, ref guid, out ICommandText commandText);
var createCommand = (IDBCreateCommand)sessionPtr;
createCommand.CreateCommand(null, typeof(ICommandText).GUID, out commandPtr);
if (commandPtr == null)
{
Logger.LogError("CreateCommand failed");
return null;
}
var commandText = (ICommandText)commandPtr;
if (commandText == null)
{
Logger.LogError("Failed to get ICommandText interface");
return null;
}
var riid = NativeHelpers.OleDb.DbGuidDefault;
commandText.SetCommandText(in NativeHelpers.OleDb.DbGuidDefault, queryStr);
commandText.Execute(null, typeof(IRowset).GUID, null, null, out var rowsetPointer);
var irowSetRiid = typeof(IRowset).GUID;
commandText.SetCommandText(ref riid, queryStr);
commandText.Execute(null, ref irowSetRiid, null, out var pcRowsAffected, out var rowsetPointer);
return rowsetPointer;
return rowsetPointer as IRowset;
}
catch (Exception ex)
{
Logger.LogError("Unexpected error.", ex);
return null;
}
finally
{
// Release the command pointer
if (commandPtr != null)
{
Marshal.ReleaseComObject(commandPtr);
}
// Release the session pointer
if (sessionPtr != null)
{
Marshal.ReleaseComObject(sessionPtr);
}
}
}
private unsafe DBPROP? GetPropset(IRowsetInfo rowsetInfo)
private IRowsetInfo GetRowsetInfo(IRowset rowset)
{
if (rowset == null)
{
return null;
}
var rowsetPtr = IntPtr.Zero;
var rowsetInfoPtr = IntPtr.Zero;
try
{
// Get the IUnknown pointer for the IRowset object
rowsetPtr = Marshal.GetIUnknownForObject(rowset);
// Query for IRowsetInfo interface
var rowsetInfoGuid = typeof(IRowsetInfo).GUID;
var res = Marshal.QueryInterface(rowsetPtr, in rowsetInfoGuid, out rowsetInfoPtr);
if (res != 0)
{
Logger.LogError($"Error getting IRowsetInfo interface: {res}");
return null;
}
// Marshal the interface pointer to the actual IRowsetInfo object
var rowsetInfo = (IRowsetInfo)Marshal.GetObjectForIUnknown(rowsetInfoPtr);
return rowsetInfo;
}
catch (Exception ex)
{
Logger.LogError($"Exception occurred while getting IRowsetInfo. ", ex);
return null;
}
finally
{
// Release the IRowsetInfo pointer if it was obtained
if (rowsetInfoPtr != IntPtr.Zero)
{
Marshal.Release(rowsetInfoPtr); // Release the IRowsetInfo pointer
}
// Release the IUnknown pointer for the IRowset object
if (rowsetPtr != IntPtr.Zero)
{
Marshal.Release(rowsetPtr);
}
}
}
private DBPROP? GetPropset(IRowsetInfo rowsetInfo)
{
var prgPropSetsPtr = IntPtr.Zero;
@@ -314,15 +403,16 @@ internal sealed partial class SearchQuery : IDisposable
return null;
}
var firstPropSetPtr = (DBPROPSET*)prgPropSetsPtr.ToInt64();
var propSet = *firstPropSetPtr;
var firstPropSetPtr = new IntPtr(prgPropSetsPtr.ToInt64());
var propSet = Marshal.PtrToStructure<DBPROPSET>(firstPropSetPtr);
if (propSet.cProperties == 0 || propSet.rgProperties == IntPtr.Zero)
{
return null;
}
var propPtr = (DBPROP*)propSet.rgProperties.ToInt64();
return *propPtr;
var propPtr = new IntPtr(propSet.rgProperties.ToInt64());
var prop = Marshal.PtrToStructure<DBPROP>(propPtr);
return prop;
}
catch (Exception ex)
{
@@ -341,8 +431,7 @@ internal sealed partial class SearchQuery : IDisposable
private uint GetReuseWhereId(IRowset rowset)
{
var rowsetInfo = (IRowsetInfo)rowset;
var rowsetInfo = GetRowsetInfo(rowset);
if (rowsetInfo == null)
{
return 0;
@@ -354,9 +443,9 @@ internal sealed partial class SearchQuery : IDisposable
return 0;
}
if (prop?.vValue.VarType == VarEnum.VT_UI4)
if (prop?.vValue.Anonymous.Anonymous.vt == VARENUM.VT_UI4)
{
var value = prop?.vValue._ulong;
var value = prop?.vValue.Anonymous.Anonymous.Anonymous.ulVal;
return (uint)value;
}
@@ -373,6 +462,18 @@ internal sealed partial class SearchQuery : IDisposable
Marshal.FreeCoTaskMem(dbPropIdSet.rgPropertyIDs);
}
if (reuseRowset != null)
{
Marshal.ReleaseComObject(reuseRowset);
reuseRowset = null;
}
if (currentRowset != null)
{
Marshal.ReleaseComObject(currentRowset);
currentRowset = null;
}
queryCompletedEvent?.Dispose();
}
}

View File

@@ -3,11 +3,12 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
using Microsoft.CmdPal.Ext.Indexer.Indexer.Utils;
using Microsoft.CmdPal.Ext.Indexer.Native;
using Windows.Win32.System.Com;
using Windows.Win32.System.Com.StructuredStorage;
using Windows.Win32.UI.Shell.PropertiesSystem;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer;
@@ -38,9 +39,14 @@ internal sealed class SearchResult
{
try
{
propStore.GetValue(NativeHelpers.PropertyKeys.PKEYItemNameDisplay, out var itemNameDisplay);
propStore.GetValue(NativeHelpers.PropertyKeys.PKEYItemUrl, out var itemUrl);
propStore.GetValue(NativeHelpers.PropertyKeys.PKEYKindText, out var kindText);
var key = NativeHelpers.PropertyKeys.PKEYItemNameDisplay;
propStore.GetValue(&key, out var itemNameDisplay);
key = NativeHelpers.PropertyKeys.PKEYItemUrl;
propStore.GetValue(&key, out var itemUrl);
key = NativeHelpers.PropertyKeys.PKEYKindText;
propStore.GetValue(&key, out var kindText);
var filePath = GetFilePath(ref itemUrl);
var isFolder = IsFoder(ref kindText);
@@ -61,34 +67,28 @@ internal sealed class SearchResult
}
}
private static bool IsFoder(ref PropVariant kindText)
private static bool IsFoder(ref PROPVARIANT kindText)
{
var kindString = GetStringFromPropVariant(ref kindText);
return string.Equals(kindString, "Folder", StringComparison.OrdinalIgnoreCase);
}
private static string GetFilePath(ref PropVariant itemUrl)
private static string GetFilePath(ref PROPVARIANT itemUrl)
{
var filePath = GetStringFromPropVariant(ref itemUrl);
filePath = UrlToFilePathConverter.Convert(filePath);
return filePath;
}
private static string GetStringFromPropVariant(ref PropVariant propVariant)
private static string GetStringFromPropVariant(ref PROPVARIANT propVariant)
{
if (propVariant.VarType == System.Runtime.InteropServices.VarEnum.VT_LPWSTR)
if (propVariant.Anonymous.Anonymous.vt == VARENUM.VT_LPWSTR)
{
var pwszVal = propVariant._ptr;
if (pwszVal == IntPtr.Zero)
var pwszVal = propVariant.Anonymous.Anonymous.Anonymous.pwszVal;
if (pwszVal != null)
{
return string.Empty;
return pwszVal.ToString();
}
// convert to string
var str = Marshal.PtrToStringUni(pwszVal);
return str;
}
return string.Empty;

View File

@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[CoClass(typeof(CSearchCatalogManagerClass))]
[Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF50")]
[ComImport]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1715:Identifiers should have correct prefix", Justification = "Using original name from type lib")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1302:Interface names should begin with I", Justification = "Using original name from type lib")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Using original name from type lib")]
public interface CSearchCatalogManager : ISearchCatalogManager
{
}

View File

@@ -0,0 +1,131 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[ComConversionLoss]
[Guid("AAB49DD5-AD0B-40AE-B654-AE8976BF6BD2")]
[ClassInterface((short)0)]
[ComImport]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1212:Property accessors should follow order", Justification = "The order of the property accessors must match the order in which the methods were defined in the vtable")]
public class CSearchCatalogManagerClass : ISearchCatalogManager, CSearchCatalogManager
{
[DispId(1610678272)]
public virtual extern string Name
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern IntPtr GetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void SetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName, [In] ref object pValue);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void GetCatalogStatus(
out object pStatus,
out object pPausedReason);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void Reset();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void Reindex();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void ReindexMatchingURLs([MarshalAs(UnmanagedType.LPWStr), In] string pszPattern);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void ReindexSearchRoot([MarshalAs(UnmanagedType.LPWStr), In] string pszRoot);
[DispId(1610678280)]
public virtual extern uint ConnectTimeout
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
[DispId(1610678282)]
public virtual extern uint DataTimeout
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern int NumberOfItems();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void NumberOfItemsToIndex(
out int plIncrementalCount,
out int plNotificationQueue,
out int plHighPriorityQueue);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public virtual extern string URLBeingIndexed();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern uint GetURLIndexingState([MarshalAs(UnmanagedType.LPWStr), In] string psz);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.Interface)]
public virtual extern object GetPersistentItemsChangedSink();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void RegisterViewForNotification(
[MarshalAs(UnmanagedType.LPWStr), In] string pszView,
[MarshalAs(UnmanagedType.Interface), In] object pViewChangedSink,
out uint pdwCookie);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void GetItemsChangedSink(
[MarshalAs(UnmanagedType.Interface), In] object pISearchNotifyInlineSite,
[In] ref Guid riid,
out IntPtr ppv,
out Guid pGUIDCatalogResetSignature,
out Guid pGUIDCheckPointSignature,
out uint pdwLastCheckPointNumber);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void UnregisterViewForNotification([In] uint dwCookie);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void SetExtensionClusion([MarshalAs(UnmanagedType.LPWStr), In] string pszExtension, [In] int fExclude);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.Interface)]
public virtual extern object EnumerateExcludedExtensions();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.Interface)]
public virtual extern CSearchQueryHelper GetQueryHelper();
[DispId(1610678295)]
public virtual extern int DiacriticSensitivity
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.Interface)]
public virtual extern object GetCrawlScopeManager();
}

View File

@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[CoClass(typeof(CSearchManagerClass))]
[Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF69")]
[ComImport]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1715:Identifiers should have correct prefix", Justification = "Using original name from type lib")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1302:Interface names should begin with I", Justification = "Using original name from type lib")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Using original name from type lib")]
public interface CSearchManager : ISearchManager
{
}

View File

@@ -0,0 +1,90 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[Guid("7D096C5F-AC08-4F1F-BEB7-5C22C517CE39")]
[TypeLibType(2)]
[ClassInterface((short)0)]
[ComConversionLoss]
[ComImport]
public class CSearchManagerClass : ISearchManager, CSearchManager
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void GetIndexerVersionStr([MarshalAs(UnmanagedType.LPWStr)] out string ppszVersionString);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void GetIndexerVersion(out uint pdwMajor, out uint pdwMinor);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern IntPtr GetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void SetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName, [In] ref object pValue);
[DispId(1610678276)]
public virtual extern string ProxyName
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
[DispId(1610678277)]
public virtual extern string BypassList
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void SetProxy(
[In] object sUseProxy,
[In] int fLocalByPassProxy,
[In] uint dwPortNumber,
[MarshalAs(UnmanagedType.LPWStr), In] string pszProxyName,
[MarshalAs(UnmanagedType.LPWStr), In] string pszByPassList);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.Interface)]
public virtual extern CSearchCatalogManager GetCatalog([MarshalAs(UnmanagedType.LPWStr), In] string pszCatalog);
[DispId(1610678280)]
public virtual extern string UserAgent
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: MarshalAs(UnmanagedType.LPWStr)]
[param: In]
set;
}
[DispId(1610678282)]
public virtual extern object UseProxy
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
[DispId(1610678283)]
public virtual extern int LocalBypass
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
[DispId(1610678284)]
public virtual extern uint PortNumber
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
}

View File

@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF63")]
[CoClass(typeof(CSearchQueryHelperClass))]
[ComImport]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1715:Identifiers should have correct prefix", Justification = "Using original name from type lib")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1302:Interface names should begin with I", Justification = "Using original name from type lib")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Using original name from type lib")]
public interface CSearchQueryHelper : ISearchQueryHelper
{
}

View File

@@ -0,0 +1,134 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[ClassInterface((short)0)]
[Guid("B271E955-09E1-42E1-9B95-5994A534B613")]
[ComImport]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1212:Property accessors should follow order", Justification = "The order of the property accessors must match the order in which the methods were defined in the vtable")]
public class CSearchQueryHelperClass : ISearchQueryHelper, CSearchQueryHelper
{
[DispId(1610678272)]
public virtual extern string ConnectionString
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
[DispId(1610678273)]
public virtual extern uint QueryContentLocale
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
[DispId(1610678275)]
public virtual extern uint QueryKeywordLocale
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
[DispId(1610678277)]
public virtual extern object QueryTermExpansion
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
[DispId(1610678279)]
public virtual extern object QuerySyntax
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
[DispId(1610678281)]
public virtual extern string QueryContentProperties
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: MarshalAs(UnmanagedType.LPWStr)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
[DispId(1610678283)]
public virtual extern string QuerySelectColumns
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: MarshalAs(UnmanagedType.LPWStr)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
[DispId(1610678285)]
public virtual extern string QueryWhereRestrictions
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: MarshalAs(UnmanagedType.LPWStr)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
[DispId(1610678287)]
public virtual extern string QuerySorting
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: MarshalAs(UnmanagedType.LPWStr)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public virtual extern string GenerateSQLFromUserQuery([MarshalAs(UnmanagedType.LPWStr), In] string pszQuery);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public virtual extern void WriteProperties(
[In] int itemID,
[In] uint dwNumberOfColumns,
[In] ref object pColumns,
[In] ref object pValues,
[In] ref object pftGatherModifiedTime);
[DispId(1610678291)]
public virtual extern int QueryMaxResults
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
}

View File

@@ -1,21 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[Guid("0C733A63-2A1C-11CE-ADE5-00AA0044773D")]
[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)]
public partial interface ICommand
{
void Cancel();
void Execute([MarshalAs(UnmanagedType.Interface)] object pUnkOuter, ref Guid riid, [Optional][MarshalAs(UnmanagedType.Interface)] object pParams, [Optional]out int pcRowsAffected, out IRowset ppRowset);
void GetDBSession(ref Guid riid, out IntPtr ppSession);
}

View File

@@ -1,23 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CmdPal.Ext.Indexer.Indexer.OleDB;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[Guid("0C733A27-2A1C-11CE-ADE5-00AA0044773D")]
[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)]
public partial interface ICommandText : ICommand
{
void GetCommandText([Optional] ref Guid pguidDialect, out IntPtr ppwszCommand);
void SetCommandText(ref Guid rguidDialect, string pwszCommand);
}

View File

@@ -1,16 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[Guid("0C733A1D-2A1C-11CE-ADE5-00AA0044773D")]
[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)]
public partial interface IDBCreateCommand
{
void CreateCommand(IntPtr pUnkOuter, ref Guid riid, out ICommandText ppCommand);
}

View File

@@ -1,20 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[Guid("0C733A5D-2A1C-11CE-ADE5-00AA0044773D")]
[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)]
public partial interface IDBCreateSession
{
void CreateSession(IntPtr pUnkOuter, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppDBSession);
}

View File

@@ -1,22 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[Guid("0C733A8B-2A1C-11CE-ADE5-00AA0044773D")]
[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)]
public partial interface IDBInitialize
{
void Initialize();
void Uninitialize();
}

View File

@@ -1,23 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CmdPal.Ext.Indexer.Native;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[Guid("0C733AAF-2A1C-11CE-ADE5-00AA0044773D")]
[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)]
public partial interface IGetRow
{
unsafe void GetRowFromHROW([MarshalAs(UnmanagedType.Interface)] object pUnkOuter, nuint hRow, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IPropertyStore ppUnk);
unsafe string GetURLFromHROW(nuint hRow);
}

View File

@@ -1,26 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
[assembly: System.Runtime.CompilerServices.DisableRuntimeMarshalling]
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99")]
[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)]
public partial interface IPropertyStore
{
uint GetCount();
PropertyKey GetAt(uint iProp);
void GetValue(in PropertyKey pkey, out PropVariant pv);
void SetValue(in PropertyKey pkey, in PropVariant pv);
void Commit();
}

View File

@@ -5,68 +5,125 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF50")]
[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Please do not change the function name")]
public partial interface ISearchCatalogManager
[ComConversionLoss]
[InterfaceType(1)]
[ComImport]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1212:Property accessors should follow order", Justification = "The order of the property accessors must match the order in which the methods were defined in the vtable")]
public interface ISearchCatalogManager
{
string get_Name();
[DispId(1610678272)]
string Name
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
void GetParameter(string pszName, out IntPtr pValue);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
IntPtr GetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName);
void SetParameter(string pszName, ref IntPtr pValue);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void SetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName, [In] ref object pValue);
void GetCatalogStatus(out uint pdwStatus, out uint pdwPausedReason);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetCatalogStatus(out object pStatus, out object pPausedReason);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void Reset();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void Reindex();
void ReindexMatchingURLs(string pszPattern);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void ReindexMatchingURLs([MarshalAs(UnmanagedType.LPWStr), In] string pszPattern);
void ReindexSearchRoot(string pszRoot);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void ReindexSearchRoot([MarshalAs(UnmanagedType.LPWStr), In] string pszRoot);
uint get_ConnectTimeout();
[DispId(1610678280)]
uint ConnectTimeout
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
void put_ConnectTimeout(uint dwTimeout);
[DispId(1610678282)]
uint DataTimeout
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
uint get_DataTimeout();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
int NumberOfItems();
void put_DataTimeout(uint dwTimeout);
uint NumberOfItems();
uint NumberOfItemsToIndex();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void NumberOfItemsToIndex(
out int plIncrementalCount,
out int plNotificationQueue,
out int plHighPriorityQueue);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
string URLBeingIndexed();
void GetURLIndexingState(string pszURL, out uint pdwState);
IntPtr GetPersistentItemsChangedSink();
void RegisterViewForNotification(string pszView, IntPtr pViewNotify, out uint pdwCookie);
IntPtr GetItemsChangedSink();
void UnregisterViewForNotification(uint dwCookie);
void SetExtensionClusion(string pszExtension, [MarshalAs(UnmanagedType.Bool)] bool fExclude);
void EnumerateExcludedExtensions();
[return: MarshalAs(UnmanagedType.Interface)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
ISearchQueryHelper GetQueryHelper();
uint GetURLIndexingState([MarshalAs(UnmanagedType.LPWStr), In] string psz);
[return: MarshalAs(UnmanagedType.Bool)]
bool get_DiacriticSensitivity();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.Interface)]
object GetPersistentItemsChangedSink();
void put_DiacriticSensitivity([MarshalAs(UnmanagedType.Bool)] bool fDiacriticSensitive);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void RegisterViewForNotification(
[MarshalAs(UnmanagedType.LPWStr), In] string pszView,
[MarshalAs(UnmanagedType.Interface), In] object pViewChangedSink,
out uint pdwCookie);
IntPtr GetCrawlScopeManager();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetItemsChangedSink(
[MarshalAs(UnmanagedType.Interface), In] object pISearchNotifyInlineSite,
[In] ref Guid riid,
out IntPtr ppv,
out Guid pGUIDCatalogResetSignature,
out Guid pGUIDCheckPointSignature,
out uint pdwLastCheckPointNumber);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void UnregisterViewForNotification([In] uint dwCookie);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void SetExtensionClusion([MarshalAs(UnmanagedType.LPWStr), In] string pszExtension, [In] int fExclude);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.Interface)]
object EnumerateExcludedExtensions();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.Interface)]
CSearchQueryHelper GetQueryHelper();
[DispId(1610678295)]
int DiacriticSensitivity
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.Interface)]
object GetCrawlScopeManager();
}

View File

@@ -3,46 +3,87 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[ComConversionLoss]
[Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF69")]
[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "Please do not change the function name")]
public partial interface ISearchManager
[InterfaceType(1)]
[ComImport]
public interface ISearchManager
{
string GetIndexerVersionStr();
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetIndexerVersionStr([MarshalAs(UnmanagedType.LPWStr)] out string ppszVersionString);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void GetIndexerVersion(out uint pdwMajor, out uint pdwMinor);
void GetParameter(string pszName, [MarshalAs(UnmanagedType.Interface)] out object pValue);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
IntPtr GetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName);
void SetParameter(string pszName, [MarshalAs(UnmanagedType.Interface)] ref object pValue);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void SetParameter([MarshalAs(UnmanagedType.LPWStr), In] string pszName, [In] ref object pValue);
[return: MarshalAs(UnmanagedType.Bool)]
bool get_UseProxy();
[DispId(1610678276)]
string ProxyName
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
string get_BypassList();
[DispId(1610678277)]
string BypassList
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void SetProxy(
string pszProxyName,
[MarshalAs(UnmanagedType.Bool)] bool fLocalBypass,
string pszBypassList,
uint dwPortNumber);
[In] object sUseProxy,
[In] int fLocalByPassProxy,
[In] uint dwPortNumber,
[MarshalAs(UnmanagedType.LPWStr), In] string pszProxyName,
[MarshalAs(UnmanagedType.LPWStr), In] string pszByPassList);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.Interface)]
ISearchCatalogManager GetCatalog(string pszCatalog);
CSearchCatalogManager GetCatalog([MarshalAs(UnmanagedType.LPWStr), In] string pszCatalog);
string get_UserAgent();
[DispId(1610678280)]
string UserAgent
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: MarshalAs(UnmanagedType.LPWStr)]
[param: In]
set;
}
void put_UserAgent(string pszUserAgent);
[DispId(1610678282)]
object UseProxy
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
string get_ProxyName();
[DispId(1610678283)]
int LocalBypass
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
[return: MarshalAs(UnmanagedType.Bool)]
bool get_LocalBypass();
uint get_PortNumber();
[DispId(1610678284)]
uint PortNumber
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
}

View File

@@ -5,74 +5,130 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF63")]
[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:Element should begin with upper-case letter", Justification = "I don't want to change the name")]
public partial interface ISearchQueryHelper
[InterfaceType(1)]
[ComImport]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1212:Property accessors should follow order", Justification = "The order of the property accessors must match the order in which the methods were defined in the vtable")]
public interface ISearchQueryHelper
{
string GetConnectionString();
[DispId(1610678272)]
string ConnectionString
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
void SetQueryContentLocale(int lcid);
[DispId(1610678273)]
uint QueryContentLocale
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
uint GetQueryContentLocale();
[DispId(1610678275)]
uint QueryKeywordLocale
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
void SetQueryKeywordLocale(int lcid);
[DispId(1610678277)]
object QueryTermExpansion
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
uint GetQueryKeywordLocale();
[DispId(1610678279)]
object QuerySyntax
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
void SetQueryTermExpansion(SEARCH_TERM_EXPANSION expandTerms);
[DispId(1610678281)]
string QueryContentProperties
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: MarshalAs(UnmanagedType.LPWStr)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
void GetQueryTermExpansion(out SEARCH_TERM_EXPANSION pExpandTerms);
[DispId(1610678283)]
string QuerySelectColumns
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: MarshalAs(UnmanagedType.LPWStr)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
void SetQuerySyntax(SEARCH_QUERY_SYNTAX querySyntax);
[DispId(1610678285)]
string QueryWhereRestrictions
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: MarshalAs(UnmanagedType.LPWStr)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
[return: MarshalAs(UnmanagedType.Interface)]
object GetQuerySyntax();
[DispId(1610678287)]
string QuerySorting
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: MarshalAs(UnmanagedType.LPWStr)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
get;
}
void SetQueryContentProperties(string pszContentProperties);
string GetQueryContentProperties();
void SetQuerySelectColumns(string pszColumns);
string GetQuerySelectColumns();
void SetQueryWhereRestrictions(string pszRestrictions);
string GetQueryWhereRestrictions();
void SetQuerySorting(string pszSorting);
string GetQuerySorting();
string GenerateSQLFromUserQuery(string pszQuery);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[return: MarshalAs(UnmanagedType.LPWStr)]
string GenerateSQLFromUserQuery([MarshalAs(UnmanagedType.LPWStr), In] string pszQuery);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
void WriteProperties(
int itemID,
uint dwNumberOfColumns,
[MarshalAs(UnmanagedType.Interface)] ref object pColumns,
[MarshalAs(UnmanagedType.Interface)] ref object pValues,
[MarshalAs(UnmanagedType.Interface)] ref object pftGatherModifiedTime);
[In] int itemID,
[In] uint dwNumberOfColumns,
[In] ref object pColumns,
[In] ref object pValues,
[In] ref object pftGatherModifiedTime);
void SetQueryMaxResults(int lMaxResults);
int GetQueryMaxResults();
}
public enum SEARCH_TERM_EXPANSION
{
SEARCH_TERM_NO_EXPANSION,
SEARCH_TERM_PREFIX_ALL,
SEARCH_TERM_STEM_ALL,
}
public enum SEARCH_QUERY_SYNTAX
{
SEARCH_NO_QUERY_SYNTAX,
SEARCH_ADVANCED_QUERY_SYNTAX,
SEARCH_NATURAL_QUERY_SYNTAX,
[DispId(1610678291)]
int QueryMaxResults
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[param: In]
set;
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
get;
}
}

View File

@@ -1,70 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using VARTYPE = System.Runtime.InteropServices.VarEnum;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter
[StructLayout(LayoutKind.Explicit, Pack = 8)]
public struct PropVariant
{
/// <summary>Value type tag.</summary>
[FieldOffset(0)]
public ushort vt;
[FieldOffset(2)]
public ushort wReserved1;
/// <summary>Reserved for future use.</summary>
[FieldOffset(4)]
public ushort wReserved2;
/// <summary>Reserved for future use.</summary>
[FieldOffset(6)]
public ushort wReserved3;
/// <summary>The decimal value when VT_DECIMAL.</summary>
[FieldOffset(0)]
internal decimal _decimal;
/// <summary>The raw data pointer.</summary>
[FieldOffset(8)]
internal IntPtr _ptr;
/// <summary>The FILETIME when VT_FILETIME.</summary>
[FieldOffset(8)]
internal System.Runtime.InteropServices.ComTypes.FILETIME _ft;
[FieldOffset(8)]
internal BLOB _blob;
/// <summary>The value when a numeric value less than 8 bytes.</summary>
[FieldOffset(8)]
internal ulong _ulong;
public PropVariant(string value)
{
ArgumentNullException.ThrowIfNull(value);
vt = (ushort)VarEnum.VT_LPWSTR;
_ptr = Marshal.StringToCoTaskMemUni(value);
}
public VarEnum VarType { get => (VarEnum)vt; set => vt = (ushort)(VARTYPE)value; }
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct BLOB
{
/// <summary>The count of bytes</summary>
public uint cbSize;
/// <summary>A pointer to the allocated array of bytes.</summary>
public IntPtr pBlobData;
}
#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter

View File

@@ -1,27 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
[StructLayout(LayoutKind.Sequential)]
public struct PropertyKey
{
public Guid FmtID;
public uint PID;
public PropertyKey(Guid fmtid, uint pid)
{
this.FmtID = fmtid;
this.PID = pid;
}
}

View File

@@ -2,17 +2,12 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
using Microsoft.CmdPal.Ext.Indexer.Native;
namespace Microsoft.CmdPal.Ext.Indexer.Indexer.Utils;
internal sealed partial class QueryStringBuilder
internal sealed class QueryStringBuilder
{
private const string Properties = "System.ItemUrl, System.ItemNameDisplay, path, System.Search.EntryID, System.Kind, System.KindText";
private const string SystemIndex = "SystemIndex";
@@ -29,46 +24,16 @@ internal sealed partial class QueryStringBuilder
{
if (queryHelper == null)
{
ComWrappers cw = new StrategyBasedComWrappers();
var searchManagerPtr = IntPtr.Zero;
var hr = NativeMethods.CoCreateInstance(ref Unsafe.AsRef(in NativeHelpers.CsWin32GUID.CLSIDSearchManager), IntPtr.Zero, NativeHelpers.CLSCTXINPROCALL, ref Unsafe.AsRef(in NativeHelpers.CsWin32GUID.IIDISearchManager), out searchManagerPtr);
if (hr != 0)
{
throw new ArgumentException($"Failed to create SearchManager instance. HR: 0x{hr:X}");
}
var searchManager = (ISearchManager)cw.GetOrCreateObjectForComInstance(
searchManagerPtr, CreateObjectFlags.None);
if (searchManager == null)
{
throw new ArgumentException("Failed to get ISearchManager interface");
}
var searchManager = new CSearchManager();
ISearchCatalogManager catalogManager = searchManager.GetCatalog(SystemIndex);
if (catalogManager == null)
{
throw new ArgumentException($"Failed to get catalog manager for {SystemIndex}");
}
if (searchManagerPtr != IntPtr.Zero)
{
Marshal.Release(searchManagerPtr);
}
queryHelper = catalogManager.GetQueryHelper();
if (queryHelper == null)
{
throw new ArgumentException("Failed to get query helper from catalog manager");
}
queryHelper.SetQuerySelectColumns(Properties);
queryHelper.SetQueryContentProperties("System.FileName");
queryHelper.SetQuerySorting(OrderConditions);
queryHelper.QuerySelectColumns = Properties;
queryHelper.QueryContentProperties = "System.FileName";
queryHelper.QuerySorting = OrderConditions;
}
queryHelper.SetQueryWhereRestrictions("AND " + ScopeFileConditions + "AND ReuseWhere(" + whereId.ToString(CultureInfo.InvariantCulture) + ")");
queryHelper.QueryWhereRestrictions = "AND " + ScopeFileConditions + "AND ReuseWhere(" + whereId.ToString(CultureInfo.InvariantCulture) + ")";
return queryHelper.GenerateSQLFromUserQuery(searchText);
}
}

View File

@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
<PropertyGroup>
<RootNamespace>Microsoft.CmdPal.Ext.Indexer</RootNamespace>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
@@ -46,9 +46,4 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="NativeMethods.txt" />
<AdditionalFiles Include="NativeMethods.json" />
</ItemGroup>
</Project>

View File

@@ -1,34 +1,25 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.CmdPal.Ext.Indexer.Indexer.SystemSearch;
using Windows.Win32.UI.Shell.PropertiesSystem;
namespace Microsoft.CmdPal.Ext.Indexer.Native;
public sealed partial class NativeHelpers
internal sealed class NativeHelpers
{
public const uint SEEMASKINVOKEIDLIST = 12;
public const uint CLSCTXINPROCALL = 0x17;
public struct PropertyKeys
internal static class PropertyKeys
{
public static readonly PropertyKey PKEYItemNameDisplay = new() { FmtID = new System.Guid("B725F130-47EF-101A-A5F1-02608C9EEBAC"), PID = 10 };
public static readonly PropertyKey PKEYItemUrl = new() { FmtID = new System.Guid("49691C90-7E17-101A-A91C-08002B2ECDA9"), PID = 9 };
public static readonly PropertyKey PKEYKindText = new() { FmtID = new System.Guid("F04BEF95-C585-4197-A2B7-DF46FDC9EE6D"), PID = 100 };
public static readonly PROPERTYKEY PKEYItemNameDisplay = new() { fmtid = new System.Guid("B725F130-47EF-101A-A5F1-02608C9EEBAC"), pid = 10 };
public static readonly PROPERTYKEY PKEYItemUrl = new() { fmtid = new System.Guid("49691C90-7E17-101A-A91C-08002B2ECDA9"), pid = 9 };
public static readonly PROPERTYKEY PKEYKindText = new() { fmtid = new System.Guid("F04BEF95-C585-4197-A2B7-DF46FDC9EE6D"), pid = 100 };
}
public static class OleDb
internal static class OleDb
{
public static readonly Guid DbGuidDefault = new("C8B521FB-5CF3-11CE-ADE5-00AA0044773D");
}
public static class CsWin32GUID
{
public static readonly Guid CLSIDSearchManager = new Guid("7D096C5F-AC08-4F1F-BEB7-5C22C517CE39");
public static readonly Guid IIDISearchManager = new Guid("AB310581-AC80-11D1-8DF3-00C04FB6EF69");
public static readonly Guid CLSIDCollatorDataSource = new Guid("9E175B8B-F52A-11D8-B9A5-505054503030");
public static readonly Guid PropertyStore = new Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99");
}
}

View File

@@ -1,47 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using Windows.Win32.UI.Shell;
namespace Microsoft.CmdPal.Ext.Indexer.Native;
public sealed partial class NativeMethods
{
[LibraryImport("ole32.dll")]
[return: MarshalAs(UnmanagedType.U4)]
public static partial uint CoCreateInstance(
ref Guid rclsid,
IntPtr pUnkOuter,
uint dwClsContext,
ref Guid riid,
out IntPtr rReturnedComObject);
[LibraryImport("SHELL32.dll", EntryPoint = "ShellExecuteExW", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static partial bool ShellExecuteEx(ref SHELLEXECUTEINFOW lpExecInfo);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SHELLEXECUTEINFOW
{
public uint cbSize;
public uint fMask;
public IntPtr hwnd;
public IntPtr lpVerb;
public IntPtr lpFile;
public IntPtr lpParameters;
public IntPtr lpDirectory;
public int nShow;
public IntPtr hInstApp;
public IntPtr lpIDList;
public IntPtr lpClass;
public IntPtr hkeyClass;
public uint dwHotKey;
public IntPtr hIconOrMonitor;
public IntPtr hProcess;
}
}

View File

@@ -1,6 +0,0 @@
{
"$schema": "https://aka.ms/CsWin32.schema.json",
"allowMarshaling": false,
"emitSingleFile": false,
"public": true
}

View File

@@ -1,2 +1,11 @@
DBID
SHOW_WINDOW_CMD
SHOW_WINDOW_CMD
CoCreateInstance
GetErrorInfo
ICommandText
IDBCreateCommand
IDBCreateSession
IDBInitialize
IGetRow
IPropertyStore
ShellExecuteEx

View File

@@ -0,0 +1,144 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
namespace Microsoft.CmdPal.Ext.System.Helpers;
[SuppressMessage("Interoperability", "CA1401:P/Invokes should not be visible", Justification = "We want plugins to share this NativeMethods class, instead of each one creating its own.")]
public sealed class Native
{
public enum HRESULT : uint
{
/// <summary>
/// Operation successful.
/// </summary>
S_OK = 0x00000000,
/// <summary>
/// Operation successful. (negative condition/no operation)
/// </summary>
S_FALSE = 0x00000001,
/// <summary>
/// Not implemented.
/// </summary>
E_NOTIMPL = 0x80004001,
/// <summary>
/// No such interface supported.
/// </summary>
E_NOINTERFACE = 0x80004002,
/// <summary>
/// Pointer that is not valid.
/// </summary>
E_POINTER = 0x80004003,
/// <summary>
/// Operation aborted.
/// </summary>
E_ABORT = 0x80004004,
/// <summary>
/// Unspecified failure.
/// </summary>
E_FAIL = 0x80004005,
/// <summary>
/// Unexpected failure.
/// </summary>
E_UNEXPECTED = 0x8000FFFF,
/// <summary>
/// General access denied error.
/// </summary>
E_ACCESSDENIED = 0x80070005,
/// <summary>
/// Handle that is not valid.
/// </summary>
E_HANDLE = 0x80070006,
/// <summary>
/// Failed to allocate necessary memory.
/// </summary>
E_OUTOFMEMORY = 0x8007000E,
/// <summary>
/// One or more arguments are not valid.
/// </summary>
E_INVALIDARG = 0x80070057,
/// <summary>
/// The operation was canceled by the user. (Error source 7 means Win32.)
/// </summary>
/// <SeeAlso href="https://learn.microsoft.com/windows/win32/debug/system-error-codes--1000-1299-"/>
/// <SeeAlso href="https://en.wikipedia.org/wiki/HRESULT"/>
E_CANCELLED = 0x800704C7,
}
public static class ShellItemTypeConstants
{
/// <summary>
/// Guid for type IShellItem.
/// </summary>
public static readonly Guid ShellItemGuid = new("43826d1e-e718-42ee-bc55-a1e261c37bfe");
/// <summary>
/// Guid for type IShellItem2.
/// </summary>
public static readonly Guid ShellItem2Guid = new("7E9FB0D3-919F-4307-AB2E-9B1860310C93");
}
/// <summary>
/// The following are ShellItem DisplayName types.
/// </summary>
[Flags]
public enum SIGDN : uint
{
NORMALDISPLAY = 0,
PARENTRELATIVEPARSING = 0x80018001,
PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
DESKTOPABSOLUTEPARSING = 0x80028000,
PARENTRELATIVEEDITING = 0x80031001,
DESKTOPABSOLUTEEDITING = 0x8004c000,
FILESYSPATH = 0x80058000,
URL = 0x80068000,
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
public interface IShellItem
{
void BindToHandler(
nint pbc,
[MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
[MarshalAs(UnmanagedType.LPStruct)] Guid riid,
out nint ppv);
void GetParent(out IShellItem ppsi);
void GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
void Compare(IShellItem psi, uint hint, out int piOrder);
}
/// <summary>
/// <see href="https://learn.microsoft.com/windows/win32/stg/stgm-constants">see all STGM values</see>
/// </summary>
[Flags]
public enum STGM : long
{
READ = 0x00000000L,
WRITE = 0x00000001L,
READWRITE = 0x00000002L,
CREATE = 0x00001000L,
}
}

View File

@@ -32,11 +32,6 @@
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="NativeMethods.txt" />
<AdditionalFiles Include="NativeMethods.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />

View File

@@ -1,7 +0,0 @@
{
"$schema": "https://aka.ms/CsWin32.schema.json",
"allowMarshaling": false,
"comInterop": {
"preserveSigMethods": [ "*" ]
}
}

View File

@@ -20,25 +20,20 @@ public class WindowsPackageManagerStandardFactory : WindowsPackageManagerFactory
protected override T CreateInstance<T>(Guid clsid, Guid iid)
{
var pUnknown = IntPtr.Zero;
unsafe
try
{
try
var hr = PInvoke.CoCreateInstance(clsid, null, CLSCTX.CLSCTX_ALL, iid, out var result);
Marshal.ThrowExceptionForHR(hr);
pUnknown = Marshal.GetIUnknownForObject(result);
return MarshalGeneric<T>.FromAbi(pUnknown);
}
finally
{
// CoCreateInstance and FromAbi both AddRef on the native object.
// Release once to prevent memory leak.
if (pUnknown != IntPtr.Zero)
{
var hr = PInvoke.CoCreateInstance(clsid, null, CLSCTX.CLSCTX_ALL, iid, out var result);
Marshal.ThrowExceptionForHR(hr);
pUnknown = new IntPtr(result);
return MarshalGeneric<T>.FromAbi(pUnknown);
}
finally
{
// CoCreateInstance and FromAbi both AddRef on the native object.
// Release once to prevent memory leak.
if (pUnknown != IntPtr.Zero)
{
Marshal.Release(pUnknown);
}
Marshal.Release(pUnknown);
}
}
}

View File

@@ -4,7 +4,6 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace Microsoft.CmdPal.Ext.WindowWalker.Helpers;
@@ -12,16 +11,18 @@ namespace Microsoft.CmdPal.Ext.WindowWalker.Helpers;
/// Interface for accessing Virtual Desktop Manager.
/// Code used from <see href="https://learn.microsoft.com/archive/blogs/winsdk/virtual-desktop-switching-in-windows-10"./>
/// </summary>
[GeneratedComInterface]
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("a5cd92ff-29be-454c-8d04-d82879fb3f1b")]
public partial interface IVirtualDesktopManager
[System.Security.SuppressUnmanagedCodeSecurity]
internal interface IVirtualDesktopManager
{
[PreserveSig]
int IsWindowOnCurrentVirtualDesktop(IntPtr hTopLevelWindow, out int onCurrentDesktop);
int IsWindowOnCurrentVirtualDesktop([In] IntPtr hTopLevelWindow, [Out] out int onCurrentDesktop);
[PreserveSig]
int GetWindowDesktopId(IntPtr hTopLevelWindow, out Guid desktop);
int GetWindowDesktopId([In] IntPtr hTopLevelWindow, [Out] out Guid desktop);
[PreserveSig]
int MoveWindowToDesktop(IntPtr hTopLevelWindow, ref Guid desktop);
int MoveWindowToDesktop([In] IntPtr hTopLevelWindow, [MarshalAs(UnmanagedType.LPStruct)][In] Guid desktop);
}

View File

@@ -13,7 +13,7 @@ using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessage
namespace Microsoft.CmdPal.Ext.WindowWalker.Helpers;
[SuppressMessage("Interoperability", "CA1401:P/Invokes should not be visible", Justification = "We want plugins to share this NativeMethods class, instead of each one creating its own.")]
public static partial class NativeMethods
public static class NativeMethods
{
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int EnumWindows(EnumWindowsProc callPtr, IntPtr lParam);
@@ -99,14 +99,31 @@ public static partial class NativeMethods
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetFirmwareType(ref FirmwareType FirmwareType);
[LibraryImport("ole32.dll")]
[return: MarshalAs(UnmanagedType.U4)]
public static partial uint CoCreateInstance(
ref Guid rclsid,
IntPtr pUnkOuter,
uint dwClsContext,
ref Guid riid,
out IntPtr rReturnedComObject);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ExitWindowsEx(uint uFlags, uint dwReason);
[DllImport("user32")]
public static extern void LockWorkStation();
[DllImport("Powrprof.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetSuspendState(bool hibernate, bool forceCritical, bool disableWakeEvent);
[DllImport("Shell32.dll", CharSet = CharSet.Unicode)]
public static extern uint SHEmptyRecycleBin(IntPtr hWnd, uint dwFlags);
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern HRESULT SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, IntPtr ppvReserved);
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern HRESULT SHCreateStreamOnFileEx(string fileName, STGM grfMode, uint attributes, bool create, System.Runtime.InteropServices.ComTypes.IStream reserved, out System.Runtime.InteropServices.ComTypes.IStream stream);
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string path, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem);
[DllImport("rpcrt4.dll")]
public static extern int UuidCreateSequential(out GUIDDATA Uuid);
}
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "These are the names used by win32.")]
@@ -144,6 +161,19 @@ public static class Win32Constants
public const int RPC_S_UUID_LOCAL_ONLY = 0x720;
}
public static class ShellItemTypeConstants
{
/// <summary>
/// Guid for type IShellItem.
/// </summary>
public static readonly Guid ShellItemGuid = new("43826d1e-e718-42ee-bc55-a1e261c37bfe");
/// <summary>
/// Guid for type IShellItem2.
/// </summary>
public static readonly Guid ShellItem2Guid = new("7E9FB0D3-919F-4307-AB2E-9B1860310C93");
}
public enum HRESULT : uint
{
/// <summary>
@@ -1094,6 +1124,26 @@ public enum ExtendedWindowStyles : uint
WS_EX_NOACTIVATE = 0x8000000,
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
public interface IShellItem
{
void BindToHandler(
IntPtr pbc,
[MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
[MarshalAs(UnmanagedType.LPStruct)] Guid riid,
out IntPtr ppv);
void GetParent(out IShellItem ppsi);
void GetDisplayName(SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
void Compare(IShellItem psi, uint hint, out int piOrder);
}
/// <summary>
/// The following are ShellItem DisplayName types.
/// </summary>

View File

@@ -6,7 +6,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Text;
using Microsoft.CmdPal.Ext.WindowWalker.Properties;
@@ -49,10 +48,6 @@ public class VirtualDesktopHelper
/// </summary>
private readonly List<Guid> _availableDesktops = [];
#pragma warning disable SA1306 // Field names should begin with lower-case letter
private readonly uint CLSCTXINPROCALL = 0x17;
#pragma warning restore SA1306 // Field names should begin with lower-case letter
/// <summary>
/// Id of the current visible Desktop.
/// </summary>
@@ -60,41 +55,21 @@ public class VirtualDesktopHelper
private static readonly CompositeFormat VirtualDesktopHelperDesktop = System.Text.CompositeFormat.Parse(Properties.Resources.VirtualDesktopHelper_Desktop);
private Guid iVirtualDesktopManagerCLSID = new("aa509086-5ca9-4c25-8f95-589d3c07b48a");
private Guid iVirtualDesktopManagerIID = new("a5cd92ff-29be-454c-8d04-d82879fb3f1b");
/// <summary>
/// Initializes a new instance of the <see cref="VirtualDesktopHelper"/> class.
/// </summary>
/// <param name="desktopListUpdate">Setting to configure if the list of available desktops should update automatically or only when calling <see cref="UpdateDesktopList"/>. Per default this is set to manual update (false) to have less registry queries.</param>
public VirtualDesktopHelper(bool desktopListUpdate = false)
{
var cw = new StrategyBasedComWrappers();
var virtualDesktopManagerPtr = IntPtr.Zero;
try
{
var hr = NativeMethods.CoCreateInstance(ref this.iVirtualDesktopManagerCLSID, nint.Zero, CLSCTXINPROCALL, ref iVirtualDesktopManagerIID, out virtualDesktopManagerPtr);
if (hr != 0)
{
throw new ArgumentException($"Failed to create IVirtualDesktopManager instance. HR: 0x{hr:X}");
}
_virtualDesktopManager = (IVirtualDesktopManager)cw.GetOrCreateObjectForComInstance(virtualDesktopManagerPtr, CreateObjectFlags.None);
_virtualDesktopManager = (IVirtualDesktopManager)new CVirtualDesktopManager();
}
catch (COMException ex)
{
ExtensionHost.LogMessage(new LogMessage() { Message = $"Initialization of <VirtualDesktopHelper> failed: An exception was thrown when creating the instance of COM interface <IVirtualDesktopManager>. {ex} " });
return;
}
finally
{
if (virtualDesktopManagerPtr != IntPtr.Zero)
{
Marshal.Release(virtualDesktopManagerPtr);
}
}
_isWindowsEleven = OSVersionHelper.IsWindows11();
_desktopListAutoUpdate = desktopListUpdate;
@@ -434,7 +409,7 @@ public class VirtualDesktopHelper
/// <param name="hWindow">Handle of the top level window.</param>
/// <param name="desktopId">Guid of the target desktop.</param>
/// <returns><see langword="True"/> on success and <see langword="false"/> on failure.</returns>
public bool MoveWindowToDesktop(IntPtr hWindow, ref Guid desktopId)
public bool MoveWindowToDesktop(IntPtr hWindow, in Guid desktopId)
{
if (_virtualDesktopManager == null)
{
@@ -442,7 +417,7 @@ public class VirtualDesktopHelper
return false;
}
var hr = _virtualDesktopManager.MoveWindowToDesktop(hWindow, ref desktopId);
var hr = _virtualDesktopManager.MoveWindowToDesktop(hWindow, desktopId);
if (hr != (int)HRESULT.S_OK)
{
ExtensionHost.LogMessage(new LogMessage() { Message = "VirtualDesktopHelper.MoveWindowToDesktop() failed: An exception was thrown when moving the window ({hWindow}) to another desktop ({desktopId})." });
@@ -480,7 +455,7 @@ public class VirtualDesktopHelper
}
Guid newDesktop = _availableDesktops[windowDesktopNumber - 1];
return MoveWindowToDesktop(hWindow, ref newDesktop);
return MoveWindowToDesktop(hWindow, newDesktop);
}
/// <summary>
@@ -511,7 +486,7 @@ public class VirtualDesktopHelper
}
Guid newDesktop = _availableDesktops[windowDesktopNumber + 1];
return MoveWindowToDesktop(hWindow, ref newDesktop);
return MoveWindowToDesktop(hWindow, newDesktop);
}
/// <summary>

View File

@@ -3,13 +3,18 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Resources;
using System.Text;
using System.Threading.Tasks;
using ManagedCommon;
using Microsoft.CmdPal.Ext.WindowsTerminal.Helpers;
using Microsoft.CmdPal.Ext.WindowsTerminal.Properties;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.UI;
namespace Microsoft.CmdPal.Ext.WindowsTerminal.Commands;
@@ -63,24 +68,7 @@ internal sealed partial class LaunchProfileAsAdminCommand : InvokableCommand
private void Launch(string id, string profile)
{
ComWrappers cw = new StrategyBasedComWrappers();
var appManagerPtr = IntPtr.Zero;
var hr = NativeHelpers.CoCreateInstance(ref Unsafe.AsRef(in NativeHelpers.ApplicationActivationManagerCLSID), IntPtr.Zero, NativeHelpers.CLSCTXINPROCALL, ref Unsafe.AsRef(in NativeHelpers.ApplicationActivationManagerIID), out appManagerPtr);
if (hr != 0)
{
throw new ArgumentException($"Failed to create IApplicationActivationManager instance. HR: 0x{hr:X}");
}
var appManager = (IApplicationActivationManager)cw.GetOrCreateObjectForComInstance(
appManagerPtr, CreateObjectFlags.None);
if (appManager == null)
{
throw new ArgumentException("Failed to get IApplicationActivationManager interface");
}
var appManager = new ApplicationActivationManager();
const ActivateOptions noFlags = ActivateOptions.None;
var queryArguments = TerminalHelper.GetArguments(profile, _openNewTab, _openQuake);
try
@@ -97,13 +85,6 @@ internal sealed partial class LaunchProfileAsAdminCommand : InvokableCommand
// _context.API.ShowMsg(name, message, string.Empty);
Logger.LogError($"Failed to open Windows Terminal: {ex.Message}");
}
finally
{
if (appManagerPtr != IntPtr.Zero)
{
Marshal.Release(appManagerPtr);
}
}
}
#pragma warning restore IDE0059, CS0168

View File

@@ -3,13 +3,18 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Resources;
using System.Text;
using System.Threading.Tasks;
using ManagedCommon;
using Microsoft.CmdPal.Ext.WindowsTerminal.Helpers;
using Microsoft.CmdPal.Ext.WindowsTerminal.Properties;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.UI;
namespace Microsoft.CmdPal.Ext.WindowsTerminal.Commands;
@@ -33,23 +38,7 @@ internal sealed partial class LaunchProfileCommand : InvokableCommand
private void Launch(string id, string profile)
{
ComWrappers cw = new StrategyBasedComWrappers();
var appManagerPtr = IntPtr.Zero;
var hr = NativeHelpers.CoCreateInstance(ref Unsafe.AsRef(in NativeHelpers.ApplicationActivationManagerCLSID), IntPtr.Zero, NativeHelpers.CLSCTXINPROCALL, ref Unsafe.AsRef(in NativeHelpers.ApplicationActivationManagerIID), out appManagerPtr);
if (hr != 0)
{
throw new ArgumentException($"Failed to create IApplicationActivationManager instance. HR: 0x{hr:X}");
}
var appManager = (IApplicationActivationManager)cw.GetOrCreateObjectForComInstance(
appManagerPtr, CreateObjectFlags.None);
if (appManager == null)
{
throw new ArgumentException("Failed to get IApplicationActivationManager interface");
}
var appManager = new ApplicationActivationManager();
const ActivateOptions noFlags = ActivateOptions.None;
var queryArguments = TerminalHelper.GetArguments(profile, _openNewTab, _openQuake);
try
@@ -66,13 +55,6 @@ internal sealed partial class LaunchProfileCommand : InvokableCommand
// _context.API.ShowMsg(name, message, string.Empty);
Logger.LogError($"Failed to open Windows Terminal: {ex.Message}");
}
finally
{
if (appManagerPtr != IntPtr.Zero)
{
Marshal.Release(appManagerPtr);
}
}
}
#pragma warning restore IDE0059, CS0168

View File

@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Microsoft.CmdPal.Ext.WindowsTerminal.Helpers;
// Application Activation Manager Class
[ComImport]
[Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")]
public class ApplicationActivationManager : IApplicationActivationManager
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)/*, PreserveSig*/]
public extern IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public extern IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId);
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public extern IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId);
}

View File

@@ -4,7 +4,6 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace Microsoft.CmdPal.Ext.WindowsTerminal.Helpers;
@@ -19,13 +18,14 @@ public enum ActivateOptions
}
// ApplicationActivationManager
[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16)]
[ComImport]
[Guid("2e941141-7f97-4756-ba1d-9decde894a3d")]
public partial interface IApplicationActivationManager
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IApplicationActivationManager
{
void ActivateApplication(string appUserModelId, string arguments, ActivateOptions options, out uint processId);
IntPtr ActivateApplication([In] string appUserModelId, [In] string arguments, [In] ActivateOptions options, [Out] out uint processId);
void ActivateForFile(string appUserModelId, IntPtr /*IShellItemArray* */ itemArray, string verb, out uint processId);
IntPtr ActivateForFile([In] string appUserModelId, [In] IntPtr /*IShellItemArray* */ itemArray, [In] string verb, [Out] out uint processId);
void ActivateForProtocol(string appUserModelId, IntPtr /* IShellItemArray* */itemArray, out uint processId);
IntPtr ActivateForProtocol([In] string appUserModelId, [In] IntPtr /* IShellItemArray* */itemArray, [Out] out uint processId);
}

View File

@@ -1,28 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.CmdPal.Ext.WindowsTerminal.Helpers;
public sealed partial class NativeHelpers
{
public const uint CLSCTXINPROCALL = 0x17;
[LibraryImport("ole32.dll")]
public static partial uint CoCreateInstance(
ref Guid rclsid,
IntPtr pUnkOuter,
uint dwClsContext,
ref Guid riid,
out IntPtr rReturnedComObject);
public static readonly Guid ApplicationActivationManagerCLSID = new Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C");
public static readonly Guid ApplicationActivationManagerIID = new Guid("2e941141-7f97-4756-ba1d-9decde894a3d");
}

View File

@@ -72,6 +72,7 @@
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<AdditionalDependencies>$(OutDir)\..\..\WinUI3Apps\PowerRenameLib.lib;comctl32.lib;pathcch.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
@@ -84,9 +85,6 @@
<ProjectReference Include="..\lib\PowerRenameLib.vcxproj" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Project>{51920f1f-c28c-4adf-8660-4238766796c2}</Project>
</ProjectReference>
<ProjectReference Include="..\lib\PowerRenameLib.vcxproj" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Project>{51920f1f-c28c-4adf-8660-4238766796c2}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>

View File

@@ -243,7 +243,7 @@
<controls:PageLink x:Uid="LearnMore_PowerPreview" Link="https://aka.ms/PowerToysOverview_FileExplorerAddOns" />
</controls:SettingsPageControl.PrimaryLinks>
<controls:SettingsPageControl.SecondaryLinks>
<controls:PageLink Link="https://noraajunker.ch" Text="Noraa Junker's work on developer file preview" />
<controls:PageLink Link="https://blog.aaron-junker.ch" Text="Aaron Junker's work on developer file preview" />
<controls:PageLink Link="https://www.pedrolamas.com" Text="Pedro Lamas's work on G-Code, STL, and QOI" />
</controls:SettingsPageControl.SecondaryLinks>
</controls:SettingsPageControl>