ClickOnce would be great if...

by Administrator 29. June 2007 19:43

My company is growing to the point where customers are asking for features that may be useful to other customers and we'd generally like to be able to easily push out new versions of desktop software.  The immediate choices seem to be the Application Updater block, ClickOnce, and home-rolling a solution.  Since the App Updater block seems to want me to write code to determine when updates happen and such, and I'd like to avoid reinventing the wheel with a complete home grown solution, ClickOnce seems like a very good possibility.  I can publish to FTP from Visual Studio, I don't have to write extra code, I can specify Full Trust, force minimum versions, firewall friendly, etc.  Yes, its got a lot going for it. 

My first concern was that as a mixed-technology shop, we might not be able to use ClickOnce.  Our production environment is predominantly Linux and BSD based and it doesn't seem out of the realm of possibility that there might be some IIS specific functionality needed for ClickOnce to work.  Luckily this is not the case, and I have an important Internal tool running with ClickOnce deployment now.  To be safe I added .application and such as mime types in the Apache config but it seems only some people actually require this step.  For this initial deployment I found only two irksome issues:

1) IE only.  Some of the people here have Firefox set up as their default browser for some reason.  Clicking on the link to the Publish.htm in their email launches Firefox which happily displays the raw Application Manifest.  Cruising around MSDN I can see that this is addressed in .NET 3.0 or 3.5, which one is not clear.  It would also seem likely that there is a way to register an external program-handler for .application in FireFox, but if I wanted users to have to do work I'd keep on giving them MSI files.  This internal tool is also the Pilot for using ClickOnce for end-user deployments, and obviously those people would prefer things to be as easy as possible.  To some degree I can cordially invite internal users to go pound sand if they think the setup is too hard but any problem my customers have, no matter how silly or made up, becomes a real problem for me.

2) Security.  My apache skills are rusty.  Still, I was able to setup basic auth with .htaccess files in my ClickOnce directory with only minimally bothering our sysadmin.  See, I have this odd notion that if I'm going to put a public URL out there that any person could stumble on to or share with their friends, I'd like some form of authentication so I can at least tell who is sharing his password with 50 of his closest friends.  Let me be explicit in that I am not looking for some type of hack-proof system, I just want the ritual of authentication and authorization to be observed here.  Basic auth blows up when you try to Launch the ClickOnce app, and this is expected behavior.  Supposedly NTLM is the solution: protect the resource with NTLM challenge/response, tell it to "remember me", and you're on your way.  Well, not really.  The sysadmin, no longer so lucky as to be only minimally burdened by my experiment, was shanghaid to track down an NTLM compatible add on for apache and cook up some form of domain to authenticate it against.  We found mod_ntlm, we configured mod_ntlm against a Samba domain, we protected the test directory with mod_ntlm.  As soon as I enter the domain credentials and check "Remember this password" I hit launch, to be met with the same kaboom as Basic Auth.  Larry Clarkin reminded me that in the Internet Zone remember me won't work.  Adding to trusted sites does not help either, at least not in my experimenting.  ClickOnce has no mechanism for asking you for credentials, nor can it ask you to confirm continuing on to a site with a self-signed SSL cert, as you are sure to find ina  test environment.

It seems likely that I might be able to tweak IE settings to a custom level for trusted sites, but if my users could infalliably do that task I wouldn't need ClickOnce.  With so many excellent designs within .NET, it seems very odd indeed that there is no Provider Model for authentication, no Event we can respond to in order to handle self-signed test certificates.  I'm trying Orcas Beta 1 this weekend, but I'm not overly optimistic.

Tags:

Security

by Administrator 29. June 2007 18:55

Security is a funny thing, and I often wonder if things like UAC and all-managed operating systems are not dancing around the issue of needing fundamental re-thinking.  Here's a funny example: DasBlog occasionally craps out on me, or maybe it's my session timing out before I can type up a post and hit "post to weblog."  Long posts are always composed in Word first, and shorter ones that I still don't want to re-type in case of the occasional fluke are saved by Ctrl+A, Ctrl+C, to be re-pasted into the edit window in case of an issue.  Is it ironic that when this event just happened, IE politely asked me for permission to allow access to the clipboard, access to the content I had just copied out of IE to begin with.

