当 DM 应用程序 window 最小化时,如何处理延迟任务发布的模态对话框?

How to handle modal dialog posted by deferred task when DM app window is minimized?

诚然,这是一个相当神秘的问题,但它确实影响了我正在使用的 DM 脚本模块。我正在尝试使用自定义模式对话框来提醒用户延迟主线程任务检测到的错误情况。在大多数情况下,这工作得很好,但如果在发布错误消息时 DM 应用程序 window 碰巧被最小化,那么当 DM 恢复为前台应用程序时,它最终会处于一种奇怪的状态。模态对话框是不可见的,但它仍然会禁止用户在 DM 中进行操作,直到通过 'enter' 或 'esc' 击键将其关闭。

下面的示例代码演示了问题并提到了在 GMS 1 中有效的解决方案。

是否有适用于 GMS 2 及更高版本的类似或更好的解决方法?

class DeferredAlertTask
{
    Number deferredTaskID;

    DeferredAlertTask(Object self)
    {
        Number taskDelay_sec = 5;
        String message = "Click OK and then minimize the DM app window.\n";
        message += "After 5 seconds, select DM on the task bar to restore it.\n";
        message += "Dialog will be invisible, must hit 'enter' or 'esc' to go on.";
        OKDialog(message);

        deferredTaskID = AddMainThreadSingleTask(self, "Task", taskDelay_sec);
    }

    void Task(Object self)
    {
        String banner = "Error dialog";
        String message = "Error message details.";

        // Create the dialog box descriptor TagGroup
        TagGroup dialogItemsSpec;
        TagGroup dialogSpec = DLGCreateDialog(banner, dialogItemsSpec);

        // Create and add the content box and text field to the layout
        TagGroup contentBoxItemsSpec;
        TagGroup contentBoxSpec = DLGCreateBox(contentBoxItemsSpec);
        TagGroup contentLabelSpec = DLGCreateLabel(message);
        contentBoxItemsSpec.DLGAddElement(contentLabelSpec);
        dialogItemsSpec.DLGAddElement(contentBoxSpec);

        // If the DM app window has been minimized, 
        // this modal dialog will be invisible,
        // but it will still inhibit further user action
        // within DM as it awaits 'esc' or 'enter'.

        // The following is a remedy that works in GMS1, but not in GMS2
        // GetApplicationWindow().WindowSelect();

        Object dialog = Alloc(UIFrame).Init(dialogSpec);
        String result = (dialog.Pose()) ? "OK" : "Cancel";
        OKDialog(result);
    }
}

void main()
{
    Alloc(DeferredAlertTask);
}

main();

基于 LaunchExternalProcess() 函数和外部程序的解决方案的建议提供了一条通往答案的途径。通过使用名为 AutoHotKey 的免费开源 Windows 宏创建包,我已经能够创建一个名为 RestoreDM.exe 的非常紧凑的可执行文件。通过将此可执行文件放在可从 DM 脚本轻松访问的文件夹中,它可以通过 LaunchExternalProcessAsync() 启动,以确保在发布自定义对话框之前恢复 DM 应用 window。下面是原始测试脚本的修改版本,说明了此解决方案并提供了有关 AutoHotKey 脚本的详细信息:

class DeferredAlertTask
{
    Number deferredTaskID;

    DeferredAlertTask(Object self)
    {
        Number taskDelay_sec = 5;
        String message = "Click OK and then minimize the DM app window.\n";
        message += "After 5 seconds, select DM on the task bar to restore it.\n";
        message += "Dialog will be invisible, must hit 'enter' or 'esc' to go on.";
        OKDialog(message);

        deferredTaskID = AddMainThreadSingleTask(self, "Task", taskDelay_sec);
    }

    void Task(Object self)
    {
        String banner = "Error dialog";
        String message = "Error message details.";

        // Create the dialog box descriptor TagGroup
        TagGroup dialogItemsSpec;
        TagGroup dialogSpec = DLGCreateDialog(banner, dialogItemsSpec);

        // Create and add the content box and text field to the layout
        TagGroup contentBoxItemsSpec;
        TagGroup contentBoxSpec = DLGCreateBox(contentBoxItemsSpec);
        TagGroup contentLabelSpec = DLGCreateLabel(message);
        contentBoxItemsSpec.DLGAddElement(contentLabelSpec);
        dialogItemsSpec.DLGAddElement(contentBoxSpec);

        // If the DM app window has been minimized, 
        // this modal dialog will be invisible,
        // but it will still inhibit further user action
        // within DM as it awaits 'esc' or 'enter'.

        // The following is a remedy that works in GMS1, but not in GMS2
        // GetApplicationWindow().WindowSelect();

        // For GMS2, we can use an executable that restores the DM app window.
        // The lines below launch RestoreDM.exe, placed in C:\ProgramData\Gatan,
        // where RestoreDM is an executable of the following AutoHotKey script:
        // IfWinNotActive, Digital Micrograph
        //      WinRestore, Digital Micrograph
        String commandDir = GetApplicationDirectory(3, 0);
        String restoreCommand = commandDir.PathConcatenate("RestoreDM");
        LaunchExternalProcessAsync(restoreCommand);
        Sleep(0.1);

        Object dialog = Alloc(UIFrame).Init(dialogSpec);
        String result = (dialog.Pose()) ? "OK" : "Cancel";
        OKDialog(result);
    }
}

void main()
{
    Alloc(DeferredAlertTask);
}

main();

有必要使用异步变体 LaunchExternalProcessAsync(),因为在主线程上调用延迟警报任务,因此在 RestoreDM 程序提示时阻止 DM 恢复其 window(导致 DM挂)。另请注意,调用外部程序后需要短暂休眠,以确保 DM 应用程序 window 在自定义对话框出现之前恢复。