Bit Fields and the SMS_Advertisement Class

I recently worked on an issue involving a customer attempting to create a mandatory Advert using a C# application. The advertisement worked but wasn't mandatory even though it looked to be in the SMS Admin console. There was also some confusion as to how the bit field properties work and why the advert wasn't mandatory. The customer had noticed that the TimeFlags value in the SMS_Advertisement class was different in their custom application than one created by the SMS Admin Console. I sent the following explanation which I wanted to share:

Dear <customer>,

I can't debug your custom application but I did end up having to create a test application (code attached) that emulates what the <custom> application does so I could see what your instance of SMS_Advertisement looks like.  After viewing the differences I saw that an advert created in the SMS Admin console, and one created with the <custom> application, were different and it wasn't just the TimeFlags property.  The SMS Admin console uses the SDK just like your program so it is just a matter of seeing that there is a difference between the two classes and informing the developer of the custom application of the difference so they can correct it. 

There was also some confusion over how the TimeFlags property works so I explain this below:

SMS uses bit fields for lots of on/off settings.  The TimeFlags property is no exception.  The TimeFlags property is a bit field, which is actually just a set of boolean flags.  By default, when a regular mandatory advertisement is created through the SMS Admin console, a decimal value of 8209 is assigned to the TimeFlags property.  8209 converted to binary is "10000000010001".  This is the important number.

Here is what the SDK says about the TimeFlags property:

TimeFlags
Datatype: uint32
Access type: Read-only
Qualifiers: Bits

Reserved for internal use. Duplicates the information in the time-related properties. For example, ENABLE_PRESENT is set when PresentTimeEnabled equals TRUE. Bit flags are as follows:

ENABLE_PRESENT (bit 0 = 1)
ENABLE_EXPIRATION (bit 1 = 2)
ENABLE_AVAILABLE (bit 2 = 4)
ENABLE_UNAVAILABLE (bit 3 = 8)
ENABLE_MANDATORY (bit 4 = 16)
GMT_PRESENT (bit 8 = 256)
GMT_EXPIRATION (bit 9 = 512)
GMT_AVAILABLE (bit 10 = 1024)
GMT_UNAVAILABLE (bit 11 = 2048)
GMT_MANDATORY (bit 12 = 4096)

So now we know what the real settings are.  You count from right to left of the binary number ignoring the first bit.  As you can see 8209 really means ENABLE_MANDATORY.  In the case of 8193 (10000000000001) none of the bits are on.  I wrote a test application that emulates what the <custom> program does (I couldn't view the differences between an advert created by the console and one by their application without doing this) and also created a test mandatory advert using the SMS admin console.  I compared the potentially relevant differences and those are noted below:

AdvertFlags Property:
<customer> App. is set to 67108864 (100000000000000000000000000)
Admin Console is set to 0
*This shouldn't matter since none of the flags are enabled anyway

AssignedScheduleEnabled Property:
<custom> App. is set to False
Admin Console is set to True
Per SDK: Indicates whether the schedule defined in AssignedSchedule is active. The default value is FALSE.

I changed the test application by setting the AssignedScheduleEnabled property and it started working and the TimeFlags property was now set to ENABLE_MANDATORY.  Basically, the problem appears to be that the advertisement created with the <custom> program isn't ever being set to mandatory.

Thanks,

Russ

Here is the code in the test application that I wrote to emulate what the customer was doing:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SystemsManagementServer.Automation;

namespace CreateAdvert
{
    class Program
    {
        static void Main(string[] args)
        {
            SMSProvider oProvider = new SMSProvider("server", "administrator", "password");
            SMSPackage oPackage = oProvider.Packages.Get("CEN00001");
            SMSProgram oProgram = oPackage.Programs.Create("Notepad", "notepad.exe");
            oProgram.AllowUserToInteractWithProgram = true;
            oProgram.RunMode = ProgramRunModes.Normal;
            oProgram.RunOnAnyPlatform = true;
            oProgram.UserRequirements = UserRequirementsFlags.OnlyRunWhenUserLoggedOn;
            oProgram.Save();
            SMSCollection oCollection = oProvider.Collections.Get("SMS00001");
            SMSAdvertisement oAdvert = oProvider.Advertisements.Create("Test", oCollection, oProgram);

            oAdvert.Comment = "Test";
            oAdvert.PresentTimeEnabled = true;
            DateTime now = DateTime.Now;
            oAdvert.PresentTime = now;
            DateTime tomorrow = DateTime.Now.AddDays(1);
            oAdvert.IncludeSubCollections = false;
            oAdvert.MandatoryOverSlowLinks = true;
            oAdvert.WhenLocalDPAvailable = LocalDPOptions.DownloadFromDistributionPoint;
            oAdvert.AssignedScheduleEnabled = true; //this is what was missing

            DateTime dtTenMin = DateTime.Now.AddMinutes(10);
            SMSScheduleToken_NonRecurring tokNonRec = new SMSScheduleToken_NonRecurring(new DateTime(dtTenMin.Year, dtTenMin.Month, dtTenMin.Day, dtTenMin.Hour, dtTenMin.Minute, dtTenMin.Second));
            oAdvert.AssignedSchedules.Add(tokNonRec);
            DateTime later = DateTime.Now.AddYears(1);
            oAdvert.ExpirationTime = new DateTime(later.Date.Year, later.Date.Month, later.Date.Day, 5, 0, 0);
       
            oAdvert.Save();
        }
    }
}