前言
在开发一款接收其他程序命令行参数并显示为列表的应用时,我们遇到了一个挑战:如何防止程序被多次启动,并且确保新收到的消息能正确传递给已经运行的实例。本文将详细介绍如何通过C#代码实现这一目标,包括防止重复运行、激活已有窗口以及消息传递。
效果图
正文
1、命令行读取
首先,需要从命令行中读取传入的参数。这可以通过修改Program.cs中的Main方法来完成:
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1(args));
}
在此基础上,在窗口初始化时处理这些参数,以添加到列表中。
2、禁止程序重复运行
为了保证程序不会被重复运行,我们可以使用以下代码检测是否已有相同进程正在运行:
public static Process RunningInstance()
{
Process current = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName(current.ProcessName);
foreach (Process process in processes)
{
if (process.Id != current.Id && Assembly.GetExecutingAssembly().Location.Replace("/", "\\") == current.MainModule.FileName)
{
return process;
}
}
return null;
}
如果检测到已有实例,则通过以下代码激活它:
[DllImport("User32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
public static void HandleRunningInstance(Process instance)
{
ShowWindowAsync(instance.MainWindowHandle, WS_SHOWNORMAL);
SetForegroundWindow(instance.MainWindowHandle);
}
3、消息传递机制
当有新的命令行参数时,我们需要将其发送给已有的程序实例。这里采用SendMessage API来实现:
const int WM_COPYDATA = 0x004A;
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr hWnd, int msg, uint wParam, ref COPYDATASTRUCT lParam);
[DllImport("User32.dll", EntryPoint = "FindWindow")]
private static extern int FindWindow(string lpClassName, string lpWindowName);
publicstruct COPYDATASTRUCT
{
public IntPtr dwData;
publicint cbData;
[MarshalAs(UnmanagedType.LPStr)]
publicstring lpData;
}
封装一个方法用于发送消息:
static void Send(string[] args)
{
int WINDOW_HANDLER = FindWindow(null, @"你的窗口标题");
if (WINDOW_HANDLER != 0)
{
StringBuilder sb = new StringBuilder();
foreach (string item in args)
{
sb.Append(item + " ");
}
byte[] sarr = System.Text.Encoding.Default.GetBytes(sb.ToString());
int len = sarr.Length;
COPYDATASTRUCT cds;
cds.dwData = (IntPtr)100;
cds.lpData = sb.ToString();
cds.cbData = len + 1;
int sendRet = SendMessage((IntPtr)WINDOW_HANDLER, WM_COPYDATA, 0, ref cds);
if (sendRet == 0)
{
MessageBox.Show("传递参数失败!");
}
}
else
{
MessageBox.Show("没有发现旧窗口!");
}
}
最后,修改Main方法以支持上述逻辑,并在已有实例的情况下调用Send方法传递参数。
4、接收消息
在主窗体中重写DefWndProc方法来接收并处理来自其他实例的消息:
protected override void DefWndProc(ref System.Windows.Forms.Message m)
{
constint WM_COPYDATA = 0x004A;
switch (m.Msg)
{
case WM_COPYDATA:
COPYDATASTRUCT mystr = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT));
string[] s = mystr.lpData.Split(newstring[]{" "}, StringSplitOptions.RemoveEmptyEntries);
// 处理接收到的数据
break;
default:
base.DefWndProc(ref m);
break;
}
}
总结
通过以上步骤,实现了C#应用程序防止重复运行并能够将新接收的命令行参数传递给已有实例的功能。
过程包括了命令行参数读取、进程检测、窗口激活及消息传递等多个环节。尽管界面美观度上可能有所妥协,但功能上满足了预期需求。
关键词
C#、#防止重复运行、#SendMessage、COPYDATASTRUCT、#命令行参数、#WinForm、WM_COPYDATA、SetForegroundWindow、ShowWindowAsync
阅读原文:原文链接
该文章在 2025/12/10 18:27:30 编辑过