shahine.com/omar/

homepage | Send mail to the author(s) contact

yet another Microsoft blogger
Page 1 of 9 in the ProgrammingNET category Next Page

# Thursday, December 06, 2007

Authenticode Signing

Frankly, I've been embarrassed for years that all my little freeware applications display a scary security warning when anyone installs them. This is because they are not authenticode signed.

image

I never bothered to go get a certificate to do so cause they cost about $200 a year. Well I have enough people downloading (and donating!) Send to SmugMug that I finally thought it was time to do something about this. The SmugMug folks have been kind enough to feature my downloader on their page for over a year now so I figured it was high time to stop scaring people.

Well Eric Lawrence (creator of Fiddler and SlickRun) clued me in to Tuows who offers Authenticode Digital Signatures for about $80 a year. So I bit the bullet. The process of getting a cert and signing your msi is pretty easy:

  1. Create an account at Tucows
  2. Buy a Cert
  3. Email them your Drivers License
  4. Download the Cert
  5. Export your certificate from the machine and store in a safe place
  6. Grab signtool.exe from the .NET 2.0 SDK
  7. Sign your binary using the certificate from step 4

Voila.

The nice thing about a Certificate is now I can sign anything I distribute including Excel Spreadsheets with Macros to prevent them from working properly.

So if you are a freeware author, for $80 bucks a year you can save your users some grief.

I used the following tutorial to help out.

Posted Friday, December 07, 2007    Permalink    Comments [4]  View blog reactions

 

# Saturday, March 03, 2007

VSTO Add-ins and Vista

So a few days go I decided to build and deploy an add-in written using VSTO 2005 Second Edition on a Vista Machine. Needless to say this didn't work. I kept getting this error:

cid:image003.png@01C75C4B.3E984E60

"The installer has encountered an unexpected error installing this package. This may indicate a problem with this package. The error code is 2869"

I emailed trusty Misha and of course he helped to solve my problem.

There are two things going on.

  1. UAC is getting in the way and Visual Studio 2005 can't build an MSI out of the box that works correctly when your installer needs elevated privileges. VSTO add-ins need elevated privileges because they have to make change to Code Access Security.
  2. Exceptions that are thrown during the install process are being obscured. Misha covers this in his post on the topic.

The fix for issue number 1 is to head over to Aaron Stebner's WebLog and follow his instructions for setting the NoImpersonate flag for your Custom Actions that set up CAS (in VSTO world this would be the SetSecurity project from the VSTO deployment article.

The fix for issue #2 is covered in Misha's post.

So, how do you make this magic all work with one single build step?

  1. Download the Windows SDK Components for Windows Installer Developers.
  2. Grab wirunsql.vbs and place it in your Setup directory.
  3. Grab CustomAction_NoImpersonate.js from Aaron's post and place it in your Setup directory.
  4. Open the project in Visual Studio 2005
  5. Press F4 to display the Properties window
  6. Click on the name of your setup/deployment project in the Solution Explorer
  7. Click on the PostBuildEvent item in the Properties window to cause a button labeled "..." to appear
  8. Click on the "..." button to display the Post-build Event Command Line dialog
  9. Add the following command line in the Post-build event command line text box: cscript.exe "$(ProjectDir)CustomAction_NoImpersonate.js" "$(BuiltOuputPath)"
    cscript.exe "$(ProjectDir)WiRunSQL.vbs" "$(BuiltOuputPath)" "INSERT INTO `Error` (`Error`, `Message`) VALUES (1001, 'Error [1]: [2]')"
  10. Build your setup project

     

Posted Sunday, March 04, 2007    Permalink    Comments [10]  View blog reactions

 

# Monday, January 29, 2007

Reading XMP Metadata from a JPEG using C#

The other day I was looking for some code that would extract some XMP metadata from a JPEG. You see on Vista, all metadata is now written to the file using XMP for a number of image formats, one of which is JPEG. This is truly glorious as on XP there was no interop story for any keywords, captions etc that were entered into Microsoft APIs (Win32 - GDI+ and .NET System.Drawing).

This is possible because Vista and the .NET Framework 3.0 have a new Photo subsystem called the Windows Imaging Component and it's part of the Windows Presentation Foundation (WPF). This is a subsystem that relies on image codecs to describe the contents of images (like video codecs). These codecs also handle reading and writing metadata.

For Vista/.NET Microsoft has written a number of codecs that ship in the box. This includes:

  • BMP
  • GIF
  • ICO
  • JPEG
  • PNG
  • TIFF
  • Windows Media Photo

Metadata support is described on the Microsoft Photography Blog in this post.

