*** 发现一个严重的问题: DLL中的全局共享变量分配的内存, 谁来释放??? (欢迎讨论)

时间:2022-07-10 02:26:38

这个DLL的特性就是:  多个应用都会从这个DLL中获取数据, 这个数据由第一次调用DLL时分配

但是什么时候释放呢?



//定义全局共享数据段
#pragma data_seg("ALL_Devide")

Device *g_AllDevice= NULL;  //全局的数据
int m_nRef=0;

#pragma data_seg()
#pragma comment (linker,"/SECTION:ALL_Devide,RWS")


当第一次使用后,  然后给这个变量分配了内存

当所有调用者都结束(我定义了一个计数器), 然后释放内存


现在的问题是: 
1. 碰到有人说全局数据段不能有指针 或者说是动态分配内存的变量???

14 个解决方案

#1


D L L从进程的地址空间中被卸载时,系统将调用D L L的D l l M a i n函数,给它传递f d w R e a s o n
的值D L L _ P R O C E S S _ D E TA C H。当D L L处理这个值时,它应该执行任何与进程相关的清除操
作。
请参考<<windows核心编程>>20章

#2



主要是有人说:

说全局数据段不能有指针 或者说是动态分配内存的变量???


#3


///////////////TestDll.h
#ifdef __cplusplus
#define EXPORT extern "C" __declspec (dllexport)
#else
#define EXPORT __declspec (dllexport)
#endif

EXPORT BOOL CALLBACK Show();

////////////TestDll.cpp
#include "TestDll.h"
#pragma data_seg ("shared")
char *pszCharTest = NULL;
#pragma data_seg ()

#pragma comment(linker,"/SECTION:shared,RWS")

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
 )
{
switch(ul_reason_for_call) 
{
case DLL_PROCESS_ATTACH:
pszCharTest = new char[10];
break;
case DLL_PROCESS_DETACH:
delete []pszCharTest;
break;
default:
break;
}
    return TRUE;
}

EXPORT BOOL CALLBACK Show()
{
strcpy(pszCharTest,"Ok");
MessageBox(NULL,pszCharTest,"",1);
return 1;
}

测试一下,可以证明Dll有自己的内存空间,动态分配的内存和调用进程无关,可通过共享数据段定义的指针访问.没发现不对的地方

#4


我最近做的一个DLL,里面有方法调用CreateProcessAsUser,将该子进程返回的记录保存在DLL的一个全局Vector中,然后用迭代器(是一种指针吧?)访问。没问题!

感谢 shizhen !学到东西了。

#5


mark

#6


分配内存时怎么会和进程无关呢?如果您在不同的进程中调用共享内存段中的指针来访问内存,并且没有问题的话,只能说你运气好。大部分情况下你的运气都会比较好,因为你的程序可能不太大,调用的模块不多,多个进程装载DLL时刚好映射到了相同的地址而已。
您可以通过几个方式来验证:
1、将共享的DLL编译成两份文件,修改每个文件的默认装载地址,然后运行看看结果。
2、测试的两个进程中多装载几个DLL文件,并且使装在它们的顺序在每个EXE中不同。

#7


感觉问题在于给dll中定义的全局共享指针申请内存空间的代码是在exe还是dll里
如果在exe里,指针指向exe空间的地址,其它调用此dll的进程访问指针指向的内容时会出错
如果在dll里,指针指向dll空间的地址,这时访问则正常

不知是不是这么回事,还请高人解答

#8


在共享段里面放内存指针,是很容易出现问题的。因为共享段的页面属性不是写时复制属性了。因此多个装载该dll的进程使用同一该全局变量,而不是进程所私有的。如果第一个进程用该全局变量分配内存,则该内存是在该进程的地址空间内。如果第二个进程恰好在该虚拟地址区域内有提交的内存页面,则第二个进程还是能访问内存,只不过访问的是自己地址空间中的内存,而且是不安全的访问,可能会覆盖破坏该内存区域已有的数据。如果说第二个进程在该地址区域没有提交内存页面,则明显会产生访问违例。所以,在共享段中还是不要放指针的好。所以楼上说“如果在exe里,指针指向exe空间的地址,其它调用此dll的进程访问指针指向的内容时会出错”是不一定的。这样的错误是很难调试的。
    至于楼上说的“如果在dll里,指针指向dll空间的地址,这时访问则正常”,也是会有错误的。因为指针是所有进程共享的,而DLL的映射地址在不同进程中可能是不同的,虽然最佳地址是0x10000000,但是如果该地址被占用,则系统会选择其他的地址并对DLL中的地址引用做修改。因此,不能保证所有进程访问该共享全局指针变量所指的地址对所有的进程空间来说都有效。如果出现访问正常,也只是说明该DLL被多个进程映射到相同的地址而已,是不安全的。
    因此,在共享段中,还是只存放一般类型的变量好。

#9


shizhen(失贞) 的代码是有问题的,运气好的情况下,是可以运行的。但是有内存泄漏和内存失效的问题。非常危险的问题。当调用该DLL的exe实例多运行几个时,就会有问题!

#10


hinstance好像也是个指针吧!我用的时候就出问题了:
我在dllmain中给它赋了值,我只是把它做为参数给SetWindowsHookEx,回来它就变了!!晕~~
这是怎么会事啊?
更怪的是,这个hinstance指向一个进程的,它变了,那个进程居然没事??不过好像有几次也出事了!这又是怎么回事?

#11


句柄并不是指针,是句柄表中句柄项的偏移量

#12


..

#13


要分配并且共享内存的话,考虑保存GlobalAlloc分配的内存句柄,或者用内存映射文件。
跨进程访问指针,或者说内存地址是不安全的。

#14


那我用它一次,它怎么会变啊??

#1


D L L从进程的地址空间中被卸载时,系统将调用D L L的D l l M a i n函数,给它传递f d w R e a s o n
的值D L L _ P R O C E S S _ D E TA C H。当D L L处理这个值时,它应该执行任何与进程相关的清除操
作。
请参考<<windows核心编程>>20章

#2



主要是有人说:

说全局数据段不能有指针 或者说是动态分配内存的变量???


#3


///////////////TestDll.h
#ifdef __cplusplus
#define EXPORT extern "C" __declspec (dllexport)
#else
#define EXPORT __declspec (dllexport)
#endif

EXPORT BOOL CALLBACK Show();

////////////TestDll.cpp
#include "TestDll.h"
#pragma data_seg ("shared")
char *pszCharTest = NULL;
#pragma data_seg ()

#pragma comment(linker,"/SECTION:shared,RWS")

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
 )
{
switch(ul_reason_for_call) 
{
case DLL_PROCESS_ATTACH:
pszCharTest = new char[10];
break;
case DLL_PROCESS_DETACH:
delete []pszCharTest;
break;
default:
break;
}
    return TRUE;
}

EXPORT BOOL CALLBACK Show()
{
strcpy(pszCharTest,"Ok");
MessageBox(NULL,pszCharTest,"",1);
return 1;
}

测试一下,可以证明Dll有自己的内存空间,动态分配的内存和调用进程无关,可通过共享数据段定义的指针访问.没发现不对的地方

#4


我最近做的一个DLL,里面有方法调用CreateProcessAsUser,将该子进程返回的记录保存在DLL的一个全局Vector中,然后用迭代器(是一种指针吧?)访问。没问题!

感谢 shizhen !学到东西了。

#5


mark

#6


分配内存时怎么会和进程无关呢?如果您在不同的进程中调用共享内存段中的指针来访问内存,并且没有问题的话,只能说你运气好。大部分情况下你的运气都会比较好,因为你的程序可能不太大,调用的模块不多,多个进程装载DLL时刚好映射到了相同的地址而已。
您可以通过几个方式来验证:
1、将共享的DLL编译成两份文件,修改每个文件的默认装载地址,然后运行看看结果。
2、测试的两个进程中多装载几个DLL文件,并且使装在它们的顺序在每个EXE中不同。

#7


感觉问题在于给dll中定义的全局共享指针申请内存空间的代码是在exe还是dll里
如果在exe里,指针指向exe空间的地址,其它调用此dll的进程访问指针指向的内容时会出错
如果在dll里,指针指向dll空间的地址,这时访问则正常

不知是不是这么回事,还请高人解答

#8


在共享段里面放内存指针,是很容易出现问题的。因为共享段的页面属性不是写时复制属性了。因此多个装载该dll的进程使用同一该全局变量,而不是进程所私有的。如果第一个进程用该全局变量分配内存,则该内存是在该进程的地址空间内。如果第二个进程恰好在该虚拟地址区域内有提交的内存页面,则第二个进程还是能访问内存,只不过访问的是自己地址空间中的内存,而且是不安全的访问,可能会覆盖破坏该内存区域已有的数据。如果说第二个进程在该地址区域没有提交内存页面,则明显会产生访问违例。所以,在共享段中还是不要放指针的好。所以楼上说“如果在exe里,指针指向exe空间的地址,其它调用此dll的进程访问指针指向的内容时会出错”是不一定的。这样的错误是很难调试的。
    至于楼上说的“如果在dll里,指针指向dll空间的地址,这时访问则正常”,也是会有错误的。因为指针是所有进程共享的,而DLL的映射地址在不同进程中可能是不同的,虽然最佳地址是0x10000000,但是如果该地址被占用,则系统会选择其他的地址并对DLL中的地址引用做修改。因此,不能保证所有进程访问该共享全局指针变量所指的地址对所有的进程空间来说都有效。如果出现访问正常,也只是说明该DLL被多个进程映射到相同的地址而已,是不安全的。
    因此,在共享段中,还是只存放一般类型的变量好。

#9


shizhen(失贞) 的代码是有问题的,运气好的情况下,是可以运行的。但是有内存泄漏和内存失效的问题。非常危险的问题。当调用该DLL的exe实例多运行几个时,就会有问题!

#10


hinstance好像也是个指针吧!我用的时候就出问题了:
我在dllmain中给它赋了值,我只是把它做为参数给SetWindowsHookEx,回来它就变了!!晕~~
这是怎么会事啊?
更怪的是,这个hinstance指向一个进程的,它变了,那个进程居然没事??不过好像有几次也出事了!这又是怎么回事?

#11


句柄并不是指针,是句柄表中句柄项的偏移量

#12


..

#13


要分配并且共享内存的话,考虑保存GlobalAlloc分配的内存句柄,或者用内存映射文件。
跨进程访问指针,或者说内存地址是不安全的。

#14


那我用它一次,它怎么会变啊??