Clique para saber mais...
  Home     Download     Produtos / Cursos     Revista     Vídeo Aulas     Fórum     Contato   Clique aqui para logar | 12 de Junho de 2026
  Login

Codinome
Senha
Salvar informações

 Esqueci minha senha
 Novo Cadastro

  Usuários
156 Usuários Online

  Revista ActiveDelphi
 Assine Já!
 Edições
 Sobre a Revista

  Conteúdo
 Apostilas
 Artigos
 Componentes
 Dicas
 News
 Programas / Exemplos
 Vídeo Aulas

  Serviços
 Active News
 Fórum
 Produtos / Cursos

  Outros
 Colunistas
 Contato
 Top 10

  Publicidade

  [Artigos]  DLL, string e PChar
Publicado por cdelphi : Sexta, Janeiro 09, 2004 - 02:36 GMT-3 (34178 leituras)
Comentários 1 Comentário   Enviar esta notícia a um amigo Enviar para um amigo   Versão para Impressão Versão para impressão
Administrador Você saberia se virar sem a unidade Sharemem ao criar suas DLLs?
Este artigo descreve um pouco do funcionamento da unidade Sharemem e do gerenciador de memória do Delphi e apresenta duas formas criar DLLs sem ela.
Funcionamento de Sharemem e BORLANDMM.DLL

Este "capítulo" descreve um pouco do funcionamento do sistema de alocação de strings e de gerenciamento de memória com e sem a unidade Sharemem. Se não estiver interessado nisso, pode ignorá-lo e ir direto ao próximo.

O problema em usar strings e outras variáveis com processamento especial de alocação e desalocação com DLLs é o gerenciamento de memória.

Primeiro, um pouco sobre long strings.

Como você deve saber, uma string longa não é muito mais que um ponteiro para um local na memória. Este local tem algumas informações importantes a respeito da string, além, é claro, dos caracteres armazenados no momento.

Além dos caracteres, há um inteiro de 32 bits indicando o comprimento da string. Ao contrário de PChar, o Delphi pode ler o tamanho da string sem precisar percorrê-la e procurar pelo caractere #0. Além disso, uma string longa pode conter caracteres #0 válidos, ou seja, que não indiquem o final da string, mas algo que você possa interpretar como quiser.

Por fim, existe o campo reference count, outro inteiro de 32 bits indicando o número de variáveis que estão apontando no momento para aquele local na memória. Isso permite ao Delphi fazer algumas otimizações na atribuição de variáveis string.

Quando você atribui um valor a uma variável string, o Delphi normalmente aloca o espaço em memória necessário ao armazenamento dos caracteres, preenche os campos que indicam o comprimento e reference count e copia os caracteres para esse local na memória.

Quando a execução sair do escopo da variável (por exemplo sair da função onde existe uma variável local do tipo string), o Delphi trata de zerar as variáveis string e verificar se precisa desalocar o espaço onde os caracteres se encontram. Se precisar, utiliza a função de desalocação do gerenciador de memória.

O gerenciador de memória é uma biblioteca (localizada na unidade System) que trata de alocação, desalocação e realocação de memória no Delphi. Veja GetMem, FreeMem e ReallocMem para saber mais sobre essas operações.

O problema é que um executável host e as DLLs ligadas à ele têm seus próprios gerenciadores de memória. Uma porção de memória alocada em um deles não pode ser liberado em outro. Veja o diagrama abaixo.

Apesar dos gerenciadores serem idênticos (ambos implementados na unidade System), a "instância" é diferente. Todas as variáveis e áreas de controle, etc, estão localizadas em locais diferentes.

Quando a unidade Sharemem é incluída em um executável e suas DLLs, ela trata de substituir o gerenciador de memória padrão para um localizado em BORLANDMM.DLL. O Delphi permite essa alteração do gerenciador de memória através da procedure SetMemoryManager.

Assim, qualquer porção de memória (incluindo strings) alocada em um pode ser desalocada em outro.

Desta forma, o gerenciador padrão para o executável e suas DLLs são ignorados, dando lugar a um gerenciador compartilhado. Ele é compartilhado também por qualquer outro aplicativo que utilizar Sharemem.

Método 1: Procedure de liberação de PChar
Esse método se baseia na implementação, na DLL, de uma procedure de liberação de PChars retornados por ela. É uma procedure bastante simples, parecida com StrDispose do Delphi:

