【问题标题】:Attempted to read or write protected memory. C++ dll imported to C#尝试读取或写入受保护的内存。导入到 C# 的 C++ dll
【发布时间】:2013-09-25 20:54:47
【问题描述】:

我有一个用 C++ 构建的 dll 和一个用 C# 构建的软件 - windows 窗体。

当我通过 C# 调用 dll 时,我遇到了一个致命异常:

试图读取或写入受保护的内存。这通常表明其他内存已损坏。

来电:

    [DllImport("dlltest.dll")]
    [return: MarshalAs(UnmanagedType.SafeArray)]
    private extern static string[] getFiles(string directory, string fileFilter, bool recrusive);

这是我的 DLL 源代码:

extern "C" __declspec(dllexport) LPSAFEARRAY getFiles(string directory, std::string fileFilter, bool recrusive);

LPSAFEARRAY getFiles(string directory, std::string fileFilter, bool recrusive)
{
       std::vector<std::string> filesArray;

if (recrusive)
    getFilesByDirs(directory, fileFilter, false);

directory += "\\";

WIN32_FIND_DATAA FindFileData;
HANDLE hFind = INVALID_HANDLE_VALUE;

std::string filter = directory + (recrusive ? "*" : fileFilter);

hFind = FindFirstFileA(filter.c_str(), &FindFileData);

if (hFind == INVALID_HANDLE_VALUE)
{

}
else
{
    if (!recrusive)
    {
        if(isGoodForUs(directory + std::string(FindFileData.cFileName))) 
        {
            filesArray.push_back(directory + std::string(FindFileData.cFileName));
        }
    }

    while (FindNextFileA(hFind, &FindFileData) != 0)
    {
        if (!recrusive)
        {
            if(!isGoodForUs(directory + std::string(FindFileData.cFileName))) continue;
                filesArray.push_back(directory + std::string(FindFileData.cFileName));
        }
        else
        {
            if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)>0 && FindFileData.cFileName[0]!='.')
            {
                if(!isGoodForUs(directory + std::string(FindFileData.cFileName))) continue;
                    filesArray.push_back(directory + std::string(FindFileData.cFileName));
            }
        }
    }
}

CComSafeArray<BSTR> a(filesArray.size());

std::vector<std::string>::const_iterator it;
int i = 0;
for (it = filesArray.begin(); it != filesArray.end(); ++it, ++i)
{
    a.SetAt(i, A2BSTR_EX((*it).c_str()), FALSE);
}
return a.Detach();
}

有人知道是什么问题吗?

【问题讨论】:

  • 你是在调试器下运行的,看看代码中哪一行触发了错误?
  • @TimoGeusch 是的,当我通过 C# 使用 getFiles 方法时。
  • 你不能编组 C++ STL 字符串。坚持 LPCTSTR。对于使用 SafeArray 的返回类型是可以的,但我不确定 Marshaller 是否会识别它。您可以尝试将其声明为返回类型对象,然后将其转换为 string[],以便您可以在调试器中检查 Marshaller 对您的数组所做的操作。
  • 又一个缺少 CallingConvention 的案例,它是 Cdecl。

标签: c# c++


【解决方案1】:

真正的问题是您为什么不使用与 GetFiles() 调用等效的 .Net 方法

Directory.GetFiles Method (String path, String searchPattern, SearchOption searchOption)

您可以避免调试 C++ 代码、额外的部署问题以及在托管代码和非托管代码之间切换的执行惩罚。

编辑添加

当您想从 C# 恢复使用 C++ DLL 时,最轻松的方法是创建一个 C++ 包装类,使其尽可能简单。 Microsoft 建议在您尝试使用的“平台调用”上使用此方法(并且由于 std::string 类而遇到问题)。

首先,请参阅 MSDN article,了解 C#/C++ 互操作性的概述。其次,this article给出一个 C++ 类包装的简单示例。 Microsoft 指的是在“正常工作”的方法中使用 C++ 包装类。

我已经用 C 和 C++ 完成了大量跨平台编程,我必须祝你好运,它需要大量的努力才能让它正确。由于您显然使用 C++ 作为跨平台互操作层,因此建议您为互操作代码创建跨平台库(将其与应用程序代码分开)。其次,尽可能为您的互操作创建外部 C 接口。这确实是“通用”调用格式,没有名称修改等问题。第二个问题是您需要多少互操作?基本文件和目录并不难。套接字是另一组问题。多线程是另一个。我之前用过ACE,它一直是C++,已经存在了很长时间——但它是一个非常强大的C++互操作库(除其他外)。

【讨论】:

  • 我的想法是在其他平台上使用这个 C++ 代码。当然我需要为跨平台更改一些功能,但真正的交易是因为在其他平台上的使用。此外,我有很多 C++ 代码连接到 DLL 中的 Main 函数。所以现在要更改为 C# 方法。
  • 嗨@GaryWalker,因为这个项目的优先级和浪费时间,我将使用临时的C#方法来执行这种操作。但现在我无法获取任何文件,结果为空。您可以在编辑后的帖子中看到代码,上面 ^^
  • 您没有调用正确的方法。我提到的方法有 3 个参数,第三个允许指定递归下降
  • 在我最初的回答中添加了一些互操作建议
猜你喜欢
  • 2014-03-16
  • 1970-01-01
  • 1970-01-01
  • 2010-10-09
  • 1970-01-01
  • 2010-10-10
相关资源
最近更新 更多