domingo, 14 de julho de 2013

Iniciantes em PowerBuilder

Todo iniciante a desenvolver com o PowerBuilder enfrenta dificuldades para encontrar tutoriais e apostilas sobre a ferramenta. A quantidade de materiais disponíveis na web é bastante escassa, principalmente em português.

Devido a isto, resolvi criar este post dando orientações para os iniciantes em tal desenvolvimento ou para aqueles que simplesmente querem conhecer melhor a linguagem.

Versão atual do PowerBuilder


As versões atuais do PowerBuilder sempre ficam disponíveis para download no site da Sybase, no modo de avaliação (trial).
  • Página da Sybase para apresentação do PowerBuilder: clique aqui.
  • Página do SAP/Sybase para download da versão 12.5 (trial) do PowerBuilder: clique aqui.

Obs: a versão 12.5 é a mais atual até o momento que elaborei este post.


Versões antigas


Caso você esteja interessado em versões antigas da ferramenta, seguem alguns links:
Obs: em breve pretendo disponibilizar mais versões antigas.

Ferramentas e bibliotecas auxiliares


Banco de dados
Como a grande maioria das aplicações escritas em PowerBuilder armazenam informações no banco de dados, disponibilizei alguns links de softwares de banco de dados recomendados:
  • Página para download do MySQL (É necessário criar conta na Oracle): clique aqui.
  • Página da Microsoft para download do SQL Server: clique aqui.

Framework
O principal framework do PowerBuilder é a PFC (PowerBuilder Foundation Class). Este conjunto de bibliotecas permite aos desenvolvedores construir poderosas aplicações com pouco esforço. Se você for construir aplicações comerciais, ela é indispensável.


Tutorial do PowerBuilder


Aqui, no próprio PowerBuilder Blog, disponibilizo um tutorial bem simples e intuitivo para iniciantes. O tutorial apresenta conceitos básicos da ferramenta e ensina ao leitor a construir uma aplicação para controle de bibliotecas. Para visualizar o tutorial: clique aqui.

Espero que as informações que apresentei neste post sejam úteis. Para outras dúvidas, ofereço uma área para contato aqui no PowerBuilder Blog.

quarta-feira, 20 de março de 2013

Tratamento de exceções

Atualmente, qualquer linguagem de programação que se preze oferece recursos para o tratamento de exceções. Nosso bom e velho PowerBuilder também oferece tais recursos (apesar de não ser da melhor forma...).

Vamos entender um pouco melhor como as exceções funcionam. Quando ocorre um erro em tempo de execução em uma aplicação PowerBuilder, é disparado um evento da aplicação chamado SystemError(). Você pode visualizar este evento abrindo um objeto "application" (figura abaixo).

Objeto "application".

Se optarmos por utilizar o SystemError() para realizar alguns tratamentos de falhas, com certeza enfrentaremos dificuldades, já que esse evento é disparado fora do local onde o código está sendo executado. Contudo, você pode querer capturar e tratar alguns erros no próprio código de uma interface ou classe. Veja o exemplo de código abaixo. Imagine que estamos tentando capturar, através de uma datawindow, o nickname de um usuário que acabou de logar em um sistema.

String ls_user

ls_user = dw_test.getItemString(1, "NICKNAME_USER")

Como programador PowerBuilder, você deve saber que, caso esta datawindow não possua tal linha ou coluna solicitadas, a aplicação irá disparar a clássica mensagem "Error: Invalid DataWindow row/column specified at line...". Para realizar o tratamento deste erro no próprio método, precisaremos conhecer o que a linguagem oferece. Veja abaixo detalhes de cada palavra reservada:

TRY
   <codificação que pode gerar uma exceção>
CATCH
   <codificação que permite capturar e tratar uma exceção>
FINALLY
   <codificação que permite encerrar o bloco de código. Ideal para fechamento de conexões, destruição de objetos, etc. >
END TRY


Vamos agora tratar o nosso código:

String ls_user

