Windows 11 在 Delphi 应用程序中共享

问题描述 投票:0回答:1

Windows 在资源管理器中有一个共享文件命令:

image

image

此命令会弹出一个对话框,让您可以将文件共享给附近的人或联系人:

image

如何添加 Share 命令支持,从我的应用程序调用相同的 Windows 操作系统功能?用于调用

Share
对话框的 API 或 rundll 命令是什么?

我尝试查看生成的进程 Explorer,我可以使用 Process ExplorerProcess Monitor 来监视命令行,但该对话框不是进程外的。

注意:显然,我的应用程序不是 Shell 浏览器。我也不是问如何重新创建已经存在的功能。我也不是问如何创建 shell

IContextMenu

我问如何在我自己的应用程序中使用 Windows 10 和 11 的此功能,以便我可以使用 Windows 的

Share
功能共享
.png
.jpg
.pdf 或任何其他类型的文件。

到目前为止我已经尝试过的事情

我尝试了通常的过程来调用 shell 命令:

  • 将文件 displayName 解析为
    IShellItem
  • 向 shell 项询问其上下文菜单处理程序
    IContextMenu
  • 创建弹出菜单
    CreatePopupMenu
  • 要求
    IContextMenu
    填写我们的
    hMenu
  • 找到名称为“共享”的菜单项
  • 调用 IContextMenu.Invoke 来调用选项

这是最后一个操作,对 Invoke 的调用失败并出现错误

0x80070057
- Win32 错误代码 87 - 无效参数

procedure InvokeShare(hwndOwner: HWND; const Filename: string);
var
    si: IShellItem;
    cm: IContextMenu;
    menu: HMENU;
    command: Cardinal;
    info: TCMInvokeCommandInfo;
    ICIEx: TCMInvokeCommandInfoEx;
    hr: HRESULT;

    function GetMenuCommand(const CommandName: string): Integer;
    var
        i: Integer;
        info: TMenuItemInfo;
        buffer: array[0..255] of Char;
    begin
        Result := -1; //default "not found"

        FillChar(info, sizeof(info), 0);
        info.cbSize := sizeof(info);
        info.fMask := MIIM_STRING;
        info.dwTypeData := buffer;
        info.cch := sizeof(buffer);

        for i := 0 to GetMenuItemCount(Menu)-1 do
        begin
            info.cch := sizeof(Buffer);
            if GetMenuItemInfo(Menu, i, True, {var}info) then
            begin
                if SameText(info.dwTypeData, CommandName) then
                begin
                    Result := i;
                    Exit;
                end;
            end;
        end;
    end;
begin
    // Get the IShellItem for the specified file
    if not Succeeded(SHCreateItemFromParsingName(PWideChar(Filename), nil, IShellItem, {out}si)) then
        RaiseLastOSError;

    // Get the IContextMenu for the IShellItem
    if not Succeeded(si.BindToHandler(nil, BHID_SFUIObject, IContextMenu, {out}cm)) then
        RaiseLastOSError;

    // Create a popup menu that the IContextMenu will add its options to
    menu := CreatePopupMenu;
    try
        // Ask the IContextMenu to populate our popup menu
        if not Succeeded(cm.QueryContextMenu(menu, 0, 1, $7FFF, CMF_NORMAL)) then
            RaiseLastOSError;

        // Find the position of the Share command
        command := GetMenuCommand('Share');  // Helper function to loop through and find the command
        if Command < 0 then
            raise Exception.Create('Share command not found.');

        // Fill the CMINVOKECOMMANDINFOEX structure
        info := Default(CMINVOKECOMMANDINFO);
        info.cbSize := sizeof(info);
        info.hwnd := hwndOwner;
        info.lpVerb := MAKEINTRESOURCEA(command);
        info.nShow := SW_SHOWNORMAL;

        // Invoke the command
        hr := cm.InvokeCommand(info);
        OleCheck(hr);
    finally
        DestroyMenu(menu);
    end;
end;

我还尝试了 ShellExecute“共享” 动词:

procedure ExecuteShare(hwndOwner: HWND; Filename: string);
var
    sei: TShellExecuteInfo;
begin
    ZeroMemory(@sei, SizeOf(sei));
    sei.cbSize := SizeOf(TShellExecuteInfo);
    sei.Wnd := hwndOwner;
    sei.lpVerb := PChar('Share');
    sei.lpFile := PChar(Filename); // PAnsiChar;
    sei.nShow := SW_SHOWNORMAL; //Integer;

    ShellExecuteEx(@sei);
end;

失败并出现错误对话框:

[内容]
此文件没有与之关联的用于执行此操作的应用程序。请安装一个应用程序,或者如果已经安装了一个应用程序,请在“默认应用程序设置”页面中创建关联。

[确定]

这让我们回到我们的问题

在 Delphi 应用程序中分享

windows delphi
1个回答
0
投票

您必须使用其相应的 Windows API,特别是 Windows.ApplicationModel.DataTransferDataTransferManager 以及 DataPackage 类来设置要共享的数据格式,如文本、Web 链接、图像、文件等。

例如,这里是一个仅调用共享对话框的示例,没有DataTransferManager对象,应该实现它,特别是它的事件

DataRequested
来设置标题和DataPackage以将对话框传递到。

unit main;

interface

uses
  Winapi.Windows, Vcl.Graphics, System.Win.ComObj, Winapi.Winrt, Winapi.Foundation,
  Vcl.Controls, Vcl.StdCtrls, Vcl.Forms, System.Classes;

const
  IDataTransferManagerInteropCLSID = 'Windows.ApplicationModel.DataTransfer.DataTransferManager';
  IDataTransferManagerInteropGUID: TGUID = '{3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8}';
  IDataTransferManagerGUID: TGUID = '{A5CAEE9B-8708-49D1-8D36-67D25A8DA00C}';

type
  IDataTransferManagerInterop = interface(IUnknown)
    ['{3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8}']
    function GetForWindow(appWindow: HWND; const riid: TGUID; out dataTransferManager: Pointer): HRESULT; stdcall;
    function ShowShareUIForWindow(appWindow: HWND): HRESULT; stdcall;
  end;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function StringToHString(Src: String; var Dest: HSTRING): Boolean;
begin
  Result := Succeeded(WindowsCreateString(PWideChar(Src), Length(Src), Dest));
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  hs: HSTRING;
  insp: IInspectable;
  factory: IActivationFactory;
  share: IDataTransferManagerInterop;
  dtm: Pointer;
begin
  if StringToHString(IDataTransferManagerInteropCLSID, hs) then
  if Succeeded(RoGetActivationFactory(hs, IDataTransferManagerInteropGUID, insp)) then
  begin
    share := IDataTransferManagerInterop(insp);

    (*TODO: implement DataTransferManager events handler*)
    share.GetForWindow(Handle, IDataTransferManagerInteropGUID, dtm);

    share.ShowShareUIForWindow(Handle);

  end;
end;

end.

share

GitHub 上有很多关于此的实现,例如 Golang 中的实现 (datatransfer_idl_windows.go),但官方 IDL 可能值得一看 ShObjIdl_core.idl 以及有关在 Delphi 中实现它的更多指导,本单元 可能有用,因为这适用于 WinAPI.UI.Notifications,它具有类似的实现。

© www.soinside.com 2019 - 2024. All rights reserved.