Monday, March 31, 2008

A friend of mine sent me this link today, which was also waiting in my RSS reader for me: http://arstechnica.com/news.ars/post/20080331-creative-irate-after-modder-spruces-up-vista-x-fi-drivers.html

Let me reproduce some of the most relevant parts here:

"We are aware that you have been assisting owners of our Creative sound cards... by providing unofficial driver packages for Vista that deliver more of the original functionality that was found in the equivalent XP packages for those sound cards." writes O'Shaughnessy. "In principle, we don't have a problem with you helping users in this way.. .Where we do have a problem is when technology and IP owned by Creative or other companies that Creative has licensed from, are made to run on other products for which they are not intended."

He continues, "By enabling our technology and IP to run on sound cards for which it was not originally offered or intended, you are in effect, stealing our goods. When you solicit donations for providing packages like this, you are profiting from something that you do not own... If we choose to develop and provide host-based processing features with certain sound cards and not others, that is a business decision that only we have the right to make."

But the real issue isn't the money. Read between the lines, and O'Shaughnessy is proclaiming that his company has the sacred right to enable and disable features in-driver even when the sound card's hardware is perfectly capable of performing the tasks at hand.

According to Creative's own FAQ, sound cards from the X-Fi and Audigy families are incapable of decoding Dolby Digital or DTS, due to the fact that "these functions are not supported at driver level in Windows Vista." This isn't true.  When two of the driver files from a standard X-Fi card are replaced with two driver files drawn from a Dell-specific driver available at the company's support web site, DTS and DD decoding immediately reappear as options and function correctly. Creative might be able to get away with saying that DTS and DD decoding aren't enabled at the driver level for X-Fi and Audigy cards, but the functionality is clearly baked into the driver and is thus supported. Creative may never come straight out and say "It's Microsoft's fault that your cards doesn't work," but the Vista support pages are loaded with descriptions of how Vista's audio system broke Creative products. 

This, presumably, is the feature that ticked Creative off, though Daniel_K has enabled other features Creative didn't plan to support in Vista, including DVD-Audio. Anecdotal forum evidence implies that his drivers are of much higher quality and are more stable than those Creative officially provides, but Ars has not verified this. "

So, this is my opinion based on my understanding of US Law and what I believe to be Ethical behavior.  First of all, I don't believe it is legal for the programmer in question to do this.  When you purchase the Creative product, you are agreeing to their license terms by using the product.  This alone should be enough to allow free markets to satisfy any need that could possibly arise in this situation:

  • If computer users do not wish to agree to any terms they feel are overly restrictive (not being able to hack drivers), they should not buy the product.  If companies lose sales because of this, products will arise to meet this demand.  The term "open source hardware" has been thrown around, where the platform itself is out there for people to implement their own software on top of.
  • If consumers do not purchase sound cards because they can't understand the EULA and yet they understand they'll be bound by the EULA, the market can respond by providing simpler EULA.
  • Taking this one step furthur, buying a pre-built machine from Alienware, for example, would implicitly require agreeing to the EULA of every single piece of the PC.  Again, if people do not buy as many computers because of this, the market can respond with PCs that are Easier to Own.

