Wednesday, 23 February 2011

Running a ClickOnce Application as Administrator

I've been using ClickOnce to deploy our Windows apps for about 12 months now, and it's been an absolutely painless way to handle installation and application updates.
One of the reasons it's so painless is that ClickOnce doesn't require admin privileges to install apps on a machine. However this also means that you cannot launch a ClickOnce app with admin privileges.
Until recently this hasn't been a problem for us, but due to some obscure requirement that 64-bit Windows requires admin privileges to access a 32-bit ODBC data source, this shortfall has reared its ugly head.
The official Microsoft stance is that you cannot use ClickOnce if admin privilges are required, and instead install using Windows Installer or similar. However this really didn't appeal because I'd need implement a new way of handling application updates and have to deal with a whole lot of change.
I came across this article by Charles Engelke, which discusses how he used a ClickOnce app to launch a secondary app as Administrator. This sounded really cool, so I wondered if it would be possible for a ClickOnce app to re-launch itself as administrator. It turns out you can.
Here's how we did it:
Our ClickOnce app is WPF, so it's entry point is via the Application_Startup method App.xaml - however you probably already know the entry point for your app. We added the following:
private bool IsRunAsAdministrator()
{
var wi = WindowsIdentity.GetCurrent();
var wp = new WindowsPrincipal(wi);

return wp.IsInRole(WindowsBuiltInRole.Administrator);
}

private void Application_Startup(object sender, StartupEventArgs e)
{
if (!IsRunAsAdministrator())
{
    // It is not possible to launch a ClickOnce app as administrator directly, so instead we launch the
    // app as administrator in a new process.
    var processInfo = new ProcessStartInfo(Assembly.GetExecutingAssembly().CodeBase);

    // The following properties run the new process as administrator
    processInfo.UseShellExecute = true;
    processInfo.Verb = "runas";
        
    // Start the new process
    try
    {
        Process.Start(processInfo);
    }
    catch (Exception)
    {
        // The user did not allow the application to run as administrator
        MessageBox.Show("Sorry, this application must be run as Administrator.");
    }

    // Shut down the current process
    Application.Current.Shutdown();
}
else
{
    // We are running as administrator
        
    // Do normal startup stuff...
}
}
The idea here is that if the current process is not running as administrator, then launch a new process as administrator, then shut down the current process. The new process will realise it's running as administrator and function as normal.
The method we're using to run as administrator is to set the following properties or ProcessStartInfo:
    // The following properties run the new process as administrator
    processInfo.UseShellExecute = true;
    processInfo.Verb = "runas";
I have a feeling this is not best practice (I believe the "correct" way is to embed a manifest in your application with a requestedExecutionLevel element) - but this way was much easier since the process will only ever be launched from within ClickOnce.

Wednesday, 24 March 2010

Table Storage Backup & Restore for Windows Azure

If you're using Table Storage in Windows Azure you're probably well aware of its real-time replication of data, which for me was a key factor in deciding to use the technology.

That said, I think the ability to perform a traditional database backup or restore (i.e a snapshot of the database) would be a really nice feature - which Table Storage does not currently support. Here are my top reasons why:

  1. Data replication may protect you from disk faults, but it doesn't protect you from accidental or malicious deletion. You'll only get this by taking snapshots of your data and storing it elsewhere.
  2. From a testing perspective, it can be really handy (or sometimes imperative) to "copy back" your production DB to your UAT or development environment.

So I thought I could write my own backup tool that retrieves all data via queries and stores it in a file - and then restore it back again by performing inserts. What started as a small & quick project turned into something much bigger - so I'm releasing it as open source.

Download Table Storage Backup

The project consists of 3 components:

  1. Backup Server. The backup server can be installed in your existing Web Role or Worker Role. The backup server performs all backup & restore operations within the Windows Azure environment.
  2. Backup Client. The backup client provides a friendly way of performing a backup & restore from a Windows PC.
  3. Backup Library. You can use the backup library to implement your own backup system or automate your backup operations, e.g. perform backups on a schedule.

How does it work?

  1. Data is backed up by retrieving all entities from all tables. The maximum number of entities are returned per table service query (until a partition entity is hit or 1000 entities are returned).
  2. Data is restored by performing batch insert operations. The maximum number of entities are inserted per batch (100 entities per partition or 4mb batch size).
  3. All transactions are performed at the raw REST level for efficiency, and to ensure data is duplicated precisely.

Please Contribute!

If you have any questions, feedback or bug reports please post them on the CodePlex site - and if you'd like to work on this project directly please contact me!

Cheers,
Anthony.

Wednesday, 23 December 2009

Azure Table Storage Client Extensions

Azure Table Storage is my storage technology of choice for my Windows Azure App. For anyone who’s used this technology, you’ll be well aware of its limitations. One in particular is its lack of relational functionality (aka table joins). For example, take the following LINQ query:

