首页 新闻 会员 周边

程序已打开,并且在托盘中,重新点击程序时,不用重新运行,仅最大化托盘中的程序

1
悬赏园豆:20 [已解决问题] 解决于 2011-12-30 23:35

单例程序。
需求见标题。
winform C#

发现使用 SendMessage  函数好像不行,因为窗体已经隐藏。
这样怎么做?
我看像 有道词典、金山词霸 都行。

或者说需要进程间通讯?


日暮青色的主页 日暮青色 | 菜鸟二级 | 园豆:475
提问于:2011-12-18 13:29
< >
分享
最佳答案
1

确实是因为自己对api不熟造成的。
之前 使用 process.MainWindowHandle 来打开之前已经运行的程序的主窗体,
但每当程序托盘化后 进程的主窗体句柄就变了 ( process.MainWindowHandle=0)。
所以我之前说 SendMessage没用。

我表达的不够清楚。谢谢各位的解答了。
今天特意试了试
FindWindow  获取句柄没问题,确实不需要进程间通讯。

但刚好这个程序不合适用 FindWindow  。

最终的解决方案是

使用了 EnumWindows、 GetWindowThreadProcessId 、GetWindowText、ShowWindow 这几个api。

日暮青色 | 菜鸟二级 |园豆:475 | 2011-12-30 23:26
其他回答(5)
0

winform 有这样的控件,你可以在工具箱里找找..

农民工老李 | 园豆:423 (菜鸟二级) | 2011-12-23 14:45
0

用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;
}
}

这就是个简单的托盘,还可以设置右键的功能,自己解决吧!

收获园豆:20
yanzhe | 园豆:213 (菜鸟二级) | 2011-12-23 15:08

程序已打开,并且在托盘中,重新点击程序时(点击程序 .exe执行文件),

程序不会再次运行,而是最大化托盘中的程序。

 

程序单运行参考园子里的文章搞定了,而这个最大化托盘中的程序问题,好像是需要用到进程通讯问题,

只是询问下方向,有空了再研究。

支持(0) 反对(0) 日暮青色 | 园豆:475 (菜鸟二级) | 2011-12-24 07:43

@青色熊: 你的意思是说,VS中运行着程序,然后点击.exe不重新打开新的程序吗?

支持(0) 反对(0) yanzhe | 园豆:213 (菜鸟二级) | 2011-12-26 09:54

@yanzhe: 

打开新的程序已经参考资料搞定了。
假若程序最小化到任务栏,点击 .exe  程序主窗体会正常化。
单程序最小化到托盘,则不行。目前没抽出空去研究……猜测需要进程通讯?

支持(0) 反对(0) 日暮青色 | 园豆:475 (菜鸟二级) | 2011-12-26 21:24

@青色熊: 点击.exe我的弹出的是个新的窗体,跟程序运行的窗体没什么联系……

支持(0) 反对(0) yanzhe | 园豆:213 (菜鸟二级) | 2011-12-27 08:45

@青色熊: 这个不用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);
}
}

}
}

}
}

 


 

支持(0) 反对(0) yanzhe | 园豆:213 (菜鸟二级) | 2011-12-28 10:27

@yanzhe:
因为这个程序允许“多开”(复制几份后 ,所以之前一直没用FindWindow试试)。

而程序托盘化后:processes[i].MainWindowHandle.ToInt32() == 0
看了你的回复,特意使用 FindWindow 抓抓了句柄,发现托盘化后也能抓到句柄。之前想歪了~~~^-^

今天抽空用 EnumWindows、 GetWindowThreadProcessId 、GetWindowText 这几个api搞定了。

实在是对api不熟,又花了不少时间。
支持(0) 反对(0) 日暮青色 | 园豆:475 (菜鸟二级) | 2011-12-30 23:34

@青色熊: 哈哈,其实我之前就没用过api,正好借这个机会学习学习!收获不小啊!

支持(0) 反对(0) yanzhe | 园豆:213 (菜鸟二级) | 2011-12-31 09:14
0

单例可以使用Mutex来保证。使用 SendMessage  函数好像不行,因为窗体已经隐藏? 怎么会不行?

ChatinCode | 园豆:2272 (老鸟四级) | 2011-12-27 10:57

对,窗体隐藏后,主窗体句柄变了。

支持(0) 反对(0) 日暮青色 | 园豆:475 (菜鸟二级) | 2011-12-27 18:27

@青色熊: 您说的主窗体句柄是什么句柄呀,怎么可能改变,要使用SendMessage, 应该先用FindWindow找出当前已在运行的实例的那个窗口的窗口句柄,然后才SendMessage, 传递的消息应该用:

SendMessage(handle, WM_SYSCOMMAND, SC_RESTORE, 0)

支持(0) 反对(0) ChatinCode | 园豆:2272 (老鸟四级) | 2011-12-28 09:57
0

请问楼主最后具体是怎么解决的

hujianwind | 园豆:202 (菜鸟二级) | 2012-05-03 18:44
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);

}
}

 

 

这是最后用的

支持(0) 反对(1) 日暮青色 | 园豆:475 (菜鸟二级) | 2012-05-04 08:11
0

@青色熊,可否把阁下的这个例子的Programes.cs文件的完整代码给看下,谢谢了

Quietly elegant | 园豆:202 (菜鸟二级) | 2013-10-21 22:33

http://www.cnblogs.com/qingse/archive/2013/02/16/2913430.html

见我的博客吧,有源码。

支持(1) 反对(0) 日暮青色 | 园豆:475 (菜鸟二级) | 2013-10-22 18:50
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册