这个是微软的一个安全补丁,但是搞得服务器非常的不稳定,经常会把Generic Host进程搞死,然后机器就不能联网访问了。
以前采取的是放任的方式处理的,把Computer Browser服务设置成死掉后自动重启能够一定程度上解决问题,但是有时候还是需要人工干预。
正规的做法是安装补丁KB958644,注意版本
这个是微软的一个安全补丁,但是搞得服务器非常的不稳定,经常会把Generic Host进程搞死,然后机器就不能联网访问了。
以前采取的是放任的方式处理的,把Computer Browser服务设置成死掉后自动重启能够一定程度上解决问题,但是有时候还是需要人工干预。
正规的做法是安装补丁KB958644,注意版本
这个其实是 用代码创建MAPI的Profile的后续问题,因为我们把这个东西封装在Web Service里面供远程的其他程序调用,而这个Web Service的运行身份很重要,结合到底是谁?讲的,我们期望是可以通过Impersonate创建在我们指定的用户下面,但是MAPI32是根据当前的Process的用户身份,也就是Application pool的运行用户来读取注册表创建Profile的,所以Impersonate基本上没有用,有一些方法:http://blogs.msdn.com/stephen_griffin/archive/2005/04/13/407925.aspx
这个问题曾经折磨了我好几天,最终放弃了。
最后在其他同事的不懈努力下最终解决:
extern "C" __declspec(dllexport) bool CreateProfileWithIProfAdmin( LPTSTR szProfile,LPTSTR szMailbox,LPTSTR szServer)
{
HRESULT hr = S_OK; // Result from MAPI calls.
LPPROFADMIN lpProfAdmin = NULL; // Profile Admin object.
LPSERVICEADMIN lpSvcAdmin = NULL; // Service Admin object.
LPMAPITABLE lpMsgSvcTable = NULL; // Table to hold services.
LPSRowSet lpSvcRows = NULL; // Rowset to hold results of table query.
SPropValue rgval[2]; // Property structure to hold values we want to set.
SRestriction sres; // Restriction structure.
SPropValue SvcProps; // Property structure for restriction.
// This indicates columns we want returned from HrQueryAllRows.
enum {iSvcName, iSvcUID, cptaSvc};
SizedSPropTagArray(cptaSvc,sptCols) = { cptaSvc, PR_SERVICE_NAME, PR_SERVICE_UID };
Windows的IIS应用的身份真的是太复杂了一点。
首先是IIS的Service是有运行用户的
然后是应用使用的Application Pool也有运行用户
然后是应用也可以指定Impersonate的用户
而如果应用没有使用Windows的用户,那么还可能有另外一个用户。
以前没有搞过Windows的Web的开发,不太清楚这个里面的最佳实践。
目前的感觉是:
IIS的身份一般不去动,因为是大家共享的
如果需要比较强的用户身份,而且比较固定,建议修改Application Pool的运行身份,另外建议创建自己的Application Pool,如果应用只是简单的需要某种身份访问系统中的资源,例如读写文件、服务数据库,那么建议简单的修改web.config,使用Impersonate指定一个用户。
最开始很不习惯MSDN格式的api文档,感觉很分散,原理性介绍、快速入门、API的说明以及常量定义都不知道怎么弄的。
经过对CDO的文档的摸索,发现这些东西都是存在的,只是和JAVA的那些习惯不一样,java的组件的文档一般比较全面,而且把这些东西组织得很好,开始就是quick start,然后是developement guide,最后就是API reference,链接也做得比较好。虽然MSDN的文档不缺少这些内容,在内容之间的引用做得似乎弱一些。
前几天的方案都比较复杂一些,再来一个简单的:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MAPI;
namespace ConsoleApplication3
{
class Program
{
[STAThread]
static void Main()
{
Session session = new Session();
session.Logon(null, null, false, true, 0, true, "exchange2007\nuser1");
InfoStores infoStores = (InfoStores)session.InfoStores;
for (int i = 0; i < (int)infoStores.Count; i++)
{
InfoStore infoStore = (InfoStore)infoStores.get_Item(i + 1);
if (((string)infoStore.Name).IndexOf("Mailbox – ") >-1)
{
Fields fields = (Fields)infoStore.Fields;
Field size = (Field)fields.get_Item(CdoPropTags.CdoPR_MESSAGE_SIZE, System.Reflection.Missing.Value);
Field count = (Field)fields.get_Item(CdoPropTags.CdoPR_CONTENT_COUNT, System.Reflection.Missing.Value);
Console.WriteLine(infoStore.Name + ": " + size.Value + "Bytes, messages:" + count.Value);
}
}
session.Logoff();
Console.ReadLine();
}
}
}
这个方案的问题是需要安装CDO,好在这个东东是微软的,而且很小,安装包才几百K,其实就是把MAPI的一些API包装成COM了,所以.net平台的那些语言都可以调。
向后兼容性和历史上的诸多古老的解决方案对于微软阵营的开发者而言就是梦魇,它的多语言支持又是另外一个败笔。
我现在被迫从Java阵营临时到了微软阵营,但是遇到的问题多多。
现在有几个比较简单的任务:得到Exchange Server的邮件大小,Exchange本身没有API做这个事情,但是有一些其他的变通方式,例如得到所有的Mailbox然后统计每个Mailbox的大小,而Mailbox的大小也没有直接的API,需要统计所有的Folder的大小,虽然性能上不太好,但是总归是可以做到的。
目前发现的解决方案有这样几个:
接下来只能尝试写COM组件把它包起来给c#用了。
昨天已经搞定了从服务器端得到Mailbox的size,但是这样不是很方便,需要在Exchange服务器上发布代码,今天继续研究从客户端得到这个Size,历经磨难终于成功:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ConsoleApplication1.exchange2007;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
namespace ConsoleApplication1
{
class EWS
{
public static void Run()
{
ServicePointManager.ServerCertificateValidationCallback =
delegate(Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
return true;
};
ExchangeServiceBinding esb = new ExchangeServiceBinding();
esb.RequestServerVersionValue = new RequestServerVersion();
esb.RequestServerVersionValue.Version = ExchangeVersionType.Exchange2007;
esb.Credentials = new NetworkCredential("Administrator", "password", "domain.com");
esb.Url = "https://exchange2007/EWS/Exchange.asmx";
Console.WriteLine("Administrator:" + GetMailboxSize(esb, "Administrator@domain.com"));
Console.WriteLine("cherami:" + GetMailboxSize(esb, "cherami@domain.com"));
Console.WriteLine("ES1Test1:" + GetMailboxSize(esb, "ES1Test1@domain.com"));
Console.ReadLine();
}
开始的时候觉得这个是一个很小的问题,肯定有API直接可以拿到的,不过看了一圈下来,给出的最多的建议是从MAPI得到Mailbox,然后遍历目录累加大小得到Mailbox的大小。可以参考微软的KB:
http://support.microsoft.com/kb/320071
http://support.microsoft.com/kb/200160/
不过感觉都很麻烦。Exchange 2007提供了Exchange Management Shell ,里面有个脚本命令Get-MailboxStatistics可以得到Mailbox的信息,其中就包括Mailbox的大小,然后可以用c#调用这个脚本:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;
using System.Collections.ObjectModel;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
PSSnapInException snapInException = null;
PSSnapInInfo info = rsConfig.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.Admin", out snapInException);
Runspace myRunSpace = RunspaceFactory.CreateRunspace(rsConfig);
myRunSpace.Open();
Pipeline pipeLine = myRunSpace.CreatePipeline();
Command myCommand = new Command("Get-MailboxStatistics");
pipeLine.Commands.Add(myCommand);
CommandParameter databaseParam = new CommandParameter("database", "Main Database");
myCommand.Parameters.Add(databaseParam);
Collection<PSObject> commandResults = pipeLine.Invoke();
foreach (PSObject cmdlet in commandResults)
{
Console.WriteLine(cmdlet.Properties["DisplayName"].Value + "\t" + cmdlet.Properties["TotalItemSize"].Value);
}
}
}
}
唯一的问题是这个需要在exchange server上执行,而且也不排除这个脚本也是通过遍历目录得到的大小(执行的速度似乎也不快)。
看了网上的SandCastle入门,但是我使用的时候还是遇到很多问题。
秉承我的一贯作风写个我的入门指南:
© 2025 解惑
本主题由Anders Noren提供 — 向上 ↑