quinta-feira, 22 de novembro de 2007

Fazer chamada POST através do PowerBuilder 7

Há ocasiões onde é necessário enviar um conjunto de informações (preenchidas em um formulário por exemplo) para algum serviço rodando em algum servidor remoto. Uma maneira de fazer isso, é enviar essas informações através do método POST. Normalmente isso é feito através de um formulário HTML armazenado em um servidor web, porém é possível que uma aplicação PowerBuilder envie informações (de uma datawindow por exemplo) da mesma maneira.

No exemplo a seguir, o conteúdo de um formulário simples será enviado através do método POST para um script PHP armazenado em um servidor web. Este script apenas exibirá o conteúdo enviado pela nossa aplicação.

O script PHP

Um script simples em PHP fará o papel do serviço que receberá a requisição POST vinda do PowerBuilder.

testePOST.php

<?php
print_r($_POST); /* Exibe o conteúdo recebido via POST */
?>
A aplicação PowerBuilder

Em uma nova aplicação, crie uma Standard Class do tipo InternetResult. Este objeto manipulará as informações que nossa aplicação receberá do servidor. Salve esta classe com o nome n_cst_internet.


Na função InternetData(blob data) no objeto recém criado, coloque o seguinte código:
// Irá exibir em uma MessageBox o conteúdo retornado pelo servidor
// que recebeu o POST
MessageBox("O script PHP retornou:", String(data))
RETURN 0
Em seguida, crie uma nova janela com dois campos de texto (sle_nome e sle_email) e um botão (cb_enviar).


No evento clicked do botão cb_enviar, temos o seguinte código:
String httprequest, ls_header, ls_url, ls_headers
Long ll_ret, ll_length
Blob lblb_args
Inet linet_main
n_cst_internet luo_data

linet_main = CREATE inet
luo_data = CREATE n_cst_internet

String nome
nome = sle_nome.Text

String email
email = sle_email.Text

ls_url = "http://www.meusite.com.br/testePOST.php"
lblb_args = blob("nome=" + nome + "&email=" + email)
ll_length = Len(lblb_args)
ls_headers = "Content-Type: " + &
"application/x-www-form-urlencoded~n" + &
"Content-Length: " + String( ll_length ) + "~n~n"

ll_ret = linet_main.PostURL(ls_url,lblb_args,ls_headers,luo_data)

IF(ll_ret <> 1) THEN
MessageBox("ERRO", "POST falhou (retorno: " + String(ll_ret))
END IF
Rodando esta aplicação, receberemos o retorno do script PHP:


Observação: A função PostURL (no PowerBuilder 7) não permite que enviemos informações para uma porta diferente da 80 (não é possível fazer uma chamada para uma página HTTPS configurada na porta 8080 por exemplo). Aparentemente, a partir do PowerBuilder 8 essa limitação foi eliminada (não cheguei a testar).

segunda-feira, 15 de outubro de 2007

Criando uma conexão ODBC utilizando C++

Ao escrever um código que efetua uma interação com um sistema de banco de dados, normalmente é necessário incluir trechos de código específicos do banco de dados utilizado. Se você quiser utilizar um banco de dados Sybase, Access ou PostgreSQL, será necessário a escrita de três códigos diferentes.

Utilizando o ODBC (Open Data Base Connectivy), você fará chamadas a funções da API do ODBC (combinadas com queries SQL). O gerenciador ODBC saberá como executar a função desejada no banco de dados escolhido. É necessário apenas que você tenha instalado no seu computador, um driver ODBC específico do banco de dados que você estará utilizando.

Existem implementações de ODBC para vários sistemas operacionais, porém vamos tratar aqui apenas da implementação da Microsoft (Microsoft Open Database Connectivity (ODBC)).

Os passos básicos para efetuar uma conexão a um base dados, efetuar um query e encerrar essa conexão podem ser vistos no fluxograma abaixo (fonte: Basic ODBC Application Steps).



Não vou explicar o funcionamento de cada uma dessas funções (pelo menos nesse post). Acredito que um pequeno exemplo de código em C++ possa ser muito mais didático para a utilização rápida.

Neste exemplo, uma conexão com uma fonte de dados ODBC é realizada e o conteúdo de dois campos é buscado e exibido na tela (caso haja algum registro).

Exemplo 1 - Selecionando campos de uma tabela - Compilado no Visual C++ 6.0


#include <windows.h>
#include <stdio.h>
#include <iostream.h>
#include <string.h>
#include <sqlext.h>

