tags:

views:

328

answers:

4

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);
}
A: 

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.

monksy
A: 

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.

Naveen
I came up with the StdStringContainer class because I need to use std::vector and to be able to iterate it. However, I cannot expose STL variables like std::vector, std::string, etc as the function parameter. How can I correct this error?
Lopper
Iterators are not invalidated when the vector reallocates-- pointers are. That's the goal behind using iterators instead of pointers on a vector. Iterators may be invalidated when an element is removed from a vector.
Greg D
@Greg D: Check note #2 here: http://www.sgi.com/tech/stl/Vector.html
Naveen
@KK: Why not store an integer index instead of iterator?
Naveen
A draw a distinction between reallocation (an implementation may shuffle a vector around memory however it wants) and invalidation (which happens when the vector is changed). Sgi's docs don't appear to draw the same distinction. Specifically, via Stroustrup (19.2): "An iterator can be invalid ...because it [isn't] initialized, it pointed into a container that was resized, the container was destroyed, or it denotes the end of a sequence." There is no concept of "reallocation" regarding invalidation of iterators.
Greg D
Can the items in std::vector be accessed with an integer index?
Lopper
I think Naveen is right. I added a default constructor with this assignment in it _it = _items.begin(); but I still get the same error!!!
Lopper
of course yes..it provides operator[].
Naveen
And, if you want out_of_range exception to be thrown in case of invalid index use at() method. Both provide O(1) time complexity.
Naveen
@KK: Of course it continues to crash. When you add a new string to your container, you invalidate your existing iterators because you've changed the container's size. This includes `_it`. Please read my comment and the quote from Stroustrup regarding invalidation of iterators.
Greg D
@Greg: Sorry I am not sure this is the best place to ask this, is there any case where vector will reallocate other than when it wants to resize?
Naveen
Thanks Naveen, problem solved when I used integer index in place of iterators!
Lopper
@Naveen: Probably not, but it's important to maintain a distinction between implementation detail (reallocation), and language spec (four things that cause invalidation of pointers). Failing to do so is likely to result in dependencies on implementation details that will break future compatibility.
Greg D
@Greg: Thanks for the reply.
Naveen
A: 

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.

Timbo
+1  A: 

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?

Greg D