Updated moved classes so they do not depend on System.Windows.Forms. There are lots of breaking changes when compared to Vanara.Windows.Shell, mostly around the use of IWin32Window.

pull/279/head
dahall 2022-01-06 15:11:41 -07:00
parent 34c31238fd
commit f17ed396f2
18 changed files with 338 additions and 268 deletions

View File

@ -2,7 +2,6 @@
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Forms;
using Vanara.InteropServices;
using Vanara.PInvoke;
using static Vanara.PInvoke.Ole32;
@ -73,7 +72,7 @@ namespace Vanara.Windows.Shell
public class FileInUseHandler : IFileIsInUse, IDisposable
{
private bool disposedValue;
private string filePath;
private string appName, filePath;
private IMoniker moniker;
private uint regId;
@ -81,11 +80,12 @@ namespace Vanara.Windows.Shell
/// <param name="filePath">The file path.</param>
/// <param name="parent">The parent.</param>
/// <param name="usageType">Type of the usage.</param>
public FileInUseHandler(string filePath, IWin32Window parent = null, FileUsageType usageType = FileUsageType.Generic)
public FileInUseHandler(string filePath, HWND parent = default, FileUsageType usageType = FileUsageType.Generic)
{
ActivationWindow = parent;
FileUsageType = usageType;
FilePath = filePath;
appName = DefAppName;
}
/// <summary>Finalizes an instance of the <see cref="FileInUseHandler"/> class.</summary>
@ -109,7 +109,14 @@ namespace Vanara.Windows.Shell
/// value is <see langword="null"/>, then the calling application will be told that it cannot activate the file's owning application.
/// </summary>
/// <value>The activation window.</value>
public IWin32Window ActivationWindow { get; set; }
public HWND ActivationWindow { get; set; }
/// <summary>Gets or sets the name of the application using the file.</summary>
/// <value>
/// The name of the application that can be passed to the user in a dialog box so that the user knows the source of the conflict and
/// can act accordingly. For instance "File.txt is in use by Litware.".
/// </value>
public string AppName { get => appName; set => appName = value; }
/// <summary>
/// Gets or sets the full path to the file that is in use by this application. Setting this value will revoke any prior file's
@ -165,7 +172,7 @@ namespace Vanara.Windows.Shell
HRESULT IFileIsInUse.GetAppName(out string ppszName)
{
ppszName = System.Windows.Forms.Application.ProductName;
ppszName = appName;
return HRESULT.S_OK;
}
@ -173,13 +180,13 @@ namespace Vanara.Windows.Shell
{
var cancelArgs = new CancelEventArgs(false);
CloseRequested?.Invoke(this, cancelArgs);
pdwCapFlags = (!cancelArgs.Cancel ? OF_CAP.OF_CAP_CANCLOSE : 0) | (ActivationWindow is null ? 0 : OF_CAP.OF_CAP_CANSWITCHTO);
pdwCapFlags = (!cancelArgs.Cancel ? OF_CAP.OF_CAP_CANCLOSE : 0) | (ActivationWindow.IsNull ? 0 : OF_CAP.OF_CAP_CANSWITCHTO);
return HRESULT.S_OK;
}
HRESULT IFileIsInUse.GetSwitchToHWND(out HWND phwnd)
{
phwnd = ActivationWindow is null ? default : ActivationWindow.Handle;
phwnd = ActivationWindow;
return HRESULT.S_OK;
}
@ -206,6 +213,22 @@ namespace Vanara.Windows.Shell
}
}
private string DefAppName
{
get
{
var asm = System.Reflection.Assembly.GetEntryAssembly();
if (asm is not null)
{
object[] attrs = asm.GetCustomAttributes(typeof(System.Reflection.AssemblyProductAttribute), false);
if (attrs != null && attrs.Length > 0)
return ((System.Reflection.AssemblyProductAttribute)attrs[0]).Product;
return System.IO.Path.GetFileNameWithoutExtension(asm.Location);
}
return "Unknown";
}
}
private void RegisterInROT()
{
GetRunningObjectTable(0, out var rot).ThrowIfFailed();

View File

@ -3,13 +3,13 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using Vanara.Extensions;
using Vanara.InteropServices;
using Vanara.PInvoke;
@ -20,6 +20,25 @@ using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;
namespace Vanara.Windows.Shell
{
/// <summary>Specifies the text data formats that can be used to query, get and set text data format with Clipboard.</summary>
public enum TextDataFormat
{
/// <summary>Specifies the standard ANSI text format.</summary>
Text,
/// <summary>Specifies the standard Windows Unicode text format.</summary>
UnicodeText,
/// <summary>Specifies text consisting of Rich Text Format (RTF) data.</summary>
Rtf,
/// <summary>Specifies text consisting of HTML data.</summary>
Html,
/// <summary>Specifies a comma-separated value (CSV) format, which is a common interchange format used by spreadsheets.</summary>
CommaSeparatedValue,
}
/// <summary>
/// Initializes and closes a session using the Clipboard calling <see cref="OpenClipboard"/> and then <see cref="CloseClipboard"/> on
/// disposal. This can be called multiple times in nested calls and will ensure the Clipboard is only opened and closed at the highest scope.
@ -27,13 +46,16 @@ namespace Vanara.Windows.Shell
/// <seealso cref="System.IDisposable"/>
public class NativeClipboard : IDisposable
{
private readonly bool dontClose = false;
private static readonly object objectLock = new();
private static int HdrLen = 0;
private static Dictionary<uint, string> knownIds;
private static ListenerWindow listener;
[ThreadStatic]
private static bool open = false;
private readonly bool dontClose = false;
/// <summary>Initializes a new instance of the <see cref="NativeClipboard"/> class.</summary>
/// <param name="empty">If set to <see langword="true"/>, <see cref="EmptyClipboard"/> is called to clear the Clipboard.</param>
/// <param name="hWndNewOwner">
@ -80,17 +102,32 @@ namespace Vanara.Windows.Shell
/// <summary>Retrieves the currently supported clipboard formats.</summary>
/// <value>A sequence of the currently supported formats.</value>
public static IEnumerable<DataFormats.Format> CurrentlySupportedFormats
public static IEnumerable<uint> CurrentlySupportedFormats
{
get
{
GetUpdatedClipboardFormats(null, 0, out var cnt);
var fmts = new uint[cnt];
Win32Error.ThrowLastErrorIfFalse(GetUpdatedClipboardFormats(fmts, (uint)fmts.Length, out cnt));
return fmts.Take((int)cnt).Select(u => DataFormats.GetFormat(unchecked((int)u))).ToArray();
return fmts.Take((int)cnt).ToArray();
}
}
/// <summary>Gets or sets a <see cref="IComDataObject"/> instance from the Windows Clipboard.</summary>
/// <value>A <see cref="IComDataObject"/> instance.</value>
public static IComDataObject DataObject
{
get
{
OleGetClipboard(out var idata).ThrowIfFailed();
return idata;
}
set => OleSetClipboard(value).ThrowIfFailed();
}
/// <summary>Carries out the clipboard shutdown sequence. It also releases any IDataObject instances that were placed on the clipboard.</summary>
public static void Flush() => OleFlushClipboard().ThrowIfFailed();
/// <summary>Retrieves the window handle of the current owner of the clipboard.</summary>
/// <returns>
/// <para>If the function succeeds, the return value is the handle to the window that owns the clipboard.</para>
@ -100,7 +137,7 @@ namespace Vanara.Windows.Shell
/// <para>The clipboard can still contain data even if the clipboard is not currently owned.</para>
/// <para>In general, the clipboard owner is the window that last placed data in clipboard.</para>
/// </remarks>
public static IntPtr GetClipboardOwner() => (IntPtr)User32.GetClipboardOwner();
public static HWND GetClipboardOwner() => User32.GetClipboardOwner();
/// <summary>Retrieves the first available clipboard format in the specified list.</summary>
/// <param name="idList">The clipboard formats, in priority order.</param>
@ -111,6 +148,32 @@ namespace Vanara.Windows.Shell
/// </returns>
public static int GetFirstFormatAvailable(params int[] idList) => GetPriorityClipboardFormat(Array.ConvertAll(idList, i => (uint)i), idList.Length);
/// <summary>Retrieves from the clipboard the name of the specified registered format.</summary>
/// <param name="formatId">The type of format to be retrieved.</param>
/// <returns>The format name.</returns>
public static string GetFormatName(uint formatId)
{
EnsureKnownIds();
if (knownIds.TryGetValue(formatId, out var value))
return value;
// Ask sysetm for the registered name
StringBuilder sb = new(80);
int ret;
while (0 != (ret = GetClipboardFormatName(formatId, sb, sb.Capacity)))
{
if (ret < sb.Capacity - 1)
{
knownIds.Add(formatId, sb.ToString());
return sb.ToString();
}
sb.Capacity *= 2;
}
// Failing all elsewhere, return value as hex string
return string.Format(CultureInfo.InvariantCulture, "0x{0:X4}", formatId);
}
/// <summary>Retrieves the handle to the window that currently has the clipboard open.</summary>
/// <returns>
/// If the function succeeds, the return value is the handle to the window that has the clipboard open. If no window has the
@ -120,17 +183,42 @@ namespace Vanara.Windows.Shell
/// If an application or DLL specifies a <c>NULL</c> window handle when calling the OpenClipboard function, the clipboard is opened
/// but is not associated with a window. In such a case, <c>GetOpenClipboardWindow</c> returns <c>IntPtr.Zero</c>.
/// </remarks>
public static IntPtr GetOpenClipboardWindow() => (IntPtr)User32.GetOpenClipboardWindow();
public static HWND GetOpenClipboardWindow() => User32.GetOpenClipboardWindow();
/// <summary>Determines whether the clipboard contains data in the specified format.</summary>
/// <param name="format">The name of a standard or registered clipboard format.</param>
/// <returns>If the clipboard format is available, the return value is <see langword="true"/>; otherwise <see langword="false"/>.</returns>
public static bool IsFormatAvailable(string format) => IsClipboardFormatAvailable((uint)DataFormats.GetFormat(format).Id);
/// <summary>Determines whether the data object pointer previously placed on the clipboard is still on the clipboard.</summary>
/// <param name="dataObject">
/// The IDataObject interface on the data object containing clipboard data of interest, which the caller previously placed on the clipboard.
/// </param>
/// <returns><see langword="true"/> on success; otherwise, <see langword="false"/>.</returns>
public static bool IsCurrentDataObject(IComDataObject dataObject) => OleIsCurrentClipboard(dataObject) == HRESULT.S_OK;
/// <summary>Determines whether the clipboard contains data in the specified format.</summary>
/// <param name="id">A standard or registered clipboard format.</param>
/// <returns>If the clipboard format is available, the return value is <see langword="true"/>; otherwise <see langword="false"/>.</returns>
public static bool IsFormatAvailable(int id) => IsClipboardFormatAvailable((uint)id);
public static bool IsFormatAvailable(uint id) => IsClipboardFormatAvailable(id);
/// <summary>Registers a new clipboard format. This format can then be used as a valid clipboard format.</summary>
/// <param name="format">The name of the new format.</param>
/// <returns>The registered clipboard format identifier.</returns>
/// <exception cref="System.ArgumentNullException">format</exception>
/// <remarks>
/// If a registered format with the specified name already exists, a new format is not registered and the return value identifies the
/// existing format. This enables more than one application to copy and paste data using the same registered clipboard format. Note
/// that the format name comparison is case-insensitive.
/// </remarks>
public static uint RegisterFormat(string format)
{
if (format is null) throw new ArgumentNullException(nameof(format));
EnsureKnownIds();
var id = knownIds.FirstOrDefault(p => p.Value == format).Key;
if (id != 0)
return id;
id = Win32Error.ThrowLastErrorIf(RegisterClipboardFormat(format), v => v == 0);
knownIds.Add(id, format);
return id;
}
/// <summary>
/// Retrieves data from the clipboard in a specified format. The clipboard must have been opened previously and this pointer cannot
@ -153,38 +241,11 @@ namespace Vanara.Windows.Shell
/// <para>
/// The system performs implicit data format conversions between certain clipboard formats when an application calls the
/// <c>GetClipboardData</c> function. For example, if the CF_OEMTEXT format is on the clipboard, a window can retrieve data in the
/// CF_TEXT format. The format on the clipboard is converted to the requested format on demand. For more information, see
/// Synthesized Clipboard Formats.
/// CF_TEXT format. The format on the clipboard is converted to the requested format on demand. For more information, see Synthesized
/// Clipboard Formats.
/// </para>
/// </remarks>
public IntPtr DanagerousGetData(int formatId) => GetClipboardData((uint)formatId);
/// <summary>
/// Retrieves data from the clipboard in a specified format. The clipboard must have been opened previously and this pointer cannot
/// be used once <see cref="NativeClipboard"/> goes out of scope.
/// </summary>
/// <param name="format">A clipboard format. For a description of the standard clipboard formats, see Standard Clipboard Formats.</param>
/// <returns>
/// <para>If the function succeeds, the return value is the handle to a clipboard object in the specified format.</para>
/// <para>If the function fails, the return value is <c>IntPtr.Zero</c>. To get extended error information, call GetLastError.</para>
/// </returns>
/// <remarks>
/// <para><c>Caution</c> Clipboard data is not trusted. Parse the data carefully before using it in your application.</para>
/// <para>An application can enumerate the available formats in advance by using the EnumClipboardFormats function.</para>
/// <para>
/// The clipboard controls the handle that the <c>GetClipboardData</c> function returns, not the application. The application should
/// copy the data immediately. The application must not free the handle nor leave it locked. The application must not use the handle
/// after the <see cref="Empty"/> method is called, after <see cref="NativeClipboard"/> is disposed, or after any of the
/// <c>Set...</c> methods are called with the same clipboard format.
/// </para>
/// <para>
/// The system performs implicit data format conversions between certain clipboard formats when an application calls the
/// <c>GetClipboardData</c> function. For example, if the CF_OEMTEXT format is on the clipboard, a window can retrieve data in the
/// CF_TEXT format. The format on the clipboard is converted to the requested format on demand. For more information, see
/// Synthesized Clipboard Formats.
/// </para>
/// </remarks>
public IntPtr DanagerousGetData(string format) => GetClipboardData((uint)DataFormats.GetFormat(format).Id);
public IntPtr DanagerousGetData(uint formatId) => GetClipboardData(formatId);
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose()
@ -214,39 +275,26 @@ namespace Vanara.Windows.Shell
/// enumerates the specified format, then enumerates the formats to which it can be converted.
/// </para>
/// </remarks>
public IEnumerable<DataFormats.Format> EnumAvailableFormats() => EnumClipboardFormats().Select(i => DataFormats.GetFormat((int)i));
/// <summary>Gets a <see cref="ShellDataObject"/> instance from the Windows Clipboard.</summary>
/// <returns>A <see cref="ShellDataObject"/> instance from the Windows Clipboard.</returns>
public ShellDataObject GetShellDataObject() => new ShellDataObject(Clipboard.GetDataObject());
public IEnumerable<uint> EnumAvailableFormats() => EnumClipboardFormats();
/// <summary>Gets the text from the native Clipboard in the specified format.</summary>
/// <param name="format">The format.</param>
/// <param name="formatId">A clipboard format. For a description of the standard clipboard formats, see Standard Clipboard Formats.</param>
/// <returns>The string value or <see langword="null"/> if the format is not available.</returns>
public string GetText(TextDataFormat format)
public string GetText(TextDataFormat formatId) => formatId switch
{
return format switch
{
TextDataFormat.Text => StringHelper.GetString(GetClipboardData(CLIPFORMAT.CF_TEXT), CharSet.Ansi),
TextDataFormat.UnicodeText => StringHelper.GetString(GetClipboardData(CLIPFORMAT.CF_UNICODETEXT), CharSet.Unicode),
TextDataFormat.Rtf => StringHelper.GetString(DanagerousGetData(DataFormats.Rtf), CharSet.Ansi),
TextDataFormat.Html => GetHtml(DanagerousGetData(DataFormats.Html)),
TextDataFormat.CommaSeparatedValue => StringHelper.GetString(DanagerousGetData(DataFormats.CommaSeparatedValue), CharSet.Ansi),
_ => null,
};
}
/// <summary>Places data on the clipboard in a specified clipboard format.</summary>
/// <param name="format">The clipboard format. This parameter can be a registered format or any of the standard clipboard formats.</param>
/// <param name="data">The binary data in the specified format.</param>
/// <exception cref="System.ArgumentNullException">data</exception>
public void SetBinaryData(string format, byte[] data) => SetBinaryData(DataFormats.GetFormat(format).Id, data);
TextDataFormat.Text => StringHelper.GetString(GetClipboardData(CLIPFORMAT.CF_TEXT), CharSet.Ansi),
TextDataFormat.UnicodeText => StringHelper.GetString(GetClipboardData(CLIPFORMAT.CF_UNICODETEXT), CharSet.Unicode),
TextDataFormat.Rtf => StringHelper.GetString(DanagerousGetData(RegisterFormat(ShellClipboardFormat.CF_RTF)), CharSet.Ansi),
TextDataFormat.Html => GetHtml(DanagerousGetData(RegisterFormat(ShellClipboardFormat.CF_HTML))),
TextDataFormat.CommaSeparatedValue => StringHelper.GetString(DanagerousGetData(RegisterFormat(ShellClipboardFormat.CF_CSV)), CharSet.Ansi),
_ => null,
};
/// <summary>Places data on the clipboard in a specified clipboard format.</summary>
/// <param name="formatId">The clipboard format. This parameter can be a registered format or any of the standard clipboard formats.</param>
/// <param name="data">The binary data in the specified format.</param>
/// <exception cref="System.ArgumentNullException">data</exception>
public void SetBinaryData(int formatId, byte[] data)
public void SetBinaryData(uint formatId, byte[] data)
{
using var pMem = new SafeMoveableHGlobalHandle(data);
Win32Error.ThrowLastErrorIfInvalid(pMem);
@ -256,7 +304,7 @@ namespace Vanara.Windows.Shell
/// <summary>Places data on the clipboard in a specified clipboard format.</summary>
/// <param name="formatId">The clipboard format. This parameter can be a registered format or any of the standard clipboard formats.</param>
/// <param name="data">The data in the format dictated by <paramref name="formatId"/>.</param>
public void SetData<T>(int formatId, T data)
public void SetData<T>(uint formatId, T data)
{
using var pMem = SafeMoveableHGlobalHandle.CreateFromStructure(data);
Win32Error.ThrowLastErrorIfInvalid(pMem);
@ -266,7 +314,7 @@ namespace Vanara.Windows.Shell
/// <summary>Places data on the clipboard in a specified clipboard format.</summary>
/// <param name="formatId">The clipboard format. This parameter can be a registered format or any of the standard clipboard formats.</param>
/// <param name="values">The data in the format dictated by <paramref name="formatId"/>.</param>
public void SetData<T>(int formatId, IEnumerable<T> values) where T : struct
public void SetData<T>(uint formatId, IEnumerable<T> values) where T : struct
{
using var pMem = SafeMoveableHGlobalHandle.CreateFromList(values);
Win32Error.ThrowLastErrorIfInvalid(pMem);
@ -278,7 +326,7 @@ namespace Vanara.Windows.Shell
/// <param name="values">The list of strings.</param>
/// <param name="packing">The packing type for the strings.</param>
/// <param name="charSet">The character set to use for the strings.</param>
public void SetData(int formatId, IEnumerable<string> values, StringListPackMethod packing = StringListPackMethod.Concatenated, CharSet charSet = CharSet.Auto)
public void SetData(uint formatId, IEnumerable<string> values, StringListPackMethod packing = StringListPackMethod.Concatenated, CharSet charSet = CharSet.Auto)
{
using var pMem = SafeMoveableHGlobalHandle.CreateFromStringList(values, packing, charSet);
Win32Error.ThrowLastErrorIfInvalid(pMem);
@ -294,7 +342,7 @@ namespace Vanara.Windows.Shell
if (text is null && htmlText is null && rtfText is null) return;
SetText(text, TextDataFormat.Text);
SetText(text, TextDataFormat.UnicodeText);
SetBinaryData(DataFormats.Locale, BitConverter.GetBytes(CultureInfo.CurrentCulture.LCID));
SetBinaryData(RegisterFormat("Locale"), BitConverter.GetBytes(CultureInfo.CurrentCulture.LCID));
if (htmlText != null) SetText(htmlText, TextDataFormat.Html);
if (rtfText != null) SetText(rtfText, TextDataFormat.Rtf);
}
@ -304,13 +352,13 @@ namespace Vanara.Windows.Shell
/// <param name="format">The clipboard text format to set.</param>
public void SetText(string value, TextDataFormat format)
{
(byte[] bytes, int fmt) = format switch
(byte[] bytes, uint fmt) = format switch
{
TextDataFormat.Text => (Encoding.ASCII.GetBytes(value + '\0'), (int)CLIPFORMAT.CF_TEXT),
TextDataFormat.UnicodeText => (Encoding.Unicode.GetBytes(value + '\0'), (int)CLIPFORMAT.CF_UNICODETEXT),
TextDataFormat.Rtf => (Encoding.ASCII.GetBytes(value + '\0'), DataFormats.GetFormat(DataFormats.Rtf).Id),
TextDataFormat.Html => (MakeClipHtml(value), DataFormats.GetFormat(DataFormats.Html).Id),
TextDataFormat.CommaSeparatedValue => (Encoding.ASCII.GetBytes(value + '\0'), DataFormats.GetFormat(DataFormats.CommaSeparatedValue).Id),
TextDataFormat.Text => (Encoding.ASCII.GetBytes(value + '\0'), (uint)CLIPFORMAT.CF_TEXT),
TextDataFormat.UnicodeText => (Encoding.Unicode.GetBytes(value + '\0'), (uint)CLIPFORMAT.CF_UNICODETEXT),
TextDataFormat.Rtf => (Encoding.ASCII.GetBytes(value + '\0'), RegisterFormat(ShellClipboardFormat.CF_RTF)),
TextDataFormat.Html => (MakeClipHtml(value), RegisterFormat(ShellClipboardFormat.CF_HTML)),
TextDataFormat.CommaSeparatedValue => (Encoding.ASCII.GetBytes(value + '\0'), RegisterFormat(ShellClipboardFormat.CF_CSV)),
_ => default,
};
SetBinaryData(fmt, bytes);
@ -325,34 +373,8 @@ namespace Vanara.Windows.Shell
if (url is null) throw new ArgumentNullException(nameof(url));
SetText(url, $"<a href=\"{url}\">{title ?? url}</a>", null);
var textUrl = url + (title is null ? "" : ('\n' + title)) + '\0';
SetBinaryData(ShellClipboardFormat.CFSTR_INETURLA, Encoding.ASCII.GetBytes(textUrl));
SetBinaryData(ShellClipboardFormat.CFSTR_INETURLW, Encoding.Unicode.GetBytes(textUrl));
}
internal static StringCollection ToSC(IEnumerable<string> e)
{
var sc = new StringCollection();
if (e != null)
sc.AddRange(e.ToArray());
return sc;
}
private static T GetComData<T>(DataObject dobj, string fmt, Func<IntPtr, T> convert, T defValue = default)
{
T ret = defValue;
if (dobj is IComDataObject cdo)
{
var fc = new FORMATETC { cfFormat = (short)DataFormats.GetFormat(fmt).Id, dwAspect = DVASPECT.DVASPECT_CONTENT, lindex = -1, tymed = TYMED.TYMED_HGLOBAL };
try
{
cdo.GetData(ref fc, out var medium);
if (medium.unionmember != default)
ret = convert(medium.unionmember);
ReleaseStgMedium(medium);
}
catch { }
}
return ret;
SetBinaryData(RegisterFormat(ShellClipboardFormat.CFSTR_INETURLA), Encoding.ASCII.GetBytes(textUrl));
SetBinaryData(RegisterFormat(ShellClipboardFormat.CFSTR_INETURLW), Encoding.Unicode.GetBytes(textUrl));
}
internal static string GetHtml(IntPtr ptr)
@ -383,6 +405,37 @@ namespace Vanara.Windows.Shell
return Encoding.UTF8.GetString(bytes, startFrag, endFrag - startFrag);
}
internal static StringCollection ToSC(IEnumerable<string> e)
{
var sc = new StringCollection();
if (e != null)
sc.AddRange(e.ToArray());
return sc;
}
private static void EnsureKnownIds()
{
if (knownIds is not null)
return;
var type = typeof(CLIPFORMAT);
knownIds = type.GetFields(BindingFlags.Static | BindingFlags.Public).Where(f => f.FieldType == type && f.IsInitOnly).ToDictionary(f => (uint)f.GetValue(null), f => f.Name);
}
private static T GetComData<T>(IComDataObject cdo, uint fmt, Func<IntPtr, T> convert, T defValue = default)
{
T ret = defValue;
var fc = new FORMATETC { cfFormat = (short)fmt, dwAspect = DVASPECT.DVASPECT_CONTENT, lindex = -1, tymed = TYMED.TYMED_HGLOBAL };
try
{
cdo.GetData(ref fc, out var medium);
if (medium.unionmember != default)
ret = convert(medium.unionmember);
ReleaseStgMedium(medium);
}
catch { }
return ret;
}
private static byte[] MakeClipHtml(string value)
{
const string Header = "Version:0.9\r\nStartHTML:{0:0000000000}\r\nEndHTML:{1:0000000000}\r\nStartFragment:{2:0000000000}\r\nEndFragment:{3:0000000000}\r\nStartSelection:{4:0000000000}\r\nEndSelection:{5:0000000000}\r\n";
@ -449,60 +502,28 @@ namespace Vanara.Windows.Shell
thread.Join();
}
/*private class ListenerWindow : BasicMessageWindow //SystemEventHandler
private class ListenerWindow : SystemEventHandler
{
public ListenerWindow() : base(MessageFilterProc)
{
Win32Error.ThrowLastErrorIfFalse(AddClipboardFormatListener(Handle));
}
//protected override void OnMessageWindowHandleCreated()
//{
// Win32Error.ThrowLastErrorIfFalse(AddClipboardFormatListener(MessageWindowHandle));
// base.OnMessageWindowHandleCreated();
//}
//protected override
static bool MessageFilterProc(HWND hwnd, uint msg, IntPtr wParam, IntPtr lParam, out IntPtr lReturn)
protected override bool MessageFilter(HWND hwnd, uint msg, IntPtr wParam, IntPtr lParam, out IntPtr lReturn)
{
lReturn = default;
switch (msg)
{
case (uint)ClipboardNotificationMessage.WM_CLIPBOARDUPDATE:
InternalClipboardUpdate?.Invoke(null, EventArgs.Empty);
return true;
case (uint)WindowMessage.WM_DESTROY:
RemoveClipboardFormatListener(hwnd);
RemoveClipboardFormatListener(MessageWindowHandle);
break;
case (uint)ClipboardNotificationMessage.WM_CLIPBOARDUPDATE:
InternalClipboardUpdate?.Invoke(this, EventArgs.Empty);
break;
}
return false;
}
}*/
private class ListenerWindow : NativeWindow, IDisposable
{
public ListenerWindow()
protected override void OnMessageWindowHandleCreated()
{
var cp = new CreateParams { Style = 0, ExStyle = 0, ClassStyle = 0, Parent = IntPtr.Zero, Caption = GetType().Name };
CreateHandle(cp);
AddClipboardFormatListener(Handle);
}
void IDisposable.Dispose() => base.DestroyHandle();
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case (int)WindowMessage.WM_DESTROY:
RemoveClipboardFormatListener(Handle);
break;
case (int)ClipboardNotificationMessage.WM_CLIPBOARDUPDATE:
InternalClipboardUpdate?.Invoke(this, EventArgs.Empty);
break;
}
base.WndProc(ref m);
base.OnMessageWindowHandleCreated();
AddClipboardFormatListener(MessageWindowHandle);
}
}

View File

@ -309,11 +309,11 @@ namespace Vanara.Windows.Shell
if (items.Length == 1)
{
ComInterface.Invoke(new ShellDataObject(items)).ThrowIfFailed();
ComInterface.Invoke(items[0].DataObject).ThrowIfFailed();
}
else
{
ComInterface.CreateInvoker(new ShellDataObject(items), out var invoker).ThrowIfFailed();
ComInterface.CreateInvoker(CreateDataObj(items), out var invoker).ThrowIfFailed();
using var pInvoker = ComReleaserFactory.Create(invoker);
var hr = invoker.SupportsSelection();
if (hr == HRESULT.S_FALSE)
@ -328,6 +328,16 @@ namespace Vanara.Windows.Shell
/// <para>A string that contains the display name of the application.</para>
/// </param>
public void MakeDefault(string description) => ComInterface.MakeDefault(description).ThrowIfFailed();
private static System.Runtime.InteropServices.ComTypes.IDataObject CreateDataObj(IEnumerable<ShellItem> items)
{
if ((items?.Count() ?? 0) == 0)
throw new ArgumentNullException(nameof(items));
if (items is not ShellItemArray litems)
litems = new ShellItemArray(items);
return litems.ToDataObject();
}
}
}
}

