InfiniTec - Henning Krauses Blog

Don't adjust your mind - it's reality that is malfunctioning

HowTo: Use the SUBSCRIBE Method to get Exchange store notifications

A few more words...

Generally, there are two event systems in place with Exchange: Eventsinks that run as COM+ applications on the server, or remote clients, which are notified via UDP packets. Depending on the task you want to accomplish, you should take the one or the other:

  • If you must process items in the store synchronously, you must use conventional eventsinks (OnSyncSave and OnSyncDelete).
  • If your task is performance critical, you should also use the conventional eventsinks.
  • If you are not allowed to run any code on the Exchange server, the only option you have is the WebDAV event system.

The WebDAV event system, has the following drawbacks:

  • You are only notified that a change (new item, update or delete) has occured. For example, if you monitor a folder for changes, you will get a notification when a change has occured. You won't, however, get the URL of the changed item. Traditionally, this means that you must du a SEARCH and search for all items with the DAV:getlastmodified property set to a greater value than your last search. With Exchange 2003, new promising system has been introduced, namely WebDAV replication.
  • The notification packets are sent via UDP. This means that there are difficulties with firewalls and such things. In this case, you can still use the system, but you must use the POLL method frequently to get information on changed items.

How it works

The chain of action is the following:

  1. The client sends a SUBSCRIBE method to the Exchange server and sets up the subscriptions for the specified folder

  2. The Exchange Server returns a subscription ID to the client that uniquely identifies the subscription.

  3. An item within the monitored folder is modified.

  4. The Exchange server sends an UDP packet to the client which contains a list of all triggered subscription IDs. Until the packet is acknowledged, Exchange will continue sending packets with a decreasing frequency.

  5. After the client has processed the request, it sends a POLL message with the subscription IDs of the events he has handled.

  6. When the client does not need the subscription any longer, it issues an UNSUBSCRIBE call on the folder specifying the subscription IDs he want to cancel.

Example code

The example code attached to this article is a console application that monitors a folder or element on an Exchange server for changes. Full source code is included. Note that this example uses the WebDAVLayer component.

Downloads

SubsriptionDemo.zip (31,370 Bytes)
Demo program with full source

Technorati:

Posted by Henning Krause on Thursday, July 28, 2005 12:00 AM, last modified on Monday, December 26, 2005 12:00 PM
Permalink | Post RSSRSS comment feed

HOWTO: Customize Delivery Status Notifications generated by Microsoft Exchange

More Information

As this blog points out, it is sometimes necessary to control the generation of delivery status notifications, for example if the Exchange Server is responsible for multiple domains. In this case, Exchange uses the default SMTP domain as the sender for DSNs.

The blog entry briefly describes the necessary steps but it turns out that the entire process of registering the sink is poorly documented. Additionally diffculties arose because I wanted to implement the sink with managed code.

Also Microsoft offers an example on how to implement managed transport event sinks and also includes the necessary type libraries, this library lacks the necesssary interface definitions required for the OnSyncGenerateDSN event sink.

This implementation changes the domain for the DSN to the domain of the first recipient of the original mal.

Solution

The first step was to generate a type library which contains the interface definitions for the event sink. To do this, I modified the smtpevent.idl file, which is included in the Platform SDK. The modified version is attached to this article.

I compiled the modified smtpevent.idl with the MIDL compiler and created an interop assembly with the Type library import utility (tlbimp.exe).

Unfortunately, some of the methods are incorrectly marshalled, and thus, can't be used. So, the next step is to decompile the generated assembly with ildasm.exe, correct the definition of the methods and recompile the source with the ilasm.exe compiler. The modified interop dll is also attached to this article.

The implemention of the actual interface is the most easy part of the entire process. The only method that has to be implemented is the IDSNGenerationSink.OnSyncGenerateDSN:

    1 public void OnSyncGenerateDSN(ISMTPServer pISMTPServer, IDSNSubmission pIDSNSubmission, IMailMsgProperties pIMsg, IMailMsgPropertyBag pIDSNProperties, IDSNRecipientIterator pIRecipIter)

    2 {

    3     StringBuilder sb;

    4     IMailMsgRecipientsBase recipients;

    5     string domain;

    6 

    7     Trace.WriteLine("[DSNRewriter] OnSyncGenerateDSN called");

    8     try

    9     {

   10         recipients = (IMailMsgRecipientsBase)pIMsg;

   11 

   12         sb = new StringBuilder(512);

   13         try

   14         {

   15             recipients.Item(0, (uint)_CAT_ADDRESS_TYPE.CAT_SMTP, 512, sb);

   16             domain = sb.ToString();

   17             domain = domain.Substring(domain.IndexOf("@") + 1);

   18 

   19             Trace.WriteLine("[DSNRewriter] Domain of first recipient is: " + domain);

   20 

   21             Trace.WriteLine("[DSNRewriter] Setting default domain to " + domain);

   22             pIDSNProperties.PutStringA((uint)eDSNProperties.DSNPROP_SZ_DEFAULTDOMAIN, domain);

   23             Trace.WriteLine("[DSNRewriter] Done");

   24         }

   25         catch (Exception ex)

   26         {

   27             Trace.WriteLine("[DSNRewriter] The following exceptions occured: " + ex.ToString());

   28         }

   29 

   30     }

   31     catch (Exception ex)

   32     {

   33         Trace.WriteLine("[DSNRewriter] Failed. Exception: " + ex.ToString());

   34     }

   35 }

