Esta código lê um arquivo no formato DFM (Delphi Form), na modalidade "texto plano" e extrai dele as propriedades e seus valores. Atualmente apenas as propriedades do form (raiz do DFM) são lidas. Para ler todo o arquivo DFM é necessário melhorar este código e usar recursividade. O código não é perfeito, mas dá uma boa ideia do que precisa ser feito
- 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;