View File

@ -29,8 +29,8 @@ namespace Vanara.Windows.Shell
/// <summary>Gets the CLSID for the specified type.</summary>
/// <param name="type">The type.</param>
/// <returns>The CLSID value for the type. Calls <see cref="Marshal.GenerateGuidForType"/> to get the value.</returns>
public static Guid CLSID(this Type type) => Marshal.GenerateGuidForType(type);
/// <returns>The CLSID value for the type. Calls <see cref="Type.GUID"/> to get the value.</returns>
public static Guid CLSID(this Type type) => type.GUID;
/// <summary>Determines if the specified type is registered as a COM Local Server.</summary>
/// <typeparam name="TComObject">The type of the COM object.</typeparam>
@ -58,7 +58,7 @@ namespace Vanara.Windows.Shell
return true;
}
#if !(NETCOREAPP3_0 || NETCOREAPP3_1 || NET5_0_OR_GREATER)
#if NETFRAMEWORK
/// <summary>Registers the specified type as a COM Local Server.</summary>
/// <typeparam name="TComObject">The type of the COM object.</typeparam>

View File

@ -240,46 +240,4 @@ namespace Vanara.Windows.Shell
};
}
}
internal static class TaskAgg
{
public static Task CompletedTask
{
get
{
#if NET20 || NET35 || NET40 || NET45
return TaskExEx.CompletedTask;
#else
return Task.CompletedTask;
#endif
}
}
public static Task Run(Action action, CancellationToken cancellationToken)
{
#if NET20 || NET35 || NET40
return TaskEx.Run(action, cancellationToken);
#else
return Task.Run(action, cancellationToken);
#endif
}
public static Task<T> Run<T>(Func<T> action, CancellationToken cancellationToken)
{
#if NET20 || NET35 || NET40
return TaskEx.Run(action, cancellationToken);
#else
return Task.Run(action, cancellationToken);
#endif
}
public static Task WhenAll(IEnumerable<Task> tasks)
{
#if NET20 || NET35 || NET40
return TaskEx.WhenAll(tasks);
#else
return Task.WhenAll(tasks);
#endif
}
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Vanara.Windows.Shell
{
internal static class TaskAgg
{
public static Task CompletedTask
{
get
{
#if NET20 || NET35 || NET40 || NET45
return TaskExEx.CompletedTask;
#else
return Task.CompletedTask;
#endif
}
}
public static Task Run(Action action, CancellationToken cancellationToken)
{
#if NET20 || NET35 || NET40
return TaskEx.Run(action, cancellationToken);
#else
return Task.Run(action, cancellationToken);
#endif
}
public static Task<T> Run<T>(Func<T> action, CancellationToken cancellationToken)
{
#if NET20 || NET35 || NET40
return TaskEx.Run(action, cancellationToken);
#else
return Task.Run(action, cancellationToken);
#endif
}
public static Task WhenAll(IEnumerable<Task> tasks)
{
#if NET20 || NET35 || NET40
return TaskEx.WhenAll(tasks);
#else
return Task.WhenAll(tasks);
#endif
}
}
}

View File

@ -40,7 +40,6 @@ namespace Vanara.Windows.Shell
/// need to implement your own interfaces. The IClassFactory implementation can get any derived interfaces through casting for calls to
/// its QueryInterface method. If you want more control, override the QueryInterface method in this class.
/// </summary>
/// <remarks>See <see cref="ShellDropTarget"/> for an example of how to use this class to create a local COM server.</remarks>
/// <seealso cref="IDisposable"/>
/// <seealso cref="IObjectWithSite"/>
public abstract class ComObject : IComObject, IDisposable, IObjectWithSite

View File

@ -141,7 +141,7 @@ namespace Vanara.Windows.Shell
/// <summary>Gets the shell item attributes.</summary>
/// <value>The shell item attributes.</value>
public ShellItemAttribute ShellAttributes { get; private set; }
public SFGAO ShellAttributes { get; private set; }
/// <summary>Gets the small icon for the file.</summary>
public Icon SmallIcon => GetIcon(ShellIconType.Small);
@ -178,7 +178,7 @@ namespace Vanara.Windows.Shell
var ret = SHGetFileInfo(FullPath, 0, ref shfi, SHFILEINFO.Size, SHGFI.SHGFI_DISPLAYNAME | SHGFI.SHGFI_TYPENAME | SHGFI.SHGFI_ATTRIBUTES);
if (ret != IntPtr.Zero)
{
ShellAttributes = (ShellItemAttribute)shfi.dwAttributes;
ShellAttributes = (SFGAO)shfi.dwAttributes;
TypeName = shfi.szTypeName.Clone().ToString();
DisplayName = shfi.szDisplayName.Clone().ToString();
}

View File

@ -2,7 +2,7 @@
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using Vanara.PInvoke;
using static Vanara.PInvoke.Shell32;
namespace Vanara.Windows.Shell
@ -287,11 +287,10 @@ namespace Vanara.Windows.Shell
/// <param name="owner">
/// A value that represents the window of the owner window for the common dialog box. This value can be <see langword="null"/>.
/// </param>
public void Start(IWin32Window owner)
public void Start(HWND owner = default)
{
if (owner is null) owner = Form.ActiveForm;
iProgressDialog = new IOperationsProgressDialog();
iProgressDialog.StartProgressDialog(owner?.Handle ?? default, DialogFlags);
iProgressDialog.StartProgressDialog(owner, DialogFlags);
iProgressDialog.SetOperation((SPACTION)operation);
iProgressDialog.SetMode((PDMODE)mode);
UpdateLocations();

View File

@ -3,7 +3,6 @@ using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Vanara.Collections;
using Vanara.InteropServices;
using Vanara.PInvoke;
@ -22,16 +21,16 @@ namespace Vanara.Windows.Shell
private bool disposedValue = false;
private IFileOperation op;
private OperationFlags opFlags = defaultOptions;
private IWin32Window owner;
private HWND owner;
private IFileOperationProgressSink sink;
private uint sinkCookie;
/// <summary>Initializes a new instance of the <see cref="ShellFileOperations"/> class.</summary>
/// <param name="owner">The window that owns the modal dialog. This value can be <see langword="null"/>.</param>
public ShellFileOperations(IWin32Window owner = null)
public ShellFileOperations(HWND owner = default)
{
op = new IFileOperation();
if (owner != null) op.SetOwnerWindow(owner.Handle);
if (owner != default) op.SetOwnerWindow(owner);
sink = new OpSink(this);
sinkCookie = op.Advise(sink);
}
@ -105,10 +104,10 @@ namespace Vanara.Windows.Shell
/// <summary>Gets or sets the parent or owner window for progress and dialog windows.</summary>
/// <value>The owner window of the operation. This window will receive error messages.</value>
public IWin32Window OwnerWindow
public HWND OwnerWindow
{
get => owner;
set => op.SetOwnerWindow((owner = value)?.Handle ?? default);
set => op.SetOwnerWindow(owner = value);
}
/// <summary>Gets the number of queued operations.</summary>

View File

@ -87,7 +87,7 @@ namespace Vanara.Windows.Shell
public static SafeHICON GetSystemIconHandle(int index, ShellImageSize iconSize = ShellImageSize.Large)
{
SHGetImageList((SHIL)iconSize, typeof(IImageList).GUID, out var il).ThrowIfFailed();
return il.GetIcon(index, IMAGELISTDRAWFLAGS.ILD_TRANSPARENT);
return ((IImageList)il).GetIcon(index, IMAGELISTDRAWFLAGS.ILD_TRANSPARENT);
}
/// <summary>Given a pixel size, return the ShellImageSize value with the closest size.</summary>

View File

@ -3,8 +3,6 @@ using System.ComponentModel;
using System.Drawing.Design;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using Vanara.Extensions;
using Vanara.PInvoke;
using static Vanara.PInvoke.User32;
@ -245,7 +243,7 @@ namespace Vanara.Windows.Shell
/// <summary>Gets or sets the path of the shell item to watch.</summary>
/// <value>The path of the shell item to monitor. The default is <see langword="null"/>.</value>
[DefaultValue(null), Category("Data"), Description("The shell item to watch."), Editor(typeof(FileNameEditor), typeof(UITypeEditor))]
[DefaultValue(null), Category("Data"), Description("The shell item to watch.")]//, Editor(typeof(FileNameEditor), typeof(UITypeEditor))]
public string Path
{
get => item is null ? null : (item.IsFileSystem ? item.FileSystemPath : item.GetDisplayName(ShellItemDisplayString.DesktopAbsoluteParsing));

View File

@ -1,13 +1,7 @@
#if !(NET5_0_OR_GREATER || NETCOREAPP3_1_OR_GREATER)
#define HASMENU
#endif
// Credit due to Gong-Shell from which this was largely taken.
// Credit due to Gong-Shell from which this was largely taken.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Vanara.Extensions;
using Vanara.InteropServices;
using Vanara.PInvoke;
@ -24,17 +18,15 @@ namespace Vanara.Windows.Shell
/// be shown.
/// </para>
/// <para>
/// To display a shell context menu in a Form's main menu, call the <c>Populate</c> or <see cref="GetItems"/> methods to populate the
/// menu. In addition, you must intercept a number of special messages that will be sent to the menu's parent form. To do this, you
/// must override <see cref="Form.WndProc"/> like so:
/// To display a shell context menu in a Form's main menu, call the <see cref="GetItems"/> methods to populate the menu. In addition,
/// you must intercept a number of special messages that will be sent to the menu's parent form by hooking its message loop as in the
/// following WinForms example:
/// </para>
/// <code>
///protected override void WndProc(ref Message m) {
///if ((m_ContextMenu == null) || (!m_ContextMenu.HandleMenuMessage(ref m))) {
///base.WndProc(ref m);
///}
///}
/// </code>
/// <code>protected override void WndProc(ref Message m) {
/// if ((m_ContextMenu == null) || (!m_ContextMenu.HandleMenuMessage(ref m))) {
/// base.WndProc(ref m);
/// }
/// }</code>
/// <para>Where m_ContextMenu is the <see cref="ShellContextMenu"/> being shown.</para>
/// Standard menu commands can also be invoked from this class, for example <see cref="InvokeDelete"/> and <see cref="InvokeRename"/>.
/// </remarks>
@ -317,7 +309,7 @@ namespace Vanara.Windows.Shell
return ComInterface.GetCommandString((IntPtr)command, stringType, default, mStr, (uint)mStr.Capacity).Succeeded ? mStr : null;
}
#if HASMENU
#if WINFORMS && HASMENU
/// <summary>Populates a <see cref="Menu"/> with the context menu items for a shell item.</summary>
/// <param name="menu">The menu to populate.</param>
/// <param name="menuOptions">The flags to pass to <see cref="IContextMenu.QueryContextMenu"/>.</param>

View File

@ -180,9 +180,9 @@ namespace Vanara.Windows.Shell
/// <param name="filter">A filter for the types of children to enumerate.</param>
/// <param name="parentWindow">The parent window.</param>
/// <returns>An enumerated list of children matching the filter.</returns>
public IEnumerable<ShellItem> EnumerateChildren(FolderItemFilter filter /*= FolderItemFilter.Folders | FolderItemFilter.IncludeHidden | FolderItemFilter.NonFolders | FolderItemFilter.IncludeSuperHidden */, System.Windows.Forms.IWin32Window parentWindow = null)
public IEnumerable<ShellItem> EnumerateChildren(FolderItemFilter filter /*= FolderItemFilter.Folders | FolderItemFilter.IncludeHidden | FolderItemFilter.NonFolders | FolderItemFilter.IncludeSuperHidden */, HWND parentWindow = default)
{
if (iShellFolder.EnumObjects(IWin2Ptr(parentWindow, false), (SHCONTF)filter, out var eo).Failed)
if (iShellFolder.EnumObjects(parentWindow, (SHCONTF)filter, out var eo).Failed)
Debug.WriteLine($"Unable to enum children in folder.");
foreach (var p in eo.Enumerate(20))
{
@ -198,10 +198,10 @@ namespace Vanara.Windows.Shell
/// <param name="parentWindow">The owner window that the client should specify if it displays a dialog box or message box..</param>
/// <param name="children">The file objects or subfolders relative to the parent folder for which to get the interface.</param>
/// <returns>The interface pointer requested.</returns>
public TInterface GetChildrenUIObjects<TInterface>(System.Windows.Forms.IWin32Window parentWindow, params ShellItem[] children) where TInterface : class
public TInterface GetChildrenUIObjects<TInterface>(HWND parentWindow, params ShellItem[] children) where TInterface : class
{
if (children is null || children.Length == 0 || children.Any(i => i is null || !IsChild(i))) throw new ArgumentException("At least one child ShellItem instances is null or is not a child of this folder.");
return iShellFolder.GetUIObjectOf<TInterface>(IWin2Ptr(parentWindow), Array.ConvertAll(children, i => (IntPtr)i.PIDL.LastId));
return iShellFolder.GetUIObjectOf<TInterface>(parentWindow, Array.ConvertAll(children, i => (IntPtr)i.PIDL.LastId));
}
/// <summary>Returns an enumerator that iterates through the collection.</summary>
@ -212,8 +212,8 @@ namespace Vanara.Windows.Shell
/// <typeparam name="TInterface">The interface to retrieve, typically IShellView.</typeparam>
/// <param name="parentWindow">The owner window.</param>
/// <returns>The interface pointer requested.</returns>
public TInterface GetViewObject<TInterface>(System.Windows.Forms.IWin32Window parentWindow) where TInterface : class =>
iShellFolder.CreateViewObject<TInterface>(IWin2Ptr(parentWindow));
public TInterface GetViewObject<TInterface>(HWND parentWindow) where TInterface : class =>
iShellFolder.CreateViewObject<TInterface>(parentWindow);
/// <summary>Determines if the supplied <see cref="ShellItem"/> is an immediate descendant of this folder.</summary>
/// <param name="item">The child item to test.</param>
@ -226,9 +226,9 @@ namespace Vanara.Windows.Shell
/// <param name="displayType">The display type.</param>
/// <param name="parentWindow">The parent window to use if any messages need to be shown the user.</param>
/// <returns>A reference to the newly named item.</returns>
public ShellItem RenameChild(PIDL relativeChildPidl, string newName, ShellItemDisplayString displayType, System.Windows.Forms.IWin32Window parentWindow)
public ShellItem RenameChild(PIDL relativeChildPidl, string newName, ShellItemDisplayString displayType, HWND parentWindow)
{
iShellFolder.SetNameOf(IWin2Ptr(parentWindow), relativeChildPidl, newName, (SHGDNF)displayType, out PIDL newPidl);
iShellFolder.SetNameOf(parentWindow, relativeChildPidl, newName, (SHGDNF)displayType, out PIDL newPidl);
return this[newPidl];
}
@ -236,8 +236,6 @@ namespace Vanara.Windows.Shell
/// <returns>An <see cref="System.Collections.IEnumerator"/> object that can be used to iterate through the collection.</returns>
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
internal static HWND IWin2Ptr(System.Windows.Forms.IWin32Window wnd, bool desktopIfNull = true) => wnd?.Handle ?? (desktopIfNull ? User32.FindWindow("Progman", null) : default);
private IShellFolder GetInstance() => iShellItem.BindToHandler<IShellFolder>(null, BHID.BHID_SFObject.Guid());
}
}

View File

@ -463,8 +463,8 @@ namespace Vanara.Windows.Shell
/// <value>The context menu.</value>
public ShellContextMenu ContextMenu => menu ??= new ShellContextMenu(this);
/// <summary>Returns a <see cref="System.Windows.Forms.DataObject"/> representing the item. This object is used in drag and drop operations.</summary>
public System.Windows.Forms.DataObject DataObject => new(GetHandler<IDataObject>(BHID.BHID_SFUIObject));
/// <summary>Returns a <see cref="IDataObject"/> representing the item. This object is used in drag and drop operations.</summary>
public IDataObject DataObject => GetHandler<IDataObject>(BHID.BHID_SFUIObject);
/// <summary>Gets the <see cref="ShellFileInfo"/> corresponding to this instance.</summary>
public ShellFileInfo FileInfo => IsFileSystem ? new ShellFileInfo(PIDL) : null;
@ -734,7 +734,7 @@ namespace Vanara.Windows.Shell
if (qi is null)
try
{
qi = (Parent ?? ShellFolder.Desktop).GetChildrenUIObjects<IQueryInfo>(null, this);
qi = (Parent ?? ShellFolder.Desktop).GetChildrenUIObjects<IQueryInfo>(default, this);
}
catch { }
if (qi is null) return "";

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Vanara.Extensions;
using Vanara.PInvoke;
using static Vanara.PInvoke.Shell32;
@ -184,9 +183,9 @@ namespace Vanara.Windows.Shell
/// <param name="allowUnindexableLocations">
/// if set to <c>true</c> do not display a warning dialog to the user in collisions that concern network locations that cannot be indexed.
/// </param>
public void ShowLibraryManagementDialog(IWin32Window parentWindow = null, string title = null, string instruction = null, bool allowUnindexableLocations = false)
public void ShowLibraryManagementDialog(HWND parentWindow = default, string title = null, string instruction = null, bool allowUnindexableLocations = false)
{
SHShowManageLibraryUI(iShellItem, parentWindow?.Handle ?? IntPtr.Zero, title, instruction,
SHShowManageLibraryUI(iShellItem, parentWindow, title, instruction,
allowUnindexableLocations ? LIBRARYMANAGEDIALOGOPTIONS.LMD_ALLOWUNINDEXABLENETWORKLOCATIONS : LIBRARYMANAGEDIALOGOPTIONS.LMD_DEFAULT).ThrowIfFailed();
}

View File

@ -1,12 +1,9 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Security.AccessControl;
using System.Security.Permissions;
using System.Windows.Forms;
using Vanara.Extensions;
using Vanara.InteropServices;
using Vanara.PInvoke;
@ -101,8 +98,8 @@ namespace Vanara.Windows.Shell
/// <param name="resolveFlags">The resolve flags.</param>
/// <param name="timeOut">The time out.</param>
/// <exception cref="System.ArgumentNullException">linkFile</exception>
public ShellLink(string linkFile, LinkResolution resolveFlags = LinkResolution.NoUI, IWin32Window window = null, TimeSpan timeOut = default) : base(linkFile) =>
LoadAndResolve(linkFile, (SLR_FLAGS)resolveFlags, ShellFolder.IWin2Ptr(window), (ushort)timeOut.TotalMilliseconds);
public ShellLink(string linkFile, LinkResolution resolveFlags = LinkResolution.NoUI, HWND window = default, TimeSpan timeOut = default) : base(linkFile) =>
LoadAndResolve(linkFile, (SLR_FLAGS)resolveFlags, window, (ushort)timeOut.TotalMilliseconds);
/// <summary>
/// Initializes a new instance of the <see cref="ShellLink"/> class and sets many properties. This link is not saved as a file.
@ -147,10 +144,38 @@ namespace Vanara.Windows.Shell
}
/// <summary>Gets/sets the HotKey to start the shortcut (if any).</summary>
public Keys HotKey
/// <value>
/// <para>
/// 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>
/// </value>
public ushort HotKey
{
get { var hk = link.GetHotKey(); return (Keys)MAKELONG(LOBYTE(hk), HIBYTE(hk)); }
set { link.SetHotKey(MAKEWORD((byte)LOWORD((uint)value), (byte)HIWORD((uint)value))); Save(); }
get => link.GetHotKey();
set { link.SetHotKey(value); Save(); }
}
/// <summary>Gets the index of this icon within the icon path's resources.</summary>
@ -202,10 +227,10 @@ namespace Vanara.Windows.Shell
/// <summary>Gets or sets the show command for a Shell link object.</summary>
/// <value>The show command for a Shell link object.</value>
public FormWindowState ShowState
public ShowWindowCommand ShowState
{
get => (FormWindowState)link.GetShowCmd() - 1;
set { link.SetShowCmd((ShowWindowCommand)value + 1); Save(); }
get => link.GetShowCmd() - 1;
set { link.SetShowCmd(value + 1); Save(); }
}
/// <summary>Gets or sets the target with a <see cref="ShellItem"/> instance.</summary>
@ -360,8 +385,9 @@ namespace Vanara.Windows.Shell
if (resolveFlags.IsFlagSet(SLR_FLAGS.SLR_NO_UI) && timeOut != 0)
resolveFlags = (SLR_FLAGS)MAKELONG((ushort)resolveFlags, timeOut);
#if !NETSTANDARD
new FileIOPermission(FileIOPermissionAccess.Read, fullPath).Demand();
#endif
((IPersistFile)link).Load(fullPath, (int)STGM.STGM_DIRECT);
link.Resolve(hWin, resolveFlags);

View File

@ -3,24 +3,23 @@
<PropertyGroup>
<Description>Common classes for Windows Shell items derived from the Vanara PInvoke libraries. Includes shell items, files, icons, links, and taskbar lists.</Description>
<AssemblyTitle>$(AssemblyName)</AssemblyTitle>
<TargetFrameworks>net20;net35;net40;net45;net5.0;net6.0;netcoreapp3.0;netcoreapp3.1</TargetFrameworks>
<AssemblyName>Vanara.Windows.Shell</AssemblyName>
<TargetFrameworks>net20;net35;net40;net45;net5.0;net6.0;netstandard2.0;netcoreapp2.0;netcoreapp2.1;netcoreapp3.0;netcoreapp3.1</TargetFrameworks>
<AssemblyName>Vanara.Windows.Shell.Common</AssemblyName>
<PackageId>$(AssemblyName)</PackageId>
<PackageTags>pinvoke;vanara;net-extensions;interop</PackageTags>
<RootNamespace>Vanara.Windows.Shell</RootNamespace>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" Condition=" $(TargetFramework.StartsWith('netcore')) Or $(TargetFramework.StartsWith('netcore')) " />
<PackageReference Include="Theraot.Core" Version="3.2.9" Condition=" $(TargetFramework.StartsWith('net2')) Or $(TargetFramework.StartsWith('net3')) Or $(TargetFramework.StartsWith('net4')) " />
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" Condition=" $(TargetFramework.StartsWith('netcore')) Or $(TargetFramework.StartsWith('netstandard')) Or $(TargetFramework.StartsWith('net5')) Or $(TargetFramework.StartsWith('net6')) " />
<PackageReference Include="System.Security.Permissions" Version="4.4.0" Condition=" $(TargetFramework.StartsWith('netcore')) Or $(TargetFramework.StartsWith('net5')) Or $(TargetFramework.StartsWith('net6')) " />
<PackageReference Include="Theraot.Core" Version="3.2.11" Condition=" $(TargetFramework.StartsWith('net2')) Or $(TargetFramework.StartsWith('net3')) Or $(TargetFramework.StartsWith('net4')) " />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Vanara.Core.csproj" />
<ProjectReference Include="..\PInvoke\Shared\Vanara.PInvoke.Shared.csproj" />
<ProjectReference Include="..\PInvoke\ComCtl32\Vanara.PInvoke.ComCtl32.csproj" />
<ProjectReference Include="..\PInvoke\Ole\Vanara.PInvoke.Ole.csproj" />
<ProjectReference Include="..\PInvoke\Shell32\Vanara.PInvoke.Shell32.csproj" />
<ProjectReference Include="..\PInvoke\User32\Vanara.PInvoke.User32.csproj" />
<ProjectReference Include="..\PInvoke\SearchApi\Vanara.PInvoke.SearchApi.csproj" />
</ItemGroup>
</Project>