将std ::向量转换为阵列和当时的p/调用它会导致访问违规例外,在编组期间mscorlib.dll



在C 中我具有以下从第三方代码的结构:

typedef struct NodeInfoTag
{
    long lResult;
    int bComplete;
    char *pszNodeAddr;
    char *pszParentAddr;
    RTS_WCHAR *pwszNodeName;
    RTS_WCHAR *pwszDeviceName;
    RTS_WCHAR *pwszVendorName;
    unsigned long ulTargetType; 
    unsigned long ulTargetId;
    unsigned long ulTargetVersion;
    unsigned short wMaxChannels;
}NodeInfotyp;

和rts_wchar的定义:

#   ifndef RTS_WCHAR_DEFINED
#       define RTS_WCHAR_DEFINED
        typedef wchar_t RTS_WCHAR;  /* wide character value */
#   endif

(基本上是WCHAR_T(

然后我有自己的类称为CScanNetworkCallback,它扩展了CPLCHandlerCallback类,同一供应商的类:

.h文件:

class CScanNetworkCallback : public CPLCHandlerCallback
{
    public:
        bool bScanComplete;
        NodeInfotyp* pNodeInfo;
        NodeInfotyp* pNodeInfoList;
        std::vector<NodeInfotyp> vList;
        CScanNetworkCallback();
        virtual ~CScanNetworkCallback(void);
        virtual long Notify(CPLCHandler *pPlcHandler, CallbackAddInfoTag CallbackAdditionalInfo);
};

实施遵循自己的准则,其中一些我自己的东西投入了:

CScanNetworkCallback::CScanNetworkCallback(void) : CPLCHandlerCallback()
{
    bScanComplete = false;
}
CScanNetworkCallback::~CScanNetworkCallback()
{
    delete pNodeInfo;
    delete pNodeInfoList;
}
long CScanNetworkCallback::Notify(CPLCHandler *pPlcHandler, CallbackAddInfoTag CallbackAdditionalInfo)
{
    if (pPlcHandler != NULL)
    {
        if (CallbackAdditionalInfo.ulType == PLCH_SCAN_NETWORK_CALLBACK)
        {
            pNodeInfo = CallbackAdditionalInfo.AddInf.pNodeInfo;
            if (pNodeInfo->lResult == RESULT_OK)
            {
                vList.push_back(*pNodeInfo);
                bScanComplete = false;
            }
            else 
            {
                pNodeInfoList = &vList[0]; //New pointer points to the vector elements, which will be used as an array later on
                // I have also tried copying it, to the same result:
                //std::copy(vList.begin(), vList.end(), pNodeInfoList);
                bScanComplete = true;
            }
        }
    }
    return RESULT_OK;
}

基本上,每次在网络中找到"节点"时,上面的Notify方法都会称为"节点",然后将节点的信息分配给pnodeinfo(请忽略节点是什么,而不是相关的ATM(。由于在扫描过程中将其调用到网络中的每个节点,因此我必须将此信息发送给C ,因此除了使用std::vector以将每个回调信息存储后来使用,我找不到其他方法不知道编译时会有多少个节点。发现所有节点后,else部分被调用。为了使C#代码有意义,我必须描述其他一些被调用的C 方法的实现:

PROASADLL __declspec(dllexport) void scanNetwork(){
    pScanHandler->ScanNetwork(NULL, &scanNetworkCallback);
}

对象scanNetworkCallback是静态的。pScanHandler是第三方供应商的另一个类的指针,其ScanNetwork方法在单独的线程上运行。在内部(而且我只知道由于此API指南,我没有其源代码(,每当网络中找到一个节点时,它就调用Notify方法,或者是该效果的某些内容

最后:

PROASADLL __declspec(dllexport) NodeInfotyp* getScanResult(int* piSize) {
    *piSize = scanNetworkCallback.vList.size();
    return scanNetworkCallback.pNodeInfoList;
}

返回指向所有节点信息的指针,并将其作为OUT参数中的数量。现在让我们看一下C#代码:

    public static List<NodeInfoTag> AsaScanNetworkAsync()
    {
        Console.WriteLine("SCANNING NETWORK");
        scanNetwork(); // C++ Method
        while (!isScanComplete()) // Holds the C# thread until the scan is complete
            Thread.Sleep(50);
        int size = 0;
        IntPtr pointer = getScanResult(out size); // works fine, I get some IntPtr and the correct size
        List<NodeInfoTag> list = Marshaller.MarshalPointerToList<NodeInfoTag>(pointer, size); // PROBLEM!!!
        // Continue doing stuff
    }

这是NodeInfoTag类,匹配C NodeInfotyp结构:

[StructLayout(LayoutKind.Sequential)]
public class NodeInfoTag
{
    public int Result;
    public int Complete;
    [MarshalAs(UnmanagedType.LPStr)] //char*
    public string NodeAddress; 
    [MarshalAs(UnmanagedType.LPStr)] //char*
    public string ParentAddress;
    [MarshalAs(UnmanagedType.LPWStr)] //wchar_t
    public string VendorName;
    public uint TargetType;
    public uint TargetId;
    public uint TargetVersion;
    public short MaxChannels;
}

这是我得到记忆访问违规的地方:

internal class Marshaller
{
    public static List<T> MarshalPointerToList<T>(IntPtr pointer, int size)
    {
        if (size == 0)
            return null;
        List<T> list = new List<T>();
        var symbolSize = Marshal.SizeOf(typeof(T));
        for (int i = 0; i < size; i++)
        {
            var current = (T)Marshal.PtrToStructure(pointer, typeof(T));
            list.Add(current);
            pointer = new IntPtr(pointer.ToInt32() + symbolSize);
        }
        return list;
    }
}

var current = (T)Marshal.PtrToStructure(pointer, typeof(T));线上应特别发生误差。这个c#代码过去可以正常工作,但是C 部分很糟糕,令人费解且容易出错,所以我决定使事情变得更简单,但我无法确定我的一生,为什么我会得到这个例外我要确保所有C 资源都可用于C#,因为出于测试目的,我不会在C 中删除任何内容,并且我仅使用类中具有全局范围的变量,该变量已分配给静态内存。那么,我想念什么?

编辑:我删除了pNodeInfoList = &vList[0];并重写getScanResult如下:

static NodeInfotyp pNodeInfoList;
//(...)
PROASADLL __declspec(dllexport) NodeInfotyp* getScanResult(int* piSize) {
    *piSize = scanNetworkCallback.vList.size();
    std::move(scanNetworkCallback.vList.begin(), 
scanNetworkCallback.vList.end(), &pNodeInfoList);
    return &pNodeInfoList;
}

没有骰子。我在所涉及的任何变量中不使用newmalloc,甚至将PnodeInfolist(数组(从类成员更改为全局变量。另外,正如我被告知,我正在使用move,可以用来解决所有权问题。还有其他技巧吗?

所有权不是幼稚的C 类型系统的一部分,因此,当您删除您不拥有或不放弃的指针时,您将不会遇到错误。

但是,在语义上某些值,指示器和数据块归某些类型或值所有。

在这种情况下,向量拥有其内存块。没有办法要求它或放弃所有权。

致电.data() Onky为您提供指针,它确实提供指针语义所有权。

您将.data()的返回值存储在成员变量中。稍后您在该成员变量上调用delete。这向我表明,成员变量应该拥有其数据。因此,您可以删除(作为向量和指针都认为他们拥有的数据所指向的数据(,并且您的编译器为您崩溃了。

您需要考虑到与您合作的每一块内存的所有权以及所有权考虑。一种方法是永远不要直接调用新的,malloc或删除或免费,并始终使用内存管理类型,例如向量和唯一的PTR。避免坚持原始指针,因为从类型中不清楚其所有权语义。

最新更新