2009-08-09 7 views
1

J'ai une application qui ouvre une série de DLL sur le système de fichiers et dans le GAC, et qui détient un verrou sur ces fichiers. Comment puis-je libérer ces handles explicitement pour pouvoir les reconstruire dans Visual Studio sans fermer mon application?C#: Pourquoi mon application ne ferme-t-elle pas ses handles de fichiers?

Code suit:

private void BuildTabPage_AssemblyTree(string filename, string foldername) 
{ 
    Assembly assembly; 
    try 
    { 
    assembly = Assembly.LoadFrom(filename); 
    } 
    catch (Exception ex) 
    { 
    MessageBox.Show("Error loading assembly " + filename + "\n" + ex.Message); 
    return; 
    } 
    TreeNode tRoot = BuildNode(assembly, foldername); 
    tvAssemblies.Nodes.Add(tRoot); 
    tvAssemblies.ExpandAll(); 

    txtResults.Text = 
    RefsFound.ToString() + " References Located in Filesystem\r\n" + 
    RefsInFramework.ToString() + " References Located in Framework\r\n" + 
    RefsInGac.ToString() + " References Located in GAC\r\n" + 
    RefsNotFound.ToString() + " References Not Found: \r\n\r\n"; 
    foreach (string s in MissingFiles) 
    txtResults.Text += s + "\r\n"; 
} 

private TreeNode BuildNode(Assembly assembly, string foldername) 
{ 
    TreeNode tn = new TreeNode(assembly.GetName().Name); 
    tn.ToolTipText = assembly.FullName + "\n" + assembly.CodeBase; 
    AssemblyName[] assemblies = assembly.GetReferencedAssemblies(); 

    foreach (AssemblyName a in assemblies) 
    { 
    string filename2 = foldername + a.Name + ".dll"; 
    TreeNode tn2; 
    if (System.IO.File.Exists(filename2)) 
    { // File found in folder 
     RefsFound++; 
     Assembly assy = Assembly.LoadFile(filename2); 
     tn2 = BuildNode(assy, foldername); 
    } 
    else if (a.Name.StartsWith("System")) 
    { // Framework assemblies not included 
     RefsInFramework++; 
     tn2 = new TreeNode(); 
     tn2.Text = a.Name; 
     tn2.ForeColor = System.Drawing.Color.Green; 
     tn2.ToolTipText += "\n.NET Framework File"; 
    } 
    else 
    { 
     try 
     { // Find file in GAC 
     Assembly assy = Assembly.Load(a); 
     tn2 = new TreeNode(a.Name); 
     tn.ToolTipText = assembly.FullName + "\n" + assembly.CodeBase + "\nFile detected in GAC"; 
     tn2.Text = "(" + filename2.Substring(filename2.LastIndexOf("\\") + 1) + ")"; 
     tn2.ForeColor = System.Drawing.Color.Green; 
     RefsInGac++; 
     } 
     catch (System.IO.FileNotFoundException) 
     { // File not Found 
     RefsNotFound++; 
     if (!MissingFiles.Contains(a.Name)) 
      MissingFiles.Add(a.Name); 
     tn2 = new TreeNode(); 
     tn2.Text = "(" + filename2.Substring(filename2.LastIndexOf("\\") + 1) + ")"; 
     tn2.ToolTipText = "File Not Found"; 
     tn2.ForeColor = System.Drawing.Color.Red; 
     } 
    } 
    tn.Nodes.Add(tn2); 
    } 

    return tn; 
} 
+2

que le code 'sent' .... BuildNode() a plusieurs responsabilités sans rapport avec la construction d'un noeud .... –

Répondre

6

Malheureusement, les assemblys chargés dans un AppDomain par défaut ne seront pas déchargés tant que l'application ne sera pas fermée. Toutefois, vous pouvez créer un AppDomain et charger des assemblys dans un AppDomain créé. Lorsque l'AppDomain créé est déchargé, les assemblys chargés dans l'AppDomain créé seront déchargés ensemble.

+0

Je ne vois pas un chemin à AppDomain.Load() un assembly par le chemin et le nom de fichier. La plupart des assemblées que je cible ne seront pas dans le GAC; comment puis-je charger une DLL par fichier dans un nouvel AppDomain? – tsilb

+0

Je préfère créer un AppDomain avec l'assembly qui contient BuildTabPage_AssemblyTree() et exécuter la méthode dans le AppDomain créé. Une fois que vous avez terminé, vous pouvez décharger l'AppDomain que vous avez créé. Tous les assemblys chargés dans AppDomain doivent être déchargés ensemble. J'espère que cela peut résoudre votre problème. –

+0

AppDomain.Unload semble n'avoir aucun effet. Le débogueur montre les assemblys en cours de chargement, mais ne les montre pas être déchargé lorsque l'AppDomain est déchargé. Les handles de fichiers restent ouverts. – Triynko

0

Je pense que si vous configurez votre AppDomain à utiliser ShadowCopy il devrait fonctionner:

http://blogs.msdn.com/junfeng/archive/2004/02/09/69919.aspx

Ce lien traite votre question en détail (si je m vous comprendre correctement): http://blogs.msdn.com/jasonz/archive/2004/05/31/145105.aspx

+0

Cela explique le verrouillage de fichier; mais pourquoi ne décharge-t-il pas le fichier quand la variable est hors de portée? – tsilb

+0

Bien «sortir de la portée» a une signification très différente en C#. Regardez dans le modèle Dispose pour comprendre la gestion des ressources. Bien sûr que C# est ramassé, mais cela ne va que très loin. Vous devez toujours comprendre comment gérer les ressources. Mais l'autre raison pour laquelle l'assembly ne «décharge» pas est que les assemblys ne peuvent pas être déchargés d'un domaine d'application par conception. –

+0

Je ne pense pas que l'utilisation de ShadowCopy soit une bonne idée. A en juger par le code, cette application finirait par copier un très grand nombre d'assemblages, y compris de nombreux assemblys système standard. De plus, le programme n'aurait aucun moyen d'actualiser les propriétés des assemblages parce que les appels suivants pour charger l'assemblage retourneraient l'assemblage déjà chargé et non celui que l'on suppose être simplement reconstruit. –

1

Une fois que vous chargez un assemblage dans votre processus, vous ne pouvez pas le décharger. Au lieu de cela, vous pouvez créer un domaine d'application pour chaque assembly que vous voulez charger, obtenir les propriétés des assemblys, puis fermer le domaine de l'application.

Questions connexes