Friday, November 09, 2007

So, WPF is the technology that is finally going to realize the vision of allowing developers and designers to work on the same project, using the same assets, at the same time, and have everyone be happy.  I posted quite a ways back that WPF was cool, and posted pictures of my first two efforts as evidence that WPF will also make it easy for developers to do ridiculous and confusing things.  In my "spare time" at work, which right now means "When I get sick of the things I need to be doing"  I am actually working in WPF, with Orcas and the Expression Blend September preview, on a real application that is going into production next year, with assets supplied by real designers.  Real designers with talent, who are used to specific non-MSFT tools, who are windows-hating Mac users who wouldn't fire up Expression Blend if it came with a free puppy and a pound of gold.  It's very early yet, but I think it's going to work because tools are appearing that will export Adobe Illustrator to XAML, and Expression Design can import vector graphics from Illustrator files.  If WPF and Silverlight gain enough traction is it too much to hope that Illustrator (on the Mac, no less) will be able to understand my VS2008 solution file?  Is it too much furthur to hope that maybe a VSS or Subversion plug-in could be written to let them check the assets right in to my source control?

In the past we've often had one of two situations:

  1. The designer jumps into the asp/jsp/<yourTemplateEngineHere> code to design it up.  Code is accidentally broken, or the designer is unsure of which parts can be edited.
  2. You receive a .TIFF or .PNG graphic depicting insane things and the unenviable task of "Turn this into a web page or windows form".  Hilarity ensues.

If we can get to the point where the UI with insane things going on is an Illustrator file that you import and it "just works", we will have accomplished something.  If the black turtleneck Mac using artist can just check in the XAML skin for my programs, life will be good. 

WPF
Friday, November 09, 2007 3:22:08 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, September 25, 2007

ClickOnce is so close to greatness, as I’ve said before.  Yes, I know the “official word” is that this is not really meant for software ISVs deploying software to end users but it seems with a few extra things it could indeed be very good for this.  With some features in the Orcas Beta 2, I think ClickOnce is getting much closer to providing the plumbing I’ve been wanting in some cases and home rolling in others.  A new product I’m working on has some Architectural Desires.  I call them “desires” because we’ve been selling desktop applications without these architectural features for a while but I really want to meet some Service Level Requirements that will make my life easier, the customer’s life easier, and our support staff’s life easier.

Environment

·         I have an existing web system that users sign into.  Credentials are stored in an app-specific schema in a MySQL database.  Users of the current desktop application must be in this MySQL database and authenticate over web services.  Users of the new system increasingly do not need access to the web based system.  Putting their credentials in MySQL is polluting this system.

·         We have an OpenLDAP server we are slowing moving credential stores to.

·         We have a Windows2003 server that’s not doing much right now

·         We have a client application that ships with specialized hardware and an install disk.

·         Upgrades go out to customers via installing a new MSI

Requirements/Desires

1.       My software requires some specialized hardware.  I’d like to ship this without install disks.

2.       I’d like the desktop application to be able to have the Role information and authenticate against our existing credential stores.  I don’t want users to be able to run this within a very short time period if we turn them off.

3.       I’d like to stop polluting the existing web system credential store with users that should not ever be able to log into this system, however SOME users of the web based system will also need to be able to authenticate using the client application.

4.       I’d like to be able to keep random people from stealing software.

5.       I’d like to be able to keep random people from installing my software.

 

Solution in Code and UML

Let’s take the big red pill and see where it leads us.

ClickOnce - Part 1

I start with .NET 3.5 Beta 2, VS 2008 Beta 2, and my current environment.  Setting up ClickOnce deployment is easy enough, I set the application to check for updates before it runs, and verify that this works.  Oddly enough with Beta 2, 100% of my testers who have tried to click “Install” rather than installing .NET 3.5 and choosing “launch” have had the install .exe lock up.  Hopefully this gets fixed.

Client Application Services – Server Side

