Это не точно верный вопрос, потому что я только что решил его, но больше как "я получающий его правильный" тип вопроса и напоминания для тех, кто мог бы застрять в это.
Оказывается, Delphi не выравнивает переменные на стеке и нет никаких директив/опций для управления этим поведением. Значение по умолчанию COM marshaller на моем XP SP3, кажется, требует 4-байтового выравнивания при маршалинге записей все же. Хуже, когда это встречается с невыровненным указателем, это не возвращает ошибку, о нет: это округляет указатель в меньшую сторону до ближайшей 4-байтовой границы и продолжается как этот.
Поэтому при передаче записи, Вы выделили на стеке в COM-упорядоченную функцию ссылкой, Вы завинчены, и Вы не будете даже знать.
Проблема может быть решена при помощи Нового/располагать для выделения записей, поскольку диспетчеры памяти имеют тенденцию выравнивать все на уровне 8 байтов или лучше, но бог, это является раздражающим, и часть неточного совмещения и "trim-down-pointers" часть.
Это - действительно причина или является мной неправильно где-нибудь?
Обновление: Как воспроизвести (Delphi 2007 для Win32).
uses SysUtils;
type
TRec = packed record
a, b, c, d, e: int64;
end;
TDummy = class
protected
procedure Proc(param1: integer);
end;
procedure TDummy.Proc(param1: integer);
var a, b, c: byte;
rec: TRec;
begin
a := 5;
b := 9;
c := 100;
rec.a := param1;
rec.b := a;
rec.c := b;
rec.d := c;
writeln(IntToHex(integer(@rec), 8));
readln;
end;
var Obj: TDummy;
begin
obj := TDummy.Create;
try
obj.Proc(0);
finally
FreeAndNil(obj);
end;
end.
Это дает нечетный адрес результата, ясно не выровненный на чем-либо. Если это не делает, попытайтесь добавить больше переменных байта к "a, b, c: байт" (и не забывают моделировать некоторую работу с ними в конце функции).
Часть с COM легче воспроизвести, но дольше объяснить. Создайте новое приложение VCL под названием Демонстрационный Сервер, добавьте COM-объект реализация SampleObject ISampleObject с библиотекой типов, единственный экземпляр со свободными потоками (удостоверьтесь, что проверили, что ISampleObject отмечен как Ole Automation в библиотеке типов). Откройте библиотеку типов, объявите новый SampleRecord с пять __ int64 поля. Добавьте SampleFunction с единственным SampleRecord* параметр к ISampleObject. Реализуйте SampleFunction в TSampleObject путем возвращения фиксированных значений:
function TSampleObject.SampleFunction(out rec: SampleRecord): HResult;
begin
rec.a := 1291;
rec.b := 742310;
//...
Result := S_OK;
end;
Отметьте, как Delphi объявляет SampleRecord как "упакованная запись" в автоматически сгенерированном коде заголовка библиотеки типов:
SampleRecord = packed record
a: Int64;
b: Int64;
//...
end;
Я проверил, и это, по крайней мере, было зафиксировано в Delphi 2010. Автоматически сгенерированные записи не упаковываются там.
Зарегистрируйте сервер COM. Выполните его.
Теперь измените источник выше (демонстрационный 1) для вызова этого сервера вместо того, чтобы просто делать writeln:
uses SysUtils, Windows, ActiveX, SampleServer_TLB;
procedure TDummy.Proc(param1: integer);
var a, b, c: byte;
rec: SampleRecord;
Server: ISampleObject;
begin
a := 5;
b := 9;
c := 100;
rec.a := param1;
rec.b := a;
rec.c := b;
rec.d := c;
Server := CoSampleObject.Create;
hr := Server.SampleFunction(rec);
writeln('@: 'IntToHex(integer(@rec), 8)+', rec.a='+IntToStr(rec.a));
readln;
end;
var Obj: TDummy;
begin
CoInitializeEx(nil, COINIT_MULTITHREADED);
obj := TDummy.Create;
try
obj.Proc(0);
finally
FreeAndNil(obj);
CoUninitialize();
end;
end.
Заметьте, что, когда адрес rec не выровненный, значения rec полей являются неправильными (а именно, bitshifted к 8, 16 или 24 бита, иногда перенесенные законченный к следующему значению).
У вас есть пример неправильного выравнивания стека? Delphi должен выравнивать все по границам 4 байт. Единственный случай, который я могу представить, который может привести к смещению, это если где-то в цепочке вызовов есть какой-то ассемблерный код, который явно сделал что-то со стеком, чтобы выровнять его неправильно.