방금 문제를 해결했지만 곧 "문제가되는 것"과 같은 문제가 생길 수 있습니다. 그걸로.델파이 스택 불일치 + COM 마샬링 = 잘못된 마셜링
Delphi는 변수를 스택에 정렬하지 않으며이 동작을 제어하기위한 지시문/옵션이 없습니다. 내 XP SP3의 기본 COM 마샬 러는 레코드를 마샬링 할 때 4 바이트 정렬이 필요합니다. 더욱이, 정렬되지 않은 포인터를 만나면 오류를 반환하지 않습니다. 포인터를 가장 가까운 4 바이트 경계로 반올림하고 그런 식으로 계속합니다.
따라서 참조로 COM 마샬링 된 함수에 스택에 할당 한 레코드를 전달하면 문제가 발생하고 알지도 못합니다.
메모리 관리자가 8 바이트 이상으로 모든 것을 정렬하는 경향이 있으므로 New/Dispose를 사용하여 레코드를 할당하면 문제가 해결 될 수 있지만 신, 불편 함이 있습니다. 불일치 부분과 "trim-down-pointer "부분.
이것이 정말로 이유입니까, 아니면 어딘가 잘못 되었습니까?
업데이트 : 재현하는 방법 (Delphi 2007 for 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 : byte"에 더 많은 바이트 변수를 추가하십시오 (함수의 끝 부분에서 일부 작업을 시뮬레이트하는 것을 잊지 마십시오).
COM이있는 부분은 재현하기가 쉽지만 설명하는 것이 더 길다. 샘플 서버라는 새 VCL 응용 프로그램을 만들고 형식 라이브러리에 ISampleObject를 구현하는 COM 개체 SampleObject를 추가합니다. ISampleObject는 유형 라이브러리에서 Ole 자동화로 표시되어 있는지 확인하십시오. 형식 라이브러리를 열고 5 개의 __int64 필드가있는 새 SampleRecord를 선언하십시오. 단일의 SampleRecord * out 파라미터를 가지는 SampleFunction를 ISampleObject에 추가합니다. 반환 고정 값으로 TSampleObject에서 SampleFunction 구현 : 적어도 내가 체크 한
SampleRecord = packed record
a: Int64;
b: Int64;
//...
end;
,이 :
function TSampleObject.SampleFunction(out rec: SampleRecord): HResult;
begin
rec.a := 1291;
rec.b := 742310;
//...
Result := S_OK;
end;
델파이가 자동으로 생성 된 형식 라이브러리 헤더 코드에 "포장 레코드"로 SampleRecord를 선언하는 방법을 참고 는 Delphi 2010에서 수정되었습니다. 자동으로 생성 된 레코드는 거기에 압축되어 있지 않습니다.
COM 서버를 등록하십시오. 그것을 실행하십시오. 특히 (
이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.
이 녹화의 주소가 일치하지 않는 경우, 녹화 필드의 값이 잘못된 것을 관찰 :
지금 대신하고있는 경우 Writeln의이 서버를 호출 (샘플 1) 위의 소스를 수정 8 비트, 16 비트 또는 24 비트로 비트 시프트, 때로는 다음 값으로 래핑 됨).
어떤 델파이 버전, 어떤 메모리 관리자입니까? 시도해 볼 수있는 예를 제공해 줄 수 있습니까? –
Delphi 2007, 즉시 사용 가능한 FastMM4. 예제가 추가되었습니다. – himself
이 테스트 케이스를 사용하여 Quality Central에 리포트를 입력하십시오. http://qc.embarcadero.com –