from o in context.Orders
join c in context.Customers on o.CustomerId equals c.CustomerId
select new
{
    o.OrderNo,
    c.FirstName,
    c.LastName,
    o.Total
};

This query will simply fail with a The method 'Join' is not supported. error (not to mention I’m also trying to use projection which will also fail).

But I was thinking that wouldn’t it be cool if a query like this just worked, and was smart enough to perform the required asynchronous queries behind the scenes to return the data.

Now of course there would be a performance hit in doing this, but for small datasets the overhead should be more than acceptable. For large data sets, the only workable option is to denormalise your data beforehand.

So I’d like to introduce my (highly experimental) XTableServiceContext:

Download from CodePlex

XTableServiceContext is a replacement to the standard TableServiceContext class provided by Microsoft. It aims to provide identical functionality, but without the limitations. At the time of writing this blog, the following additional features are supported:

  • Relationships via Join()
  • Projection via Select()

However I hope to keep filling in the gaps (Union, GroupBy etc) as I need more functionality.

Test Harness

Thanks to the guys at Microsoft Australia for giving me some extra CTP tokens, I'm able to publish the test harness while the CTP is still available:

http://storageclientext.cloudapp.net/

Important Note!

Remember that we are still limited by the underlying technology provided by Table Storage, and that this class retrieves data using the most efficient method available. This may mean performing multiple transactions behind the scenes so please keep this in mind.

Please send me your questions, feedback and suggestions!

Monday, 16 November 2009

Development Storage Sync (Azure November 2009 SDK)

With the November 2009 release of the Windows Azure SDK, Microsoft have changed the development storage to support dynamic schemas - just like the real Azure environment.

Because of this change, there is no longer a need to run the Create Test Storage Tables function (hoorah!) - which also means my existing Development Storage Sync tool is now defunct too.

However not all is sunshine and fairies. In order for development storage to update its schema, you must first insert an entity with the most recent properties (see this forum). If you don't, you'll get a crash if you attempt to query on a property that's not in the schema.

Essentially this means that you still need to update your development db each time you change an entity!

To make life easier, I've updated my existing Development Storage Sync tool to do this. Using reflection, it will update the schema for your entire context in one action:

Download Development Storage Sync

Installation

The application requires a reference to your assemblies that contain your TableServiceContext - which means you can't just double-click the app to run it. However a really easy way to run the app is to create an External Tool in Visual Studio. To do this:

  1. Click Tools > External Tools...
  2. Add a new External Tool:

    The Command field should point to DevStorageSync.exe in your installation directory.

    The Arguments field should point to your assembly that contains your TableServiceContext (surround with talking marks, separate multiple assemblies with a semicolon).

Now, whenever you want to synchronise your development db, just click Tools > Sync Development Storage.

Please let me know of any bugs or feedback.

Cheers,
Anthony.

Saturday, 31 October 2009

Parsing Multipart Form Data in a WCF Service

Recently I needed a web page that uploaded files directly to a Windows Communication Foundation (WCF) service. On the face of it this seemed achievable: plonk a file upload field on my web page, and write a WCF service that accepts a file Stream.

So I did this, and it sort of worked - however the file that arrived on my server was all garbled! Then I remembered that a browser does not just post the binary contents of a file, it posts "multipart form data" which can include any other field data included in the form.

SURELY there is some sort of low-level .NET API that can read multipart form data, and just give me my file stream...right? Well I bet somewhere there is, but I couldn't find anything that I could use within a WCF service. Ugh.

So I'm left to deal with it myself. By inspecting the contents of the multipart data, I get something like this:

------------cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5
Content-Disposition: form-data; name=\"Filename\"

PB020344.jpg
------------cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5
Content-Disposition: form-data; name=\"Filedata\"; filename=\"PB020344.jpg\"
Content-Type: application/octet-stream

BINARY DATA IS HERE
------------cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5
Content-Disposition: form-data; name=\"Upload\"

Submit Query
------------cH2ae0GI3KM7GI3Ij5ae0ei4Ij5Ij5--

The binary data starts after the Content-Type, and ends with a line break. So I wrote a parsing class that uses some regular expressions to extract the binary data. It also retrieves the posted file name, and the content type.

Download from CodePlex

It works great in my scenario, however there may be scenarios where it will fail - for example posting multiple files in a single form.

To use the class in a WCF service, your implementation would look something like this:

public string Upload(Stream stream)
{
    MultipartParser parser = new MultipartParser(stream);

    if(parser.Success)
    {
        // Save the file
        SaveFile(parser.Filename, parser.ContentType, parser.FileContents);
    }
    else
    {
        throw new WebException(System.Net.HttpStatusCode.UnsupportedMediaType, "The posted file was not recognised.");
    }
}

Cheers,
Anthony.