You are on page 1of 6

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


memria do Delphi e apresenta duas formas criar DLLs sem ela.

Funcionamento de Sharemem e BORLANDMM.DLL

Este "captulo" descreve um pouco do funcionamento do sistema de alocao de strings e de


gerenciamento de memria com e sem a unidade Sharemem. Se no estiver interessado nisso, pode
ignor-lo e ir direto ao prximo.

O problema em usar strings e outras variveis com processamento especial de alocao e


desalocao com DLLs o gerenciamento de memria.

Primeiro, um pouco sobre long strings.

Como voc deve saber, uma string longa no muito mais que um ponteiro para um local na
memria. Este local tem algumas informaes importantes a respeito da string, alm, claro, dos
caracteres armazenados no momento.

Alm dos caracteres, h um inteiro de 32 bits indicando o comprimento da string. Ao contrrio de


PChar, o Delphi pode ler o tamanho da string sem precisar percorr-la e procurar pelo caractere #0.
Alm disso, uma string longa pode conter caracteres #0 vlidos, ou seja, que no 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 nmero de variveis
que esto apontando no momento para aquele local na memria. Isso permite ao Delphi fazer
algumas otimizaes na atribuio de variveis string.

Quando voc atribui um valor a uma varivel string, o Delphi normalmente aloca o espao em
memria necessrio ao armazenamento dos caracteres, preenche os campos que indicam o
comprimento e reference count e copia os caracteres para esse local na memria.

Quando a execuo sair do escopo da varivel (por exemplo sair da funo onde existe uma varivel
local do tipo string), o Delphi trata de zerar as variveis string e verificar se precisa desalocar o
espao onde os caracteres se encontram. Se precisar, utiliza a funo de desalocao do gerenciador
de memria.

O gerenciador de memria uma biblioteca (localizada na unidade System) que trata de alocao,
desalocao e realocao de memria no Delphi. Veja GetMem, FreeMem e ReallocMem para saber
mais sobre essas operaes.

O problema que um executvel host e as DLLs ligadas ele tm seus prprios gerenciadores de
memria. Uma poro de memria alocada em um deles no pode ser liberado em outro. Veja o
diagrama abaixo.

Apesar dos gerenciadores serem idnticos (ambos implementados na unidade System), a "instncia"
diferente. Todas as variveis e reas de controle, etc, esto localizadas em locais diferentes.

Quando a unidade Sharemem includa em um executvel e suas DLLs, ela trata de substituir o
gerenciador de memria padro para um localizado em BORLANDMM.DLL. O Delphi permite essa
alterao do gerenciador de memria atravs da procedure SetMemoryManager.

Assim, qualquer poro de memria (incluindo strings) alocada em um pode ser desalocada em
outro.

Desta forma, o gerenciador padro para o executvel e suas DLLs so ignorados, dando lugar a um
gerenciador compartilhado. Ele compartilhado tambm por qualquer outro aplicativo que utilizar
Sharemem.

Mtodo 1: Procedure de liberao de PChar


Esse mtodo se baseia na implementao, na DLL, de uma procedure de liberao de PChars
retornados por ela. uma procedure bastante simples, parecida com StrDispose do Delphi:

procedure DllStrDispose(Str: PChar);

Vantagens

Simplifica o processo de preparao para as chamadas. No necessrio pr-alocar um PChar e nem


chamar a funo antes para retornar o tamanho necessrio. Por s precisar chamar a funo uma
vez, evita processamento repetido.
Esse mtodo pode ser usado para a criao 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 no chega a ser uma desvantagem pois isso uma consequncia do uso de PChar.
No prtico se voc utiliza vrias DLLs. interessante para aplicativos com s uma DLL.
Como mencionado, cada DLL deve exportar sua funo de desalocao de PChar, o que pode tornar
o programa confuso se voc precisar usar vrias. Cada uma teria que ter um nome diferente e isso
poderia criar algumas confuses como tentar desalocar uma PChar criada por uma DLL com a
procedure de liberao de outra. Isso, claro, iria gerar uma exceo.

