Discuz!NT 邮件插件机制分析

        大约还是去年12月份,当时项目中遇到了一个很棘手的问题,就是管理员(或站长)在后台设置
了邮箱信息之后,使用注册邮件发送激活验证码时,总有用户反映不能收到激活信息的邮件。
        虽然不能收到邮件的情况有很多,甚至我已通过这个邮件发送程序测试过国内大多数知名网站的
邮箱(如126,sina ,sohu ,gmail等),但还是有站长或用户隔三差五反映这个问题。甚至到今天我
偶尔还会得到技术支持部门有关这方面问题的报怨。因此,今天这篇文章虽然说到了一个有关这个问
题的解决方案(但不完善),但还是希望园子里以前处理过这方面问题或有成功经验的朋友指点一二。

         好了,不费话了,开始今天的话题。

         相信下载到了我们发布的DLL并已Reflector相关程序结构的朋友,如果您留心看一下解决方案中的
Discuz.PlugIn项目并在VS下打开就会看到ISmtpMail.cs,SmtpEmailAttribute.cs,SmtpMail.cs,以
及SysMailMessage.cs这几个CS文件。现在简单说明一下:
 
         ISmtpMail.cs,定义了邮件收发类代码要实现的接口,主要的属性如下:

 

 1public interface ISmtpMail
 2{
 3
 4        int MailDomainPort set;}
 5
 6        string From set;get;}
 7
 8        string FromName set;get;}
 9
10        bool Html set;get;}
11
12        string Subject set;get;}
13
14        string Body set;get;}
15
16        string MailDomain set;}
17
18        bool Send();
19
20
21

 
     而SmtpMail,SysMailMessage这两个文件就是实现了这个接口的具体邮件发送类。
     因为今天只讲插件机制分析,所以就不在这里说这两个类中具体的实现方式了。下面将要说的是
这两个类中使用的"用户定制属性",这里以SysMailMessage类为例:

 

1     [SmtpEmail(".net邮件发送程序", Version = "1.0", Author = "Discuz!NT "
2  DllFileName = "Discuz.PlugIn.dll")]
3     public class SysMailMessage : ISmtpMail
4     {
5         private string _subject;
6         private string _body;
7         private string _from;

 

    大家看到了在这个类上的SmtpEmail属性了吧,它就是下面要说明的"用户定制属性"的绑定应用。
而SmtpEmail请看如下代码(相关说明见注释):


 

 1定制发送邮件的插件属性值
59

 

         如果您以前使用过采取属性绑定方式来标识和加载程序(或插件)的话,那么这时
您就会很清楚一会要使用的一些调用方法了。但我相信还会有一些朋友对这方面不是很熟
悉,所以我只能继续“贫”下去,希望您能耐着性子所我讲完:)

         有了这几个文件之后,如何调用和设置要使用的是SysMailMessage还是SmtpMail中的
邮件发送功能呢,其实这个选择的问题我们留给了用户,因为插件是对那些使用它们的人
来说才是有意义的,而使用什么插件以及使用的方式都是用户喜好。因此我们在后台提供
了如下的界面来让用户进行相关的设置(图如下):

         如何加载上图中下拉列框中的邮件发送邮件的数据呢,这里用的是对DLL进行反射。
找出相关的用上面“用户定制属性”进行标识的类代码.相关的代码如下(位于Discuz.Web
项目下的admin\global\global_emailconfig.cs文件中)

 1public void GetSmtpEmailPlugIn()
 2{
 3    
 4    DirectoryInfo dirinfo = new DirectoryInfo(HttpRuntime.BinDirectory);
 5    foreach (FileSystemInfo file in dirinfo.GetFileSystemInfos())
 6    {
 7        if (file != null)
 8        {
 9            if (file.Extension.ToLower().Equals(".dll"))
10            {
11                Assembly a = Assembly.LoadFrom(HttpRuntime.BinDirectory + file);
12                foreach (Module m in a.GetModules())
13                {    
14                     //采用过滤器进行类名过滤
15                     foreach (Type t in m.FindTypes(Module.FilterTypeName, "*")) 
16                     {
17                          foreach (object arr in t.GetCustomAttributes(typeof(SmtpEmailAttribute), true))
18                          {
19                               SmtpEmailAttribute sea = (SmtpEmailAttribute)arr;
20
21                               smtpemail.Items.Add(new ListItem(sea.PlugInName, t.FullName + "," + 
22    (sea.DllFileName != "" ? sea.DllFileName : file.ToString().Replace(".dll"""))));
23                          }

24                     }

25                }

26            }

27        }

28    }

29    
30
31

         实现的代码比较简单,这面就不多说什么了。

         上面只是解决了显示插件列表的问题,而如果将用户选取的插件信息进行保存以备使用的
的代码请看CS文件中的这部分代码:

 1private void SaveEmailInfo_Click(object sender, EventArgs e)
 2{
 3    
 4    try
 5    {
 6         __emailinfo.PluginNameSpace = smtpemail.SelectedValue.Split(',')[0];
 7         __emailinfo.DllFileName = smtpemail.SelectedValue.Split(',')[1];
 8    }

 9    catch
10    {
11       ;
12    }

13
14    EmailConfigs.SaveConfig(__emailinfo); //序列化保存相关信息
15    Emails.ReSetISmtpMail();
16}

17
18

         这样就通过了“序列化”的方式保存了用户定制的信息,这个信息有邮件程序的名空间
和所在的DLL信息。这些信息最后都放在了config/email.config中。

        目前设置方面已经万事俱备,就看前台如果调用了。为了方便起见,我们定制了一个类
用于发送不同邮件内容信息,它位于(Discuz.Forum项目下的Emails.cs文件),其中的静态
构造函数声明如下:


 

 1static  Emails()
 2{
 3
 4   try
 5   {
 6 //读取相应的DLL信息
 7 Assembly asm = Assembly.LoadFrom(HttpRuntime.BinDirectory+emailinfo.DllFileName+".dll");
 8 ESM  = (ISmtpMail)  Activator.CreateInstance(asm.GetType(emailinfo.PluginNameSpace));
 9   }

10   catch
11   {
12        
13 catch
14 {
15    if(emailinfo!=null)
16    {
17  //如果没有有效的设置则使用系统指定的邮件发送类代码
18  if(emailinfo.PluginNameSpace!=null && emailinfo.PluginNameSpace.IndexOf("SmtpMail")>0)
19  {
20           ESM  = new SmtpMail();
21  }

22  else
23  {
24     ESM  = new SysMailMessage();
25  }

26  
27           }

28
29
30

         其中了ESM就是接口实例protected static ISmtpMail ESM;
         这样就通过加载指定的DLL得到了要使用的类实例,而如下使用它大家只要看一上这个CS文件
的其余代码就会一清二楚了:)

         可能有些朋友会问这样一个问题, 为什么要用DLL而不是直接用下面的方法来获取实例
         Activator.CreateInstance('Discuz.PlugIn.SysMailMessage,Discuz.PlugIn');
         其实原因很简单,我当时考虑的是如果DLL文件不是位于BIN目录下的话,那么这种方式将会
出现返回为空的情况。因为用户完全可能自己创建一个插件目录来自己进行上传和管理。
         好了,这个机制基本上就贫到这里了,虽然解决一些问题,但还是不能从根本上解决用户收不
到邮件的问题,这也是一个让我郁闷到今天的一个心病:(   

        希望园子里的朋友有这方面经验的给我一点启示, 同时也祝大家工作愉快!

   

posted @ 2007-09-03 11:52  代震军  阅读(7196)  评论(39编辑  收藏  举报