在C#winform项目中遇到这样一种需求:在shell extension的上下文菜单中,点击一个子菜单,弹出主程序的一个窗体。
解决这个问题的思路有两个:
(1)在shell扩展中通过p/invoke调用windows api ——sendmessage,想主程序窗体发送特定信息,同时override主窗体程序的wndProc方法来处理消息。
(2)注册一个signal,由exporer进程来修改这个signal,主程序来侦听这个signal并作相应的处理。
由于不清楚windows如何注册signal,我采用的是第一种思路,在codeproject中找到代码,实现功能。
coder如下:
common变量:
/// <summary>
/// 平台调用帮助类
/// </summary>
public class Helpers
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LocalFree(IntPtr p);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LocalAlloc(int flag, int size);
/// <summary>
/// The SendMessage API
/// </summary>
/// <param name="hWnd">handle to the required window</param>
/// <param name="Msg">the system/Custom message to send</param>
/// <param name="wParam">first message parameter</param>
/// <param name="lParam">second message parameter</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref CopyDataStruct lParam);
}
context menu 中:
using System.Runtime.InteropServices;
public struct CopyDataStruct : IDisposable
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
public void Dispose()
{
if (this.lpData != IntPtr.Zero)
{
Helpers.LocalFree(this.lpData);
this.lpData = IntPtr.Zero;
}
}
}
public static bool SendArgs(IntPtr targetHWnd, string args)
{
CopyDataStruct cds = new CopyDataStruct();
try
{
cds.cbData = (args.Length + 1) * 2;
cds.lpData = Helpers.LocalAlloc(0x40, cds.cbData);
Marshal.Copy(args.ToCharArray(), 0, cds.lpData, args.Length);
cds.dwData = (IntPtr)1;
Helpers.SendMessage(targetHWnd, Helpers.WM_COPYDATA, IntPtr.Zero, ref cds);
}
finally
{
cds.Dispose();
}
return true;
}
主窗体后台:
/// <summary>
/// 消息处理函数
/// </summary>
/// <param name="m"></param>
protected override void WndProc(ref Message m)
{
if (m.Msg == Helpers.WM_COPYDATA)
{
CopyDataStruct st = (CopyDataStruct)Marshal.PtrToStructure(m.LParam, typeof(CopyDataStruct));
string strData = Marshal.PtrToStringUni(st.lpData);
if (strData.Equals(Helpers.SETTING))
{
// 弹出setting窗体
preferences_Click(null, null);
}
}
else
{
base.WndProc(ref m);
}
}