环境:VS2005 C# WinForm注:我的方法仅适用于同一系统内的进程,并且相关进程都是您自己编写的C#程序。
网上关于C#进程间通信的资料:WM_COPYDATA、共享内存、管道、消息队列、SOCKET...
其中WM_COPYDATA是最简单的,但它受制于窗口。当窗口不处在顶层时,WM_COPYDATA就可能失效。共享内存是较为基础的一种办法,但要用好它需要一些技巧。另外小弟对进程间消息传递的及时性非常看重,希望一个进程发出消息后,目标进程能够立即做出反应。但是进程边界决定了这是不可能的,因为发送消息的进程不可能直接调用目标进程里的函数。归根结底,目标进程得自己通过循环来查看外面是否有属于自己的消息。WM_COPYDATA是通过窗口消息循环来获取,其它几种方法则是在内部通过一个while循环来获取信息。因为要避免这个while循环影响到UI线程,所以必需另开线程。在了解了进程间通迅一些基础原理后,小弟总结出自己的方法。其原理是:以共享内存为基础,发送方将消息码(代表了消息含义)及消息相关数据,以及接收进程的标识(进程ID或主窗口Text)都放到共享内存中。接收方则循环检查共享内存中有无属于自己的消息数据,若有则取出处理。处理完后接收方将共享内存数据清空。
理论上需要对共享内存的写操作进行同步,不过目前没提供同步代码(小弟工程中不需要,所以懒得弄了...)
首先是共享内存操作类,用于进程间数据传递。可根据需要对其进行修改以附合您要求,比如其中结构体定义。 public static class ProcessMessaging
{
static ShareMem Data = new ShareMem(); /// <summary>
/// 获取共享内存(MyData结构)
/// </summary>
/// <returns></returns>
public static MyData GetShareMem()
{
int MemSize = Marshal.SizeOf(typeof(MyData)); if (Data.Init("MyData", MemSize) != 0)
{
return new MyData(-1);
} byte[] temp = new byte[MemSize]; try
{
Data.Read(ref temp, 0, temp.Length);
MyData stuc = (MyData)Tools.BytesToStuct(temp, typeof(MyData));
return stuc;
}
catch (Exception)
{
return new MyData(-1);
}
}//end fun /// <summary>
/// 设置共享内存(MyData结构)
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static bool SetShareMem(MyData data)
{
int MemSize = Marshal.SizeOf(typeof(MyData)); if (Data.Init("MyData", MemSize) != 0)//"MyData"共享内存名称,您起别的名字也可以
{
return false;
} try
{
byte[] b = Tools.StructToBytes(data);
Data.Write(b, 0, b.Length);
}
catch (Exception)
{
return false;
}
return true;
}//end fun
}//end class public class ShareMem
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr CreateFileMapping(int hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr OpenFileMapping(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, string lpName); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr MapViewOfFile(IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool UnmapViewOfFile(IntPtr pvBaseAddress); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle); [DllImport("kernel32", EntryPoint = "GetLastError")]
public static extern int GetLastError(); const int ERROR_ALREADY_EXISTS = 183; const int FILE_MAP_COPY = 0x0001;
const int FILE_MAP_WRITE = 0x0002;
const int FILE_MAP_READ = 0x0004;
const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004; const int PAGE_READONLY = 0x02;
const int PAGE_READWRITE = 0x04;
const int PAGE_WRITECOPY = 0x08;
const int PAGE_EXECUTE = 0x10;
const int PAGE_EXECUTE_READ = 0x20;
const int PAGE_EXECUTE_READWRITE = 0x40; const int SEC_COMMIT = 0x8000000;
const int SEC_IMAGE = 0x1000000;
const int SEC_NOCACHE = 0x10000000;
const int SEC_RESERVE = 0x4000000; const int INVALID_HANDLE_VALUE = -1; IntPtr m_hSharedMemoryFile = IntPtr.Zero;
IntPtr m_pwData = IntPtr.Zero;
bool m_bAlreadyExist = false;
bool m_bInit = false;
long m_MemSize = 0; public ShareMem()
{
} ~ShareMem()
{
Close();
} /// <summary>
/// 初始化共享内存
/// </summary>
/// <param name="strName">共享内存名称</param>
/// <param name="lngSize">共享内存大小</param>
/// <returns></returns>
public int Init(string strName, long lngSize)
{
if (lngSize <= 0 || lngSize > 0x00800000) lngSize = 0x00800000;
m_MemSize = lngSize;
if (strName.Length > 0)
{
//创建内存共享体(INVALID_HANDLE_VALUE)
m_hSharedMemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, IntPtr.Zero, (uint)PAGE_READWRITE, 0, (uint)lngSize, strName);
if (m_hSharedMemoryFile == IntPtr.Zero)
{
m_bAlreadyExist = false;
m_bInit = false;
return 2; //创建共享体失败
}
else
{
if (GetLastError() == ERROR_ALREADY_EXISTS) //已经创建
{
m_bAlreadyExist = true;
}
else //新创建
{
m_bAlreadyExist = false;
}
}
//---------------------------------------
//创建内存映射
m_pwData = MapViewOfFile(m_hSharedMemoryFile, FILE_MAP_WRITE, 0, 0, (uint)lngSize);
if (m_pwData == IntPtr.Zero)
{
m_bInit = false;
CloseHandle(m_hSharedMemoryFile);
return 3; //创建内存映射失败
}
else
{
m_bInit = true;
if (m_bAlreadyExist == false)
{
//初始化
MyData data = new MyData(-1);
byte[] bd = Tools.StructToBytes(data);
Marshal.Copy(m_pwData, bd, 0, bd.Length);
}
}
//----------------------------------------
}
else
{
return 1; //参数错误
} return 0; //创建成功
}//end fun /// <summary>
/// 关闭共享内存
/// </summary>
public void Close()
{
if (m_bInit)
{
UnmapViewOfFile(m_pwData);
CloseHandle(m_hSharedMemoryFile);
}
} /// <summary>
/// 读数据
/// </summary>
/// <param name="bytData">数据</param>
/// <param name="lngAddr">起始地址</param>
/// <param name="lngSize">个数</param>
/// <returns></returns>
public int Read(ref byte[] bytData, int lngAddr, int lngSize)
{
if (lngAddr + lngSize > m_MemSize) return 2; //超出数据区
if (m_bInit)
{
Marshal.Copy(m_pwData, bytData, lngAddr, lngSize);
}
else
{
return 1; //共享内存未初始化
}
return 0; //读成功
} /// <summary>
/// 写数据
/// </summary>
/// <param name="bytData">数据</param>
/// <param name="lngAddr">起始地址</param>
/// <param name="lngSize">数据长度</param>
/// <returns></returns>
public int Write(byte[] bytData, int lngAddr, int lngSize)
{
if (lngAddr + lngSize > m_MemSize) return 2; //超出数据区
if (m_bInit)
{
Marshal.Copy(bytData, lngAddr, m_pwData, lngSize);
}
else
{
return 1; //共享内存未初始化
}
return 0; //写成功
}
}//end class--下面继续
网上关于C#进程间通信的资料:WM_COPYDATA、共享内存、管道、消息队列、SOCKET...
其中WM_COPYDATA是最简单的,但它受制于窗口。当窗口不处在顶层时,WM_COPYDATA就可能失效。共享内存是较为基础的一种办法,但要用好它需要一些技巧。另外小弟对进程间消息传递的及时性非常看重,希望一个进程发出消息后,目标进程能够立即做出反应。但是进程边界决定了这是不可能的,因为发送消息的进程不可能直接调用目标进程里的函数。归根结底,目标进程得自己通过循环来查看外面是否有属于自己的消息。WM_COPYDATA是通过窗口消息循环来获取,其它几种方法则是在内部通过一个while循环来获取信息。因为要避免这个while循环影响到UI线程,所以必需另开线程。在了解了进程间通迅一些基础原理后,小弟总结出自己的方法。其原理是:以共享内存为基础,发送方将消息码(代表了消息含义)及消息相关数据,以及接收进程的标识(进程ID或主窗口Text)都放到共享内存中。接收方则循环检查共享内存中有无属于自己的消息数据,若有则取出处理。处理完后接收方将共享内存数据清空。
理论上需要对共享内存的写操作进行同步,不过目前没提供同步代码(小弟工程中不需要,所以懒得弄了...)
首先是共享内存操作类,用于进程间数据传递。可根据需要对其进行修改以附合您要求,比如其中结构体定义。 public static class ProcessMessaging
{
static ShareMem Data = new ShareMem(); /// <summary>
/// 获取共享内存(MyData结构)
/// </summary>
/// <returns></returns>
public static MyData GetShareMem()
{
int MemSize = Marshal.SizeOf(typeof(MyData)); if (Data.Init("MyData", MemSize) != 0)
{
return new MyData(-1);
} byte[] temp = new byte[MemSize]; try
{
Data.Read(ref temp, 0, temp.Length);
MyData stuc = (MyData)Tools.BytesToStuct(temp, typeof(MyData));
return stuc;
}
catch (Exception)
{
return new MyData(-1);
}
}//end fun /// <summary>
/// 设置共享内存(MyData结构)
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static bool SetShareMem(MyData data)
{
int MemSize = Marshal.SizeOf(typeof(MyData)); if (Data.Init("MyData", MemSize) != 0)//"MyData"共享内存名称,您起别的名字也可以
{
return false;
} try
{
byte[] b = Tools.StructToBytes(data);
Data.Write(b, 0, b.Length);
}
catch (Exception)
{
return false;
}
return true;
}//end fun
}//end class public class ShareMem
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr CreateFileMapping(int hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr OpenFileMapping(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, string lpName); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr MapViewOfFile(IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool UnmapViewOfFile(IntPtr pvBaseAddress); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle); [DllImport("kernel32", EntryPoint = "GetLastError")]
public static extern int GetLastError(); const int ERROR_ALREADY_EXISTS = 183; const int FILE_MAP_COPY = 0x0001;
const int FILE_MAP_WRITE = 0x0002;
const int FILE_MAP_READ = 0x0004;
const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004; const int PAGE_READONLY = 0x02;
const int PAGE_READWRITE = 0x04;
const int PAGE_WRITECOPY = 0x08;
const int PAGE_EXECUTE = 0x10;
const int PAGE_EXECUTE_READ = 0x20;
const int PAGE_EXECUTE_READWRITE = 0x40; const int SEC_COMMIT = 0x8000000;
const int SEC_IMAGE = 0x1000000;
const int SEC_NOCACHE = 0x10000000;
const int SEC_RESERVE = 0x4000000; const int INVALID_HANDLE_VALUE = -1; IntPtr m_hSharedMemoryFile = IntPtr.Zero;
IntPtr m_pwData = IntPtr.Zero;
bool m_bAlreadyExist = false;
bool m_bInit = false;
long m_MemSize = 0; public ShareMem()
{
} ~ShareMem()
{
Close();
} /// <summary>
/// 初始化共享内存
/// </summary>
/// <param name="strName">共享内存名称</param>
/// <param name="lngSize">共享内存大小</param>
/// <returns></returns>
public int Init(string strName, long lngSize)
{
if (lngSize <= 0 || lngSize > 0x00800000) lngSize = 0x00800000;
m_MemSize = lngSize;
if (strName.Length > 0)
{
//创建内存共享体(INVALID_HANDLE_VALUE)
m_hSharedMemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, IntPtr.Zero, (uint)PAGE_READWRITE, 0, (uint)lngSize, strName);
if (m_hSharedMemoryFile == IntPtr.Zero)
{
m_bAlreadyExist = false;
m_bInit = false;
return 2; //创建共享体失败
}
else
{
if (GetLastError() == ERROR_ALREADY_EXISTS) //已经创建
{
m_bAlreadyExist = true;
}
else //新创建
{
m_bAlreadyExist = false;
}
}
//---------------------------------------
//创建内存映射
m_pwData = MapViewOfFile(m_hSharedMemoryFile, FILE_MAP_WRITE, 0, 0, (uint)lngSize);
if (m_pwData == IntPtr.Zero)
{
m_bInit = false;
CloseHandle(m_hSharedMemoryFile);
return 3; //创建内存映射失败
}
else
{
m_bInit = true;
if (m_bAlreadyExist == false)
{
//初始化
MyData data = new MyData(-1);
byte[] bd = Tools.StructToBytes(data);
Marshal.Copy(m_pwData, bd, 0, bd.Length);
}
}
//----------------------------------------
}
else
{
return 1; //参数错误
} return 0; //创建成功
}//end fun /// <summary>
/// 关闭共享内存
/// </summary>
public void Close()
{
if (m_bInit)
{
UnmapViewOfFile(m_pwData);
CloseHandle(m_hSharedMemoryFile);
}
} /// <summary>
/// 读数据
/// </summary>
/// <param name="bytData">数据</param>
/// <param name="lngAddr">起始地址</param>
/// <param name="lngSize">个数</param>
/// <returns></returns>
public int Read(ref byte[] bytData, int lngAddr, int lngSize)
{
if (lngAddr + lngSize > m_MemSize) return 2; //超出数据区
if (m_bInit)
{
Marshal.Copy(m_pwData, bytData, lngAddr, lngSize);
}
else
{
return 1; //共享内存未初始化
}
return 0; //读成功
} /// <summary>
/// 写数据
/// </summary>
/// <param name="bytData">数据</param>
/// <param name="lngAddr">起始地址</param>
/// <param name="lngSize">数据长度</param>
/// <returns></returns>
public int Write(byte[] bytData, int lngAddr, int lngSize)
{
if (lngAddr + lngSize > m_MemSize) return 2; //超出数据区
if (m_bInit)
{
Marshal.Copy(bytData, lngAddr, m_pwData, lngSize);
}
else
{
return 1; //共享内存未初始化
}
return 0; //写成功
}
}//end class--下面继续
//根据需要扩展该结构体
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)]
public struct MyData
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)]
public char[] c0;
public int l0; public int i0;
public int i1;
public int i2;
public int i3;
public int i4;
public int i5; //placeholder:该参数我用不着,可C#规定必需有,所以起个这名字
public MyData(int placeholder)
{
c0 = new char[1024];
l0 = 0;
i0 = 0;
i1 = 0;
i2 = 0;
i3 = 0;
i4 = 0;
i5 = 0;
}//end fun /// <summary>
/// 进程间通信,进程ID,-2表示所有进程
/// </summary>
public int ProcessID
{
get
{ return i4; }
set
{ i4 = value;
}
} /// <summary>
/// 进程间通信,消息码
/// </summary>
public int InfoCode
{
get
{ return i5; }
set
{ i5 = value; }
} /// <summary>
/// 示例字符串参数
/// </summary>
public string Url
{
get
{
if (l0 > 0)
{ return new string(c0, 0, l0); }
else
{
return "";
}
}
set
{
if (value != null)
{
value.CopyTo(0, c0, 0, value.Length);
l0 = value.Length;
}
}
} /// <summary>
/// 示例矩形参数
/// </summary>
public Rectangle WindowPosition
{
get
{ return new Rectangle(i0, i1, i2, i3); }
set
{
i0 = value.X;
i1 = value.Y;
i2 = value.Width;
i3 = value.Height;
}
}
}//end struct
public static class Tools
{
//序列化结构体复制入byte数组
public static byte[] StructToBytes(object structObj)
{
//得到结构体的大小
int size = Marshal.SizeOf(structObj);
//创建byte数组
byte[] bytes = new byte[size];
//分配结构体大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将结构体拷到分配好的内存空间
Marshal.StructureToPtr(structObj, structPtr, false);
//从内存空间拷到byte数组
Marshal.Copy(structPtr, bytes, 0, size);
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回byte数组
return bytes;
} //byte数组复制入序列化结构体,注意在返回结果前加上强制转换
public static object BytesToStuct(byte[] bytes, Type type)
{
//得到结构体的大小
int size = Marshal.SizeOf(type);
//byte数组长度小于结构体的大小
if (size > bytes.Length)
{
//返回空
return null;
}
//分配结构体大小的内存空间
IntPtr structPtr = Marshal.AllocHGlobal(size);
//将byte数组拷到分配好的内存空间
Marshal.Copy(bytes, 0, structPtr, size);
//将内存空间转换为目标结构体
object obj = Marshal.PtrToStructure(structPtr, type);
//释放内存空间
Marshal.FreeHGlobal(structPtr);
//返回结构体
return obj;
}//end fun
}//end class
如果要向一个进程发送消息: MyData data = ProcessMessaging.GetShareMem();//您new一个也行
data.InfoCode = ...;//消息码
data.ProcessID = ...;//接收进程ID
...其它数据
ProcessMessaging.SetShareMem(data);接下来说下接收方,以下代码都位于主窗口CPP中: int ProcessID = 0;
System.Timers.Timer time = new System.Timers.Timer(10);//实例化Timer类,设置间隔时间为10毫秒; 下面4行代码在构造函数中添加:
ProcessID = Process.GetCurrentProcess().Id;
time.Elapsed += new System.Timers.ElapsedEventHandler(theout);//到达时间的时候执行事件;
time.AutoReset = true;//设置是执行一次(false)还是一直执行(true);
time.Enabled = false;//是否执行System.Timers.Timer.Elapsed事件;
//load事件中启动time
public void theout(object source, System.Timers.ElapsedEventArgs e)
{
ProcessMessageHandler pmh = new ProcessMessageHandler(ProcessMessage);
this.Invoke(pmh);
}//end fun public delegate void ProcessMessageHandler(); public void ProcessMessage()
{
MyData data = ProcessMessaging.GetShareMem();
if (data.ProcessID == ProcessID)
{
switch (InfoCode)
{
...处理
}
}
...清空data
ProcessMessaging.SetShareMem(data);
}
最后,欢迎大家做出修改补充!
.NET4.0提供了操作内存映射文件的直接支持。换vs2010吧,这样你这不必那么辛苦的PInvoke了。
貌似代码没有贴全,楼主能否再提供以下,非常感谢
这句话是什么意思?一个解决方案下有两个工程,产生两个进程。如果就是这两个进程通信,那么,楼主的这个类class ProcessMessaging应该放在哪里呢?我试了一下,好像不行啊?求帮助……