Delphi DLL未卸载,可能是由于仍然分配了GDI

Delphi DLL未卸载,可能是由于仍然分配了GDI,delphi,dll,delphi-2007,Delphi,Dll,Delphi 2007,我们编写了一个DLL来执行所有的打印功能。它允许我们进行打印预览、打印以及生成PDF 我们现在有了这个DLL的一种特殊的使用模式,在此模式之后,DLL无法正确卸载。结果是,当我们退出主程序时,它会消失但不会终止,我们必须手动终止该进程 我认为罪魁祸首是一些GDI资源没有被适当地“关闭”(当我说这句话时,我是出于对GDI的极大无知而说的)。我们不直接处理GDI资源,而是在使用各种Delphi组件时使用GDI资源 有没有关于如何发现和解决此类问题的提示?我们使用GDIView来确认在我们尝试终止程序

我们编写了一个DLL来执行所有的打印功能。它允许我们进行打印预览、打印以及生成PDF

我们现在有了这个DLL的一种特殊的使用模式,在此模式之后,DLL无法正确卸载。结果是,当我们退出主程序时,它会消失但不会终止,我们必须手动终止该进程

我认为罪魁祸首是一些GDI资源没有被适当地“关闭”(当我说这句话时,我是出于对GDI的极大无知而说的)。我们不直接处理GDI资源,而是在使用各种Delphi组件时使用GDI资源

有没有关于如何发现和解决此类问题的提示?我们使用GDIView来确认在我们尝试终止程序后,一些GID资源仍然存在,但是我不知道如何将这些窗口/内核句柄与底层Delphi代码关联起来。我可以在程序执行期间的不同时间提供GIDView列表

谢谢

乔恩(德尔福2007)


我回应了提出的建议,但没有人回应我的回应。最终,我放弃了。最后我写了一个自杀程序,它不仅杀死了我自己的进程,还杀死了其他同名进程,以此来解决问题。荒谬的过度杀戮,但我似乎别无选择。我正在使用我在某处找到的免费软件ProcessInfo工具

procedure KillNamedProcesses(pName : String);
// used to clean up programs that hang as a result of DLLs not unloading
   var
      ProcessInfo : TProcessInfo;
      ProcessName : String;
      i : INTEGER;
      currentPID : cardinal;
   BEGIN
   currentPID := GetCurrentProcessID;
   pName := UpperCase(pName);
   ProcessInfo := TProcessInfo.Create(nil);

   // kill all old processes (not our process)
   for i := 0 to ProcessInfo.RunningProcesses.Count - 1 do begin
      ProcessName := ProcessInfo.RunningProcesses[i].ExeFile;
      IF (UpperCase(ProcessName) <> pName) THEN CONTINUE;
      IF (currentPID <> ProcessInfo.RunningProcesses[i].ProcessID) then
         ProcessInfo.RunningProcesses[i].TerminateProcess;
      END;
   // kill the last one (ourselves)
   for i := 0 to ProcessInfo.RunningProcesses.Count - 1 do begin
      ProcessName := ProcessInfo.RunningProcesses[i].ExeFile;
      IF (UpperCase(ProcessName) <> pName) THEN CONTINUE;
      ProcessInfo.RunningProcesses[i].TerminateProcess;
      END;
   ProcessInfo.Free;
   END; // KillNamedProcess
过程KillNamedProcesses(pName:String);
//用于清理由于DLL未卸载而挂起的程序
变量
ProcessInfo:TProcessInfo;
ProcessName:String;
i:整数;
当前PID:基数;
开始
currentPID:=GetCurrentProcessID;
pName:=大写字母(pName);
ProcessInfo:=TProcessInfo.Create(nil);
//杀死所有旧流程(不是我们的流程)
对于i:=0到ProcessInfo.runningprocesss.Count-1,则不开始
ProcessName:=ProcessInfo.RunningProcesses[i].ExeFile;
IF(大写(ProcessName)pName)则继续;
如果是(currentPID ProcessInfo.runningprocesss[i].ProcessID),则
ProcessInfo.RunningProcesses[i].TerminateProcess;
结束;
//杀死最后一个(我们自己)
对于i:=0到ProcessInfo.runningprocesss.Count-1,则不开始
ProcessName:=ProcessInfo.RunningProcesses[i].ExeFile;
IF(大写(ProcessName)pName)则继续;
ProcessInfo.RunningProcesses[i].TerminateProcess;
结束;
ProcessInfo.Free;
完KillNamedProcess

您无需返回GDI资源即可关闭流程。当然,你应该归还它们,但这不会阻止你终止合同。听起来很像是DLL卸载中出现了死锁。由于DLL无法卸载,因此自然无法释放所有GDI资源


调试这将涉及调试死锁。在这方面很难给出一般性的建议,不幸的是,您使用的是较旧且功能较差的Delphi版本。现代的Delphi调试器支持等待链遍历,使调试死锁变得相当简单。我想如果我是你,当你的应用程序处于死锁状态时,我会做一些事后调试。我会查看所有线程的堆栈跟踪,并使用它来确定无限期阻塞的对象。当程序处于死锁状态时,使用Process Explorer和map2dbg获取有意义的堆栈跟踪。

GDI句柄未释放不会导致DLL(或应用程序)未正确卸载或关闭。你得去别处看看。您是否使用任何与COM相关的资源或接口(可能在您提到的PDF创建中)?您的dll是否使用gdi+?当应用程序处于挂起状态时暂停调试器不会显示任何内容?Ken-感谢您关于gdi句柄的观点。我可以让库挂起而不生成PDF,所以这不是问题所在。虽然我们确实使用了一些其他DLL,但我不相信我们使用COM对象。有没有明确的方法来回答你的问题?换句话说,我们没有在应用程序中显式安装任何COM对象(尽管我猜我们可能在不知道的情况下使用了一些OS COM对象)mrabat-我假设GDI+是一个库。我在源代码中搜索了“GDIPlus”,但没有找到它。如果GDI不是根本问题,那么什么会导致“DLL卸载中的死锁”?请问,我在哪里可以阅读更多关于它的信息?当处于死锁状态时,我使用Process Explorer检查属性。有四个线程正在运行:!CreateThread、lhwindowthread、lExitThread和ntdll.dlllrtlconvertlistoapilist。如果您愿意,我可以为这些提供堆栈跟踪(500个字符的限制很难做到)