procedure DllStrDispose(Str: PChar);

Vantagens

Simplifica o processo de preparação para as chamadas. Não é necessário pré-alocar um PChar e nem chamar a função antes para retornar o tamanho necessário. Por só precisar chamar a função uma vez, evita processamento repetido.
Esse método pode ser usado para a criação de DLLs para uso com qualquer linguagem (Delphi, C/C++, VB, etc).

Desvantagens

Sempre que a DLL retornar uma PChar, o programa deve se encarregar de liberá-la ao final do uso. Isso não chega a ser uma desvantagem pois isso é uma consequência do uso de PChar.
Não é prático se você utiliza várias DLLs. É interessante para aplicativos com só uma DLL.
Como mencionado, cada DLL deve exportar sua função de desalocação de PChar, o que pode tornar o programa confuso se você precisar usar várias. Cada uma teria que ter um nome diferente e isso poderia criar algumas confusões como tentar desalocar uma PChar criada por uma DLL com a procedure de liberação de outra. Isso, claro, iria gerar uma exceção.

Vamos ao exemplo de uma DLL criada com este método:

library StrDLL2;

uses
SysUtils,
Classes;

{ Aloca um PChar e copia os caracteres da string AString para ele }
function DllStrGet(AString: string): PChar;
var
Size: Integer;
begin
Size := Length(AString) + 1;
Result := StrAlloc(Size); { Aloca usando o gerenciador de memória da DLL }
Move(Pointer(AString)^, Result^, Size);
end;

{ Função de liberação das PChars retornadas pela DLL }
procedure DllStrFree(Str: PChar); stdcall;
begin
StrDispose(Str); { Libera usando o gerenciador de memória da DLL }
end;

{ Função de demonstração }
function GetAutoexec: PChar; stdcall;
var
SL: TStringList;
begin
SL := TStringList.Create;
try
SL.LoadFromFile('c:\autoexec.bat');
Result := DllStrGet(SL.Text);
finally
SL.Free;
end;
end;

exports
DllStrFree, GetAutoexec;

begin
end.

A função de alocação e cópia da string (DllStrGet) é usada só internamente, sempre que você for retornar uma string. Se quiser, você pode tranquilamente alocar a PChar usando diretamente StrAlloc ou outras funções do Delphi.

Para usar as funções da DLL em seu aplicativo, você deve primeiro declarar as funções:

function GetAutoexec: PChar; stdcall;; stdcall; external 'StrDLL2.dll';
procedure DllStrFree(Str: PChar); stdcall; external 'StrDLL2.dll';

Para chamar as funções, faça como abaixo:

procedure ShowAutoexec;
var
P: PChar;
begin
P := GetAutoexec;
try
ShowMessage(P);
finally
DllStrFree(P);
end;
end;


O bloco try..finally foi usado para garantir a desalocação da variável. Se ocorresse exceção entre a chamada de GetAutoexec e DllStrFree, esta não seria chamada, fazendo com que o PChar retornado não fosse desalocado.

Método 2: Interface IString
Este segundo método é muito mais interessante que o primeiro. Ele tem todas as vantagens dele, além de:

Nenhuma função de desalocação precisa ser chamada. A desalocação é feita automaticamente pelo Delphi, mesmo utilizando PChar.
Pode ser usada em várias DLLs sem causar confusão.
A única desvantagem é com relação à performance. O processo de transferência de strings é um pouco mais lento do que com strings longas.

A solução baseia-se nas facilidades que o Delphi apresenta com relação a interfaces COM. Iremos utilizar duas unidades novas. Uma delas será usada tanto no aplicativo host quanto nas DLLs e a outra, só nas DLLs.

A primeira é a declaração de uma interface que chamaremos de IString. Em sua declaração, existe só um método, o Str, que deve retornar um PChar:

unit StrInt;

interface

type
IString = interface(IUnknown)
['{6ADF6275-82C0-4290-8C8D-DFDAA2AA17CC}']
function Str: PChar;
end;

implementation

end.


A segunda é a impementação de um objeto (TString) que implementa uma interface IString:

unit StrIntImp;

interface

uses SysUtils, StrInt;

type
TString = class(TInterfacedObject, IString)
private
FStr: PChar;
{ IString }
function Str: PChar;
public
constructor Create(AString: string); overload;
destructor Destroy; override;
end;

implementation

{ TString }

