确实是因为自己对api不熟造成的。
之前 使用 process.MainWindowHandle 来打开之前已经运行的程序的主窗体,
但每当程序托盘化后 进程的主窗体句柄就变了 ( process.MainWindowHandle=0)。
所以我之前说 SendMessage没用。
我表达的不够清楚。谢谢各位的解答了。
今天特意试了试
FindWindow 获取句柄没问题,确实不需要进程间通讯。
但刚好这个程序不合适用 FindWindow 。
最终的解决方案是
使用了 EnumWindows、 GetWindowThreadProcessId 、GetWindowText、ShowWindow 这几个api。
winform 有这样的控件,你可以在工具箱里找找..
用NotifyIcon
public Form1()
{
InitializeComponent();
notifyIcon1.Icon = SystemIcons.Application;
notifyIcon1.BalloonTipTitle = "托盘";
notifyIcon1.BalloonTipText = "这是系统托盘.";
notifyIcon1.BalloonTipIcon = ToolTipIcon.Info;
notifyIcon1.ShowBalloonTip(30);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
this.Visible = false;
}
private void notifyIcon1_MouseClick(object sender, MouseEventArgs e)
{
if (this.Visible == false)
{
this.Visible = true;
this.WindowState = FormWindowState.Normal;
}
}
这就是个简单的托盘,还可以设置右键的功能,自己解决吧!
程序已打开,并且在托盘中,重新点击程序时(点击程序 .exe执行文件),
程序不会再次运行,而是最大化托盘中的程序。
程序单运行参考园子里的文章搞定了,而这个最大化托盘中的程序问题,好像是需要用到进程通讯问题,
只是询问下方向,有空了再研究。
@青色熊: 你的意思是说,VS中运行着程序,然后点击.exe不重新打开新的程序吗?
@yanzhe:
打开新的程序已经参考资料搞定了。
假若程序最小化到任务栏,点击 .exe 程序主窗体会正常化。
单程序最小化到托盘,则不行。目前没抽出空去研究……猜测需要进程通讯?
@青色熊: 点击.exe我的弹出的是个新的窗体,跟程序运行的窗体没什么联系……
@青色熊: 这个不用SendMessage
public Form1()
{
InitializeComponent();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (this.Visible==true)
{
e.Cancel = true;
this.Visible = false;
}
}
private void notifyIcon1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button==MouseButtons.Left)
{
if (this.Visible == false)
{
this.Visible = true;
this.WindowState = FormWindowState.Normal;
}
}
}
private void 退出ToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Visible = false;
Application.Exit();
}
这是前台的。图标什么的直接在界面设置了
然后修改Program.cs
static class Program
{
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll ", SetLastError = true)]
static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
[DllImport("user32.dll", EntryPoint = "ShowWindow", CharSet = CharSet.Auto)]
public static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
public const int SW_RESTORE=9;
public static IntPtr formhwnd;
static Form1 form = null;
///<summary>
/// 应用程序的主入口点。
///</summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
string proc = Process.GetCurrentProcess().ProcessName;
Process[] processes = Process.GetProcessesByName(proc);
if (processes.Length <= 1)
{
form = new Form1();
Application.Run(form);
}
else
{
for (int i = 0; i < processes.Length; i++)
{
if (processes[i].Id != Process.GetCurrentProcess().Id)
{
if (processes[i].MainWindowHandle.ToInt32() == 0)
{
formhwnd = FindWindow(null, "Form1");
ShowWindow(formhwnd,SW_RESTORE);
SwitchToThisWindow(formhwnd, true);
}
else
{
SwitchToThisWindow(processes[i].MainWindowHandle, true);
}
}
}
}
}
}
@yanzhe:
因为这个程序允许“多开”(复制几份后 ,所以之前一直没用FindWindow试试)。
而程序托盘化后:processes[i].MainWindowHandle.ToInt32() == 0
看了你的回复,特意使用 FindWindow 抓抓了句柄,发现托盘化后也能抓到句柄。之前想歪了~~~^-^
今天抽空用 EnumWindows、 GetWindowThreadProcessId 、GetWindowText 这几个api搞定了。
实在是对api不熟,又花了不少时间。
@青色熊: 哈哈,其实我之前就没用过api,正好借这个机会学习学习!收获不小啊!
单例可以使用Mutex来保证。使用 SendMessage 函数好像不行,因为窗体已经隐藏? 怎么会不行?
对,窗体隐藏后,主窗体句柄变了。
@青色熊: 您说的主窗体句柄是什么句柄呀,怎么可能改变,要使用SendMessage, 应该先用FindWindow找出当前已在运行的实例的那个窗口的窗口句柄,然后才SendMessage, 传递的消息应该用:
SendMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0)
请问楼主最后具体是怎么解决的
private static Process RunningInstance() { Process current = Process.GetCurrentProcess(); Process[] processes = Process.GetProcessesByName(current.ProcessName); //遍历正在有相同名字运行的例程 foreach (Process process in processes) { //忽略现有的例程 if (process.Id != current.Id) { //确保例程从EXE文件运行 //if (Assembly.GetExecutingAssembly().Location.Replace("/", "\\") == current.MainModule.FileName) if (process.MainModule.FileName == current.MainModule.FileName) { //返回另一个例程实例 return process; } } } //没有其它的例程,返回Null return null; } /// <summary> /// 窗体焦点 /// </summary> /// <param name="hWnd"></param> /// <param name="fAltTab"></param> [DllImport("user32.dll ", SetLastError = true)] private static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab); /// <summary> /// 显示窗体,同 ShowWindowAsync 差不多 /// </summary> /// <param name="hwnd"></param> /// <param name="nCmdShow"></param> /// <returns></returns> [DllImport("user32.dll", EntryPoint = "ShowWindow", CharSet = CharSet.Auto)] private static extern int ShowWindow(IntPtr hwnd, int nCmdShow); private const int SW_RESTORE = 9; /// <summary> /// 枚举窗体 /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> [DllImport("user32")] private static extern int EnumWindows(CallBack x, int y); private delegate bool CallBack(IntPtr hwnd, int lParam); /// <summary> /// 根据窗体句柄获得其进程ID /// </summary> /// <param name="hwnd"></param> /// <param name="ID"></param> /// <returns></returns> [DllImport("User32.dll", CharSet = CharSet.Auto)] private static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID); /// <summary> /// 根据窗体句柄获得窗体标题 /// </summary> /// <param name="hWnd"></param> /// <param name="lpText"></param> /// <param name="nCount"></param> /// <returns></returns> [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpText, int nCount); /// <summary> /// 根据进程,显示其主窗体 /// </summary> private static void HandleRunningInstance(Process instance) { ShowWindow(instance.MainWindowHandle, SW_RESTORE); SwitchToThisWindow(instance.MainWindowHandle, true); } private static bool Report(IntPtr hwnd, int lParam) { //获得窗体标题 StringBuilder sb = new StringBuilder(50); GetWindowText(hwnd, sb, sb.Capacity); int calcID; //获取进程ID GetWindowThreadProcessId(hwnd, out calcID); if ((sb.ToString() == formName) && (pro != null) && (calcID == pro.Id)) //标题栏、进程id符合 { ShowWindow(hwnd, SW_RESTORE); SwitchToThisWindow(hwnd, true); return true; } else return true; } private static string formName = string.Empty; private static Process pro = null; public static void Singling(string str) { formName = str; Process instance = RunningInstance(); if (instance != null) //首先确定有无进程 { pro = instance; if (pro.MainWindowHandle.ToInt32() != 0) //是否托盘化 { HandleRunningInstance(pro); } else { // System.Windows.Forms.MessageBox.Show("程序已运行!\n\r请查看托盘图标!"); CallBack myCallBack = new CallBack(Report); EnumWindows(myCallBack, 0); } System.Environment.Exit(System.Environment.ExitCode); } }
这是最后用的
@青色熊,可否把阁下的这个例子的Programes.cs文件的完整代码给看下,谢谢了
http://www.cnblogs.com/qingse/archive/2013/02/16/2913430.html
见我的博客吧,有源码。