4

我有一些关于 DLL 的设计问题,我目前在你可能称之为它的地方,正如维基百科所指的那样DLL Hell问题如下:

我创建了一个系统,该系统具有多个实现为 DLL 的模块。这些在应用程序中使用,并且可以加载 DLL,但并非所有这些都是必需的。如果只完成数学之类的事情,它可以链接到“Utilities.dll”并使用它。问题是,我有一个记录器/跟踪器。这会将所有内容记录到一个文件和一个调试控制台中,调试控制台只是一个流输出。问题是如何处理多个 DLL 尝试使用同一个日志类。看到日志类在这个“Utilities.dll”中,“DataManagers.dll”和其他 dll 之类的东西也想使用日志类功能。这包括登录到文件。我目前正在使用临界区来确保不会发生写入冲突,但看到临界区是在用户模式下实现的 d 必须在某些时候切换到互斥锁或类似的东西才能拥有内核模式对象。但是,如果我只使用关键部分,那么在 DLL 内存中拥有多个日志类实例意味着我会遇到一些严重的问题。

我似乎无法一起困惑的是,所有 DLL 都能够使用相同的日志类实例,而不必一个一个地链接到 Utilities.dll。我不想将 8 个 dll 加载到我的演示项目中,并让所有这 8 个引用带有日志类的那个 dll,如果我需要更多类似日志类的东西,这将是一个连锁反应。有没有办法正确地做到这一点?使用类的功能,在其他 DLL 中的 DLL 中使用静态函数,在 .exe Windows 二进制文件中使用相同的“静态”函数,从而不会在写入日志文件甚至调试控制台的输出流时发生冲突。

如果我完全错了并试图做不可能的事情,请告诉我并帮助实现尽可能接近此的目标。我知道在 DLL 中使用 Singleton 模式时会出现一些类似的问题,但这可以通过以下方式解决

到目前为止我已经尝试过:

  • 初始化 DLL 的类时,给它们一个日志库的实例,但这违背了类具有所有静态成员的目的。

我也发现了这个问题,它是 similair (甚至我的全局工具的库的名称都在那个..)但它没有回答我的问题并且有一些不同的方法以及来自 '09 . 如何模仿静态库的“应用程序中全局变量的多个实例”行为但使用 DLL?

4

3 回答 3

5

您的问题根本不是“DLL Hell”。

这是一个基本了解 DLL 如何工作的问题。每个进程最多加载一次 DLL。因此,如果您的 DLL 被多个其他 DLL 使用,它仍然会在每个进程中存在一次。如果您的日志类对象在您的 DLL 中实现为单例(例如全局对象),那么每个进程都有一个对象。

然后应该保护一个对象以防止在进程中同时使用。关键部分是流程本地的,非常适合这一点。您不需要互斥体,因为两个进程各自都有自己的 Utilities.DLL 及其对象的副本。

如果您的记录器记录到单个固定文件,您可能会遇到问题。在这种情况下,两个进程会尝试登录到同一个文件。这是一个设计问题,无论如何您都不想破解。将您的日志输出分开,因此请确保每个记录器写入一个唯一的日志文件。

于 2012-05-22T07:19:19.767 回答
1

从下图可以看出图片

实际上,在一个进程中多次映射一个 DLL 是很常见的(与一种非常流行的观点相反)!这是一个基本了解 DLL 如何工作的问题 :-)

在干净的 Windows XP 系统上,您可以在 iexplore.exe 进程中看到多个 dxtmsft.dll、ieframe.dll、iepeers.dll 等实例。所有这些 Dll 都只是映射到不同的地址。

于 2012-06-22T11:28:44.400 回答
0

使用 WinApi 的 CreatePipe 或 CreateNamedPipe 函数,这是一个使用 Windows 共享内存的类似队列的通信部分:

    BOOL WINAPI CreatePipe(
  __out         PHANDLE hReadPipe,
  __out         PHANDLE hWritePipe,
  __in          LPSECURITY_ATTRIBUTES lpPipeAttributes,
  __in          DWORD nSize
);

或者

    HANDLE WINAPI CreateNamedPipe(
  __in          LPCTSTR lpName,
  __in          DWORD dwOpenMode,
  __in          DWORD dwPipeMode,
  __in          DWORD nMaxInstances,
  __in          DWORD nOutBufferSize,
  __in          DWORD nInBufferSize,
  __in          DWORD nDefaultTimeOut,
  __in          LPSECURITY_ATTRIBUTES lpSecurityAttributes
);

使用 ReadFile 和 WriteFile 通过管道从其他线程/进程中进行通信。

要从管道中读取,进程在调用 ReadFile 函数时使用读取句柄。ReadFile 在以下情况之一为真时返回:在管道的写端完成写操作、已读取请求的字节数或发生错误。

当进程使用 WriteFile 写入匿名管道时,直到写入所有字节后,写入操作才会完成。如果在写入所有字节之前管道缓冲区已满,则 WriteFile 不会返回,直到另一个进程或线程使用 ReadFile 来提供更多可用缓冲区空间。

使用一个线程处理输入并将日志写入文件和/或控制台。

更多关于管道的信息:http: //msdn.microsoft.com/en-us/library/windows/desktop/aa365780 (v=vs.85).aspx

于 2012-05-21T23:45:42.340 回答