c#共享內存操作相對c++共享內存操作來說原理是一樣,但是c#會顯得有點復雜。
現把昨天封裝的讀寫共享內存封裝的函數記錄下來,一方面希望給需要這塊的有點幫助,另一方面則是做個備份吧。
[csharp]
/// <summary>
/// 寫共享內存
/// </summary>
/// <param name="structSize">需要映射的文件的字節數量</param>
/// <param name="obj">映射對象(簡單類型、結構體等)</param>
/// <param name="fileName">文件映射對象的名稱</param>
/// <param name="windowName">發送消息的窗口句柄</param>
/// <param name="Msg">發送消息</param>
/// <returns></returns>
public static int WriteToMemory(uint structSize, Object obj, string fileName, string windowName, uint Msg)
{
IntPtr hShareMemoryHandle = IntPtr.Zero;
IntPtr hVoid = IntPtr.Zero;
//判斷參數的合法性
if (structSize > 0 && fileName.Length > 0)
{
hShareMemoryHandle = CreateFileMapping(INVALID_HANDLE_VALUE, IntPtr.Zero, (uint)PAGE_READWRITE, 0, (uint)structSize, fileName);
if (hShareMemoryHandle == IntPtr.Zero)
{
//創建共享內存失敗,記log
MessageBox.Show("創建共享內存失敗"+publicInfo.GetLastError().ToString());
return -2;
}
else
{
if (ERROR_ALREADY_EXISTS == GetLastError())
{
//共享內存已經存在,記log
MessageBox.Show("共享內存已經存在");
return -3;
}
}
hVoid = MapViewOfFile(hShareMemoryHandle, FILE_MAP_WRITE, 0, 0, structSize);
if (hVoid == IntPtr.Zero)
{
CloseHandle(hShareMemoryHandle);
//文件映射失敗,記log
MessageBox.Show("文件映射失敗");
return -4;
}
Marshal.StructureToPtr(obj, hVoid, false);
//發送消息,通知接收
IntPtr handle = FindWindow(null, windowName.Trim());
if (handle == IntPtr.Zero)
{
//查找窗口失敗,記log
MessageBox.Show("查找窗口失敗");
return -5;
}
else
{
if (PostMessage(handle, (uint)Msg, 0, 0))
{
//發送消息成功
//MessageBox.Show("寫共享內存,通知發送消息成功");
}
}
}
else
{
//參數不合法,記log
MessageBox.Show("共享內存已經存在");
return -1;
}
return 0;
}
寫共享內存函數並沒有什麼需要說明,完全按照:
創建共享內存文件(CreateFileMapping)---》映射文件視圖到調用進程的地址空間(MapViewOfFile)---》寫數據到共享內存(Marshal.StructureToPtr)----》發送消息通知需要讀共享內存的窗口(PostMessage)
[csharp]
/// <summary>
/// 讀共享內存
/// </summary>
/// <param name="structSize">需要映射的文件的字節數量</param>
/// <param name="type">類型</param>
/// <param name="fileName">文件映射對象的名稱</param>
/// <returns>返回讀到的映射對象</returns>
public static Object ReadFromMemory(uint structSize, Type type, string fileName)
{
IntPtr hMappingHandle = IntPtr.Zero;
IntPtr hVoid = IntPtr.Zero;
hMappingHandle = OpenFileMapping((uint)FILE_MAP_READ, false, fileName);
if (hMappingHandle == IntPtr.Zero)
{
//打開共享內存失敗,記log
MessageBox.Show("打開共享內存失敗:" + publicInfo.GetLastError().ToString());
return null;
}
hVoid = MapViewOfFile(hMappingHandle, FILE_MAP_READ, 0, 0, structSize);
if (hVoid == IntPtr.Zero)
{
//文件映射失敗,記log
MessageBox.Show("文件映射失敗——讀共享內存");
return null;
}
Object obj = Marshal.PtrToStructure(hVoid, type);
if (hVoid != IntPtr.Zero)
{
UnmapViewOfFile(hVoid);
hVoid = IntPtr.Zero;
}
if (hMappingHandle != IntPtr.Zero)
{
CloseHandle(hMappingHandle);
hMappingHandle = IntPtr.Zero;
}
return obj;
}
讀共享內存,上邊代碼是一種方式,這裡是傳入一個Type類型,這樣就確保可以傳入任何類型。當讀到共享內存的數據時,采用
public static object PtrToStructure(IntPtr ptr, Type structureType);
函數,把非托管指針(共享內存獲得的指針)轉換為需要轉換的Type類型的對象。如果需要的話,可以通過顯示類型轉換轉換為需要的類型(例子繼續看)。
[csharp]
/// <summary>
/// 讀共享內存
/// </summary>
/// <param name="structSize">需要映射的文件的字節數量</param>
/// <param name="type">類型</param>
/// <param name="fileName">文件映射對象的名稱</param>
/// <returns>返回讀到的映射字節數據</returns>
public static byte[] ReadFromMemory(uint structSize, Type type, string fileName)
{
IntPtr hMappingHandle = IntPtr.Zero;
IntPtr hVoid = IntPtr.Zero;
hMappingHandle = OpenFileMapping((uint)FILE_MAP_READ, false, fileName);
if (hMappingHandle == IntPtr.Zero)
{
//打開共享內存失敗,記log
MessageBox.Show("打開共享內存失敗:" + publicInfo.GetLastError().ToString());
return null;
}
hVoid = MapViewOfFile(hMappingHandle, FILE_MAP_READ, 0, 0, structSize);
if (hVoid == IntPtr.Zero)
{
//文件映射失敗,記log
MessageBox.Show("文件映射失敗——讀共享內存");
return null;
}
//Object obj = Marshal.PtrToStructure(hVoid, type);
byte[] bytes = new byte[structSize];
Marshal.Copy(hVoid, bytes, 0, bytes.Length);
if (hVoid != IntPtr.Zero)
{
UnmapViewOfFile(hVoid);
hVoid = IntPtr.Zero;
}
if (hMappingHandle != IntPtr.Zero)
{
CloseHandle(hMappingHandle);
hMappingHandle = IntPtr.Zero;
}
return bytes;
}
此代碼和第一個讀共享內存不同的是,采用byte[]讀需要的共享內存。使用托管類中的Copy來轉換指針。
[csharp]
byte[] bytes = new byte[structSize];
arshal.Copy(hVoid, bytes, 0, bytes.Length);
調用簡單例子部分代碼如下:
注:passiveInfo是NotifyInfo結構體對象。
寫共享內存:
[csharp]
int iRet = publicInfo.WriteToMemory((uint)Marshal.SizeOf(passiveInfo),(Object)passiveInfo, "memName","FormMsg",(uint)publicInfo.WM_NOTIFY);
讀共享內存:
第一種情況調用:
[csharp]
passiveInfo = (NotifyPassiveInfo)publicInfo.ReadFromMemory((uint)Marshal.SizeOf(passiveInfo), typeof(NotifyPassiveInfo), publicInfo.SN_PASSIVEINFO);
第二種情況調用:
[csharp]
byte[] bytes = publicInfo.ReadFromMemory((uint)Marshal.SizeOf(passiveInfo), "memName");
passiveInfo = (NotifyInfo)BytesToStuct(bytes, typeof(NotifyInfo));
希望對你有幫助。
摘自 richerg85的專欄