VC++的共享内存操作代码实现起来相对比较容易,但是用C#语言来实现,就有一定难度,由于工作需要,把以前VC开发的共享内存代码要用C#实现,别说,还费了不少周折,毕竟C#操作API函数和地址指针不是那么直接,还好,总算完成了,效果还不错。

    

共享内存操作类:    

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace ShareMemLib
{
    
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, 00, (uint)lngSize);
                
if (m_pwData == IntPtr.Zero)
                {
                    m_bInit 
= false;
                    CloseHandle(m_hSharedMemoryFile);
                    
return 3//创建内存映射失败
                }
                
else
                {
                    m_bInit 
= true;
                    
if (m_bAlreadyExist == false)
                    {
                        
//初始化
                    }
                }
                
//----------------------------------------
            }
            
else
            {
                
return 1//参数错误     
            }

            
return 0;     //创建成功
        }
        
/// <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;     //写成功
        }
    }
}

测试例程: 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using ShareMemLib;

namespace YFShareMem
{
    
public partial class frmShareMem : Form
    {
        ShareMem MemDB
=new ShareMem();
        
public frmShareMem()
        {
            InitializeComponent();
        }

        
private void btnOpen_Click(object sender, EventArgs e)
        {
            
if (MemDB.Init("YFMemTest"10240!= 0)
            {
                
//初始化失败
                MessageBox.Show("初始化失败");
            }
            
else
            {
                btnOpen.Enabled 
= false;
                chkWrite.Enabled 
= true;
                tmrTime.Enabled 
= true;
            }
        }

        
private void tmrTime_Tick(object sender, EventArgs e)
        {
            
byte[] bytData = new byte[16];
            
int intRet = MemDB.Read(ref bytData, 016);
            lstData.Items.Clear(); 
            
if (intRet == 0)
            {
                
for (int i = 0; i < 16; i++)
                {
                    lstData.Items.Add(bytData[i].ToString());
                }

                
if (chkWrite.Checked)
                {
                    bytData[
0]++;
                    bytData[
1+= 2;
                    
if (bytData[0> 200) bytData[0= 0;
                    
if (bytData[1> 200) bytData[1= 0;
                    MemDB.Write(bytData, 
016);
                }
            }           
        }

    }
}
全文】

首先还是定义非托管调用,如下:


const int INVALID_HANDLE_VALUE = -1;
const int PAGE_READWRITE = 0x04;
  //共享内存
  [DllImport("Kernel32.dll",EntryPoint="CreateFileMapping")]
  private static extern IntPtr CreateFileMapping(IntPtr hFile, //HANDLE hFile,
   UInt32 lpAttributes,//LPSECURITY_ATTRIBUTES lpAttributes,  //0
   UInt32 flProtect,//DWORD flProtect
   UInt32 dwMaximumSizeHigh,//DWORD dwMaximumSizeHigh,
   UInt32 dwMaximumSizeLow,//DWORD dwMaximumSizeLow,
   string lpName//LPCTSTR lpName
   );

  [DllImport("Kernel32.dll",EntryPoint="OpenFileMapping")]
  private static extern IntPtr OpenFileMapping(
   UInt32 dwDesiredAccess,//DWORD dwDesiredAccess,
   int bInheritHandle,//BOOL bInheritHandle,
   string lpName//LPCTSTR lpName
   );

  const int FILE_MAP_ALL_ACCESS = 0x0002;
  const int FILE_MAP_WRITE = 0x0002;

  [DllImport("Kernel32.dll",EntryPoint="MapViewOfFile")]
  private static extern IntPtr MapViewOfFile(
   IntPtr hFileMappingObject,//HANDLE hFileMappingObject,
   UInt32 dwDesiredAccess,//DWORD dwDesiredAccess
   UInt32 dwFileOffsetHight,//DWORD dwFileOffsetHigh,
   UInt32 dwFileOffsetLow,//DWORD dwFileOffsetLow,
   UInt32 dwNumberOfBytesToMap//SIZE_T dwNumberOfBytesToMap
   );

  [DllImport("Kernel32.dll",EntryPoint="UnmapViewOfFile")]
  private static extern int UnmapViewOfFile(IntPtr lpBaseAddress);

  [DllImport("Kernel32.dll",EntryPoint="CloseHandle")]
  private static extern int CloseHandle(IntPtr hObject);

然后分别在AB两个进程中定义如下两个信号量及相关变量;

  private Semaphore m_Write;  //可写的信号
  private Semaphore m_Read;  //可读的信号
  private IntPtr handle;     //文件句柄
  private IntPtr addr;       //共享内存地址
  uint mapLength;            //共享内存长


定义这两个信号量是为读写互斥用的。
在A进程中创建共享内存:

m_Write = new Semaphore(1,1,"WriteMap");
m_Read = new Semaphore(0,1,"ReadMap");
mapLength = 1024;
IntPtr hFile = new IntPtr(INVALID_HANDLE_VALUE);   
handle = CreateFileMapping(hFile,0,PAGE_READWRITE,0,mapLength,"shareMemory");
addr = MapViewOfFile(handle,FILE_MAP_ALL_ACCESS,0,0,0);

然后再向共享内存中写入数据:

m_Write.WaitOne();
byte[] sendStr = Encoding.Default.GetBytes(txtMsg.Text + '\0');
//如果要是超长的话,应另外处理,最好是分配足够的内存
if(sendStr.Length < mapLength)
      Copy(sendStr,addr);
m_Read.Release();


这是在一个单独的方法中实现的,可多次调用,但受信号量的控制。其中txtMsg是一个文本框控件,实际中可用任意字符串,加最后的'\0'是为了让在共享内存中的字符串有一个结束符,否则在内存中取出时是以'\0'为准的,就会出现取多的情况。
Copy方法的实现如下:

static unsafe void Copy(byte[] byteSrc,IntPtr dst)
  {
   fixed (byte* pSrc = byteSrc)
   {
    byte* pDst = (byte*)dst;
    byte* psrc = pSrc;
    for(int i=0;i<byteSrc.Length;i++)
    {
     *pDst = *psrc;
     pDst++;
     psrc ++;
    }
   }
  }

注意unsafe 关键字,在编译时一定要打开非安全代码开关。
最后不要忘了在A进程中关闭共享内存对象,以免内存泄露。

   UnmapViewOfFile(addr);
   CloseHandle(handle);


要在B进程中读取共享内存中的数据,首先要打开共享内存对象:

m_Write = Semaphore.OpenExisting("WriteMap");
m_Read = Semaphore.OpenExisting("ReadMap");
handle = OpenFileMapping(0x0002,0,"shareMemory");

读取共享内存中的数据:

   m_Read.WaitOne();
   string str = MapViewOfFile(handle,FILE_MAP_ALL_ACCESS,0,0,0);
   txtMsg.Text = str;
   m_Write.Release();
posted on 2009-05-21 16:39  arong.NET  阅读(596)  评论(0编辑  收藏  举报