【问题标题】:Reading SAFEARRAY of UDT containing SAFEARRAY读取包含 SAFEARRAY 的 UDT 的 SAFEARRAY
【发布时间】:2019-06-28 00:33:13
【问题描述】:

我正在尝试创建和读取 SAFEARRAY(MyUDT)*,其中 MyUDT 包含 SAFEARRAY,但是当我尝试读取它时出现“访问冲突”异常。

我定义了以下结构和枚举:

typedef [uuid(...)]
enum CollisionDetectorMoveType
{
CollisionDetectorMoveType_Unknown = -1,
CollisionDetectorMoveType_Move = 0,
CollisionDetectorMoveType_Measure
} CollisionDetectorMoveType;

typedef [uuid(...)]  
struct CollisionDetectorXYZ
{
double X;
double Y;
double Z;
} CollisionDetectorXYZ;

typedef [uuid(...)]
struct CollisionDetectorMultiPosition
{
SAFEARRAY(CollisionDetectorXYZ) pos;
CollisionDetectorMoveType type;
} CollisionDetectorMultiPosition;


bool createTestCollisionDetectorMultiPositionSafeArray(SAFEARRAY** psa)
{

    IRecordInfoPtr recordset_info = nullptr;
    HRESULT hr = GetRecordInfoFromGuids(LIBID_CollisionDetectorLib, 1, 0, 0, UUID_CollisionDetectorMultiPosition, &recordset_info);
    if (FAILED(hr))
    {
        return false;
    }

    SAFEARRAYBOUND safearray_bound;
    memset(&safearray_bound, 0, sizeof(safearray_bound));
    safearray_bound.cElements = 10;
    safearray_bound.lLbound = 0;
    *psa = ::SafeArrayCreateEx(VT_RECORD, 1, &safearray_bound, (PVOID)recordset_info);

    for (size_t i = 0; i < 10; ++i)
    {
        CollisionDetectorMultiPosition multiPosition;
        multiPosition.type = i % 2 == 0 ? CollisionDetectorMoveType_Move : CollisionDetectorMoveType_Measure;
        if (!createTestCollisionDetectorXYZSafeArray(&multiPosition.pos))
        {
            return false;
        }

        LONG current_position = i;
        ::SafeArrayPutElement(*psa, &current_position, &multiPosition);
    }

    ::SafeArrayUnaccessData(*psa);

    return true;
}
}

bool createTestCollisionDetectorXYZSafeArray(SAFEARRAY** psa)
{

    IRecordInfoPtr recordset_info = nullptr;
    HRESULT hr = GetRecordInfoFromGuids(LIBID_CollisionDetectorLib, 1, 0, 0, UUID_CollisionDetectorXYZ, &recordset_info);
    if (FAILED(hr))
    {
        return false;
    }

    SAFEARRAYBOUND safearray_bound;
    memset(&safearray_bound, 0, sizeof(safearray_bound));
    safearray_bound.cElements = 10;
    safearray_bound.lLbound = 0;
    *psa = ::SafeArrayCreateEx(VT_RECORD, 1, &safearray_bound, (PVOID)recordset_info);

    for (size_t i = 0; i < 10; ++i)
    {
        CollisionDetectorXYZ current_point;
        current_point.X = i;
        current_point.Y = i;
        current_point.Z = i;
        LONG current_position = i;
        ::SafeArrayPutElement(*psa, &current_position, &current_point);
    }

    ::SafeArrayUnaccessData(*psa);

    return true;    
}

然后我尝试创建并读取 SAFEARRAY:

    SAFEARRAY* psa2 = NULL;
    if (!createTestCollisionDetectorMultiPositionSafeArray(&psa2))
    {
        *retVal = SYSERR;
        return S_OK;
    }

    CollisionDetectorMultiPosition* current_pointer_to_element;
    auto access_result = ::SafeArrayAccessData(psa2, (void **)&current_pointer_to_element);
    if (FAILED(access_result))
    {
        return S_OK;
    }
    LONG upper_bound, lower_bound;
    ::SafeArrayGetLBound(psa2, 1, &lower_bound);
    ::SafeArrayGetUBound(psa2, 1, &upper_bound);

    std::vector<CollisionDetectorMultiPosition> multiPositionVector;
    LONG size = upper_bound - lower_bound + 1;
    for (LONG i = 0; i < size; i++)
    {
        CollisionDetectorMultiPosition this_value;
        ::SafeArrayGetElement(psa2, &i, (void *)&this_value);

        multiPositionVector.push_back(this_value);
    }  

但是,当我调用 ::SafeArrayGetElement(psa2, &i, (void *)&this_value);我得到了例外。

我做错了什么?