There is a wonderful bit of plumbing available now Called Client Application Services.  In order to use this you need an ASP.Net web site.  You need to add a reference to System.Web.Extensions, and configure your web.config such that the Role and Membership services are enabled.  With .NET 3.5 this is done like so:

      <system.web.extensions>

            <scripting>

                  <webServices>

                        <authenticationService enabled="true" requireSSL="false"/>                   

                        <roleService enabled="true"/>

Now, the Role and Membership providers I have configured are my LDAP based home rolled code.  Discussing these is outside the scope of this article; suffice to say they are straight up Provider implementations.  I will need to set up my Forms authentication a certain way to meet my single sign on goals:

            <authentication mode="Forms">

                  <forms domain=".office.carspot"

                           enableCrossAppRedirects="true"

                           cookieless="UseCookies"

                           slidingExpiration="false"

                           defaultUrl="Cookies.aspx"

                           path="/"

                           protection="Encryption"

                           timeout="1440"

                           name="FooBar"/>

            </authentication>

For testing I am allowing the clients to sign on once for a day.  The domain attribute is important, as the cookie created by my web site will now be passed to anysite.office.carspot.  There is one more thing to configure here.  At this point I intended that I would manually provide the Symmetric Key to Trusted Applications written in PERL or Python etc. to decrypt the data in the cookie.  I therefore manually created some keys for a MachineKey element.  This will still be needed when I cluster this solution.

      <system.web>

            <machineKey validationKey="FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"

            decryptionKey="FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"

            validation="SHA1" decryption="AES" />

Client Application Services – Client Side

With the server side set up, I return to my ClickOnce application.  There will be some work to do in the Properties of my app and some work to do in code.  The first step is to enable Client Application Services.

Only the root directory of the ASP.Net application is required.  This also requires references to System.Web and System.Web.Extensions in my Client application.  This is the first hole I’ve seen thus far in my argument that a Client-only .NET install should be available.  The advanced tab bears some discussion now and also a little later:

In my case I want to make sure they are authenticating to the server at regular intervals.  The Use custom connection string setting is the key to a later problem.  Interested readers may want to use reflector on System.Web.ClientServices.Providers.ClientData,  System.Web.ClientServices.Providers.ClientDataManager, and System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, as well as exploring the new System.IO.IsolatedStorage namespace, unless using Reflector on system assemblies is against some rule, in which case you should not and I never have done so.

Now I need to write some code and see if I can log in.  Note that back in the Services tab I included a Class and Assembly name for a credential provider type.  This tells the plumbing what method to invoke in order to request credentials from the users.  The class must implement IClientFormsAuthenticationCredentialsProvider, in my case this is a login window written in WPF.  Insofar as my research has gone so far, authenticating members and checking roles must be done explicitly.  The code to do so is simple:

bool valid = System.Web.Security.Membership.ValidateUser("", "");

This will cause the plumbing to invoke my Login window and give me an answer as to whether authentication was successful, and since Role management is enabled I should be able to get at my roles as well.  Now I’m going to set up a proxy and try to log in.  Here’s what Fiddler shows me:

 

You can see the AJAX/JSON requests going back and forth, and I’m showing the headers of the response in order to show you the persistant cookie.  The actual body of the response is just {“d”, true}.  Now I’m going to try to get this to authenticate against a Web service written in another language on a different server.

This Cookie is not for you

My thought was to use cookies with a common Domain in order to authenticate against my ASP.NET site via Client Services but have the information available in a usable form to other non-.NET systems.  This turned out to be a pipe dream at first.

·         Authenticating via Login.aspx returns a cookie as expected.  Cookies are turned off for HttpWebRequest by default.  Nothing I could find would get Windows/.NET to nicely populate cookies on my HttpWebRequest once I gave it a CookieContainer.  This is probably desired behavior.

·         Authenticating via Client Services does not create a cookie in the same way IE does anyway (more on this in a moment).

·         Any IE specific hacks I do are likely to be broken in Firefox, and supposedly ClickOnce will work with Firefox later this year.

I started by experimenting with the Use Custom Connection String setting, but realized this may be problematic in ClickOnce since the actual Application directory is not a clean or dependable Path.  When facing a dilemma like this I pull out my handy File System Watcher program and go to work. 

While logging in, this “User_damonpayne.clientdata” file created and changed looks promising, and it turns out to be.  I can already tell by calling Thread.CurrentPrincipal.Identity.Name that my server-side user name “damonpayne”, is indeed available on the Client.  Opening up this .clientdata file reveals the following:

<?xml version="1.0" encoding="utf-8"?>

<ClientData>

      <LastLoggedInUserName></LastLoggedInUserName>

      <LastLoggedInDateUtc>1C6E0B92F1DBE7E</LastLoggedInDateUtc>

      <PasswordHash></PasswordHash>

      <PasswordSalt></PasswordSalt>

      <Roles>

            <item>Default</item>

            <item>Technology</item>

            <item>ScrumMaster</item>

      </Roles>

      <RolesCachedDateUtc>1C7FF8AA84CDD3E</RolesCachedDateUtc>

      <SettingsNames></SettingsNames>

      <SettingsStoredAs></SettingsStoredAs>

      <SettingsValues></SettingsValues>

      <SettingsNeedReset>0</SettingsNeedReset>

      <SettingsCacheIsMoreFresh>0</SettingsCacheIsMoreFresh>

      <CookieNames>

            <item>09a630885b744552b25cf95da4f7e20f</item>

      </CookieNames>

      <CookieValues>

      <item>SoloAuth=9816CAC289A2ADBE7E88A0498E71733E0C480CAD631CB57460BF8AB9822747A43FE1ABE6F0D20DCAA95D8339FE8EC866C878A10A1A2BB188AE42B50DCB4C9862</item>

      </CookieValues>

</ClientData>

Those are indeed my roles, and that does look like my server cookie value.  How convenient.  Of course from the Client I can simply call System.Web.Security.Roles.GetRolesForUser() to get my roles, but remember that I sought to pass a token to someone else that would vouch for who I am.  I will now spare you some of the details of my research and skip to the punch line.  By converting my Hex string from <machinekey/> back to a byte array I was hoping to privately share this Symmetric Algorithm key with trusted systems and allow them to decrypt the cookie value.  Well, this didn’t work, and switching algorithms from AES to 3DES didn’t work, and changing the key sizes didn’t work.  Digging through FormsAuthentication through reflector I found the 1st issue: the data is not plain text but rather a binary serialized object.  Ok, so one could use BitConverter to pull out the desired values.  The real sticking point is that FormsAuthentication, when it creates a Symmetric Algorithm of the type specified by your <machineKey/> setting, creates a random Initialization Vector that is not available to you.  How this voodoo works in a clustered environment I don’t know, but this behavior is somewhat disappointing.   Community Server, for example, allows you to put the IV in the web.config as well since this is a very necessary part of the cryptographic process.  My hopes of sharing a symmetric key with a trusted application were killed, and I spent quite a bit of time on Google and USENET looking for a solution.  This was one of those situations where the obvious answer eluded me for a while because I had been too close to this problem for too many hours. 

I can still write code to dig out the cookie value:

        /// <summary>

        /// Assuming local storage, get the client data cookie for this application

        /// </summary>

        /// <returns></returns>

        public static string GetClientDataCookie()

        {

            string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);

            string ver = Assembly.GetCallingAssembly().GetName().Version.ToString();

            appData = Path.Combine(appData, @"CarSpot\CarSpot.Solo\" + ver);

 

            string userName = Thread.CurrentPrincipal.Identity.Name;

 

            string userFile = "User_" + userName + ".clientdata";

            string path = Path.Combine(appData, userFile);

            XmlDocument doc = new XmlDocument();

            doc.Load(path);

            string token = doc["ClientData"]["CookieValues"]["item"].InnerText;

            token = token.Substring(token.IndexOf("=") + 1);

            return token;

        }

… and I still have a public facing Web server that magically (in memory?) has access to the Initialization Vector:

        [WebMethod(Description="Attempt to decrypt a Forms auth ticket issued from this server farm")]

        [SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Bare)]

        public ValidateClientTicketResponse ValidateClientTicket(string ticketString)

        {

            FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(ticketString);

            ValidateClientTicketResponse response = new ValidateClientTicketResponse();

            response.Expiration = ticket.Expiration;

            response.Name = ticket.Name;

            response.UserData = ticket.UserData;

            return response;

        }

… and of course, for trusted systems, I can secure this Endpoint however I please.  I can pass my Ticket from the client to anyone, who can call this service to determine if I am who I say I am and if this is a valid, unexpired ticket.

 

Solution Diagram

So, this is my environment with the solution in place.  Users who only need my Client Application and its accompanying Web Application are created in LDAP.  Users of the special Perl web site that also need to have access to my client are pushed to LDAP via a replication scheme.    My client application can pass it’s authentication ticket value as a SOAP header or an in-band argument when communicating with other trusted endpoints. 

Since I worked through this step by step, there may be a slightly cleaner way to do some of this.  I have been meaning to see if WS-Federation could be used, if only as a more standard API for the web service.

Final Thoughts

Of my original requirements, I have met most of them.  I can keep random people from stealing my code by deploying Obfuscated assemblies via ClickOnce.  I can keep people from running my application using the authentication, and with this method many customers will only have a single password to remember for all their CarSpot products.  I did not write about it here but the ASP.Net Profile information is available via this method as well.  This opens up the door for many unexpected niceness for our users; by implementing a new custom Profile provider, someone might log into one of the Perl web sites and change their display preferences, to discover them magically reflected in their client application as well. 

As for keeping random people from ever even installing my software to begin with, that will have to come later or Obfuscation will have to do.  It would be ideal to protect the .application file for my program so that unwanted visitors cannot even download the manifest in the first place.  Are you listening, Microsoft?  ClickOnce should be able to respond to an authorization challenge or SSL certificate warning in some fashion.   In our tests, Firefox still does not work for launching a ClickOnce application. Are you listening Microsoft?

Tuesday, September 25, 2007 11:57:32 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Thursday, September 20, 2007

My time continues to be eaten up by something I cannot discuss yet.  I am seeking permission to make an announcement...

I'm running VS2008 Beta 2 in a non VM environment now.  So far there's only been one crash and the performance is acceptable though not as fast as VS2005 runs for me right now.  I am working on a WPF application right now and I have to say so far I greatly prefer the designer from the August CTP of Expression Blend 2 to the WPF designer in VS2008.

.NET | WPF
Thursday, September 20, 2007 9:35:50 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Monday, July 16, 2007

The original series of articles on hosting the Visual Studio designer shall be wrapped up in the next installment.  The designer stuff is mostly done anyway, but I had planned on following up with a great deal of obscure Windows Forms tricks involved in interacting with the design time environment.  I've reevaluated the worth of this in light of the coolness of WPF and I think I'll shelve that effort for the forseeable future.  Messing around with the Orcas designer surface for a WPF window today, I realized I need to get on the ball with the Orcas design-time environment.

Monday, July 16, 2007 11:04:40 AM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, July 03, 2007

There are various talks online about ClickOnce improvements in Orcas.  I installed a Vista Virtual PC and Orcas Beta 1, which took a rather long amount of time since I had work to do on this PC while VPC was fighting for resources.  Details were scarce but I had hoped the gaping hole of no authentication mechanisms being supported by ClickOnce would be in some way addressed out of the box with Orcas and .NET 3.5.  No Joy, at least not without a little bit of work and digging.  There are references to the ability to customize the User Interface you see while downloading the application files, ostensibly for branding but I have some hope that this may also allow one to provide a user interface to responde to an authentication challenge.  If not, I'll begin brainstorming some kind of frankenstein solution that may end up being no easier than home rolling the whole shebang.

Tuesday, July 03, 2007 2:54:10 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Friday, June 29, 2007

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.

Friday, June 29, 2007 1:43:28 PM (Central Standard Time, UTC-06:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, June 05, 2007

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);