C# & C++: "Attempted to read or write protected memory" Error
The following code compile without errors. Basically, the C#2005 Console application calls VC++2005 class library which in turn calls native VC++6 code. I get the following error when I run the C#2005 application:
"Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
What is the cause of this error? And how to go about correcting it?
Edit1: It crashes at the line StdStringWrapper ssw = w.GetNext();
Edit2: I followed the advice of Naveen and used an integer index instead of iterators and there is no more errors now. A big thanks to all who commented as well!
Code Written in C#2005 as Console Application:
class Program
{
static void Main(string[] args)
{
Class1 test= new Class1();
test.PerformAction();
test.PerformAction();
test.PerformAction();
test.PerformAction();
}
}
Code Written in VC++2005 as Class Library:
public ref class Class1
{
public:
void PerformAction();
};
void Class1::PerformAction()
{
DoSomethingClass d;
StdStringContainer w;
d.PerformAction(w);
for(int i=0; i<w.GetSize(); i++)
{
StdStringWrapper ssw = w.GetNext();
std::cout << ssw.CStr() << std::endl;
}
}
Code Written in VC++6 as Dynamic Link Library:
#ifdef NATIVECODE_EXPORTS
#define NATIVECODE_API __declspec(dllexport)
#else
#define NATIVECODE_API __declspec(dllimport)
#endif
class NATIVECODE_API开发者_运维知识库 StdStringWrapper
{
private:
std::string _s;
public:
StdStringWrapper();
StdStringWrapper(const char *s);
void Append(const char *s);
const char* CStr() const;
};
StdStringWrapper::StdStringWrapper()
{
}
StdStringWrapper::StdStringWrapper(const char *s)
{
_s.append(s);
}
void StdStringWrapper::Append(const char *s)
{
_s.append(s);
}
const char* StdStringWrapper::CStr() const
{
return _s.c_str();
}
//
class NATIVECODE_API StdStringContainer
{
private:
std::vector<StdStringWrapper> _items;
std::vector<StdStringWrapper>::iterator _it;
public:
void Add(const StdStringWrapper& item);
int GetSize() const;
StdStringWrapper& GetNext();
};
void StdStringContainer::Add(const StdStringWrapper &item)
{
_items.insert(_items.end(),item);
}
int StdStringContainer::GetSize() const
{
return _items.size();
}
StdStringWrapper& StdStringContainer::GetNext()
{
std::vector<StdStringWrapper>::iterator it = _it;
_it++;
return *it;
}
//
class NATIVECODE_API DoSomethingClass
{
public:
void PerformAction(StdStringContainer &s);
};
void DoSomethingClass::PerformAction(StdStringContainer &s)
{
StdStringWrapper w1;
w1.Append("This is string one");
s.Add(w1);
StdStringWrapper w2;
w2.Append("This is string two");
s.Add(w2);
}
The member _it
in StdStringContainer
is never initialized to point into the _items
vector. This means it's an invalid iterator. When you assign _it
to it
in GetNext()
, you've given it
the invalid, uninitialized value that existed in _it
. You then increment the uninitialized _it
via _it++
, which is what's triggering your fault.
As Stroustrup says in 19.2, an uninitialized iterator is an invalid iterator. This means that your uninitialized _it
is invalid and that operations performed with it are undefined, and likely to cause dramatic failure.
Your problem is deeper, however. Iterators have a fundamentally different lifetime from the containers that they enumerate. There aren't really any "good" ways to do what you're trying to do with a single iterator member like this unless the container is immutable and initialized in the constructor.
If you can't expose the std:: namespace names, have you considered aliasing them via typedef's, e.g.? What about your organization or project makes it impossible to expose the template classes?
The main problem from my point of view is you are storing an iterator
to a vector
in your stdStringContainer
class. Remember that whenever vector resizes all the existing iterators are invalidated. So whenever you do insert operation into the vector it may be possible that it resizes and your existing iterator becomes invalid. If you try to to dereference it in GetNext()
then it will access invalid memory location. For checking whether this really the case try to reserve the initial vector size to some relatively big number so that the resizing doesn't happen. You can reserve the size using reserve()
method, in which case it is guaranteed that the capacity()
of the vector is greater than or equal to the reserved value.
Sounds like you have a memory leak. I would suggest looking anywhere where there is pointer arithmetic, writing to memory, or array usage. Check for the bounds conditions in the array accessing.
Another issue: The leak many not even be in your code. If this is the case you'll have to exclude the library from your project.
My guess is, that you have the crash because std::string
and std::vector
in the interface between two C++ modules were compiled with different compilers and runtime libraries.
The memory layout of vector and string maybe changed between VC6 and 2005.
When the 2005 DLL allocates objects of type StdStringContainer
and StdStringWrapper
, it does so based on the declarations of string
and vector
in the 2005 headers.
When member functions are called on these objects (which have been compiled with the VC6 compiler and libraries), they assume a different memory layout and fail with access violations.
精彩评论