不安全的代码对安全代码有影响吗?
据我所知,将方法标记为不安全将禁用对该代码的一些CLR检查,但除了DLL / EXE无法运行之外,这对系统的其他部分是否安全有任何影响。不受信任的环境。
特别是,
- 它们是否是对完整dll无效的任何安全检查,因为它被标记为不安全?
- 如果DLL被标记为不安全,但标记为不安全的方法实际上没有被调用,这是否与DLL标记为安全相同?
- 将不安全的代码保存在单独的DLL中是否有任何运行时的好处?
我有重写64位窗口上的嵌套控件的问题,如此处详述和解决方案(看起来工作的那个)涉及不安全的代码,我想了解添加此代码对我的项目的影响。
您的问题的答案是: unsafe
关键字并不意味着“不安全”,它意味着“可能不安全”。 编译器和框架无法确保它是安全的。 您可以确保代码无法对内存执行不安全的读取或写入操作。
我强烈建议您遵循您链接的文章中给出的建议:
1)重新设计应用程序以减少容器 数量并减少嵌套级别 。
如果您使用容器仅用于控制安排,请编写您自己的容器,可以使用一个级别进行所有安排。
更新
您可以修改该文章中的代码,使其不使用指针(即不需要unsafe关键字)。 请记住,现在需要编组,这意味着额外的复制。 这可能是一件好事,因为原始代码将一个WINDOWPOS指针从OS传递给BeginInvoke,它在操作系统生成指针的同一个调度事件期间不执行。换句话说,该代码已经臭了。
internal class MyTabPage : TabPage { private const int WM_WINDOWPOSCHANGING = 70; private const int WM_SETREDRAW = 0xB; private const int SWP_NOACTIVATE = 0x0010; private const int SWP_NOZORDER = 0x0004; private const int SWP_NOSIZE = 0x0001; private const int SWP_NOMOVE = 0x0002; [DllImport("User32.dll", CharSet = CharSet.Auto)] extern static int SendMessage(HandleRef hWnd, int msg, int wParam, int lParam); [DllImport("User32.dll", ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)] extern static bool SetWindowPos(HandleRef hWnd, HandleRef hWndInsertAfter, int x, int y, int cx, int cy, int flags); [StructLayout(LayoutKind.Sequential)] private class WINDOWPOS { public IntPtr hwnd; public IntPtr hwndInsertAfter; public int x; public int y; public int cx; public int cy; public int flags; }; private delegate void ResizeChildDelegate(WINDOWPOS wpos); private void ResizeChild(WINDOWPOS wpos) { // verify if it's the right instance of MyPanel if needed if ((this.Controls.Count == 1) && (this.Controls[0] is Panel)) { Panel child = this.Controls[0] as Panel; // stop window redraw to avoid flicker SendMessage(new HandleRef(child, child.Handle), WM_SETREDRAW, 0, 0); // start a new stack of SetWindowPos calls SetWindowPos(new HandleRef(child, child.Handle), new HandleRef(null, IntPtr.Zero), 0, 0, wpos.cx, wpos.cy, SWP_NOACTIVATE | SWP_NOZORDER); // turn window repainting back on SendMessage(new HandleRef(child, child.Handle), WM_SETREDRAW, 1, 0); // send repaint message to this control and its children this.Invalidate(true); } } protected override void WndProc(ref Message m) { if (m.Msg == WM_WINDOWPOSCHANGING) { WINDOWPOS wpos = new WINDOWPOS(); Marshal.PtrToStructure(m.LParam, wpos); Debug.WriteLine("WM_WINDOWPOSCHANGING received by " + this.Name + " flags " + wpos.flags); if (((wpos.flags & (SWP_NOZORDER | SWP_NOACTIVATE)) == (SWP_NOZORDER | SWP_NOACTIVATE)) && ((wpos.flags & ~(SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE)) == 0)) { if ((wpos.cx != this.Width) || (wpos.cy != this.Height)) { BeginInvoke(new ResizeChildDelegate(ResizeChild), wpos); return; } } } base.WndProc(ref m); } }
注意 :WINDOWPOS从值类型到引用类型的更改是有意的。 使用引用类型将副本数量减少到只有一个(初始编组)(**)。
再次更新我刚刚注意到代码最初使p / invoke声明公开。 永远不要在类(*)之外暴露p / invoke。 编写托管方法,如果您的目的是公开所提供的function,则调用私有p / invoke声明; 在这种情况下不是真的,p / invoke是严格内部的。
(*)好的,一个例外。 您正在创建NativeMethods
, UnsafeNativeMethods
等。这是FxCop进行p / invoke的推荐方法。
更新
(**)有人问我(其他地方)准确地描述为什么在这里使用引用类型更好,所以我在这里添加了这些信息。 我被问到的问题是,“这不会增加记忆压力吗?”
如果WINDOWPOS
是值类型,那么这将是事件序列:
1)从非托管内存复制到托管内存
WINDOWPOS wpos = Marshal.PtrToStructure(m.LParam, typeof(WINDOWPOS));
2)第二份?
BeginInvoke(new ResizeChildDelegate(ResizeChild), wpos);
等待! BeginInvoke
的签名是(Delegate, params object[])
。 这意味着wpos将被装箱。 所以是的,第二个副本发生在这里:拳击操作。
BeginInvoke
将委托和对象[]添加到调用列表并发布已注册的窗口消息。 当消息泵从队列中删除该消息时,将使用object []参数调用该委托。
3)对ResizeChild
调用进行Unbox和复制。
此时您可以看到副本数量甚至不是问题。 它被转换为引用类型(盒装)这一事实意味着我们最好将其作为引用类型开始。
不安全的代码能够 破坏托管堆 。 因此,在同一过程中运行的任何事物都会受到影响。
这包括同一进程中的所有其他库以及可能的所有其他AppDomain 。
UPDATE
这是一个例子: http : //blogs.msdn.com/b/tess/archive/2006/02/09/net-crash-managed-heap-corruption-calling-unmanaged-code.aspx
更新2
是不是很难写的不安全的代码?
没有。在.NET框架本身中有大量不安全的代码。 示例很多,但这里是System.String
:
public static unsafe string Copy(string str) { if (str == null) { throw new ArgumentNullException("str"); } int length = str.Length; string str2 = FastAllocateString(length); fixed (char* chRef = &str2.m_firstChar) { fixed (char* chRef2 = &str.m_firstChar) { wstrcpyPtrAligned(chRef, chRef2, length); } } return str2; }