EXIF, IPTC, and XMP – oh my!
There are a number of competing standards for imaging metadata. That is, different ways of reading and writing metadata for photos. One of the biggest standards, EXIF, is commonly written to photos by most cameras, but has many limitations. It’s somewhat antiquated, fragile, not very flexible, and doesn’t support international languages like Japanese very well. IPTC is a standard that is used pretty widely in journalism applications, but is undergoing a transformation towards an XMP-based system.

XMP is an extensible framework for embedding metadata in files that was developed by Adobe, and is the foundation for our “truth is in the file” goal. All metadata written to photos by Windows Vista will be written to XMP (always directly to the file itself, never to a ‘sidecar’ file). When reading metadata from photos on Windows Vista, we will first look for XMP metadata, but if we don’t find any, we’ll also look for legacy EXIF and IPTC metadata as well. If we find legacy metadata, we’ll write future changes back to both XMP and the legacy metadata blocks (to improve compatibility with legacy applications).

Well, what I wanted to do is add some code to Send to smugmug that can read the keywords, ratings and captions that I enter in using Vista as well as Adobe Photoshop Bridge and Microsoft's new iView Media Pro Microsoft Expression Media. However, Send to smugmug is a .NET 1.1 application and all this cool new stuff is in .NET 3.0. Ugh.

After a lot of searching I came up empty handed. It seemed impossible to extract XMP from JPEG. Or so I thought. But I found this hidden gem. It turns out that if you just open the JPEG file and read it in using a StreamReader the XMP text is sitting right there in plain view. Right in the middle of all this binary text.

Here is a code snippet to load a jpeg and extract the XMP section.

public static string GetXmpXmlDocFromImage(string filename) 
{ 
    string contents; 
    string xmlPart; 
    string beginCapture = "<rdf:RDF"; 
    string endCapture = "</rdf:RDF>"; 
    int beginPos; 
    int endPos; 
    
    using (System.IO.StreamReader sr = new System.IO.StreamReader(filename))
    {
        contents = sr.ReadToEnd(); 
        Debug.Write(contents.Length + " chars" + Environment.NewLine); 
        sr.Close(); 
    }
    
    beginPos = contents.IndexOf(beginCapture, 0); 
    endPos = contents.IndexOf(endCapture, 0); 

    Debug.Write("xml found at pos: " + beginPos.ToString() + " - " + endPos.ToString()); 
    
    xmlPart = contents.Substring(beginPos, (endPos - beginPos) + endCapture.Length); 

    Debug.Write("Xml len: " + xmlPart.Length.ToString()); 

    return xmlPart; 
} 

Notice that I am looking for the <rdf:RDF and </rdf:RDF> start and end tags here. This is to ensure maximum compatibility. Normally an XMP blog starts with <x:xmpmeta and ends with </x:xmpmeta> however, this root tag is optional per the XMP spec and for some reason Vista uses <xmp:xmpmeta and </xmp:xmpmeta>.

Once you have the xml extracted from the binary file you simply load it into an XML Document and go looking for what you want. In the below code example I'm looking for Rating, Keywords and Description.