Tags:

Gaming

by Administrator 29. June 2007 18:48

I bought Super Stardust HD last night and it was so much fun I blew off a lot of other things I've been meaning to do.  I did not play Geometry Wars but apprenntly the dual-analog control scheme is borrowed from that game.  It took a while to get used to the gameplay but I'm hoping over the next week I can better my #300 slot in this ridiculous and addicting shooter.  I believe this is either already on XBLA or is on its way there.  Buy it, now.

I cannot wait for my chance to buy this: http://gear.ign.com/articles/799/799262p1.html.  This is an ingenious idea, and the Xbox 360 absolutely needs this.  I often wonder if PC based Halo players on Live Anywhere are slaughtering console players due to the natural advantage of the glorious mouse.  I imagine cries of "camper!" and "Low ping bastard!" being replaced with "PC snob" or "mouser!" or maybe "m04Z0r!" Yes, elite speak is dumb.

Tags:

Back

by Administrator 18. June 2007 15:16

Mr. and Mrs. Payne are back in town after our honeymoon in St. Lucia.  It was too much fun for me to describe here.  Check out the Flickr link at the top of the page to see a few of my favorite pictures of our time in Castries and Soufrieres.  It was hard to get up toay.

Tags:

Objectivism in Bioshock

by Administrator 7. June 2007 01:11

From Joystiq today, it appears that the next BioShock post apocalyptic game will borrow heavily from Atlas Shrugged for its backstory and plot.

   "Julian Murdoch at Gamers With Jobs takes us on a tour of Quincy, MA's Irrational Games, the developers behind this underwater, art-deco opus. But it's not the usual tour of a game in development, but a tour of the motivation behind creating a intellectually sophisticated game inside the framework of a "kick-ass shooter." With hefty dollops of Objectivism, Ayn Rand's political ideology, on display not only in the game's story but in it's construction, BioShock roughly mirrors the plot of Rand's polemical Atlas Shrugged. Knowing that may not interest some of you, they're of course eager to remind everyone that it's also a "kick-ass shooter."

Read the rest and view screenshots here.  As shown in Lair, Gears of War, Resistance: Fall of Man, and Tetris, games are beginning to have much better plots than most Hollywood movies.  I will certainly have to check out BioShock when it arrives.

Tags:

Exif Editor, Windows Shell File Types, and Interprocess Communication

by Administrator 5. June 2007 21:22

According to the DasBlog stats engine, loyal Damon readers are surpassed by Googlers looking for a specific answer to a specific design or code question by an order of magnitude.  I suppose I could apply political spin to this and state that I am Very Useful, but not too popular.  At any rate, Googlers should utilize the Ctrl+F chord about now, since this article contains various loosely related but hard won observations.

I wrote about  a program I gave to my support people that did something simple and gave me an excuse to dig around in WPF.  Since I am always looking for ways to lessen the burden of our support department (so I can tap them for more dedicated testing, which is in their rational self interest anyway) I try to identify commonalities in support calls and do something about it.  Sometimes “doing something” means new features or bug fixes disguised as new features.  Yep, I said that out loud.  Sometimes these are knowledge base articles; sometimes they are internal only Software Tools.

My company has a special camera that uses a barcode scanner (not TTL) to embed data within images.  Sometimes our illustrious employees and customers make innocent mistakes like “accidentally doing a days’ work with their daughter’s Fisher Price camera and not our ultrahighend special camera” and end up with 1000 photos with no data in them.  This is a conundrum.  Luckily the massively parallel nature of the human brain makes us very good at pattern recognition tasks such as “These 20 photos go with that 1999 dodge”, and rather than the ultra painful manual process our support crew does now to assign these, I thought I’d just spend an hour and write a tool that allowed them to right-click on a group of photos in Explorer and assign some scan data to them using Exif.  It took slightly more than an hour.

File Types in a Setup Project