This is my logic concerning the compay's rights:

  1. Few people would argue with the fact that a compay (like Creative) might want to offer different products, with different levels of Capabilities, at different price points.
  2. The market self-organizes, with people who want the extra capabilities able to get them if they are able to pay for them.
  3. Most people would have no issue deciding for themselves whether or not to buy the V6 Mustang or the V8 Mustang based on their own resources and how important more horespower was to them personally.  In the case of less-tangible Products such as software, it seems that people lose their ability recognize Property as Property, theft as theft, which I've written about before.
  4. A company may determine at some point in it's product development lifecycle that it is cheaper for them to include the same hardware in both the less featured and the fully featured versions of the final product.  When R&D and Testing costs are taken into account this can have some surprising manifestations.  At some point many years ago I bought an Aragon Stage One preprocessor (made by Klipsch) for my home theater, this product retailed for $4,000 at the time.  Shortly thereafter I bought the Klipsch Promedia Ultra 5.1 computer speakers for perhaps $299 or so.  I found out later that the SAME processing chip was in each one, the good, expensive surround processing chip.  Klipsch had determined it would be cheaper to use the chip from the Stage One even though the Silicon was very expensive: it was already proven and could be implemented with almost no engineering work.
  5. Creative should be allowed to turn on or off features in its Products via hardware or software as it sees fit.  The fact that the chip could do this but is "artificially" held back by drivers is an aspect of the agreement between the customer and Creative Labs, and is not a chance for people who disregard Intellectual Property rights to get something for nothing.  Whatever features are purposely crippled in this or that product are value-add features that Creative Labs intended to sell for money.
Monday, March 31, 2008 1:20:28 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback

Before I go ahead and publish my next article, I have two minor gripes about Extension Method Syntax.  Suppose I have a simple extension method:

public static string FriendlyClassName(this object o)

{

string className = o.ToString();

return className.Substring(className.LastIndexOf(".") + 1);

}

I don't care much for explicity use of "this", sice this is basic OO:

//I don't like Extension Methods making me explicitly use "this"

Console.WriteLine(this.FriendlyClassName() + " complete");

I also don't care much for the following being invalid syntax:

Console.WriteLine(base.FriendlyClassName() + " complete");

That one informs me that type "Object" does not contain the FriendlyClassName() method, it would appear that the "this" keyword triggers the syntactical sugar of the extension method paths of the compiler? 

Take a look at the next one, which Dan thinks is wonderful.  Using this extension method:

        public static bool IsNullOrEmpty(this System.Collections.ICollection collection)

        {

            if (null == collection || 0 == collection.Count)

            { return true; }

            return false;           

        }

The following code does not throw any exceptions:

            List<string> nullList = null;

            if (nullList.IsNullOrEmpty())

            {

                //Whiskey Tango Foxtrot?

            }

Hmm... you can read about the NullObject pattern here http://www.cs.oberlin.edu/~jwalker/nullObjPattern/

In a disgusting way, we could use a set of Extension Methods to impliment the NullObject pattern.  IF it makes sense in your program, IF doing the usual coding for nulls isn't appropriate, IF the classical Null Object pattern isn't appropirate, IF Nullable<T> isn't appropriate for your scenario.  That's a lot of ifs.  Still, I found this to be useful in certain cases.

Monday, March 31, 2008 12:58:11 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

http://rss.slashdot.org/~r/Slashdot/slashdot/~3/261336950/article.pl

I believe this to be awesome news.  While Intel, John Carmack, NVidia, and others debate the degree to which Ray Tracing is useful, I'll be hoping that the Direct X Ray Tracing solution using the Task Paralell Library (or something like it) to exploit multiple cores for ray tracing a scene.

Monday, March 31, 2008 11:17:06 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, March 30, 2008

A small part in my decision to make the leap to Vista 64 was Scott Hanselman's findings upon building his home office and a new workstation therein: moving to 64bit was a non issue.  I wish I could report the same.

My first issue was with installation.  With certain hardware configurations Vista x64 will crash with 4gb of RAM installed until a hotfix is applied.  My understanding of the state of the 64bit world is this: the current 64bit processors are not entirely 64bit.  They have 64 bit registers and some 64 bit instructions.  To the average PC hotrodder or developer the preceived benefit is the ability to use 4gb of RAM instead of 2gb.  I installed the hotfix, got my 4gb of RAM working, and set about playing the Sweeny Todd soundtrack whilst I decided what order to install things in...

The drivers for my X-Fi sound card installed, I started Windows Media Player.  Windows Media Player crashes the first time I start it 100% of the time.  Vista Sp1 and a horde of updates has not fixed this.  Since the X-Fi is probably the most popular aftermarket sound card in the known universe at this time this ought to to work.  It may have nothing to do with sound drivers at all.  The 2nd or 3rd time I launch WMP it will work happily forever.

My graphics card is a PNY GeForce 8800GT series.  It clearly lables itself as having both 32 and 64 bit drivers on the driver disc.  I experienced an new Vista phenomenon with this product: installing the drivers using the OEM program declared that no changes were made to the system.  I pointed Vista at the .inf file on the disc for x64 and Vista basically said its own driver ("Standard VGA Port", yeah right) was better and refused to take the OEM drivers.  Only the Nvidia unified driver install would work.  Odd...

My next stage is to get my various messenger programs up and running so I can chat whilst I wait on things to intall.  MSN messenger is objectively the best messenging experience by far so that went worst, no issues there.  I have used Pidigin for AIM (most CarSpot folks and some friends are on AIM) since having crashtastic experiences with Trillian and an absolutely astounding clusterfuck with the actual AIM product ruining a Vista install; it would seem AOL is the reigning champ for the national heavyweight Invasive Install Championships.  Pidgin has similar behavior as WMP, it will crash and then run perfectly for as long as the machine is up.

My Blu-Ray drive was working fine as a Serial ATA DVD drive.  Folks, installations of things like Office 2007 are actually quite painless with something better than an IDE CD drive.  This alone is worth the extra expense.  Still, it came with software that can play Blu-Ray and there are nights (like tonight) where I'd like to watch a movie on my 1080p monitor while some long-running tasks scroll by on my ancient 19" CRT.  My first attempts at getting Blu-Ray playback working met with failure.  The install process (damn you Pioneer) demanded ridiculously old and specific versions of DirectX that were clearly not going to happen on Vista 64.  Some combination of Windows Update and SP1 magically fixed this, so I watched Blood Diamond on Blu-Ray while logs scrolled by tonight. 

My last two 64bit issues dealt with actual software development.  Visual Studio and the like installed and ran fine except for one issue so infuriating I have a dent in my forehead: copying an ASP.net solution from one machine to another suddenly started claiming that "{MyCustomThinger} RoleProvider has already been added", and commenting this part of Web.config out certainly allows the site to run.  As expected, putting this web.config with the role provider commented out on any other machine (including my development laptop and the two servers where the site actually lies) crashes because of course the configuration is incorrect.  I still can't explain this one.  The next issue was the most time consuming.

MySQL comes in 32 and 64 bit flavors for Windows.  I needed MySQL for one of my development efforts.  MySQL x64 does not like Vista x64.  In fact, the slightly out of date platform notes on their site claims Vista x64 is not supported, but there are enough success stories out there suggesting this is just a CYA that I gave it a whirl anyway.  MySQL 64bit would not run, claimin a "side by side configuration" was incorrect.  OK, knowing very litting about anything this sounds like some sort of thunking layer issue, so I'll try the 32bit version.  Same error.  What does Google say?  Google says that the Application Manifest in the server configuration process is broken for 64bit windows.  The MySQL forums claim that only a program called Reshack can fix this.  The problem with Reshack is that it runs on 32bit platforms only.  For those who don't know: the various Icons, embedded resources, and execution manifests for a Windows .exe get compiled into a special section of a windows PE and programs like Reshack can read/write this area without doing anything to the code itself.  I eventually found a Delphi program posted by a company in the UK that would work for me.  Having had some bad experience with libraries without a strong name that I am missing the code to recompile, I was glad MySQL doesn't attempt to use authenticode signatures with their releases.  I changed the Vista application manifest XML to the appropriate "requireAdministrator" and I was finally off and running.

