Esto no es exactamente una pregunta directa porque acabo de resolverlo, sino más bien como un tipo de pregunta "estoy haciendo lo correcto" y un recordatorio para aquellos que podrían quedarse atascados en eso.desalineamiento de la pila Delphi + clasificación combinada = clasificación errónea
Resulta que Delphi no alinea las variables en la pila y no hay directivas/opciones para controlar este comportamiento. El Marshaller COM predeterminado en mi XP SP3 parece requerir una alineación de 4 bytes cuando se combinan los registros. Peor aún, cuando encuentra un puntero desalineado, no devuelve un error, oh no: redondea el puntero al límite de 4 bytes más cercano y continúa así.
Por lo tanto, si pasa un registro que ha asignado en la pila a la función de referencia de COM por referencia, está jodido y ni siquiera lo sabrá.
El problema se puede resolver usando Nuevo/Eliminar para asignar registros, ya que los administradores de memoria tienden a alinear todo a 8 bytes o mejor, pero Dios, esto es molesto, tanto la parte de desalineación como los "recortar-apuntadores" "parte.
¿Es este realmente el motivo, o me equivoco?
Actualización: Cómo reproducir (Delphi 2007 para 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.
Esto da una dirección de resultado impar, claramente no alineada en nada. Si no es así, intente agregar más variables de bytes a "a, b, c: byte" (y no olvide simular algún trabajo con ellas al final de la función).
La pieza con COM es más fácil de reproducir pero más larga de explicar. Cree una nueva aplicación VCL llamada Sample Server, agregue un objeto COM SampleObject que implemente ISampleObject, con una biblioteca de tipos, una única instancia de subproceso libre (asegúrese de comprobar que ISampleObject está marcado como Ole Automation en la biblioteca de tipos). Abra la biblioteca de tipos, declare un nuevo SampleRecord con cinco campos __int64. Agregue un SampleFunction con un solo parámetro SampleRecord * out a ISampleObject. Implementar sampleFunction en TSampleObject mediante la devolución de valores fijos:
function TSampleObject.SampleFunction(out rec: SampleRecord): HResult;
begin
rec.a := 1291;
rec.b := 742310;
//...
Result := S_OK;
end;
Nota cómo Delphi declara SampleRecord como "registro lleno" en el código de cabecera biblioteca de tipos generada automáticamente:
SampleRecord = packed record
a: Int64;
b: Int64;
//...
end;
He comprobado, y esto, por lo menos , se arregló en Delphi 2010. Los registros generados automáticamente no están empaquetados allí.
Registre el servidor COM. Ejecutarlo.
Ahora modificará el código anterior (muestra 1) para llamar a este servidor en vez de hacer 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.
Observe que cuando la dirección de REC no está alineado, los valores de los campos rec están equivocados (en concreto, cambio de bits a 8, 16 o 24 bits, a veces envuelto en el siguiente valor).
¿Qué versión de Delphi, qué Administrador de memoria? ¿Puedes darnos un ejemplo que podamos probar? –
Delphi 2007, FastMM4 listo para usar. Ejemplos agregados – himself
Ingrese un informe en Quality Central con este caso de prueba; http://qc.embarcadero.com –