This implementation sets the domain for the DSN to the domain of the first recipient of the original message.

After the code has been compiled to an assembly it must be registered on the Exchange server with the regasm.exe utility.

The final step is the registration of the event sink within the store. Again, Microsoft offers a script which handles registration, unregistration and enumeration of SMTP event sinks. And again, this script lacks the functionality to register DSN related event sinks. A modified version of this script is also attached to this article.

To register the event sink run the modified smtpevent.vbs with the following parameters:

    1 csript smtpevent.vbs /add 1 dsngenerationsink dsnrewriter dsnrewriter.dsnrewriter ehlo=*

This installs the event sink on the first SMTP virtual server.

To uninstall the event sink, run the following command:

    1 cscript smtpevent.vbs /remove 1 dsngenerationsink dsnrewriter

Remarks

  • If you want to modify the interop DLLs or the implementation of the event sink, you must sign them with a strong name. Use the sn.exe utility to create one. The attached binary is signed with a key that is, for obvious reasons, not included in the attached files.
  • The example implementation does not write any log files. Trace output is, however, written to the Windows debug interface. You can use DebugView from Sysinternals to view this trace output.

Downloads

SMTPInterop.zip (64,375 Bytes)
Modified interop files
Binaries.zip (17,054 Bytes)
Binary of the described implementation
DsnRewriter.zip (56,221 Bytes)
Sourcecode as Visual Studio 2003 Project
SMTPEventScript.zip (6,775 Bytes)
Modified SMTPEvent.vbs management script

Technorati:

Posted by Henning Krause on Sunday, June 26, 2005 12:00 AM, last modified on Monday, December 26, 2005 12:00 PM
Permalink | Post RSSRSS comment feed

Scripts no longer function after you run an ASP.NET page which displays performance data

Symptoms

After you view an ASP.NET webpage which displays performance counter data via the PerformanceCounter class, scripts no longer function. This includes scripts launched from a command line as well as client-side scripts in any browser window that is opened after you viewed the original page. Scripts are working again after you have stopped the Word Wide Publishing service.

Cause

A deadlock occurs because of a bug in one of the WMI providers. An event tracing is done which calls the RegisterTraceGuids API which in turn calls into one of the WMI Service hosted within the Services.exe.

Resolution

A hotfix is available from Microsoft, at least in english and german.

To resolve this problem, contact Microsoft Product Support Services to obtain the hotfix for KB834010. For a complete list of Microsoft Product Support Services phone numbers and information about support costs, visit the following Microsoft Web site: http://support.microsoft.com/default.aspx?scid=fh;[LN];CNTACTMS

Status

This problem was first corrected in Microsoft Windows XP Service Pack 2

Steps to reproduce the problem

  1. Create a new ASP.NET web application
  2. Add a label control to the page
  3. Add this code to the Page_Load event:
  4.     1 using (PerformanceCounter counter = new PerformanceCounter())

        2 {

        3     counter.CategoryName = "Memory";

        4     counter.CounterName = "Available MBytes";

        5     Label1.Text = counter.NextValue().ToString();

        6 }

  5. Compile the application
  6. Open the webform with Internet Explorer

  7. Now, open any website which has client-side scripting

References

See KB Article 834010 for more information

Posted by Henning Krause on Tuesday, May 31, 2005 9:46 PM, last modified on Tuesday, July 26, 2011 9:57 PM
Permalink | Post RSSRSS comment feed

Scripts no longer function after you run an ASP.NET page which displays performance data

Symptoms

After you view an ASP.NET webpage which displays performance counter data via the PerformanceCounter class, scripts no longer function. This includes scripts launched from a command line as well as client-side scripts in any browser window that is opened after you viewed the original page. Scripts are working again after you have stopped the Word Wide Publishing service.

