Массивы в качестве параметров
От: Max_Akimov Россия  
Дата: 24.12.01 16:42
Оценка:
Здрасте!
Возникли некоторые затруднения с передачей массивов — параметров
методам COM объекта. Метод описан в IDL следующим образом.


[id(2), helpstring("method m")] HRESULT m([in] long count, [in, size_is(count)] long *s);


Клиент на C++, объекты используются с помощью #import. Если использую out-proc сервер, при вызове метода сервер получает значение только нулевого элемент массива, в остальных — мусор. Прокси нет.
Все это работает под win 2000 COM+, VC6, sp5.

Объясните на пальцах плз. возможна ли в принципе передача массивов под COM+ без использования proxy.
Заране благодарен.
Re: Массивы в качестве параметров
От: VladD2 Российская Империя www.nemerle.org
Дата: 25.12.01 00:05
Оценка: 8 (2)
Здравствуйте Max_Akimov, Вы писали:

MA>...Прокси нет.


Ну, дык а чё ты хотел без прокси? Если прокси не зарегистрирована, то по умолчанию генерируется динамическая oleautomation-прокаи/стабина на базе tlb-хи. В tlb не попадает ни слова о твоих size_is-ах. Она видит "[in] long *s" и по правилам COM-а думает что ей должны передать размещенный указатель.

MA>Все это работает под win 2000 COM+, VC6, sp5.


Да уж под другой ОСью вряд ли заработает даже так...

MA>Объясните на пальцах плз. возможна ли в принципе передача массивов под COM+ без использования proxy.


Конечно возможна. Кстати COM+ ничем не отличается от простого COM-а, т.е. DCOM-а. Только для этого нужно меньше умных книг читать. Т.е. нужно забыть о любых атрибутах для параметров кроме in, out и retval. Остальные просто не пободают в tlb (используются только для "ручной" компиляции прокси/стаба).

Предвижу вопрос... а как же тогда?

А очень просто ... с помощью SAFEARRAY. При этом код становится oleautomation-совместимым и может вызываться из VB и Delphi.

IDL для oleautomation-варианта буде выглядеть так:
Клиент на C++/ATL:

// Импорт для нашего COM-сервера. В реальной жизни проще пользоваться включение инклюда...
#import "..\Debug\OleautAry.dll" no_namespace, named_guids, no_implementation, raw_interfaces_only, raw_dispinterfaces,  
raw_native_types 

...

// Использование массива...
LRESULT OnClickedButton1(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
    // Массив будет состоять из 6-и элементов и иметь нижнюю границу равную нулю
    const int iLBound = 0, iCnt = 6;

    // Создаем COM-сервер
    CComPtr<IOleautIntAry> spObj;
    HRESULT hr = spObj.CoCreateInstance(CLSID_OleautIntAry);
    if(hr)
        return 0;

    // Создаем SAFEARRAY long-ов (VT_I4)
    LPSAFEARRAY psa = SafeArrayCreateVector(VT_I4, iLBound, iCnt);
    if(FAILED(psa))
        return 0;

    // Получаем доступ к "телу"
    long * pLng = NULL;
    hr = SafeArrayAccessData(psa, (void**)&pLng);
    if(FAILED(hr))
        return 0;
    
    // Помещаем данные в наш массив
    for(int i = iLBound; i < iCnt; i++)
    {
        pLng[i] = i * 6;
    }

    // "Отпускаем" массив
    hr = SafeArrayUnaccessData(psa);
    if(FAILED(psa))
        return 0;

    // Вызываем удаленный вызов
    hr = spObj->InLongAry(&psa);
    ATLASSERT(!hr);


    // Уничтожаем массив
    SafeArrayDestroy(psa);
    return 0;
}


Сервер C++/ATL:

// IDL:
[
    object,
    uuid(3F252A3D-9109-46B6-BD7E-FBF4E59C0626),
    dual, // Обязательно для oleautomation-маршалинга
    //oleautomation, // Можно использовать вместо dual

    pointer_default(unique)
]
interface IOleautIntAry : IDispatch
{
    [id(1), helpstring("Метод принимающий SAFEARRAY long-ов")] 
    HRESULT InLongAry([in] SAFEARRAY(long) * ary);
};


