下载程序代码:http://www.cnblogs.com/Files/zhenyulu/RemotingEvent.rarg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
一、问题提出g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
.net中的Remoting技术为我们提供了一种分布式系统开发的捷径。远程对象必须继承自MarshalByRefObject,通过设置运行通道,就可以实现对远程对象的调用。我们可以选择在客户端实例化远程对象也可以选择仅仅调用远程实例化好的对象,可以选择服务器激活对象,也可以选择客户激活对象。g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
使用 .NET Remoting 时,必须在将应用程序部署到不同的程序集中时要多加小心。主要目标是要确保服务器上的代码不会传送到客户端。因此通常需要从远程对象中取一个接口(请参考微软的《使用 Microsoft .NET 的企业解决方案模式》)。另外,尽可能让远程对象与远程系统分离开,做到松耦合,这样也可以确保远程对象不至于做的太大,影响系统效率。g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
现在我们面临一个问题:就是我们的远程系统可能很庞大(比如说是一个Windows窗体)。而提供给客户调用的远程对象可能需要很小(比如说只有一个方法),如何把远程系统和远程的Remoting对象分离开,以确保远程系统的复杂度跟远程对象无关呢?显然事件(Event)是实现这种松耦合的非常好的办法。现在,我们的思路是:借助Remoting技术传递信息,再根据信息触发不同的事件,远程系统订阅这些事件并进行响应。于是远程系统就和远程对象剥离开了。g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
在系统实现中,SysCommon包封装了远程调用接口,确保客户端不知道服务器端对象的实现代码;Receiver对象继承自MarshalByRefObject,由客户端进行调用。同时利用delegate将调用转发给dispatcher对象;Dispatcher对象负责解释消息并根据消息内容触发不同的事件;Server对象封装了Receiver与Dispatcher,用来侦听网络调用并触发Dispatcher事件;ServerWin属于远程系统,订阅Dispatcher触发的事件并作出响应。g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
二、系统特点g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
在这里强调一下,这篇文章所讨论的Event与《C#高级编程》中的“远程调用和事件”完全是两回事。《C#高级编程》中所谓的Event是触发客户端的事件,而不是服务器端的事件,其原理是基于本地实例化远程“事件接收器”对象,而且《C#高级编程》中讲到的“远程调用和事件”也无法解决服务系统与服务对象相剥离的问题。如果对触发客户端事件感兴趣的话,可以参考Simon Robinson等著,康博译的《C#高级编程》一书第23章:带有.NET Remoting的分布式应用程序。g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
我这里主要讨论的是用Event将服务系统与服务对象实现相互分离。g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
三、系统实现g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
先看看系统的UML图(点击小图看大图):g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
其中,SysCommon包封装了远程调用接口,确保客户端不知道服务器端对象的实现代码,包括消息CallingMessage的定义以及调用接口ICommon的定义,同时将调用规范(delegate)RemotingEventHandler也放在这里了。代码如下:g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using System;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
namespace RemotingEvent.SysCommong©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
  [Serializable]g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
 
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
struct CallingMessageg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
 
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
string MsgType;      //消息类型g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
string MessageInfo;  //消息主体g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
  }g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
 
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
interface ICommong©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
 
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
void ReceiveMessage(CallingMessage message);g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
  }
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
 
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
delegateg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
void RemotingEventHandler (CallingMessage message);g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
}
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
Receiver对象继承自MarshalByRefObject,由客户端进行调用,实现了ICommon接口。Receiver对象复写了InitializeLifetimeService()方法,确保当创建 Singleton 时, 第一个实例永远不会过期。Receiver包含了一个静态的符合RemotingEventHandler的方法InvokeMessageDispatcher(这是实现松耦合的关键)。这个静态delegate将由Dispatcher对象的OnDispatchHandler方法初始化(代码在Server对象中)。代码如下:g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using System;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using System.Runtime.Remoting.Messaging;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using RemotingEvent.SysCommon;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
namespace RemotingEvent.MessageReceiverg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
 
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
class Receiver : MarshalByRefObject, ICommong©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
 
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
static RemotingEventHandler InvokeMessageDispatcher;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
      [OneWay]g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
void ReceiveMessage(CallingMessage message)g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
       
