Streaming Files (for Upload/Download) in WCF (Message Contracts)

I recently had to write some code to perform an upload to a WCF service, and there was a chance that the files could be a touch on the large side so streaming seemed like the best option.

There is quite a limited amount of information about this subject – and configuring the web config is a bit tricky, so I have posted some examples of how to do this in the hope that someone will find it useful..

You need to start by defining some message contracts for your upload / download.. these need to be defined in the  interface for your WCF service (the file that contains the definitions and contracts for your service) :-

[MessageContract]
public class FileUploadMessage
    {
        [MessageHeader(MustUnderstand = true)]
        public PublishingMetaData Metadata;
        [MessageHeader(MustUnderstand = true)]
        public string AuthenticationKey;
        [MessageBodyMember(Order = 1)]
        public Stream FileByteStream;
    }

    [MessageContract]
    public class FileDownloadMessage
    {
        [MessageHeader(MustUnderstand = true)]
        public PublishingMetaData FileMetaData;
        [MessageHeader(MustUnderstand = true)]
        public string AuthenticationKey;
    }

[MessageContract]
    public class FileDownloadReturnMessage
    {
        public FileDownloadReturnMessage(PublishingMetaData metaData, Stream stream)
        {
            this.DownloadedFileMetadata = metaData;
            this.FileByteStream = stream;
        }

        [MessageHeader(MustUnderstand = true)]
        public PublishingMetaData DownloadedFileMetadata;
        [MessageBodyMember(Order = 1)]
        public Stream FileByteStream;
    }

Notice  that for the FileUploadMessage I have included a Stream.. the PublishingMetaData and AuthenticationKey are custom classes / properties and don’t need to be implemented in your version.

I also need to define a couple of web methods in the interface which are used for uploading / downloading files :-

[OperationContract(IsOneWay = false)]
        FileDownloadReturnMessage DownloadFile(FileDownloadMessage request);

[OperationContract(IsOneWay = true)]
        void UploadFile(FileUploadMessage request);

[OperationContract]
        void AttemptToCloseStream(string authenticationKey, PublishingMetaData metaData);

Now we have defined our contracts – here is the implementation (which is contained in the main WCF service class).. I have left my security checking and various other custom code in for illustration purposes, but again this can be removed in your implementation :-

public void UploadFile(FileUploadMessage request)
        {
            if (!CheckAuthenticationKey(request.AuthenticationKey)) { throw new SecurityException("The user does not have access."); }
            Stream fileStream = null;
            Stream outputStream = null;

            try
            {
                fileStream = request.FileByteStream;

                string rootPath = ConfigurationManager.AppSettings["RootPath"].ToString();

                DirectoryInfo dirInfo = new DirectoryInfo(rootPath);
                if (!dirInfo.Exists)
                {
                    dirInfo.Create();
                }

                //Create the file in the filesystem - change the extension if you wish, or use a passed in value from metadata ideally
                string newFileName = Path.Combine(rootPath, Guid.NewGuid() + ".xml");

                outputStream = new FileInfo(newFileName).OpenWrite();
                const int bufferSize = 1024;
                byte[] buffer = new byte[bufferSize];

                int bytesRead = fileStream.Read(buffer, 0, bufferSize);

                while (bytesRead > 0)
                {
                    outputStream.Write(buffer, 0, bufferSize);
                    bytesRead = fileStream.Read(buffer, 0, bufferSize);
                }
            }
            catch (IOException ex)
            {
                throw new FaultException<IOException>(ex, new FaultReason(ex.Message));
            }
            finally
            {
                if (fileStream != null)
                {
                    fileStream.Close();
                }
                if (outputStream != null)
                {
                    outputStream.Close();
                }
            }
        }

And here is my download implementation (notice the use of a list of OpenStreams.. this is a workaround to fix a problem I was having with streams being left open .. I use this to allow my program to call the service and ensure the stream is closed after the file is downloaded) :-

static Dictionary<string, Stream> OpenStreams { get; set; }

