如何调试由sendto菜单运行的应用程序启动

发布于 2025-02-01 19:49:49 字数 3141 浏览 3 评论 0原文

在Windows上,我想让我的FMX应用程序从sendto上下文菜单中运行。如果应用程序已经运行,我希望第二个实例将其命令行传递到第一个,然后退出。代码在下面。问题在于,如果我在调试器中运行了一项实例,然后双击适当的文件,我没有看到第一个实例从新启动的实例接收消息。如果该应用程序尚未运行,则双击启动了预期的新实例。

有没有办法调试SendTo菜单启动的实例的启动?

此代码将应用程序添加到sendto菜单:

class procedure TInstallationController.CreateSendTo;
var
    lExePath: string;
    lObject: IUnknown;
    lSLink: IShellLink;
    lPFile: IPersistFile;
    lFolderPath: array[0..MAX_PATH] of char;
    lLinkName: WideString;
begin
    SHGetFolderPath(0, CSIDL_SENDTO, 0, 0, lFolderPath);
    lLinkName := Format('%s\%s.lnk', [lFolderPath, 'AppName']);

{$IFNDEF DEBUG}
    if String(lLinkName).Contains('debug') then
        Tfile.Delete(lLinkName);
{$ENDIF DEBUG}
    if not TFile.Exists(lLinkName) then
        if CoInitializeEx(nil, COINIT_MULTITHREADED) = S_OK then
        begin
            lExePath := ParamStr(0);
            lObject := CreateComObject(CLSID_ShellLink);
            lSLink := lObject as IShellLink;
            lPFile := lObject as IPersistFile;

            with lSlink do
            begin
                SetPath(pChar(lExePath));
                SetWorkingDirectory(PChar(TPath.GetDirectoryName(lExePath)));
            end;

            lPFile.Save(PWChar(WideString(lLinkName)), false);
        end;
end;

代码是在应用程序之前放置

var
    lWindow: HWND;
    lMutex: THandle;
    lCopyDataStruct: TCopyDataStruct;
    i: integer;
    lArg: string;
    lResult: DWORD;
begin
    lMutex := CreateMutex(nil, False, PChar('43671EDF1E5A4B419F213336F2387B0D'));
    if lMutex = 0 then
        RaiseLastOSError;
    if GetLastError = Error_Already_Exists then
    begin
        FillChar(lCopyDataStruct, Sizeof(lCopyDataStruct), 0);
        for I := 1 to ParamCount do
        begin
            lArg := ParamStr(i);
            lCopyDataStruct.cbData := (Length(lArg) + 1)*SizeOf(Char);
            lCopyDataStruct.lpData := PChar(lArg);
            lWindow := FindWindow('FMT' + STRMainWindowClassName, nil);
            SendMessageTimeout(lWindow, WM_COPYDATA, 0, NativeInt(@lCopyDataStruct),
                SMTO_BLOCK, 3000, @lResult);
        end;

        exit;
    end;
...
end.

...
FHwnd := FmxHandleToHwnd(Handle);
FOldWndProc := GetWindowLongPtr(FHwnd, GWL_WNDPROC);
SetWindowLongPtr(FHwnd, GWL_WNDPROC, NativeInt(@WindowProc));
...

function WindowProc (HWND: HWND; Msg: UINT; wParam: wParam; lParam: lParam): LRESULT; stdcall;
begin
  Result := MasterDetailView.WndProc (HWND, Msg, wParam, lParam);
end;

此 方法接收到转发的消息:

function TViewMasterDetail.WndProc(aHwnd: HWND; aMsg: UINT; aWParam: WPARAM;
    aLParam: LPARAM): LResult;
begin
    Result := 0;

    if aMsg = WM_COPYDATA then
    begin
TUtils.Log('External file: ' + PChar(PCopyDataStruct(aLParam)^.lpData));
        Viewmodel.HandleExternalFile(PChar(PCopyDataStruct(aLParam)^.lpData));
        Exit;
    end;

    result := CallWindowProc(Ptr(fOldWndProc), aHwnd, aMsg, aWParam, aLParam);
end;

TViewMasterDetail.wndproc被称为多次,但据我所知,AMSG从来都不是WM_COPYDATA。 “外部文件:”消息在日志中从未出现。谢谢

On Windows, I'd like for my FMX app to run from the SendTo context menu. If the app is already running I'd like for the second instance to pass its command line to the first and then exit. Code is below. The problem is that if I have first instance running in the debugger, and then double-click an appropriate file, I see no evidence that the first instance receives a message from a newly started instance. If the app is not already running then the double click starts a new instance as expected.

Is there a way to debug the startup of the instance launched by the SendTo menu?

This code adds the app to the SendTo menu:

