Skip to content

Instantly share code, notes, and snippets.

@latop2604
Created February 16, 2024 10:31
Show Gist options
  • Save latop2604/73bd7d8008e7ed925f5713fda9201ced to your computer and use it in GitHub Desktop.
Save latop2604/73bd7d8008e7ed925f5713fda9201ced to your computer and use it in GitHub Desktop.
Jumplist Native AOT Net8
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Runtime.Versioning;
using JumpListsAOT.Nati;
namespace JumpListsAOT;
[SupportedOSPlatform("windows")]
internal class JumpLists
{
public static void Init()
{
var targetExe = Path.Combine(AppContext.BaseDirectory, "JumpLists.exe");
var profilePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var dir = new DirectoryInfo(profilePath);
var files = ["fileA", "fileB", "fileB"];
try
{
ComWrappers cw = new StrategyBasedComWrappers();
var cdl = ActivateClass<ICustomDestinationList>(
cw,
new Guid("77f10cf0-3db5-4966-b520-b7c54fd35ed6"),
new Guid("6332debf-87b5-4670-90c0-5e57b408a49e"));
var obj = cdl.BeginList(out uint pcMinSlots, typeof(IObjectArray).GUID);
var objArray = (IObjectArray)obj;
var removedObjs = ToArray<object>(cw, objArray);
var exceptions = new System.Collections.Generic.List<Exception>();
var poc = ActivateClass<IObjectCollection>(
cw,
new Guid("2d3468c1-36a7-43b6-ac24-d3f02fd9607a"),
new Guid("5632b1a4-e38a-400a-928a-d4cd63230295"));
//var count = poc.GetCount();
foreach (var profile in files)
{
var psho = CreateTaskListShellObject(cw, profile, targetExe);
//if (!IsRemoved(psho.Item))
poc.AddObject(psho);
}
//var pocCount = poc.GetCount();
cdl.AddUserTasks(poc);
cdl.CommitList();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
public static I ActivateClass<I>(ComWrappers cw, Guid clsid, Guid iid)
{
Debug.Assert(iid == typeof(I).GUID);
int hr = NativeMethod.CoCreateInstance(ref clsid, IntPtr.Zero, /*CLSCTX_INPROC_SERVER*/ 1, ref iid, out IntPtr obj);
if (hr < 0)
{
Marshal.ThrowExceptionForHR(hr);
}
return (I)cw.GetOrCreateObjectForComInstance(obj, CreateObjectFlags.None);
}
public static T[] ToArray<T>(ComWrappers cw, IObjectArray a) where T : class
{
const string IID_IUnknown = "00000000-0000-0000-C000-000000000046";
var gIUnk = typeof(T) == typeof(object) ? new Guid(IID_IUnknown) : typeof(T).GUID;
var c = a.GetCount();
var ret = new T[c];
for (var i = 0U; i < c; i++)
{
var ppvObject = a.GetAt(i, gIUnk);
ret[i] = (T)ppvObject;
}
return ret;
}
public static IShellLinkW CreateTaskListShellObject(ComWrappers cw, string? profile, string targetExe)
{
var link = ActivateClass<IShellLinkW>(
cw,
new Guid("00021401-0000-0000-C000-000000000046"),
new Guid("000214F9-0000-0000-C000-000000000046"));
link.SetPath(targetExe);
//if (!string.IsNullOrEmpty(AppUserModelID))
// (link as IPropertyStore)?.SetValue(PROPERTYKEY.System.AppUserModel.ID, AppUserModelID);
//if (!string.IsNullOrEmpty(WorkingDirectory))
// link.SetWorkingDirectory(WorkingDirectory);
if (!string.IsNullOrEmpty(profile))
link.SetArguments(profile);
// -1 is a sentinel value indicating not to use the icon.
//if (IconResourceIndex != -1)
//link.SetIconLocation(IconResourcePath ?? ApplicationPath, IconResourceIndex);
//if (!string.IsNullOrEmpty(Description))
link.SetDescription($"Open with profile {profile ?? "Default"}");
//if (!string.IsNullOrEmpty(profile ?? "Default"))
var pv = new PROPVARIANT(profile ?? "Default");
(link as IPropertyStore)?.SetValue(PROPERTYKEY.System.Title, pv);
NativeMethod.PropVariantClear(ref pv);
return link;
}
}
// <AllowUnsafeBlocks>true</AllowUnsafeBlocks> required in csproj
// Based on https://github.com/dahall/Vanara/blob/master/Windows.Shell.Common/TaskBar/JumpList.cs
// And https://github.com/jlnewton87/Programming/blob/master/C%23/Windows%20API%20Code%20Pack%201.1/source/WindowsAPICodePack/Core/PropertySystem/PropVariant.cs
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Runtime.Versioning;
[assembly: System.Runtime.CompilerServices.DisableRuntimeMarshalling]
namespace JumpListsAOT.Nati;
[SupportedOSPlatform("windows")]
public static partial class NativeMethod
{
[LibraryImport("Ole32")]
internal static partial int CoCreateInstance(
ref Guid rclsid,
IntPtr pUnkOuter,
int dwClsContext,
ref Guid riid,
out IntPtr ppObj);
[LibraryImport("Ole32")] // returns hresult
internal static partial int PropVariantClear(ref PROPVARIANT pvar);
}
/// <summary>
/// Exposes methods that allow an application to provide a custom Jump List, including destinations and tasks, for display in the taskbar.
/// </summary>
//[SuppressUnmanagedCodeSecurity]
//[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("6332debf-87b5-4670-90c0-5e57b408a49e"), CoClass(typeof(CDestinationList))]
//[PInvokeData("Shobjidl.h", MSDNShortId = "dd378402")]
[GeneratedComInterface]
[Guid("6332debf-87b5-4670-90c0-5e57b408a49e")]
public partial interface ICustomDestinationList
{
/// <summary>
/// Specifies a unique Application User Model ID (AppUserModelID) for the application whose taskbar button will hold the custom
/// Jump List built through the methods of this interface. This method is optional.
/// </summary>
/// <param name="pszAppID">
/// A pointer to the AppUserModelID of the process or application whose taskbar representation receives the Jump List.
/// </param>
void SetAppID([MarshalAs(UnmanagedType.LPWStr)] string pszAppID);
/// <summary>Initiates a building session for a custom Jump List.</summary>
/// <param name="pcMaxSlots">
/// A pointer that, when this method returns, points to the current user setting for the Number of recent items to display in
/// Jump Lists option in the Taskbar and Start Menu Properties window. The default value is 10. This is the maximum number of
/// destinations that will be shown, and it is a total of all destinations, regardless of category. More destinations can be
/// added, but they will not be shown in the UI.
/// <para>A Jump List will always show at least this many slots—destinations and, if there is room, tasks.</para>
/// <para>
/// This number does not include separators and section headers as long as the total number of separators and headers does not
/// exceed four. Separators and section headers beyond the first four might reduce the number of destinations displayed if space
/// is constrained. This number does not affect the standard command entries for pinning or unpinning, closing the window, or
/// launching a new instance. It also does not affect tasks or pinned items, the number of which that can be displayed is based
/// on the space available to the Jump List.
/// </para>
/// </param>
/// <param name="riid">
/// A reference to the IID of an interface to be retrieved in ppv, typically IID_IObjectArray, that will represent all items
/// currently stored in the list of removed destinations for the application. This information is used to ensure that removed
/// items are not part of the new Jump List.
/// </param>
/// <returns>
/// When this method returns, contains the interface pointer requested in riid. This is typically an IObjectArray, which
/// represents a collection of IShellItem and IShellLink objects that represent the removed items.
/// </returns>
[return: MarshalAs(UnmanagedType.Interface)]
object BeginList(out uint pcMaxSlots, in Guid riid);
/// <summary>Defines a custom category and the destinations that it contains, for inclusion in a custom Jump List.</summary>
/// <param name="pszCategory">
/// A pointer to a string that contains the display name of the custom category. This string is shown in the category's header in
/// the Jump List. The string can directly hold the display name or it can be an indirect string representation, such as
/// "@shell32.dll,-1324", to use a stored string. An indirect string enables the category header to be displayed in the user's
/// selected language. <note>Each custom category must have a unique name. Duplicate category names will cause presentation
/// issues in the Jump List.</note>
/// </param>
/// <param name="poa">
/// A pointer to an IObjectArray that represents one or more IShellItem objects that represent the destinations in the category.
/// Some destinations in the list might also be represented by IShellLink objects, although less often. <note>Any IShellLink used
/// here must declare an argument list through SetArguments. Adding an IShellLink object with no arguments to a custom category
/// is not supported since a user cannot pin or unpin this type of item from a Jump List, nor can they be added or removed.</note>
/// </param>
void AppendCategory([MarshalAs(UnmanagedType.LPWStr)] string pszCategory, IObjectArray poa);
/// <summary>Specifies that the Frequent or Recent category should be included in a custom Jump List.</summary>
/// <param name="category">One of the KNOWNDESTCATEGORY values that indicate which known category to add to the list.</param>
void AppendKnownCategory(KNOWNDESTCATEGORY category);
/// <summary>Specifies items to include in the Tasks category of a custom Jump List.</summary>
/// <param name="poa">
/// A pointer to an IObjectArray that represents one or more IShellLink (or, more rarely, IShellItem) objects that represent the
/// tasks. <note>Any IShellLink used here must declare an argument list through SetArguments. Adding an IShellLink object with no
/// arguments to a custom category is not supported. A user cannot pin or unpin this type of item from a Jump List, nor can they
/// be added or removed.</note>
/// </param>
void AddUserTasks(IObjectArray poa);
/// <summary>
/// Declares that the Jump List initiated by a call to ICustomDestinationList::BeginList is complete and ready for display.
/// </summary>
void CommitList();
/// <summary>
/// Retrieves the current list of destinations that have been removed by the user from the existing Jump List that this custom
/// Jump List is meant to replace.
/// </summary>
/// <param name="riid">A reference to the IID of the interface to retrieve through ppv, typically IID_IObjectArray.</param>
/// <returns>
/// When this method returns, contains the interface pointer requested in riid. This is typically an IObjectArray, which
/// represents a collection of IShellItem or IShellLink objects that represent the items in the list of removed destinations.
/// </returns>
[return: MarshalAs(UnmanagedType.Interface)]
object GetRemovedDestinations(in Guid riid);
/// <summary>Deletes a custom Jump List for a specified application.</summary>
/// <param name="pszAppID">
/// A pointer to the AppUserModelID of the process whose taskbar button representation displays the custom Jump List. In the beta
/// release of Windows 7, this AppUserModelID must be explicitly provided because this method is intended to be called from an
/// uninstaller, which runs in a separate process. Because it is in a separate process, the system cannot reliably deduce the
/// AppUserModelID. This restriction is expected to be removed in later releases.
/// </param>
void DeleteList([Optional, MarshalAs(UnmanagedType.LPWStr)] string pszAppID);
/// <summary>
/// Discontinues a Jump List building session initiated by ICustomDestinationList::BeginList without committing any changes.
/// </summary>
void AbortList();
}
/// <summary>
/// <para>Exposes methods that enable clients to access items in a collection of objects that support IUnknown.</para>
/// </summary>
/// <remarks>
/// <para>When to Implement</para>
/// <para>Clients do not need to implement this interface.</para>
/// <para>When to Use</para>
/// <para>Use this interface to access generic objects in an array.</para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/desktop/api/objectarray/nn-objectarray-iobjectarray
//[PInvokeData("objectarray.h", MSDNShortId = "ab0bb213-dc9c-4853-98d7-668e7ca76583")]
//[SuppressUnmanagedCodeSecurity]
//[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("92CA9DCD-5622-4bba-A805-5E9F541BD8C9")]
[GeneratedComInterface]
[Guid("92CA9DCD-5622-4bba-A805-5E9F541BD8C9")]
public partial interface IObjectArray
{
/// <summary>Provides a count of the objects in the collection.</summary>
/// <returns>The number of objects in the collection.</returns>
uint GetCount();
/// <summary>
/// Provides a pointer to a specified object's interface. The object and interface are specified by index and interface ID.
/// </summary>
/// <param name="uiIndex">The index of the object</param>
/// <param name="riid">Reference to the desired interface ID.</param>
/// <returns>Receives the interface pointer requested in riid.</returns>
[return: MarshalAs(UnmanagedType.Interface)]
object GetAt(uint uiIndex, in Guid riid);
}
/// <summary>
/// <para>
/// Extends the IObjectArray interface by providing methods that enable clients to add and remove objects that support IUnknown in a collection.
/// </para>
/// </summary>
/// <remarks>
/// <para>When to Use</para>
/// <para>Use this interface to interact with a collection of generic objects.</para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/desktop/api/objectarray/nn-objectarray-iobjectcollection
//[PInvokeData("objectarray.h", MSDNShortId = "d7665b26-5839-4b08-a099-ef25a68c65db")]
//[SuppressUnmanagedCodeSecurity]
//[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("5632b1a4-e38a-400a-928a-d4cd63230295"), CoClass(typeof(CEnumerableObjectCollection))]
[GeneratedComInterface]
[Guid("5632B1A4-E38A-400A-928A-D4CD63230295")]
public partial interface IObjectCollection : IObjectArray
{
/// <summary>Provides a count of the objects in the collection.</summary>
/// <returns>The number of objects in the collection.</returns>
//new uint GetCount();
/// <summary>
/// Provides a pointer to a specified object's interface. The object and interface are specified by index and interface ID.
/// </summary>
/// <param name="uiIndex">The index of the object</param>
/// <param name="riid">Reference to the desired interface ID.</param>
/// <returns>Receives the interface pointer requested in riid.</returns>
//[return: MarshalAs(UnmanagedType.Interface)]
//new object GetAt(uint uiIndex, in Guid riid);
/// <summary>Adds a single object to the collection.</summary>
/// <param name="punk">Pointer to the IUnknown of the object to be added to the collection.</param>
void AddObject([MarshalAs(UnmanagedType.Interface)] object punk);
/// <summary>Adds the objects contained in an IObjectArray to the collection.</summary>
/// <param name="poaSource">Pointer to the IObjectArray whose contents are to be added to the collection.</param>
void AddFromArray(IObjectArray poaSource);
/// <summary>Removes a single, specified object from the collection.</summary>
/// <param name="uiIndex">A pointer to the index of the object within the collection.</param>
void RemoveObjectAt(uint uiIndex);
/// <summary>Removes all objects from the collection.</summary>
void Clear();
}
/// <summary>Exposes methods that create, modify, and resolve Shell links.</summary>
//[SuppressUnmanagedCodeSecurity]
//[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046"), CoClass(typeof(CShellLinkW))]
//[PInvokeData("Shobjidl.h", MSDNShortId = "bb774950")]
[GeneratedComInterface(Options = ComInterfaceOptions.ComObjectWrapper)]
[Guid("000214F9-0000-0000-C000-000000000046")]
public partial interface IShellLinkW
{
/// <summary>Gets the path and file name of the target of a Shell link object.</summary>
/// <param name="pszFile">The address of a buffer that receives the path and file name of the target of the Shell link object.</param>
/// <param name="cchMaxPath">
/// The size, in characters, of the buffer pointed to by the pszFile parameter, including the terminating null character. The
/// maximum path size that can be returned is MAX_PATH. This parameter is commonly set by calling ARRAYSIZE(pszFile). The
/// ARRAYSIZE macro is defined in Winnt.h.
/// </param>
/// <param name="pfd">
/// A pointer to a WIN32_FIND_DATA structure that receives information about the target of the Shell link object. If this
/// parameter is NULL, then no additional information is returned.
/// </param>
/// <param name="fFlags">Flags that specify the type of path information to retrieve.</param>
void GetPath([Out, MarshalAs(UnmanagedType.LPArray)] char[] pszFile, int cchMaxPath, out /*Vanara.PInvoke.WIN32_FIND_DATA*/ IntPtr pfd, /*SLGP*/uint fFlags);
/// <summary>Gets the list of item identifiers for the target of a Shell link object.</summary>
/// <returns>When this method returns, contains the address of a PIDL.</returns>
//Vanara.PInvoke.Shell32.PIDL GetIDList();
IntPtr GetIDList();
/// <summary>Sets the pointer to an item identifier list (PIDL) for a Shell link object.</summary>
/// <param name="pidl">The object's fully qualified PIDL.</param>
//void SetIDList(PIDL pidl);
void SetIDList(IntPtr pidl);
/// <summary>Gets the description string for a Shell link object.</summary>
/// <param name="pszFile">A pointer to the buffer that receives the description string.</param>
/// <param name="cchMaxName">The maximum number of characters to copy to the buffer pointed to by the pszName parameter.</param>
void GetDescription([Out, MarshalAs(UnmanagedType.LPArray)] char[] pszFile, int cchMaxName);
/// <summary>Sets the description for a Shell link object. The description can be any application-defined string.</summary>
/// <param name="pszName">A pointer to a buffer containing the new description string.</param>
void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);
/// <summary>Gets the name of the working directory for a Shell link object.</summary>
/// <param name="pszDir">The address of a buffer that receives the name of the working directory.</param>
/// <param name="cchMaxPath">
/// The maximum number of characters to copy to the buffer pointed to by the pszDir parameter. The name of the working directory
/// is truncated if it is longer than the maximum specified by this parameter.
/// </param>
void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPArray)] char[] pszDir, int cchMaxPath);
/// <summary>Sets the name of the working directory for a Shell link object.</summary>
/// <param name="pszDir">The address of a buffer that contains the name of the new working directory.</param>
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir);
/// <summary>Gets the command-line arguments associated with a Shell link object.</summary>
/// <param name="pszArgs">A pointer to the buffer that, when this method returns successfully, receives the command-line arguments.</param>
/// <param name="cchMaxPath">
/// The maximum number of characters that can be copied to the buffer supplied by the pszArgs parameter. In the case of a Unicode
/// string, there is no limitation on maximum string length. In the case of an ANSI string, the maximum length of the returned
/// string varies depending on the version of Windows—MAX_PATH prior to Windows 2000 and INFOTIPSIZE (defined in Commctrl.h) in
/// Windows 2000 and later.
/// </param>
void GetArguments([Out, MarshalAs(UnmanagedType.LPArray)] char[] pszArgs, int cchMaxPath);
/// <summary>Sets the command-line arguments for a Shell link object.</summary>
/// <param name="pszArgs">
/// A pointer to a buffer that contains the new command-line arguments. In the case of a Unicode string, there is no limitation
/// on maximum string length. In the case of an ANSI string, the maximum length of the returned string varies depending on the
/// version of Windows—MAX_PATH prior to Windows 2000 and INFOTIPSIZE (defined in Commctrl.h) in Windows 2000 and later.
/// </param>
void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
/// <summary>Gets the keyboard shortcut (hot key) for a Shell link object.</summary>
/// <returns>
/// <para>
/// The address of the keyboard shortcut. The virtual key code is in the low-order byte, and the modifier flags are in the
/// high-order byte. The modifier flags can be a combination of the following values.
/// </para>
/// <list type="table">
/// <listheader>
/// <term>Value</term>
/// <term>Meaning</term>
/// </listheader>
/// <item>
/// <term><c>HOTKEYF_ALT</c> 0x04</term>
/// <term>ALT key</term>
/// </item>
/// <item>
/// <term><c>HOTKEYF_CONTROL</c> 0x02</term>
/// <term>CTRL key</term>
/// </item>
/// <item>
/// <term><c>HOTKEYF_EXT</c> 0x08</term>
/// <term>Extended key</term>
/// </item>
/// <item>
/// <term><c>HOTKEYF_SHIFT</c> 0x01</term>
/// <term>SHIFT key</term>
/// </item>
/// </list>
/// </returns>
// https://docs.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ishelllinkw-gethotkey
// HRESULT GetHotkey( WORD *pwHotkey );
ushort GetHotKey();
/// <summary>Sets a keyboard shortcut (hot key) for a Shell link object.</summary>
/// <param name="wHotKey">
/// The new keyboard shortcut. The virtual key code is in the low-order byte, and the modifier flags are in the high-order byte.
/// The modifier flags can be a combination of the values specified in the description of the IShellLink::GetHotkey method.
/// </param>
void SetHotKey(ushort wHotKey);
/// <summary>Gets the show command for a Shell link object.</summary>
/// <returns>
/// A pointer to the command. The following commands are supported.
/// <list>
/// <item>
/// <term>SW_SHOWNORMAL</term>
/// <description>
/// Activates and displays a window. If the window is minimized or maximized, the system restores it to its original size and
/// position. An application should specify this flag when displaying the window for the first time.
/// </description>
/// </item>
/// <item>
/// <term>SW_SHOWMAXIMIZED</term>
/// <description>Activates the window and displays it as a maximized window.</description>
/// </item>
/// <item>
/// <term>SW_SHOWMINIMIZED</term>
/// <description>Activates the window and displays it as a minimized window.</description>
/// </item>
/// </list>
/// </returns>
//ShowWindowCommand GetShowCmd();
ShowWindowCommand GetShowCmd();
/// <summary>Sets the show command for a Shell link object. The show command sets the initial show state of the window.</summary>
/// <param name="iShowCmd">
/// SetShowCmd accepts one of the following ShowWindow commands.
/// <list>
/// <item>
/// <term>SW_SHOWNORMAL</term>
/// <description>
/// Activates and displays a window. If the window is minimized or maximized, the system restores it to its original size and
/// position. An application should specify this flag when displaying the window for the first time.
/// </description>
/// </item>
/// <item>
/// <term>SW_SHOWMAXIMIZED</term>
/// <description>Activates the window and displays it as a maximized window.</description>
/// </item>
/// <item>
/// <term>SW_SHOWMINIMIZED</term>
/// <description>Activates the window and displays it as a minimized window.</description>
/// </item>
/// </list>
/// </param>
void SetShowCmd(ShowWindowCommand iShowCmd);
//void SetShowCmd(uint iShowCmd);
/// <summary>Gets the location (path and index) of the icon for a Shell link object.</summary>
/// <param name="pszIconPath">The address of a buffer that receives the path of the file containing the icon.</param>
/// <param name="cchIconPath">The maximum number of characters to copy to the buffer pointed to by the pszIconPath parameter.</param>
/// <param name="piIcon">The address of a value that receives the index of the icon.</param>
void GetIconLocation([Out, MarshalAs(UnmanagedType.LPArray)] char[] pszIconPath, int cchIconPath,
out int piIcon);
/// <summary>Sets the location (path and index) of the icon for a Shell link object.</summary>
/// <param name="pszIconPath">The address of a buffer to contain the path of the file containing the icon.</param>
/// <param name="iIcon">The index of the icon.</param>
void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);
/// <summary>Sets the relative path to the Shell link object.</summary>
/// <param name="pszPathRel">
/// The address of a buffer that contains the fully-qualified path of the shortcut file, relative to which the shortcut
/// resolution should be performed. It should be a file name, not a folder name.
/// </param>
/// <param name="dwReserved">Reserved. Set this parameter to zero.</param>
void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, [Optional] uint dwReserved);
/// <summary>Attempts to find the target of a Shell link, even if it has been moved or renamed.</summary>
/// <param name="hwnd">
/// A handle to the window that the Shell will use as the parent for a dialog box. The Shell displays the dialog box if it needs
/// to prompt the user for more information while resolving a Shell link.
/// </param>
/// <param name="fFlags">Action flags.</param>
//void Resolve(HWND hwnd, SLR_FLAGS fFlags);
void Resolve(IntPtr hwnd, uint fFlags);
/// <summary>Sets the path and file name for the target of a Shell link object.</summary>
/// <param name="pszFile">The address of a buffer that contains the new path.</param>
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
//[PInvokeData("propsys.h", MSDNShortId = "63afd5b1-87cc-4e0a-8964-2138c5fbff46")]
//[SuppressUnmanagedCodeSecurity]
//[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99")]
[GeneratedComInterface(Options = ComInterfaceOptions.ComObjectWrapper)]
[Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99")]
public partial interface IPropertyStore
{
/// <summary>This method returns a count of the number of properties that are attached to the file.</summary>
/// <returns>A value that indicates the property count.</returns>
/// <remarks>
/// <para>
/// <c>IPropertyStore</c> provides an abstraction over an array of property keys via the and IPropertyStore::GetAt methods. The
/// property keys in this array represent the properties that are currently stored by the <c>IPropertyStore</c>.
/// </para>
/// <para>
/// When succeeds, the value pointed to by cProps is a count of property keys in the array. The caller can expect calls to
/// <c>IPropertyStore::GetAt</c> to succeed for values of iProp less than cProps.
/// </para>
/// <para>
/// In the case of failures such as E_OUTOFMEMORY, you should set cProps to zero. It is preferable that errors are discovered
/// during creation or initialization of the property store.
/// </para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/propsys/nf-propsys-ipropertystore-getcount HRESULT GetCount( DWORD *cProps );
uint GetCount();
/// <summary>Gets a property key from an item's array of properties.</summary>
/// <param name="iProp">
/// <para>[in] Type: <c>DWORD</c></para>
/// <para>The index of the property key in the array of <c>PROPERTYKEY</c> structures. This is a zero-based index.</para>
/// </param>
/// <returns>
/// <para>[out] Type: <c>PROPERTYKEY*</c></para>
/// <para>When this method returns, contains a <c>PROPERTYKEY</c> structure that receives the unique identifier for a property.</para>
/// </returns>
/// <remarks>
/// <para>The <c>PROPERTYKEY</c> returned in pkey can be used in subsequent calls to <c>IPropertyStore::GetValue</c> and <c>IPropertyStore::SetValue</c>.</para>
/// <para>
/// There is no specific order to an item's set of enumerated properties. To find a specific property, you must walk the array
/// until you find the property that matches your criteria.
/// </para>
/// <para>
/// iProp cannot be greater than or equal to the cProps parameter retrieved by <c>IPropertyStore::GetCount</c>. If it is greater
/// than or equal to that value, <c>IPropertyStore::GetAt</c> returns E_INVALIDARG and pkey is set to <c>NULL</c> by the
/// property handler.
/// </para>
/// </remarks>
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/bb761471(v=vs.85) HRESULT GetAt( [in] DWORD iProp,
// [out] PROPERTYKEY *pkey );
PROPERTYKEY GetAt(uint iProp);
/// <summary>Gets data for a specific property.</summary>
/// <param name="pkey">The pkey.</param>
/// <param name="pv">
/// <para>[out] Type: <c>PROPVARIANT*</c></para>
/// <para>When this method returns, contains a <c>PROPVARIANT</c> structure that contains the property data.</para>
/// </param>
/// <remarks>
/// <para>
/// If the <c>PROPERTYKEY</c> referenced in key is not present in the property store, this method returns <c>S_OK</c> and the
/// <c>vt</c> member of the structure pointed to by pv is set to VT_EMPTY.
/// </para>
/// <para>
/// File property handler implementers can use <c>IPropertyStore::GetValue</c> to retrieve the property value by using the
/// filestream with which <c>Initialize</c> initialized the property handler. The value can also be computed from an in-memory
/// cache, or other means. However, most consumers of the property system obtain <c>IPropertyStore</c> through
/// <c>GetPropertyStore</c> and are not—and have no need to be—aware of the method of initialization.
/// </para>
/// </remarks>
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/bb761473(v=vs.85) HRESULT GetValue( [in]
// REFPROPERTYKEY key, [out] PROPVARIANT *pv );
void GetValue(in PROPERTYKEY pkey, out PROPVARIANT pv);
/// <summary>Sets a new property value, or replaces or removes an existing value.</summary>
/// <param name="pkey">The pkey.</param>
/// <param name="pv">The pv.</param>
/// <remarks>
/// <para>
/// <c>Note</c> When this method is implemented under <c>IPropertyStoreCache</c>, be aware that the interface is used only by
/// the in-memory store. Many of the following remarks may not apply in that case.
/// </para>
/// <para>
/// <c>SetValue</c> affects the current property store instance only. A property handler implements <c>SetValue</c> by
/// accumulating property changes in an in-memory data structure. Property changes are written to the stream only when
/// <c>IPropertyStore::Commit</c> is called.
/// </para>
/// <para>If <c>Commit</c> is called on a read-only property store, the property handler determines this and returns STG_E_ACCESSDENIED.</para>
/// <para>
/// In general, errors should be detected and reported by <c>SetValue</c>. However, when processing is deferred until
/// <c>Commit</c> is called, the same error semantics apply.
/// </para>
/// <para>
/// If a value was added or removed as a result of <c>SetValue</c>, subsequent enumerations by <c>IPropertyStore::GetCount</c>
/// and <c>IPropertyStore::GetAt</c> reflect that change and subsequent calls to <c>SetValue</c> reflect the changed value.
/// </para>
/// <para>Adding a New Property</para>
/// <para>If the property value pointed to by key does not exist in the store, <c>SetValue</c> adds the value to the store.</para>
/// <para>Replacing an Existing Property Value</para>
/// <para>If the property value pointed to by the key parameter already exists in the store, the stored value is replaced.</para>
/// <para>Removing an Existing Property</para>
/// <para>Removing a property values from a property store is not supported and could lead to unexpected results.</para>
/// <para>Support for Different Properties</para>
/// <para>
/// A property handler must handle three types of properties in <c>SetValue</c>: new properties introduced by the handler
/// author, pre-existing properties that are germane to the property handler, and other properties that do not fall in either of
/// those categories.
/// </para>
/// <list type="bullet">
/// <item>
/// <term>
/// New properties introduced by the property handler author must be described in a property description XML file. Clients
/// consume a file property handler through a wrapping layer called the Windows Property Provider. This layer delegates calls to
/// <c>SetValue</c> to the property handler after it enforces restrictions on the new property according to the property
/// description XML. Therefore, a handler should not attempt to enforce restrictions itself and must store any values that
/// satisfy the property description XML. If the property description does not provide the necessary information to restrict
/// values for the new property, the handler must alter the value as necessary to store those values successfully. For example,
/// in the property description XML there is no way to specify that an integer-valued property can never be an odd number. If an
/// odd integer were provided to <c>SetValue</c>, the handler is still required to store a value successfully. It could
/// accomplish this by perhaps adding one to the value. If data is lost, <c>SetValue</c> must succeed with the return value INPLACE_S_TRUNCATED.
/// </term>
/// </item>
/// <item>
/// <term>
/// Pre-existing properties that are germane to the handler are those that have a representation in the handler's property
/// storage format. For example, Adobe Acrobat .pdf files have representations for PKEY_Author and PKEY_Comments. These
/// properties are described in a property description XML file provided by Windows. It is likely that the restrictions provided
/// by Windows do not exactly match how the equivalent property must be stored in the author's file format. For example, the
/// author value may be restricted to fewer characters in Acrobat files than in the property description for PKEY_Author. In
/// this case, the handler author must make a best-effort attempt to coerce the property value. If data is lost, <c>SetValue</c>
/// must succeed with the return value INPLACE_S_TRUNCATED. In the unusual case that the value cannot be coerced,
/// <c>SetValue</c> can return E_FAIL. If the author's file format is less restrictive than a property description provided by
/// Windows, the Windows Property Provider layer described earlier will already have returned an error and therefore the
/// property handler's <c>SetValue</c> will never be called to store the value.
/// </term>
/// </item>
/// <item>
/// <term>
/// Properties that are neither new nor pre-existing properties germane to the handler must still be stored by the property
/// handler. The utility class CLSID_InMemoryPropertyStore, provided by Windows, can be used by handler authors to temporarily
/// store such properties in memory and save those properties to a stream for storage in the file.
/// </term>
/// </item>
/// </list>
/// </remarks>
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/bb761475(v=vs.85) HRESULT SetValue( [in]
// REFPROPERTYKEY key, [in] REFPROPVARIANT propvar );
void SetValue(in PROPERTYKEY pkey, in PROPVARIANT pv);
/// <summary>After a change has been made, this method saves the changes.</summary>
/// <remarks>
/// <para>
/// Before the method returns, it releases the file stream or path that was initialized to be used by the method. Therefore, no
/// <c>IPropertyStore</c> methods succeed after returns. At that point, they return E_FAIL.
/// </para>
/// <para>
/// Property handlers must ensure that property changes result in a valid destination file, even if the process terminates
/// abnormally, or encounters any errors.
/// </para>
/// </remarks>
// https://docs.microsoft.com/en-us/windows/win32/api/propsys/nf-propsys-ipropertystore-commit HRESULT Commit();
void Commit();
}
[StructLayout(LayoutKind.Explicit, Pack = 8)]
public struct PROPVARIANT
{
/// <summary>Value type tag.</summary>
[FieldOffset(0)] public VarEnum vt;
/// <summary>Reserved for future use.</summary>
[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;
/// <summary>The BLOB when VT_BLOB</summary>
[FieldOffset(8)] internal BLOB _blob;
/// <summary>The value when a numeric value less than 8 bytes.</summary>
[FieldOffset(8)] internal ulong _ulong;
/// <summary>Initializes a new instance of the <see cref="PROPVARIANT"/> class as VT_EMPTY.</summary>
//public PROPVARIANT()
//{
//}
public PROPVARIANT(string value)
{
ArgumentNullException.ThrowIfNull(value);
vt = VarEnum.VT_LPWSTR;
_ptr = Marshal.StringToCoTaskMemUni(value);
}
public VarEnum VarType { get => (VarEnum)vt; set => vt = (VarEnum)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;
}
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public partial struct PROPERTYKEY
{
/// <summary>
/// <para>Type: <c>GUID</c></para>
/// <para>A unique GUID for the property.</para>
/// </summary>
public Guid fmtid;
/// <summary>
/// <para>Type: <c>DWORD</c></para>
/// <para>
/// A property identifier (PID). This parameter is not used as in SHCOLUMNID. It is recommended that you set this value to
/// PID_FIRST_USABLE. Any value greater than or equal to 2 is acceptable.
/// </para>
/// <para><c>Note</c> Values of 0 and 1 are reserved and should not be used.</para>
/// </summary>
public uint pid;
/// <summary>Initializes a new instance of the <see cref="PROPERTYKEY"/> struct.</summary>
/// <param name="key">The key.</param>
/// <param name="id">The identifier.</param>
public PROPERTYKEY(Guid key, uint id)
{
fmtid = key;
pid = id;
}
public static class System
{
/// <summary>
/// <para>Name: System.Title -- PKEY_Title</para>
/// <para>Description: Title of item.</para>
/// <para>Type: String -- VT_LPWSTR (For variants: VT_BSTR) Legacy code may treat this as VT_LPSTR.</para>
/// <para>FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 2 (PIDSI_TITLE)</para>
/// </summary>
public static PROPERTYKEY Title
=> new(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 2);
}
}
public enum ShowWindowCommand
{
/// <summary>Hides the window and activates another window.</summary>
SW_HIDE = 0,
/// <summary>
/// Activates and displays a window. If the window is minimized or maximized, Windows restores it to its original size and position.
/// An application should specify this flag when displaying the window for the first time.
/// </summary>
SW_SHOWNORMAL = 1,
/// <summary>
/// Activates and displays a window. If the window is minimized or maximized, Windows restores it to its original size and position.
/// An application should specify this flag when displaying the window for the first time.
/// </summary>
SW_NORMAL = 1,
/// <summary>Activates the window and displays it as a minimized window.</summary>
SW_SHOWMINIMIZED = 2,
/// <summary>Activates the window and displays it as a maximized window.</summary>
SW_SHOWMAXIMIZED = 3,
/// <summary>Maximizes the specified window.</summary>
SW_MAXIMIZE = 3,
/// <summary>Displays a window in its most recent size and position. The active window remains active.</summary>
SW_SHOWNOACTIVATE = 4,
/// <summary>Activates the window and displays it in its current size and position.</summary>
SW_SHOW = 5,
/// <summary>Minimizes the specified window and activates the next top-level window in the z-order.</summary>
SW_MINIMIZE = 6,
/// <summary>Displays the window as a minimized window. The active window remains active.</summary>
SW_SHOWMINNOACTIVE = 7,
/// <summary>Displays the window in its current state. The active window remains active.</summary>
SW_SHOWNA = 8,
/// <summary>
/// Activates and displays the window. If the window is minimized or maximized, Windows restores it to its original size and
/// position. An application should specify this flag when restoring a minimized window.
/// </summary>
SW_RESTORE = 9,
/// <summary>
/// Sets the show state based on the SW_ flag specified in the STARTUPINFO structure passed to the CreateProcess function by the
/// program that started the application. An application should call ShowWindow with this flag to set the initial show state of its
/// main window.
/// </summary>
SW_SHOWDEFAULT = 10,
/// <summary>
/// Minimizes a window, even if the thread that owns the window is not responding. This flag should only be used when minimizing
/// windows from a different thread.
/// </summary>
SW_FORCEMINIMIZE = 11,
}
public enum KNOWNDESTCATEGORY
{
/// <summary>Add the Frequent category.</summary>
KDC_FREQUENT = 1,
/// <summary>Add the Recent category.</summary>
KDC_RECENT = 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment