I have inherited an application, that uses Microsoft Enterprise Library. The VS solution of the applications contains about 200 projects.
I have been restructuring the solution moving the projects around and removing obsolete ones. I need to tidy it up to make it ready for other developers.
The solution contains Microsoft Enterprise Library dlls of a specific version, however when I moved stuff around, I noticed that some projects do not reference these dlls but instead dlls from my GAC.
I need to make sure that the solution either do not depend on anything else installed (in GAC or otherwise) or document if it does, so the next person do not have a nasty surprise when pulling it from source control.
I tried to removed the Enterprise Library dlls from GAC. It is failed like this:
C:\WINDOWS\system32>gacutil /u Microsoft.Practices.EnterpriseLibrary.Common
Microsoft (R) .NET Global Assembly Cache Utility. Version 4.0.30319.0
Copyright (c) Microsoft Corporation. All rights reserved.
Assembly: Microsoft.Practices.EnterpriseLibrary.Common, Version=4.1.0.0, Culture=neutral, PublicKeyToken=e44a2bc38ed2c13c, processorArchitecture=MSIL
Unable to uninstall: assembly is required by one or more applications
Pending references:
SCHEME: <WINDOWS_INSTALLER> ID: <MSI> DESCRIPTION : <Windows Installer>
Number of assemblies uninstalled = 0
Number of failures = 0
This message makes me think that this dll is referenced by an application installed with Windows Installer (MSI).
My machine does not have Microsoft Enterprise Library as one of the items in the Control Panel / Programs and Features.
How do I find out which MSI-installed product installed the DLL in gac?
EDIT: not a dupe. The other question asks about a situation when the assembly is not shown in GAC by the process explorer, and KB is talking about a situation when an assembly cannot be deleted even if it is not referenced by anything. The process explorer part of that other question is irrelevant to me, as I do not expect the assembly to be loaded all the time. The kb response also does not help since I do not want to delete the assembly I want to find out what program installed that. That other question answers do not answer that (because that's not what was asked there), but luckily I got quality answers here.
The quick and dirty way that's always worked for me is to open a cmd prompt, go to c:\windows\installer and do a findstr:
findstr /s -i -m AssemblyName.dll *.msi
If you find any hits you'll get back one or more files with a short hashed name ending in .msi. Use the Windows Installer Database Editor "ORCA" (install platform SDK or google ORCA.msi ) to examine each of these databases and see which of these has a component that installs said file to the GlobalAssemblyCache directory. If an assembly is present in the MsiAssembly table and File_Application field is empty then it's being installed to GAC.
Once you find it, look at the Property table and Summary Information Stream (top menu -> View -> Summary information) to identify what product the cached MSI represents.
I use a script to enumerate all the components on a system, their guids, where they are installed and the owning product code and name. It can take a while to run.
Option Explicit
Public installer, fullmsg, comp, a, prod, fso, pname, ploc, pid, psorce
Set fso = CreateObject("Scripting.FileSystemObject")
Set a = fso.CreateTextFile("comps.txt", True)
' Connect to Windows Installer object
Set installer = CreateObject("WindowsInstaller.Installer")
a.writeline ("MSI Components")
on error resume next
For Each comp In installer.components
a.writeline (comp & " is used by the product:")
for each prod in Installer.ComponentClients (comp)
pid = installer.componentpath (prod, comp)
pname = installer.productinfo (prod, "InstalledProductName")
a.Writeline (" " & pname & " " & prod & "and is installed at " & pid)
Next
Next
There are few things to understand here. First is, that GAC will be preferred location for getting the assembly. So, on your dev machine remove EL assemblies from GAC. Add EL from a Nuget package to your project. Assemblies in GAC could be manually added there. Not necessarily using MSI. If you want your project to ignore other versions, like the one in GAC. In the references in your project, select reference in question and set "Specific Version" to true
. Now, your project will no longer substitute original reference with one from GAC if version doesn't match. At this point, you can add your EL of that version to the bin, or whatever you have there.
For such large solution my suggestion is to create the following structure
- Solution
- packages(nuget)
- bin
- Project 1
- . . . . .
- Project N
- Remove all project references and replace them with references to dlls in
bin
.
- Set your projects to build output to
bin
.
- Create build order.
- Remove any of your dlls from GAC, remove any 3rd party dlls from GAC and place into
bin
. Only dlls that start with "system" can be in GAC or "Project Files" etc. Any others should be ither in packages
or bin
. (Although in some cases it is useful to grab ones from packages
and move to bin
)
- In your project references set any non system dlls to
copy local = true
, and generally/usually all should have specific version = false
Once you do these items, life will be great for you.