从C++/CLI调用C++-DLL时出现AccessViolation错误

3
我已经为一个C++-DLL写了一个C++/CLI包装器,以便在C#程序中使用该DLL。

然而,在调用一个需要char*参数的函数时,我会遇到AccessViolation错误。

int Wrapper::Net_methodX(int a, String^ key, long v)
{
    IntPtr ptr = Marshal::StringToHGlobalAnsi(key);
    pin_ptr<char> cKey = static_cast<char*>(ptr.ToPointer());
    int val = methodX(a,cKey, v); // AccessViolation here

    Marshal::FreeHGlobal(ptr);
    return val;
}

C++函数的签名为:
int methodX(int a, char *Key, long v);

编辑1

仅仅像下面这样“固定”也不起作用:

int Wrapper::Net_methodX(int a, String^ key, long v)
{
    IntPtr ptr = Marshal::StringToHGlobalAnsi(key);
    char* cKey = static_cast<char*>(ptr.ToPointer());
    pin_ptr<char> pinned = cKey;
    int val = methodX(a,cKey, v);

    Marshal::FreeHGlobal(ptr);
    return val;
}

编辑1结束

编辑2

我也尝试了以下方式使用PtrToStringChars(感谢Matt,我还在这里找到了一些文档here):

int Wrapper::Net_methodX(int a, String^ key, long v)
{
    pin_ptr<const wchar_t> wkey = PtrToStringChars(key);

    size_t convertedChars = 0;
    size_t  sizeInBytes = ((key->Length + 1) * 2);
    errno_t err = 0;
    char * ckey = (char * ) malloc(sizeInBytes);

    err = wcstombs_s(&convertedChars, ckey, sizeInBytes, wkey, sizeInBytes);

    int val = methodX(A_Symbol_Table,ckey, Value);

    return val;
}

仍然发生AccessViolation,可能是方法X()(第三方DLL)中的错误。

编辑2结束

我已经阅读了一些相关的问题,但还没有找到解决方案。

有什么提示吗? 谢谢。

3个回答

2

我知道这是一个老问题,但对于任何想要找到答案的人,以下是一些更简单的解决方案。

  1. 只需使用 sprintf 进行转换,例如:sprintf(cStr, "%s", clrString);。请参见我的回答,其中包含完整的示例。
  2. 按照 Matt Smith 的建议阅读KB311259。如果您正在使用 VS 2008 或更高版本,请使用 marshal_as<>(KB 中的第 4 种方法)。它比该文档中的其他方法简单得多。

刚刚遇到了同样的问题。marshal_as<const char*> 对我非常有效,之前使用 Marshal::StringToHGlobalAnsi 时遇到了麻烦。 - KChaloux

0

如链接中所建议的,我尝试将cKey“固定”以避免GC,并将char*传递给C++函数。但仍然发生了AccessViolation。 - Simon

0

Simon, 我尝试了你的例子,但我没有遇到访问冲突。这是我的代码:

using namespace System;
using namespace System::Runtime::InteropServices;

ref class Wrapper
{
public:
    static int Net_methodX(int a, String^ key, long v);
};

int methodX(int a, char * pKey, long v)
{
    IntPtr ptr = static_cast<IntPtr>(pKey);
    String ^ pString = Marshal::PtrToStringAnsi(ptr);
    System::Console::WriteLine(pString);
    return a;
}

int Wrapper::Net_methodX(int a, String^ pKey, long v)
{     
    IntPtr ptr = Marshal::StringToHGlobalAnsi(pKey);     
    pin_ptr<char> cKey = static_cast<char*>(ptr.ToPointer());     
    int val = methodX(a,cKey, v); // AccessViolation here      
    Marshal::FreeHGlobal(ptr);     
    return val; 
}

void main()
{
    Wrapper wrapper;
    String ^ p = gcnew String("Hello");
    wrapper.Net_methodX(0, p, 0);
}

另外,我有几个评论:

  1. 请阅读这里:http://support.microsoft.com/kb/311259
  2. 您正在使用 pin_ptr 来访问本地内存。StringToHGlobalAnsi 方法返回本地内存,因此在这里使用 pin_ptr 没有意义。如果您使用的是返回指向托管内存的指针的方法(例如 PtrToStringChars),那么使用 pin_ptr 就有意义了。除非您要修改字符串,否则最好采用 PtrToStringChars 方法--以避免不必要的分配和复制。
  3. 您能发布一个导致问题的 methodX 的示例版本吗?如果我能重现这个问题,我可能会更有帮助。

感谢在第二条回复中的澄清。我尝试使用PtrToStringChars(请参见我原始帖子中的编辑2),但仍然出现了访问冲突。不幸的是,methodX()来自第三方DLL,因此没有源代码,只有简陋的文档。我只能跟踪方法已输入a、key和v的值。 - Simon
为了排除 methodX 可能存在问题的可能性,您尝试过传递一个本地分配的 char * 吗(实际上,看起来您的编辑 2 尝试了这个)?如果您得到了一个指示该方法已被调用的 TRACE,则我怀疑 methodX 是罪魁祸首(或者您正在向其传递某些它不期望的内容)。 - Matt Smith
是的,我也认为问题在 methodX 方法中。我只是想确认一下,包装器中没有错误。感谢您的帮助。 - Simon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接