The last issue I experienced this weekend was with some code I "inherited".  It uses Microsoft Jet ODBC to treat a file as a "data source", access the file rows using DataReader constructs, and sort the contents in a DataGrid.  This code bombed when I ran it.  Google tells me that Jet does not exist for x64.  I had planned on rewriting this code anyway but wasn't up to the task tonight, so I kept looking.  .NET programs are usually targetted to "any CPU" by default, but Google told me changing the target specifically to x86 would allow some extra Thunking Magic to happen, and as soon as I did this I was back on my way with Jet magically found now.

After these experiments, I'm definately going to test all of my production code to make sure I haven't done anything that won't work with 64bit editions of Windows or the .NET framework.  I also need to take it upon myself to do some research: what does 64bit do for me and for Joe Consumer besides 4gb of RAM on Vista?

.NET | ASP.NET | Rant
Sunday, March 30, 2008 1:17:26 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [2]  |  Trackback
 Wednesday, March 26, 2008
yay

It would appear the new workstation is stable now, after flashing the BIOS and other tomfoolery. 

Despite being "overkill" in terms of hardware, it still takes for freaking ever to install things in Vista, Live Messenger being the worst offender by far.  Now I need to reorganize my office, set up the extra monitors and speakers, and go about intalling my various development tools.  I should have a very productive weekend.

Wednesday, March 26, 2008 10:34:20 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback

I had to comment on this article over at Slashdot:

http://developers.slashdot.org/article.pl?sid=08/03/25/0145208&from=rss

I don't have a feel for how people in France feel about concurrency programming, but it certainly seems like a hot topic among .Net developers in the USA these days.  The Task Paralell Library, Polyphonic C#, the inreased interest in functional programming languages because of the benefits functional programming can bring to concurrency; I would say interest in this aspect of programmnig is ever-rising.

This reminds me, if I ever get my workstation running, to finish my next concurrency in .NET article.

Wednesday, March 26, 2008 10:38:36 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Yesterday when I got home I plugged the new mobo in, and wonder of wonders it would post.

Last night I had birthing classes, had to go get our daughter from Grandma's after than, so at 11pm I sat down to finish building the workstation.  Vista kept treating me to bluescreen/stop errors after copying files.  Doing some research I found that many motherboards + Vista64 have an issue booting with 4gb of ram installed, so I eventually took some RAM out and got windows installed.  There was a hotfix for this issue but I kept having hard lockups in Vista.  Long story short: I flashed the BIOS after about 4 Vista lockups, Vista insisted on using it's "standard VGA driver" instead  of the Vista 64 driver that came with my 8800 GT.  I'll have to see tonight if Vista is happy with the new BIOS and/or the NVIDIA unified drivers when I try that. 

Stupid computers...

Wednesday, March 26, 2008 9:35:46 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Tuesday, March 25, 2008

I've always been a proactive person:

"Rochester researchers showed for the first time that a natural antioxidant found in grape skins and red wine can help destroy pancreatic cancer cells by reaching to the cell's core energy source, or mitochondria, and crippling its function. The study is published in the March edition of the journal, Advances in Experimental Medicine and Biology.

The study also showed that when the pancreatic cancer cells were doubly assaulted -- pre-treated with the antioxidant, resveratrol, and irradiated -- the combination induced a type of cell death called apoptosis, an important goal of cancer therapy."

http://www.scienceblog.com/cms/mounting-evidence-shows-red-wine-antioxidant-kills-cancer-15734.html

The part about "Pre-treated" is especially precious.  Now please excuse me, I'm going to go pre-treat my cells against cancer.

Tuesday, March 25, 2008 5:04:03 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback

It would appear the UPS man has dropped off my replacement, time for attempt #2.

Tuesday, March 25, 2008 1:23:53 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, March 23, 2008

I thought that a nice feature for my KlipschCorner System Profiles Area might be a deep zoom for the people who upload very high res photos.  I quickly realized I do not currently see any way to automate the creation of the Deep Zoom .bin files needed by the MultiScaleImage control.  One step beyond automation, it ought to be available in a server-side Silverlight API for the many people in hosted environmnets where they may not be able to install Deep Zoom Composer and shell execute an .exe. 