TRY

ls_user = dw_test.getItemString(1, "NICKNAME_USER")

CATCH (Throwable aoException)
Messagebox ("Falha", "Não foi possível identificar o  usuário: " + aoException.getMessage())
CLOSE(THIS)
END TRY


Entenda que, desta forma, ao ser disparada a falha no getItemString(), automaticamente a próxima instrução a ser executada será o CATCH, independente se existir mais codificações ao longo do TRY. 

Vamos complementar o código fonte do TRY com mais algumas instruções. Nelas você verá que também é possível lançar exceções manualmente. Desta forma, podemos centralizar o tratamento de outras situações:

String ls_user

TRY

ls_user = dw_test.getItemString(1, "NICKNAME_USER")

   IF ISNULL(ls_user) OR Trim(ls_user) = "" THEN
  Exception loException
      loException = CREATE Exception
      loException.setMessage("O nickname do usuário está nulo ou vazio")
      THROW (loException)
  END IF

CATCH (Throwable aoException)
Messagebox ("Falha", "Não foi possível identificar o usuário: " + aoException.getMessage())
CLOSE(THIS)
END TRY


Métodos que disparam exceções

Para elaboramos códigos com qualidade e organização, é importante conhecermos as boas práticas de programação relacionadas. Quando conhecemos os recursos para tratamento de exceções, já podemos eliminar uma (má) prática que ainda é bastante utilizada. Quantas vezes você codificou um método cujo valor de retorno era um tipo int e sua codificação retornava -1 para indicar falha? Com o tratamento de exceções isto não é mais necessário!

Vamos adaptar o código que vimos anteriormente para que esteja contido em uma função chamada of_getNickname(). O retorno desta função será do tipo "string", ou seja, o nickname do usuário. Se ocorrer algum problema na recuperação desse nickname, vamos evitar retornar uma string vazia ou nula para indicar a falha. Iremos lançar uma nova exceção a fim de indicar ao código de origem (o que invocou a função) que houve uma ação inesperada.

// Função: of_getnickname() Retorno: string

String ls_user

TRY

ls_user = dw_test.getItemString(1, "NICKNAME_USER")

   IF ISNULL(ls_user) OR Trim(ls_user) = "" THEN
  Exception loException
      loException = CREATE Exception
      loException.setMessage("O nickname do usuário está nulo ou vazio")
      THROW (loException)
  END IF

CATCH (Throwable aoException)
THROW (aoException)
END TRY

RETURN ls_user

Se você já inseriu o código acima para fazer algum teste, perceberá que o PowerBuilder acusou um erro. O motivo disto é porque devemos informar que nossa função of_getnickname() está pronta para tratar exceções. Para definir isto, você precisará preencher o campo "Throws", na especificação da função, com o valor "exception". Veja na imagem abaixo.



Pronto. Agora basta que função de origem faça um tratamento semelhante ao que você vê a seguir.



TRY

 String ls_user = of_getNickname()

CATCH (Throwable aoException)
Messagebox ("Falha", "Não foi possível identificar o usuário: " + aoException.getMessage())
CLOSE(THIS)
END TRY


Caso queira tratar suas exceções mais especificamente, listo abaixo algumas classes de exceção:
  • Throwable - Tratamento mais genérico, englobando todos os erros;
  • Exception - Exceções criadas pelo usuário e herdadas do Throwable;
  • RuntimeError - Erros de Runtime do PowerBuilder e herdado do Throwable;
  • NullObjectError - Referência nula para objetos e herdado do RuntimeError;
  • DivideByZeroError - Divisão por zero e herdado do RuntimeError;
  • DWRuntimeError - Erros em comandos da Datawindow e herdado do RuntimeError;
  • OLERuntimeError - Erros em comandos de Objetos OLE e herdado do RuntimeError;
  • CORBASystemException - Erros em comando de objetos CORBA e herdado do RuntimeError.