private void LoadDoc(string xmpXmlDoc) 
{ 
    XmlDocument doc = new XmlDocument(); 

    try 
    { 
        doc.LoadXml(xmpXmlDoc); 
    } 
    catch (Exception ex) 
    { 
        throw new ApplicationException("An error occured while loading XML metadata from image. The error was: " + ex.Message); 
    } 

    try 
    { 
        doc.LoadXml(xmpXmlDoc);

        NamespaceManager = new XmlNamespaceManager(doc.NameTable);
        NamespaceManager.AddNamespace("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
        NamespaceManager.AddNamespace("exif", "http://ns.adobe.com/exif/1.0/");
        NamespaceManager.AddNamespace("x", "adobe:ns:meta/");
        NamespaceManager.AddNamespace("xap", "http://ns.adobe.com/xap/1.0/");
        NamespaceManager.AddNamespace("tiff", "http://ns.adobe.com/tiff/1.0/");
        NamespaceManager.AddNamespace("dc", "http://purl.org/dc/elements/1.1/");

        // get ratings
        XmlNode xmlNode = doc.SelectSingleNode("/rdf:RDF/rdf:Description/xap:Rating", NamespaceManager);
        
        // Alternatively, there is a common form of RDF shorthand that writes simple properties as
        // attributes of the rdf:Description element.
        if (xmlNode == null)
        {
            xmlNode = doc.SelectSingleNode("/rdf:RDF/rdf:Description", NamespaceManager);
            xmlNode = xmlNode.Attributes["xap:Rating"];
        }
        
        if (xmlNode != null)
        {
            this.Rating = Convert.ToInt32(xmlNode.InnerText);
        }

        // get keywords
        xmlNode = doc.SelectSingleNode("/rdf:RDF/rdf:Description/dc:subject/rdf:Bag", NamespaceManager);
        
        if (xmlNode != null)
        {
             
            foreach (XmlNode li in xmlNode)
            {
                Keywords.Add(li.InnerText);
            }
        }

        // get description
        xmlNode = doc.SelectSingleNode("/rdf:RDF/rdf:Description/dc:description/rdf:Alt", NamespaceManager);
        
        if (xmlNode != null)
        {
            this.Description = xmlNode.ChildNodes[0].InnerText;
        }

    } 
    catch (Exception ex) 
    { 
        throw new ApplicationException("Error occured while readning meta-data from image. The error was: " + ex.Message); 
    } 
    finally 
    { 
        doc = null; 
    } 
} 

There you have it. I hope this saves someone a few hours when they try and do this.

Posted Tuesday, January 30, 2007    Permalink    Comments [23]  View blog reactions

 

# Wednesday, August 16, 2006

ClipboardToImage

I posted a little utlity I wrote a while back called ClipboardToImage.This app does one thing, it takes the contents of your clipboard (an image) ad saves it to disk using a number of fileformat options. I wrote this so that I could use Alt-PrtScn, save to disk and then post to my blog. Prior to that you need to launch a program like Paint.exe (which is scary to use), find a way to crop, and then save.

This reduces the number of steps and makes it really easy. Place ClipboardToImage.exe in a portable/launchable place and just run it when you have your screen shot on the clipboard. It will then ask you where you want to save the image and with which format.

Oh, it does one other bonus. If the contents of the clipboard is text, then it will remove any linebreaks (like in broken urls).

I basically use ClipboardToImage for window screenshots and Cropper for all the others. It's a winning combination :-).

Enjoy.

Here is the code for the app:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;

namespace ClipboardToImage
{
    /// <summary>
    /// Summary description for ImageType.
    /// </summary>
    public enum ImageType : short
    {
        Png = 1,
        Jpeg = 2,
        Gif = 3,
        Bmp = 4
    }
    /// <summary>
    /// Summary description for Program.
    /// </summary>
    class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            object image = GetClipboardContents();
            ImageType imageType = ImageType.Png;

            if (args.Length > 0)
            {
                try
                {
                    imageType = (ImageType)Enum.Parse(typeof(ImageType), args[0], true);
                }
                catch
                {
                    // TODO: spit out command line help
                }
            }

            if (image is Bitmap)
            {
                SaveImage((Bitmap)image, imageType);
            }
        }

        private static void SaveImage(Bitmap bitmap, ImageType type)
        {
            SaveFileDialog saveFileDialog1 = new SaveFileDialog();
            saveFileDialog1.Filter = "PNG file (*.png)|*.png|Jpeg file 
(*.jpg)|*.jpg|GIF file (*.gif)|*.gif|Bitmap file (*.bmp)|*.bmp|All files (*.*)|*.*"
; switch(type) { case(ImageType.Png): saveFileDialog1.FilterIndex = 1 ; saveFileDialog1.DefaultExt = "png"; break; case(ImageType.Jpeg): saveFileDialog1.FilterIndex = 2 ; saveFileDialog1.DefaultExt = "jpg"; break; case(ImageType.Gif): saveFileDialog1.FilterIndex = 3 ; saveFileDialog1.DefaultExt = "gif"; break; case(ImageType.Bmp): saveFileDialog1.FilterIndex =4 ; saveFileDialog1.DefaultExt = "bmp"; break; default: break; } DialogResult dr = saveFileDialog1.ShowDialog(); if (dr == DialogResult.OK) { string fileName = saveFileDialog1.FileName; switch(saveFileDialog1.FilterIndex) { case(1): bitmap.Save(fileName, ImageFormat.Png); break; case(2): bitmap.Save(fileName, ImageFormat.Jpeg); break; case(3): bitmap.Save(fileName, ImageFormat.Gif); break; case(4): bitmap.Save(fileName, ImageFormat.Bmp); break; default: break; } } bitmap.Dispose(); } private static object GetClipboardContents() { IDataObject iData = Clipboard.GetDataObject(); System.IO.MemoryStream ms = new MemoryStream(); // Determines whether the data is in a format you can use. if(iData.GetDataPresent(DataFormats.Bitmap)) { Bitmap bitmap = (Bitmap)iData.GetData(DataFormats.Bitmap); bitmap.Save(ms, ImageFormat.Bmp); return bitmap; } else return null; } } }

Posted Wednesday, August 16, 2006    Permalink    Comments [9]  View blog reactions

 

# Friday, March 10, 2006

Windows Live Mail and AJAX

Walter Hsueh (one of our dev leads) has a good post on best practices for building AJAX applications. I’m assuming there will be many more since this is labeled part 1 :-).

Posted Saturday, March 11, 2006    Permalink    Comments [2]  View blog reactions

 

Page 1 of 9 in the ProgrammingNET category Next Page