分享
 
 
 

.NET 技术FAQ(二)

王朝c#·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

7. 代码访问安全性

7.1 什么是代码访问安全性 (CAS)?

CAS 是 .NET 安全性模型的一部分,它确定一段代码是否允许被运行,以及当它运行是可以使用什么资源。例如,CAS 可以防止一个 .NET 的 Web applet 将你的硬盘格式化。

7.2 CAS 如何起作用?

CAS 安全策略设计两个关键概念—代码组和权限。每个 .NET 元件是特定 代码组的成员,并且每个代码组被授予由有名权限集所指定的权限。

例如,使用默认的安全策略时,一个从 Web 站点下载的控件属于“Zone - Internet”代码组,它保持由有名权限集“Internet”所定义的权限。(自然,有名权限集“Internet”表示一组受到严格限制的权限。)

7.3 谁定义 CAS 代码组?

Microsoft 定义了一些默认代码组,但你可以改变这些甚至创建你自己的代码组。要想看到你的系统中定义的代码组,可以从命令横行运行“caspol -lg”命令。再我的系统里它看起来像这些:

Level = Machine

Code Groups:

1. All code: Nothing

1.1. Zone - MyComputer: FullTrust

1.1.1. Honor SkipVerification requests: SkipVerification

1.2. Zone - Intranet: LocalIntranet

1.3. Zone - Internet: Internet

1.4. Zone - Untrusted: Nothing

1.5. Zone - Trusted: Internet

1.6. StrongName - 0024000004800000940000000602000000240000525341310004000003

000000CFCB3291AA715FE99D40D49040336F9056D7886FED46775BC7BB5430BA4444FEF8348EBD06

F962F39776AE4DC3B7B04A7FE6F49F25F740423EBF2C0B89698D8D08AC48D69CED0FC8F83B465E08

07AC11EC1DCC7D054E807A43336DDE408A5393A48556123272CEEEE72F1660B71927D38561AABF5C

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:\test.txt", FileMode.Open, FileAccess.Read );

FileStream 继承于 Stream,所以你可以用一个 StreamReader 对象把 FileStream 对象包装起来。这样为一行一行地进行流处理提供了一个良好的界面:

StreamReader sr = new StreamReader( fs );

string curLine;

while( (curLine = sr.ReadLine()) != null )

Console.WriteLine( curLine );

最后关闭 StreamReader 对象:

sr.Close();

注意这样将自动地在底层 Stream 对象上调用 Close (),所以不必显示地执行 fs.Close()。

11.1.2 如何写文本文件?

和读文件的例子相似,只是把 StreamReader 换成 StreamWriter。

11.1.3 如何读写二进制文件?

和文本文件类似,只是要用 BinaryReader/Writer 对象而不是 StreamReader/Writer 来包装 FileStream 对象。

11.1.4 如何删除文件?

在 System.IO.File 对象上使用静态方法 Delete ():

File.Delete( @"c:\test.txt" );

11.2 文本处理

11.2.1 是否支持正规表达式?

是的。使用 System.Text.RegularExpressions.Regex 类。例如,以下代码更新 HTML 文件的标题:

FileStream fs = new FileStream( "test.htm", FileMode.Open, FileAccess.Read );

StreamReader sr = new StreamReader( fs );

Regex r = new Regex( "<TITLE>(.*)</TITLE>" );

string s;

while( (s = sr.ReadLine()) != null )

{

if( r.IsMatch( s ) )

s = r.Replace( s, "<TITLE>New and improved ${1}</TITLE>" );

Console.WriteLine( s );

}

11.3 Internet

11.3.1 如何下载网页?

首先使用 System.Net.WebRequestFactory 类来获得一个 WebRequest 对象:

WebRequest request = WebRequestFactory.Create( "http://localhost" );

然后请求应答:

WebResponse response = request.GetResponse();

GetResponse 方法被阻塞直到下载完成。然后你能像下面那样访问应答流:

Stream s = response.GetResponseStream();

// Output the downloaded stream to the console

StreamReader sr = new StreamReader( s );

string line;

while( (line = sr.ReadLine()) != null )

Console.WriteLine( line );

注意 WebRequest 和 WebReponse 对象分别向下兼容 HttpWebRequest 和 HttpWebReponse 对象,它们被用来访问和 http 相关的功能。

11.3.2 如何使用代理服务器 (proxy)?

两种—这样做以便影响所有 Web 请求:

System.Net.GlobalProxySelection.Select = new DefaultControlObject( "proxyname", 80 );

