{"id":1862,"date":"2022-07-14T12:17:55","date_gmt":"2022-07-14T15:17:55","guid":{"rendered":"https:\/\/gladiston.net.br\/?page_id=1862"},"modified":"2022-07-14T17:13:12","modified_gmt":"2022-07-14T20:13:12","slug":"criando-bibliotecas-de-funcoes-dll-em-pascal","status":"publish","type":"page","link":"https:\/\/gladiston.net.br\/en\/programacao-em-delphi\/criando-bibliotecas-de-funcoes-dll-em-pascal\/","title":{"rendered":"Criando e consumindo DLL"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\" style=\"text-transform:capitalize\">Vis\u00e3o geral<\/h2>\n\n\n\n<p>Criar bibliotecas \u00e9 pr\u00e1tico, voc\u00ea mant\u00eam um conjunto de funcionalidades presos em arquivos externos ao seu programa e ent\u00e3o voc\u00ea distribui junto com o seu execut\u00e1vel.<\/p>\n\n\n\n<p>Uso pr\u00e1tico<\/p>\n\n\n\n<p>Voc\u00ea pode incluir um conjunto de fun\u00e7\u00f5es dentro de uma DLL para que v\u00e1rios programas juntos consumam as suas fun\u00e7\u00f5es, com isso voc\u00ea garante que programas diferentes usem o mesmo m\u00e9todo, por exemplo, uma fun\u00e7\u00e3o que testa se o login de uma pessoa, isto \u00e9, nome de usu\u00e1rio e senha est\u00e3o corretos para prosseguir com acesso ao banco de dados. Para impedir que cada programa seu tenha um Ctrl+C\/Ctrl+V da mesma fun\u00e7\u00e3o em todos os programas, voc\u00ea criaria uma biblioteca em pascal que fizesse esse teste e colocaria todos os programas para compilar junto com essa biblioteca e isso daria certo, mas passam-se os anos e algu\u00e9m altera essa biblioteca e agora teremos os programas novos que usam o c\u00f3digo novo, por\u00e9m programas mais antigos deveriam ser recompilados com a nova biblioteca ou pode acontecer problemas. Da\u00ed a ideia de usar bibliotecas externas ao seu programa principal que normalmente manter\u00e1 sua consist\u00eancia.<\/p>\n\n\n\n<p>Outro exemplo pr\u00e1tico para usar DLLs s\u00e3o relat\u00f3rios. Ao inv\u00e9s de colocar relat\u00f3rios embutidos dentro do seu execut\u00e1vel, \u00e9 mais saud\u00e1vel deix\u00e1-los externamente em forma de DLL. Esse m\u00e9todo permite que clientes diferentes solicitem novos relat\u00f3rios que afetem apenas eles.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Como criar uma fun\u00e7\u00e3o para DLL bem b\u00e1sica usando strings<\/h2>\n\n\n\n<p>O pascal tem m\u00e9todos de lidar com variaveis que diferem muito de outras linguagens como C\/C++, ent\u00e3o no exemplo abaixo, vamos lidar com um tipo de string chamado de pChar. O tipo pChar \u00e9 um array ou buffer de caracteres ou bytes enfileirados, ele n\u00e3o \u00e9 t\u00e3o pr\u00e1tico quanto String, mas escrever DLLs que possam ser usadas por outros programas que foram escritos em outra linguagem requer que usemos ele, ent\u00e3o veja um exemplo bem b\u00e1sico de como funciona.<\/p>\n\n\n\n<p>O exemplo abaixo \u00e9 uma fun\u00e7\u00e3o gen\u00e9rica que n\u00e3o implementa, mas cria um modelo de como eu posso testar um login no banco de dados fornecendo par\u00e2metros como login, senha e role por meio de uma TStringList e retorna novamente uma TStringList.<\/p>\n\n\n\n<p>Escolhi TStringList porque elas s\u00e3o f\u00e1ceis de manipular, eu posso usar TStringList.Values[&#8216;parametro&#8217;] para descobrir um valor ou TStringList.Items.IndexOf[&#8216;parametro&#8217;] para saber se um par\u00e2metro foi fornecido e assim por diante.<\/p>\n\n\n\n<p>Algo que pode muito confundi-lo \u00e9 que ao olhar para o c\u00f3digo voc\u00ea notar\u00e1 que o par\u00e2metro de entrada \u00e9 pChar e n\u00e3o TStringList como eu disse, por que?<\/p>\n\n\n\n<p>N\u00e3o vou usar exatamente o TStringList, mas TStringList.Text que tamb\u00e9m \u00e9 uma string. Ao lidar com DLLs precisamos que ela seja criada de um jeito que outros programas possam entender, outras linguagens n\u00e3o usam uma String do jeito que o pascal o faz, assim precisamos usar um tipo especial de String chamada de pChar. <\/p>\n\n\n\n<p>O tipo pChar \u00e9 uma cadeia especial de caracteres que pode facilmente ser convertido para string e vice-versa. Ent\u00e3o vamos ao c\u00f3digo:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;pascal&quot;,&quot;mime&quot;:&quot;text\/x-pascal&quot;,&quot;theme&quot;:&quot;default&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;TrpContentRestriction&quot;:{&quot;restriction_type&quot;:&quot;exclude&quot;,&quot;selected_languages&quot;:[],&quot;panel_open&quot;:true},&quot;language&quot;:&quot;Pascal&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;pascal&quot;}\">library DLL_Teste;\nuses\n  Classes,\n  SysUtils\n  { you can add units after this };\n\nfunction F_TestLogin(pParamList:pChar): pChar; stdcall;\nvar\n  sLoginName, sLoginPassword, sRole, sConfigFile:String;\n  ListaIN:TStringList;\n  ListaOUT:TStringList;\n  function FakeLogin(ALoginName, APassword, ARole:String):String;\n  begin\n    Result:=emptyStr;\n  end;\nbegin\n  ListaIN:=TStringLIST.Create;\n  ListaOUT:=TStringLIST.Create;\n  ListaOUT.Values['RESULT']:='FAIL';\n  \/\/ garantindo que qualquer exit antes do tempo retorne FAIL\n  Result:=pChar(ListaOUT.Text);\n  try\n    \/\/ A estrutura de minha string \u00e9 compativel com TStrings ent\u00e3o posso\n    \/\/ import\u00e1-la como se fosse uma StringList\n    ListaIN.Text:=String(pParamList);   \/\/ pChar para string\n    sConfigFile:=Trim(ListaIN.Values['CONFIG_FILE']);\n    sLoginName:=Trim(ListaIN.Values['User_Name']);\n    sLoginPassword:=Trim(ListaIN.Values['Password']);\n    sRole:=UPPERCASE(Trim(ListaIN.Values['Role']));\n    \/\/ apenas um exemplo de teste de autentica\u00e7\u00e3o\n    if FakeLogin(sLoginName, sLoginPassword, sRole)=emptyStr then\n    begin\n      \/\/ Prosseguindo...\n\t  ListaOUT.Values['RESULT']:='OK';\n\t  ListaOUT.Values['MSG_RESULT']:='OK';\n    end\n    else\n    begin\n      \/\/ Erro ao tentar conectar ao servidor\n      ListaOUT.Values['RESULT']:='FAIL';\n      ListaOUT.Values['MSG_RESULT']:='Autentica\u00e7\u00e3o falhou';\n    end;\n  except\n  on e:exception do\n     begin\n\t   ListaOUT.Values['RESULT']:='FAIL';\n       ListaOUT.Values['MSG_RESULT']:=e.message;\n\t end;\n  end;\n  ListaOUT.Values['User_Name']:=sLoginName;\n  ListaOUT.Values['Password']:=sLoginPassword;\n  ListaOUT.Values['Role']:=sRole;\n  Result:=pChar(ListaOUT.Text);\n  ListaIN.Free;\n  ListaOUT.Free;\nend;\n\nexports\n  F_TestLogin;\n\nbegin\nend.\n<\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">N\u00e3o usar\u00e1 outra linguagem, apenas pascal?<\/h2>\n\n\n\n<p>Se voc\u00ea usar\u00e1 apenas pascal e nada mais, seja delphi ou freepascal ent\u00e3o n\u00e3o \u00e9 necess\u00e1rio usar pChar ou qualquer outro tipo de ponteiros para tipos primitivos. Ao usar os tipos primitivos do pascal ao inv\u00e9s de pChar e ponteiros voc\u00ea economiza muito c\u00f3digo dedicado a convers\u00e3o de tipos. tamb\u00e9m poder\u00e1 remover as indica\u00e7\u00f5es do tipo <strong>stdcall<\/strong>;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Como consumir DLLs<\/h2>\n\n\n\n<p>Agora, como posso consumir a DLL acima? <\/p>\n\n\n\n<p>Voc\u00ea pode consumir DLLs de duas formas:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Est\u00e1tica<\/strong>: quando seu aplicativo \u00e9 carregado, todas as DLLs que foram informada sem seu programa ser\u00e3o carregadas conjuntamente e se falhar uma delas, o programa ser\u00e1 interrompido. Quando usar? Voc\u00ea precisa que seja est\u00e1tica quando se tratar de um programa ou servi\u00e7o indispens\u00e1vel ao seu programa, sem essa(s) biblioteca(s) o programa n\u00e3o deve nem funcionar. Por exemplo, uma DLL de acesso a impressora fiscal.<\/li><li><strong>Dinamicamente<\/strong>: A DLL \u00e9 requerida apenas no momento do uso, voc\u00ea a executa e em seguida a mesma \u00e9 liberada da mem\u00f3ria. Quando usar? Quando n\u00e3o est\u00e1 certo se a funcionalidade dentro da biblioteca ser\u00e1 usada ou n\u00e3o, \u00e9 o caso de relat\u00f3rios, um usu\u00e1rio poder\u00e1 decidir imprimir ou n\u00e3o o relat\u00f3rio ent\u00e3o porque carregar a biblioteca todas as vezes?<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Carregar uma DLL estaticamente <\/h2>\n\n\n\n<p>N\u00e3o h\u00e1 muito segredo ao carregar uma DLL estaticamente. Por\u00e9m, visto que em Delphi ou FreePascal podemos gerar um c\u00f3digo bin\u00e1rio num \u00fanico execut\u00e1vel ent\u00e3o porque dever\u00edamos usar uma DLL dessa forma? Realmente n\u00e3o h\u00e1 muitos motivos para isso, mas vamos ao exemplo mesmo assim:<\/p>\n\n\n\n<p>(todo)<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Carregar uma DLL dinamicamente<\/h2>\n\n\n\n<p>Essa \u00e9 a principal raz\u00e3o de usar uma DLL no Delphi ou FreePascal, uma vez que voc\u00ea pode carreg\u00e1-la e descarreg\u00e1-la ap\u00f3s o uso. Muito vers\u00e1til, por exemplo, no caso de relat\u00f3rios, pode manter vers\u00f5es diferentes do mesmo relat\u00f3rio ou atualiz\u00e1-los mantendo o programa principal intacto. Vamos a um exemplo de como carregar e descarregar uma DLL dinamicamente:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:false,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;pascal&quot;,&quot;mime&quot;:&quot;text\/x-pascal&quot;,&quot;theme&quot;:&quot;default&quot;,&quot;lineNumbers&quot;:true,&quot;styleActiveLine&quot;:true,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;TrpContentRestriction&quot;:{&quot;restriction_type&quot;:&quot;exclude&quot;,&quot;selected_languages&quot;:[],&quot;panel_open&quot;:true},&quot;language&quot;:&quot;Pascal&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;pascal&quot;}\">procedure TForm1.BtnConsumirClick(Sender: TObject);\ntype\n  TF_TestLogin = function (pParamList:pChar): pChar; stdcall;\nvar\n  F_TestLogin: TF_TestLogin;  \/\/ Uma vari\u00e1vel para acessar a sub-rotina na DLL\n  LibHandle: THandle; \/\/ um handle para a DLL\n  pResultado:pChar;   \/\/ resultado da fun\u00e7\u00e3o \u00e9 um pchar\n  ListaIN:TStringList;   \/\/ lista de parametros de entrada\n  ListaRecebe:TStringList;  \/\/ valores recebidos em forma de lista\n  pListaIN:Pchar;        \/\/ lista de parametros de entrada em formato pChar\n  ErrorMode: UINT;       \/\/ codigo de erro caso aconte\u00e7a\n  ErrorMsg:String;       \/\/ mensagem de erro que impe\u00e7a de prosseguir \nconst\n  DLL='C:\\caminho\\para\\DLL_Teste.dll';\nbegin\n  \/\/ variavel que indicar\u00e1 a ocorr\u00eancia de algum erro\n  ErrorMsg:=emptyStr;\n  \/\/ variavel que conter\u00e1 o retorno da fun\u00e7\u00e3o dentro da DLL\n  pResultado:=pChar(emptyStr);\n  \/\/ Minha lista que receber\u00e1 o retorno\n  ListaRecebe:=TStringList.Create;\n  \/\/ Meu parametro de entrada\n  ListaIN:=TStringList.Create;\n  ListaIN.Values['User_Name']:='meulogin';\n  ListaIN.Values['Password'] :='minhasenha';\n  ListaIN.Values['Role']     :='RDB$ADMIN';\n  \/\/ Caminho para a DLL\n  if not FileExists(DLL)\n    then ErrorMsg:='Arquivo n\u00e3o existe: '+DLL;\n  if ErrorMsg=emptyStr then\n  begin\n    \/\/ Desliga OS error messages\n    ErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);\n    \/\/ Pegar o handle identificador da biblioteca a ser usada\n    try\n      LibHandle := LoadLibrary(PChar(DLL));\n    finally\n      SetErrorMode(ErrorMode);\n    end;\n    if LibHandle  &lt;= HINSTANCE_ERROR\n      then ErrorMsg:='Erro ao carregar: '+DLL;\n    if ErrorMsg=emptyStr then\n    begin\n      \/\/ Confere se o carregamento da DLL foi bem-sucedido\n      if Win32Check(Bool(LibHandle)) then\n      begin\n        \/\/ Atribui o endere\u00e7o da chamada da subrotina \u00e0 vari\u00e1vel F_TestLogin, mas\n        \/\/ nesse ponto o FPC e Delphi tem jeitos diferentes:\n        {$IFDEF FPC}\n          Pointer(F_TestLogin) := GetProcAddress(LibHandle, 'F_TestLogin');\n        {$ELSE}\n          F_TestLogin := GetProcAddress(LibHandle, 'F_TestLogin');\n        {$ENDIF}\n\n        \/\/ Verifica se um endere\u00e7o v\u00e1lido foi retornado\n        if @F_TestLogin &lt;&gt; nil then\n        begin\n          \/\/ pegando o resultado\n          pListaIN:=pChar(ListaIN.Text);\n          pResultado := F_TestLogin(pListaIN);\n          \/\/ como o resultado \u00e9 pChar ent\u00e3o tenho que converter \n          \/\/ para string e jogar numa lista onde fica mais facil\n          \/\/ processar depois\n          ListaRecebe.Text:=String(pResultado);\n        end;\n      end;\n    end;\n  end;\n  \/\/\n  \/\/ tudo pronto para usufruir do retorno\n  \/\/\n  if ErrorMsg=emptyStr then\n  begin\n    memoResultado.Text:=ListaRecebe.Text;\n    if SameText(ListaRecebe.Values['RESULT'],'OK') then\n      memoResultado.Lines.Add('Legal que tudo tenha ocorrido bem.')\n    else\n      memoResultado.Lines.Add('Que chato, parece que o login falhou.');\n  end\n  else\n  begin\n    memoResultado.Lines.Add('N\u00e3o pude chamar a DLL: '+ErrorMsg);\n  end;\n\n  \/\/ liberando memoria\n  F_TestLogin := nil;\n  while not FreeLibrary(LibHandle) do\n    Sleep(5); \/\/ espera 5s para uma nova tentativa\n  ListaIN.Free;\n  ListaRecebe.Free;\nend;<\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Fatora\u00e7\u00e3o de c\u00f3digo<\/h2>\n\n\n\n<p>Voc\u00ea pode vir a achar que carregar uma DLL dinamicamente \u00e9 muito verboso, no entanto, raramente vamos us\u00e1-lo do jeito que foi mostrado, normalmente vamos fatorar, isto \u00e9, criar uma fun\u00e7\u00e3o com o c\u00f3digo acima e apenas variando o nome da DLL, os par\u00e2metros de entrada e retornando o resultado conseguido. Ao fatorar uma \u00fanica vez como por exemplo ChamarRelatorio(&#8216;relatorio_vendas.dll&#8217;) evita-se o esfor\u00e7o de copiar\/colar em todas as ocorr\u00eancias praticamente o mesmo c\u00f3digo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclus\u00e3o<\/h2>\n\n\n\n<p>Espero que tenha achado o artigo ben\u00e9fico. O exemplo fornecido funciona em Delphi e tamb\u00e9m em FreePascal.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Vis\u00e3o geral Criar bibliotecas \u00e9 pr\u00e1tico, voc\u00ea mant\u00eam um conjunto de funcionalidades presos em arquivos externos ao seu programa e ent\u00e3o voc\u00ea distribui junto com o seu execut\u00e1vel. Uso pr\u00e1tico Voc\u00ea pode incluir um conjunto de fun\u00e7\u00f5es dentro de uma DLL para que v\u00e1rios programas juntos consumam as suas fun\u00e7\u00f5es, com isso voc\u00ea garante que [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":0,"parent":1186,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"templates\/template-full-width.php","meta":{"footnotes":""},"class_list":["post-1862","page","type-page","status-publish","hentry"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/pages\/1862","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/comments?post=1862"}],"version-history":[{"count":39,"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/pages\/1862\/revisions"}],"predecessor-version":[{"id":1919,"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/pages\/1862\/revisions\/1919"}],"up":[{"embeddable":true,"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/pages\/1186"}],"wp:attachment":[{"href":"https:\/\/gladiston.net.br\/en\/wp-json\/wp\/v2\/media?parent=1862"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}