public FileDownloadReturnMessage DownloadFile(FileDownloadMessage request)
        {
            try
            {
                if (!CheckAuthenticationKey(request.AuthenticationKey)) { throw new SecurityException("The user does not have access."); }
                string rootPath = ConfigurationManager.AppSettings["RootPath"].ToString();
                Stream fileStream = new FileStream(Path.Combine(rootPath, Path.GetFileName(request.FileMetaData.FileName)), FileMode.Open);
                if (ExecutionResearchService.OpenStreams == null)
                {
                    ExecutionResearchService.OpenStreams = new Dictionary<string, Stream>();
                }
                ExecutionResearchService.OpenStreams.Add(Path.GetFileName(request.FileMetaData.FileName), fileStream);
                return new FileDownloadReturnMessage(new PublishingMetaData(), fileStream);
            }
            catch (IOException ex)
            {
                throw new FaultException<IOException>(ex, new FaultReason(ex.Message));
            }
        }

public void AttemptToCloseStream(string authenticationKey, PublishingMetaData metaData)
        {
            if (!CheckAuthenticationKey(authenticationKey)) { throw new SecurityException("The user does not have access."); }
            if (ExecutionResearchService.OpenStreams != null)
            {
                if (ExecutionResearchService.OpenStreams.ContainsKey(Path.GetFileName(metaData.FileName)))
                {
                    Stream stream = ExecutionResearchService.OpenStreams[Path.GetFileName(metaData.FileName)];
                    stream.Flush();
                    stream.Close();
                    OpenStreams.Remove(Path.GetFileName(metaData.FileName));
                }
            }
        }

OK – so now we have a file upload method, download method, and the required contracts we need. The services section of the web config looks like this :-

<system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="serviceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true" httpHelpPageEnabled="true" />
          <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <!--http://services.myserviceaddress.com/service.svc-->
      <service behaviorConfiguration="serviceBehavior" name="Projects.MyServiceName">
        <endpoint address="http://services.myserviceaddress.com/service.svc"
        name="basicHttpStream"
        binding="basicHttpBinding"
        bindingConfiguration="httpLargeMessageStream"
        contract="Projects.IMyServiceInterface" />
        <host>
          <baseAddresses>
            <add baseAddress="http://services.myserviceaddress.com/service.svc" />
            <!--<add baseAddress="http://localhost/ExecutionResearchService/ExecutionResearchService.svc" />-->
          </baseAddresses>
        </host>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <bindings>
      <basicHttpBinding>
        <binding name="httpLargeMessageStream"
                    maxReceivedMessageSize="2147483647"
                    transferMode="Streamed"
                    messageEncoding="Mtom" />
      </basicHttpBinding>
    </bindings>
  </system.serviceModel>

The important bits are the binding section at the bottom – transferMode = “Streamed” and messageEncoding = “Mtom” .. also I have set the maxReceivedMessageSize to it’s maximum value to ensure I can transfer massive files across my web service without issues.

Now – once these are set up and working in your WCF Service – we can add a reference to it and call the methods using our client application.. here is some code on how to do this too, because I found help lacking in this area also!

This is how we upload a file – please change variables, and remove AuthenticationKey and the MetaData objects if you didn’t use them.

using (ResearchServiceClient client = WebServiceProxy.GetResearchServiceClient())
{
    Stream fileStream = null;
 
    try
    {
        string rootPath = @"C:\MyRootFolder";
        string localDocumentPath = Path.Combine(rootPath, "MyNewFileName.xml");
        fileStream = new FileInfo(localDocumentPath).OpenRead();
        client.UploadFile(WebServiceProxy.AuthenticationKey, LivePaths.WorkingPublishingMetaData, fileStream);
 
        byte[] buffer = new byte[2048];
        int bytesRead = fileStream.Read(buffer, 0, 2048);
        while (bytesRead &gt; 0)
        {
            fileStream.Write(buffer, 0, 2048);
            bytesRead = fileStream.Read(buffer, 0, 2048);
        }
    }
    catch
    {
        throw;
    }
    finally
    {
        if (fileStream != null)
        {
            fileStream.Close();
        }
    }
}

using (ResearchServiceClient client = WebServiceProxy.GetResearchServiceClient())
{
    Stream fileStream = null;
    client.DownloadFile(WebServiceProxy.AuthenticationKey, metaData, out fileStream);
 
    Stream outputStream = null;
 
    try
    {
		outputStream = new FileInfo("PathForLocalDocument.xml").OpenWrite();
        byte[] buffer = new byte[2048];
 
        int bytesRead = fileStream.Read(buffer, 0, 2048);
 
        while (bytesRead &gt; 0)
        {
            outputStream.Write(buffer, 0, 2048);
            bytesRead = fileStream.Read(buffer, 0, 2048);
        }
    }
    catch
    {
 
    }
    finally
    {
        if (fileStream != null)
        {
            fileStream.Close();
        }
        if (outputStream != null)
        {
            outputStream.Close();
        }
        client.AttemptToCloseStream(WebServiceProxy.AuthenticationKey, metaData);
    }
}