另外一种,要想对特定的 Web 请求设置代理服务,这样做:

ProxyData proxyData = new ProxyData();

proxyData.HostName = "proxyname";

proxyData.Port = 80;

proxyData.OverrideSelectProxy = true;

HttpWebRequest request = (HttpWebRequest)WebRequestFactory.Create( "http://localhost" );

request.Proxy = proxyData;

11.4 XML

11.4.1 是否支持 DOM?

是的。看看以下示例 XML文档:

<PEOPLE>

<PERSON>Fred</PERSON>

<PERSON>Bill</PERSON>

</PEOPLE>

可以这样处理此文档:

XmlDocument doc = new XmlDocument();

doc.Load( "test.xml" );

XmlNode root = doc.DocumentElement;

foreach( XmlNode personElement in root.ChildNodes )

Console.WriteLine( personElement.FirstChild.Value.ToString() );

输出为:

Fred

Bill

11.4.2 是否支持 SAX?

不。作为替换,提供了一个新的 XmlReader/XmlWriter API。像 SAX 一样,它是基于流的,但它使用“pull”模型而不是 SAX 的“push”模型。这是一个例子:

XmlTextReader reader = new XmlTextReader( "test.xml" );

while( reader.Read() )

{

if( reader.NodeType == XmlNodeType.Element && reader.Name == "PERSON" )

{

reader.Read(); // Skip to the child text

Console.WriteLine( reader.Value );

}

}

11.4.3 是否支持 XPath?

是的,通过 XmlNavigator 类 (DocumentNavigator 是从 XmlNavigator 导出的):

XmlDocument doc = new XmlDocument();

doc.Load( "test.xml" );

DocumentNavigator nav = new DocumentNavigator(doc);

nav.MoveToDocument();

nav.Select( "descendant::PEOPLE/PERSON" );

while( nav.MoveToNextSelected() )

{

nav.MoveToFirstChild();

Console.WriteLine( "{0}", nav.Value );

}

11.5 线程

11.5.1 是否支持多线程?

是的,对多线程有广泛的支持。系统能产生新线程,并提供应用程序可以使用的线程池。

11.5.2 如何产生一个线程?

创建 System.Threading.Thread 对象的一个实例,把将要在新线程中执行的 ThreadStart 示例传递给它。例如:

class MyThread

{

public MyThread( string initData )

{

m_data = initData;

m_thread = new Thread( new ThreadStart(ThreadMain) );

m_thread.Start();

}

// ThreadMain() is executed on the new thread.

private void ThreadMain()

{

Console.WriteLine( m_data );

}

public void WaitUntilFinished()

{

m_thread.Join();

}

private Thread m_thread;

private string m_data;

}

这里创建 MyThread 的一个实例就足以产生线程并执行 MyThread.ThreadMain () 方法:

MyThread t = new MyThread( "Hello, world." );

t.WaitUntilFinished();

11.5.3 如何停止一个线程?

有好几个办法。首先,你能使用自己的通讯机制告诉 ThreadStart 方法结束。另外 Thread 类有内置的支持来命令线程停止。基本的两个方法是 Thread.Interrupt () 和 Thread.Abort ()。前者导致抛出一个 ThreadInterruptedException 并随后进入 WaitJoinSleep 状态。换句话说,Thread.Interrupt 是一种礼貌的方式,它请求线程在不再进行任何有用的工作时自行停止的。与此相对应,Thread.Abort () 抛出一个 ThreadAbortException 而不管线程正在做什么。而且,ThreadAbortException 不能像通常的异常那样被捕获 (即使最终将执行 ThreadStart 的终止方法)。Thread.Abort () 是一般情况下不需要的非常手段。

11.5.4 怎样使用线程池?

通过向 ThreadPool.QueueUserWorkItem () 方法传递 WaitCallback 的一个实例:

class CApp

{

static void Main()

{

string s = "Hello, World";

ThreadPool.QueueUserWorkItem( new WaitCallback( DoWork ), s );

Thread.Sleep( 1000 ); // Give time for work item to be executed

}

// DoWork is executed on a thread from the thread pool.

static void DoWork( object state )

{

Console.WriteLine( state );

}

}

11.5.5 怎样知道我的线程池工作项目是在何时完成的?

没有方法询问线程池这类信息。你必须在 WaitCallback 方法中放置代码来发出信号以表明它已经完成。这里事件也很有用处。

11.5.6 怎样防止对数据的并发访问?

每个对象有一个与之相联的并发锁 (受批评的部分)。System.Threading.Monitor.Enter/Exit 方法用来获得和释放锁。例如,下面类的实例只允许一个线程同时进入方法 f ():