如果我只是调用

CollisionDetectorMultiPosition multiPosition;
    multiPosition.type = CollisionDetectorMoveType_Move;
    if (!createTestCollisionDetectorXYZSafeArray(&multiPosition.pos))
    {
        *retVal = SYSERR;
        return S_OK;
    }

我可以毫无问题地读取 multiPosition.pos SAFEARRAY。

感谢您的帮助。

已解决

我找到了解决方案,我为所有可能遇到相同问题的用户报告了代码。基本上,诀窍是创建一个 VARIANT 的 SAFEARRAY,并用 CollisionDetectorMultiPosition 填充 VARIANT 结构。

CComVariant variant;
variant.vt = VT_RECORD;
variant.pvRecord = &multiPosition;
variant.pRecInfo = recordset_info;

创建 SAFEARRAY

bool createTestCollisionDetectorMultiPositionSafeArray(SAFEARRAY** psa)
{

    IRecordInfoPtr recordset_info;
    HRESULT hr = GetRecordInfoFromGuids(LIBID_CollisionDetectorLib, 1, 0, 0, UUID_CollisionDetectorMultiPosition, &recordset_info);
    if (FAILED(hr))
    {
        return false;
    }

    SAFEARRAYBOUND safearray_bound;
    memset(&safearray_bound, 0, sizeof(safearray_bound));
    safearray_bound.cElements = 10;
    safearray_bound.lLbound = 0;

    *psa = SafeArrayCreate(VT_VARIANT, 1, &safearray_bound);


    for (size_t i = 0; i < 10; ++i)
    {
        CollisionDetectorMultiPosition multiPosition;
        multiPosition.type = i % 2 == 0 ? CollisionDetectorMoveType_Move : CollisionDetectorMoveType_Measure;
        if (!createTestCollisionDetectorXYZSafeArray(&multiPosition.pos))
        {
            return false;
        }

        CComVariant variant;
        variant.vt = VT_RECORD;
        variant.pvRecord = &multiPosition;
        variant.pRecInfo = recordset_info;


        LONG current_position = i;
        ::SafeArrayPutElement(*psa, &current_position, &variant);

        variant.vt = VT_EMPTY;

    }

    ::SafeArrayUnaccessData(*psa);

    return true;
}

阅读 SAFEARRAY

    SAFEARRAY* psa2 = NULL;
    if (!createTestCollisionDetectorMultiPositionSafeArray(&psa2))
    {
        *retVal = AC3SYSERR;
        return S_OK;
    }

    LONG upper_bound, lower_bound;
    ::SafeArrayGetLBound(psa2, 1, &lower_bound);
    ::SafeArrayGetUBound(psa2, 1, &upper_bound);

    std::vector<std::pair<CollisionDetectorMoveType, std::vector<CollisionDetectorXYZ>>> multiPositionVector;
    LONG size = upper_bound - lower_bound + 1;
    for (LONG i = 0; i < size; i++)
    {
        VARIANT this_value;
        VariantInit(&this_value);
        VariantClear(&this_value);

        ::SafeArrayGetElement(psa2, &i, (void *)&this_value);

        CollisionDetectorMultiPosition* this_value2 = (CollisionDetectorMultiPosition *)this_value.pvRecord;

        CollisionDetectorXYZ* current_pointer_to_element;
        auto access_result = ::SafeArrayAccessData(this_value2->pos, (void **)&current_pointer_to_element);
        if (FAILED(access_result))
        {
            return S_OK;
        }

        LONG upper_bound, lower_bound;
        ::SafeArrayGetLBound(this_value2->pos, 1, &lower_bound);
        ::SafeArrayGetUBound(this_value2->pos, 1, &upper_bound);

        std::vector<CollisionDetectorXYZ> moves;
        LONG size = upper_bound - lower_bound + 1;
        for (LONG i = 0; i < size; i++)
        {
            CollisionDetectorXYZ this_value;
            ::SafeArrayGetElement(this_value2->pos, &i, (void *)&this_value);

            moves.push_back(this_value);
        }

        multiPositionVector.push_back(std::make_pair(this_value2->type, moves));

        ::SafeArrayUnaccessData(this_value2->pos);
    }

【问题讨论】:

  • 尝试通过正确格式化来澄清您的问题
  • 我想知道recordset_info是否应该为空。
  • @john 我试着把初始化去掉nullptr,结果还是一样
  • @Davide 不,你误会了。我认为 recordset_info 应该 not 为空。但我真的不知道我在说什么。
  • 最重要的是 - 你有时打电话给SafeArrayUnaccessData 而不先打电话给SafeArrayAccessData

标签: c++ struct com safearray


【解决方案1】:

