• Sign In
 
  • MSDN Blogs
  • Microsoft Blog Images
  • More ...

  • Advanced search options...
  • About
  • Email Blog Author
  • RSS for posts
  • Atom
  • RSS for comments
    • OK
  • CDO (20)
  • Code Snippet (42)
  • Custom Providers (15)
  • Debugging (7)
  • DevMsgTeam (278)
  • Documentation (101)
  • DST (8)
  • EWS (7)
  • Exchange (100)
  • Gotchas (91)
  • Hotfix (28)
  • MAPI (220)
  • MAPI Download (48)
  • MFCMAPI (92)
  • MSDN (54)
  • Non Dev (11)
  • OOM (17)
  • Outlook (163)
  • Outlook 2007 Auxiliary Reference (45)
  • Outlook Integration API (11)
  • Protocol Docs (20)
  • PST/OST (22)
  • Referrals (8)
  • Vista (12)
  • WrapPST (15)
Links:
  • Download MFCMAPI
  • MFCMAPI on Facebook
  • Troubleshooting Outlook Crashes
  • Office Update Center
  • Developer Messaging Team Blog
This site is provided "AS IS" with no warranties, and confers no rights. Use of included code samples are subject to the terms specified in the Terms of Use.
Archives
  • May 2012 (1)
  • April 2012 (3)
  • March 2012 (3)
  • February 2012 (3)
  • January 2012 (1)
  • December 2011 (3)
  • November 2011 (1)
  • October 2011 (3)
  • September 2011 (1)
  • August 2011 (1)
  • July 2011 (4)
  • June 2011 (3)
  • May 2011 (3)
  • April 2011 (3)
  • March 2011 (5)
  • February 2011 (1)
  • January 2011 (2)
  • December 2010 (1)
  • November 2010 (4)
  • October 2010 (1)
  • September 2010 (3)
  • August 2010 (5)
  • July 2010 (3)
  • June 2010 (3)
  • May 2010 (1)
  • April 2010 (3)
  • March 2010 (3)
  • February 2010 (3)
  • January 2010 (2)
  • December 2009 (3)
  • November 2009 (5)
  • October 2009 (4)
  • September 2009 (5)
  • August 2009 (5)
  • July 2009 (11)
  • June 2009 (6)
  • May 2009 (5)
  • April 2009 (3)
  • March 2009 (18)
  • February 2009 (10)
  • January 2009 (3)
  • December 2008 (2)
  • November 2008 (2)
  • October 2008 (5)
  • September 2008 (4)
  • August 2008 (10)
  • July 2008 (6)
  • June 2008 (8)
  • May 2008 (2)
  • April 2008 (4)
  • March 2008 (2)
  • February 2008 (2)
  • January 2008 (5)
  • December 2007 (3)
  • November 2007 (2)
  • October 2007 (3)
  • September 2007 (1)
  • August 2007 (4)
  • July 2007 (5)
  • June 2007 (3)
  • May 2007 (4)
  • April 2007 (1)
  • March 2007 (6)
  • February 2007 (3)
  • January 2007 (2)
  • December 2006 (4)
  • November 2006 (3)
  • October 2006 (1)
  • August 2006 (1)
  • June 2006 (5)
  • May 2006 (5)
  • December 2005 (1)
  • November 2005 (4)
  • October 2005 (2)
  • September 2005 (1)
  • April 2005 (3)
  • December 2004 (2)
  • September 2004 (2)
  • August 2004 (3)
  • July 2004 (3)
Blogs I Read
  • Exchange

  • Raymond Chen [MSFT]

  • Larry Osterman [MSFT]

  • Peter David

  • Aaron Margosis [MSFT]

  • Jason Johnston [MSFT]

  • Matt Stehle (MSFT)

  • Patrick Creehan [MSFT]

  • Ryan Gregg [MSFT]

    Outlook PM
  • WebDav 101

    Dan Bagley (MSFT)
  • Dave Vespa [MSFT]

  • Randy Topken

    Outlook EE

December, 2006

MSDN Blogs > SGriffin's MAPI Internals > December, 2006
  • Subscribe via RSS