int main(int argc, char* argv[])
{
UCHAR campo1[100];
UCHAR campo2[100];

// Query SQL que será executada
unsigned char szSqlStr[255];
strcpy((char*)szSqlStr, "SELECT campo1, campo2 FROM tabela WHERE condicao = '2'");

SDWORD cbCampo1; // Model buffer bytes recieved
SDWORD cbCampo2; // Model buffer bytes recieved

SQLRETURN retcode;
SQLHENV henv; // Environment handle
SQLHDBC hdbc; // Connection handle
SQLHSTMT hstmt; // Statement handle

UCHAR szDSN[SQL_MAX_DSN_LENGTH] = "nome_fonte"; // Data Source Name buffer
UCHAR* szUID = (unsigned char *) "user_id"; // User ID buffer
UCHAR* szPasswd = (unsigned char *) "pass"; // Password buffer

// Alocando manipulador de ambiente
retcode = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);

// Definir atributo de ambiente de versão do ODBC
retcode = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);

// Alocando manipulador de conexão
retcode = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);

// Conexão com a fonte de dados
retcode = SQLConnect(hdbc, szDSN, SQL_NTS, szUID, SQL_NTS, szPasswd, SQL_NTS);

// Alocando manipulador da query
retcode = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);

// Executa a query
retcode = SQLExecDirect(hstmt, szSqlStr, SQL_NTS);
if(retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
SQLBindCol (hstmt, 1, SQL_C_CHAR, campo1, sizeof(campo1), &cbCampo1);
SQLBindCol (hstmt, 2, SQL_C_CHAR, campo2, sizeof(campo2), &cbCampo2);
retcode = SQLFetch (hstmt);
if(retcode != SQL_NO_DATA_FOUND) {
// Se encontrou dados
cout << "Campo 1: " << campo1 << endl;
cout << "Campo 2: " << campo2 << endl;
} else {
cout << "Nenhum registro encontrado";
}
}

// Libera manipulador da query
retcode = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);

// Desconecta da fonte de dados
retcode = SQLDisconnect(hdbc);

// Libera manipuladorde conexão
retcode = SQLFreeHandle(SQL_HANDLE_DBC, hdbc);

// Libera manipulador de ambiente
retcode = SQLFreeHandle(SQL_HANDLE_ENV, henv);

return 0;
}

sexta-feira, 21 de setembro de 2007

Iniciando o Internet Explorer a partir do PowerBuilder

Esta semana tive que iniciar um projeto em que a principal funcionalidade seria executar, a partir de um botão em uma window, o Internet Explorer na máquina do cliente em uma determinada página. Pensei em usar inicialmente a função Run(), porém uma das limitações do projeto era que a janela do navegador não poderia ter nenhuma barra de ferramentas disponível (endereço, status, favoritos, etc)

Como não é possível iniciar o IE em linha de comando com parâmetros para ocultar essas barras de ferramentas tive que procurar outra solução. Pesquisando dentro da empresa, um Arquiteto de Software me sugeriu utilizar um objeto OLE para fazer essa tarefa.

Fuçando no site do MSDN, descobri o objeto InternetExplorer que permite trabalhar com uma instância do IE. Você pode configurar diversas propriedades desse objeto (como exibição das barras de ferramentas, tamanho da janela, etc). Mais informações em http://msdn2.microsoft.com/en-us/library/aa752084.aspx.

Bom, no final fiz o seguinte código (utilizei o PowerBuilder 7) dentro do evento clicked() de um botão:

OLEObject uo_ie
uo_ie = CREATE OLEObject
Integer ii_handleoleobject = -999

ii_handleoleobject = uo_ie.ConnectToNewObject("InternetExplorer.Application")
IF ii_handleoleobject < 0 THEN
DESTROY uo_ie
MessageBox('Erro','Não foi ´possível criar o objeto OLE')
ELSE
uo_ie.AddressBar = FALSE
uo_ie.MenuBar = FALSE
uo_ie.Resizable = FALSE
uo_ie.StatusBar = FALSE
uo_ie.ToolBar = FALSE
uo_ie.Visible = TRUE
uo_ie.Left = 200
uo_ie.Top = 200
uo_ie.Height = 500
uo_ie.Width = 500
uo_ie.Navigate(is_urlchamada)
SetForegroundWindow(uo_ie.HWND)
END IF

Declarando uma função externa (para que a janela recém-criada fique ativada):

FUNCTION boolean SetForegroundWindow( long hWnd ) LIBRARY "USER32"