if(InvokeMessageDispatcher !=g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
null)g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
       
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
            InvokeMessageDispatcher(message);g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
        }
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
      }
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
//=======================================================g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
// 用来确保当创建 Singleton 时, 第一个实例永远不会过期g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
//=======================================================g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
overrideg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
object InitializeLifetimeService()g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
       
returng©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
null;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
      }
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
  }
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
}
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
Dispatcher对象负责解释消息并根据消息内容触发不同的事件,所有事件都是在这个对象中声明的。代码如下:g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using System;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using RemotingEvent.SysCommon;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using RemotingEvent.MessageReceiver;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
namespace RemotingEvent.MessageDispatcherg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
 
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
class Dispatcherg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
 
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
event RemotingEventHandler BeginEvent;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
event RemotingEventHandler EndEvent;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
//=========================================================g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
// 消息处理及分发主函数g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
//=========================================================g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
void OnDispatchHandler(CallingMessage message) g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
       
switch(message.MsgType)g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
       
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
           
caseg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
"Begin":g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
             
if (BeginEvent !=g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
null)g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
                  BeginEvent(message);g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
             
break;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
           
caseg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
"End":g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
             
if (EndEvent !=g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
null)g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
                  EndEvent(message);g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
             
break;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
        }
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
      }
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
  }
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
}
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
Server对象主要负责将Receiver与Dispatcher整合起来构成一个有机整体。其StartServer方法用来启动服务并初始化所有对象间的关系(这也是实现松耦合的关键)。Server对象的构造函数接受一个dispatcher对象,这个dispatcher是在ServerWin服务系统中生成的,其目的为了能让ServerWin服务系统能订阅到dispatcher对象发布的事件。代码如下:g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using System;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using System.Collections;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using System.Runtime.Remoting;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using System.Runtime.Remoting.Channels;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using System.Runtime.Remoting.Channels.Http;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using System.Runtime.Serialization.Formatters;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using RemotingEvent.SysCommon;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using RemotingEvent.MessageDispatcher;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
using RemotingEvent.MessageReceiver;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
namespace RemotingEvent.LocalServerg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
 
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
class Serverg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
 
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
private Dispatcher dispatcher;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
public Server(Dispatcher dispatcher)g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
       
this.dispatcher = dispatcher;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
      }
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
publicg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
void StartServer(int port)g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
     
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
       
tryg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
       
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
            BinaryServerFormatterSinkProvider serverProvider
=g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
new BinaryServerFormatterSinkProvider();g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
            BinaryClientFormatterSinkProvider clientProvider
=g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
new BinaryClientFormatterSinkProvider();g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
            serverProvider.TypeFilterLevel
= TypeFilterLevel.Full;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
            IDictionary props
=g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
new Hashtable();g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
            props[
"port"] = port;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
            props[
"timeout"] =g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
2000;g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
            HttpChannel channel
=g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
new HttpChannel(props, clientProvider, serverProvider);g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
            ChannelServices.RegisterChannel(channel);g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
            RemotingConfiguration.RegisterWellKnownServiceType(g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
             
typeof(Receiver),g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
             
"Receiver.soap",g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
              WellKnownObjectMode.Singleton);g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
            Receiver.InvokeMessageDispatcher
+=g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
new RemotingEventHandler(dispatcher.OnDispatchHandler);g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
        }
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
       
catchg©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
       
{g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
            Console.WriteLine(
"Start Server Error!");g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
        }
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
      }
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
  }
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
}
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
ServerWin与ClientWin是服务系统与客户端,具体代码自己下载后看吧。贴个运行结果图上来:g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
四、小经验g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ
本篇文章写作时遇到一个问题,就是UML图制作问题。最开始使用Visio 2003,不过无法在其中画出Event,后来不得不使用Together 6,但是屏幕显示的东西不太漂亮,于是我便将其打印到PDF文件中,再用Acrobat 6的快照将其中的UML图放到剪贴板上,剩下的事情就是PhotoShop的事了。存盘时存储为Web格式,选择gif 16色(颜色再少就失真了)存储,便得到了现在大家看到的图片。呵呵,希望这点经验对大家有帮助。g©™½¹”¯Š˜www.netcsharp.cn{äœØ±ðŽæŸ