Cause

A deadlock occurs because of a bug in one of the WMI providers. An event tracing is done which calls the RegisterTraceGuids API which in turn calls into one of the WMI Service hosted within the Services.exe.

<xsl:template match="xhtml:img" mode="xhtmlimage">

<xsl:param name="caption" select="@title" />

< xsl:copy >

<xsl:apply-templates select="@*" mode="xhtmlattributes" />

</ xsl:copy >

<xsl:element name="span">

<xsl:attribute name="class">clsCaption</xsl:attribute>

<xsl:value-of select="$caption" />

</ xsl:element >

</ xsl:template >

Resolution

A hotfix is available from Microsoft, at least in english and german.

To resolve this problem, contact Microsoft Product Support Services to obtain the hotfix. For a complete list of Microsoft Product Support Services phone numbers and information about support costs, visit the following Microsoft Web site: http://support.microsoft.com/default.aspx?scid=fh;[LN];CNTACTMS

Status

This problem was first corrected in Microsoft Windows XP Service Pack 2

Steps to reproduce the problem

  1. Create a new ASP.NET web application
  2. Add a label control to the page
  3. Add this code to the Page_Load event:

    using (PerformanceCounter counter = new PerformanceCounter())

    {

    counter.CategoryName = "Memory";

    counter.CounterName = "Available MBytes";

    Label1.Text = counter.NextValue().ToString();

    }

  4. Compile the application

  5. Open the webform with Internet Explorer

  6. Now, open any website which has client-side scripting

References

See KB Article 834010 for more information

Posted by Henning Krause on Tuesday, May 31, 2005 9:46 PM, last modified on Tuesday, July 26, 2011 9:57 PM
Permalink | Post RSSRSS comment feed

How to get the Global Address List programatically

More Information

The Exchange Address Lists are not stored somewhere on the Exchange Store, neither are they directly stored within Active Directory. The only thing that is stored in the Active Directory database is a LDAP search filter that matches the participants of the address list.
To get a glimpse on these filters you can use ADSI Edit, a very handy tool to examine the Active Directory database.
BUT: As with regedit, the improper use of this tool may affect the stability of your Active Directory. You have been warned! The tool can be found on the Windows CDROM under the Support Tools.


(click to enlarge)
Anyway, take a look on the image to left. Under the node
CN=All Global Address Lists,CN=Address Lists Container,CN=Contoso Ltd,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=contoso,DC=local you will find all global address lists. In most cases, however, there will only be one.
The entry CN=Default Global Address Lists has a property called purportedSearch which contains the LDAP filter for this address list.
The default value is:

    1 (& (mailnickname=*)​(|(&(objectCategory=person)​(objectClass=user)​(!(homeMDB=*))​(!(msExchHomeServerName=*)))​(&(objectCategory=person)​(objectClass=user)​(|(homeMDB=*)​(msExchHomeServerName=*)))​(&(objectCategory=person)​(objectClass=contact))​(objectCategory=group)​(objectCategory=publicFolder)​(objectCategory=msExchDynamicDistributionList)))

This is the default LDAP filter for the Global Address List used by the Recipient Update Service (RUS).

Under the node CN=All Address Lists you will find the other address lists. As with the Global Address List, every list has its own node with the property purportedSearch set to the corresponding LDAP filter.

However, it would be very slow to actually use this LDAP filter. Instead, one would do the search the other way around: Every element which is listed in the global address book has the attribute showInAddressbook. It is much faster to use the following search filter:

    1 (&(mailNickname=*)​(showInAddressBook=<dn of addressbook>))

Simply replace the <dn of addressbook> with the the distinguished name of the address book you want to get. For the global address book, this would be CN=Default Global Address List,CN=All Global Address Lists,CN=Address Lists Container,CN=<your organization>,CN=Microsoft
Exchange,CN=Services,CN=Configuration,DC=joe,DC=com.

Depending on your programming language you can use different tools to do a LDAP search. If you are using Visual Basic or VBScript you’ll use the IADS Interfaces via the GetObject() command. In any .NET language you can use the System.DirectoryServices.DirectorySearcher class.

VBScript example

