据我了解,将方法标记为不安全将禁用对该代码的某些 CLR 检查,但这对系统的其余安全部分有任何影响吗?除了 DLL/EXE 无法运行之外不受信任的环境。
特别是,
- 它们是否有任何安全检查,因为它被标记为不安全而无法对整个 dll 起作用?
- 如果 DLL 被标记为不安全,但标记为不安全的方法是
实际上没有被调用,这与 DLL 被标记为相同吗
安全的?
- 将不安全代码保留在运行时是否有任何好处?
单独的DLL?
我在 64 位 Windows 上重绘嵌套控件时遇到问题,详细信息 此处 以及解决方案(似乎有效的解决方案)涉及不安全代码,我想了解添加此代码对我的项目的影响。
As I understand it, marking an method as unsafe will disable some of the CLR checks on that code, but does this have any effect on the rest of the system which is safe, other than the fact that the DLL/EXE can not run in a untrusted environment.
In particular,
- Are they are any safety checks that will not work on the complete dll because it is marked as unsafe?
- If a DLL is marked as unsafe, but the methods marked as unsafe are
not actually called, is this the same as if the DLL is marked as
safe?
- Are they any run-time benefits on keeping the unsafe code in a
separate DLL?
I have the problem with redrawing nested controls on 64-bit windows as detailed here and the one the solutions (the one that appears to work) involves unsafe code and I would like to understand the effect that adding this code has to my project.
发布评论
评论(2)
不安全的代码能够破坏托管堆。因此,同一进程中运行的任何内容都可能受到影响。
这包括同一进程中的所有其他库以及可能的所有其他AppDomains。
更新
这是一个例子:
http://blogs.msdn.com/b/tess/archive/2006/02/09/net-crash-management-heap-corruption-calling-unmanagement-code.aspx
更新2
不。.NET 框架本身存在大量不安全代码。示例有很多,但以下是
System.String
中的一个:An unsafe code is capable of corrupting the managed heap. As such, anything that runs in the same process can be affected.
This includes all other libraries and potentially all other AppDomains in the same process.
UPDATE
Here is an example:
http://blogs.msdn.com/b/tess/archive/2006/02/09/net-crash-managed-heap-corruption-calling-unmanaged-code.aspx
UPDATE 2
No. There are tons of unsafe code in the .NET framework itself. Examples many, but here is one in the
System.String
:您的问题的答案是:
unsafe
关键字并不意味着“不安全”,它意味着“潜在不安全”。编译器和框架无法确保其安全。您需要确保代码不能对内存执行不安全的读取或写入。我强烈鼓励您遵循您链接的文章中给出的建议:
1) 重新设计应用程序以减少容器并减少嵌套级别的数量。
如果您使用容器的唯一目的是控制排列,请编写自己的容器,可以在一个级别上完成所有排列。
已更新
您可以修改该文章中的代码,使其不使用指针(即不需要 unsafe 关键字)。请记住,这现在需要编组,这意味着额外的复制。这可能是一件好事,因为原始代码将 WINDOWPOS 指针从操作系统传递到 BeginInvoke,而 BeginInvoke 不会在操作系统生成指针的同一调度事件期间执行。换句话说,该代码已经很臭了。
注意:WINDOWPOS 从值类型更改为引用类型是有意为之。使用引用类型可将副本数量减少到只有一个(初始编组)(**)。
再次更新
我刚刚注意到代码最初公开了 p/invoke 声明。永远不要在类之外暴露 p/invoke(*)。如果您的目的是公开所提供的功能,请编写调用私有 p/invoke 声明的托管方法;在这种情况下不正确,p/invoke 严格是内部的。
(*) 好的,有一个例外。您正在创建
NativeMethods
、UnsafeNativeMethods
等。这是 FxCop 执行 p/invoke 的推荐方法。更新
(**) 我被要求(在其他地方)准确描述为什么在这里使用引用类型更好,所以我在这里添加了该信息。我被问到的问题是,“这不会增加内存压力吗?”
如果 WINDOWPOS 是值类型,则事件顺序如下:
1) 从非托管内存复制到托管内存
2) 第二次复制?
等待!
BeginInvoke
的签名是(Delegate, params object[])
。这意味着 wpos 将被封杀。所以是的,第二个副本发生在这里:装箱操作。BeginInvoke
将把委托和对象[]添加到调用列表中并发布注册的窗口消息。当消息泵从队列中删除该消息时,将使用 object[] 参数调用委托。3) 拆箱并复制以供
ResizeChild
调用。此时您会发现副本数量根本不是问题。它被转换为引用类型(盒装)的事实意味着我们最好一开始就将其设为引用类型。
The answer to your question is: The
unsafe
keyword does not mean "unsafe", it means "potentially unsafe". The compiler and framework cannot work to make certain that it's safe. It is up to you to make certain that the code cannot perform unsafe reads or writes to memory.I would strongly encourage you to follow this advice given in the article you linked:
1) Redesign the application to have less containers and reduce the number of nesting levels.
If you're using containers for the sole purpose of control arrangement, write your own container that can do all the arrangement with one level.
Updated
You can modify the code in that article so that it doesn't use pointers (i.e. doesn't require the unsafe keyword). Keep in mind that this will now require marshalling which means extra copying. This is probably a good thing because the original code is passing a WINDOWPOS pointer from the OS to BeginInvoke which does not execute during the same dispatch event that the OS generated the pointer in. In other words, that code was smelly already.
Note: The change in WINDOWPOS from value type to reference type is intentional. Using a reference type reduces the number of copies to just one (the initial marshal)(**).
Updated Again
I just noticed that the code originally made the p/invoke declarations public. Never, ever expose p/invoke outside of a class(*). Write managed methods that invoke private p/invoke declarations if your intent is to expose the capabilities provided; which in this case is not true, the p/invoke is strictly internal.
(*) Ok, one exception. You're creating a
NativeMethods
,UnsafeNativeMethods
, etc. Which is the recommended way to do p/invoke by FxCop.Updated
(**) I was asked (elsewhere) to describe precicely why using a reference type here is better, so I've added that info here. The question I was asked was, "Doesn't this add memory pressure?"
If
WINDOWPOS
was a value type, this would be the sequence of events:1) Copy from unmanaged to managed memory
2) Second copy?
Wait! The signature of
BeginInvoke
is(Delegate, params object[])
. That means wpos is going to get boxed. So yes, a second copy occurs here: The boxing operation.BeginInvoke
will add the delegate and object[] to an invocation list and post a registered window message. When that message is removed from the queue by the message pump, the delegate will be called with the object[] parameters.3) Unbox and copy for
ResizeChild
call.At this point you can see that the number of copies isn't even the issue. The fact that it gets converted to a reference type (boxed) means that we are better off making it a reference type to begin with.