其实我也有类似的问题,这个帖子帮了我很多,但是有一些特定于 MFC ActiveX 的细节让我花了几天时间来揭秘,我想与社区分享。

我的目标是在 MFC/ATL ActiveX 中添加一个方法,该方法将 UDT 数组返回到支持 COM 的编程语言。

我的第一个问题是将 UDT 的 UUID 暴露给 C++ 代码;出于某种原因,尽管向 UDT 提供了 GUID,但它并没有被生成,也没有被 C++ 使用。我发现我必须在 IDL 中添加 cpp_quote 才能强制它。

[
    uuid(GUID_LIBRARY),
    version(VERSION)
    ... 
]
library WebKitXCEF3Lib
{
    importlib(STDOLE_TLB);

    typedef [public, uuid(<your guid here>)] struct Cookie
    {
        BSTR Name;
        BSTR Domain;
        BSTR Path;
        BSTR Value;
        VARIANT_BOOL HttpOnly;
        VARIANT_BOOL Secure;
        VARIANT_BOOL Expires;
        VARIANT ExpiryDateUTC;

    } Cookie;

    cpp_quote("struct __declspec(uuid(\"{<your guid here>}\")) Cookie;")
    ...    
}

接下来,返回UDT数组的方法的返回类型必须是VARIANT

[id(100)] VARIANT GetCookies(BSTR Domain);

DISPATCH MAP 也应该将返回类型定义为 VT_VARIANT

BEGIN_DISPATCH_MAP(CMyCtrl, COleControl)
    DISP_FUNCTION_ID(CMyCtrl, "GetCookies", dispidGetCookies, GetCookies, VT_VARIANT, VTS_BSTR)
END_DISPATCH_MAP()

最后,C++ 代码必须使用 SafeArrayCreateEx 创建一个 SAFEARRAY 并提供 UDT 的 IRecordInfo,最后包装成这样:

VARIANT CMyCtrl::GetCookies(LPCTSTR Domain)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    SAFEARRAY* psaCookies = nullptr;

    if(AmbientUserMode() && !Unloading || BROWSER_READY)
    {
        LONG L = Cookies.size();

        SAFEARRAYBOUND rgsabound[1];
        rgsabound[0].lLbound = 0;
        rgsabound[0].cElements = L;

        IRecordInfoPtr pRecInfo = nullptr; // DO NOT RELEASE !!!
        if(SUCCEEDED(GetRecordInfoFromGuids(LIBID_MyLib, VERSION_MAJOR, VERSION_MINOR, 0, __uuidof(Cookie), &pRecInfo)))
        {
            psaCookies = ::SafeArrayCreateEx(VT_RECORD, 1, rgsabound, pRecInfo);            

            if(psaCookies)
            {
                Cookie* pCookies = NULL;

                if(SUCCEEDED(SafeArrayAccessData(psaCookies, reinterpret_cast<PVOID*>(&pCookies))))
                {
                    for(LONG i = 0; i < L; i++)
                    {                               
                        pCookies[i].Name = WSTR_to_BSTR(Cookies[i].Name);
                        pCookies[i].Domain = WSTR_to_BSTR(Cookies[i].Domain);
                        pCookies[i].Path = WSTR_to_BSTR(Cookies[i].Path);
                        pCookies[i].Value = WSTR_to_BSTR(Cookies[i].Value);
                        pCookies[i].HttpOnly = Cookies[i].HttpOnly;
                        pCookies[i].Secure = Cookies[i].Secure;
                        pCookies[i].Expires = Cookies[i].Expires;
                        VariantCopy(&(pCookies[i].ExpiryDateUTC), &(Cookies[i].ExpiryDateUTC));
                    }
                    
                    SafeArrayUnaccessData(psaCookies);
                }               
            }
        }
    }

    VARIANT result;
    VariantInit(&result);
    if(psaCookies)
    {       
        result.vt = VT_ARRAY | VT_RECORD;
        result.parray = psaCookies;
    }

    return result;
}

注意不要释放 IRecordInfo,返回的 VARIANT 必须是 VT_ARRAY | 类型。 VT_RECORD

VB6 代码如下所示:

Private Sub wxDesigner_OnPageComplete(ByVal URL As String)    
    Dim Cookies As Variant    
    Cookies = wxDesigner.GetCookies("foo.com")
End Sub

在 Watches 中,VARIANT 看起来像这样:

【讨论】:

    猜你喜欢
    • 2012-08-03
    • 2012-05-21
    • 2023-01-04
    • 2019-06-18
    • 2016-02-24
    • 1970-01-01
    • 2014-05-14
    • 2018-04-27
    • 2013-06-22
    相关资源
    最近更新 更多