2015-08-29 2 views
2

J'ai créé une fonction définie par l'utilisateur SQLCLR qui appelle une DLL C++ native. La DLL C++ native est la mienne et si je veux apporter des modifications, j'ai besoin d'arrêter le processus sqlservr pour copier le nouveau. Dans la production, ce n'est pas acceptable.Comment faire pour décharger la DLL native (chargée en utilisant l'attribut [DllImport()]) à partir du processus SQL Server?

Même si je supprime l'assembly qui utilise cette DLL, le fichier est toujours utilisé. Que puis-je faire pour écraser la DLL native?

EDIT

DLL déclaration de méthode:

[DllImport("Library64.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 
public static extern IntPtr GetTiming(); 

MS semble charger la bibliothèque sur le premier appel à lui, et semble décharger lorsque le processus se termine .... :)

+0

Puisque la DLL native est la vôtre, je vous suggère de la réécrire en tant que code managé. Cela vous permettra de charger le code dans SQL Server en tant qu'assemblage au lieu d'une référence externe dangereuse et d'éviter ce problème. –

+1

Je n'aurais pas la vitesse de l'algorithme dans .NET comme je l'ai en C++. – John

+0

https://social.msdn.microsoft.com/Forums/en-US/a2682b53-12ad-4719-b05c-df1476cb39c9/how-to-unload-a-dll?forum=csharpgeneral – lad2025

Répondre

1

Il semble comme la "référence" est détenue par le domaine App. Vous pouvez essayer de décharger le domaine de l'application qui devrait l'effacer (mieux vaut le deviner car je n'ai aucun moyen de le tester). Vous pouvez le faire en apportant des modifications de sécurité à la base de données. Les travaux suivants:

ALTER DATABASE {db_name} SET TRUSTWORTHY ON (or OFF if already ON); 
GO 
ALTER DATABASE {db_name} SET TRUSTWORTHY OFF (or ON if already OFF); 
GO 

Gardez à l'esprit que cela va décharger tous les AppDomains dans cette base de données particulière. Ce n'est généralement pas un problème car les utilisateurs ont rarement plusieurs domaines d'application dans une seule base de données (ce qui nécessiterait que les assemblys soient détenus par des utilisateurs différents, et la plupart des gens utilisent simplement dbo).


Pour voir ce que App domaines existent et qui sont chargés des ensembles en eux, exécutez ce qui suit:

SELECT DB_NAME(dca.[db_id]) AS [DatabaseName], dca.*, '---' AS [---], dcla.* 
FROM sys.dm_clr_appdomains dca 
INNER JOIN sys.dm_clr_loaded_assemblies dcla 
     ON dca.appdomain_address = dcla.appdomain_address 
WHERE dca.[db_id] <> 32767; 

Si rien est renvoyé par cette requête et vous ne pouvez toujours pas remplacer cette DLL externe, essayez ce qui suit (ce qui semble être un peu beaucoup, mais nous devons savoir si cela fonctionne avant d'essayer quoi que ce soit d'autre):

sp_configure 'clr enabled', 0; 
RECONFIGURE; 
GO 
sp_configure 'clr enabled', 1; 
RECONFIGURE; 

Deux autres options sont à essayer:

  • Créer une DLL wrapper que vous appelez via DLLImport. Et il appellerait votre Library64.dll

  • Le dernier recours serait de supprimer de force la DLL non managée avec encore un autre appel non géré. Vous n'avez pas créé la DLL à l'aide de LoadLibrary(), mais vous devriez pouvoir obtenir une référence en utilisant GetModuleHandleExA() et en utilisant ce handle dans un appel à FreeLibrary(). Ceci est décrit dans l'article de blog suivant: PInvoke Library Load/Unload Behavior – Freeing Unmanaged Libraries. Il semble que c'était la seule méthode pour réussir. S'il vous plaît voir @answer de John pour le code spécifique.

+0

Eh bien, malheureusement, cela ne fonctionne pas . Après OFF et ON encore je ne peux pas supprimer la DLL natif ... – John

+0

@John Je viens d'ajouter une requête pour montrer ce que les domaines d'application et les assemblys sont actuellement chargés. Veuillez confirmer que rien dans cette base de données n'est chargé après avoir réglé TRUSTWORTHY OFF puis à nouveau ON. Pourrait-il y avoir un autre DB avec une référence? J'ai également posté la requête pour désactiver l'intégration CLR et la réactiver. Je pense que si rien d'autre, _that_ supprimerait des références. Pouvez-vous mettre à jour la question avec un peu de votre code peut-être? La partie qui charge la DLL externe? –

+0

Je confirme qu'après avoir mis OFF, la requête n'a rien donné. Ne peut toujours pas supprimer le fichier. – John

1

Voici ce que j'ai fait et ce qui semble fonctionner. S'il vous plaît dites-moi si c'est faux de le faire ..

J'ai ajouté deux ces StoredProcedure comme CLR supplémentaire:

[SqlProcedure] 
public static void asdUnloadLibrary() 
{ 
    try 
    { 
     var hMod = IntPtr.Zero; 
     if (GetModuleHandleExA(0, "Engine64.dll", ref hMod)) 
     { 
      while (FreeLibrary(hMod)) 
      { } 
     } 
     else 
     { 
      throw new Exception("Library not found"); 
     } 
    } 
    catch (Exception e) 
    { 
     throw e; 
    } 
    return; 
} 

[SqlProcedure] 
public static void asdLoadLibrary() 
{ 
    try 
    { 
     var hMod = IntPtr.Zero; 
     LoadLibrary("Engine64.dll"); 
    } 
    catch (Exception e) 
    { 
     throw e; 
    } 
    return; 
} 

maintenant ... Si je veux copier au serveur un nouveau fichier DLL natif je vais:

1) Execute asdUnloadLibrary stored procedure. This will unload the dll. 
2) Then, I can copy another version of dll to system folder 
3) Then I can (but I think it is not necessary) do it: 

ALTER DATABASE TEST SET TRUSTWORTHY OFF 
GO 
ALTER DATABASE TEST SET TRUSTWORTHY ON 
GO 

4) Execute asdLoadLibrary 

Et maintenant la fonction UDF d'origine fonctionne à nouveau comme prévu ...

+0

John, j'ai mis à jour ma réponse pour inclure cette suggestion qui était à l'origine juste dans un commentaire que j'ai fait sur ma réponse. J'ai également ajouté un lien vers cette réponse pour tous ceux qui cherchent le code exact. –