Saturday, March 22, 2008 11:58:13 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Saturday, March 22, 2008

I am now one NewEgg hat and $50 better off than I was the other day, but my replacement part still has not shipped.  So much for having it overnighted to me.  In addition to the claimed system glitches that kept this from shipping when it was supposed to, their systems showed on Friday that the part was boxed but no tracking number had made it out yet.  This morning, I find an email notification that my $$ has been refunded for the part.  I'm not sure if this is additional goodwill or another mistake, but I'll have to wait until Monday to find out.

Saturday, March 22, 2008 5:21:52 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, March 21, 2008

http://www.forbes.com/opinions/2008/03/19/yaron-campaign-finance-oped-cx_ybr_0321yaron.html

I encourage you to leave comments on Forbes as well.

Friday, March 21, 2008 10:16:01 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Thursday, March 20, 2008

So, I set about to RMA my motherboard on Wedesday.  To make a long story short, they gave me a UPS shipping lable and the transit times plus their RMA processing time put the round trip at something like 3 weeks for me.  This is complete BS since by then my window to return/exchange the other parts will be gone.  I shouldn't have to wait a month for something I bought to be made right.  I called NewEgg's customer support to explain this to them, and lo and behold without the slightest bit of resistance they agree to overnight me a new one without making me wait on the whole RMA Process.  I left them a strongly positive note at Reseller ratings and I should be in Quad Core Heaven Friday night or Saturday.

There's an important item to not here.  A DOA Asus motherboard is not the fault of NewEgg, but I am more directly their customer than a customer of Asus.  A company cannot directly pin their customer's satisfaction on the performance of their own vendors.  A layer of indirection is needed.

Thursday, March 20, 2008 11:02:03 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, March 18, 2008

It would appear, at least on my machine, that I can add "No script debugging" to my list of IE8 complaints.  Despite making the necessary Internet Options changes I can't hit breakpoints in javascript in VS2008 any longer.  It's a beta browser and I don't really need this feature, but be warned.

Tuesday, March 18, 2008 9:19:01 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback

Securing ClickOnce Deployments

 

I have written in the past (http://www.damonpayne.com/PermaLink,guid,da078e56-eb5b-4616-8605-13c38d54efc3.aspx) about some of the plumbing involved in ClickOnce and Client Application Services geared towards preventing unauthorized operation of a client application.  At the time I noted that there is at least one issue with this level of “security”.  While it does keep unauthorized users from running your application, it doesn’t protect your code.  As has been lamented elsewhere, a ClickOnce application deployed on the Internet does not have any supported means of keeping people from getting your code.  Even with obfuscation this may be undesirable: we don’t want people getting a hold of the application manifest file and subsequently the assemblies.

A while back I started down a few paths to secure the code and the Application Deployment Manifest.  I have a pretty decent partial solution for protecting your code.  Read on.

Step 1: Security by Obscurity?

Your ClickOnce deployment consists of a deployment manifest, the executable, and all of the referenced assemblies and content files required to run your Windows Forms or WPF application.  By turning off directory browsing and not having any publicly available links to your .application file you can discourage casual spelunking.   I’m not very satisfied with this as the only aspect of a code protection solution. 

It’s fairly unlikely than an uneducated attacker (outside job) could guess the names of the assemblies that make up your application.  If only you could lock down the .application file…

Eliminating the Obvious Suspects

To get the obvious thoughts out of the way:

1)      You cannot protect a ClickOnce application deployment manifest using Forms authentication.  The client side aspects of ClickOnce that get installed with .NET 2.0 can’t handle Forms authentication.

2)      Windows Authentication is at best a poor option here, and not a real option for most people.  The performance of “Remember me” in an NTLM challenge form is flaky and is not supported in the Internet zone.

