C# - 如何重命名我开始的进程窗口?

时间:2009-06-19 07:57:16

标签: c# process window titlebar

有没有办法可以重命名我启动的应用程序的窗口标题栏?即如果我启动了Notepad.exe,我可以将其标题栏从“Untitled - Notepad”重命名为“New Notepad Name”。

7 个答案:

答案 0 :(得分:14)

您可以使用P / Invoke执行此操作:

[DllImport("user32.dll")]
static extern int SetWindowText(IntPtr hWnd, string text);



private void StartMyNotepad()
{
    Process p = Process.Start("notepad.exe");
    Thread.Sleep(100);  // <-- ugly hack
    SetWindowText(p.MainWindowHandle, "My Notepad");
}

代码示例中丑陋黑客的背景是,似乎在启动进程后立即调用SetWindowText,标题不会更改。也许消息在记事本的消息队列中过早结束,因此记事本将在之后再次设置标题。

另请注意,这是一个非常短暂的变化;如果用户选择文件 - &gt;新的(或其他任何会导致记事本更新窗口标题的东西),原始标题将返回...

答案 1 :(得分:4)

实际上,我自己整理了它并且它完美无缺。不管怎样,谢谢。

[DllImport("user32.dll")]
static extern SetWindowText(IntPtr hWnd, string windowName);

IntPtr handle = p.MainWindowHandle;
SetWindowText(handle, "This is my new title");

答案 2 :(得分:4)

您无法在C#中执行此操作,但您可以使用低级API执行此操作。 在进程中注入一个线程,从中调用SetWindowText()

答案 3 :(得分:3)

正如@FredrikMörk所认为的,问题是需要等待窗口可以接收消息来设置其标题。等待100毫秒可以破坏程序的内部消息循环,这只是一个解决方法。 要接收消息,窗口必须有一个用于引用该窗口的句柄,因此您只需等待窗口的句柄,在启动时应该是IntPtr.Zero(空句柄)。

以下是此方法的一个示例:

Process p = Process.Start("notepad.exe");
while (p.MainWindowHandle == IntPtr.Zero)
    Application.DoEvents();
SetWindowText(p.MainWindowHandle, "My Notepad");

使用Application.DoEvents()程序将继续接收和处理系统消息,因此它不会阻塞(也不会崩溃),尽管它不是异步的。

您还可以考虑通过while调用替换SpinWait.SpinUntil语句来避免CPU开销(因此,您需要导入System.Threading):

Process p = Process.Start("notepad.exe");
SpinWait.SpinUntil(delegate
{
    return p.MainWindowHandle != IntPtr.Zero;
});
SetWindowText(p.MainWindowHandle, "My Notepad");

答案 4 :(得分:0)

我实现了@Davide Cannizo解决方案,发现了一个可能与其他循环解决方案相同的问题-该过程可能在意识到MainWindowHandle不为零之前就已经完成了。当我并行运行许多进程并且一两个很短的生命时,这发生在我身上。特定的问题是SystemAggregrationError,因为支票p.MainWindowHandle != IntPtr.Zero导致了多个InvalidOperationError

解决方案是添加一个检查程序是否退出。在下面寻找p.HasExited

using System;
using System.Runtime.InteropServices;

class Program
{
  [DllImport("user32.dll")]
  static extern int SetWindowText(IntPtr hWnd, string text);

  static void Main(string[] args)
  {
    using (System.Diagnostics.Process p = new System.Diagnostics.Process())
    {
      p.StartInfo = new System.Diagnostics.ProcessStartInfo()
      {
        FileName = @"notepad.exe",
        Arguments = @""
      };

      string window_title = "notepad demo";
      p.Start();
      System.Threading.SpinWait.SpinUntil(() => p.HasExited || p.MainWindowHandle != IntPtr.Zero);
      if (!p.HasExited)
        SetWindowText(p.MainWindowHandle, window_title);
      p.WaitForExit();
    }
  }
}

另外两件事要注意:

  • 在开发此程序时,我首先尝试使用.NET Core。失败-p.MainWindowHandler始终为0。我不得不切换到.NET Framework。
  • 我不认为SetWindowText(p.MainWindowHandle, window_title)会在进程退出时导致异常。根据文档here,它可能会失败,但只会返回0,而不是非零。这意味着如果我们完成SpinUntil是因为MainWindowHandle不为零,但是在该点和SetWindowText之间,过程退出了,它不会导致异常。但是,在测试中,我确实必须添加if (!p.HasExited)行。

答案 5 :(得分:0)

我必须使用以下代码来使其正常工作:

while (p.MainWindowHandle == IntPtr.Zero)
      Thread.Sleep(100);

答案 6 :(得分:-2)

没有

这将要求目标应用程序允许完全修改窗口标题。许多程序使用其标题来显示有用的信息(例如,在记事本中打开以进行编辑的文件的名称,或在Firefox中打开的HTML文档的<TITLE>)。

我知道的一个案例是让用户设置标题文本的限制很少,CMD.EXE在控制台窗口中运行。 CMD支持TITLE内置命令,该命令根据参数设置窗口标题。但是,如果不将关键笔划注入特定的控制台窗口,则无法通过第二个窗口完成此操作,这通常不建议使用。

修改

由于这个想法是浮动的,SetWindowText()会为你做这件事,让我澄清一下。

该API函数确实改变了顶级窗口的标题栏。事实上,这是一个调用,像记事本这样的应用程序可能会在它认为标题发生变化时设置自己的标题。

我声称这是解决方案的原因是记事本确实在需要时更改了标题。支持对其标题进行任意更改的应用程序将具有某种机制来记住标题已更改,而不是任意恢复其首选标题字符串。