Here is an example using VBScript. Note that this example only searches the default domain of an Active Directory.

    1 optionexplicit

    2 

    3 dim oRoot

    4 dim oConnect, oCommand, rs

    5 dim strConfigurationContext

    6 dim oGAL

    7 dim strGalContainer

    8 dim strSearchRoot

    9 dim entry

   10 dim oSearchRoot

   11 

   12 'to get LDAP namespace

   13 set oRoot = GetObject("LDAP://RootDSE")

   14 strConfigurationContext = oRoot.Get("configurationNamingContext")

   15 

   16 'Open the global address list container

   17 strGalContainer = "LDAP://CN=All Global Address Lists,CN=Address Lists Container,CN=Contoso,CN=Microsoft Exchange,CN=Services, " & strConfigurationContext

   18 

   19 ' Iterate through global address lists. In this example, only the last one is used.

   20 foreach entry in GetObject(strGalContainer)

   21     set oGAL = entry

   22 next

   23 

   24 'Get the searchroot

   25 strSearchRoot = "LDAP://" & oRoot.Get("defaultNamingContext")

   26 

   27 'Using ADO to query the Active Directory for a particule user

   28 set oConnect = CreateObject("ADODB.Connection")

   29 set oCommand = CreateObject("ADODB.Command")

   30 

   31 'Open the connection

   32 oConnect.Provider = "ADsDSOObject"   

   33 oConnect.Open "Active Directory Provider"

   34 

   35 'Create a command object for this connection

   36 Set  oCommand.ActiveConnection = oConnect

   37 oCommand.CommandText = "SELECT samAccountName, givenName, sn FROM '" & strSearchRoot & "' WHERE msExchHideFromAddressLists <> true AND showInAddressBook = '" & oGal.Get("distinguishedName") & "' ORDER BY sAMAccountName ASC"

   38 

   39 'Create a recordset based on the ADO command

   40 set rs = oCommand.Execute

   41 

   42 while (not rs.EOF)

   43     wscript.echo rs.Fields("samAccountName") & " (" & rs.Fields("givenName") & " " & rs.Fields("sn") & ")"

   44     rs.MoveNext

   45 wend

References

Many thanks to Joe Richards for the hint on the showInAddressBook property.

Technorati:

Posted by Henning Krause on Sunday, February 27, 2005 12:00 AM, last modified on Monday, November 29, 2010 8:01 PM
Permalink | Post RSSRSS comment feed

ExCmd

Features

  • Issue PROPFIND, PROPPATCH, MKCOL and all the other WebDAV commands on any exchange folder.
  • Issue GET and PUT methods to retrieve and update attachments on a file
  • Support for various authentication schemes like BASE, NTLM and Form-Based authentication
  • Use of variables within the command files which are replaced before the command is passed to the server.
  • The response to a WebDAV command is normally a XML file. In conjunction with msxsl, this tool becomes even more powerful because the responses can be transformed into other representations like HTML or even as input for new commands.

Examples

The download link below contains three xml files which you can use as a start. Consider you want to get the name of the default inbox folder. On an English installation this would be Inbox, but on a german installation, this folder is called Posteingang. To get this information, simply use the getproperty.xml as command file:

    1 ExCmd PROPFIND http://contoso/exchange/administrator /c:getproperty.xml ns="urn:schemas:httpmail:" name="inbox"

In the case of my test environment the answer was this:

    1 <?xml version="1.0" encoding="ibm850"?>

    2 <a:multistatus xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" xmlns:d="urn:schemas:httpmail:" xmlns:c="xml:" xmlns:a="DAV:">

    3   <a:response>

    4     <a:href>http://contoso/exchange/administrator/</a:href>

    5     <a:propstat>

    6       <a:status>HTTP/1.1 200 OK</a:status>

    7       <a:prop>

    8         <d:inbox> http://contoso/exchange/administrator/Posteingang</d:inbox>

    9       </a:prop>

   10     </a:propstat>

   11   </a:response>

   12 </a:multistatus>

   13 

   14 The command completed Successfully.

The contents of the getproperty.xml are like this:

    1 <?xml version="1.0" encoding="utf-8" ?>

    2 <a:propfind xmlns:a="DAV:"

    3   <a:prop>

    4     <%name% xmlns="%ns%" />

    5   </a:prop>

    6 </a:propfind>

So, there are two variables which are replaced before the request is transmitted to the server.

Changelog

1.3.1885.37559 – Feb 28, 2005

  • Added a new switch: /h. This allows the specification of additional headers that should be sent along with the request.

1.2.1883.23641 – Feb 26, 2005

  • Initial Release

Downloads

ExCmd.1.3.1885.37559.zip (25,400 Bytes)
Binaries for version 1.3.1885.37559

Technorati:

Posted by Henning Krause on Saturday, February 26, 2005 12:00 AM, last modified on Monday, December 26, 2005 12:00 PM
Permalink | Post RSSRSS comment feed

OWA Options dialog from a programmers perspective

Description