3)      ClickOnce does not support HTTP Basic authentication schemes.

An Idea

One nice thing about ClickOnce is that, like Silverlight, it’s really a Client Side technology.  You don’t NEED to be running Windows Server and IIS in order to benefit from these technologies.  However since I do know some Microsoft server side technologies I had an idea.

I wanted to associate a Product Key of some kind with a ClickOnce deployment.  Product Keys are not the final word on application licensing but it is a means of accountability.  Look at Windows Genuine Advantage: you can’t keep people from sharing CD keys but if you see the same CD key being used 1,000 times from different hosts all over the nation, you can shut it down.  Given a test deploy URL:

http://localhost:5785/CarSpot/MyApp.application

It would be nice to be able to do something like this:

http://localhost:5785/CarSpot/MyApp.application?ProductKey=ZZZZ-ZZZZ-ZZZZ-ZZZZ

… and have it be meaningful.

HttpHandler Attempt 1

To keep people from getting at my .application file, I thought a simple IHttpHandler implementation configured to intercept requests for the .application extension could be the answer. 

Web.config and sample code:

<add verb="GET,HEAD" path="*.application" type="ClickOnceHandler"/>

    public void ProcessRequest(HttpContext context)

    {

        string key = context.Request.QueryString["key"];

        if (ValidateKey(key))

        {

            //Go ahead and give up the .application file

        }

        else

        {

            throw new ApplicationException("Invalid or missing product Key");

        }

    }

I had a notion of allowing users to sign up for the application online by entering a privately exchanged product key in a web page:

The code behind for the install button is simple:

Response.Redirect("Publish/MyApp.application?key=" + _prodKeyTxt.Text);

Sadly, the solution is not this simple.  The Client side ClickOnce libraries exhibit some bizarre behavior.  Using Fiddler and the debugger, this is what I see:

1)      The .application file is requested and the HttpHandler validates the product key.

2)      Mysteriously, ClickOnce requests the entire URL of the .application file again: http://localhost:5785/CarSpot/Publish/MyApp.application?key=123; The HttpHandler validates the key and writes the contents of the .application file to the Response.

3)      Mysteriously (and infuriatingly) ClickOnce requests the .application file again: http://localhost:5785/CarSpot/Publish/MyApp.application ; note the missing piece of the query string.

I was unable to dig up any reason for what ClickOnce is doing here, but I do know that without getting that product key my scheme is ruined.  Looking at the .application file there’s a likely explanation:

<deploymentProvider codebase="http://127.0.0.1:5785/CarSpot /Publish/MyApp.application" />

This is where I left it for a long time, while dealing with some other things at work.

HttpHandler Attempt 2

The next, step, I felt, was to dynamically alter the application deployment file so that the provider codebase as recorded by the client included the product key on the URL.  I added some code to do this:

                XmlDocument doc = new XmlDocument();

                doc.Load(path);

                //Get around the issue of the query strong not being

                doc.DocumentElement["deployment"]["deploymentProvider"].Attributes["codebase"].Value += "?key=123";

However, this introduces another issue.  When you choose to give your ClickOnce application Full Permissions (Otherwise why not go the XBAP route?) you will find that you are signing it.   Look what Visual Studio did for me:

<publisherIdentity name="CN=CORNZOR\Damon" issuerKeyHash="" /><Signature Id="StrongNameSignature" {nothing to see here}</Signature></asmv1:assembly>

Yes, my laptop is named cornzor.  At any rate, this signature is there for your clients’ protection: if a man in the middle alters the XML payload of the .application file it will not verify and ClickOnce will refuse to do anything further.  Since I’m not a man in the middle I assumed there must be a way to dynamically do what I want.

http://msdn2.microsoft.com/en-us/library/acz3y3te.aspx

http://msdn2.microsoft.com/en-us/library/bb384246.aspx

