C++.NET包装程序:试图读取或写入受保护的内存.这通常表示其他内存已损坏

本文关键字:内存 常表示 已损坏 其他 表示 受保护 程序 包装 NET 读取 C++ | 更新日期: 2023-09-27 18:25:46

我有一个未经管理的MFC dll的C++.net包装器。这个包装器由一个vb.net dll使用,它被调用到我的c#代码中。在运行时,有时包装器会抛出"尝试读取或写入受保护内存"的异常。

System.AccessViolationException: Attempted to read or write protected memory. 
This is often an indication that other memory is corrupt

它似乎随机出现在我的"While"循环中。有时它在开始时抛出,有时在中间抛出,有时什么也没抛出。

其工作方式:我的程序需要一个MFC dll。我的程序中引用了一个wrapper.dll(c++)和一个myVbDell.dll(vb.net)。我还添加了MFC dll作为内容,因为它不是有效的COM组件。这就是它的工作原理:

myProgramm.exe->myVbDell.dll->wrapper.dll->myMFC.dll->myMFC函数

信息:如果我在调用MyWrappedFunction之前设置field = "WHATSOEVER";,则永远不会引发错误!!

更新:经过多次更改后,问题仍然存在。这一次我看到了将unicode字符串转换为Ansi。也许有什么可以找到的。。。因为当您如上所述在字符串中写入文本时,它是有效的,但当使用ToString函数时,它不起作用。

有人能说出为什么会发生这种情况吗。

我在c#中的部分程序(使用TextFieldParser从.csv文件中读取5000行以获取字段):

string[] fields;
string field ;
string temp = "";
TextFieldParser parser = new TextFieldParser(textbox_csv.Text, System.Text.Encoding.UTF8);
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(";");
while (!parser.EndOfData)
{
    fields = parser.ReadFields();
    field = fields[0];
    temp = field.ToUpper();
    field = myVbDll.MyWrappedFunction(ref temp, false);
}

VB.net Dll的一部分,由我的c#程序调用:

Public Class myVbDll
Public Declare Auto Function MyWrappedFunction Lib "myWrapper.dll" (ByVal name As String, ByVal opt As Boolean) As String
End Class

MFC包装器的一部分,由VB.net Dll调用(错误肯定不在MFC Dll中):

typedef void (*MYFUNCTION)(CString&, CString&, BYTE);
MYFUNCTION Myfunction;
LPWSTR _stdcall MyWrappedFunction(LPWSTR ValInput, BYTE opt)
{
    HINSTANCE gLibtestDLL=NULL;
    CString S_ValInput(ValInput);
    CString S_resultat;
    gLibtestDLL = AfxLoadLibrary(TEXT(".''test.dll"));
    if(gLibtestDLL == NULL)
    {
        MessageBox(NULL, TEXT("unable to load test.DLL"), TEXT("Error"),MB_OK | MB_ICONINFORMATION);
        return NULL;
    }
    Myfunction = (MYFUNCTION)GetProcAddress(gLibtestDLL, "Myfunction");
    if (Myfunction == NULL)
    {
        MessageBox(NULL, TEXT("Can't find Myfunction."), TEXT("Error"),MB_OK | MB_ICONINFORMATION);
        return NULL;
    }
    //******************************************************************
    S_resultat.LockBuffer();
    S_resultat.Format("%64c", ' ');
    Myfunction(S_ValInput , S_resultat , opt);
    S_resultat.ReleaseBuffer();
    S_resultat.LockBuffer();
    S_resultat.TrimRight();
    S_resultat.ReleaseBuffer();
    // CString To UNICODE
    USES_CONVERSION;
    S_resultat.LockBuffer();
    LPWSTR C_tmp= A2OLE(S_resultat.GetBuffer(S_resultat.GetLength()));
    S_resultat.ReleaseBuffer();
    AfxFreeLibrary(gLibtestDLL);
    LPWSTR C_resultat=C_tmp;
    //******************************************************************
    return C_resultat;
}

C++.NET包装程序:试图读取或写入受保护的内存.这通常表示其他内存已损坏

LPWSTR C_tmp= A2OLE(S_resultat.GetBuffer(S_resultat.GetLength()));

