Delphi 6 OleServer.pas Вызывает утечку памяти - PullRequest
1 голос
/ 15 марта 2010

В delphi 6 есть ошибка, которую вы можете найти в Интернете для справки, когда при импорте tlb порядок параметров в вызове события меняется на противоположный. Он инвертируется один раз в импортированном заголовке и один раз в TServerEventDIspatch.Invoke.

Вы можете найти больше информации об этом здесь: http://cc.embarcadero.com/Item/16496

Отчасти это связано с тем, что в TServerEventDispatch возникает утечка памяти с параметром Variant типа Var_Array (возможно, других, но это более очевидный, который я мог видеть). Код вызова копирует аргументы в VarArray для передачи в обработчик событий, а затем копирует VarArray обратно в аргументы после вызова, соответствующий код вставлен ниже:

  // Set our array to appropriate length
  SetLength(VarArray, ParamCount);

  // Copy over data
  for I := Low(VarArray) to High(VarArray) do
    VarArray[I] := OleVariant(TDispParams(Params).rgvarg^[I]);

  // Invoke Server proxy class
  if FServer <> nil then FServer.InvokeEvent(DispID, VarArray);

  // Copy data back
  for I := Low(VarArray) to High(VarArray) do
    OleVariant(TDispParams(Params).rgvarg^[I]) := VarArray[I];

  // Clean array
  SetLength(VarArray, 0);

В моем случае есть некоторые очевидные обходные пути: если я пропускаю копирование в случае параметра VarArray, это устраняет утечку. чтобы не изменять функциональность, я подумал, что я должен скопировать данные в массив вместо варианта обратно в параметры, но это может стать сложным, поскольку он может содержать другие варианты и мне кажется, что это должно быть сделано рекурсивно.

Поскольку изменение в OleServer будет иметь волновой эффект, я хочу убедиться, что мои изменения здесь строго верны.

Может кто-нибудь пролить свет на то, почему именно здесь просачивается память? Кажется, я не могу посмотреть стека вызовов ниже, чем TServerEventDIspatch.Invoke (почему это так?)

Я полагаю, что в процессе копирования Variant, удерживающего VarArray обратно в список параметров, он добавил ссылку на массив, что не позволило ему быть выпущенным как обычно, но это всего лишь приблизительное предположение, и я не могу отследить код для резервного копирования.

Может быть, кто-то с лучшим пониманием всего этого мог бы пролить свет?

1 Ответ

2 голосов
/ 16 марта 2010

Интересно, что я думаю, что решение было в ссылке, которую я предоставил в вопросе, но я не понял смысла, пока не углубился в это.

Несколько вещей, чтобы уточнить:

  1. Когда вариант, содержащий массив, назначается из VarArray обратно в Params, создается копия.Это объясняется на страницах справки delphi.
  2. Присвоение существующего варианта определенно освободит память, связанную с предыдущим значением Variant, поэтому массив, содержащийся в варианте перед присваиванием, был бы освобожден наприсваивание.
  3. VarClear освобождает память, связанную с вариантом, и тесты показывают, что VarClear для варианта удерживается в Params после назначения, фактически устраняет утечку памяти.

Похоже, что проблема связана с неразборчивой обратной записью значений параметров.Конкретное событие, с которым я имею дело, не имеет параметров, помеченных как var, поэтому COM-объект не ожидает изменений в параметре вызова для освобождения новой выделенной памяти.

Примерно COM-объект выделил массив, вызывает событие, а затем освобождает свою память после события.Однако Oleserver выделяет некоторую новую память, когда копирует параметр массива обратно в список параметров, о которых COM-объект даже не знает, поскольку он ничего не передавал по ссылке и не ожидает изменений в своих параметрах.Там должна быть некоторая дополнительная магия маршаллинга, которой я пренебрегаю, если кто-то знает подробности, мне определенно было бы любопытно.

Поле vt TVariantArg имеет флаг, чтобы указать, передается ли оно по значению или по ссылке.Насколько я могу различить, мы должны копировать значение обратно, только если параметр помечен как переданный по ссылке.

Кроме того, может потребоваться сделать больше, чем просто назначить вариант, если это действительно так.Передача по ссылке, хотя об этом может позаботиться маршаллинг, все еще не уверен насчет этой части.

На данный момент решение состоит в том, чтобы изменить код на:

if ((TDispParams(Params).rgvarg^[I].vt and VT_BYREF) <> 0) then begin
  OleVariant(TDispParams(Params).rgvarg^[I]) := VarArray[I];
end;
...