delphi - versiones - Error de propiedades de la interfaz publicada y soluciones
que es delphi 2010 (1)
Escribí un conjunto de componentes que se vinculan entre sí a través de propiedades de interfaz publicadas. Están registrados e instalados en un paquete de diseño.
El uso de propiedades de interfaz publicadas no es tan común en Delphi, y por lo tanto, como era de esperar, no parece funcionar tan bien.
Funciona bien cuando los componentes residen en el mismo formulario, sin embargo, los enlaces de propiedad de la interfaz entre los componentes en diferentes formularios causan problemas.
A diferencia de los enlaces de objeto a componentes en otro formulario, IDE no parece reconocer los enlaces de interfaz. Lo que quiero decir se describe mejor con un ejemplo, cuando tiene 2 formularios abiertos en IDE y tiene enlaces entre componentes, entonces tratar de cambiar a la vista de formulario como texto (Alt + F12) haría que IDE se queje correctamente de que:
Module ''UnitXXX.pas'' has open descendents or linked modules. Cannot close.
Pero si la propiedad es una interfaz, entonces esto no sucede, lo que sucede es que el enlace se corta (y ese es el mejor de los casos cuando se usa el mecanismo de Notificación para borrar referencias, de lo contrario, se queda con un puntero no válido)
Otro problema, probablemente como consecuencia del mismo error, es que cuando abre un proyecto en IDE, el orden en que se volverán a abrir los formularios no está definido, por lo que IDE puede intentar abrir un formulario que contenga componentes que tengan enlaces de interfaz a los componentes otra forma, pero esa otra forma no se recrea aún. Por lo tanto, esto resulta efectivamente en enlaces AV o cortados.
En los años 90, mientras usaba Datasets
y Datasources
, recuerdo problemas similares con la desaparición de los enlaces entre formularios, por lo que esto es algo similar.
Como una solución temporal añadí propiedades publicadas duplicadas, para cada propiedad de la interfaz agregué otra que se declara como TComponent
. Esto hace que Delphi sepa que existe un vínculo entre formularios, pero es una solución fea por decir lo menos.
Entonces, me pregunto si hay algo que pueda hacer para solucionar este problema. Es un error IDE y probablemente no se puede reparar directamente, pero tal vez pueda anular algo o engancharme al mecanismo de transmisión para solucionar este error de manera más efectiva.
No he profundizado tanto en el mecanismo de transmisión, pero sospecho que se supone que el mecanismo de reparación se ocupa de esto. Hay un csFixups
TComponentState
así que espero que haya una solución alternativa.
Editar: Usando D2007 .
Actualizar:
Nuevo ejemplo reproducible actualizado subido a http://www.filedropper.com/fixupbugproject2
Se agregó la property ComponentReference: TComponent
para que sea fácil comparar y rastrear la interfaz frente a la transmisión de componentes.
Reduje el problema al nivel de ensamblador, que está un poco fuera de mi alcance.
En el procedimiento GlobalFixupReferences
en la unidad de classes
llama:
(GetOrdProp(FInstance, FPropInfo) <> 0)
que eventualmente ejecuta:
function TInterfacedComponent.GetInterfaceReference: IInterface;
begin
// uncomment the code bellow to avoid exception
{ if (csLoading in ComponentState) and (FInterfaceReference = nil) then
// leave result unassigned to avoid exception
else
}
result := FInterfaceReference; // <----- Exception happens here
end;
Como puede ver en el comentario, la única forma que encontré para evitar la excepción es dejar el resultado sin asignar, pero eso rompe la funcionalidad ya que la comparación anterior en GlobalFixupReferences
falla debido a GetOrdProp <> 0
, que corta el vínculo.
rastreando más profundo, la ubicación más exacta de la excepción está en
procedure _IntfCopy(var Dest: IInterface; const Source: IInterface);
en la unidad del system
Esta línea en particular levanta una read of address 0x80000000
{ Now we''re into the less common cases. }
@@NilSource:
MOV ECX, [EAX] // get current value
Entonces, por qué MOV
falla y qué pasa con ECX
o EAX
no tengo ni idea.
Para resumir, el problema ocurre solo con las propiedades de interfaz publicadas que tienen un método getter, y la propiedad apunta al componente en otro formulario / módulo (y ese formulario / módulo aún no se recrea). En tal caso, la restauración del formulario DFM causa un AV.
Estoy bastante seguro de que el error está en el código ASM en GetOrdProp
, pero está más allá de mi capacidad de corrección, por lo que la solución más fácil es utilizar un campo en lugar de un método getter y leerlo directamente en la propiedad. Esto es, afortunadamente, lo suficientemente bueno para mi caso actualmente.
Alternativamente, puede declarar la propiedad como TComponent
lugar de la interfaz, luego escribir un descendiente TComponentProperty
, anular ComponentMayBeSetTo
para filtrar el componente que no admite la interfaz requerida. Y, por supuesto, registrarlo usando RegisterPropertyEditor