我创建了一个SQLCLR用户定义函数,它调用本机C ++ DLL。
本机C ++ DLL是我的,如果我想在其中进行更改,我需要停止sqlservr
进程来复制新进程。在生产中是不可接受的。
即使我删除了使用该DLL的程序集,该文件仍在使用中。
如何覆盖原生DLL?
修改
DLL方法声明:
[DllImport("Library64.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr GetTiming();
MS似乎在第一次调用它时加载库,并且当进程终止时似乎卸载....:)
答案 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功能按预期再次工作......