So – here’s the total solution, and notice we put the client.AttemptToCloseStream in the finally section of our Try / Catch which attempts to close the download stream when we have finished with it. It seems like a little bit of a hack, but I scratched my head trying to find a solution to this, and this is the best thing I could come up with.. it works, so it isn’t that bad.

Delivery Notifications in .NET MailMessage

If you ever need to get delivery reports when you send emails from within .NET – here is a nice code snippet which does it. I used this approach to stamp emails with a “Mail-CustomData” header which contained information leading back to a database entity. I then watched the mailReplyToAddress with a small program I wrote and updated the database when the message was received, or failed to deliver.

            MailMessage message = new MailMessage();
            message.From = new MailAddress("DoNotReply@EmailAddress.com");
            message.ReplyToList.Add(new MailAddress(mailReplyToAddress));
            message.Headers.Add("Disposition-Notification-To", mailReplyToAddress);

            message.To.Add(new MailAddress(emailAddress));
            message.IsBodyHtml = true;
            message.Priority = MailPriority.High;
            message.Body = "This is the message.";
            message.Subject = "Mail Message With Delivery Reports";

            message.Headers.Add("Mail-CustomData", "1203495Dflgrk3012");

            message.DeliveryNotificationOptions = DeliveryNotificationOptions.OnSuccess | DeliveryNotificationOptions.OnFailure | DeliveryNotificationOptions.Delay;

            System.Net.Mail.SmtpClient client = new SmtpClient();
            client.Send(message);

Visual Studio 2010 Intellisense

Is anyone else having problems with Visual Studio 2010 intellisense? I find myself constantly having to guess the name of properties and events in my web controls (mostly UserControls, but also ASP.NET Ajax controls) and it’s starting to get to me!

I haven’t seen anyone complaining about this yet, so I thought it was definitely about time someone did.

Heres some sites I have been working on recently :- adding the links as a test to see if it makes any difference with the SEO (PS. It does!)

www.dougmyallblinds.com / www.blindsjersey.co.uk

www.phatlicks.com

www.perfectdog.co.uk

www.animals-on-camera.com

Searching Directories With Linq

Haven’t posted for quite a while – reason being is that I started a new job with a stockbroker in London and have been very busy. Here’s the first of many new posts on useful .NET code snippets.

Recursively looking through directories for files has always been a bit of a fiddle – here’s a nice easy way to do it with Linq.

NB. I use a custom object here called “FileDefinition”.. this can be swapped out to yield return the FileInfo object itself.. just replace :-

yield return new filedefinition;
with
yield return f;

        public static IEnumerable<FileDefinition> SearchFiles(DirectoryInfo directoryinf)
        {
            foreach (var f in directoryinf.GetFiles())
                yield return new FileDefinition(MapUrl(f.DirectoryName), f.DirectoryName, f.Name, f.Length);

            foreach (DirectoryInfo d in directoryinf.GetDirectories())
            {
                //Recursevly go over all the other directories
                foreach (var f in SearchFiles(d))
                    yield return f;
            }
        }

        public static IEnumerable<FileDefinition> SearchFilesByExtention(DirectoryInfo directoryinf, string extention)
        {
            //Return the files first
            foreach (string singleExtension in ParseExtensions(extention))
            {
                foreach (var f in directoryinf.GetFiles().Where(file => file.Extension == singleExtension))
                    yield return new FileDefinition(MapUrl(f.DirectoryName), f.DirectoryName, f.Name, f.Length);
            }

            foreach (DirectoryInfo d in directoryinf.GetDirectories())
            {
                //Recursevly go over all the other directories
                foreach (var f in SearchFilesByExtention(d, extention))
                    yield return f;
            }
        }

        public static string[] ParseExtensions(string extention)
        {
            string[] strings = extention.Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries);
            return strings;
        }

How to get album art from Amazon Web Services

OK – After a few people have asked .. I have finally rewritten the Amazon Web Services tool to use my newly updated code. It’s a fresh rewrite with some special wordlist matching code, and it seems to work nicely.

It is available here as a Windows Installer (.msi) package

http://downloads.seesharpdot.net/files/CoverArtInstaller.msi

So – all you need to do is install it, then click Browse to find the directory that you use to store all of your movie files. The extension doesn’t matter as the program does not actually open the files, so they can be any type of movie file. Change the file mask to match the files you want to search for (*.avi *.mov), each separated with a space .. then click “Create Images”. The program will now attempt to create a .jpg cover with the same name as the movie file in that folder but with the .jpg extension.. if it cannot find a match it will try some clever searching techniques to try and find one, and eventually give up.

Vista and Windows 7 users – make sure you run with the correct privileges (right click and run as administrator), or you may not be able to create the files in your target location.

File naming hints – if you choose file names that match the name of the movie, you have the best chance of success – if it ends in random tags and words, the program will remove them from the search if they aren’t in the dictionary.. but ultimately it is just guessing, as it doesn’t know the real name of the film.

Enjoy! If you have any comments or suggestions, please register and leave them using the form. Don’t forget to buy me a coffee if you like the application. If I get enough fuel (coffee), I will make the next incarnation more interactive, and the “Manual Mode” will allow you to choose the images yourself if several are returned.

Creating a DataTable from a CSV input string (Comma Separate File)

Recently, I needed to create a data table from a CSV data stream coming back from a Blackberry web service, which was basically a wrapper I wrote for the blackberry client tools executable. The client tools executable returns a string of comma separated values which consists of a table definition, followed by a new line, then each row in CSV format also separated with a new line.

