NullReference

Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
使用共享内存在进程之间传送数据

今日在项目中碰到一个问题,就是一个程序的两个进程之间需要频繁的传送数据,具体的来说是一个需要频繁的发送数据而另一个实例需要频繁的访问获得这些数据。当然,这个问题是有很多解的,例如:数据库,再例如:文件。可是因为这个频繁程度太高了,例如:一秒钟十次,在这种情况下,使用数据库或者是文件可能都不是一个Good Idea。


Update: 其实开始想到的方案还包括Remoting, Socket之类的,不过明显的是小题大做,杀鸡用牛刀了,呵呵。所以最后也就放弃了。


那么什么解决方案好呢?众所周知,内存的速度是除了CPU缓存外最快的,当然是我们的首选了,可是看遍了.Net的类库也没有找到可以用来实现这个方案的办法,看来只能依靠Win32 API了。在网上得到资料,可以使用API开辟共享内存从而达到在不同的进程之间共享数据的可能。


关键API类:

    public static class API
    {
        [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();

        [DllImport("msvcrt.dll", SetLastError = true)]
        public static extern int memcmp(IntPtr ptr1, IntPtr ptr2, int count);

        [DllImport("msvcrt.dll", SetLastError = true)]
        public static extern unsafe int memcmp(void* ptr1, void* ptr2, int count);

        [DllImport("ntdll.dll")]
        public static extern int RtlCompareMemory(IntPtr Destination, IntPtr Source, int Length);

        public const int ERROR_ALREADY_EXISTS = 183;

        public const int FILE_MAP_COPY = 0x0001;
        public const int FILE_MAP_WRITE = 0x0002;
        public const int FILE_MAP_READ = 0x0004;
        public const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004;

        public const int PAGE_READONLY = 0x02;
        public const int PAGE_READWRITE = 0x04;
        public const int PAGE_WRITECOPY = 0x08;
        public const int PAGE_EXECUTE = 0x10;
        public const int PAGE_EXECUTE_READ = 0x20;
        public const int PAGE_EXECUTE_READWRITE = 0x40;

        public const int SEC_COMMIT = 0x8000000;
        public const int SEC_IMAGE = 0x1000000;
        public const int SEC_NOCACHE = 0x10000000;
        public const int SEC_RESERVE = 0x4000000;

        public const int INVALID_HANDLE_VALUE = -1;
    }
代码实现:

    public enum MemoryResult
    {
        Success,
        NotInitialized,
        NoChange,
        Failed
    }

    public class ShareMemory<T> : IDisposable
        where T : class
    {
        IntPtr m_hSharedMemoryFile = IntPtr.Zero;
        IntPtr m_pwData = IntPtr.Zero;
        bool m_bAlreadyExist = false;
        bool m_bInit = false;
        long m_MemSize = 0;
        int m_size;
        byte[] m_lastData;

        public ShareMemory()
        {
        }
        ~ShareMemory()
        {
            Close();
        }

        /// <summary>
        /// 初始化共享内存
        /// </summary>
        public MemoryResult Init(string strName)
        {
            m_size = 10240; //固定10K
            var lngSize = m_size;

            if (lngSize <= 0 || lngSize > 0x00800000) lngSize = 0x00800000;
            m_MemSize = lngSize;
            if (strName.Length > 0)
            {
                //创建内存共享体 (INVALID_HANDLE_VALUE)
                m_hSharedMemoryFile = API.CreateFileMapping(API.INVALID_HANDLE_VALUE, IntPtr.Zero, (uint)API.PAGE_READWRITE, 0, (uint)lngSize, strName);
                if (m_hSharedMemoryFile == IntPtr.Zero)
                {
                    m_bAlreadyExist = false;
                    m_bInit = false;
                    return MemoryResult.Failed; //创建共享体失败
                }
                else
                {
                    if (API.GetLastError() == API.ERROR_ALREADY_EXISTS)  //已经创建
                    {
                        m_bAlreadyExist = true;
                    }
                    else                                         //新创建
                    {
                        m_bAlreadyExist = false;
                    }
                }
                //---------------------------------------
                //创建内存映射
                m_pwData = API.MapViewOfFile(m_hSharedMemoryFile, API.FILE_MAP_WRITE, 0, 0, (uint)lngSize);
                if (m_pwData == IntPtr.Zero)
                {
                    m_bInit = false;
                    API.CloseHandle(m_hSharedMemoryFile);
                    return MemoryResult.Failed; //创建内存映射失败
                }
                else
                {
                    m_bInit = true;
                    if (m_bAlreadyExist == false)
                    {
                        //初始化
                    }
                }
                //----------------------------------------
            }
            else
            {
                return MemoryResult.Failed; //参数错误     
            }

            return MemoryResult.Success;     //创建成功
        }
        /// <summary>
        /// 关闭共享内存
        /// </summary>
        public void Close()
        {
            if (m_bInit)
            {
                API.UnmapViewOfFile(m_pwData);
                API.CloseHandle(m_hSharedMemoryFile);
            }
        }

        /// <summary>
        /// 读数据
        /// </summary>
        public unsafe MemoryResult Read(out T obj)
        {
            obj = default(T);
            byte[] bytData = new byte[m_size];

            if (m_bInit)
            {
                Marshal.Copy(m_pwData, bytData, 0, m_size);

                if (m_lastData != null)
                {
                    fixed (byte* p1 = m_lastData)
                    {
                        fixed (byte* p2 = bytData)
                        {
                            if (API.memcmp(p1, p2, m_size) == 0)
                                return MemoryResult.NoChange;
                        }
                    }
                }

                m_lastData = bytData;

                var fmt = new BinaryFormatter();
                using (var ms = new MemoryStream(bytData))
                {
                    try
                    {
                        obj = (T)fmt.Deserialize(ms);
                    }
                    catch (SerializationException)
                    {
                        return MemoryResult.Failed;
                    }
                }
            }
            else
            {
                return MemoryResult.NotInitialized; //共享内存未初始化
            }
            return MemoryResult.Success;     //读成功
        }

        /// <summary>
        /// 写数据
        /// </summary>
        public MemoryResult Write(T obj)
        {
            var fmt = new BinaryFormatter();
            byte[] bytData;

            if (obj is byte[])
                bytData = obj as byte[];
            else
            {
                using (var ms = new MemoryStream())
                {
                    fmt.Serialize(ms, obj);
                    bytData = ms.ToArray();
                }
            }

            if (m_bInit)
                Marshal.Copy(bytData, 0, m_pwData, bytData.Length);
            else
                return MemoryResult.NotInitialized; //共享内存未初始化

            return MemoryResult.Success;     //写成功
        }

        #region IDisposable Members

        public void Dispose()
        {
            Close();
        }

        #endregion
    }

使用也很简单:



写数据:

            if (mem == null)
            {
                mem = new ShareMemory<ChannelStatus[]>();
                mem.Init("ChannelStatus");
            }

            var xml = PhoneCallHelper.SendRequest(PhoneCallAction.getChStatus);
            foreach (XmlElement ele in xml.DocumentElement.SelectNodes("chStatus"))
            {
                var status = GetChannelStatusFromXElement(ele);
                _status[status.Channel] = status;
            }

            mem.Write(_status.Values.ToArray());

读数据:
                using (var mem = new ShareMemory<ChannelStatus[]>())
                {
                    if (mem.Init("ChannelStatus") != MemoryResult.Success)
                        throw new Exception("内存初始化失败");

                    ChannelStatus[] status;
                    if (mem.Read(out status) == MemoryResult.Success)
                        return status;

                    return null;
                }

posted on 2010-02-05 15:44  NullReference  阅读(4412)  评论(15编辑  收藏  举报