Как инициализировать CComSafeArray, который SAFEARRAY возвратил из метода Scripting.Dictionary Keys

Я использую Scripting.Dictionary из библиотеки COM "Microsoft Scripting Runtime" (c:\windows\System32\scrrun.dll). Я использую импорт, чтобы получить мои обертки, и они работают. После добавления некоторых элементов я пытаюсь получить список ключей, но я застрял.

У меня есть какой-то способ. Я могу получить SAFEARRAY ключей из словаря, но я хочу использовать CComSafeArray класс, определенный в <atlsafe.h> но я не могу понять, хорошая конструкция. В настоящее время конструктор создает ATL утверждение жалуется на vartype несоответствие. Следует признать, что недавно построенный CComSafeArray имеет vartype 52428 CCCC в шестнадцатеричном виде, и это выглядит потенциально как неинициализированная память. Но выведенный тип также выглядит неправильно, я ожидаю, что vartype safearray будет строками, но код подтверждения дает vartype 12, который является UI2. Очень странно.

Во всяком случае, это, вероятно, легко для тех, кто знаком с этим. Вот консольная программа Minimal, Complete, Verifiable, Example (MCVE) в соответствии со стандартами SO.

#include "stdafx.h"
#include <atlbase.h>
#include <atlsafe.h>

#import "c:\windows\System32\scrrun.dll" raw_interfaces_only,raw_native_types, named_guids, rename("DeleteFile", "_DeleteFile"), rename("MoveFile","_MoveFile"), rename("CopyFile", "_CopyFile"), rename("GetFreeSpace", "_GetFreeSpace")

int main()
{
    HRESULT hr = S_OK;

    CoInitialize(0);

    CComQIPtr<Scripting::IDictionary> dictColours;
    hr = dictColours.CoCreateInstance(__uuidof(Scripting::Dictionary));
    if (!SUCCEEDED(hr)) { return hr; }

    CComVariant varZero(0); //dummy value, only interested in keys

    hr = dictColours->Add(&variant_t(L"red"), &varZero);
    if (!SUCCEEDED(hr)) { return hr; }

    hr = dictColours->Add(&variant_t(L"green"), &varZero);
    if (!SUCCEEDED(hr)) { return hr; }

    hr = dictColours->Add(&variant_t(L"blue"), &varZero);
    if (!SUCCEEDED(hr)) { return hr; }

    long lColourCount(0);
    hr = dictColours->get_Count(&lColourCount);
    if (!SUCCEEDED(hr)) { return hr; }

    CComVariant varColoursKeys;
    hr = dictColours->Keys(&varColoursKeys);
    if (!SUCCEEDED(hr)) { return hr; }

    SAFEARRAY keys(*(varColoursKeys.parray));

    //fine up to this point

    CComSafeArray<VARIANT> varColours;

    /* The following code throws an error for the next (line) live, vt=52428 or hex CCCC which looks like uninitialised memory
       whilst GetType() returns 12 which is UI2 (and somehow I expected string type 8!) 
    VARTYPE vt;
    HRESULT hRes = ::ATL::AtlSafeArrayGetActualVartype(const_cast<LPSAFEARRAY>(psaSrc), &vt);
    ATLENSURE_SUCCEEDED(hRes);
    ATLENSURE_THROW(vt == GetType(), E_INVALIDARG);
    */

    varColours = (keys);

    CoUninitialize();
    return 0;
}

1 ответ

Решение

Изменить это:

SAFEARRAY keys(*(varColoursKeys.parray));

К этому:

SAFEARRAY* keys = varColoursKeys.parray;

(по желанию, сначала убедитесь, что varColoursKeys.vt имеет VT_ARRAY флаг)

И тогда вы можете запросить keys для его vt:

hr = ::ATL::AtlSafeArrayGetActualVartype(keys, &vt);

Обратите внимание, что varColours = keys собирается сделать копию данных массива. Если это не то, что вы действительно хотите, то вам следует Detach() массив из CComVariant (или просто использовать VARIANT прямо а не CComVariant) а также Attach() это к CComSafeArray (тогда вы можете использовать CComSafeArray::GetType() метод).

Кроме того, вам нужно убедиться, что все ваши COM-оболочки выходят за рамки и высвобождают ресурсы перед вызовом CoUninitialize(),

Попробуй это:

#include "stdafx.h"
#include <atlbase.h>
#include <atlsafe.h>

#import "c:\windows\System32\scrrun.dll" raw_interfaces_only,raw_native_types, named_guids, rename("DeleteFile", "_DeleteFile"), rename("MoveFile","_MoveFile"), rename("CopyFile", "_CopyFile"), rename("GetFreeSpace", "_GetFreeSpace")

HRESULT DoIt()
{
    CComQIPtr<Scripting::IDictionary> dictColours;
    hr = dictColours.CoCreateInstance(__uuidof(Scripting::Dictionary));
    if (FAILED(hr)) { return hr; }

    CComVariant varZero(0); //dummy value, only interested in keys

    hr = dictColours->Add(&variant_t(L"red"), &varZero);
    if (FAILED(hr)) { return hr; }

    hr = dictColours->Add(&variant_t(L"green"), &varZero);
    if (FAILED(hr)) { return hr; }

    hr = dictColours->Add(&variant_t(L"blue"), &varZero);
    if (FAILED(hr)) { return hr; }

    long lColourCount(0);
    hr = dictColours->get_Count(&lColourCount);
    if (FAILED(hr)) { return hr; }

    VARIANT varColoursKeys;
    hr = dictColours->Keys(&varColoursKeys);
    if (FAILED(hr)) { return hr; }

    CComSafeArray<VARIANT> varColours;
    varColours.Attach(varColoursKeys.parray);

    // use varColours as needed...

    VARTYPE vt = varColours.GetType();

    LONG lLower = varColours.GetLowerBound();
    LONG lUpper = varColours.GetUpperBound();
    for (LONG i = lLower; i <= lUpper; ++i)
    {
        VARIANT &v = varColours.GetAt(i);
        // use v.bstrVal as needed ...
    }

    //...

    return S_OK;
}

int main()
{
    HRESULT hr = CoInitialize(0);
    if (FAILED(hr)) { return hr; }

    hr = DoIt();

    CoUninitialize();
    return hr;
}
Другие вопросы по тегам