class C

{

public void f()

{

try

{

Monitor.Enter(this);

...

}

finally

{

Monitor.Exit(this);

}

}

}

C# 有一个关键字‘lock’提供了以上代码的简单形式:

class C

{

public void f()

{

lock(this)

{

...

}

}

}

注意,调用 Monitor.Enter (myObject) 并不意味着对 myObject 的所有访问都被串行化了。它意味着请求同 myObject 相联的同步锁,并且在调用 Monitor.Exit(o) 之前,没有任何其它线程可以请求该锁。换句话说,下面的类和以上给出的类在功能上是等同的:

class C

{

public void f()

{

lock( m_object )

{

...

}

}

private m_object = new object();

}

11.6 跟踪

11.6.1 有内置的跟踪/日志支持吗?

是的,在 System.Diagnostics 命名空间中。有两个处理跟踪的主要的类—Debug 和 Trace。它们以相似的方式工作—不同之处是 Debug 类中的跟踪只能在用 DEBUG 标志生成的代码中工作,而 Trace 类中的跟踪只能在指明了 TRACE 标记生成的代码中工作。典型地,这意味着你应该在你希望能在 debug 和 release 版本中都能跟踪时使用 System.Diagnostics.Trace.WriteLine,而在你希望只能在 debug 版本中能跟踪时使用 System.Diagnostics.Debug.WriteLine。

11.6.2 能否将跟踪输出重定向到一个文件?

是的。Debug 类和 Trace 类都有一个 Listeners 属性,它们分别收集你用 Debug.WriteLine 或 Trace.WriteLine 产生的输出。默认情况下 Listeners 只有一个收集槽,它是 DefaultTraceListener 类的一个实例。它将输出发送到 Win32 的 OutputDebugString () 函数和 System.Diagnostics.Debugger.Log () 方法。调试时这很有用,但如果你试图从客户站点跟踪一个问题,将输出重定向到一个文件中就更为恰当。幸运的是,为此目的提供了 TextWriterTraceListener 类。

这里是 TextWriterTraceListener 如何将 Trace 输出重定向到一个文件:

Trace.Listeners.Clear();

FileStream fs = new FileStream( @"c:\log.txt", FileMode.Create, FileAccess.Write );

Trace.Listeners.Add( new TextWriterTraceListener( fs ) );

Trace.WriteLine( @"This will be writen to c:\log.txt!" );

注意使用 Trace.Listeners.Clear () 去掉了默认的 listener。如果不这样做,输出将在文件和 OutputDebugString () 中同时产生。一般情况下你不希望如此,因为 OutputDebugString () 导致很大的性能开销。

11.6.3 能否定制跟踪的输出?

是的。你能编写你自己的 TraceListener 导出类,并把所有的输出重定向到它上面。这里有一个简单的例子,它从 TextWriterTraceListener 导出 (并随后内建了对写文件的支持) 并在每个输出行上添加时间信息和线程 ID:

class MyListener : TextWriterTraceListener

{

public MyListener( Stream s ) : base(s)

{

}

public override void WriteLine( string s )

{

Writer.WriteLine( "{0:D8} [{1:D4}] {2}",

Environment.TickCount - m_startTickCount,

AppDomain.GetCurrentThreadId(),

s );

}

protected int m_startTickCount = Environment.TickCount;

}

(注意这个实现并不完整—例如没有覆盖 TraceListener.Write 方法。)

这种方法的美妙之处在于,向 Trace.Listener 添加 MyListener 之后,所有对 Trace.WriteLine () 的调用都转向了 MyListener,包括从对 MyListener 一无所知的被引用元件发出的调用。

12. 资源

12.1 从哪里可以获得关于 .NET 的详情?

Microsoft .NET 主页位于 http://www.microsoft.com/net/。Microsoft 同时将它发布在 GOTDOTNET。

Microsoft 还发布了 .NET Framework FAQ,和本文很相似。可以在那里找到这里许多问题更“权威”的解答。

Robert Scoble 编辑了一个很容易理解的在线列表 http://www.devx.com/dotnet/resources/,这里还有一个 http://www.singularidad.com.ar/dotnet.asp

http://www.devx.com/free/press/2000/vs-qalist.asp Robert 还有一个 .NET“著名问题与解答”主页。

Richard Grimes 和 Richard Anderson 有一个叫作 Managed World.COM.的站点。

http://www.ibuyspy.com/ 是一个以展示 .NET 平台为目的创建的示例站点。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有