如何从SQL Server进程卸载本机DLL(使用[DllImport()]属性加载)?

时间:2015-08-29 20:25:56

标签: sql-server sqlclr

我创建了一个SQLCLR用户定义函数,它调用本机C ++ DLL。

本机C ++ DLL是我的,如果我想在其中进行更改,我需要停止sqlservr进程来复制新进程。在生产中是不可接受的。

即使我删除了使用该DLL的程序集,该文件仍在使用中。

如何覆盖原生DLL?

修改

DLL方法声明:

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

MS似乎在第一次调用它时加载库,并且当进程终止时似乎卸载....:)

2 个答案:

答案 0 :(得分:1)

听起来App Domain正在举办“参考”。您可以尝试卸载应该清除它的应用域(最好的猜测,因为我无法测试这个)。您可以通过对数据库进行任何安全性更改来实现此目的。以下作品:

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

请记住,这将卸载该特定数据库中的所有AppDomain。这通常不是问题,因为人们很少在一个数据库中拥有多个应用程序域(这需要程序集由不同的用户拥有,大多数人只使用dbo)。

要查看应用程序域存在的内容以及加载到哪些程序集,请运行以下命令:

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;

如果该查询没有返回任何内容并且您仍然无法替换该外部DLL,请尝试以下操作(这看起来有点多,但我们需要在尝试其他任何操作之前知道它是否有效):

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

另外两个要尝试的选项是:

  • 创建一个可以通过DLLImport调用的包装器DLL。它会调用你的Library64.dll

  • 最后一种方法是用另一个非托管调用强制删除非托管DLL。您没有使用LoadLibrary()创建DLL,但您应该能够使用GetModuleHandleExA()获取对它的引用,然后在调用FreeLibrary()时使用该句柄。以下博文中介绍了这一点:PInvoke Library Load/Unload Behavior – Freeing Unmanaged Libraries这似乎是唯一获得成功的方法。有关具体代码,请参阅@ John answer

答案 1 :(得分:1)

这是我所做的以及似乎有效的方法。请告诉我这样做是不对的..

我添加了两个额外的CLR存储过程,如下所示:

[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;
}

现在......如果我想将新的本机DLL文件复制到服务器,我将:

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

现在原始UDF功能按预期再次工作......