Vamos ao exemplo de uma DLL criada com este mtodo:

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 memria da DLL }
Move(Pointer(AString)^, Result^, Size);
end;

{ Funo de liberao das PChars retornadas pela DLL }


procedure DllStrFree(Str: PChar); stdcall;
begin
StrDispose(Str); { Libera usando o gerenciador de memria da DLL }
end;

{ Funo de demonstrao }
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 funo de alocao e cpia 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 funes do Delphi.

Para usar as funes da DLL em seu aplicativo, voc deve primeiro declarar as funes:

function GetAutoexec: PChar; stdcall;; stdcall; external 'StrDLL2.dll';


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

Para chamar as funes, faa 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 desalocao da varivel. Se ocorresse exceo entre a
chamada de GetAutoexec e DllStrFree, esta no seria chamada, fazendo com que o PChar retornado
no fosse desalocado.

Mtodo 2: Interface IString


Este segundo mtodo muito mais interessante que o primeiro. Ele tem todas as vantagens dele,
alm de:

Nenhuma funo de desalocao precisa ser chamada. A desalocao feita automaticamente pelo
Delphi, mesmo utilizando PChar.
Pode ser usada em vrias DLLs sem causar confuso.
A nica desvantagem com relao performance. O processo de transferncia de strings um
pouco mais lento do que com strings longas.

A soluo baseia-se nas facilidades que o Delphi apresenta com relao 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 declarao de uma interface que chamaremos de IString. Em sua declarao, existe
s um mtodo, 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 impementao 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 atravs de IString,
e no 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, ento, que onde se retornaria um PChar, retornamos um IString criado atravs de
TString.Create, que tem como parmetro a string longa original que queremos retornar.

Para usar uma DLL criada com este mtodo em um aplicativo host, voc deve incluir na clusula
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 tambm a seguinte forma para fazer a mesma coisa:

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

Perceba que a utilizao muito mais simples que quando com o primeiro mtodo (o que usa uma
procedure para liberar o PChar).

Para entender o funcionamento desse mtodo, voc deve ter um pouco de conhecimento de
interfaces COM. Se quiser, pode pular os pargrafos seguintes.

O mtodo baseia-se na chamada automtica do mtodo Release das interfaces quando a execuo
sai do escopo de uma varivel do tipo interface COM. Ao sair do escopo de uma dessas variveis o
Delphi zera ela e chama o mtodo Release automaticamente, de forma semelhante liberao de
strings.
Aproveitamos isso, ento, para criarmos um objeto que implementa uma interface especial (IString).
O objeto (TString) mantm uma PChar apontada para a string. Quando o objeto destrudo, ele
automaticamente libera a varivel PChar.

Vamos analisar o que ocorre no exemplo acima.

Quando o aplicativo chama a funo GetAutoexec, esta (que est na DLL), cria uma string e a usa
como parmetro para a criao de um objeto TString. O construtor desse objeto, ento, aloca o
espao necessrio e copia os caracteres da string para l. GetAutoexec retorna a interface IString
associada ao objeto (o Delphi faz automaticamente a "converso" entre TString e IString baseado no
tipo que deve ser retornado por GetAutoexec).

No aplicativo host, usamos o mtodo Str da interface como parmetro para ShowMessage. Ao final
da procedure ShowAutoexec, o Delphi executa automaticamente o mtodo Release da interface
IString.

Perceba que a classe TString derivada de TInterfacedObject. Ela implementa os mtodos de


IUnknown de forma que o ltimo Release destrua o objeto. Assim, quando o mtodo Release
chamado ao final de ShowAutoexec, o objeto verifica se no existe mais nenhuma outra varivel
apontada para ele e executa o destrutor do objeto que, por suz vez, libera a PChar.

Claro que voc no precisa entender isso para usar o mtodo. Basta seguir os exemplos.

Importante
S utilize estes mtodos se realmente no quiser incluir a unidade Sharemem e a DLL
BORLANDMM.DLL em seu projeto. A incluso desses componentes facilita bastante a comunicao
entre seu aplicativo e as DLLs criadas para ele, visto que as operaes com strings longas em Delphi
so bastante facilitadas pela linguagem.

You might also like