Espero que tenha dado uma noção de como construir aplicações com uma estrutura que utiliza tratamento de exceções. Apesar de toda a sintaxe utilizada nos exemplos ter sido em PowerScript, não descarto o aprendizado também para desenvolvedores C#, Java, Visual Basic, etc.


quarta-feira, 9 de janeiro de 2013

Autenticação Windows em uma aplicação PowerBuilder

Quando falamos em autenticar usuários de uma aplicação, estamos tratando de aspectos de segurança de dados. Existem diversas formas de se realizar autenticações, porém é necessário ter cautela na escolha de qual modo é mais adequado ao seu sistema.

Há softwares que autenticam usuário através de tabelas e métodos próprios e outros autenticam através de mecanismos oferecidos pelos bancos de dados. Neste post, irei descrever como a autenticação de usuários da sua aplicação pode ser realizada via usuários do Windows. Isto significa que podem ser os próprios usuários de um domínio (DNS - Domain Name System) da sua empresa.

Antes do código fonte da autenticação propriamente dita, é interessante conhecer outras funções relacionadas. A primeira é a função que captura o nickname do usuário logado atualmente. Para utilizá-la, acesse a área de "Local External Functions" da sua janela ou objeto PowerBuilder e insira o código abaixo:

Function ulong GetUserNameW(ref string lpBuffer, ref ulong nSize) Library "advapi32.dll"

Perceba que estamos utilizando bibliotecas fornecidas pelo próprio sistema operacional.

Após isto, basta inserir o código abaixo em uma função ou evento do seu objeto para poder recuperar o nickname em questão:

String is_nome_usuario
ulong ll_size

// Captura o nome do usuário da sessão atual do windows
ll_size = 256
is_nome_usuario = Space(ll_size + 1)
GetUserNameW(ref is_nome_usuario, ref ll_size)

Abaixo, apresento mais duas funções úteis. Uma para recuperar o ID do processo atual e outra para recuperar o nome da estação (computador) utilizado:

Function ulong GetCurrentProcessId() Library "kernel32.DLL" 

Function ulong GetComputerNameW(ref string lpBufferref ulong nSizeLibrary "kernel32.DLL"

E aqui o código da função:

String is_nome_computador
ulong ii_process_id
ulong ll_size

// Captura o ID do processo atual da aplicação
ii_process_id = GetCurrentProcessId()

// Captura o nome da estação de trabalho
ll_size = 256
is_nome_computador = Space(ll_size + 1)
GetComputerNameW(is_nome_computador, ll_size)

Agora, segue a declaração da função de autenticação citada anteriormente:

Function boolean LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, ulong dwLogonType, ulong dwLogonProvider, ref ulong phToken) Library "advapi32.dll" Alias For "LogonUserA;Ansi"

Function boolean CloseHandle(ulong hObject) Library "kernel32.dll"

E, finalmente, o código fonte para autenticação do usuário:


Boolean lb_resultado
String ls_dominio
String ls_windows_usuario
String ls_windows_senha
ULong lul_token

Constant ULong LOGON32_LOGON_NETWORK = 3
Constant ULong LOGON32_PROVIDER_DEFAULT = 0

// Captura de variáveis
ls_dominio   = "DEFINA O DOMÍNIO (SE HOUVER)"
ls_windows_usuario = "DEFINA O USUÁRIO"
ls_windows_senha = "DEFINA A SENHA"

// Autentica o usuário
lb_resultado = LogonUserA(ls_windows_usuario, ls_dominio, ls_windows_senha, &
LOGON32_LOGON_NETWORK,  LOGON32_PROVIDER_DEFAULT, lul_token )

// Verifica se autenticou com sucesso
IF NOT lb_resultado THEN
CloseHandle(lul_token)
Messagebox("Não autenticado", "O login ou a senha informados estão incorretos.",stopsign! )
RETURN TRUE
END IF

RETURN FALSE


Caso deseje conhecer a API dos comandos do sistema operacional utilizados nos exemplos acima, seguem os links: