Refresh rate switching app

Hi all,

I was looking around for an application which sits in the system tray and watches for key combinations to automatically change your monitor refresh rate – I assumed somebody would have written something to do this, but couldn’t find a single app, so I wrote something.

I have uploaded it here :-

http://downloads.seesharpdot.net/files/RefreshSwitcher.zip

It creates an icon in the system tray – if you right click you will get a mapping of your current resolution to the different refresh rates supported .. if you change resolution it will update itself to the new available refresh rates. Just add it to your startup section or HKLM/Software/Microsoft/CurrentVersion/Run/ in the  registry to get it to auto-launch at startup.

To use it – simple note the key combinations .. they will be in the range of :-

Control+Alt+ F1 to Control+Alt+F10

If I get enough coffees bought for me – I might consider adding some sort of configuration section to it with user configurable key mappings and multi resolution support.

PS. It uses the Low Level Keyboard Hook which is mentioned in the following post :-

http://www.seesharpdot.net/?p=96

Copy paste from Excel into a DataTable

Ever wanted to copy and paste from Excel into a System.Data.DataTable for displaying in a DataGrid? Here’s some quick and dirty code to do just that :-

private void PasteFromExcel()

        {
            DataTable tbl = new DataTable();
            tbl.TableName = "ImportedTable";
            List<string> data = new List<string>(ClipboardData.Split('\n'));
            bool firstRow = true;

            if (data.Count > 0 && string.IsNullOrWhiteSpace(data[data.Count - 1]))
            {
                data.RemoveAt(data.Count - 1);
            }

            foreach (string iterationRow in data)
            {
                string row = iterationRow;
                if (row.EndsWith("\r"))
                {
                    row = row.Substring(0, row.Length - "\r".Length);
                }

                string[] rowData = row.Split(new char[] { '\r', '\x09' });
                DataRow newRow = tbl.NewRow();
                if (firstRow)
                {
                    int colNumber = 0;
                    foreach (string value in rowData)
                    {
                        if (string.IsNullOrWhiteSpace(value))
                        {
                            tbl.Columns.Add(string.Format("[BLANK{0}]", colNumber));
                        }
                        else if (!tbl.Columns.Contains(value))
                        {
                            tbl.Columns.Add(value);
                        }
                        else
                        {
                            tbl.Columns.Add(string.Format("Column {0}", colNumber));
                        }
                        colNumber++;
                    }
                    firstRow = false;
                }
                else
                {
                    for (int i = 0; i < rowData.Length; i++)
                    {
                        if (i >= tbl.Columns.Count) break;
                        newRow[i] = rowData[i];
                    }
                    tbl.Rows.Add(newRow);
                }
            }

            this.WorkingTableElement.WorkingTable = tbl;

            tableImportGrid.DataSource = null;
            tableImportGrid.RefreshDataSource();
            
            tableImportGrid.DataSource = tbl;
            tableImportGrid.RefreshDataSource();
            tableImportGrid.Refresh();
        }

Custom WebBrowser Control with Zooming and CSS Injection

I thought I would post this custom browser I wrote for a project I am working on. It allows you to access the zoom function of the Internet Explorer browser (AxWebBrowser) by calling a Zoom method (passing in a whole number, expressed as percentage). I also added an InjectCSS() method which allows you to insert some extra CSS into the page (after it is loaded). I used this in a project recently where I didn’t want to mess with my generated page CSS or HTML, but simple wanted to add a couple of styles for display only purposes.

    public partial class CustomBrowser : WebBrowser
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern short GetAsyncKeyState(int keyCode);
        public bool IsKeyDown(Keys key)
        {
            return (GetAsyncKeyState((int)key) & 0x8000) != 0;
        }

        #region enums
        public enum OLECMDID
        {
            // ...
            OLECMDID_OPTICAL_ZOOM = 63,
            OLECMDID_OPTICAL_GETZOOMRANGE = 64,
            // ...
        }

        public enum OLECMDEXECOPT
        {
            // ...
            OLECMDEXECOPT_DONTPROMPTUSER,
            // ...
        }

        public enum OLECMDF
        {
            // ...
            OLECMDF_SUPPORTED = 1
        }
        #endregion

        #region IWebBrowser2
        [ComImport, /*SuppressUnmanagedCodeSecurity,*/
         TypeLibType(TypeLibTypeFlags.FOleAutomation |
                     TypeLibTypeFlags.FDual |
                     TypeLibTypeFlags.FHidden),
         Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E")]
        public interface IWebBrowser2
        {
            [DispId(100)]
            void GoBack();
            [DispId(0x65)]
            void GoForward();
            [DispId(0x66)]
            void GoHome();
            [DispId(0x67)]
            void GoSearch();
            [DispId(0x68)]
            void Navigate([In] string Url,
                          [In] ref object flags,
                          [In] ref object targetFrameName,
                          [In] ref object postData,
                          [In] ref object headers);
            [DispId(-550)]
            void Refresh();
            [DispId(0x69)]
            void Refresh2([In] ref object level);
            [DispId(0x6a)]
            void Stop();
            [DispId(200)]
            object Application
            {
                [return:
                 MarshalAs(UnmanagedType.IDispatch)]
                get;
            }
            [DispId(0xc9)]
            object Parent
            {
                [return:
                 MarshalAs(UnmanagedType.IDispatch)]
                get;
            }
            [DispId(0xca)]
            object Container
            {
                [return:
                 MarshalAs(UnmanagedType.IDispatch)]
                get;
            }
            [DispId(0xcb)]
            object Document
            {
                [return:
                 MarshalAs(UnmanagedType.IDispatch)]
                get;
            }
            [DispId(0xcc)]
            bool TopLevelContainer { get; }
            [DispId(0xcd)]
            string Type { get; }
            [DispId(0xce)]
            int Left { get; set; }
            [DispId(0xcf)]
            int Top { get; set; }
            [DispId(0xd0)]
            int Width { get; set; }
            [DispId(0xd1)]
            int Height { get; set; }
            [DispId(210)]
            string LocationName { get; }
            [DispId(0xd3)]
            string LocationURL { get; }
            [DispId(0xd4)]
            bool Busy { get; }
            [DispId(300)]
            void Quit();
            [DispId(0x12d)]
            void ClientToWindow(out int pcx, out int pcy);
            [DispId(0x12e)]
            void PutProperty([In] string property,
                             [In] object vtValue);
            [DispId(0x12f)]
            object GetProperty([In] string property);
            [DispId(0)]
            string Name { get; }
            [DispId(-515)]
            int HWND { get; }
            [DispId(400)]
            string FullName { get; }
            [DispId(0x191)]
            string Path { get; }
            [DispId(0x192)]
            bool Visible { get; set; }
            [DispId(0x193)]
            bool StatusBar { get; set; }
            [DispId(0x194)]
            string StatusText { get; set; }
            [DispId(0x195)]
            int ToolBar { get; set; }
            [DispId(0x196)]
            bool MenuBar { get; set; }
            [DispId(0x197)]
            bool FullScreen { get; set; }
            [DispId(500)]
            void Navigate2([In] ref object URL,
                           [In] ref object flags,
                           [In] ref object targetFrameName,
                           [In] ref object postData,
                           [In] ref object headers);
            [DispId(0x1f5)]
            OLECMDF QueryStatusWB([In] OLECMDID cmdID);
            [DispId(0x1f6)]
            void ExecWB([In] OLECMDID cmdID,
                        [In] OLECMDEXECOPT cmdexecopt,
                        ref object pvaIn, IntPtr pvaOut);
            [DispId(0x1f7)]
            void ShowBrowserBar([In] ref object pvaClsid,
                                [In] ref object pvarShow,
                                [In] ref object pvarSize);
            [DispId(-525)]
            WebBrowserReadyState ReadyState { get; }
            [DispId(550)]
            bool Offline { get; set; }
            [DispId(0x227)]
            bool Silent { get; set; }
            [DispId(0x228)]
            bool RegisterAsBrowser { get; set; }
            [DispId(0x229)]
            bool RegisterAsDropTarget { get; set; }
            [DispId(0x22a)]
            bool TheaterMode { get; set; }
            [DispId(0x22b)]
            bool AddressBar { get; set; }
            [DispId(0x22c)]
            bool Resizable { get; set; }
        }
        #endregion

        private IWebBrowser2 axIWebBrowser2;

        public CustomBrowser()
        {
        }

        ~CustomBrowser()
        {
        }

        protected override void AttachInterfaces(
            object nativeActiveXObject)
        {
            base.AttachInterfaces(nativeActiveXObject);
            this.axIWebBrowser2 = (IWebBrowser2)nativeActiveXObject;
        }

        protected override void DetachInterfaces()
        {
            base.DetachInterfaces();
            this.axIWebBrowser2 = null;
        }

        protected override void OnDocumentCompleted(WebBrowserDocumentCompletedEventArgs e)
        {
            base.OnDocumentCompleted(e);
        }

        public void InjectCSS()
        {
            try
            {
                mshtml.HTMLDocument test = (mshtml.HTMLDocument)this.Document.DomDocument;
                //inject CSS
                if (test.styleSheets.length < 31)
                { // createStyleSheet throws "Invalid Argument if >31 stylesheets on page

                    mshtml.IHTMLStyleSheet css = (mshtml.IHTMLStyleSheet)test.createStyleSheet("", 0);
                    css.cssText = "//Insert Custom CSS here!";
                    // CSS should now affect page
                }
                else
                {
                    System.Console.WriteLine("Could not inject CSS due to styleSheets.length greater than 31");
                    return;
                }
            }
            catch { }
        }

        int currentZoom = 100;
        public void Zoom(int factor)
        {
            object pvaIn = factor;
            try
            {
                this.axIWebBrowser2.ExecWB(OLECMDID.OLECMDID_OPTICAL_ZOOM,
                   OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER,
                   ref pvaIn, 
                   IntPtr.Zero);
            }
            catch (Exception)
            {
                throw;
            }
        }
    }

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 > 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 > 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.