0xC0000005: Access violation reading location 0xff

2019-08-09 02:33发布

问题:

I have:

  • An unmanaged C++ app
  • A C++/CLI Wrapper
  • A C# GUI

I am seeing this crash occur only in Release and not debug. The crash also does not occur on neither debug or release when the unmanaged C++ app is run by itself.

All I can say is the crash occurs in this line of code:

            if ((std::find(vstrEEDRRMachines.begin(), vstrEEDRRMachines.end(), m_sFrame.strSourceAddress) != vstrEEDRRMachines.end()
                && std::find(vstrEEDRRMachines.begin(), vstrEEDRRMachines.end(), m_sFrame.strDestAddress) != vstrEEDRRMachines.end())
                || (std::find(vstrRRRHMachines.begin(), vstrRRRHMachines.end(), m_sFrame.strSourceAddress) != vstrRRRHMachines.end()
                && std::find(vstrRRRHMachines.begin(), vstrRRRHMachines.end(), m_sFrame.strDestAddress) != vstrRRRHMachines.end())) 
            {
                // Create appropriate buffer size for raw message (i.e. size of payload along with the extra padding
                // for decoding)
                m_sFrame.iMessageSize = m_sFrame.iPayloadLength;
                m_sFrame.iOriginalMessageSize = m_sFrame.iPayloadLength;
                m_sFrame.pszMessage = new unsigned char[m_sFrame.iPayloadLength + Constants::RSSL_DECODE_PADDING];
                m_sFrame.pszOriginalMessage = new unsigned char[m_sFrame.iPayloadLength + Constants::RSSL_DECODE_PADDING];
            }

Although I can't see what's inside vstrEEDRRMachines and vstrRRRHMachines (because we are in release), I can see when in the original C++/CLI wrapper that there are valid string entries in the vector.

The stack trace is as follows:

msvcr100.dll!0000000069abbdc0() [Frames below may be incorrect and/or missing, no symbols loaded for msvcr100.dll]
DataVerifier.exe!std::_Find,std::allocator

  • __ptr64,std::basic_string,std::allocator

    (std::basic_string,std::allocator > * _First, std::basic_string,std::allocator > * _Last, const std::basic_string,std::allocator > & _Val) Line 41 + 0x4a bytes C++ DataVerifier.exe!DataVerifier::CPCAPParser::Parse(const char * szFileName, std::vector & vSFramesRWF1Messages, std::vector,std::allocator ,std::allocator,std::allocator

    • vstrEEDRRMachines, std::vector,std::allocator ,std::allocator,std::allocator
    • vstrRRRHMachines, RsslDataDictionary & rsslDataDictionary) Line 178 + 0x19 bytes C++ [External Code]
      DataVerifierLib.dll!DataVerifierLib::PCAPParserWrapper::ParseWrapper(System::String^ strInputFileNames) Line 136 + 0xf6 bytes C++ DataVerifierGUI.exe!DataVerifierGUI.Form1.button1_Click(object sender, System.EventArgs e) Line 42 + 0x30 bytes C# user32.dll!00007fff7a8c250d() user32.dll!00007fff7a8c2367()
      System.Windows.Forms.ni.dll!00007fff535368c0()

The crash specifically occurs in c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\algorithm in this part of the code:

        // TEMPLATE FUNCTION find
template<class _InIt,
    class _Ty> inline
    _InIt _Find(_InIt _First, _InIt _Last, const _Ty& _Val)
    {   // find first matching _Val
    for (; _First != _Last; ++_First)
        if (*_First == _Val) <-- CRASH OCCURS HERE
            break;
    return (_First);
    }

I have no idea what's going on, since release mode with C# + C++/CLI + C++ apps is where the crash is occurring. Is there any way I can easily fix this?

EDIT

It seems like this occurs 9 times out of 10, but i've noticed the times it does work, when I check the parameters of the Parse function just as it goes into the function (no lines are executed yet), all 3 vectors have normal values (the 1st one is 0, the second and third contain IP addresses as expected). However, most of the time, as soon as we get into the Parse function, all 3 vectors have negative size and capacities... and when they are used, everything goes boom. I'm going to past the entrance of the Parse function and also the C++/CLI wrapper before it.

