用SetForegroundWindow将窗体提到最前的实现

前段时间写的C#程序启动欢迎窗体实现,在运行时虽然能显示欢迎窗体,但运行后不能把主窗体提到最前。结合网上搜罗了好多资料,自己做了好几个方案来实现,最终可以解决了。

起因:
2000/XP 改变了SetForegroundWindow的执行方式,不允许随便把窗口提前,打扰用户的工作。

思路:
可以用附加本线程到最前面窗口的线程,从而欺骗windows。

实现:
原理:将显示欢迎窗体的线程作为Foreground的线程。
1. API函数的导入
public const int SW_HIDE = 0;
public const int SW_NORMAL = 1;
public const int SW_SHOWMINIMIZED = 2;
public const int SW_SHOWMAXIMIZED = 3;

[DllImport("User32.dll")]
public static extern void SetForegroundWindow(IntPtr hwnd);

[DllImport("User32.dll")]
public static extern IntPtr GetForegroundWindow();

[DllImport("User32.dll")]
public static extern int GetWindowThreadProcessId(IntPtr hwnd, IntPtr pProcId);

[DllImport("User32.dll")]
public static extern bool AttachThreadInput(int idAttach, int idAttachTo, int fAttach);

[DllImport("User32.dll")]
public static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);

2. 使用API函数实现
private static void ActivateMainInstance(IntPtr mainWindowHandle, int foreId)
{
int curId = GetWindowThreadProcessId(mainWindowHandle, IntPtr.Zero);

if (curId != foreId)
{
AttachThreadInput(foreId, curId, 1);
SetForegroundWindow(mainWindowHandle);
AttachThreadInput(foreId, curId, 0);
}
else
{
SetForegroundWindow(mainWindowHandle);
}
ShowWindowAsync(mainWindowHandle, SW_SHOWMAXIMIZED);
}

3. 调用:
private static ApplicationContext _context = null;
private static SplashScreen _splash = null;
private static MainFrame _mForm = null;

private static void OnAppIdle(object sender, EventArgs e)
{
if (_context.MainForm == null)
{
Application.Idle -= new EventHandler(OnAppIdle);

// Initialize main frame and show
_mForm.InitializeFrame(); // 花时间点
_context.MainForm = _mForm;
_context.MainForm.Show();

// Active main form
ActivateMainInstance(_mForm.Handle, _splash.ThreadId);

// Close splash screen
_splash.Close();
_splash = null;
}
}

4. Main函数中:
// Show splash screen
_splash = new SplashScreen();
_splash.Run();

// Application running
_mForm = new MainFrame();
_context = new ApplicationContext();
Application.Idle += new EventHandler(OnAppIdle);
Application.Run(_context);

5. SplashScreen的实现:
public class SplashScreen
{
private SplashForm _sForm = null;
private Thread _workThread = null;

///

/// Thread function: shows this splash form.
///

private void Show()
{
_sForm = new SplashForm();
_sForm.ShowDialog();
}

///

/// Thread id property
///

public int ThreadId
{
get
{
return _workThread.ManagedThreadId;
}
}

///

/// Runs this instance.
///

public void Run()
{
_workThread = new Thread(new ThreadStart(Show));
_workThread.Start();
}

///

/// Closes this instance.
///

public void Close()
{
if (_sForm != null)
{
_sForm.HideSplash = true;
_workThread.Join();
_workThread = null;
}
}
}

2 Responses to “用SetForegroundWindow将窗体提到最前的实现”

  1. 王伟晔说道:

    🙂

    C#.net设置窗体属性
    theForm.TopMost=true;

    可以达到相同的效果。

  2. lordong说道:

    回复王伟晔:
    你说的这个我知道,但结果是这个窗体总在之前已打开程序的窗体前面,这是不合理的。合理的应该是把刚刚运行的窗体提到前面,但不影响切换到别的程序时显示其窗体。

Leave a Reply


提醒: 评论者允许使用'@user空格'的方式将自己的评论通知另外评论者。例如, ABC是本文的评论者之一,则使用'@ABC '(不包括单引号)将会自动将您的评论发送给ABC。请务必注意user必须和评论者名相匹配(大小写一致)。