Like a mathematician who cannot be bothered to determine the sum of the square root of four with itself, I generally find it to be a better use of my time to talk to Tools and APIs than to learn the guts of HKEY_CLASSES_ROOT.  In my last WPF example I used VS2005 and an MSI project’s “View File Types” designer to assign all JPG files to a custom shell verb that runs my program.  Oddly enough it seems to be impossible to find information related to making more than one program a windows shell command for a single file type.  I’m free to add “Output from program one” and “Output from program two” as actions, however Windows is nice enough to employ a WIWO (whichever in, whichever out) technique to randomly choose which program is actually run  out of the two JPG associated programs via the installer.  Looking up the meaning of the “Verb” option on MSDN it would appear that the Verb gives me further command information (which menu item the user clicked) on the JPG and therefore will allow my program to determine what to do with the command line argument of the file path.  Of course I wrote completely separate programs before finding this.  This lack of foresight aside, the “verb” is not passed as a command line argument nor is it set as an environment variable or passed to my program in any way that I can see as useful.

To use multiple custom programs from the windows shell for the same file type, alter the arguments to contain the verb and use a launcher program:

If you want to “run program A for JPG files” and also “run program B for JPG files”, create two commands, two main executables, and a launcher executable.  In some cases the two executables may not make sense but in my case I already had two entirely separate solutions built in Expression Blend and I wasn’t about to look into merging them.  My launcher program is of project type Windows Application yet does not run a Form; this prevents users from seeing the black flash of a console window.

Inter process communication between Windows programs is harder than it needs to be in managed code:

So, as I said in the intro I want to right click on the 30 photos taken of the 1995 Dodge Minivan and work with them all at once inside one program instance. I found that the Windows shell, by default, will open up one instance of the associated program per file selected when dragging the mouse.  Do this with enough files and Windows is nice enough to warn me that “Edit Exif Data for this many files may be slow, continue?”  I would have assumed that each file would be passed as a separate command line argument to a single instance.  Without the ole Init() vs. InitInstance() from win32, I had to find another way to communicate quickly between multiple running Windows applications, WPF applications in this case.

My first attempt was using SendMessage() through P/Invoke and sending a message containing all the command line arguments to the first running instance.  WPF is an entirely new model, obviously, so I had to determine how to get a hold of the wndproc and filter messages.  On WPF this is done like so:

            System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(this);

            System.Windows.Interop.HwndSource hwndSource = System.Windows.Interop.HwndSource.FromHwnd(helper.Handle);

            hwndSource.AddHook(new System.Windows.Interop.HwndSourceHook(MessageHook));

Where the HwndSourceHook delegate looks like:

protected IntPtr MessageHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)

The problem here was that although I could P/Invoke SendMessage to a window after obtaining its main Window handle using System.Diagnostics.Process, and even though I could convince it to marshal wParam or lParam as type LPTSTR, the string in the received message was unconvertible by any means back to a string.  After trying every  Marshal.PtrToStringXXX(IntPtr) method I had to assume something was fundamentally wrong with this method.  Windows pointers are relative to the base memory space of the process.  If a string is marshaled as something like TCHAR* then all the second process is getting is a pointer to a memory address that is only valid in the calling address space. Either more P/Invoke or Marshal calls to do something to memory are needed or this won’t work.  I found a potentially cleaner method anyway.

Remoting

Some people are afraid of Remoting in the same way they are afraid of Threads and probably say things like “We should never use threads or Remoting on this project.”  I have to admit I’ve done almost zero Remoting outside of studying for MCXX tests but I was convinced by my favorite Iron Man/Software Architect/Short Order Cook that Remoting was a fine way to implement trivial inter process communication between applications on the same machine.  Fine, it’s easy enough.  

    public class InterprocessListener : IDisposable

    {

        public const int PORT = 5785;

        private TcpChannel _channel;

 

        public InterprocessListener()

        {

            _channel = new TcpChannel(PORT);

            ChannelServices.RegisterChannel(_channel,false);

            RemotingConfiguration.RegisterWellKnownServiceType(typeof(ArgServer), "ExifEditor", WellKnownObjectMode.Singleton);

        }

 

        public void Dispose()

        {

            if (null != _channel)

            {               

                _channel.StopListening(null);

                ChannelServices.UnregisterChannel(_channel);

            }

        }

 

    }