bool CPCAPParser::Parse(const char* szFileName, std::vector<CRWFCapsule> &vSFramesRWF1Messages, std::vector<std::string> vstrEEDRRMachines, std::vector<std::string> vstrRRRHMachines, RsslDataDictionary &rsslDataDictionary) // change to a vector of rwf1capsules
{

This is the C++/CLI Wrapper:

// This is the main DLL file.

#include "stdafx.h"

#include "DataVerifierLib.h"

// Constructor Implementation
DataVerifierLib::PCAPParserWrapper::PCAPParserWrapper()
{
}

std::vector<std::string> DataVerifierLib::PCAPParserWrapper::CreateNewMachineCollection(std::vector<std::string> vstrNewMachines, std::vector<std::string> vstrMachine1, std::vector<std::string> vstrMachine2)
{
    vstrNewMachines.reserve(vstrMachine1.size() + vstrMachine2.size()); // preallocate memory
    vstrNewMachines.insert(vstrNewMachines.end(), vstrMachine1.begin(), vstrMachine1.end());
    vstrNewMachines.insert(vstrNewMachines.end(), vstrMachine2.begin(), vstrMachine2.end());
    return vstrNewMachines;
}

bool DataVerifierLib::PCAPParserWrapper::ParseWrapper(String^ managedString)
{
    // String conversion from c# to c++
    String^ managedStringTmp = managedString;
    std::string strInputFileNames = msclr::interop::marshal_as<std::string>(managedStringTmp);

    std::vector<std::string> vRRMachines;
    std::vector<std::string> vRHMachines;
    std::vector<std::string> vEEDMachines;
    std::vector<std::string> vPorts; // decide on what checks are to be made. Should frame have matching src/dest ports? or just one of them.
    std::vector<std::string> vEEDRRMachines;
    std::vector<std::string> vRRRHMachines;
    std::vector<std::string> vstrLines;

    std::string strTxtFile = "ServerIPList.txt"; //argv[2]; // ServerIPList.txt
    std::string strLine;
    std::ifstream in(strTxtFile);

    if (!in)
    {
        std::cout << "There was a problem opening the file." << std::endl;
        std::cerr << "Error: " << strerror(errno) << std::endl;
        return -1;
    }

    while (std::getline(in, strLine))
    {
        vstrLines.push_back(strLine);
    }

    for (int i = 0; i < vstrLines.size(); ++i)
    {
        if (vstrLines[i].substr(0, 2) == "rr")
        {
            boost::split(vRRMachines, vstrLines[i], boost::is_any_of(","));
            if (vRRMachines.size() < 2)
                return -1;
            else
                vRRMachines.erase(vRRMachines.begin());
        }
        else if (vstrLines[i].substr(0, 2) == "rh")
        {
            boost::split(vRHMachines, vstrLines[i], boost::is_any_of(","));
            if (vRHMachines.size() < 2)
                return -1;
            else
                vRHMachines.erase(vRHMachines.begin());
        }
        else if (vstrLines[i].substr(0, 3) == "eed")
        {
            boost::split(vEEDMachines, vstrLines[i], boost::is_any_of(","));
            if (vEEDMachines.size() < 2)
                return -1;
            else
                vEEDMachines.erase(vEEDMachines.begin());
        }
        else if (vstrLines[i].substr(0, 5) == "ports")
        {
            boost::split(vPorts, vstrLines[i], boost::is_any_of(","));
            if (vPorts.size() < 2)
                return -1;
            else
                vPorts.erase(vPorts.begin());
        }
    }

    // Create a vector with EED/RR and RR/RH combined addresses
    vEEDRRMachines = CreateNewMachineCollection(vEEDRRMachines, vEEDMachines, vRRMachines);
    vRRRHMachines = CreateNewMachineCollection(vRRRHMachines, vRRMachines, vRHMachines);

    // Initialise Rssl
    RsslRet rsslRet;
    RsslError rsslError;
    rsslRet = rsslInitialize(RSSL_LOCK_NONE, &rsslError);

    if (rsslRet != RSSL_RET_SUCCESS)
        return -1;

    // Initialise Field Dictionary
    // To prevent memory issues, we need to use "malloc" for the data dictionary in order to load RWF.dat and enumtype.def
    RsslDataDictionary *rsslDataDictionary = (RsslDataDictionary*)malloc(sizeof(RsslDataDictionary));
    RsslBuffer rsslBufferError;
    rsslClearDataDictionary(rsslDataDictionary);
    if (rsslRet = rsslLoadFieldDictionary("RWF.DAT", rsslDataDictionary, &rsslBufferError) != RSSL_RET_SUCCESS)
    {
        // Ensure we free the data dictionary memory when finished
        free(rsslDataDictionary);
        //if (iDisplayType != DISPLAY)
        //  std::cout << "Could not load RDM Field Dictionary file." << std::endl;
        return -1;
    }

    // Load enum dictionary
    if (rsslLoadEnumTypeDictionary("enumtype.def", rsslDataDictionary, &rsslBufferError) != RSSL_RET_SUCCESS)
    {
        // Ensure we free the data dictionary memory when finished
        free(rsslDataDictionary);
        //if (iDisplayType != DISPLAY)
        //  std::cout << "Could not load Enum Type Dictionary file." << std::endl;
        return -1;
    }

    std::string strCombinedFileName;
    std::vector<CRWFCapsule> vRWFCapsules;
    std::vector<std::string> vstrInputFileNames;
    pPcapParser = new CPCAPParser(1,1); // Initiate C++ class instance
    boost::algorithm::split_regex(vstrInputFileNames, strInputFileNames, boost::regex(","));

    // Let's iterate through each PCAP file and parse it.
    for (int i = 0; i < vstrInputFileNames.size(); ++i)
    {
        if (false == strCombinedFileName.empty())
        {
            strCombinedFileName.append("-");
        }

        if (false == pPcapParser->Parse(vstrInputFileNames[i].c_str(), vRWFCapsules, vEEDRRMachines, vRRRHMachines, *rsslDataDictionary))
        {
            delete pPcapParser;
            // Ensure we free the data dictionary memory when finished
            free(rsslDataDictionary);
            vRWFCapsules.clear();
            return -1;
        }

        strCombinedFileName = strCombinedFileName.append(pPcapParser->GetFileName());
    }

    //if (iDisplayType != NO_DISPLAY)
    //  std::cout << "Clearing up..." << std::endl;
    delete pPcapParser;

    if (rsslRet = rsslDeleteDataDictionary(rsslDataDictionary) != RSSL_RET_SUCCESS)
    {
        // Ensure we free the data dictionary memory when finished
        free(rsslDataDictionary);
        //if (iDisplayType != NO_DISPLAY)
        //  std::cout << "Problem deleting dictionary." << std::endl;
        return -1;
    }

    // Ensure we free the data dictionary memory when finished
    free(rsslDataDictionary);

    if (vRWFCapsules.empty())
        return -1;

    CMessageSorter msgSorter(vEEDMachines, vRRMachines);
    std::map<std::string, SMessageInfo> m_msMessageInfo;
    std::map<std::string, SSystemInfo> m_msSystemInfo = msgSorter.SortMessages(vRWFCapsules, 1, m_msMessageInfo);
    if (m_msSystemInfo.empty())
        return -1;

    CDataVerifier dataVerifier(strCombinedFileName, 1, 1);
    dataVerifier.CreateOutputForAllMessages(m_msMessageInfo);
    dataVerifier.Process(m_msSystemInfo);

    // When clearing the vector, we have to make sure that the destructor of the RWF message is being called... so everything destroys itself correctly.
    // Will need to check if destruction is down properly. However, how can we delete RWF Capsules encapsulated in eachother?...

    vRWFCapsules.clear();
}

And its appropriate header file:

// DataVerifierLib.h

#pragma once
#include "../DataVerifier/CRWFCapsule.h"
#include "../DataVerifier/ISystem.h"
#include "../DataVerifier/CFrameAnalyser.h"
#include "../DataVerifier/CPCAPParser.h"
#include "../DataVerifier/CMessageSorter.h"
#include "../DataVerifier/CDataVerifier.h"
#include "../DataVerifier/CSourceDirectoryResponse.h"
#include "../DataVerifier/CDataVerifierConstants.h"
#include <vector>
#include <fstream>
#include <sstream>
#include <stdexcept>
#include <boost/algorithm/string.hpp>
#include <msclr/marshal_cppstd.h>

using namespace System;
using namespace DataVerifier;

namespace DataVerifierLib {

    public ref class PCAPParserWrapper
    {
        // TODO: Add your methods for this class here.
    public:
        // Constructor
        PCAPParserWrapper();

        // Wrapper Methods
        bool ParseWrapper(String^ strInputFileNames);

        // Fill vectors with appropriate RR/EED/RH addresses
        std::vector<std::string> CreateNewMachineCollection(std::vector<std::string> vstrNewMachines, std::vector<std::string> vstrMachine1, std::vector<std::string> vstrMachine2);

        // Public Variable
        double initVal;

    private:
        CPCAPParser *pPcapParser; // an instance of class in C++
    };
}

回答1:

Don't know why I have received down votes for this, as the solution could be quite useful to people mixing C# and a C++/CLI wrapper.

I realized that the suspect line was actually this line:

//String^ managedStringTmp = managedString;
//std::string strInputFileNames = msclr::interop::marshal_as<std::string>(managedStringTmp);
std::string strInputFileNames = marshalString<E_UTF8>(managedString);

I've obviously changed it so that it is using this new "marshalString" function. I used a marshal string template class from the link here, as it seems to address performance issues.

http://blog.nuclex-games.com/mono-dotnet/cxx-cli-string-marshaling/

Now all I need to do is figure out why returning true from the wrapper and assigning it to a boolean in C# returns false... but hopefully this will help some people out.

UPDATE

This explains why returning false or true in the wrapper back to C# won't work, and that we either need to use marshalling of the function (didn't want to do) or return 0 for false and 1 for true.

C# DllImport with C++ boolean function not returning correctly