// Реализация:
STDMETHODIMP COleautIntAry::InLongAry(SAFEARRAY ** ppsa)
{
    if(!ppsa)
        return E_POINTER;
    if(!*ppsa)
        return E_INVALIDARG;

    // Посылаем товарищей если размерность массива не единица :)
    if(SafeArrayGetDim(*ppsa) != 1)
        return E_INVALIDARG;

    
    // Получаем доступ к "телу"
    long * pLng = NULL;
    HRESULT hr = SafeArrayAccessData(*ppsa, (void**)&pLng);
    if(FAILED(hr))
        return hr;

    // Узнаем верхнюю и нижнюю границу массива
    LONG iLBound; LONG iUBound;
    hr = SafeArrayGetLBound(*ppsa, 1, &iLBound);
    ATLASSERT(!hr);
    hr = SafeArrayGetUBound(*ppsa, 1, &iUBound);
    ATLASSERT(!hr);

    // Читаем данные и выводим из в окно отладчика (сервера!)
    for(int i = iLBound; i <= iUBound; i++)
    {
        ATLTRACE("pLng[%d] = %d\n", i, pLng[i]);
    }

    // "Отпускаем" массив
    return SafeArrayUnaccessData(*ppsa);
}


Исползование хелперов (например, нашего CascSaheArray из ascLib http://www.rsdn.ru/files/?libs/asclib.xml) упрощает код в 3-5 раз и (естественно) уменьшает количество ошибок.

PS

Еще можно просто запаковать данные в BSTR (используя SysAllocStrinByteLen. Но этот вариант менее предпочтительнее, так так могут появится проблемы с конвертациями (об этом предупреждают гуру, но я ни разу не нарывался), а так же в виду корявости и не совместимости с другими средствами разработки.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Массивы в качестве параметров
От: Max_Akimov Россия  
Дата: 25.12.01 11:26
Оценка:
Здравствуйте VladD2, Вы писали:

MA>>...Прокси нет.


VD>Ну, дык а чё ты хотел без прокси? Если прокси не зарегистрирована, то по умолчанию генерируется динамическая oleautomation-прокаи/стабина на базе tlb-хи. В tlb не попадает ни слова о твоих size_is-ах. Она видит "[in] long *s" и по правилам COM-а думает что ей должны передать размещенный указатель.


MA>>Все это работает под win 2000 COM+, VC6, sp5.


VD>Да уж под другой ОСью вряд ли заработает даже так...


[...]

VD>Еще можно просто запаковать данные в BSTR (используя SysAllocStrinByteLen. Но этот вариант менее предпочтительнее, так так могут появится проблемы с конвертациями (об этом предупреждают гуру, но я ни разу не нарывался), а так же в виду корявости и не совместимости с другими средствами разработки.


Тогда вопрос, как понимать следующую фразу из статьи COM
Автор(ы): Чистяков В.Ю.

Эта статья входит в цикл "COM vs. CORBA" и знакомит читателя с основами COM, начиная с интерфейса IUnknown
и заканчивая маршалингом, DCOM и COM+.
глава "Основные возможности низкоуровневого MIDL/С++ варианта"
"Под Windows 2000 этот пример не требует создания и регистрации заглушки"
Re[3]: Массивы в качестве параметров
От: VladD2 Российская Империя www.nemerle.org
Дата: 25.12.01 19:07
Оценка:
Здравствуйте Max_Akimov, Вы писали:

MA>Тогда вопрос, как понимать следующую фразу из статьи COM
Автор(ы): Чистяков В.Ю.

Эта статья входит в цикл "COM vs. CORBA" и знакомит читателя с основами COM, начиная с интерфейса IUnknown
и заканчивая маршалингом, DCOM и COM+.
глава "Основные возможности низкоуровневого MIDL/С++ варианта"

MA>"Под Windows 2000 этот пример не требует создания и регистрации заглушки" :???:

Там наверное не точно сформулирована, мысль, а может быть просто ошибка. Под управлением W2k этот пример действительно будет работать, но в виду отсутствия информации о количестве елементов в массиве последний метод будет работать некорректно. На рдугих же платформах не получится даже установить соеденение с удаленным объектом.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Массивы в качестве параметров
От: Николай Украина http://www.nickolya.wallst.ru
Дата: 10.12.02 15:03
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Предвижу вопрос... а как же тогда?


VD>А очень просто ... с помощью SAFEARRAY. При этом код становится oleautomation-совместимым и может вызываться из VB и Delphi.


VD>IDL для oleautomation-варианта буде выглядеть так:

VD>[ccode]
VD>Клиент на C++/ATL:

VD>// Импорт для нашего COM-сервера. В реальной жизни проще пользоваться включение инклюда...

VD>#import "..\Debug\OleautAry.dll" no_namespace, named_guids, no_implementation, raw_interfaces_only, raw_dispinterfaces,
VD>raw_native_types

VD>...


VD>// Использование массива...

VD>LRESULT OnClickedButton1(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
VD>{
VD> // Массив будет состоять из 6-и элементов и иметь нижнюю границу равную нулю
VD> const int iLBound = 0, iCnt = 6;

VD> // Создаем COM-сервер

VD> CComPtr<IOleautIntAry> spObj;
VD> HRESULT hr = spObj.CoCreateInstance(CLSID_OleautIntAry);
VD> if(hr)
VD> return 0;

VD> // Создаем SAFEARRAY long-ов (VT_I4)

VD> LPSAFEARRAY psa = SafeArrayCreateVector(VT_I4, iLBound, iCnt);
VD> if(FAILED(psa))
VD> return 0;

VD> // Получаем доступ к "телу"

VD> long * pLng = NULL;
VD> hr = SafeArrayAccessData(psa, (void**)&pLng);
VD> if(FAILED(hr))
VD> return 0;
VD>
VD> // Помещаем данные в наш массив
VD> for(int i = iLBound; i < iCnt; i++)
VD> {
VD> pLng[i] = i * 6;
VD> }

VD> // "Отпускаем" массив

VD> hr = SafeArrayUnaccessData(psa);
VD> if(FAILED(psa))
VD> return 0;

VD> // Вызываем удаленный вызов

VD> hr = spObj->InLongAry(&psa);
VD> ATLASSERT(!hr);

VD>

VD> // Уничтожаем массив
VD> SafeArrayDestroy(psa);
VD> return 0;
VD>}

VD>

Извините, но может вы подскажите как мне быть в аналогичной ситуации, но только вот с таким методом?
HRESULT AddUrlList (VARIANT * pList );
Пробовал так:

::CoInitialize(NULL);
JCCATCHLib::IJetCarNetscapePtr spObj;
HRESULT hr=spObj.CreateInstance(L"JetCar.Netscape");

LPSAFEARRAY psa = SafeArrayCreateVector(VT_BSTR, iLBound, iCnt);

BSTR * pLng = NULL;
hr = SafeArrayAccessData(psa, (void**)&pLng);
   
pLng[0] = _bstr_t("http://www.one.com.ua/dm/");
pLng[1] = _bstr_t("http://www.one.com.ua/dm/test.exe");

hr = SafeArrayUnaccessData(psa);

VARIANT myVariant; 
myVariant.parray = psa;
myVariant.vt = VT_ARRAY|VT_BSTR;

hr = spObj->AddUrlList(&myVariant);

SafeArrayDestroy(psa);

ругается: "abnormal program termination"
Помогите пожалуйста если можно.
Re[3]: Массивы в качестве параметров
От: Jekky  
Дата: 10.12.02 16:23
Оценка:
Здравствуйте, Николай, Вы писали:

Н>pLng[0] = _bstr_t("http://www.one.com.ua/dm/");

Н>pLng[1] = _bstr_t("http://www.one.com.ua/dm/test.exe");

Вот тут ошибка. Следует писать pLng[0] = ::SysAllocString(L"http://www.one.com.ua/dm/");
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.