Outlook Web Access can be configured to some extent by the means of a settings page. This page is opened when you click Options in the main menu. However, Microsoft lacks documentation about which properties within the store reflect these settings.
All the properties below are stored on the root folder of a mailbox.
Here is an example that gets the current timezone setting from the administrators mailbox:

    1 PROPFIND /exchange/administrator

    2 Host: www.contoso.com

    3 Translate: f

    4 

    5 <?xml version="1.0" encoding="utf-8" ?>

    6 <a:propfind xmlns:a="DAV:">

    7   <a:prop>

    8     <timezone xmlns="http://schemas.microsoft.com/exchange/" />

    9   </a:prop>

   10 </a:propfind>

The properties

All the affected properties are within the http://schemas.microsoft.com/exchange/ namespace.

(click to enlarge)

1-3: <unknown>

4: http://schemas.microsoft.com/exchange/viewrowcount; Integer type

5: http://schemas.microsoft.com/exchange/nextsel; Valid values:

    0: Open the previous item
    1: Open the next item
    2: return to the view
6: http://schemas.microsoft.com/exchange/newmailnotify; To enable, set bit 0 to 1. To disable, clear the flag
7: http://schemas.microsoft.com/exchange/newmailnotify; To enable, set bit 1 to 1. To disable, clear the flag
8: http://schemas.microsoft.com/exchange/autoaddsignature; True/False
9: http://schemas.microsoft.com/exchange/signaturetext and http://schemas.microsoft.com/exchange/signaturehtml
10: Different properties are managed with this setting:
    http://schemas.microsoft.com/exchange/composefontcolor: HTML Code of the color, i.e. #000000 for black
    http://schemas.microsoft.com/exchange/composefontname: The name of the font, i.e. Arial
    http://schemas.microsoft.com/exchange/composefontsize: The fontsize to use. Valid values are:
    http://schemas.microsoft.com/exchange/composefontflags. The value is a combination of the following bit values:

11: http://schemas.microsoft.com/exchange/previewmarkasread; Valid values:

    0: Mark item displayes in reading pane as read
    1: Mark item as read when seletion changes
    2: Do not automatically mark items as read

12: http://schemas.microsoft.com/exchange/prevreaddelaytime; Number of seconds;

13: http://schemas.microsoft.com/exchange/spellingignoreuppercase; True/False
14: http://schemas.microsoft.com/exchange/spellingignoremixeddigits; True/False
15: http://schemas.microsoft.com/exchange/spellingcheckbeforesend; True/False
16: http://schemas.microsoft.com/exchange/spellingdictionarylanguage; Valid values:

    "en-au": English (Australia)
    "en-ca": English (Canada)
    "en-gb": English (United Kingdom)
    "en-us": English (United States)
    "fr": French
    "de-p": German (post-reform)
    "de-a": German (pre-reform)
    "it": Italian
    "ko": Korean
    "es": Spanish

17: http://schemas.microsoft.com/exchange/smimeencrypt; True/False
18: http://schemas.microsoft.com/exchange/smimesign; True/False
19: <unkown>
20: http://schemas.microsoft.com/exchange/readreceipt; Valid values:

    0: Do not automatically send a response
    1: Always send a response
21: http://schemas.microsoft.com/exchange/blockexternalcontent; True/False
22: http://schemas.microsoft.com/exchange/themeid; Valid values:
    0: Default (blue)
    1: Olive Green 
    2: Silver
    3: Burgundy
    4: Dark Blue

23: http://schemas.microsoft.com/exchange/shortdateformat; Valid values:
    "M/d/yyyy": 1/1/2005 - 12/31/2005
    "M/d/yy": 1/1/05 - 12/31/05
    "MM/dd/yy" 01/01/05 - 12/31/05
    "MM/dd/yyyy" 01/01/2005 - 12/31/2005
    "yy/MM/dd" 05/01/01 - 05/12/31
    "yyyy-MM-dd" 2005-01-01 - 2005-12-31
    "dd-MMM-yy" 01-Jan-05 - 31-Dec-05

24: http://schemas.microsoft.com/exchange/longdateformat; Valid values:

    "dddd, MMMM dd, yyyy": Saturday, January 01, 2005 - Saturday, December 31, 2005
    "MMMM dd, yyyy": January 01, 2005 - December 31, 2005
    "dddd, dd MMMM, yyyy": Saturday, 01 January, 2005 - Saturday, 31 December, 2005
    "dd MMMM, yyyy": 01 January, 2005 - 31 December, 2005

