powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Delphi [игнор отключен] [закрыт для гостей] / 2 варианта создания PSafeArray для маршаллинга - в чём разница?
5 сообщений из 5, страница 1 из 1
2 варианта создания PSafeArray для маршаллинга - в чём разница?
    #39974431
WinterGraveyard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Есть библиотека типов вот с таким IDL (фрагмент):

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
[
  odl,
  uuid(1EF13171-6593-42BF-9BCF-CE2E2A036624),
  version(1.0),
  oleautomation,
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "TestLib.ITest")    
]
interface ITest : IUnknown {
  HRESULT _stdcall GetData([out, retval] SAFEARRAY(VARIANT)* pRetVal);
};

[
  odl,
  uuid(98CB1A74-6895-444B-8CC8-394C23543B2D),
  version(1.0),
  dual,
  oleautomation,
  custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "TestLib.ITestUser")    
]
interface ITestUser : IDispatch {
  [id(0x60020000)]
  HRESULT Do([in] ITest* Test);
};

В дельфи эта библиотека экспортируется вот так:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
ITest = interface(IUnknown)
  ['{1EF13171-6593-42BF-9BCF-CE2E2A036624}']
  function GetData(out pRetVal: PSafeArray): HResult; stdcall;
end;

ITestUser = interface(IDispatch)
  ['{98CB1A74-6895-444B-8CC8-394C23543B2D}']
  procedure Do_(const Test: ITest); safecall;
end;


Реализация интерфейса ITestUser - внешняя (дотнетовская сборка, если это имеет значение), интерфейс ITest реализуется в дельфи.
Если мы собираем PSafeArray как-то так:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
function TTest.Variant1: PSafeArray;
var
  Bounds: array [0..0] of TSafeArrayBound;
  i: Integer;
  b: TBytes;
  procedure PutElement(Value: Variant);
  begin
    OleCheck(SafeArrayPutElement(Result, i, Value));
    Inc(i);
  end;
begin
  Bounds[0].lLbound:=0;
  Bounds[0].cElements:=4;
  Result:=SafeArrayCreate(varVariant, 1, Bounds);
  OleCheck(SafeArrayLock(Result));
  PutElement(123);
  PutElement(WideString('Мама мыла раму'));
  PutElement(Now);
  SetLength(b, 2);
  b[0]:=32;
  b[1]:=255;
  PutElement(b);
  OleCheck(SafeArrayUnlock(Result));
end;


то всё отрабатывает нормально - внешний компонент через интерфейс получет данные, и нормально их разбирает.
Но если мы собираем PSafeArray вот так (этот способ фигурирует во многих примерах и пособиях в интернете):
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
function TTest.Variant2: PSafeArray;
var
  i: Integer;
  b: TBytes;
  Arr: Variant;
  procedure PutElement(Value: Variant);
  begin
    Arr[i]:=Value;
    Inc(i);
  end;
begin
  Arr:=VarArrayCreate([0, 4], varVariant);
  VarArrayLock(Arr);
  PutElement(123);
  PutElement(WideString('Мама мыла раму'));
  PutElement(Now);
  SetLength(b, 2);
  b[0]:=32;
  b[1]:=255;
  PutElement(b);
  VarArrayUnlock(Arr);
  Result:=PSafeArray(TVarData(Arr).VArray);
end;


то внешний компонент на вызове ITest.GetData выбрасывает ошибку "SafeArray ранга 65262 передан в метод, которому требуется массив ранга 1". Пробовал пробежаться по результату второго варианта (в дельфи) - вроде всё в порядке, количество измерений какое нужно, размер требуемый, все данные на месте - но вот видимо всё же есть какая-то разница между первым способом и вторым, и она важна при маршаллинге внешнему компоненту. А вот в чём она, эта разница?
Delphi 2010.
...
Рейтинг: 0 / 0
2 варианта создания PSafeArray для маршаллинга - в чём разница?
    #39974472
GunSmoker
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Серьёзно? Во втором варианте вы передаёте из функции указатель на память и тут же эту память освобождаете (Arr будет удалён до выхода из функции)!

В каких-таких местах такой способ показан?
...
Рейтинг: 0 / 0
2 варианта создания PSafeArray для маршаллинга - в чём разница?
    #39974504
WinterGraveyard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
GunSmoker
Серьёзно? Во втором варианте вы передаёте из функции указатель на память и тут же эту память освобождаете (Arr будет удалён до выхода из функции)!

В каких-таких местах такой способ показан?

Вот здесь, например . Плюс очень часто встречал такой способ, изучая исходник JclDotNet . Но в первом случае нюанс передачи данных куда-то вообще не играет роли, а во втором все внешние методы, в которые передаются данные, возвращают управление до выхода из метода, который формирует данные. А у меня фактически callback, и ситуацию с автоматической очисткой данных после выхода из метода я как-то упустил. Для второго варианта вынес Arr в поле класса - заработал и он. Теперь всё понятно, спасибо.
...
Рейтинг: 0 / 0
2 варианта создания PSafeArray для маршаллинга - в чём разница?
    #39974567
GunSmoker
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Что-то вы не то делаете.

Если вы используете первый вариант, то вызывающий поюзает массив и спокойно его удалит вызовом SafeArrayDestroy. По сути, здесь используется shared менеджер памяти, скрытый за фасадом SafeArrayXYZ функций.

Если же вы используете второй вариант, то вызывающий никак не может удалить массив, ведь он хранится у вас в поле класса, доступа к которому у вызывающего нет.

В указанных вами ссылках есть существенное отличие: в них права на владение массивом не отдаются во внешний код.
...
Рейтинг: 0 / 0
2 варианта создания PSafeArray для маршаллинга - в чём разница?
    #39974584
WinterGraveyard
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
GunSmoker,

Для второго способа я вынес вариантную переменную в поле только для проверки того, что маршаллинг пройдёт нормально, и сделал это только в тестовом наколеночном примере, чтобы окончательно разобраться. В рабочий код пошёл первый способ, с SafeArrayCreate.
Про shared менеджер памяти я по пути примерно догадался сам, потому что тоже пытался указатель на PSafeArray сохранять в поле класса, и при повторном обращении к методу, если указатель инициализирован, вызывать SafeArrayDestroy - нарвался на EOlySysError: Memory is locked, посмотрел на счетчик блокировок экземпляра SafeArray, и увидев странное значение типа -123789, предположил, что его до меня уже зачистили. Убрал хранение указателя в поле и SafeArrayDestroy, и никаких заметных утечек памяти не обнаружил (поскольку это всё в рабочем коде используется для передачи достаточно больших объемов, утечки были бы очень заметны).
Жаль, что литературу по этой теме сейчас трудно найти - хрестоматийную Inside COM Роджерсона я прочитал, но там такие нюансы не разъясняются. Всё остальное - оставшиеся обзорные статьи и MSDN, которые надо долго перелопачивать, чтобы узнать про вот этот же shared-менеджер.
...
Рейтинг: 0 / 0
5 сообщений из 5, страница 1 из 1
Форумы / Delphi [игнор отключен] [закрыт для гостей] / 2 варианта создания PSafeArray для маршаллинга - в чём разница?
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]