The following static class is the culmination of my efforts – It basically looks at a the described string and returns a nicely formatted data table. SplitString(string) does the actual splitting on the CSV – first method is quite specific to the task I had and can be modified depending on the stream you are passing in.

        private DataTable CreateTableFromOutputStream(string outputStreamText, string tableName)
        {
            //Process output and return
            string[] split = outputStreamText.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
 
            if (split.Length >= 2)
            {
                int iteration = 0;
                DataTable table = null;
 
                foreach (string values in split)
                {
                    if (iteration == 0)
                    {
                        string[] columnNames = SplitString(values);
                        table = new DataTable(tableName);
 
                        List<DataColumn> columnList = new List<DataColumn>();
 
                        foreach (string columnName in columnNames)
                        {
                            columnList.Add(new DataColumn(columnName));
                        }
 
                        table.Columns.AddRange(columnList.ToArray());
                    }
                    else
                    {
                        string[] fields = SplitString(values);
                        if (table != null)
                        {
                            table.Rows.Add(fields);
                        }
                    }
 
                    iteration++;
                }
 
                return table;
            }
 
            return null;
        }
 
        private string[] SplitString(string inputString)
        {
            System.Text.RegularExpressions.RegexOptions options = ((System.Text.RegularExpressions.RegexOptions.IgnorePatternWhitespace | System.Text.RegularExpressions.RegexOptions.Multiline)
                        | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            Regex reg = new Regex("(?:^|,)(\\\"(?:[^\\\"]+|\\\"\\\")*\\\"|[^,]*)", options);
            MatchCollection coll = reg.Matches(inputString);
            string[] items = new string[coll.Count];
            int i = 0;
            foreach (Match m in coll)
            {
                items[i++] = m.Groups[0].Value.Trim('"').Trim(',').Trim('"').Trim();
            }
            return items;
        }

How to get album, dvd, and blueray cover art from Amazon [UPDATED]

[UPDATED! – I have updated this post to use the new WCF style web services – this works a lot better]

A friend of mine recently asked if it was possible to take a list of movies which he had on his hard drive, and to use amazon web services to find the correct cover art thumbnails based on the movie’s filename.

I looked into this for him, and it turned out that Amazon require you as a developer to register with them before using any of their services.. you can do that here:-

Product Advertising API home page

Now, once you have an account with them.. using the web service is relatively easy. The code calls upon the service reference in my proxy project. You need to add a service reference to the following service :-

http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl

Visual studio takes care of the creation of objects so you can start using this reference immediately.

I wrote myself a small windows application which selects folders to search in on the local machine, and then outputs the images to disk. It calls a separate project in my application (a “proxy” project which contains references to the web service WSDL) and passes it’s arguments across.

List<Image> images = AmazonGateway.SearchAlbumArt(Path.GetFileNameWithoutExtension(fileInfo.Name));

So, this AmazonHelper class also resides in my proxy project.. the method it calls looks like this :-


NB. Please note that this code block now adds 3 new classes – I believe these are provided by Amazon in an code example somewhere.. they are called AmazonHeader, AmazonSigningMessageInspector, and AmazonSigningEndpointBehaviour. They basically add the correct signatures to the web service requests so that they comply with Amazon’s strict security policy which was enforced recently. I didn’t write these.

namespace AmazonService.Proxy

{

    public static class AmazonGateway

    {

        private const string privateKey = "MYPRIVATEKEY (Secret key)";

        private const string publicKey = "MYPUBLICKEY (AWSAccessKey)";

 

        public static List<System.Drawing.Image> SearchAlbumArt(string movieKeywords)

        {

            string filteredKeywords = string.Empty;

 

            foreach (char c in movieKeywords)

            {

                if (char.IsLetterOrDigit(c))

                {

                    filteredKeywords += c;

                }

                else

                {

                    filteredKeywords += " ";

                }

            }

 

            filteredKeywords = filteredKeywords.Trim();

 

            ItemSearchRequest itemRequest = new ItemSearchRequest();

            itemRequest.Keywords = filteredKeywords;

            itemRequest.SearchIndex = "DVD"; // dvd’s only

            itemRequest.ResponseGroup = new string[] { "Images" }; // images only

 

            ItemSearch request = new ItemSearch();

            request.AWSAccessKeyId = publicKey;

            request.Request = new ItemSearchRequest[] { itemRequest };

 

            BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);

            binding.MaxReceivedMessageSize = int.MaxValue;

            AWSECommerceServicePortTypeClient client = new AWSECommerceServicePortTypeClient(

                binding,

                new EndpointAddress("https://webservices.amazon.com/onca/soap?Service=AWSECommerceService"));

 

            // add authentication to the ECS client 

            client.ChannelFactory.Endpoint.Behaviors.Add(new AmazonSigningEndpointBehavior(publicKey, privateKey));  

 

            ItemSearchResponse response = client.ItemSearch(request);

 

            // Determine if book was found

            bool itemFound = ((response.Items[0].Item != null)

                && (response.Items[0].Item.Length > 0));

            if (itemFound)

            {

                List<System.Drawing.Image> images = new List<System.Drawing.Image>();

 

                foreach (Item currItem in response.Items[0].Item)

                {

                    try

                    {

                        images.Add(ConvertByteArrayToImage(

                            GetBytesFromUrl(currItem.LargeImage.URL)));

                    }

                    catch { }

                }

 

                return images;

            }

            else

            {

                return null;

            }

        }

 

        public static System.Drawing.Image ConvertByteArrayToImage(byte[] byteArray)

        {

            try

            {

                if (byteArray != null)

                {

                    MemoryStream ms = new MemoryStream(byteArray, 0,

                    byteArray.Length);

                    ms.Write(byteArray, 0, byteArray.Length);

                    return System.Drawing.Image.FromStream(ms, true);

                }

                return null;

            }

            catch { }

            return null;

        }

 

        static public byte[] GetBytesFromUrl(string url)

        {

            byte[] b;

            HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(url);

            WebResponse myResp = myReq.GetResponse();

 

            Stream stream = myResp.GetResponseStream();

            //int i;

            using (BinaryReader br = new BinaryReader(stream))

            {

                //i = (int)(stream.Length);

                b = br.ReadBytes(500000);

                br.Close();

            }

            myResp.Close();

            return b;

        }

 

    }

 

    public class AmazonSigningEndpointBehavior : IEndpointBehavior

    {

        private string    accessKeyId    = "";

        private string    secretKey    = "";

 

        public AmazonSigningEndpointBehavior(string accessKeyId, string secretKey) {

            this.accessKeyId    = accessKeyId;

            this.secretKey        = secretKey;

        }

 

        public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime clientRuntime) {

            clientRuntime.MessageInspectors.Add(new AmazonSigningMessageInspector(accessKeyId, secretKey));

        }

 

        public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher) { return; }

        public void Validate(ServiceEndpoint serviceEndpoint) { return; }

        public void AddBindingParameters(ServiceEndpoint serviceEndpoint, BindingParameterCollection bindingParameters) { return; }

 

    }

    public class AmazonSigningMessageInspector : IClientMessageInspector

    {

        private string accessKeyId = "";

        private string secretKey = "";

 

        public AmazonSigningMessageInspector(string accessKeyId, string secretKey)

        {

            this.accessKeyId = accessKeyId;

            this.secretKey = secretKey;

        }

 

        public object BeforeSendRequest(ref Message request, IClientChannel channel)

        {

            // prepare the data to sign

            string operation = Regex.Match(request.Headers.Action, "[^/]+$").ToString();

            DateTime now = DateTime.UtcNow;

            string timestamp = now.ToString("yyyy-MM-ddTHH:mm:ssZ");

            string signMe = operation + timestamp;

            byte[] bytesToSign = Encoding.UTF8.GetBytes(signMe);

 

            // sign the data

            byte[] secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);

            HMAC hmacSha256 = new HMACSHA256(secretKeyBytes);

            byte[] hashBytes = hmacSha256.ComputeHash(bytesToSign);

            string signature = Convert.ToBase64String(hashBytes);

 

            // add the signature information to the request headers

            request.Headers.Add(new AmazonHeader("AWSAccessKeyId", accessKeyId));

            request.Headers.Add(new AmazonHeader("Timestamp", timestamp));

            request.Headers.Add(new AmazonHeader("Signature", signature));

 

            return null;

        }

 

        public void AfterReceiveReply(ref Message reply, object correlationState) { }

    }

    public class AmazonHeader : MessageHeader

    {

        private string name;

        private string value;

 

        public AmazonHeader(string name, string value)

        {

            this.name = name;

            this.value = value;

        }

 

        public override string Name { get { return name; } }

        public override string Namespace { get { return "http://security.amazonaws.com/doc/2007-01-01/"; } }

 

        protected override void OnWriteHeaderContents(XmlDictionaryWriter xmlDictionaryWriter, MessageVersion messageVersion)

        {

            xmlDictionaryWriter.WriteString(value);

        }

    }

}

