AC1DF1734633C602F8F2D5: Everything
注意代码组的层次?顶层 ('All code') 是最通用的,它随后分为几个组,每个还可以再分。同时注意,和一般的想象不同,子组可以被赋予比它的上级更宽的权限集。
7.4 如何定义自己的代码组?
使用 caspol。例如,假定你信任来自 www.mydomain.com 的代码,并且希望它对你的系统拥有完全的访问权,但是希望对其它 Internet 站点保持默认的限制。要实现这些,你可以在“Zone - Internet”组中增加一个子组,就像下面那样:
caspol -ag 1.3 -site www.mydomain.com FullTrust
现在如果你运行 caspol -lg 就可以看到新的代码组被增加为 1.3.1 组:
...
1.3. Zone - Internet: Internet
1.3.1. Site - www.mydomain.com: FullTrust
...
注意数字标号 (1.3.1) 只是 caspol 编出来以便能从命令行方便地操纵代码组的。底层的运行库永远看不到它。
7.5 如何改变代码组的权限集?
使用 caspol。如果你是机器的管理员,你能在 'machine' 层次上操作?这不仅意味着你所做的改变将成为机器的默认设置,而且用户不能把权限改得更宽。如果你是一个普通用户 (不是管理员) 你仍然可以修改权限,但只能使它们变得更严格。例如,为使 intranet 代码能做它们想做的事,你可能需要这样:
caspol -cg 1.2 FullTrust
注意,因为 (在标准的系统里) 这比默认的安全策略权限更大,你应该在 machine 层次上做这些?在 user 层次上这样做不起作用。
7.6 能否创建自己的权限集?
是的。使用 caspol -ap,指定一个包含权限集中所有的权限的 XML 文件。这里 是一个指定 'Everything' 权限集的示例文件?修改它以适应你的需要,这样可以节省一些时间。修改完成后,用以下方法将它添加到可用的权限集中:
caspol -ap samplepermset.xml
然后,用以下方法将此权限集施加到一个代码组上:
caspol -cg 1.3 SamplePermSet
(默认情况下,1.3 是 'Internet' 代码组)
7.7 CAS 有问题时,如何诊断自己的程序?
caspol 有一组可能有用的选项。首先,使用 caspol -rsg,你能让 caspol 告诉你一个元件属于哪一个代码组。类似地,使用 caspol -rsp,你能询问在特定元件上施加了什么权限。
7.8 我受不了 CAS 带来的麻烦,能否关掉它?
是的,只要你是系统管理员。只要运行:
caspol -s off
8. 中间语言 (IL)
8.1 我能看到元件的中间语言吗?
是的。Microsoft 提供了一个称为 Ildasm 的工具,它可以用来查看元件的 metadata 和 IL。
8.2 能否通过反向工程从 IL 中获得源代码?
是的。相对而言,从 IL 来重新生成高级语言源代码 (例如 C#) 通常是很简单的。
8.3 如何防止别人通过反向工程获得我的代码?
目前唯一的办法是运行带有 /owner 选项的 ilasm。这样生成的元件的 IL 不能通过 ildasm 来查看。然而,意志坚定的代码破译者能够破解 ildasm 或者编写自己的 ildasm 版本,所以这种方法只能吓唬那些业余的破译者。
不幸的事,目前的 .NET 编译器没有 /owner 选项,所以要想保护你的 C# 或 VB.NET 元件,你需要像下面那样做:
csc helloworld.cs
ildasm /out=temp.il helloworld.exe
ilasm /owner temp.il
(这个建议是 Hany Ramadan 贴到 DOTNET 上的。)
看起来过一段时间能有 IL 加密工具 (无论来自 Microsoft 或第三方)。这些工具会以这样的方式来“优化” IL:使反向工程变得更困难。
当然,如果你是在编写 Web 服务,反向工程看起来就不再是一个问题,因为客户不能访问你的 IL。
8.4 我能直接用 IL 编程吗?
是的。Peter Drayton 在 DOTNET 邮件列表里贴出了这个简单的例子:
.assembly MyAssembly {}
.class MyApp {
.method static void Main() {
.entrypoint
ldstr "Hello, IL!"
call void System.Console::WriteLine(class System.Object)
ret
}
}
将其放入名为 hello.il 的文件中,然后运行 ilasm hello.il,将产生一个 exe 元件。
8.5 IL 能做到 C# 中做不到的事吗?
是的。一些简单的例子是:你能抛出不是从 SystemException 导出的异常,另外你能使用非以零起始的数组。
9. 关于 COM
9.1 COM 消亡了吗?
就像你在邮件列表中看到的那样,这个主题导致了激烈的争论。看看以下两个地方:
http://discuss.develop.com/archives/wa.exe?A2=ind0007&L=DOTNET&D=0&P=68241
http://discuss.develop.com/archives/wa.exe?A2=ind0007&L=DOTNET&P=R60761
我的理解是:COM 包含很多内容,并且对于不同的人而言它是不同的东西。但是对我来说,COM 基本上是关于一小段代码如何找到另一小段代码,以及当它们相互找到后该如何相互通讯。COM 准确地指明了这种定位和通讯该如何进行。在完全由 .NET 对象构成的“纯” .NET 世界里,小段代码依然相互寻找并相互交谈,但它们不使用 COM 来做这些。它们使用在某些地方和 COM 很相像的一种模型?例如,类型信息保存在和组件封装在一起的表单中,这和在 COM 组件中封装一个类型库十分相似。但它不是 COM。
所以,这里有什么问题吗?好吧,我确实不关心大多数 COM 消失了?我不关心寻找组件不再和注册表有关,我也不使用 IDL 来定义我的借口。但有一件东西我不希望它消失?我不希望失去基于接口的开发这种思想。照我看来,COM 最强大的力量是它坚持在接口和实现之间竖起铸铁般的隔墙。不幸的是,看来 .NET 不再那样坚持?它允许你做基于接口的开发,但它并不坚持。一些人可能会辩解说有一个选择总不会是坏事,可能他们是对的,但我不能不觉得这可能是一个退步。
9.2 DCOM 消亡了吗?
差不多是,尤其是对于 .NET 开发者。.NET 框架有一个不基于 DCOM 的新的远程模型。当然 DCOM 还会在互操作场合下使用。
9.3 MTS/COM+ 消亡了吗?
不。第一个 .NET 版本考虑的是提供对现有 COM+ 服务 (通过一个互操作层) 而不是使用 .NET 自己的服务来取代它们。很多工具和属性被用以实现尽可能平滑的过渡。.NET SDK 的 PDC 版本包括对核心服务 (JIT 活动、事务) 的支持,但不包括一些高层服务 (例如 COM+ 事件、队列化组件)。
在一段时间内看来,互操作性可以预期是无缝集成的?这意味着一些服务将成为 CLR 的一部分,并且/或者意味着一些服务将以可管理代码的形式重写并运行在 CLR 的顶层。
关于这个主题,参见 Joe Long 的贴子?Joe 是 Microsoft 的 COM+ 组的经理。从这里开始:
http://discuss.develop.com/archives/wa.exe?A2=ind0007&L=DOTNET&P=R68370
9.4 能在 .NET 中使用 COM 组件吗?
可以。可以通过 Runtime Callable Wrapper (RCW) 从 .NET 中访问 COM 组件。它通过将 COM 组件映射为与 .NET 兼容的接口来使 COM 接口可以被访问。对于 oldautomation 接口,可以自动地从一个类型库中产生。对于非 oleautomation 接口,可以开发一个定制的 RCW,以便手工地将 COM 接口的类型映射为与 .NET 兼容的类型。
对于熟悉 ATL 的读者,这里有一个简单的示例。首先,创建一个 ATL 组件以实现以下 IDL:
import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(EA013F93-487A-4403-86EC-FD9FEE5E6206),
helpstring("ICppName Interface"),
pointer_default(unique),
oleautomation
]
interface ICppName : IUnknown
{
[helpstring("method SetName")] HRESULT SetName([in] BSTR name);
[helpstring("method GetName")] HRESULT GetName([out,retval] BSTR *pName );
};
[
uuid(F5E4C61D-D93A-4295-A4B4-2453D4A4484D),
version(1.0),
helpstring("cppcomserver 1.0 Type Library")
]
library CPPCOMSERVERLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
[
uuid(600CE6D9-5ED7-4B4D-BB49-E8D5D5096F70),
helpstring("CppName Class")
]
coclass CppName
{
[default] interface ICppName;
};
};
建立了组件以后,你会得到一个 typelibrary。在 typelibrary 上运行 TLBIMP 实用程序,就像这样:
tlbimp cppcomserver.tlb
如果成功,你会得到像这样的信息:
Typelib imported successfully to CPPCOMSERVERLib.dll
现在你需要一个 .NET 客户端?我们用 C# 创建一个包含以下代码的 .cs 文件:
using System;
using CPPCOMSERVERLib;
public class MainApp
{
static public void Main()
{
CppName cppname = new CppName();
cppname.SetName( "bob" );
Console.WriteLine( "Name is " + cppname.GetName() );
}
}
注意我们使用 typelibrary 的名字作为命名空间,COM 类的名字作为类名。我们也可以选择使用 CPPCOMSERVERLib.CppName 作为类名而且不需要语句 using CPPCOMSERVERLib。
像这样编译以上 C# 代码:
csc /r:cppcomserverlib.dll csharpcomclient.cs
注意,编译被告知,引用我们刚才用 TLBIMP 从 typelibrary 产生的 DLL。
现在你应该可以运行 csharpcomclient.exe,并从控制台得到如下输出:
Name is bob
9.5 能在 COM 中使用 .NET 组件吗?
可以。可以通过一个 COM Callable Wraper (CCW) 从 COM 中访问 .NET 组件。这和 RCW 很相似 (参见上一个问题),但以相反的方向工作。同样,如果它不能由 .NET 开发工具自动产生,或不想要自动产生的行为逻辑,可以开发一个定制的 CCW。为使 COM 可以“看见” .NET 组件,.NET 组件必须在注册表里注册。
这里是一个简单的例子。创建一个名为 testcomserver.cs 的 C# 文件并输入下面的代码:
using System;
namespace AndyMc
{
public class CSharpCOMServer
{
public CSharpCOMServer() {}
public void SetName( string name ) { m_name = name; }
public string GetName() { return m_name; }
private string m_name;
}
}
然后编译 .cs 文件:
csc /target:library testcomserver.cs
你会得到一个 dll,这样将它注册:
regasm testcomserver.dll /tlb:testcomserver.tlb
现在你需要创建一个客户端程序来测试你的 .NET COM 组件。VBScript 可以?将以下内容放到一个名为 comclient.vbs 的文件中:
Dim dotNetObj
Set dotNetObj = CreateObject("AndyMc.CSharpCOMServer")
dotNetObj.SetName ("bob")
MsgBox "Name is " & dotNetObj.GetName()
运行此脚本:
wscript comclient.vbs
嘿!你得到一个显示文本“Name is bob”的消息框。
(注意,编写此程序时,看起来可以通过几种路径将 .NET 类作为 COM 组件访问?为了避免问题,在 testcomserver.dll 相同的目录下运行 comclient.vbs。
一种替代的方法是使用 Jason Whittington 和 Don Box 开发的 dm.net moniker。到这里 http://staff.develop.com/jasonw/clr/readme.htm 查看。
9.6 在 .NET 的世界中 ATL 是多余的吗?
是的。如果你在编写 .NET 框架内的应用程序。当然许多开发者希望继续使用 ATL 来编写 .NET 框架以外的 C++ COM 组件,但当你在 .NET 框架内时你差不多总是希望使用 C#。在 .NET 世界里,原始的 C++ (以及基于它的 ATL) 并没有太多的地位?它太直接了,并且提供了太多的适应性,以至于运行库不能管理它。
10. 杂项
10.1 .NET 的远程计算如何工作?
.NET 的远程计算涉及通过通道发送消息。两种标准的通道是 HTTP 和 TCP。仅仅在局域网上才倾向于使用 TCP?HTTP 能在局域网和广域网 (internet) 上使用。
现在提供了对多种消息串行化格式的支持,例如 SOAP (基于 XML) 和二进制格式。默认情况下,HTTP 通道使用 SOAP (通过 .NET 运行库的 Serialization SOAP Formatter),而 TCP 通道使用二进制格式 (通过 .NET 运行库的 Serialization Binary Formatter)。但每个通道可以使用任一串行化格式。
这里是远程访问的一些方式:
SingleCall。每个来自客户端的请求由一个新对象服务。当请求完成后对象被丢弃。可以在 ASP+ 环境中使用 ASP+ 国家服务来保存应用程序或会话的国家,从而使这种模型 (无国家之分的) 变成有国家支持的。
Singleton。所有来在客户端的请求由单一的服务器对象处理。
Client-activated object。这是老的有国家支持的 (D)COM 模型,这里客户端受到一个远端对象的引用并保留此引用 (以保持远端对象的生存),直到对它的访问完成。
对象的分布式垃圾收集由称为“基于租用的生命周期”管理。每个对象拥有一个租用时间,这个时间到达时,从 .NET 运行库的远程子结构断开对象。对象有默认的更新时间?从客户端发起的成功调用会更新租用时间。客户端也可以显示地更新租用时间。
如果你对使用 XML-RPC 来代替 SOAP,可以看看 Charles Cook 在 http://www.cookcomputing.com/xmlrpc/xmlrpc.shtml 的 XML-RPC.Net 站点。
10.2 如何在 .NET 程序中获得 Win32 API?
使用 P/Invoke。它使用了和 COM 互操作性相似的技术,但被用来访问静态 DLL 入口点而不是 COM 对象。以下是一个调用 Win32 MessageBox 函数的 C# 程序示例:
using System;
using System.Runtime.InteropServices;
class MainApp
{
[dllimport("user32.dll", EntryPoint="MessageBox", SetLastError=true, CharSet=CharSet.Auto)]
public static extern int MessageBox(int hWnd, String strMessage, String strCaption, uint uiType);
public static void Main()
{
MessageBox( 0, "Hello, this is PInvoke in operation!", ".NET", 0 );
}
}
11. 类库
11.1 文件 I/O
11.1.1 如何读文本文件?
首先,使用 System.IO.FileStream 对象打开文件:
FileStream fs = new FileStream( @"c: est.txt", F