25: http://schemas.microsoft.com/exchange/timeformat; Valid values:
    "h:mm tt": 1:01 AM - 11:59 PM
    "hh:mm tt": 01:01 AM - 11:59 PM
    "H:mm": 1:01 - 23:59
    "HH:mm": 01:01 - 23:59

26: http://schemas.microsoft.com/exchange/timezone (See appendix A for valid values)
27: http://schemas.microsoft.com/exchange/weekstartday; Valid Values:
    "0": Sunday
    "1": Monday
    "2": Tuesday
    "3": Wednesday
    "4": Thursday
    "5": Friday
    "6": Saturday

28: http://schemas.microsoft.com/exchange/workdaystarttime; Time offset in minutes from midnight: 30 equals 0:30, 720 equals 12 PM
29: http://schemas.microsoft.com/exchange/workdayendtime; Values have the same meaning as in field 30.
30: http://schemas.microsoft.com/exchange/firstweekofyear; Valid Values are:

    "0": Do not display week numbers
    "1": Starts on Jan 1
    "2": First 4-day week
    "3": First full week
31: http://schemas.microsoft.com/exchange/enablereminders; To enable, set bit 2 to 1. To disable, clear the bit.
32: http://schemas.microsoft.com/exchange/enablereminders; To enable, set bit 3 to 1. To disable, clear the bit.
33: http://schemas.microsoft.com/exchange/enablereminders; To enable, set bit 0 to 1. To disable, clear the bit.
34: http://schemas.microsoft.com/exchange/reminderinterval; Interval in seconds.
35: http://schemas.microsoft.com/exchange/anrcontactsfirst; Valid values:

    "0": Global Address Book
    "1": Contacts

Appendix A – Timezone settings