So, effectively what you end up with here is a static method you can call, and pass in a search keyword from any source you need to lookup cover art for. It will return a series of images which you can then display dynamically in a .NET application or just save to a designated path using the Image.Save method.

ActionScript 3.0 – Creating a simple video player with hidden button

Ok, I have moved over to the “dark side” a little on this one. ActionScript 3.0 and flash – What a great tool for creating dynamic content on websites.

I knocked together a simple video player for use on a website I am working on at the moment – It basically pops up a little arrow (like on youtube) which when clicked, plays the background video. When the video finishes the arrow pops back up again.

groundswell_btn is a simple button with an arrow that I converted to a symbol.

player is the name of my video player which I imported from an .flv file in the import menu.

backgroundBlock is a false button which sits in between the two of these ^ and hides the video content until the user presses “play”.

Heres the code:-

import fl.video.VideoEvent;

var playbackBegun:Boolean = false;

groundswell_btn.addEventListener(MouseEvent.CLICK, buttonClicked);
player.addEventListener(VideoEvent.STATE_CHANGE, videoHandler);

function buttonClicked(event:MouseEvent):void
{
player.play();
groundswell_btn.visible = false;
backgroundBlock.visible = false;
}

function playerStopped(event:NetStatusEvent):void
{
groundswell_btn.visible = true;
backgroundBlock.visible = true;
}

function videoHandler(evt:VideoEvent) {
// Detect state of theVideo
switch(evt.state) {
case “playing”:
playbackBegun = true;
break;
case “stopped”:
if(playbackBegun == true) {
playbackBegun = false;
groundswell_btn.visible = true;
}
break;
default:
}
}

ASP.NET Posts

I have been working heavily with ASP.NET over the previous few months, hence the relatively few posts. I am planning to write a few ASP.NET posts soon, as there are several things I searched for that I could not find an answer for.

Incidentally, the post relating to low level keyboard hooks are taking google by storm! My pages appear right at the top of several google searches now, which is great news. My page is even starting to get linked to by established C# sites, which is very nice indeed.

No plans to put adverts on the site just yet, as I prefer to keep it nice and clean.

Thanks for using SeeSharpDot.net and come back soon!

MPS – Microsoft Provisioning System

I am going to write a few blog posts on coding to the microsoft provisioning system, because I am currently writing a provisioning ASP.NET portal and cannot find any help anywhere on it, so perhaps my experiences might be useful to others.

The microsoft provisioning system exposes a web service to allow several hosting changes to be submitted, and data to be retrieved.. it’s a well written system that exposes great control over the provisioning system.

