views:

293

answers:

2

I've got a project going where I need to programmatically load DLLs and run some code in them. The DLLs and their dependencies are stored in a database, and I write them to disk, but I want to check if a DLL is already there before writing it again. Currently, the database contains the strong assembly name and binary data of the file and a version number. I'm thinking I also need to store the public key for the assembly to check against the key of the existing files in the DLL directory.

I know I can get the public key of the assemblies using AssemblyName.GetPublicKey(). Should I just store the public key of the file in the database also? How is an assembly verified if you don't have the public key in your code (can it be)? All the code examples I have found really just show getting the public key or checking that a strong name exists.

Right now, I'm working on a library management class that is just a Dictionary at it core. It checks the dll directory on start up and adds the public key of each file to the dictionary. Then, when I need to check if an assembly is already on disk, I can just check the dictionary (but this requires me storing the public key in the database). It also uses FileSystemWatcher to monitor any changes to the libraries. If I need to write an updated assembly, I just overwrite the one on disk and the class updates the dictionary.

A: 

I found that I cannot rely on the DotNET framework to really load the assembly I'm asking for. It sometimes returns another assembly which has a higher version number (it's too clever).

Now I read the assembly file into memory, then load the assembly from the COFF stream:

''' <summary>
''' Converts a DotNET class library to a COFF-based image containing the entire assembly
''' </summary>
Public Shared Function ReadAssembly(ByVal iPath As String) As Byte()
  If (Not IO.File.Exists(iPath)) Then Return Nothing

  Try
    Dim file_info As New FileInfo(iPath)
    Dim file_byte(Convert.ToInt32(file_info.Length)) As Byte
    Dim file_stream As New FileStream(iPath, FileMode.Open)

    file_stream.Read(file_byte, 0, file_byte.Length)
    file_stream.Close()

    Return file_byte

  Catch ex As Exception
    Kernel.EH_LogServer.AddMessage("Assembly reading failed: " & ex.Message, EH_RuntimeMessageType.Error)
    Return Nothing
  End Try
End Function

Then it's a matter of calling Assembly.Load(coff_image)

I'd run a CRC check on the byte-array to see if there's a difference between two assemblies.

David Rutten
Did you set "Specific Version" to true in the assembly's reference in the project? This would prevent a newer version from being loaded.
Steven Sudit
It's not a design time reference. I'm loading plugin dlls at runtime so I have no idea which plugins will be available when I compile.
David Rutten
I don't understand. If you're loading the assembly from a file, you're going to get whatever version is in that file.
Steven Sudit
Nope, if there's a newer version of the same assembly in a nearby location, the system will return that one. Also, I need the ability to load assemblies over a network, and this method allows me to circumvent certain problems with that.
David Rutten
A: 

I suppose you could reuse the SN data, but I wouldn't. First, it's not enough to get the public key, which isn't going to change from version to version. Rather, you would need to extract the hash that's encrypted with the private key. I don't offhand know how to do that, so that's one reason I'd try something else.

Instead, I would ensure that the version number of each build was unique and just use that. If you really must, you could use the SHA1 class to run your own hash, but that seems paranoid.

edit

Just to be clear, the public key is used to decrypt the stored hash, which is then compared against a freshly generated hash. Any public key will work, which is why the loading program has to use the strong name, which specifies the allowed public key.

Also, you should be storing the full strong name, not just the public key. This includes the version and the public key.

Steven Sudit
I have the strong name stored, should I just check against that?
scottm
Yes, but only if you enforce the constraint that each compile generates a new version number, which is really something you ought to be doing in the first place.
Steven Sudit
Can I set it so that each new release build generates a new version and not just builds in debug mode?
scottm
Yes, you can and should. The easy way is to use "*" for the build and/or revision numbers. The harder but more flexible way is to have the build machine assign the numbers incrementally.
Steven Sudit
Just to be clear, the reason for wanting the build machine to assign numbers is to make it possible to exactly reproduce a prior build, down to the number.
Steven Sudit