Valid values for the http://schemas.microsoft.com/exchange/timezone property are the following:

  • "Dateline Standard Time": (GMT-12:00) Eniwetok, Kwajalein
  • "Samoa Standard Time": (GMT-11:00) Midway Island, Samoa
  • "Hawaiian Standard Time": (GMT-10:00) Hawaii
  • "Alaskan Standard Time": (GMT-09:00) Alaska
  • "Pacific Standard Time": (GMT-08:00) Pacific Time (US & Canada); Tijuana
  • "Mexico Standard Time 2": (GMT-07:00) Chihuahua, La Paz, Mazatlan
  • "US Mountain Standard Time": (GMT-07:00) Arizona
  • "Mountain Standard Time": (GMT-07:00) Mountain Time (US & Canada)
  • "Central America Standard Time": (GMT-06:00) Central America
  • "Central Standard Time": (GMT-06:00) Central Time (US & Canada)
  • "Mexico Standard Time": (GMT-06:00) Mexico City
  • "Canada Central Standard Time": (GMT-06:00) Saskatchewan
  • "SA Pacific Standard Time": (GMT-05:00) Bogota, Lima, Quito
  • "Eastern Standard Time": (GMT-05:00) Eastern Time (US & Canada)
  • "US Eastern Standard Time": (GMT-05:00) Indiana (East)
  • "Atlantic Standard Time": (GMT-04:00) Atlantic Time (Canada)
  • "SA Western Standard Time": (GMT-04:00) Caracas, La Paz
  • "Pacific SA Standard Time": (GMT-04:00) Santiago
  • "Newfoundland Standard Time": (GMT-03:30) Newfoundland
  • "E. South America Standard Time": (GMT-03:00) Brasilia
  • "Greenland Standard Time": (GMT-03:00) Greenland
  • "SA Eastern Standard Time": (GMT-03:00) Buenos Aires, Georgetown
  • "Mid-Atlantic Standard Time": (GMT-02:00) Mid-Atlantic
  • "Azores Standard Time": (GMT-01:00) Azores
  • "Cape Verde Standard Time": (GMT-01:00) Cape Verde Is.
  • "Greenwich Standard Time": (GMT) Casablanca, Monrovia
  • "GMT Standard Time": (GMT) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London
  • "W. Europe Standard Time": (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
  • "Central Europe Standard Time": (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
  • "Romance Standard Time": (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
  • "Central European Standard Time": (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
  • "W. Central Africa Standard Time": (GMT+01:00) West Central Africa
  • "GTB Standard Time": (GMT+02:00) Athens, Istanbul, Minsk
  • "E. Europe Standard Time": (GMT+02:00) Bucharest
  • "Egypt Standard Time": (GMT+02:00) Cairo
  • "South Africa Standard Time": (GMT+02:00) Harare, Pretoria
  • "FLE Standard Time": (GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius
  • "Israel Standard Time": (GMT+02:00) Jerusalem
  • "Arabic Standard Time": (GMT+03:00) Baghdad
  • "Arab Standard Time": (GMT+03:00) Kuwait, Riyadh
  • "Russian Standard Time": (GMT+03:00) Moscow, St. Petersburg, Volgograd
  • "E. Africa Standard Time": (GMT+03:00) Nairobi
  • "Iran Standard Time": (GMT+03:30) Tehran
  • "Arabian Standard Time": (GMT+04:00) Abu Dhabi, Muscat
  • "Caucasus Standard Time": (GMT+04:00) Baku, Tbilisi, Yerevan
  • "Afghanistan Standard Time": (GMT+04:30) Kabul
  • "Ekaterinburg Standard Time": (GMT+05:00) Ekaterinburg
  • "West Asia Standard Time": (GMT+05:00) Islamabad, Karachi, Tashkent
  • "India Standard Time": (GMT+05:30) Kolkata, Chennai, Mumbai, New Delhi
  • "Nepal Standard Time": (GMT+05:45) Kathmandu
  • "N. Central Asia Standard Time": (GMT+06:00) Almaty, Novosibirsk
  • "Central Asia Standard Time": (GMT+06:00) Astana, Dhaka
  • "Sri Lanka Standard Time": (GMT+06:00) Sri Jayawardenepura
  • "Myanmar Standard Time": (GMT+06:30) Rangoon
  • "SE Asia Standard Time": (GMT+07:00) Bangkok, Hanoi, Jakarta
  • "North Asia Standard Time": (GMT+07:00) Krasnoyarsk
  • "China Standard Time": (GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi
  • "North Asia East Standard Time": (GMT+08:00) Irkutsk, Ulaan Bataar
  • "W. Australia Standard Time": (GMT+08:00) Perth
  • "Singapore Standard Time": (GMT+08:00) Kuala Lumpur, Singapore
  • "Taipei Standard Time": (GMT+08:00) Taipei
  • "Tokyo Standard Time": (GMT+09:00) Osaka, Sapporo, Tokyo
  • "Korea Standard Time": (GMT+09:00) Seoul
  • "Yakutsk Standard Time": (GMT+09:00) Yakutsk
  • "Cen. Australia Standard Time": (GMT+09:30) Adelaide
  • "AUS Central Standard Time": (GMT+09:30) Darwin
  • "E. Australia Standard Time": (GMT+10:00) Brisbane
  • "AUS Eastern Standard Time": (GMT+10:00) Canberra, Melbourne, Sydney
  • "West Pacific Standard Time": (GMT+10:00) Guam, Port Moresby
  • "Tasmania Standard Time": (GMT+10:00) Hobart
  • "Vladivostok Standard Time": (GMT+10:00) Vladivostok
  • "Central Pacific Standard Time": (GMT+11:00) Magadan, Solomon Is., New Caledonia
  • "New Zealand Standard Time": (GMT+12:00) Auckland, Wellington
  • "Fiji Standard Time": (GMT+12:00) Fiji, Kamchatka, Marshall Is.
  • "Tonga Standard Time": (GMT+13:00) Nuku'alofa

Technorati:

Posted by Henning Krause on Wednesday, February 16, 2005 12:00 AM, last modified on Monday, December 26, 2005 12:00 PM
Permalink | Post RSSRSS comment feed

SmartMemoryManagement

Posted under .NET Tools | Comments (0)

Description

The .NET Runtime has four different heaps where it stores objects. There are many good articles on how the runtime manages object on these heaps so I won’t into great details about the first three. Here are a two good links to blogs with more information on this topic:

The Large Object Heap

Most of the time, the GC does a well job managing the memory of your programs.
But there are situations, where it performs sub-optimally. One of these situations are so-called Large Objects. All objects with a size greater than 85000 bytes fall within this category. These objects are stored on a separate heap, called Large Object Heap. This heap will neither be defragmented, nor is it collected often, as the GC processes this heap along with the Generation-2 heap.
To reproduce the problem, try to read a large file (say 70MB or so) from a file stream and store it in a MemoryStream. You will see that your memory usage will climb to about 250 MB. Now, destroy your Memorystream and read the file again into another MemoryStream. Now, depending on your memory pressure, the GC will do a Gen-2 Collection (remember.. this is expensive) and reclaim that memory, or you end up with about 500MB of consumed memory.
Now you will probably argue that 250MB are way to much because you could preallocate one large buffer of 70MB for the file via MemoryStream.SetLength(), so you’ll end up with a smaller memory consumption. But there are two things with this argument:
  1. On the second read of the file you memory usage will increase anyway, albeit not to 500 MB but perhaps to about 150MB.
  2. You might not always know in advance how much memory you will need. Think of a server application that reads data over a TCP connection, there might be no hint on how large the amount of received data is. So you end up with increasing your buffer every now and then.

The Solution

The classes you can download below will give you the ability to reuse previously allocated chunks of memory. And once you are done with the memory, you can return it to the pool.
Note that I used weak references within the pool, so the GC can reclaim the buffers within the pool if memory pressure comes up.
Additionally, I have developed a new MemoryStream that uses this object pooling technique.

Downloads


Posted by Henning Krause on Saturday, February 12, 2005 12:26 PM, last modified on Saturday, February 12, 2005 1:25 PM
Permalink | Post RSSRSS comment feed

WebDAVLayer 1.0.1735.12520

Posted under .NET Tools | Comments (2)

More Information

The database that is used by Exchange 2000/2003 is called WebStorageSystem. Each element within this store is accessible via an URL using the WebDAV protocol. This protocol extends the HTTP protocol by exposing several new verbs like PROPFIND, PROPPATCH or SEARCH to name a few. Additionally, the Exchange implementation of the WebDAV protocol has some proprietary commands for batch operations: BMOVE and BCOPY. The parameters of these commands are expressed within the body of the HTTP/WebDAV request as XML.

Features

  • Encapsulation of most of the WebDAV commands.
  • Easy access to folders, elements and properties within the store
  • Support for various authentication schemas: Basic, Digest, NTLM, Kerberos and even the Form-based authentication of Exchange 2003
Unfortunately, there is currently no documentation available. But on the other hand: You can use it free of charge!

Downloads


Posted by Henning Krause on Monday, February 7, 2005 9:04 PM, last modified on Monday, February 7, 2005 9:25 PM
Permalink | Post RSSRSS comment feed

Recover deleted elements from an Exchange 2000/2003 folder

More Information

Outlook and Exchange know two ways how to delete a file: A soft-delete, which is performed when you simply press delete on an item. The affected item is then moved to the recycle-bin. The other method is called hard-deletion. These items are kept in the store for a configurable duration, typically 30 days.
To check if there are any deleted items or folders within a given public or private folder, you can read the values of these properties:

  • PR_DELETED_MSG_COUNT
    This property contains the number of deleted messages in the folder
  • PR_DELETED_FOLDER_COUNT
    This property contains the number of deleted folders in the folder

To enumerate the deleted items, a SEARCH command is issued on the folder:

    1 SEARCH /public/folder / HTTP/1.1

    2 Host: www.contoso.com

    3 Depth: 0

    4 Content-type: text/xml;

    5 Content-Length: XXXXX

    6 

    7 <?xml version="1.0"?>

    8 <a:searchrequest xmlns:a="DAV:">

    9   <a:sql>

   10     SELECT “DAV:displayname", "DAV:contentclass", "DAV:getcontentlength", "DAV:href", "DAV:isfolder" FROM SCOPE('SOFTDELETED TRAVERSAL OF "http://www.contoso.com/public/folder"')

   11   </a:sql>

   12 </a:searchrequest>

To recover one or more of these items, a BCOPY command is issued:

    1 BCOPY /public/container/ HTTP/1.1

    2 Host: www.contoso.com

    3 Depth: 0

    4 Content-type: text/xml;

    5 Content-Length: XXXXX

    6 

    7 <?xml version="1.0"?>

    8 <a:copy xmlns:a="DAV:">

    9   <a:target>

   10     <a:href>http://www.contoso.com/public/folder-softdeleted-/-FlatUrlSpace-/93c5676ddb37ee4690b4756339818e46-6d89</a:href>

   11     <a:dest>http://www.contoso.com/public/folder/recovereditem</a:dest>

   12   </a:target>

   13 </a:copy>

After the item has been recovered, the deleted element can safely be removed from the store via the BDELETE command:

    1 BDELETE /public/container/ HTTP/1.1

    2 Host: www.contoso.com

    3 Depth: 0

    4 Content-type: text/xml;

    5 Content-Length: XXXXX

    6 

    7 <?xml version="1.0" encoding="UTF-8"?>

    8 <a:delete xmlns:a="DAV:">

    9   <a:target>

   10     <a:href>http://www.contoso.com/public/folder/-softdeleted-/-FlatUrlSpace-/93c5676ddb37ee4690b4756339818e46-6d89</a:href>

   11   </a:target>

   12 </a:delete>


Technorati:

Posted by Henning Krause on Monday, February 7, 2005 12:00 AM, last modified on Monday, December 26, 2005 12:00 PM
Permalink | Post RSSRSS comment feed