none
Can someone tell me what criteria ClickOnce uses to create a hash code for assemblies? RRS feed

  • Question

  • Using a ClickOnce online only publication how does ClickOnce determine if a particular assembly has changed from one version to the next.  When you open an updated deployment manifest ClickOnce compares each file's hash code in the deployment package on the server with that of the current version of the file in the clients application cache.  If the hash is different it downloads the file from the server.  If the hash is the same it simply copies the file from the previous version in the client's application cache.  The question is what criteria does ClickOnce use in creating this hash?  It is not simply assembly identity, i.e. Name, Version, Culture and Key, as I routinely see hash codes changed from build to build for assemblies where some code may have changed but none of the identity values have.  It also doesn't seem to be as simple as the file stamp or size as we occasionally see files with the same hash code even though the file has been recompiled (sometimes with new code).

    And yes I'm aware that new hash codes values will be generated for all assemblies every time you publish from VS but we are using mage not VS to publish.

    Friday, April 24, 2009 5:41 PM

Answers

All replies

  • Hi Dane R. Vinson,

    I don't think it's dependent on the hash code.

    If an assembly is strongly named and hasn't been changed, it does not copy it down again, it pulls it from the local cached copy and copies it into the new version's folders. For example, we deploy DirectX with our application, the dll's of which are strongly named, and it only copies them across the wire the first time. In fact, it does this for all of our 3rd party dll's and only copies across the ones that we re-build when we redeploy our application.

    RobinS.
    http://robindotnet.wordpress.com

    Saturday, April 25, 2009 3:52 PM
    Moderator
  • You're correct about unchanged files being copied instead of downloaded, however ClickOnce compares file hashes in the old an new application manifests  to determine if a file has changed.  Below is a typical application manifest entry for an assembly.  If the DisgestValue changes for any file in the mainfest that file will be downloaded from the server.  If not ClickOnce copies the existing file to the new version's folder.  What I need to know is what factors does mage use in calculating that value.  We are occasionally finding instances of assemblies, with changed code, being recompiled and retaining their previous DigestValue in the application mainfest.  This, of course, prevents ClickOnce from downloading the new verions of those assemblies on client machines. The client ends up with a mix of build files which is obviously a bad thing.

      <dependency>
        <dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="FormsProject1.dll" size="20480">
          <assemblyIdentity name="FormsProject1" version="1.0.0.0" publicKeyToken="475179D994202229" language="neutral" processorArchitecture="msil" />
          <hash>
            <dsig:Transforms>
              <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
            </dsig:Transforms>
            <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
            <dsig:DigestValue>EN2gsF2OzxwGZ457mNxpFCgXL8g=</dsig:DigestValue>
          </hash>
        </dependentAssembly>
      </dependency>
    
    
    
    
    Sunday, April 26, 2009 5:25 AM
  • The Hash is generated via the SHA1CryptoServiceProvider.ComputeHash method. If you are seeing hash values agree between what you think should be different versions two files, I would take a closer look at your build and/or manifest generation process: most likely you are somehow accidentally hashing the same file.
    -- Mike
    Tuesday, May 5, 2009 8:49 PM
  • The problem with that is that if you hash one file but include a different file in the build will get an error when you click the manifest.  That isn't occuring, yet we can clearly see the hash code for a particular assembly is the same in the manifests of two seperate builds but the assembly itself contains different code.
    Wednesday, May 6, 2009 5:02 PM
  • We've been working with MS support on this issue now for roughly 2 weeks.  I've been in contact with several people and four different times I've asked the question; what criteria are used in calculating the hash code for an assembly?  It's the only truly pertinent question for us right now.  I've received no answer to that question, only more questions about our "process".  I don't need MS to understand our process, I need to understand theirs.  Specifically, what does the mage encryption algorithm (linked above by Mr. Wade) use as factors in creating a file hash code.

    Saturday, May 9, 2009 6:40 AM
  • Here's a short C# program that will display the hash of the file you pass on the command line.:

            static void Main(string[] args)
            {
                if (args.Length <= 0)
                {
                    Console.WriteLine("Please specify a file to hash.");
                    return;
                }

                SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
                FileInfo info = new FileInfo(args[0]);
                byte[] hashBytes = sha1.ComputeHash(info.OpenRead());
                Console.WriteLine(Convert.ToBase64String(hashBytes));
            }

    Like I said before, most likely the wrong version of the file is being included in the manifest generation. If the above program is run on the two different files, does it still display the exact same hash value?


    -- Mike
    Tuesday, May 12, 2009 7:40 PM
  • No the hash values don't match for the same assembly however neither do either of them match what is in the manifest file.  The problem with what you are advocating is that ClickOnce will fail to load the application at startup if you try to generate a hash code on one file then include a different file in the build.
    Tuesday, May 12, 2009 10:20 PM
  • "The question is what criteria does ClickOnce use in creating this hash?"

    The hash is based on the contents of the file being hashed, using code like what is in the example.


    -- Mike
    Tuesday, May 12, 2009 10:39 PM
  • Dane,
    Going to jump in here since I believe I have also received your query through product support.
    The answer for criteria for hash creation is exactly as Mike says “
    The hash is based on the contents of the file being hashed, using code like what is in the example.”

     

    I think the answer you are looking for can be found by digging deeper into this statement:

    No the hash values don't match for the same assembly however neither do either of them match what is in the manifest file.”

    By same assembly I assume you mean two different versions of the same assembly. This probably means that the manifest file is not being updated correctly with the updated hash value for the assembly -> Hence the question about the process used to generate the manifest and the build steps at some point the manifest generation is not picking up the updated assembly and hence no updated hash. So its more about the criteria for creating an updated manifest then the criteria for creating an updated hash.

     

    The problem with what you are advocating is that ClickOnce will fail to load the application at startup if you try to generate a hash code on one file then include a different file in the build.

    I think there might be a disconnect here. We are simply advocating include the hash for the file that you want to include and deploy.

     

    Lets walk through an example that might clear things up:

    You have Solution V1 with two assemblies A.dll and B.dll. In Version 1 A.dll has the hash “ABCD” and B.dll has the hash  “XYZ”.

    The application manifest also contains these hash values.

     

    Now say you update assembly B.dll -> assembly B’s file stream has changed and it has a new hash value “PQR”

     

    You want to update your solution to V2 such that the same assembly A.dll with hash “ABCD” is used and the new assembly B.dll with hash value “PQR” is used.

    You update the deployment manifest and bump up the version number.

    You update the application manifest and update the hash value for assembly B with the new assembly B.

    >>> This is where the process matters. You could manually update the application manifest or use mage to do this. Mage commandline has multiple ways to do this too.

    You could potentially store all the assemblies old assembly A and new assembly B in a directory and then run the mage –update command on the application manifest. To check that this works you can confirm the file hash for Assembly B as created by the little C# snippet above is also the same as in the application manifest.

     

    At runtime ClickOnce will detect that the solution has an update. It will check assembly A see the same hash and copy over the file. Then it will check assembly B notice a different hash and download it.

    Hope that helps clarify.
    Thanks
    Saurabh


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Thursday, May 14, 2009 3:53 AM
  • All of that is very sensible and is exactly what we are doing yet we still are finding instances where (to use you example) the application manifest for solution V2 has the original B.dll hash value of "XYZ".  We have an automated process who's final step is to create a ClickOnce build.  The process is as follows:

    1. Copy all assemblies to be included to ClickOnce application directory in a temporary location.
    2. Build the application manifest by calling mage with the following arguments: -n Application -t {path to application folder}\{app exe file name}.manifest -Name "{app name}" -v {ClickOnce version} -fd {path to application folder} -tr "FullTrust" -p x86
    3. Rename assembly files with the .deploy extension.
    4. Sign the application manifest by calling mage with the following arguments: -s {path to application manifest} -cf {path to pfx file} -pwd "{pfx file password}"
    5. Copy a deployment manifest originally generated with mageui to the temporary location (one directory up from the ClickOnce application directory).  We have to update an existing deployment manifest instead of generating a new one as some arguments found in mageui are not accessable in mage.
    6. Update the deployment manifest by calling mage with the following arguments: -u {path to deployment manifest} -v {ClickOnce version, same as step 2} -appm {path to the application manifest} -appc {applicaiton directory name}\{application manifest name}
    7. Sign the deployment manifest by calling mage with the following arguments: -s {path to deployment manifest} -cf {path to pfx file} -pwd "{pfx file password}"

    The entire package is then zipped up, copied to a customer's server and extracted.  The deployment manifest overwrites the exsiting file and the application folder is named using the version so it is simply a new folder.

    What is being implied is that somehow our application manifest has a hash code generated for one version of an assembly but then a second version is being included in build and the manifest is not being updated.  This is not possible for two reasons.  First, we do not update our applicaiton manifest, we build an entirely new one each time.  Second, ClickOnce will fail if the hash for a file in a build does not match the hash value for that file in the application manifest.
    Thursday, May 14, 2009 8:18 PM
  • Thanks Dane
    This provides a lot more context in trying to understand the issue you are running into. It seems to be specific to the way Mage is generating the application manifest.
    Notably after step 2 it seems to be using the hash for the old assembly and not the new one. Essentially if you update the manifest with the hash value of the new assembly that you get from running the sample code that Mike provided above then everything should work.

    Unfortunately I am not too savvy with the inner workings of Mage but I have forwarded this issue to the appropriate team who should be following up with you.

    {Update] We just tried this out manually and running mage using  the commandline you provided. It updated the application manifest as expected so I think its more about mage not being able to find the right file in the directory specified by the -fd option.
    Can you investigate step 1 and whether the files are being copied appropriately everytime?



    This posting is provided "AS IS" with no warranties, and confers no rights.
    Thursday, May 14, 2009 9:43 PM
  • All of that is very sensible and is exactly what we are doing yet we still are finding instances where (to use you example) the application manifest for solution V2 has the original B.dll hash value of "XYZ".  We have an automated process who's final step is to create a ClickOnce build.  The process is as follows:

    1. Copy all assemblies to be included to ClickOnce application directory in a temporary location.

    <snip>

    What is being implied is that somehow our application manifest has a hash code generated for one version of an assembly but then a second version is being included in build and the manifest is not being updated.  This is not possible for two reasons.  First, we do not update our applicaiton manifest, we build an entirely new one each time.  Second, ClickOnce will fail if the hash for a file in a build does not match the hash value for that file in the application manifest.


    I won't speak for Saurabh, but that was never my implication, because as was pointed out that would result in installation failures. I never assumed there was an "update" or file swap going on anywhere. My initial thoughts were that somehow the wrong version of the assembly is being used in the manifest generation. Seeing the steps laid out above is very helpful, and do not diminish my belief.

    I'm by no means eliminating the possibility of a mage failure, and I'm sure that aspect will be investigated by the owners of mage. Of course, its easy and certainly a lot less frustrating, to have theories about the problems without actually being in the trenches. It would probably be very helpful to find where things go south by tracing the hash of the troublesome file throughout the process. That may help narrow down the point of failure, and allow the relevant engineers to better come up with a fix.


    -- Mike
    Friday, May 15, 2009 6:29 PM
  • "My initial thoughts were that somehow the wrong version of the assembly is being used in the manifest generation."

    Can you tell me specifically how you see this as a possibility?  We pull all assemblies to a temp directory, generate new manifest file unique for that set of assemblies, package that exact set with the manifest and send to the customer.  I cannot see how its possible for the wrong version of an assembly to have been used in manifest generation.  The assembly in the temp directory that gets used for manifest generation is the same assembly that gets sent.  We have found cases, however, where the assembly in two consecutive ClickOnce builds has had the same hash in the manifest but is a decidely different version of the assembly.  If the "wrong version" or a different version of the assembly is used to generate a hash than is sent ClickOnce will fail.

    Monday, May 18, 2009 5:50 PM
  • Some more questions:

    "We have found cases, however, where the assembly in two consecutive ClickOnce builds has had the same hash in the manifest but is a decidely different version of the assembly."

    What is the exact behavior you are seeing when this happens- Does clickonce install/update the different assembly or do you get an error at runtime? Or does it simply install and run without downloading the updated assembly?
    Is there a possibility that the assembly present in the temporary directory is in fact an older assembly which was not replaced during the build process?
    When you see the same hash in the manifest what is the hash value you get for the assembly by using the program Mike provided above?



    This posting is provided "AS IS" with no warranties, and confers no rights.
    Thursday, May 21, 2009 3:28 PM
  • In the cases we've had this problem ClickOnce runs the new version and appears to install correctly but if you get into the cache you find that it didn't download the latest version of some assemblies.  This is consistent with what ClickOnce should do since the hash code in the manifest for the old and the new versions of the assembly are the same even though the assembly itself is not.

    The assembly in the cache directory on the client machine is, in fact, the old version because ClickOnce does not download the new version.  The hash for the assembly in the two ClickOnce builds matches for the assembly in question so ClickOnce simply copies the assembly from the previous build's client cache to the new build's client cache directory.  The assembly in the deployment package on the server, however, is definitely not the same assembly.

    The hash code in both versions (old and new) of the manifest files found on the client machine matches what is generated by the crypto provider for the assembly that was send in the ClickOnce build.  However, if I run it against eithe of the assemblies in the client's cache it differs, which is consistent with the fact that the user isn't getting the latest assembly.  The question becomes how is the new hash code in the old manifest files?

    I sent along zipped up copies of the relavant files from a client's cache along with a complete explaination when I put in the support call a month ago.

    Thursday, May 21, 2009 5:21 PM
  • I’ve spent a fair amount of time using the hash algorithm provided earlier in this post to examine builds.  I focused on the builds for a single customer where we found this problem.  I looked back through the last 7 or 8 builds for them that we have backed up in house.  In every case the hashes generated in the manifest match those generated by the algorithm run manually on the same files.  That is I’ve confirmed mage uses the same algorithm and that it’s using that algorithm on the correct files.  When we found this problem a while ago we were able to download one client’s entire application cache.  That directory contained two complete builds, as you might expect, the latest and one previous.  The manifest file for the latest build on the client machine is identical to the manifest file in the build’s deployment package we have in house.  The problem is that some of the assemblies in the latest version of the application on the client are different than the files in the deployment package.  If I apply the hash algorithm to one of these application cache files it does not match what is in the manifest file from the cache.  It appears, as was suggested earlier in this post, that at some point a manifest was generated with a newer version of the assembly but then the build included a later version.  I’m having a very difficult time understanding conceptually how this is possible though since ClickOnce simply fails on the click of the deployment manifest if the hash in the manifest does not match the hash for the file itself.
    Friday, May 22, 2009 5:26 PM
  • Hey Dane, I could use mage to get the new hashcode after updating the binaries. Could you let me know what's pains? Hash code didn't update for you when you switch to the new binaries, or hash code updated, but fails to download at runtime? You're saying the scenarios worked for you before, but failed recently?
    Could you provide the steps to repro your issue, and failed at which step? I couldn't repro the steps you provided before.

    Thanks very much! This may help us to identify the problems.
    Monday, May 25, 2009 5:40 AM
  • I wish I could provide you with steps to reproduce the issue but we cannot reproduce it on demand.  We're finding occurances of the issue intermittently on client machines but have had no success with on demand reproduction.  The result of the issue is that client machines end up with a manifest which has a hash code for one version of an assembly but a different version of that assembly in the application cache.  The hash codes in manifest files of the ClickOnce builds themselves appear to be correct.  The client machine ends up with the manifest file from a new build but the assemblies in the in the client cache do not match what's in the build.
    Tuesday, May 26, 2009 5:44 PM
  • This sure looks like a hard one to debug....

    Can we use another example just to make sure the scenario you are experiencing is understood correctly:

    Consider the two machines build machine where you are building your app and generating manifests.
    Client machine where the app is being installed.


    Build Machine

    Client Machine

    Version 1 built

    Manifest contains Hash for A1=XYZ, A2=ABC

    File:A1 hash=XYZ

    File:A2 hash=ABC

    Version 1 Installed

    Files In Cache:

    Manifest: hash for A1=XYZ, A2=ABC

    File A1=XYZ

    File A2=ABC

    Version 2 built

    Manifest contains Hash for A1=XYZ, A2=PQR

    File:A1 hash=XYZ

    File:A2 hash=PQR

    Version 2 Installed

    Files In Cache:

    Manifest: hash for A1=XYZ, A2=PQR

    File A1=XYZ

    File A2=UVW?

     

    So is the problem that when you look at the Client Machine you see a manifest file with the same hash value as that generated on the build machine. This is also the same as the the hash of the file itself being generated on the build machine but if you look at the same file on the Client machine that is in the Cache it has a different hash altogether?

     


    This posting is provided "AS IS" with no warranties, and confers no rights.
    Thursday, May 28, 2009 1:58 AM
  • This turned out to be a bug in ClickOnce caused by the installation of .net 3.5 SP1.  Here is the MS KB article I was given by MS support.

    http://support.microsoft.com/kb/971052
    • Marked as answer by Dane Vinson Tuesday, June 30, 2009 11:03 PM
    Tuesday, June 30, 2009 11:03 PM