procedure ReadTextDFM(ADFMFile: TFileName; List: TStrings); var FileStream: TFileStream; BinaryStream: TMemoryStream; AuxShortString: ShortString; AuxUTF8String: UTF8String; AuxInt8: Int8; AuxInt16: Int16; AuxInt32: Int32; AuxInt64: Int64; PropertyName: ShortString; PropertyValue: String; Buffer: PByte; begin List.Clear; FileStream := TFileStream.Create(ADFMFile, fmOpenRead); try BinaryStream := TMemoryStream.Create; try FileStream.Seek(0, sofrombeginning); ObjectTextToBinary(FileStream, BinaryStream); BinaryStream.Seek(0,soFromBeginning); // Signature (ShortString) // Numa ShortString o elemento zero é o tamanho da string e o elemento 1 é // o início da string propriamente dita! Eu sei que a string de assinatura // tem 4 bytes, logo, pra começar eu configuro o tamanho corretamente da // string em seguida eu copio os 4 bytes a partir do elemento 1 da string, // dessa forma eu terei uma ShortString bem formada que contém a // assinatura do arquivo (magic number) AuxShortString[0] := AnsiChar(4); BinaryStream.Read(AuxShortString[1],4); List.Add('Signature = ' + AuxShortString); // FormClassName (ShortString) // Primeiro obtém o tamanho, depos usa esse tamanho para obter o valor do // nome da classe do form BinaryStream.Read(AuxInt8,1); AuxShortString[0] := AnsiChar(AuxInt8); BinaryStream.Read(AuxShortString[1],AuxInt8); List.Add('Form Class Name = ' + AuxShortString); // FormName (ShortString) BinaryStream.Read(AuxInt8,1); AuxShortString[0] := AnsiChar(AuxInt8); BinaryStream.Read(AuxShortString[1],AuxInt8); List.Add('Form Name = ' + AuxShortString); // A partir desse ponto é a parte que varia de acordo com qual componente // está sendo lido do dfm. while BinaryStream.Position < BinaryStream.Size do begin // Cada loop vai ser responsável por ler uma propriedade, então no // inicio do loop eu sempre estarei lendo o tamanho do nome da // propriedade. Quando o byte lido aqui é zero, significa que um // componente vai começar, um componente colocado no form. Neste momento // a função atual tem que ser chamada desde o começo, porém a partir do // ponto onde estamos em BinaryStream. Não vou implementar isso... Se // vira! (Envolve recursividade) // NomeDaPropriedade BinaryStream.Read(AuxInt8,1); if AuxInt8 = 0 then Break; PropertyName[0] := AnsiChar(AuxInt8); BinaryStream.Read(PropertyName[1],AuxInt8); // Tipo da propriedade BinaryStream.Read(AuxInt8,1); case TValueType(AuxInt8) of vaInt8: begin // Lê 1 Byte BinaryStream.Read(AuxInt8,1); PropertyValue := AuxInt8.ToString; end; vaInt16: begin // Lê 2 Bytes BinaryStream.Read(AuxInt16,2); PropertyValue := AuxInt16.ToString; end; vaInt32: begin // Lê 4 Bytes BinaryStream.Read(AuxInt32,4); PropertyValue := AuxInt32.ToString; end; vaInt64: begin // Lê 8 Bytes BinaryStream.Read(AuxInt64,8); PropertyValue := AuxInt64.ToString; end; vaUTF8String: begin // String UTF8 (Esta algoritmo não está bom) BinaryStream.Read(AuxInt32,4); // Variáveis declaradas que não são ponteiros ou objetos são // inicializadas automaticamente pelo Delphi. Variáveis String, e // isso inclui UTF8String, são ponteiros que apontam para o local // real onde a string está. O tamanho da string está nos 4 bytes // anteriores ao endereço de onde está a string propriamente dita, // mais ou menos assim: // XX XX XX XX YY YY YY YY YY YY // Onde XX são os bytes que represetam o tamanho da string e YY são // os bytes da string propriamente dita. O valor de uma variável // string é um ponteiro que aponta para o primeiro Byte da string // (Primeiro YY no exemplo). Ao declarar uma variável string, a sua // memória já estará alocada, mas a memória da string propriamente // dita, não! Antes da primeira atribuição a string, ela aponta para // "nil" e é por isso que não podemos acessar o endereço da string // propriamente dita, porque ele contém nil. Ao fazer a primeira // atribuição a string é inicializada e passa a apontar para um // endereço válido, aliás, toda vez que se atribui um novo valor a // uma string, ela passa a apontar para outro local da memória, // sempre ocupando o tamanho da string em Bytes + 4 Bytes de tamanho // no offset negativo. O gerenciamento da memória das strings é // feito automaticamente pelo Delphi. Como abaixo eu quero acessar // os dados da string diretamente, eu preciso inicializar a string // com a quantidade de Bytes, para (re)inicializar a string de forma // que ela aponte para um endereço válido. O SetString em combinação // com DupeString resolvem esse problema! Após a linha a seguir // teremos um endereço válido de memória que poderá ser preenchido // diretamente SetString(AuxUTF8String,PChar(DupeString(#0,AuxInt32)),AuxInt32); BinaryStream.Read(Pointer(AuxUTF8String)^,AuxInt32); // Isso já resolve a conversão entre UTF-8 e Unicode automaticamente PropertyValue := String(AuxUTF8String); end; vaIdent, vaString: begin // Membro de uma enumeração ou ShortString (é shortstring mesmo) BinaryStream.Read(AuxInt8,1); AuxShortString[0] := AnsiChar(AuxInt8); BinaryStream.Read(AuxShortString[1],AuxInt8); PropertyValue := String(AuxShortString); end; vaExtended: begin // Não implementei end; vaBinary: begin // Não implementei end; vaSet: begin BinaryStream.Read(AuxInt8,1); if AuxInt8 > 0 then begin // Não implementei end else PropertyValue := '[]'; end; vaFalse: begin // Não precisa ler nada, pois o valor é o tipo, no caso False PropertyValue := 'False'; end; vaTrue: begin // Não precisa ler nada, pois o valor é o tipo, no caso True PropertyValue := 'True'; end; end; List.Add(PropertyName + ' = ' + PropertyValue); end; finally BinaryStream.Free; end; finally FileStream.Free; end; end;