作为一个学习/测试我的限制练习,我正在尝试创建一个可以获取给定值的DLL,对其进行序列化,并对其进行反序列化以用于使用其他编译器编译的程序。到目前为止,一切都好于预期。但是,我遇到了一个似乎是内存损坏的问题。
这是我正在使用的代码:
//POD_base.h: contains the base template POD (plain old data) class. Also contains memory allocation and deallocation functions.
namespace pod_helpers
{
void* pod_malloc(size_t size)
{
HANDLE heapHandle = GetProcessHeap();
HANDLE storageHandle = nullptr;
if (heapHandle == nullptr)
{
return nullptr;
}
storageHandle = HeapAlloc(heapHandle, 0, size);
return storageHandle;
}
void pod_free(void* ptr)
{
HANDLE heapHandle = GetProcessHeap();
if (heapHandle == nullptr)
{
return;
}
if (ptr == nullptr)
{
return;
}
HeapFree(heapHandle, 0, ptr);
}
}
template<typename T>
class pod
{
protected:
pod();
pod(const T& value);
pod(const pod& copy); // no copy ctor in any pod
~pod();
pod<T>& operator=(pod<T> value);
operator T() const;
T get() const;
void swap(pod<T>& first, pod<T>& second);
};
pod
的{{1}}专门化:
int
我的DLL利用了这样的幕后类型转换:
//POD_basic_types.h: contains pod specializations for basic datatypes
template<>
class pod<int>
{
typedef int original_type; //these typedefs are meant to make the specializations easier to write and understand, since they're all the same except for the underlying datatypes.
typedef std::int32_t safe_type;
public:
pod() : data(nullptr) {}
pod(const original_type& value)
{
set_from(value);
}
pod(const pod<original_type>& copyVal)
{
original_type copyData = copyVal.get();
set_from(copyData);
}
~pod()
{
release();
}
pod<original_type>& operator=(pod<original_type> value)
{
swap(*this, value);
return *this;
}
operator original_type() const
{
return get();
}
protected:
safe_type* data;
original_type get() const
{
original_type result;
result = static_cast<original_type>(*data);
return result;
}
void set_from(const original_type& value)
{
data = reinterpret_cast<safe_type*>(pod_helpers::pod_malloc(sizeof(safe_type)));
if (data == nullptr)
{
return;
}
new(data) safe_type (value);
}
void release()
{
if (data)
{
pod_helpers::pod_free(data);
data = nullptr;
}
}
void swap(pod<original_type>& first, pod<original_type>& second)
{
using std::swap;
swap(first.data, second.data);
}
};
然后我的测试程序通过virtual pod<int> Add(const pod<int> number1, const pod<int> number2);
pod<int> CCDLL_v1_implementation::Add(const pod<int> number1, const pod<int> number2)
{
int workingNum1, workingNum2;
workingNum1 = number1;
workingNum2 = number2;
return workingNum1 + workingNum2;
}
/ LoadLibrary
加载DLL。到现在为止还挺好;我已经确认实际加载了DLL并调用了GetProcAddress
函数。我还验证了使用正确的值调用Add
的{{1}}。但是,这就是事情发生的地方。
我希望pod<int>
为一个set_from
(在本例中为set_from
)值分配足够的空间,然后将传递的值存储在已分配的内存中。当我在safe_type
中检查std::int32_t
的值时,似乎就是这种情况。但是,当我通过*data
检索set_from
的值时,广告连播的pod<int>
似乎是垃圾。它不再指向get
期间data
传递的值。
我知道在EXE方面调用了pod
,在DLL方面调用了set_from
。我对这个过程的理解如下:
set_from
这似乎是破坏DLL或EXE必须访问其他分配的内存的地方。我见过elsewhere on SO使用get
/ EXE -> creates pod<int> (allocating memory via GetProcessHeap/HeapAlloc) -> constructs the pod with a given value, which is passed to set_from.
The DLL's Add function is called, with the given pod passed to it.
DLL -> accesses pod<int> -> gets the pod's stored value via get -> accesses the memory the EXE allocated
DLL does its calculations (here, a simple addition) with the pod's value
DLL -> creates pod<int> (allocating memory via GetProcessHeap/HeapAlloc) -> constructs the pod with a given value, which is passed to set_from.
The DLL's newly-constructed pod is returned to the EXE.
EXE -> accesses the pod's internal value via get -> accesses the memory the DLL allocated
的这种组合应跨DLL边界工作。 HeapCreate
's documentation也暗示了同样的想法:
私有堆对象的内存只能由创建它的进程访问。如果动态链接库(DLL)创建私有堆,则会在调用DLL的进程的地址空间中创建堆,并且只能由该进程访问它。
按the documentation for GetProcessHeap
(强调我的):
GetProcessHeap函数获取调用进程的默认堆的句柄。
然而,奇怪的是,GlobalAlloc
遇到了同样的问题GetProcessHeap
/ HeapAlloc
,让我进一步质疑这里出了什么问题。甚至更奇怪,当我使用相同的编译器编译EXE和DLL时,一切都按预期工作。
我对这个分配/解除分配过程的工作方式做出了错误的假设吗?我应该使用GetProcessHeap
/ HeapAlloc
以外的其他内容吗?或者我只是想做不可能的事情?
使用评论中获得的信息进行更新:
通过引用传递GetProcessHeap
(HeapAlloc
正常工作。只传递值不会。
我是否将“unwrapped”参数传递给pod
- 获取函数或者我是否首先将参数包装在CCDLL_v1_implementation::Add(const pod<int>& number1, const pod<int>& number2)
中似乎无关紧要:
pod
产生与
相同的损坏pod
pod<int> a = 9;
pod<int> b = 2;
CCDLL_lib->Add(a, b);
唯一的区别似乎是在data
s中首先包装参数会在调用CCDLL_lib->Add(9, 2);
时调用copy c'tor,而保留参数unwrapped只会调用常规c'tor。 / p>
这似乎也不是类布局问题:
pod
在EXE / DLL边界的两边评估为true。
我设法证明问题在于EXE / DLL边界,尽管我仍然不知道为什么。
我在DLL中添加了一个新方法,因此我可以专注于一个参数而不是两个:
Add
然后我这样称呼它:
if (reinterpret_cast<const void*>(&data) == reinterpret_cast<const void*>(this))
{
//simple debug messagebox here
}
从一系列调试消息框中,出现以下序列:
void Square(pod<int> number);
如果我更改pod<int> a = 9;
CCDLL_lib->Square(a);
以接受引用(Regular c'tor called (EXE)
set_from(9) called (EXE)
copy c'tor called (EXE)
get returns 9 to the copy c'tor (EXE)
set_from(9) called (EXE)
Square called (DLL) with an invalid pod (Visual Studio shows the data pointer pointing to garbage at this point)
),则会出现以下序列:
Square