传统的应用程序间跨进程高速的传输数据有多种方式,作为Win32下的主流,许多程序中都会看到CreateFileMapping函数,这让大家分享一下怎样合理而且安全的建立一个内查对象,以及整个情况的配景和作业流程。
[StructLayout(LayoutKind.Sequential)]
internal class SECURITY_ATTRIBUTES
{
public int nLength;
public SafeLocalMemHandle lpSecurityDescriptor;
public bool bInheritHandle;
public SECURITY_ATTRIBUTES()
{
this.nLength = 12;
this.lpSecurityDescriptor = new SafeLocalMemHandle(IntPtr.Zero, false);
}
}
[SuppressUnmanagedCodeSecurity, HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true)]
internal sealed class SafeFileMappingHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal SafeFileMappingHandle() : base(true) { }
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal SafeFileMappingHandle(IntPtr handle, bool ownsHandle)
: base(ownsHandle)
{
SetHandle(handle);
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
}
[SuppressUnmanagedCodeSecurity, HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true)]
internal sealed class SafeLocalMemHandle : SafeHandleZeroOrMinusOneIsInvalid
{
internal SafeLocalMemHandle() : base(true) { }
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal SafeLocalMemHandle(IntPtr existingHandle, bool ownsHandle)
: base(ownsHandle)
{
SetHandle(existingHandle);
}
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool ConvertStringSecurityDescriptorToSecurityDescriptor(string StringSecurityDescriptor, int StringSDRevision, out SafeLocalMemHandle pSecurityDescriptor, IntPtr SecurityDescriptorSize);
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport("kernel32.dll")]
private static extern IntPtr LocalFree(IntPtr hMem);
protected override bool ReleaseHandle()
{
return (LocalFree(handle) == IntPtr.Zero);
}
}
internal sealed class SafeViewOfFileHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal SafeViewOfFileHandle() : base(true) { }
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal SafeViewOfFileHandle(IntPtr handle, bool ownsHandle) : base(ownsHandle) { SetHandle(handle); }
protected override bool ReleaseHandle()
{
if (UnmapViewOfFile(handle))
{
base.handle = IntPtr.Zero;
return true;
}
return false;
}
}
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;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern SafeFileMappingHandle OpenFileMapping(int dwDesiredAccess, bool bInheritHandle, string lpName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern SafeFileMappingHandle CreateFileMapping(IntPtr hFile, SECURITY_ATTRIBUTES lpFileMappingAttributes, int flProtect, int dwMaximumSizeHigh, int dwMaximumSizeLow, string lpName);
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport("kernel32.dll", ExactSpelling = true)]
static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern SafeViewOfFileHandle MapViewOfFile(SafeFileMappingHandle handle, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, UIntPtr dwNumerOfBytesToMap);
安全的建立非托管平台的handle是整篇我推许的地方,由于内查对象如果以外的未能释放会产生内存泄漏,危害很严重。.net平台下PInvoke支持多种封送策略,以是尽量的使用安全的handle显得尤为重要。
下面简要讲一下情况,方法流程和序次参数。
情况:
文件映射的主体是文件,所有的映射会把文件流在当前建立视图的进程区建立一个地址映射,对这个内存地址的作业,就是对文件的作业,由于是在内存中建立了映射,以是每个进程可以独立的读写本身的映射区,最后可以调用方法同步革新到文件流上(本文内未实现)。 更为具体的介绍请自行google,必要注意的是命名对象的建立会和建立的进程相干。
方法流程和序次参数:
CreateFileMapping 第一位是一个文件的handle,如果用INVALID_HANDLE_VALUE将会使用默认的内存页文件作为映射。第二位是一个安全设置,为空的话会使用默认的安全控制表。第三位是读写权限和映射设置,支持OR操作。第三位是64位最大高位地址(为0,由于不会有这么大的文件)第四位是64位最大低位地址(未实际对象的物理内存长度)第五位是命名,为空是匿名对象,这里不讨论。
OpenFileMapping 第一位访问权限,第二位指定子进程是否可以共享该handle资源(涉及到handle的复制)。第三位就是命名。
MapViewOfFile第一位是上面两个方法得到的handle实例,第二位是访问权限,第三和第四位同CreateFileMapping意义上是视图相对的位移量。第五位是映射字节数,为0则映射整个建立块区。
从建立(或打开)映射开始,到建立视图,现在就可以在.net情况下使用对内存的读写了。
可以使用Marshal类的Copy方法,前面建立的安全handle上的DangerousGetHandle()方法将返回作业的安全指针。
handle是可以自释放的。
好了到告终束部分了,本来打算用这来实现某些功能,后来发现不太符合我的需求,结果这些代码就要被我废弃了,大抵上我看了十个不到的实现特例,此中以C++为主,而且这些作者都习惯性的作了包装类。
请看完这篇以后参考我的建议,不要在这之上再更多的去包装,对于简朴的数据传送和共享已经够了,我觉得毫无必要再繁琐下去加一个本身命名的class也不会突现什么万一设计不当反而会招人詈骂......当然如果刻意要做什么,除非你也往内里加了许多原子操作以及其他的同步作业控制,我也不能飞过来反对。就这样吧,盼望大家可以都学到知识。
最后这些方法和常量完全的颠末测试,请放心使用。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |