Monday, January 21, 2008

Clickonce Manifest Woes - The deployment identity does not match the subscription

January 21, 2008 Posted by Jason Irwin , 1 comment

As blogged about previously, the organization I work for has a number of developer tools which are published using Clickonce which provides quite a lot of functionality for very little overhead. However, this morning I attempted to update and re-publish one of said applications I realized that the certificate with which the application was originally deployed had expired. Since we do not use private (trusted) certificates, I was able to click the ‘Create Test Certificate’ in Visual Studio to create another certificate. Everything seemed fine – I deployed the application without any issue and even tested it on my box. Everything worked nicely.

Later in the day I received an email from a colleague who had received a rather unsavory error message in his attempt to open the application. It read:

“The deployment identity does not match the subscription.”

It was pretty clear that this was directly related to the change in manifest…after a quick google I realized that I was not the first user this happened to (nor the 100th). It has been logged as a bug on Microsoft Comment with the status of having ‘been fixed in the Orcas release’.

http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=207513

It also has its own page on Microsoft Support which explains it as a conflict of certificates – suggesting two workarounds:

  • Uninstalling and re-installing the application (and totally defeating the purpose of click-once applications). This is fine for small applications with a (very) limited user base – but for enterprise class applications this approach is pretty much unacceptable.
  • Creating a command line assembly to update the certificate – it was too late for me but this is a method I will certainly test in the future as this will undoubtedly happen 365 days from now J The whole support article can be found at http://support.microsoft.com/Default.aspx?kbid=925521

Friday, January 18, 2008

ITVN - Is this the End???

January 18, 2008 Posted by Jason Irwin , 7 comments

About a year ago, cut off from Rugby and soccer games back home in Ireland, and going a little bit crazy, I decided enough was enough. I weighed up my options for watching such games in the United States (even at the God-awful hours of the morning they are broadcast due to timezone differences) and found that Setanta Sports was the way to go. Every bar I had watched a game in in the US was hooked up to their premium service and I was impressed with both their content and presentation (commentators and panelists included).


Deciding on Setanta was easy, but there were really only 3 ways I could get it:

  1. Directtv. I'd love it, but I already had a comcast subscription and didn't feel like going to all that bother to switch.
  2. Setanta Sports Online. The demo I saw on their website showed a tiny viewing window (I had to squint), though I believe this has changed now. The main drawback with this one is that I'd have to a) watch games on my pc or b) hook my laptop up to my tv (and giving said size of playing window I'm sure this wouldn't have looked great on a big widescreen tv).
  3. The winner - purchase what at the time seemed like a futuristic gizmo - a box that streamed the games from the internet straight into my living room tv. The box was from ITVN (http://www.itvn.com). The future seemed bright...

I was immediately impressed. The box came in the mail and setup was a cinch. Even for non technical users, this wouldn't have been a great ordeal. Video was good - by no means high definition and perhaps a little blurry when close up on the action - but extremely watchable. For the first 6 months I was very happy.


I'll be honest now and tell you that I personally had few gripes with the system. Very rarely one of their servers would go down - but there always seemed to be two more ready to go. Some servers were slower than others - I'm pretty patient and don't mind playing around (so long as the game hasn't started) to get the right one.


However, one major issue I had was when the Southern Hemisphere rugby tournaments started. An announcement on the ITVN forums stated that Setanta had forced them to pull coverage of these games - coverage which was advertised on the ITVN website before I made my purchase - coverage which I had paid for. And not just me…there were a number of disgruntled users who didn't appreciate a)not getting to see the games and b) having no other scheduled entertainment in their place - just a screen telling you that they weren't allowed to show these games - for the entire length of each game!!! Sure, I was annoyed - but there was other content I couldn't live without, so I struggled on.


Things got tricky on their forums in the last few months, with users complaining about performance issues, and minor feature related issues - such as not being able to record from the box on their dvr as after 30 minutes a screensaver appeared without any option to disable it. Technical support seemed to stop and users started to get very angsty.


Tonight I decided to take a look at the forums to see what is new…only to find the following message:


"ITVN is currently going through a corporate reorganization and we are no longer accepting new subscribers while we focus on our current subscribers.


ITVN is switching to an email only system. Accordingly, please send all Customer Service and Technical questions to help@itvn.com.


ITVN Forums is no longer a method to reach customer service."



Not only are ITVN not taking on any new customers - tech support is only contactable via email. There's nothing like asynchronous communication when you want an immediate reply. Is ITVN dead? My hope is that they've been bought out by a bigger organization that can pump in the resources necessary for such a worthy attempt to succeed. My belief is that the end is near….prove me wrong ITVN!

Certification: Making it Count

January 18, 2008 Posted by Jason Irwin , No comments

2008 is going to be a busy year. Work is heating up, and I've decided that it is time to put my knowledge into a more tangible form - that's right, I'm going to get certified.


This has been my plan for a while now and I decided to cough up the cash for one of Microsoft's MCPD training kits. Fortunately for me angelab of 'Ravings of a Developer TS' (http://blogs.msdn.com/angelab/default.aspx) fame posted a link this week to a company giving 45/50% off these titles.


I've never used this site, so do not in any way vouch for them, but I trusted it enough to by my first set of certification books . Free shipping was the icing on the cake.


Check out http://www.bookpool.com/ct/254 for more details. Happy studying!

Wednesday, January 16, 2008

Practical ClickOnce - Simple Example

January 16, 2008 Posted by Jason Irwin No comments

Like many organizations, the company I work for has a pretty diverse history (up to present times) when it comes to programming languages used, application type created and…to the point of this post…deployment methods. It is difficult to picture the future however, without picturing Clickonce deployed applications. Right now a number of our internal developer apps use Clickonce. This was an easy decision - we wanted easy deployment and total control over application versioning (and I don't mean source control) - Clickonce delivers in a big way.

So I've worked with Clickonce a little while now - setting up applications and deploying updates - the basics. But, in an effort to keep up to date (on a technology already at least 3 years old!!!) I decided to invest some time and learn a little more….leading to this first simple example.


In the following example I develop a simple launchpad-type application - a winform with 3 buttons - Accounting, LegalForms and RiskManagement in order to demonstrate one simple task - delayed assembly deployment.



Nobody can accuse this of being a realistic demonstration - but I wanted to demonstrate the ability to delay the deployment of assemblies until specifically required at runtime. The solution itself contains 5 projects - the startup form, and 4 assemblies called at runtime. The accounting/legal forms/risk management projects are basically shells returning string values - for demonstrative purposes.

The deployment strategy is as follows: the ClickOnceDemo (project) is the only required download group. This is the startup project and obviously must be downloaded (or else the application couldn't run to begin with…). I created 3 other download groups - one for accounting assemblies and one each for risk management and legal assemblies.






When the New method is invoked in my main form, an event handler is added for the AppDomain.CurrentDomain.AssemblyResolve event - and the LoadNeededAssemblies function is called. In short, when the application attempts to resolve an assembly that is not in memory, it calls this function. For the purposes of this demo, the relationship between deployment groups and physical DLLs is stored in a two dimensional array named 'assemblyToGroupMapping'. When LoadNeededAssemblies is invoked, we loop through this array searching for an assembly that matches the name passed in parameter 'args' of type ResolveEventArgs in the LoadNeededAssemblies function. Once we find an assembly that matches the one being resolved, we download the entire group using the DownloadFileGroup procedure in the System.Deployment procedure.



Once the filegroup has been downloaded (for brevity we used a synchronous download in this example but asynchronous download of groups is just as straight forward) we load the assembly into memory using the LoadFile procedure in the System.Reflection.Assembly namespace.



Once the assembly is resolved (i.e. the assembly is returned and the function exited) the application seamlessly continues its processing - using the objects in that assembly, in this case without the user ever knowing what was happening behind the scenes.


<br /> Imports System.Reflection<br /> Imports System.Deployment.Application<br /> Imports System.Collections.Generic<br /> Imports System.Security.Permissions<br /> <br /> Public Class MainForm<br /> <br /> Private assemblyToGroupMapping(3, 3) As String<br /> Private WithEvents currentDeployment As ApplicationDeployment<br /> <br /> <securitypermission(securityaction.demand,> _<br /> Public Sub New()<br /> InitializeComponent()<br /> assemblyToGroupMapping(0, 0) = ("AccountingAssemblies")<br /> assemblyToGroupMapping(0, 1) = ("AccountingFunctions")<br /> assemblyToGroupMapping(1, 0) = ("AccountingAssemblies")<br /> assemblyToGroupMapping(1, 1) = ("AccountingGeneralLedger")<br /> assemblyToGroupMapping(2, 0) = ("RMAssemblies")<br /> assemblyToGroupMapping(2, 1) = ("RiskManagementFunctions")<br /> assemblyToGroupMapping(3, 0) = ("LegalAssemblies") 'NOTE: INCORRECT NAME FOR DEMO PURPOSES<br /> assemblyToGroupMapping(3, 1) = ("LegalForms")<br /> AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf LoadNeededAssemblies<br /> End Sub<br /> <br /> Private Function LoadNeededAssemblies(ByVal sender As Object, ByVal args As ResolveEventArgs) As System.Reflection.Assembly<br /> Dim loadedAssembly As Assembly = Nothing<br /> If (ApplicationDeployment.IsNetworkDeployed) Then<br /> currentDeployment = ApplicationDeployment.CurrentDeployment<br /> Dim groupName As String = String.Empty<br /> Dim neededAssembly As String = args.Name.Split(",")(0)<br /> For i = 0 To assemblyToGroupMapping.GetUpperBound(0)<br /> If (assemblyToGroupMapping(i, 1) = neededAssembly) Then<br /> currentDeployment.DownloadFileGroup(assemblyToGroupMapping(i, 0))<br /> loadedAssembly = Assembly.LoadFile(Application.StartupPath & "\" & assemblyToGroupMapping(i, 1) & ".dll")<br /> End If<br /> i = i + 1<br /> Next<br /> End If<br /> Return loadedAssembly<br /> End Function<br /> <br /> Private Sub btnLegalForms_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLegalForms.Click<br /> Dim lforms As New LegalForms.LegalFormsBase<br /> lforms.CreateLegalForm()<br /> End Sub<br /> <br /> Private Sub btnRiskManagement_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRiskManagement.Click<br /> Dim rm As New RiskManagementFunctions.RiskManagementBaseClass<br /> MessageBox.Show(rm.CalculateRisk())<br /> End Sub<br /> <br /> Private Sub btnAccounting_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAccounting.Click<br /> Dim af As New AccountingFunctions.AccountingFunctionsCommon<br /> MessageBox.Show(af.ReturnFinancialStatus())<br /> End Sub<br /> End Class<br /> 


In the case of this demo, the event handler for each of the three buttons attempts to instantiate objects contained in assemblies which have not yet been downloaded - i.e. in the deployment groups that we have explicitly named. Clicking on a button (the first time only) results in the system attempting to resolve the required assembly and invoking the functionality just described. Once done, functions are called returning some feedback for demonstrative purposes.

There are two additional things to note in the above source code:

  1. In the mapping array I have purposefully used the wrong group name in one instance - using LegalAssemblies in place of LegalFormsAssemblies. When the legal forms button is clicked an exception will occur as the necessary assembly will not be available.
  2. The AccountingFunctions assembly references the AccountingGeneralLedger assembly. Since they are part of the same group, when we attempt to resolve the first assembly (AccountingFunctions) both assemblies are actually downloaded. Since they are both downloaded, we do not need to return to the function to resolve the second assembly

Sunday, January 13, 2008

SQL Server Management Studio Templates - My Small Gripe

January 13, 2008 Posted by Jason Irwin No comments

I love SQL Server. I love SQL Server Management Studio for the most part. I love the idea of templates in SQL - allowing query templates to be created with variable fields which, with a simple CTRL-M can be populated in a handy dialog. This is a feature I would use all the time…if it were not for my propensity to overwrite the template when saving the resulting file. I do not advocate sloppy coding, nor am I a sloppy code. But every now and again, when things are really busy, I will find that I have saved a file out for future use, forgetting that it was a template to begin with….

I know what you're thinking - the obvious thought would be to set the SQL Script to read-only - allowing changes to be made and executed, but never saved. Unfortunately, in SQL 2005 at least, making the file read only and attempting to populate said variable fields, results in the following message (and no, the file is most certainly not under source code control):

This I cannot explain, and I'd be interested to hear if anyone else has this issue. Personally, and I know this is wishful thinking, I would like to see some separation of template and resulting code (even the option to output the generated code to a separate query window. It's a very small point, but one that (to me) would add quite a bit of finish to this feature. And that is my gripe for today!

Thursday, January 10, 2008

Crystal Reports - Access to report file denied

January 10, 2008 Posted by Jason Irwin 1 comment

Recently I had to set up some virtual development and testing servers for some of our ASP.NET apps. Some existing servers were being retired and it was a good time to clean up the existing environments. The task was simple, install Windows 2000, SQL 2000, IIS 6 and promote our existing dev/test apps. Or so I thought….


Everything ran smoothly until I decided to test our reporting functionality and I was hit with an error message that appears to be pretty common:


"C:\DOCUME~1\<server>\ASPNET\LOCALS~1\Temp\<temp report file>.rpt: Access to report file denied. Another program may be using it."


My original thought, and seemingly the one with the greatest possibility for success, was granting appropriate permissions to this temp folder. A quick (unnecessary) perusal of the code confirmed that the file was being output to the current working directory - the temp folder of the ASP.NET account. So I gave ASP.NET write access to this folder….still no good. The same message appeared! A little confused, I gave ASP.NET full control of the folder - a temporary measure to determine if this was indeed the issue. Alas, still nothing! So I gave everyone full control….and yet I received the same message.


Much time passed, much frustration ensued, much googling was done, and yet I consistently received this error. So I swallowed my pride and did what I should have done the previous afternoon - asked for help. Lo and behold, a colleague had faced the same challenge a few years back.


The answer, on Windows 2000 at least, was to give the ASP.NET account write access to the wwwroot folder! I haven't yet been able to determine why that fixed my issue (though I will follow up when I do), but until next time I am content.