【问题标题】:How to close the form of 1 application in other application?如何在其他应用程序中关闭 1 个应用程序的表单?
【发布时间】:2017-04-19 18:04:00
【问题描述】:

应用程序A(例如:AutoCAD、Word、...)在运行时会在运行时调用DLL X来执行任务→打开表单登录

应用程序B(例如:Photoshop、Excel...)在运行时会调用一个DLL Y在运行时执行任务→我想关闭上面的登录表单可以这样做吗?

这可能吗?

Image description

【问题讨论】:

  • cad、excel等都是很糟糕的例子。
  • @LeiYang 他们和任何人一样好,这只是一个开始的好主意,因为你将不得不与 ROT 搞混。 Oosutsuke 你应该用谷歌 ROT 并在计时器中实现一些东西
  • @LeiYang:我只想示例(cad、word、...)易于想象。因为我不知道怎么做,我希望有人提出想法。 EpicKip:谢谢。我会用谷歌搜索 ROT!
  • 在打开 word、excel、cad 等之前我从来没有看到任何登录表单,所以根本不知道你在问什么。
  • @LeiYang 他想在某个应用打开时自己打开一个登录表单

标签: c# winforms


【解决方案1】:

这里有一个小示例,展示了如何在另一个进程中关闭窗口。创建一个新的 Windows 窗体项目,在其中添加两个窗体并在第一个窗体上添加两个按钮并将该代码放在 Form1 中:

public partial class Form1 : Form
{
    delegate bool EnumWindowsProc(IntPtr Hwnd, IntPtr lParam);

    [DllImport("user32", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private extern static bool EnumThreadWindows(int threadId, EnumWindowsProc callback, IntPtr lParam);

    [DllImport("user32", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);

    [DllImport("user32", SetLastError = true, CharSet = CharSet.Auto)]
    private extern static int GetWindowText(IntPtr hWnd, StringBuilder text, int maxCount);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

    private const int WM_CLOSE = 0x10;

    public Form1()
    {
        InitializeComponent();
        button1.Click += button1_Click;
        button2.Click += button2_Click;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var frm = new Form2();
        frm.Show(this);
    }

    private void button2_Click(object sender, EventArgs e)
    {
        Process[] processes = Process.GetProcessesByName("FindWindowSample");

        foreach (Process p in processes)
        {
            var handle = FindWindowInProcess(p, x => x == "Form2");

            if (handle != IntPtr.Zero)
            {
                SendMessage(handle, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
            }
        }
    }

    public static IntPtr FindWindowInProcess(Process process, Func<string, bool> compareTitle)
    {
        IntPtr windowHandle = IntPtr.Zero;

        foreach (ProcessThread t in process.Threads)
        {
            windowHandle = FindWindowInThread(t.Id, compareTitle);
            if (windowHandle != IntPtr.Zero)
            {
                break;
            }
        }

        return windowHandle;
    }

    private static IntPtr FindWindowInThread(int threadId, Func<string, bool> compareTitle)
    {
        IntPtr windowHandle = IntPtr.Zero;
        EnumThreadWindows(threadId, (hWnd, lParam) =>
        {
            StringBuilder text = new StringBuilder(200);
            GetWindowText(hWnd, text, 200);
            if (compareTitle(text.ToString()))
            {
                windowHandle = hWnd;
                return false;
            }
            return true;
        }, IntPtr.Zero);

        return windowHandle;
    }
}

编译然后运行应用程序的两个实例。在第一个上单击 Button1 以显示 Form2。在第二个实例上单击 Button2 以从第一个实例关闭 Form2。

【讨论】:

  • 那么代码在哪里:打开 word/cad... 并显示登录表单?
  • @LeiYang:我只是想举个例子让读者轻松想象我的想法。
  • 所以他们真的是非常糟糕的例子。
【解决方案2】:

好吧,如果它只是关闭正在运行的进程,最简单的方法可能类似于

using System.Diagnostics;
var autocad = Process.GetProcesses().FirstOrDefault(w=>w.ProcessName == "AutoCAD");
autocad.Close();

对于打开,您可能会循环使用所需名称的过程。它不是很有效,但如果它只是打开和关闭你就可以完成工作

【讨论】:

  • 我想关闭在 AutoCAD 中打开的表单,但是 AutoCAD 没有关闭(没有退出)
  • 所以您正试图关闭一个由 AutoCAD 打开但只有对话框而不是整个 AutoCAD 进程的表单?
  • 是的,这是真的。
  • 如果窗口被正确识别,你可以尝试使用win api,例如 [DllImport("user32.dll")] public static extern int FindWindow(string lpClassName,string lpWindowName);然后发送关闭消息(我认为是 F060)-但这并不能保证成功,这在很大程度上取决于您要修改的应用程序,总体而言,这不是一项简单的任务
  • 谢谢。我能够根据 bN_ 的回答做到这一点。
【解决方案3】:

您可以使用(只是一个示例)无窗口控制台应用程序继续检查新进程。我有一个例子:

  1. 使用控制台应用程序和登录表单创建一个新项目

  2. 使控制台应用程序成为这样的 Windows 应用程序:


新闻属性 选择 Windows 应用程序

在控制台应用程序中创建一个计时器来检查进程:

public static bool IsOpen = false;

static void Main(string[] args)
{
    Timer t = new Timer(TimerCallback, null, 0, 1200);
    while ( true )
    {
        //keep it running
    }
}

private static void TimerCallback(Object o)
{
    if ( !IsOpen && Process.GetProcesses().Select( r => r.ProcessName ).Contains( "EXCEL" ) )
    {
        //Excel is running, show the form
        Form1 loginForm = new Form1();
        loginForm.ShowDialog();
        //Stop it from spawning a million forms by setting bool
        IsOpen = true;
    }
}

应用关闭时关闭Form的逻辑也可以放在这个定时器里面,使用方法相同。

要从控制台窗口打开表单,但您需要添加对控制台应用程序的引用:

如果您想要进程名称:
只需使用表单并添加以下内容:

foreach ( var proc in Process.GetProcesses() )
{
    MessageBox.Show( proc.ProcessName );
}

这将显示当前正在运行的所有进程的名称。

【讨论】:

  • 所以登录表单是在一个单独的进程中?
  • @LeiYang 它不是来自excel本身所以是的。
  • @LeiYang 不知道你为什么要这样做,但是......像这样你可以
  • 我不是 OP。
  • @LeiYang 我知道我说的是一般意义上的为什么有人会在其他应用程序打开时生成登录表单
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-22
  • 2011-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多