constructor TString.Create(AString: string);
var
Size: Integer;
begin
inherited Create;
Size := Length(AString) + 1;
FStr := StrAlloc(Size);
Move(Pointer(AString)^, FStr^, Size);
end;

destructor TString.Destroy;
begin
StrDispose(FStr);
inherited;
end;

function TString.Str: PChar;
begin
Result := FStr;
end;

end.


Ao implementar a DLL, sempre que quiser retornar uma string, você deve fazê-lo através de IString, e não mais PChar. Exemplo:

library strdll;

uses
SysUtils,
Classes,
StrIntImp,
StrInt;

{$R *.RES}

function GetAutoexec: IString; stdcall;
var
S: TStringList;
begin
S := TStringList.Create;
try
S.LoadFromFile('c:\autoexec.bat');
Result := TString.Create(S.Text);
finally
S.Free;
end;
end;

exports
GetAutoexec;

begin
end.


Perceba, então, que onde se retornaria um PChar, retornamos um IString criado através de TString.Create, que tem como parâmetro a string longa original que queremos retornar.

Para usar uma DLL criada com este método em um aplicativo host, você deve incluir na cláusula uses de todas as unidades que usarem a DLL a unidade StrInt e usar o valor retornado de forma parecida com o seguinte:

procedure ShowAutoexec;
var
I: IString;
begin
I := GetAutoexec;
ShowMessage(I.Str);
end;


Note que podemos usar também a seguinte forma para fazer a mesma coisa:

procedure ShowAutoexec;
begin
ShowMessage(GetAutoexec.Str);
end;


Perceba que a utilização é muito mais simples que quando com o primeiro método (o que usa uma procedure para liberar o PChar).

Para entender o funcionamento desse método, você deve ter um pouco de conhecimento de interfaces COM. Se quiser, pode pular os parágrafos seguintes.

O método baseia-se na chamada automática do método Release das interfaces quando a execução sai do escopo de uma variável do tipo interface COM. Ao sair do escopo de uma dessas variáveis o Delphi zera ela e chama o método Release automaticamente, de forma semelhante à liberação de strings.

Aproveitamos isso, então, para criarmos um objeto que implementa uma interface especial (IString). O objeto (TString) mantém uma PChar apontada para a string. Quando o objeto é destruído, ele automaticamente libera a variável PChar.

Vamos analisar o que ocorre no exemplo acima.

Quando o aplicativo chama a função GetAutoexec, esta (que está na DLL), cria uma string e a usa como parâmetro para a criação de um objeto TString. O construtor desse objeto, então, aloca o espaço necessário e copia os caracteres da string para lá. GetAutoexec retorna a interface IString associada ao objeto (o Delphi faz automaticamente a "conversão" entre TString e IString baseado no tipo que deve ser retornado por GetAutoexec).

No aplicativo host, usamos o método Str da interface como parâmetro para ShowMessage. Ao final da procedure ShowAutoexec, o Delphi executa automaticamente o método Release da interface IString.

Perceba que a classe TString é derivada de TInterfacedObject. Ela implementa os métodos de IUnknown de forma que o último Release destrua o objeto. Assim, quando o método Release é chamado ao final de ShowAutoexec, o objeto verifica se não existe mais nenhuma outra variável apontada para ele e executa o destrutor do objeto que, por suz vez, libera a PChar.

Claro que você não precisa entender isso para usar o método. Basta seguir os exemplos.

Importante
Só utilize estes métodos se realmente não quiser incluir a unidade Sharemem e a DLL BORLANDMM.DLL em seu projeto. A inclusão desses componentes facilita bastante a comunicação entre seu aplicativo e as DLLs criadas para ele, visto que as operações com strings longas em Delphi são bastante facilitadas pela linguagem.


Comentários Comentários
   Ordem:  
Comentários pertencem aos seus respectivos autores. Não somos responsáveis pelo seus conteúdos.


por: cristianok : Ago 16, 2006 - 08:56
(Informações sobre o membro | Enviar uma mensagem)
Seria muito interessante se indicassem o autor do artigo:
http://www.cristianok.hpg.ig.com.br/art2/index.html

Dá para ver as imagens, inclusive. Peço que o webmaster inclua o link no cabeçalho da página.

Obrigado.
  Edição 112

Revista ActiveDelphi

  50 Programas Fontes


  Produtos

Conheça Nossos Produtos

Copyright© 2001-2016 – Active Delphi – Todos os direitos reservados