Reputation: 497
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
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
Reputation: 52621
std::string tKeyset::DiversifyKeyset(...) { return 0; }
This calls std::string(const char*) constructor, passing a NULL pointer. This is illegal.
Upvotes: 1