I started out with the two links above, researching how the manifests are generated, and initially experimenting with manually generating un-signed deployment manifests from the command line.  After an infuriating XML character encoding setback, these articles did lead me to the answer.  Now I can protect my .application file in an appropriate fashion.

HttpHandler Complete

The articles above give us the tools we need to put the product key in the application manifest for each client and re-sign on the fly.

    /// <summary>

    /// Execute whatever business logic will make this a valid program key

    /// </summary>

    /// <param name="key"></param>

    /// <returns></returns>

    protected bool ValidateKey(string key)

    {

        return key.Equals("123");

    }

    /// <summary>

    /// Create a new signature in the deployment manifest

    /// </summary>

    /// <param name="path"></param>

    /// <param name="pfxPath"></param>

    protected void ReSign(string path, string pfxPath)

    {

        string mageArgFormat = "-Sign {0} -CertFile {1} -ToFile {2}";

        string mageExe = @"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\mage.exe";

        string mageArgs = string.Format(mageArgFormat, path, pfxPath, path);

        ProcessStartInfo info = new ProcessStartInfo();

        info.Arguments = mageArgs;

        info.FileName = mageExe;

        info.UseShellExecute = false;

        info.RedirectStandardError = true;

        info.RedirectStandardOutput = true;

        Process p = Process.Start(info);

        p.WaitForExit();

        using (StreamReader stdOutReader = p.StandardOutput)

        {

            string stdOut = stdOutReader.ReadToEnd();

            //Error handling here

        }

 

        using (StreamReader stdErrReader = p.StandardError)

        {

            string stdErr = stdErrReader.ReadToEnd();

            //Error handling here

        }

    }

This method uses mage.exe to re-sign the application manifest after we’ve modified it using the BuildClientApplicationFile method.  There are two things to note about this method.  You will see that I’m referring to mage.exe from the Visual Studio directory here.  Obviously, installing Visual Studio on servers is not ideal.  Mage.exe also ships separately with the SDKs as indicated in the two articles I’ve linked, which is a more palatable install than Visual Studio.  I have not yet tested to see if mage.exe will properly operate on Windows Server 2003 by itself.

The second note on this method is that we are passing in a fully qualified path to a .pfx key file.  I’ve only tested this using the same .PFX key I used when I initially published the application.

    private static void BuildClientApplicationFile(string path, string signedPath, string key)

    {

        XmlDocument doc = new XmlDocument();

        doc.Load(path);

        //Get around the issue of the query string not being persisted by the client

        doc.DocumentElement["deployment"]["deploymentProvider"].Attributes["codebase"].Value += "?key=" +key;

        doc.Save(signedPath);

    }

This method stores a special version of the application deployment manifest for this particular client.  I didn’t keep this in memory due to ClickOnce’s odd habit of re-requesting the manifest many times.  This means I’ve got one copy of the manifest per client sitting on my server.  In my testing I’ve found two side-effects for publishing updates using this method:

1)      If I re-activate from the URL and updates have been published, the client does not get prompted

2)      If I run the program from the start menu, the client does get prompted for updates.

Here is the main HttpHandler method:

    public void ProcessRequest(HttpContext context)

    {

        string key = context.Request.QueryString["key"];

        if (ValidateKey(key))

        {

            //Just in case the client doesn't know what to do with .application

            context.Response.ContentType = "application/x-ms-application";

 

            //The current version deploy URL

            string path = context.Server.MapPath("~/Publish/CarSpot.MyApp.application");

 

            //Check for existing signed manifest for this user:

            //keeping a specially signed copy of the manifest per client

            string signedFileName = key + ".CarSpot.MyApp.application";

            string signedFileServerPath = "~/Publish/" + signedFileName;

            string signedPath = context.Server.MapPath(signedFileServerPath);

 

            if (!File.Exists(signedPath))

            {

                //Load the generated deployment manifest as an XML document to make it easier

                //to edit.

                BuildClientApplicationFile(path, signedPath,key);

 

                </