Firstly, we need to make sure our web service is running on the provisioning server, and that we can connect to it. It’s useful to then add web references to the services it exposes using visual studio.

In my project, I have included references to the microsoft provisioning example that was included as a sample. It contains three projects, called :-

MPSSampleWebUI.BaseLibrary

MPSSampleWebUI.CommonType

MPSSampleWebUI.WebServiceProxy

PS. If you don’t have this, you are going to be at a severe disadvantage. Get the SDK from here – http://www.microsoft.com/downloads/details.aspx?FamilyID=ce3fc537-86ae-4802-9a05-43e2a27a4b45&DisplayLang=en

If you want to reuse some of the helpful code that microsoft have so helpfully written, it’s a good idea to copy these directly into your project and add references to them from the main ASP.NET project. You will of course have to change the web service reference paths to point to your local web service, and copy over the Web.Config file from the microsoft portal example as it contains some useful configuration items (which you will need to change to reflect your local settings also.)

So, once we have this basic setup sorted, it’s very easy to code to the MPS and submit web requests.

//Get immediate child nodes of this object from AD

XmlNode[] childNodes = OrganizationDetailUtility.GetADChildren(distinguishedName, ADSearchPrefScopes.SearchAtChildLevel);

if (childNodes.Length > 0)

{

    foreach (XmlNode xmlADObjectNode in childNodes)

    {

        ADObjectClass adObjectClass = OrganizationDetailUtility.GetADObjectClass(xmlADObjectNode);

 

        string adObjectDistinguishedName = OrganizationDetailUtility.GetADProperty(ADPropertyNames.DistinguishedName, xmlADObjectNode);

        string adObjectName = OrganizationDetailUtility.GetAdObjectName(adObjectDistinguishedName, adObjectClass);

        string adObjectDisplayName = null;

 

        if (adObjectClass == ADObjectClass.OrganizationalUnit)

        {

            adObjectDisplayName = adObjectName;

        }

        else

        {

            adObjectDisplayName = OrganizationDetailUtility.GetADProperty(ADPropertyNames.DisplayName, xmlADObjectNode);

        }

 

        //Do something with the child item here

    }

}

So, this request for example calls the OrganizationDetailUtility class and pulls back a list of all active directory children for a DSN (distinguished name). I use this call initially to retrieve a list of all provisioned customers under our reseller DSN.

We can also use the web service to retrieve all sorts of useful information about the provisioned customers, for instance this call allows us to get a list of available hosting plans for a customer :-

GetOrganizationAvailablePlansRequest getOrganizationAvailablePlansRequest = new GetOrganizationAvailablePlansRequest();

 

getOrganizationAvailablePlansRequest.Data = new GetOrganizationAvailablePlansRequestData();

getOrganizationAvailablePlansRequest.Data.preferredDomainController = HostingUtility.GetConfigItem(SettingsKey.Pdc);

getOrganizationAvailablePlansRequest.Data.organization = OrganizationDetailUtility.GetLDAPPath(organisationDN);

 

GetOrganizationAvailablePlansResponse getOrganizationAvailablePlansResponse = (GetOrganizationAvailablePlansResponse)WebServiceGeneral.Submit(

    ServiceNamespaces.HostedEmailNamespace,

    HostedEmailProcedures.GetOrganizationAvailablePlans,

    getOrganizationAvailablePlansRequest, true);

 

ArrayOfGetOrganizationAvailablePlansResponseDataPlanPlan[] responsePlans = null;

if (getOrganizationAvailablePlansResponse != null)

{

    responsePlans = getOrganizationAvailablePlansResponse.Data.availablePlans;

    if (responsePlans.Length > 0)

    {

        foreach (ArrayOfGetOrganizationAvailablePlansResponseDataPlanPlan servicePlan in responsePlans)

        {

            planList.Add(servicePlan.planName);

        }

    }

}

 

planList.Sort();

So, there you have it. By changing the Response and Request to the other web methods, and changing the data objects accordingly we can call all of the web methods that the provisioning system exposes.

What I have done is to write another level of abstraction onto Microsofts provisioning sample, which allows me to simplify the usage of these commands using a gateway. For instance, I have encapulated the above code into a static method in a static class called ProvisioningGateway, and exposed methods such as GetPlansForOrganisation(string), ChangePlanForUser(string userDN, string planType) etc. etc.

Doing this will make life easier in the long run, and make the ASP.NET classes look a lot clearer.