So, let’s assume we right click on 30 JPG images and choose to run “Edit Exif Data”.  The first instance up would instantiate this simple InterprocessListener on my favorite port, 5785.  The ArgServer implementation is equally trivial:

    public class ArgServer : MarshalByRefObject

    {

        /// <summary>

        /// Delegate to let listeners in this process know we got a message

        /// </summary>

        /// <param name="newArg"></param>

        public delegate void ArgAddedCallback(string newArg);

 

        public static event ArgAddedCallback ArgAdded;

 

        /// <summary>

        /// Pass an arbitrary string out of process

        /// </summary>

        /// <param name="argument"></param>

        public void PassArg(string argument)

        {

            OnArgAdded(argument);

        }

 

        protected void OnArgAdded(string arg)

        {

            if (null != ArgAdded)

            {

                ArgAdded(arg);

            }

        }

    }

Now, as our WPF programs are starting up we can execute some simple logic:

If(this is the first ExifEditor instance to start)

{

Start Interprocess listener

 add ourselves to the static arg passed event
} else {

Get a remote reference and pass our own arguments

Exit as we are no longer needed
}

The SendArgs() method shows the simple Remoting client code used to contact the primary instance.

        /// <summary>

        /// Find a local server and try to send it my arguments

        /// </summary>

        private void SendArguments()

        {

            TcpChannel chan = new TcpChannel();

            ChannelServices.RegisterChannel(chan, false);

            ArgServer argServer = (ArgServer)Activator.GetObject(typeof(ExifEditor.ArgServer), "tcp://localhost:5785/ExifEditor");

            if (null != argServer && Environment.GetCommandLineArgs().Length > 1)

            {

                argServer.PassArg(Environment.GetCommandLineArgs()[1]);

            }

 

            chan.StopListening(null);

        }

Mutexen

Now we need to handle running only a single Primary instance of our program and quickly detecting that program exists and sending arguments to it without trying to use the same TCP port from 30 different programs at once.  We do this with two Mutexes.   Mutices?  Mutexen?  Ox is to Oxen as Mutex is to Mutexen?    Here is the first chunk of code from my Window class, another cool WPF example in my opinion.  The Window is shaped like the camera we give to most of our clients, with image previews and buttons where the camera LCD screen and buttons where the camera buttons are.  I have some WPF clip geometry to take care of to get circular buttons.  Cool?  Lame?  I’m just the .NET software architect slinging code.

       public partial class CameraWindow

       {

        InterprocessListener _listener;

        Mutex _m;

        private delegate void VoidDelegate();

 

        /// <summary>

        /// Check to see if this instance should run or share its arguments with an already established instance

        /// </summary>

              public CameraWindow()

              {

            Application.Current.DispatcherUnhandledException +=

                new System.Windows.Threading.DispatcherUnhandledExceptionEventHandler(Current_DispatcherUnhandledException);

            bool createdNew;

            _m = new Mutex(false, "ExifEditorMutex", out createdNew);

            _m.WaitOne(5, false);

            // All other instances will now fail to create the mutex

            if (createdNew)

            {

                //First instance

                _listener = new InterprocessListener();                   

                ArgServer.ArgAdded += new ArgServer.ArgAddedCallback(ArgServer_ArgAdded);                   

            }

            else

            {

                //Another program is listening already

                Mutex mChild = new Mutex(false, "ExifEditorChild");

                mChild.WaitOne();

 

                SendArguments();

               

                mChild.ReleaseMutex();

                Close();

            }

 

            this.InitializeComponent();

            if (Environment.GetCommandLineArgs().Length > 1)

            {

                _imageList.Items.Add(Environment.GetCommandLineArgs()[1]);

            }

            else if (Environment.GetCommandLineArgs().Length == 1)

            {

                _imageList.Items.Add(Environment.GetCommandLineArgs()[0]);

            }

            //Sorry we couldn't work things out, SendMessage(), it's not you, it's me...

            //System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(this);

            //System.Windows.Interop.HwndSource hwndSource = System.Windows.Interop.HwndSource.FromHwnd(helper.Handle);

            //hwndSource.AddHook(new System.Windows.Interop.HwndSourceHook(MessageHook));

              }

        /// <summary>

        /// When the in-process arg server gets a message, add arguments to our list of images

        /// </summary>

        /// <param name="newArg"></param>

        void ArgServer_ArgAdded(string newArg)

        {

            Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,

               (VoidDelegate)delegate()

               {

                   _imageList.Items.Add(newArg);

                   _countTxt.Text = _imageList.Items.Count + " selected";

               });

        }

        private void Exif_Editor_Closed(object sender, EventArgs e)

        {

            if (null != _listener)

            {

                _listener.Dispose();

            }

        }

 

        private void Exif_Editor_Closing(object sender, System.ComponentModel.CancelEventArgs e)

        {

            if (null != _m)

            {

                _m.ReleaseMutex();                

            }

        }

So, 30 of these are trying to start at once.  One and only one of them will gain an exclusive lock on a System level Mutex named Underbar M.   Because it has a Name this synchronization primitive is visible outside the current process.  In the Exif_Editor_Closing event we release this mutex, failure to do so would be abandoning the mutex and is a serious error condition in Windows.  The other 29 instances trying to start up wait for 5 milliseconds and fail to acquire the Mutex.  They go into the else block where the second Mutex is encountered.  This is where I have to confess I didn’t look at the Remoting threading model too closely but it seems likely that 29 programs trying to write to port 5785 at the same time could be an issue.  Lucky numbers 2 through 29 WaitOne() indefinately to obtain the mChild Mutex for their turn at sending a message, the mChild mutex is then released and the Window kills itself.  Gracefuly.

I noticed some Google near-hits in the DasBlog stats of people looking for a simple example of the Control.Invoke() equivalent in WPF.  Take a look at my descriptive VoidDelegate and ArgServer_ArgAdded above.

Wrapping it Up

So, we have several useful things here.  You can use a System Mutex to keep more than one instance of your app from running.  Of course there is a Windows Forms property (IsSingleInstance in VB) that does this for you in .NET 2.0 but so far I didn’t see the same property for a Window in WPF.  You could use the same logic and a Semaphor to allow n instances of your program to run.  We also have an extremely simple inter process communication example using .Net Remoting.  Finally, if you wish to have some fancy windows shell actions but want to use APIs and managed code rather than figuring out how to properly hack the registry to get your program to get multiple files passed as arguments, you can put it all together and do that.

Tags:

Sore

by Administrator 4. June 2007 01:47

Although I'm nearly as skinny as ever I am only about 2 months on my way back up from the rock bottom of "Worst shape of my adult life".  This climb was inspired by my pending beach vacation to St. Lucia and accomplished mostly by doing a few pushups and yard work.  Last week I got a love note from our neighborhood association stating that a $5/day fine was on its way to me if I didn't stop ruining the neighborhood and plant my two required street trees.  On one hand I suppose I did agree to all 800 pages of restrictions when I bought this land and on the other: busybodies, have you nothing better to do than shake your fingers at delinquent neighbors?  We are getting married and subsequently leaving the country in 7 days, not the best time for items requiring surpise time and expense.

This weekend I bought the two largest autumn blaze maple trees I could find, which apparently only barely meet the necessary restrictions and set about digging holes.  The wonderful Kettle Moraine soil is mostly clay, sand, and rocks.  Lots of rocks.  My back is well-worked, I can barely sit up to write this post.  Despite planting one tree each day and doing a ton of other preparation work I still managed to clean my garage and cook two pretty good meals.  Saturday was steak au poivre with gorganzola sauce, using the Cook's Illustrated skillet to oven method and my own worsteshire and butter skillet technique and addition of chives to the gorgonzola.  Ate this with the pan-roasted asparagus that has become a staple of our diet and roasted red potato.  Today I mequite smoked a ginormous rack of ribs on the grill with a sweet dry rub and took half of them to Jen at work.  That woman will never know how good she has it unless she divorces me and subsequently lives entirely on mac 'n cheese.  I need to take some more pictures of some of this stuff but I'm working on my photographic technique.

I'm going to try to get a very long article out tonight, June will likely end up being a light month for writing due to the goings on.

Tags:

About the author

Damon Payne is a Microsoft MVP specializing in Smart Client solution architecture. 

INETA Community Speakers Program

Month List

Page List

flickr photostream