Sort by: Most Recent | Most Views | Most Comments
Excerpt View | Full Post View
  • SGriffin's MAPI Internals

    Lego Star Wars II: Rudolph the Red Nose Jedi

    Posted over 6 years ago
    by Stephen Griffin - MSFT
    • 35 Comments

    Ok - fine. I admit it. I'm a Lego and Star Wars geek. So it's no surprise I love this game. That's why, when I heard there was a cheat to make a Santa Claus figure(http://www.rebelscum.com/story/front/LEGO_Star_Wars_II_Easter_Christmas_Egg_102422.asp), I rushed to try it out.

    Something about the cheat bugged me though. After you input the TYH319 beard cheat, you activate it by turning on Disguise 3. The old Groucho glasses are then Disguise 1. But what about Disguise 2? There has to be a 2, right? But how to find it?

    Maybe, if I can find the TYH319 code in a file somewhere, the Disguise 2 cheat will be somewhere near it. As luck would have it, I found TYH319 with the first thing I tried - I ran strings.exe on LegoStarWarsII.exe. And right near it was CL4U5H, the code for the Claus hat and clothes. Smack in between them was NBP398. Was this the magic string I was looking for?

    Yep. Try it out. After entering it, go to the Extras menu and you'll find Disguise 2 is available. It's your very own shiny red nose!

    DISCLAIMER: I only tried this on the PC. I have no idea if it works on the consoles.

    BTW - if anyone knows how to take a screenshot in this game (maybe of Vader or Yoda with the red nose) I'd be happy to post it. (A note to the comedians out there - I'm looking for a way in game to take a screenshot. The print screen button rarely works in games. And yes - I did try it.)

  • SGriffin's MAPI Internals

    Exchange Web Services and MAPI Props

    Posted over 6 years ago
    by Stephen Griffin - MSFT
    • 12 Comments

    Merry Christmas! With Exchange 12 (whoops! Exchange 2007) just around the corner, we're starting to get questions about Exchange Web Services. I just helped a customer with a sample illustrating the use of AdditionalProperties to fetch arbitrary MAPI properties from folders and messages. I figured I should clean up the sample and share it with the world.

    Comment #1: It wasn't clear to me how to generate the web service proxy in Visual Studio. I don't do development directly on my Exchange server, so it wasn't as easy as doing "Add Web Reference" and picking "Web Services on the local machine". I finally found a reference indicating you can use either of these:

    http(s)://MyServer/ews/services.wsdl
    http(s)://MyServer/EWS/exchange.asmx?wsdl

    Incidently, neither of these URLs is documented in the Exchange 2007 SDK. Maybe someone more familiar with web services would have found it obvious, but I certainly didn't. The SDK folks are working to correct this oversight.

    Comment #2: The object ExchangeServiceBinding isn't documented in the SDK either. I had to borrow snippets from other samples to figure out how to use it. The SDK folks are working on that too.

    Comment #3: The usual caveats for samples apply here - use at your own risk. Error handling is minimal, so if it crashes - it's not my fault. Enjoy!

    using System;
    using System.Net;
    using EWS; //this is the web reference
    
    namespace GetProps
    {
        class Program
        {
            static void Main(string[] args)
            {
                ExchangeServiceBinding exchangeServer = new ExchangeServiceBinding();
                ICredentials creds = new NetworkCredential("SomeUser", "SomePassword", "SomeDomain");
    
                exchangeServer.Credentials = creds;
                exchangeServer.Url = @"http://MyServer/EWS/Exchange.asmx";
    
                DistinguishedFolderIdType[] folderIDArray = new DistinguishedFolderIdType[1];
                folderIDArray[0] = new DistinguishedFolderIdType();
                folderIDArray[0].Id = DistinguishedFolderIdNameType.inbox;
    
                PathToUnindexedFieldType ptuftDisplayName = new PathToUnindexedFieldType();
                ptuftDisplayName.FieldURI = UnindexedFieldURIType.folderDisplayName;
    
                PathToExtendedFieldType pteftComment = new PathToExtendedFieldType();
                pteftComment.PropertyTag = "0x3004"; // PR_COMMENT
                pteftComment.PropertyType = MapiPropertyTypeType.String;
    
                GetFolderType myfoldertype = new GetFolderType();
                myfoldertype.FolderIds = folderIDArray;
                myfoldertype.FolderShape = new FolderResponseShapeType();
                myfoldertype.FolderShape.BaseShape = DefaultShapeNamesType.IdOnly;
                myfoldertype.FolderShape.AdditionalProperties = new BasePathToElementType[2];
                myfoldertype.FolderShape.AdditionalProperties[0] = ptuftDisplayName;
                myfoldertype.FolderShape.AdditionalProperties[1] = pteftComment;
    
                Console.WriteLine("Getting inbox");
                GetFolderResponseType myFolder = exchangeServer.GetFolder(myfoldertype);
    
                FolderInfoResponseMessageType firmtInbox = 
                    (FolderInfoResponseMessageType) myFolder.ResponseMessages.Items[0];
    
                Console.WriteLine("got folder: {0}",firmtInbox.Folders[0].DisplayName);
    
                if (null != firmtInbox.Folders[0].ExtendedProperty)
                {
                    Console.WriteLine("Comment: {0}",firmtInbox.Folders[0].ExtendedProperty[0].Item.ToString());
                }
                else
                {
                    Console.WriteLine("Comment: not found");
                }
    
                PathToUnindexedFieldType ptuftSubject = new PathToUnindexedFieldType();
                ptuftSubject.FieldURI = UnindexedFieldURIType.itemSubject;
    
                PathToExtendedFieldType pteftFlagStatus = new PathToExtendedFieldType();
                pteftFlagStatus.PropertyTag = "0x1090"; // PR_FLAG_STATUS
                pteftFlagStatus.PropertyType = MapiPropertyTypeType.Integer;
    
                FindItemType findItemRequest = new FindItemType();
                findItemRequest.Traversal = ItemQueryTraversalType.Shallow;
                findItemRequest.ItemShape = new ItemResponseShapeType();
                findItemRequest.ItemShape.BaseShape = DefaultShapeNamesType.IdOnly;
                findItemRequest.ItemShape.AdditionalProperties = new BasePathToElementType[2];
                findItemRequest.ItemShape.AdditionalProperties[0] = ptuftSubject;
                findItemRequest.ItemShape.AdditionalProperties[1] = pteftFlagStatus;
                findItemRequest.ParentFolderIds = new FolderIdType[1];
                findItemRequest.ParentFolderIds[0] = firmtInbox.Folders[0].FolderId;
    
                FindItemResponseType firt = exchangeServer.FindItem(findItemRequest);
    
                foreach (FindItemResponseMessageType firmtMessage in firt.ResponseMessages.Items)
                {
                    if (firmtMessage.RootFolder.TotalItemsInView > 0)
                    {
                        foreach (ItemType it in ((ArrayOfRealItemsType)firmtMessage.RootFolder.Item).Items)
                        {
                            Console.WriteLine("got item: {0}",it.Subject);
                            if (null != it.ExtendedProperty)
                            {
                                Console.WriteLine("Prop 0x1090: {0}",it.ExtendedProperty[0].Item.ToString());
                            }
                            else
                            {
                                Console.WriteLine("Prop 0x1090: not found");
                            }
                        }
                    }
                }
    
                Console.WriteLine("\nHit any key to continue");
                Console.ReadKey(true);
            }
        }
    }
  • SGriffin's MAPI Internals

    Preview Handlers in Outlook 2007

    Posted over 6 years ago
    by Stephen Griffin - MSFT
    • 2 Comments

    I figure it won't be long before folks are asking me how to do this. Stephen Toub put up a great article in the MSDN that shows how:

    http://msdn.microsoft.com/en-us/magazine/cc163487.aspx

    He even has a followup post on handling associations for viewers:

    http://blogs.msdn.com/toub/archive/2006/12/14/preview-handler-association-editor.aspx

    For another example of how cool these viewers can be, Tim Heuer put together a viewer for Foxit:

    http://timheuer.com/blog/archive/2006/12/13/13945.aspx

  • SGriffin's MAPI Internals

    Outlook 2007 Timezone Structures

    Posted over 6 years ago
    by Stephen Griffin - MSFT
    • 9 Comments

    [This is now documented here: http://msdn.microsoft.com/en-us/library/ff960198.aspx, among other articles ]

    Topic

    Properties used by Outlook 2007 to maintain timezone information on appointments.

    Timezones

    Historically Outlook has maintained a property, dispidTimeZoneStruct, on recurring appointments which describes the time zone in which the appointment was created. The problem with this system is that it ignores the possibility that time zone rules can change over time. Since Outlook could potentially use old rules when computing the times of meetings, some of the meetings that users scheduled before the rules change in the registry will now occur at incorrect times. Additionally, non-recurring meetings had no timezone information stamped on them at all. Because of this Microsoft is writing a rebasing tool to help users adjust the times at which these meetings will occur. Documentation for that tool will appear separately.

    The information in this document will help developers who are attempting to write their own rebasing tool. Note that while this information documents direct manipulation of timezone structures on appointments by MAPI, it should not be construed as full support for manipulation of time and recurrence information through MAPI. CDO and the Outlook Object Model are still recommended for manipulation of time and recurrence information.

    In the context of this documentation, “older” and “legacy” clients are defined as Outlook 2003 and earlier, and any version of CDO 1.21 prior to the upcoming timezone update. New clients are defined as Outlook 2007 and higher and any version of CDO 1.21 after the upcoming timezone update.

    Definitions

    #define dispidTimeZoneStruct         0x8233  // legacy timezone property
    #define dispidApptTZDefStartDisplay  0x825E  // timezone that was used when picking start time
    #define dispidApptTZDefEndDisplay    0x825F  // timezone that was used when picking end time
    #define dispidApptTZDefRecur         0x8260  // timezone for recurring meeting expansion
    
    DEFINE_OLEGUID(PSETID_Appointment,  MAKELONG(0x2000+(0x02),0x0006),0,0); 
    
    // TZREG
    // =====================
    //   This is an individual description that defines when a daylight
    //   saving shift, and the return to standard time occurs, and how
    //   far the shift is.  This is basically the same as
    //   TIME_ZONE_INFORMATION documented in MSDN, except that the strings
    //   describing the names "daylight" and "standard" time are omitted.
    //
    typedef struct RenTimeZone
    {
        long        lBias;           // offset from GMT
        long        lStandardBias;   // offset from bias during standard time
        long        lDaylightBias;   // offset from bias during daylight time
        SYSTEMTIME  stStandardDate;  // time to switch to standard time
        SYSTEMTIME  stDaylightDate;  // time to switch to daylight time
    } TZREG;
    
    // TZRULE
    // =====================
    //   This structure represents both a description when a daylight. 
    //   saving shift occurs, and in addition, the year in which that
    //   timezone rule came into effect. 
    //
    typedef struct
    {
        WORD        wFlags;   // indicates which rule matches legacy recur
        SYSTEMTIME  stStart;  // indicates when the rule starts
        TZREG       TZReg;    // the timezone info
    } TZRULE;
    
    const WORD TZRULE_FLAG_RECUR_CURRENT_TZREG  = 0x0001; // see dispidApptTZDefRecur
    const WORD TZRULE_FLAG_EFFECTIVE_TZREG      = 0x0002;
    
    // TZDEFINITION
    // =====================
    //   This represents an entire timezone including all historical, current
    //   and future timezone shift rules for daylight saving time, etc.  It's
    //   identified by a unique GUID.
    //
    typedef struct
    {
        WORD     wFlags;       // indicates which fields are valid
        GUID     guidTZID;     // guid uniquely identifying this timezone
        LPWSTR   pwszKeyName;  // the name of the key for this timezone
        WORD     cRules;       // the number of timezone rules for this definition
        TZRULE*  rgRules;      // an array of rules describing when shifts occur
    } TZDEFINITION;
    
    const WORD TZDEFINITION_FLAG_VALID_GUID     = 0x0001; // the guid is valid
    const WORD TZDEFINITION_FLAG_VALID_KEYNAME  = 0x0002; // the keyname is valid
    
    const ULONG  TZ_MAX_RULES          = 1024; 
    const BYTE   TZ_BIN_VERSION_MAJOR  = 0x02; 
    const BYTE   TZ_BIN_VERSION_MINOR  = 0x01; 

    Properties

    These are named properties in the PSETID_Appointment property set. They are all of the type PT_BINARY, each containing a stream with the persisted format of their respective types. The persistence details are below.

    dispidTimeZoneStruct

    Legacy versions of Outlook store the start and end time of single instance meetings in UTC time. The start and end time of recurring meetings are stored as relative time with a timezone description in the TZREG format. A copy of the timezone description is available in dispidTimeZoneStruct. Third party code should consider this property to be strictly read only. Attempts to write this property can and will corrupt appointments.

    This property contains a persisted TZREG structure.

    dispidApptTZDefStartDisplay

    This represents the timezone that the user was in or picked when picking the start time of the meeting. This is used to show the meeting in the original time it was scheduled and also to determine if the meeting should be adjusted if the definition of a timezone changes.

    If this property is missing, the current local timezone is assumed.

    This property is used for display purposes only and is not used in recurrence expansion.

    This property contains a persisted TZDEFINITION structure.

    dispidApptTZDefEndDisplay

    This represents the timezone that the user was using, or picked when picking the end time of the meeting.

    This property is for display purposes only and is not used in recurrence expansion.

    If this property is missing or is invalid, then the dispidApptTZDefStartDisplay is considered instead. If that property is also missing or is invalid, then the current local timezone is assumed.

    This property is used for display purposes only and is not used in recurrence expansion.

    This property contains a persisted TZDEFINITION structure.

    dispidApptTZDefRecur

    This property is used for multi-rule recurrence expansion and to determine if the recurring meeting should be adjusted if the definition of a timezone changes.

    This property needs to be kept in sync with dispidTimeZoneStruct since older clients may still manipulate dispidTimeZoneStruct. To detect if the two properties are in sync, the wFlags parameter for the rule that matches dispidTimeZoneStruct should have the TZRULE_FLAG_RECUR_CURRENT_TZREG flag set. If this flag is not set, or it is set and the rule in dispidTimeZoneStruct does not match the marked rule, then dispidApptTZDefRecur should be discarded and dispidTimeZoneStruct instead should be used.

    When writing both dispidApptTZDefRecur and dispidTimeZoneStruct to a new recurring meeting, when an arbitrary choice needs to be made to pick dispidTimeZoneStruct, the current definition for the timezone (according to the windows registry) should be written to dispidTimeZoneStruct.

    This property contains a persisted TZDEFINITION structure.

    Structures

    TZREG

    This is the structure used by legacy clients to store time zone information for recurring meetings. The members of this structure are similar to and derived from TIME_ZONE_INFORMATION.

    TZRULE

    This structure augments TZREG by providing additional information indicating when time zone rules take effect

    The stStart value in the TZRULE structure indicates the time in GMT that the rule took effect.

    There are two valid wFlags in the TZRULE structure:

    TZRULE_FLAG_EFFECTIVE_TZREG – this marks one of the rules as the rule that should currently be used. Only one rule can be marked as the “effective” rule. All other rules are for comparison purposes only.

    TZRULE_FLAG_RECUR_CURRENT_TZREG – on recurring meetings, this marks one of the rules as matching the rule in “dispidTimeZoneStruct”. This can be used to detect if dispidTimeZoneStruct has been significantly modified by a legacy client which would be otherwise unaware of the new more complete property.

    TZDEFINITION

    This structure fully describes a timezone with multiple rules.

    The wFlags in the TZDEFINITION structure indicates if the guid and/or the key name fields are valid. For now, TZDEFINITION_FLAG_VALID_GUID should not be set. Instead, TZDEFINITION_FLAG_VALID_KEYNAME should be set and the key name field should be set to the registry key name for the timezone. These registry key names should not be localized and have a maximum size of MAX_PATH.

    If two structures both have guids, then guids are the preferred way to determine if two timezones are the same. If one does not have a guid, then the keyname is used to determine if the timezones are the same. There MUST be at least a keyname.

    The first rule in the list or rules is special and is considered to be the rule to use until the second rule starts regardless of the stStart on the first rule.

    The rules should be sorted from oldest to newest. There is no overlap allowed between rules and so a prior rule is deemed to end when a new rule starts. Also, there MUST be at least one rule.

    Persisting TZREG to a stream

    Care must be taken when persisting TZREG to a stream for commitment to a binary property. The following describes the little endian format for persisting the structure

    long        lBias;           // offset from GMT.
    long        lStandardBias;   // offset from bias during standard time.
    long        lDaylightBias;   // offset from bias during daylight time.
    WORD        wReserved1;      // reserved
    SYSTEMTIME  stStandardDate;  // time to switch to standard time.
    WORD        wReserved2;      // reserved
    SYSTEMTIME  stDaylightDate;  // time to switch to daylight time.


    Note that the reserved WORD members do not map to any component of the TZREG structure. When parsing the persisted form of the structure, they should be ignored. When writing the persisted form of the structure, they should be NULL.

    Persisting TZDEFINITION to a stream

    Care must be taken when persisting TZDEFINITION to a stream for commitment to a binary property. The following describes the little endian format for persisting the structure

    BYTE  bMajorVersion;  // breaking change
    BYTE  bMinorVersion;  // extensibility
    WORD  cbHeader;       // size of following data up until rule data
    WORD  wFlags;         // TZDEFINITION_FLAG
    
    if (TZDEFINITION_FLAG_VALID_GUID) 
        GUID  guid;                  // guid
    
    if (TZDEFINITION_FLAG_VALID_KEYNAME) 
        WORD   cchKeyName;           // does not include null char
        WCHAR  rgchKeyName;          // not null terminated
    
    WORD  cRules;                    // number of rules
    
    for each rule
        BYTE        bMajorVersion;   // breaking change
        BYTE        bMinorVersion;   // extensibility
        WORD        cbRule;          // size of following data
        WORD        wFlags;          // flags
        SYSTEMTIME  stStart;         // GMT when this rule started
    // Following are the fields of the TZREG sub structure
        long        lBias;           // offset from GMT.
        long        lStandardBias;   // offset from bias during standard time.
        long        lDaylightBias;   // offset from bias during daylight time.
        SYSTEMTIME  stStandardDate;  // time to switch to standard time.
        SYSTEMTIME  stDaylightDate;  // time to switch to daylight time.

    If the appropriate TZEFINITION_FLAG_VALID_GUID is not set, then the guid is not present in the stream. Likewise the key name is also not present unless the appropriate flag is set, although the key name should be persisted for the foreseeable future. The key name will have a maximum size of MAX_PATH.

    If a parser does not understand the major version of the header, it should not read the rest of the structure and behave as if the data is missing.

    If a parser does not understand the minor version of the header, it should read the portions of the stream that it understands, and should use the cbHeader to skip past the portions that it does not understand.

    If a parser does not recognize the major version of a rule, the client should skip past the future rule using the cbRule, and then try to parse the rule it was looking for at as if it were at the next location.

    If a parser does not recognize the minor version of a block, the client should only parse the parts of the rule that it understands, and use cbHeader/cbRule to skip past the data it does not understand.

    When persisting blocks back as a modification, a parser should not try to write any information it does not understand – it should wipe out the information it does not understand.

    There is a limit to the number of rules, being 1024.

    The major version number is used to make a breaking change. Clients that are unfamiliar with the major version number should treat the property as if it is not there. Clients writing the structure should use TZ_BIN_VERSION_MAJOR.

    The minor version number is used for extensibility. Clients that are unfamiliar with the minor version number should read the data that they understand, and skip over the data that might be appended to each rule or the overall stream. Clients writing the structure should use TZ_BIN_VERSION_MINOR.

    Note that the TZREG structure is persisted here differently than when persisted alone, so the same code cannot be used to parse it.

    Example code

    The following code samples illustrate one way to read the TZREG and TZDEFINITION structures from their persisted formats: Code for writing to the perisisted formats is not provided but should be easily derived from the provided examples.

    // Allocates return value with new.
    // clean up with delete. 
    TZREG* BinToTZREG(ULONG cbReg, LPBYTE lpbReg) 
    {
        if (!lpbReg) return NULL; 
    
        // Update this if parsing code is changed! 
        if (cbReg < 3*sizeof(long) + 2*sizeof(WORD) + 2*sizeof(SYSTEMTIME)) return NULL;
    
        TZREG tzReg = {0};
        LPBYTE lpPtr = lpbReg;
    
        tzReg.lBias = *((long*)lpPtr);
        lpPtr += sizeof(long);
        tzReg.lStandardBias = *((long*)lpPtr);
        lpPtr += sizeof(long);
        tzReg.lDaylightBias = *((long*)lpPtr);
        lpPtr += sizeof(long);
        lpPtr += sizeof(WORD);// reserved
    
        tzReg.stStandardDate = *((SYSTEMTIME*)lpPtr);
        lpPtr += sizeof(SYSTEMTIME);
        lpPtr += sizeof(WORD);// reserved
        tzReg.stDaylightDate = *((SYSTEMTIME*)lpPtr);
        lpPtr += sizeof(SYSTEMTIME);
    
        TZREG* ptzReg = NULL;
        ptzReg = new TZREG;
        if (ptzReg)
        {
            *ptzReg = tzReg;
        }
    
        return ptzReg;
    }
    // Allocates return value with new.
    // clean up with delete[].
    TZDEFINITION* BinToTZDEFINITION(ULONG cbDef, LPBYTE lpbDef)
    {
        if (!lpbDef) return NULL;
    
        // Update this if parsing code is changed!
        // this checks the size up to the flags member
        if (cbDef < 2*sizeof(BYTE) + 2*sizeof(WORD)) return NULL;
    
        TZDEFINITION tzDef = {0};
        TZRULE* lpRules = NULL;
        LPBYTE lpPtr = lpbDef;
        WORD cchKeyName = NULL;
        WCHAR* szKeyName = NULL;
        WORD i = 0;
    
        BYTE bMajorVersion = *((BYTE*)lpPtr);
        lpPtr += sizeof(BYTE);
        BYTE bMinorVersion = *((BYTE*)lpPtr);
        lpPtr += sizeof(BYTE);
    
        // We only understand TZ_BIN_VERSION_MAJOR
        if (TZ_BIN_VERSION_MAJOR != bMajorVersion) return NULL;
    
        // We only understand if >= TZ_BIN_VERSION_MINOR
        if (TZ_BIN_VERSION_MINOR > bMinorVersion) return NULL;
    
        WORD cbHeader = *((WORD*)lpPtr);
        lpPtr += sizeof(WORD);
    
        tzDef.wFlags = *((WORD*)lpPtr);
        lpPtr += sizeof(WORD);
    
        if (TZDEFINITION_FLAG_VALID_GUID & tzDef.wFlags)
        {
            if (lpbDef + cbDef - lpPtr < sizeof(GUID)) return NULL;
            tzDef.guidTZID = *((GUID*)lpPtr);
            lpPtr += sizeof(GUID);
        }
    
        if (TZDEFINITION_FLAG_VALID_KEYNAME & tzDef.wFlags)
        {
            if (lpbDef + cbDef - lpPtr < sizeof(WORD)) return NULL;
            cchKeyName = *((WORD*)lpPtr);
            lpPtr += sizeof(WORD);
            if (cchKeyName)
            {
                if (lpbDef + cbDef - lpPtr < (BYTE)sizeof(WORD)*cchKeyName) return NULL;
                szKeyName = (WCHAR*)lpPtr;
                lpPtr += cchKeyName*sizeof(WORD);
            }
        }
    
        if (lpbDef+ cbDef - lpPtr < sizeof(WORD)) return NULL;
        tzDef.cRules = *((WORD*)lpPtr);
        lpPtr += sizeof(WORD);
        if (tzDef.cRules)
        {
            lpRules = new TZRULE[tzDef.cRules];
            if (!lpRules) return NULL;
    
            LPBYTE lpNextRule = lpPtr;
            BOOL bRuleOK = false;
    		
            for (i = 0;i < tzDef.cRules;i++)
            {
                bRuleOK = false;
                lpPtr = lpNextRule;
    			
                if (lpbDef + cbDef - lpPtr < 
                    2*sizeof(BYTE) + 2*sizeof(WORD) + 3*sizeof(long) + 2*sizeof(SYSTEMTIME)) return NULL;
                bRuleOK = true;
                BYTE bRuleMajorVersion = *((BYTE*)lpPtr);
                lpPtr += sizeof(BYTE);
                BYTE bRuleMinorVersion = *((BYTE*)lpPtr);
                lpPtr += sizeof(BYTE);
    			
                // We only understand TZ_BIN_VERSION_MAJOR
                if (TZ_BIN_VERSION_MAJOR != bRuleMajorVersion) return NULL;
    			
                // We only understand if >= TZ_BIN_VERSION_MINOR
                if (TZ_BIN_VERSION_MINOR > bRuleMinorVersion) return NULL;
    			
                WORD cbRule = *((WORD*)lpPtr);
                lpPtr += sizeof(WORD);
    			
                lpNextRule = lpPtr + cbRule;
    			
                lpRules[i].wFlags = *((WORD*)lpPtr);
                lpPtr += sizeof(WORD);
    			
                lpRules[i].stStart = *((SYSTEMTIME*)lpPtr);
                lpPtr += sizeof(SYSTEMTIME);
    			
                lpRules[i].TZReg.lBias = *((long*)lpPtr);
                lpPtr += sizeof(long);
                lpRules[i].TZReg.lStandardBias = *((long*)lpPtr);
                lpPtr += sizeof(long);
                lpRules[i].TZReg.lDaylightBias = *((long*)lpPtr);
                lpPtr += sizeof(long);
    			
                lpRules[i].TZReg.stStandardDate = *((SYSTEMTIME*)lpPtr);
                lpPtr += sizeof(SYSTEMTIME);
                lpRules[i].TZReg.stDaylightDate = *((SYSTEMTIME*)lpPtr);
                lpPtr += sizeof(SYSTEMTIME);
            }
            if (!bRuleOK)
            {
                delete[] lpRules;
                return NULL;			
            }
        }
        // Now we've read everything - allocate a structure and copy it in
        size_t cbTZDef = sizeof(TZDEFINITION) +
            sizeof(WCHAR)*(cchKeyName+1) +
            sizeof(TZRULE)*tzDef.cRules;
    
        TZDEFINITION* ptzDef = (TZDEFINITION*) new BYTE[cbTZDef];
        
        if (ptzDef)
        {
            // Copy main struct over
            *ptzDef = tzDef;
            lpPtr = (LPBYTE) ptzDef;
            lpPtr += sizeof(TZDEFINITION);
    
            if (szKeyName)
            {
                ptzDef->pwszKeyName = (WCHAR*)lpPtr;
                memcpy(lpPtr,szKeyName,cchKeyName*sizeof(WCHAR));
                ptzDef->pwszKeyName[cchKeyName] = 0;
        
                lpPtr += (cchKeyName+1)*sizeof(WCHAR);
            }
    
            if (ptzDef -> cRules)
            {
                ptzDef -> rgRules = (TZRULE*)lpPtr;
                for (i = 0;i < ptzdef -> cRules;i++)
                {
                    ptzDef -> rgRules[i] = lpRules[i];
                }
            }
        }
        delete[] lpRules;
    
        return ptzDef;
    }

    The following code sample illustrates how the above sample code might be used to read timezone properties from an appointment:

    void ReadTimeZones(LPMAPIPROP lpAppointment)
    {
        HRESULT hRes = S_OK;
        LPSPropTagArray lpNamedPropTags = NULL;
        MAPINAMEID NamedID[2] = {0};
        LPMAPINAMEID lpNamedID[2];
        lpNamedID[0] = &NamedID[0];
        lpNamedID[1] = &NamedID[1];
        NamedID[0].lpguid = (LPGUID)&PSETID_Appointment;
        NamedID[0].ulKind = MNID_ID;
        NamedID[0].Kind.lID = dispidTimeZoneStruct;
        NamedID[1].lpguid = (LPGUID)&PSETID_Appointment;
        NamedID[1].ulKind = MNID_ID;
        NamedID[1].Kind.lID = dispidApptTZDefStartDisplay;
        hRes = lpAppointment->GetIDsFromNames(
            2,
            lpNamedID,
            NULL,
            &lpNamedPropTags);
        if (SUCCEEDED(hRes) && lpNamedPropTags)
        {
            SizedSPropTagArray(2,sptaTzProps) = {2,
                CHANGE_PROP_TYPE(lpNamedPropTags->aulPropTag[0],PT_BINARY),
                CHANGE_PROP_TYPE(lpNamedPropTags->aulPropTag[1],PT_BINARY),
            };
            LPSPropValue lpProps = NULL;
            ULONG cProps = 0;
            hRes = lpAppointment->GetProps(
                (LPSPropTagArray)&sptaTzProps,
                NULL,
                &cProps,
                &lpProps);
            if (SUCCEEDED(hRes) && 2 == cProps && lpProps)
            {
                if (PT_BINARY == PROP_TYPE(lpProps[0].ulPropTag))
                {
                    TZREG* ptzReg = BinToTZREG(lpProps[0].Value.bin.cb,lpProps[0].Value.bin.lpb);
                    // TODO: Do something with ptzReg
                    delete ptzReg;
                }
                if (PT_BINARY == PROP_TYPE(lpProps[1].ulPropTag))
                {
                    TZDEFINITION* ptzDef = BinToTZDEFINITION(lpProps[1].Value.bin.cb,lpProps[1].Value.bin.lpb);
                    // TODO: Do something with ptzDef
                    delete[] ptzDef;
                }
               }
            MAPIFreeBuffer(lpProps);
        }
        MAPIFreeBuffer(lpNamedPropTags);
    }// ReadTimeZones

    Remarks

    Understanding of these properties should not be needed for appointments which have been rebased by our tool or created with Outlook 2007 or higher or CDO 1.21 after the upcoming time zone update. These properties only need to be looked at by developers writing tools to rebase appointments. The suggested approach for rebasing an appointment is:

    - Using CDO or the Outlook Object Model, examine the time and recurrence information on an appointment to determine if the appointment is a candidate for rebasing. If necessary, present information to the user to allow them to decide.

    - Using CDO or the Outlook Object Model, write the new time and recurrence information.

    - Stamp the appropriate time zone information into dispidApptTZDefStartDisplay, dispidApptTZDefEndDisplay, and dispidApptTZDefRecur.

Page 1 of 1 (4 items)
  • © 2012 Microsoft Corporation.
  • Terms of Use
  • Trademarks
  • Privacy Statement
  • Report Abuse
  • 5.6.402.223