user2577497
user2577497

Reputation: 497

C++ DLLs to C# error: "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

I have 2 DLLs. One is PrimaryDLL.dll and the other is DLLWrap.dll. The PrimaryDLL has an object a function named DiversifyKeyset which is decalred like so in the class constructor:

static PRIMARYDLL_API std::string DiversifyKeyset(std::string Key1, std::string Key2, std::string Key3);

and here is the method defined:

    std::string tKeyset::DiversifyKeyset(std::string Key1, std::string Key2, std::string Key3)
{
    tKeyset* keyset = new tKeyset();

    keyset ->Key1 = "12345678";
    keyset ->Key2 = "23456789";
    keyset ->Key3 = "34567890";

    //return keyset --eventually I want to return this
    return 0;

}

My DLLWrap.dll calls this function like so in its .cpp file:

cout << "Here is what is returned:  " <<
firstDllLayer::tKeyset::DiversifyKeyset(a,b,c) << endl;

Now this all compiles fine and the libraries are built. My error comes when I call the C# code to call the second DLLWrap.dll. I receieve the error:

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

So I have been racking my brain as to why this is not working. I am under alot of pressure but cannot seem to find why I am receieveing this memory write error.

So to help I am providing my code.

My C++ PrimaryDLL.h file:

#pragma once

#include "stdafx.h"
#include <string>
#include <stdexcept>

extern "C"{

#ifdef PRIMARYDLL_EXPORT
    #define PRIMARYDLL_API __declspec(dllexport) 
#else
    #define PRIMARYDLL_API __declspec(dllimport)  
#endif



namespace firstDllLayer
{

class tKeyset
{


public:
    tKeyset();
    std::string Key1;
    std::string Key2;
    std::string Key3;
    tKeyset(std::string _key1, std::string _key2, std::string _key3);
    static PRIMARYDLL_API std::string DiversifyKeyset(std::string Key1, std::string Key2, std::string Key3);


};

tKeyset::tKeyset()
{
    Key1 = "";
    Key2 = "";
    Key3 = "";
}
tKeyset::tKeyset(std::string _Key1, std::string _Key2, std::string _Key3)
{
    Key1 = _Key1;
    Key2 = _Key2;
    Key3 = _Key3;
}


}
}

Here is the PrimaryDll.cpp file:

// PrimaryDll.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "PrimaryDll.h"
#include <stdexcept>
#include <string>
using namespace std;
namespace firstDllLayer
{
    tKeyset* MasterKeyset = new tKeyset("1234","5678","0910");
    std::string tKeyset::DiversifyKeyset(std::string Key1, std::string Key2, std::string Key3)
{
    tKeyset* keyset = new tKeyset();
    keyset ->Key1 = "12345678";
    keyset ->Key2 = "23456789";
    keyset ->Key3 = "34567890";
    //return Keyset; -eventually I would like to return this
    return 0;
}
}

Here is my DLLWrap.h file:

#pragma once

#include "stdafx.h"
#include "Primary.h"
#include <stdexcept>
#include <string>
extern "C"{
#ifdef DLLWRAPDLL_EXPORT
    #define DLLWRAP_API __declspec(dllexport) 
#else
    #define DLLWRAP_API __declspec(dllimport)  
#endif
namespace MyFunc
{
    class MyCall
        {
        public:
            static DLLWRAP_API std::string DiversifyKeysetCall(std::string a,std::string b,std::string c);
    };
}
}

The DLLWrap.cpp file:

// DLLWrap.cpp : Defines the exported functions for the DLL application.
//


#include "stdafx.h"
#include "PrimaryDll.h"
#include <stdexcept>
#include "DLLWrap.h"
#include <iostream>
#include <string>

using namespace std;

namespace MyFunc
{
    std::string MyCall::DiversifyKeysetCall(std::string a,std::string b,std::string c)
    {
            /*cout << "Here is what is returned:  " <<
                firstDllLayer::tKeyset::DiversifyKeyset(a,b,c) << endl;

            return 0;*/
        cout << "Here is what is returned:  " <<
                firstDllLayer::tKeyset::DiversifyKeyset(a,b,c) << endl;

            return 0;           
    }
}

And finally the C# Program.cs file:

// Marshal.cs
using System;
using System.Runtime.InteropServices;
class PlatformInvokeTest
{    public class DllHelper
    {
        [DllImport(@"C:\Users\user\Documents\Visual Studio 2010\Projects\KeyDll\Debug\DLLWrap.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?DiversifyKeysetCall@MyCall@MyFunc@@SA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V34@00@Z")]

        public static extern string DiversifyKeysetCall(string a, string b, string c);
    }
    static void Main()
    {    
        try
        {
            DllHelper.DiversifyKeysetCall("1234","5678","0910");           
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        }
}

In my PrimaryDLL.cpp file, I would eventually like to return the object but because it is so confusing on how to reference the pointer and when i just leave the :

return keyset; 

un commented I receieve the error that it cannot convert from object type to string type.

So please anyhelp would be greatly appreciated to why I am receiving the "Attempted to read or write protected memory. This is often an indication that other memory is corrupt" error.

Best Regards.

Upvotes: 0

Views: 7954

Answers (2)

madnut
madnut

Reputation: 144

Its a bad Idea in general to create an object in c++ dll and hand it back to c#. The ownership of the memory becomes a problem. Strings are ok because they get Marshaled across by coping from c memory to the c# owned memory.

I suggest you simplify the whole thing by passing the three strings separately and building your keyset object already in c#.

Another option is to flatten you keyset object into a binary array of bytes. Then have a custom function when you get it back to unflatten it back into an object.

Here is what i have done before

[DllImport("Helper.dll", CharSet = CharSet.Auto)]
private static extern bool GetStuff(
    [MarshalAs(UnmanagedType.LPStr)] StringBuilder inputString,
    byte[] outputBuffer, 
    UInt32 outputSize,
    ref UInt32 outputFinalSize);

public static void DoStuff(string inputString)
{
    int buffSize = 4000;
    byte[] buff = new byte[buffSize];
    StringBuilder inputString = new StringBuilder();
    inputString.Append(blobs);
    UInt32 size = 0;

    if (!GetStuff(inputString, buff, (uint)buffSize, ref size) || size == 0)
    {
        //ERROR
    }

    when it comes back i just do

    StringBuilder outputString;
    for (uint i = 0; i < outputFinalSize; i++)
    {
        outputString.Append((char)buff[i]);
    }

    outputString.toString();
}

c signatrue looks like this

extern "C" __declspec(dllexport) bool GetStuff(char* inputString, char* outputBuffer, UInt32 outputSize, UInt32& outputFinalSize);

Note that i use string builder to pass the string in as well, you may not need that... As you can see i allocate everything on the c# side so i don't have to worry about memory management of the c++, everything in c++ should get deallocated when the function completes.

Your call should look somthing like

private static extern bool DiversifyKeysetCallAndRetrunFlatBuffer(
    [MarshalAs(UnmanagedType.LPStr)] StringBuilder inputStringA,
    [MarshalAs(UnmanagedType.LPStr)] StringBuilder inputStringB,
    [MarshalAs(UnmanagedType.LPStr)] StringBuilder inputStringC,
    byte[] outputBuffer, 
    UInt32 outputSize, 
    ref UInt32 outputFinalSize);

{
    std::string str1(inputStringA);
    std::string str2(inputStringB);
    std::string str3(inputStringC);
    KeySet key = DiversifyKeyset(str1, str2, str3);

    outputFinalSize = key.SerializeToBuffer(outputBuffer, outputSize);

    if (outputFinalSize == 0) 
    {
        return false;
    }
    return true;
}

int KeySet::SerializeToBuffer(char* buffer, size_t bufferSize)
{
    //We are going to fill the buffer like so "key1|key2|key3"

    size_t totalSize = Key1.size() + Key2.size() + Key3.size() + 4;
    if (bufferSize < totalSize)
    {
        return 0; // buffer too small
    }
    char* bufferCurr = buffer;


    memcpy(bufferCurr, Key1.c_str(), Key1.size());
    bufferCurr += Key1.size();
    bufferCurr[0] = '|';
    bufferCurr++;

    memcpy(bufferCurr, Key2.c_str(), Key2.size());
    bufferCurr += Key2.size();
    bufferCurr[0] = '|';
    bufferCurr++;

    memcpy(bufferCurr, Key3.c_str(), Key3.size());
    bufferCurr += Key3.size();
    bufferCurr[0] = '\0';
    return totalSize;
}

And finally on the c# side you would convert the buffer to a string and do a split by | to get all 3 keys, then create a c# KeySet Object that has the 3 strings.

Upvotes: 3

Igor Tandetnik
Igor Tandetnik

Reputation: 52621

std::string tKeyset::DiversifyKeyset(...) {
  return 0;
}

This calls std::string(const char*) constructor, passing a NULL pointer. This is illegal.

Upvotes: 1

Related Questions