这是C++代码中非常严重的错误。您正在返回一个局部变量的地址。由A2OLE()创建的缓冲区。调用C++代码中的未定义行为。当你从C++代码中调用这个函数时,这种情况往往是偶然发生的,你很可能不会有另一个函数调用覆盖该局部变量曾经所在的堆栈地址。当你对函数进行pinvoke时,这些几率会变为零,堆栈地址会被pinvoke-mashaller函数调用抹杀。如果这本身并没有导致崩溃,那么pinvokemarshaller将确保在尝试使用CoTaskMemFree()释放字符串时崩溃。

您必须首先修复您的C++代码。不,仅仅将指针复制到C_resultat中并不能解决问题。

请注意,尝试挽救此功能没有多大意义。它不会做任何你在C#中做不到的事情。只需为"MyFunction"函数编写一个[DllImport]属性。

终于解决了这个问题!

问题的真正原因是编码字符串。以下是我为改进代码所做的所有更改。

如果你看到我做的可疑事情可以改进,请随时评论

我的主要应用程序:

private void processImport()
{
    string[] fields;
    string field ;
    string temp = "";
    byte[] tempByte;
    TextFieldParser parser = new TextFieldParser(textbox_csv.Text, System.Text.Encoding.UTF8);
    parser.TextFieldType = FieldType.Delimited;
    parser.SetDelimiters(";");
    while (!parser.EndOfData)
    {
        fields = parser.ReadFields();
        field = fields[0];
        temp = RemoveDiacritics(field).ToUpper();
        //this line is very important. It seems to change the Encoding of my string to Unicode.
        temp = temp.Normalize(NormalizationForm.FormC);
        field = myVbDll.MyWrappedFunction(temp, false);
    }
    parser.Close();
}
//Transforms the culture of a letter to its equivalent representation in the 0-127 ascii table, such as the letter 'é' is substituted by an 'e'
public string RemoveDiacritics(string s)
{
    string normalizedString = null;
    StringBuilder stringBuilder = new StringBuilder();
    normalizedString = s.Normalize(NormalizationForm.FormD);
    int i = 0;
    char c = ''0';
    for (i = 0; i <= normalizedString.Length - 1; i++)
    {
        c = normalizedString[i];
        if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
        {
            stringBuilder.Append(c);
        }
    }
    return stringBuilder.ToString().ToLower();
}

VB.net Dll的一部分,由我的c#程序调用:

Public Class myVbDll
    <DllImport("Wrapper.dll", CharSet:=CharSet.Unicode)> Public Shared Function MyWrappedFunction (ByVal nom As String, ByVal opt As Boolean) As String
    End Function
End Class

C++包装器的一部分,由VB.net Dll:调用

typedef void (*MYFUNCTION)(CString&, CString&, BYTE);
MYFUNCTION Myfunction;
LPWSTR _stdcall MyWrappedFunction(LPWSTR ValInput, BYTE opt)
{
    HINSTANCE gLibtestDLL=NULL;
    CString S_ValInput(ValInput);
    CString S_resultat;
    gLibtestDLL = AfxLoadLibrary(TEXT(".''test.dll"));
    if(gLibtestDLL == NULL)
    {
        MessageBox(NULL, TEXT("unable to load test.DLL"), TEXT("Error"),MB_OK | MB_ICONINFORMATION);
        return NULL;
    }
    Myfunction = (MYFUNCTION)GetProcAddress(gLibtestDLL, "Myfunction");
    if (Myfunction == NULL)
    {
        MessageBox(NULL, TEXT("Can't find Myfunction."), TEXT("Error"),MB_OK | MB_ICONINFORMATION);
        return NULL;
    }
    //******************************************************************
    S_resultat.Format("%64c", ' ');
    Myfunction(S_ValInput , S_resultat , opt);  //Run MFC function
    S_resultat.TrimRight();
    S_resultat.ReleaseBuffer();
    char* tmp = (char*) CoTaskMemAlloc((S_resultat.GetLength()*2)+1);
    memset(tmp,0,sizeof(tmp));
    strcpy_s(tmp, (S_resultat.GetLength()+1), S_resultat.GetBuffer(S_resultat.GetLength()));
    S_resultat.ReleaseBuffer();
    wchar_t wtext[1024];
    memset(wtext,0,sizeof(wtext));
    mbstowcs(wtext, tmp, strlen(tmp)+1);//Plus '0
    LPWSTR resultat = wtext;
    AfxFreeLibrary(gLibtestDLL);
    return resultat;
}