class procedure TInstallationController.CreateSendTo;
var
    lExePath: string;
    lObject: IUnknown;
    lSLink: IShellLink;
    lPFile: IPersistFile;
    lFolderPath: array[0..MAX_PATH] of char;
    lLinkName: WideString;
begin
    SHGetFolderPath(0, CSIDL_SENDTO, 0, 0, lFolderPath);
    lLinkName := Format('%s\%s.lnk', [lFolderPath, 'AppName']);

{$IFNDEF DEBUG}
    if String(lLinkName).Contains('debug') then
        Tfile.Delete(lLinkName);
{$ENDIF DEBUG}
    if not TFile.Exists(lLinkName) then
        if CoInitializeEx(nil, COINIT_MULTITHREADED) = S_OK then
        begin
            lExePath := ParamStr(0);
            lObject := CreateComObject(CLSID_ShellLink);
            lSLink := lObject as IShellLink;
            lPFile := lObject as IPersistFile;

            with lSlink do
            begin
                SetPath(pChar(lExePath));
                SetWorkingDirectory(PChar(TPath.GetDirectoryName(lExePath)));
            end;

            lPFile.Save(PWChar(WideString(lLinkName)), false);
        end;
end;

This code is placed before Application.Initialize in the .dpr file:

var
    lWindow: HWND;
    lMutex: THandle;
    lCopyDataStruct: TCopyDataStruct;
    i: integer;
    lArg: string;
    lResult: DWORD;
begin
    lMutex := CreateMutex(nil, False, PChar('43671EDF1E5A4B419F213336F2387B0D'));
    if lMutex = 0 then
        RaiseLastOSError;
    if GetLastError = Error_Already_Exists then
    begin
        FillChar(lCopyDataStruct, Sizeof(lCopyDataStruct), 0);
        for I := 1 to ParamCount do
        begin
            lArg := ParamStr(i);
            lCopyDataStruct.cbData := (Length(lArg) + 1)*SizeOf(Char);
            lCopyDataStruct.lpData := PChar(lArg);
            lWindow := FindWindow('FMT' + STRMainWindowClassName, nil);
            SendMessageTimeout(lWindow, WM_COPYDATA, 0, NativeInt(@lCopyDataStruct),
                SMTO_BLOCK, 3000, @lResult);
        end;

        exit;
    end;
...
end.

Assignments in FormCreate of the main form to support Windows message forwarding:

...
FHwnd := FmxHandleToHwnd(Handle);
FOldWndProc := GetWindowLongPtr(FHwnd, GWL_WNDPROC);
SetWindowLongPtr(FHwnd, GWL_WNDPROC, NativeInt(@WindowProc));
...

This forwards Windows messages to my main FMX form:

function WindowProc (HWND: HWND; Msg: UINT; wParam: wParam; lParam: lParam): LRESULT; stdcall;
begin
  Result := MasterDetailView.WndProc (HWND, Msg, wParam, lParam);
end;

This main form method receives forwarded messages:

function TViewMasterDetail.WndProc(aHwnd: HWND; aMsg: UINT; aWParam: WPARAM;
    aLParam: LPARAM): LResult;
begin
    Result := 0;

    if aMsg = WM_COPYDATA then
    begin
TUtils.Log('External file: ' + PChar(PCopyDataStruct(aLParam)^.lpData));
        Viewmodel.HandleExternalFile(PChar(PCopyDataStruct(aLParam)^.lpData));
        Exit;
    end;

    result := CallWindowProc(Ptr(fOldWndProc), aHwnd, aMsg, aWParam, aLParam);
end;

TViewMasterDetail.WndProc is called many time, but as far as I can tell aMsg is never WM_COPYDATA. The 'External file:' message never appears in the log. Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

虚拟世界 2025-02-08 19:49:49

程序员错误。为了近似调试启动代码,我在调试器之外运行了该应用程序的副本,然后在调试器中启动了该应用程序的第二份副本,并在命令行上传递了通往目标文件的路径。这告诉我Findwindow失败了。我很久以前就写了此启动代码,从那时起,就更改了应用程序中的UI类的名称,包括主窗口。但是我忽略了我用于主窗口的类名称的常数,然后传递到Findwindow。固定常数清除了错误。只是使用文字的弊端的另一个胜利!

Programmer error. To approximate debugging the startup code I ran a copy of the app outside the debugger and then launched a second copy of the app in the debugger, passing the path to the target file on the command line. This told me FindWindow was failing. I wrote this startup code a long time ago and since then have changed the names of UI classes in the app, including the main window. But I neglected to change the constant I used for the class name of the main window and pass to FindWindow. Fixing the constant cleared the error. Just another win for the evils of using text!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文