Ver Mensaje Individual
  #5  
Antiguo 24-11-2015
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: may 2003
Posts: 5.604
Reputación: 29
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
Hola David.

El artículo que enlazas contiene esto en la cabecera:
Cita:
Empezado por Marc Durdin
Update 21 Sep 2015: This bug has been fixed in Delphi 10 Seattle.
Creo que podrías asumir que la falla está presente en todas las versiones anteriores. Aunque da más confianza cuando algún documento oficial del fabricante lo confirma (al menos esta fecha de corrección nos permite inferirlo).

Ahora bien, revisando su propuesta y la solución que Embarcadero implementó, te diría que ambas son equivalentes en resultado. Sin embargo, no hace falta sabiduría para darnos cuenta de que la segunda opción consume más ciclos de CPU, mientras que la primera es parca pero más eficiente. En la solución de Embarcadero, la sentencia "Result := IDispatch(Temp.VDispatch)" obtiene una copia de la interfaz contenida en Temp.VDispatch e incrementa en uno su contador de referencias. Luego la sentencia "IDispatch(Temp.VDispatch) := nil" disminuye en 1 ese mismo contador, lo cual, dada la sentencia anterior, se vuelve necesario. En cambio, en la solución que Marc Durdin propone, "Pointer(Result) := Temp.VDispatch" obtiene una copia de la interfaz sin incrementar su contador de referencias, por lo tanto sin necesidad de hacer un decremento explícito.

La solución del artículo es más elegante y eficiente. Según los enlaces del autor, al parecer lo tomó de aquí. Mientras que el programador que escribió la corrección en Delphi Seattle parece haberse limitado a añadir una línea de parche, sin atender el problema de fondo. (La única justificación, aunque ligera, que podríamos darle a añadir esa línea de más, es que estuviera previendo que Embarcadero revisara el manejo de la estructura de los variants por parte del compilador).

La causa del problema reside en que la estructura interna de un valor Variant —el tipo TVarData— es, valga la redundancia, un registro con parte variante. El compilador de Delphi no admite que en la parte variante de un record aparezca declarado un campo cuyo tipo lo haga emplear un contador de referencias, como es el caso de las cadenas de caracteres, las interfaces y algunos otros. Así que, para salvar esa limitante —que es bastante razonable y justificada—, algunos campos del registro variante TVarData fueron declarados como punteros genéricos:
Código Delphi [-]
  TVarData = packed record
    ...
                      case Integer of
                        varDispatch: (VDispatch: Pointer);
                        ...
                        varUnknown:  (VUnknown: Pointer);
                        ...
                        varString:   (VString: Pointer);
                        ...
                        varUString:  (VUString: Pointer);
    ...
El problema con estos es que, al quedar el registro fuera de ámbito (la variable local Temp queda fuera de ámbito cuando GetIDispatchProp llega a su end), nada se encarga de revisarlos para ver si contienen algo que haga falta ser liberado. Muy al contrario de si VDispatch fuera un campo normal declarado de tipo IDispatch en lugar de Pointer.

Así que al extraer valores que usan contadores de referencias de variables, parámetros, propiedades o campos explícitamente declarados con un tipo "comodín", como el Pointer, es menester recordar que ahí hay algo que habrá de ser descontado tras la extracción (solución de Embarcadero), o bien, evitar que aumente el contador utilizando un molde de tipo sobre el receptor (solución del artículo). El truco con esta opción es que el contador se mantiene intacto, y transfiere la responsabilidad de reducirlo al receptor del valor. En el ejemplo que nos ocupa, GetProperty pone en Temp.VDispatch una interfaz a la cual le incrementa en uno su contador de referencias, y la responsabilidad de restar ese 1 queda en el llamador de GetIDispatchProp (puesto que a él le pertenece Result). El intermediario GetIDispatchProp ni se entera de que está contrabandeando una interfaz.

Espero esta pequeña contribución dé algún valor al tema.

Al González.

P.D. David: Te confirmo que en Delphi XE7 Update 1 el código de los métodos mencionados aún presentaban esa falla:
Código:
function TOleControl.GetIDispatchProp(Index: Integer): IDispatch;
var
  Temp: TVarData;
begin
  GetProperty(Index, Temp);
  Result := IDispatch(Temp.VDispatch);
end;

...

function TOleControl.GetIUnknownProp(Index: Integer): IUnknown;
var
  Temp: TVarData;
begin
  GetProperty(Index, Temp);
  Result := IUnknown(Temp.VUnknown);
end;

Última edición por Al González fecha: 24-11-2015 a las 01:16:20.
Responder Con Cita