安全的字符串复制超过DLL边界

Safe string copying over DLL boundaries

本文关键字:DLL 边界 复制 字符串 安全      更新时间:2023-10-16

我试图使一个DLL暴露了一个特定的API,因此想实现一个安全的方式来复制字符串在DLL边界。DLL的实现非常直接——所有返回字符串值的函数都有两个参数——char*size_t&。如果大小足够大I memcpy从DLL内到给定指针的字符串的内容,设置大小为实际的大小,并返回一个成功的返回码。如果不是,我将大小设置为应有的大小并返回一个错误代码。DLL端非常简单。

现在更复杂的是-如何制作一个很好的模板函数,它给出了一个指向DLL中的一些函数的指针,将执行所有正确的操作来填充std::string的实例。这是我得出的结论:

template<typename I>
CErrorCode GetStringValue(const I& Instance, CErrorCode(I::*pMethod)(char*, size_t&) const, std::string& sValue)
{
    std::string sTemporaryValue;
    size_t nValueLength = sTemporaryValue.capacity();
    sTemporaryValue.resize(nValueLength);
    do
    {
        auto eErrorCode = (Instance.*pMethod)(const_cast<char*>(sTemporaryValue.c_str()), nValueLength);
        if (eErrorCode == CErrorCode::BufferTooSmall)
        {
            sTemporaryValue.resize(nValueLength);
        }
        else
        {
            if (eErrorCode == CErrorCode::NoError)
            {
                sTemporaryValue.resize(nValueLength);
                sValue = std::move(sTemporaryValue);
            }
            return eErrorCode;
        }
    } 
    while (true);
}

所以我做了最初的resize,因为我不能在第一次调用后这样做(因为它会擦除内容,因为字符串最初是空的)。但是resize用零字符填充字符串,这让我有点不安(我的意思是我知道我要自己填充)。然后我需要resize,即使在成功运行后,因为如果长度实际上更小,我需要调整大小。有什么建议,如果这可以做一个更好的方式?

既然你正在使用dll,我的理解是你是在Windows上,所以我将建议一个特定于Windows的解决方案。

在Windows上,COM内存分配器和 BSTR 已经解决了安全传递字符串跨模块边界的问题。

所以,而不是传递一个字符串指针和一个大小指针,并检查调用者分配的缓冲区是否足够大,如果不返回所需的大小等,对我来说似乎更简单,只是从DLL返回 BSTRs。

BSTR使用COM分配器分配,因此您可以跨不同的模块边界分配和释放它们。

BSTR周围也有很好的c++包装类,例如 _bstr_t 或ATL的 CComBSTR 。你可以在来电者的网站上使用。

一旦你有了一个BSTR实例,可能在调用者的站点被一个方便的RAII包装器正确包装,你可以很容易地将它转换为std::wstring

或者,如果您想使用Unicode UTF-8编码和std::string,您可以使用WideCharToMultiByte()BSTR的本机UTF-16编码转换为UTF-8(这篇MSDN杂志文章可以派上用场)。

附加阅读:Eric 's Complete Guide To BSTR Semantics

注:
如果你不想使用BSTR类型,你仍然可以使用COM提供的通用内存分配器:例如,你可以在DLL中使用 CoTaskMemAlloc() 分配(UTF-8)字符串内存,并在调用者的站点使用 CoTaskMemFree() 释放它。