卸载DLL后,我可以存储在DLL中创建的对象吗?

时间:2013-09-02 04:46:49

标签: c++ winapi memory-management dll

我需要对以下代码做一些澄清。

在下面的代码中,我正在从DLL中检索接口的私有实现。

卸载DLL后(CASE#2),从接口检索到的字符串,接口本身都变为无效,访问时会发生(预期)访问冲突。

但令我困惑的是,当我重新加载DLL时,我可以再次使用接口和字符串,而无需从DLL中重新检索它们。

这应该是这样的吗?

内存如何变得无效,再次加载DLL后它们会突然再次生效?

这只是运气,并且由于我的测试程序相对简单,DLL第二次方便地加载到内存中的完全相同的位置?

int main(int argc, int *argv[])
{
    Interface *itf;
    const char *name;

    {
        // loads the dll and gets functions
        PersistenInterface pi("PersistentInterface.dll");

        // returns a private implementation of the interface
        itf = pi.CreateInterface();
        name = itf->GetName();

        // CASE #1
        cout << name << endl; // suceeds

    } // dll is unloaded in ~PersistenInterface()

    // CASE #2
    //cout << name << endl; // crashes
    //cout << itf->GetName() << endl; // crashes

    {
        PersistenInterface pi("PersistentInterface.dll");

        // CASE #3
        cout << name << endl; // suceeds !?
        cout << itf->GetName() << endl; // suceeds !?
    }

    return 0;
}

2 个答案:

答案 0 :(得分:2)

我希望你不要从DLL返回堆栈上的本地对象。它肯定会崩溃您的系统,因为在DLL返回函数后本地对象被销毁。它与DLL的概念无关。如果在包含函数返回后尝试引用本地对象,则会看到同样的问题。

如果内存是在DLL内的堆上动态分配的,那么在未初始化DLL之后它仍然有效。您仍然可以在主应用程序中使用内存,因为DLL本身并不拥有任何内容。请记住,堆属于进程,而不是DLL。

但是,一般规则是该内存的所有者(创建该内存,例如您的DLL中的内存)负责释放相同的内存。如果双方使用不同的CRT实例,那么在DLL中分配内存并在另一方取消分配可能会给您带来不必要的麻烦,例如

答案 1 :(得分:1)

答案有点复杂。实际上,如果你加载一个DLL,它并没有真正加载到你的堆内存中。它被加载到全局内存中,并且这个内存被映射到你的地址空间。通过这种机制,操作系统可以节省内存,因为它可以将相同的代码映射到不同的进程中。因此,如果访问DLL空间中的地址,访问将重定向到DLL真正存在的全局内存中。卸载后,此地址不再有内存。您的访问权限将不再重定向,将引发异常。这与malloc和free(或new和delete)不同,其中只有内存被标记为未使用,但仍然存在所有数据(除非激活零存储器)。静态DLL数据(如常量字符串)和DLL的代码的行为如上所述。再次加载DLL后,您拥有的地址(显然指向静态数据或代码)再次生效。至少Windows XP总是在同一地址空间加载DLL。但是不要依赖于此!出于安全原因,现代操作系统可以决定在每次使用loadLibrary时或每次启动进程时将进程的DLL映射到不同的地址。在调试器或Windows XP中,据我所知,DLL总是映射到同一地址,这就是代码行为的原因。它不是bug或功能,而是DLL概念。