domingo, março 18, 2007

Curso de wxWidgets, post 14: Comunicando-se pela rede

Olá, pessoal! Como havia prometido no post anterior, este será sobre sockets e comunicação via rede.

Nesta primeira parte, abordarei o cliente, ou seja, o programa irá conectar em algum servidor. Tive a idéia de fazer um código para enviar e-mails, já que o protocolo SMTP é simples e fácil para mensagens em texto plano e sem anexos. Para quem não conhece o protocolo SMTP, sugiro que leia a RFC822, que descreve seu formato:

ftp://ftp.rfc-editor.org/in-notes/rfc822.txt

De qualquer forma, vou dar uma resumida no protocolo.

Nos parágrafos abaixo, <- indica uma mensagem que está vindo do servidor e -> indica uma mensagem que o cliente está enviando para o servidor.

Ao se conectar em um servidor SMTP, o cliente recebe uma mensagem de boas-vindas, geralmente com o endereço do servidor e alguns dados do software:

<- 220 spaceymail-mx1.g.dreamhost.com ESMTP


Em seguida, o cliente deve se idenficar enviando a string "EHLO" e o seu endereço. Após a identificação, o servidor dá algumas informações sobre o envio da mensagem (não relevante para nós no momento):

-> EHLO just.justsoft.com.br
<- 250-spaceymail-mx1.g.dreamhost.com
<- 250-PIPELINING
<- 250-SIZE 40960000
<- 250-ETRN
<- 250-STARTTLS
<- 250 8BITMIME


Agora o cliente deve dizer o endereço do remetente com o comando "MAIL From:" e do destinatário com "RCPT To:":

-> MAIL From: <teste@teste.com>
<- 250 Ok
-> RCPT To: <jpjust@justsoft.com.br>
<- 250 Ok


Após estas informações, o cliente começa a mensagem com o comando "DATA":

-> DATA
<- 354 End data with <CR><LF>.<CR><LF>
-> From: <just@just.com>
-> To: <jpjust@justsoft.com.br>
-> Date, Thu, 15 Mar 2007 21:00:00 +0000
-> Subject: Teste
->
-> teste
->
-> .
<- 250 Ok: queued as 6C0C3CE937


O fim da mensagem deve ser indicado com um ponto (.) sozinho em uma linha. Por último, o cliente pede para o servidor fechar a conexão com "QUIT":

-> QUIT
<- 221 Bye


E é isto! Você pode testar os comandos conectando em algum servidor SMTP pelo telnet. Basta executar o comando "telnet mx1.hotmail.com 25" para conectar no servidor SMTP do Hotmail e enviar mensagens para endereços de lá. Veja abaixo uma pequena lista com o servidor SMTP de alguns serviços de e-mail famosos:

Hotmail: mx1.hotmail.com
Yahoo!: a.mx.mail.yahoo.com
GMail: gmail-smtp-in.l.google.com
UOL: mx.uol.com.br
BOL: mx.boil.com.br


Se você usa Linux ou algum outro UNIX, use o comando host para saber o servidor SMTP de algum domínio de e-mail. Por exemplo, para saber o servidor SMTP de gmail.com:

$ host -t MX gmail.com


Voltando à programação, o que iremos fazer é um programa em wxWidgets para se conectar a um servidor SMTP e enviar um e-mail.

A parte wxWidgets da coisa

Para todo o trabalho de rede no wxWidgets, usaremos a classe wxSocketBase e uma classe derivada, a wxSocketClient.

wxSocketBase é a classe base para todas as outras classes de socket no wxWidgets. Neste post, usaremos apenas a classe wxSocketClient, que é responsável por fazer as conexões do nosso programa, que é o cliente, no servidor.

Conectando-se no servidor

Antes de fazer alguma conexão com a wxSocketClient, precisamos do endereço do servidor ao qual vamos nos conectar e também a porta da conexão. Para manusear essas informações, usaremos a classe wxIPV4address. Essa classe serve para guardar informações de um endereço, como número IP e porta. Para definir o endereço do servidor e a porta, usamos dois métodos:

wxIPV4address host;
host.Hostname(wxT("smtp.server.com"));
host.Service(wxT("25"));


No trecho de código acima, criamos um objeto de nome host, que é uma instância de wxIPV4address. Em seguida, definimos o endereço como smtp.server.com e a porta como 25.

Após criar um objeto com o endereço e a porta do servidor, podemos chamar o método wxSocketClient::Connect() para conectar no servidor:

wxSocketClient sock;
bool status;
status = sock.Connect(host);


Se a conexão for feita com sucesso, o método retorna true, que será guardado na variável status. Em caso de erro na conexão, logicamente, o método retornará false.

Por padrão, o método wxSocketClient::Connect() aguarda a conexão ser feita ou a ocorrência de um erro para prosseguir a execução. Mas é possível chamar o método e continuar a execução do programa enquanto o socket está se conectando, basta adicionar um parâmetro ao método:

sock.Connect(host, false);


O segundo parâmetro indica se o método deverá aguardar a conexão ser completada. Se você escolheu esta maneira para se conectar, poderá verificar se a conexão foi feita posteriormente com o método wxSocketBase::IsConnected() ou até mesmo, aguardar pela conexão em um ponto posterior com wxSocketClient::WaitOnConnect().

Enviando dados

Após ter sido feita a conexão, já é possível enviar e receber dados. Para fazer o envio, usamos o método wxSocketBase::Write(). Após enviar qualquer dado, podemos verificar se o envio foi feito com sucesso com o método wxSocketBase::Error():

wxString dado = wxT("EHLO localhost\r\n");
socket.Write((char *)dado.mb_str(), dado.Len());
if (socket.Error())
{
wxMessageBox(wxT("Erro ao enviar o dado."));
}


No trecho acima, enviamos a string "EHLO localhost\r\n" e em caso de erro, uma mensagem é exibida ao usuário. Perceba o cast (char *) e o método wxString::mb_str(). Este método retorna a string no formato ANSI. Perceba também que o método wxSocketBase::Write() não define um tipo de dado específico, você pode enviar texto puro ou binário. Como no nosso exemplo de um cliente SMTP estamos enviando texto puro, obtemos o formato ANSI da string e usamos o cast (char *) para indicar ao método que estamos enviando um char. O segundo parâmetro do método indica o tamanho do dado que estamos enviando. No caso, como é texto puro em ANSI (1 byte por caracter), indicamos o tamanho da string.

Em caso de erro no envio, wxSocketBase::Error() retorna true.

Recebendo dados

Após enviar algum dado pelo socket, geralmente esperamos por uma resposta, esta é a hora de fazer a leitura do socket. Para isto, usaremos o método wxSocketBase::Read():

char buffer[1024] = {0};
socket.Read(buffer, 1024 - 1);
if (socket.Error())
{
wxMessageBox(wxT("Erro ao fazer a leitura."));
}
int contagem = socket.LastCount();


Primeiro, criamos um buffer do tipo char com 1 KB. Em seguida, fazemos a leitura, armazenando a saída em buffer e indicando o máximo de dados que deverá ser lido (subtraímos 1 do tamanho pois devemos guardar um espaço para o terminador de string \0 da variável).

Mais uma vez, wxSocketBase::Error() entra em ação para nos informar se houve algum erro.

Neste trecho, usamos também o método wxSocketBase::LastCount(), ele retorna o número de bytes lidos no wxSocketBase::Read(). Ele também pode ser usado após um wxSocketBase::Write() para saber quantos bytes foram enviados de fato.

Fechando a conexão

Após enviarmos e recebermos todos os dados necessários para a conexão, devemos terminá-la. Basta um único método para isso, o wxSocketBase::Close():

socket.Close()


Existe também um outro método relacionado ao término de conexão, o wxSocketBase::WaitForLost(). Com ele, você pode indicar um timeout em segundos ou milissegundos. Se a conexão for fechada antes do timeout (o servidor pode fechar a conexão), o método retorna true, caso o timeout seja atingido, ele retorna false.

Outras formas de E/S

Além do wxSocketBase::Read() e do wxSocketBase::Write(), existem também o wxSocketBase::WriteMsg() e o wxSocketBase::ReadMsg(). Com esses dois métodos, é possível trocar mensagens entre duas aplicações em wxWidgets sem precisar se preocupar com a contagem de bytes enviados ou recebidos.

Mostrarei estes e outros métodos das classes de socket em posts futuros. Ainda há muito o que falar sobre sockets no wxWidgets :)

Configurando o socket

Também é possível fazer algumas configurações no socket antes de utilizá-lo. Veja abaixo.

wxSocketBase::SetTimeout(): configura o tempo de timeout em segundos para os métodos de E/S e de espera do socket. O valor padrão é de 10 minutos.

socket.SetTimeout(120); // Configura o timeout para 120 segundos


wxSocketBase::SetFlags(): configura o comportamento de espera do socket nas operações de E/S. Pode receber como argumento os valores abaixo:

wxSOCKET_NONE: Funcionamento normal.
wxSOCKET_NOWAIT: Lê ou grava o máximo possível de dados e retorna imediatamente.
wxSOCKET_WAITALL: Aguarda que todo o dado seja lido ou gravado ou que um erro ocorra para retornar.
wxSOCKET_BLOCK: Bloqueia a interface gráfica durante a operação.
wxSOCKET_REUSEADDR: Permite que o socket escute em uma porta que já está em uso (apenas para sockets de servidor).

Para configurar o socket para sempre aguardar que os dados sejam lidos ou gravados por completo antes de continuar a execução do código, use:

socket.SetFlags(wxSOCKET_WAITALL);


Outros métodos de configuração do socket serão vistos nos outros posts.

Nosso código de exemplo

Finalmente, o código do programa que irá fazer a conexão e enviar o e-mail. O código está bem documentado para que você possa entender cada parte do programa.

A interface gráfica já vai aparecer preenchida com os dados do servidor SMTP do GMail e meu endereço de e-mail de lá. Por favor, envie um e-mail para mim pelo programa de exemplo :) Obrigado.

/* A Casa de Just - http://jpjust.blogspot.com
* Curso de wxWidgets: Enviando e-mails via SMTP
*
* O objetivo deste código-fonte é demonstrar diversas classes
* ensinadas no curso de wxWidgets do blog "A Casa de Just".
*
* As aulas do curso de wxWidgets podem ser encontradas em forma
* de posts no blog: http://jpjust.blogspot.com
*
* Copyright (c) João Paulo Just <jpjust@justsoft.com.br>
* A Casa de Just - http://jpjust.blogspot.com
* 18 de março de 2007, 00:48, Ilhéus, BA, Brasil.
*/

#include <wx/wx.h>
#include <wx/socket.h>
#include <string.h>
#include <time.h>

// Tamanho do buffer que será utilizado no recebimento de mensagens
#define BUFFER 256

// Enumeração dos IDs
enum
{
ID_ENVIAR
};

// Classe: MailApp
class MailApp: public wxApp
{
public:
virtual bool OnInit();
};

// Classe: MailFrame
class MailFrame: public wxFrame
{
public:
MailFrame(void);

DECLARE_EVENT_TABLE()

private:
void EnviarMensagem(wxCommandEvent &event); // Método para enviar a mensagem
wxString Envia(wxSocketBase *socket, wxString msg); // Método para enviar dados pelo socket
wxString Le(wxSocketBase *socket); // Método para obter dados no socket

wxStaticText *lb_servidor;
wxStaticText *lb_porta;
wxStaticText *lb_de;
wxStaticText *lb_para;
wxStaticText *lb_assunto;

wxTextCtrl *txt_servidor;
wxTextCtrl *txt_porta;
wxTextCtrl *txt_de;
wxTextCtrl *txt_para;
wxTextCtrl *txt_assunto;
wxTextCtrl *txt_mensagem;
wxTextCtrl *txt_proto;

wxButton *btn_enviar;
};

// Tabela de eventos
BEGIN_EVENT_TABLE(MailFrame, wxFrame)
EVT_BUTTON(ID_ENVIAR, MailFrame::EnviarMensagem)
END_EVENT_TABLE()

// Método: MailApp::OnInit()
// Inicialização do programa
bool MailApp::OnInit()
{
MailFrame *frame = new MailFrame();
frame->Show();
return true;
}

// Método: MailFrame::MailFrame
// Construtor do frame
MailFrame::MailFrame(void)
:wxFrame(NULL, wxID_ANY, wxT("Enviar e-mail - http://jpjust.blogspot.com"))
{
//wxMessageBox(wxNow());

// Sizers
wxGridSizer *sizer_g = new wxGridSizer(5, 2, 0, 0);
wxBoxSizer *sizer_v = new wxBoxSizer(wxVERTICAL);

// Texto indicativo
lb_servidor = new wxStaticText(this, wxID_ANY, wxT("Servidor SMTP:"));
lb_porta = new wxStaticText(this, wxID_ANY, wxT("Porta:"));
lb_de = new wxStaticText(this, wxID_ANY, wxT("De:"));
lb_para = new wxStaticText(this, wxID_ANY, wxT("Para:"));
lb_assunto = new wxStaticText(this, wxID_ANY, wxT("Assunto:"));

// Caixas de texto
txt_servidor = new wxTextCtrl(this, wxID_ANY, wxT("gmail-smtp-in.l.google.com"), wxDefaultPosition, wxSize(200, -1));
txt_porta = new wxTextCtrl(this, wxID_ANY, wxT("25"));
txt_de = new wxTextCtrl(this, wxID_ANY, wxT("seu@email.com"), wxDefaultPosition, wxSize(200, -1));
txt_para = new wxTextCtrl(this, wxID_ANY, wxT("just1982@gmail.com"), wxDefaultPosition, wxSize(200, -1));
txt_assunto = new wxTextCtrl(this, wxID_ANY, wxT("Post 14"), wxDefaultPosition, wxSize(200, -1));
txt_mensagem = new wxTextCtrl(this, wxID_ANY, wxT("Eu li o post 14!"), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_WORDWRAP);
txt_proto = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_WORDWRAP | wxTE_READONLY);

// Botão de enviar
btn_enviar = new wxButton(this, ID_ENVIAR, wxT("Enviar"));

// Adiciona itens nos sizers
sizer_g->Add(lb_servidor, 0, wxALL, 5);
sizer_g->Add(txt_servidor, 0, wxALL, 5);
sizer_g->Add(lb_porta, 0, wxALL, 5);
sizer_g->Add(txt_porta, 0, wxALL, 5);
sizer_g->Add(lb_de, 0, wxALL, 5);
sizer_g->Add(txt_de, 0, wxALL, 5);
sizer_g->Add(lb_para, 0, wxALL, 5);
sizer_g->Add(txt_para, 0, wxALL, 5);
sizer_g->Add(lb_assunto, 0, wxALL, 5);
sizer_g->Add(txt_assunto, 0, wxALL, 5);

sizer_v->Add(sizer_g, 0, wxALL, 0);
sizer_v->Add(txt_mensagem, 2, wxALL | wxEXPAND, 5);
sizer_v->Add(txt_proto, 1, wxALL | wxEXPAND, 5);
sizer_v->Add(btn_enviar, 0, wxALL | wxALIGN_RIGHT, 5);

SetSizerAndFit(sizer_v);
}

/////////////////////////////////////////////////
// A partir daqui, a interface gráfica já está criada e
// veremos os métodos que realmente importam neste exemplo.

// Método: Envia
// Envia 'msg' para o socket e efetua a leitura logo em seguida, retornando o resultado
wxString MailFrame::Envia(wxSocketBase *socket, wxString msg)
{
// Se estiver desconectado, sai do método e retorna uma string vazia
if (socket->IsDisconnected())
return wxEmptyString;

wxString res;

// Envia 'msg' pelo socket
socket->Write((char *)msg.mb_str(), msg.Len());

// Em caso de erro, retorna uma mensagem avisando e fecha a conexão
if (socket->Error())
{
return wxT(">> Ocorreu um erro ao se comunicar com o servidor!\n");
socket->Close();
}

// Recebe a resposta enviada pelo outro host e a retorna
return msg + Le(socket);
}

// Método: Le
// Lê o conteúdo do socket (ou seja, qualquer mensagem enviada pelo outro host)
wxString MailFrame::Le(wxSocketBase *socket)
{
wxString res;
char buf[BUFFER]; // Buffer para recebimento

do
{
memset(buf, 0, BUFFER);
socket->Read(buf, BUFFER - 1); // Faz a leitura e armazena no buffer
res.Append(buf);
// Enquanto 'res' estiver vazio (nenhuma leitura foi feita ainda) ou
// enquanto houver dados para serem lidos, continuaremos percorrendo o laço
} while ((socket->LastCount() > 0) || (res.Len() == 0));

return res;
}

// Método: MailFrame::Envia
// Envia o e-mail
// Este método vai fazer a conexão, enviar os dados e fechar a conexão
void MailFrame::EnviarMensagem(wxCommandEvent &event)
{
wxIPV4address host;
wxSocketClient sock;
wxString msg, saida;

// Obtém a hora no formato requerido pela RFC822
// O formato é "Dia, data mês ano hora fuso"
// Ex.: Thu, 15 Mar 2007 20:19:00 BRT
char hora[50] = {0};
time_t now = time(NULL);
strftime(hora, 50, "%a, %d %b %Y %T %Z", localtime(&now));

// Configura o objeto 'host'
// Aqui definimos o endereço do servidor e a porta
host.Hostname(txt_servidor->GetValue());
host.Service(txt_porta->GetValue());

// Configuração do socket
// O timeout padrão para operações de E/S será de 120 segundos
// A flag 'wxSOCKET_NOWAIT' indica que operações de E/S irão retornar imediatamente
// (com esta flag, o programa não irá ficar parado em um Read() ou Write() do socket)
sock.SetTimeout(120);
sock.SetFlags(wxSOCKET_NOWAIT);
txt_proto->AppendText(wxT(">> Tentando se conectar...\n"));
if (sock.Connect(host) == false)
{
// Erro na conexão
txt_proto->AppendText(wxT(">> Ocorreu um erro ao tentar conectar no servidor!\n"));
return;
}
txt_proto->AppendText(wxT(">> Conectado!\n"));
txt_proto->AppendText(Le(&sock)); // Lê a mensagem de boas-vindas do servidor

// Neste bloco, enviamos uma identificação (EHLO), o remetente (MAIL From),
// o destinatário (RCPT To) e indicamos o início da mensagem (DATA)
txt_proto->AppendText(Envia(&sock, wxT("EHLO ") + wxGetFullHostName() + wxT("\r\n")));
txt_proto->AppendText(Envia(&sock, wxT("MAIL From: <") + txt_de->GetValue() + wxT(">\r\n")));
txt_proto->AppendText(Envia(&sock, wxT("RCPT To: <") + txt_para->GetValue() + wxT(">\r\n")));
txt_proto->AppendText(Envia(&sock, wxT("DATA\r\n")));

// Agora, o e-mail será montado. O corpo do e-mail tem o seguinte formato:
//
// From: "Nome do remetente" <email_do_remetente>
// To: "Nome do destinatário" <email_do_destinatário>
// Date: Data de envio (obedecendo a RFC822)
// Subject: Assunto do e-mail
//
// Mensagem, linha 1...
// Mensagem, linha 2...
//
// . (deve conter um ponto na última linha para indicar o fim da mensagem)
msg.Clear();
msg.Append(wxT("From: <") + txt_de->GetValue() + wxT(">\r\n"));
msg.Append(wxT("To: <") + txt_para->GetValue() + wxT(">\r\n"));
msg.Append(wxT("Date: ") + wxString(hora) + wxT("\r\n"));
msg.Append(wxT("Subject: ") + txt_assunto->GetValue() + wxT("\r\n\r\n"));
msg.Append(txt_mensagem->GetValue() + wxT("\r\n\r\n"));
msg.Append(wxT("--\r\nVisite A Casa de Just: http://jpjust.blogspot.com\r\n"));
msg.Append(wxT("\r\n.\r\n"));

// O corpo do e-mail é enviado pelo socket e a resposta do servidor é
// inserida na caixa de texto
txt_proto->AppendText(Envia(&sock, msg));

// Por último, fechamos a conexão com o comando QUIT
txt_proto->AppendText(Envia(&sock, wxT("QUIT\r\n")));

// Aguarda que a conexão seja fechada pelo servidor
if (sock.WaitForLost(120) == false)
{
txt_proto->AppendText(wxT(">> Erro ao enviar mensagem!\n"));
}
else
{
txt_proto->AppendText(wxT(">> Mensagem enviada!\n"));
}

// Pronto! O e-mail está enviado! :)
}

IMPLEMENT_APP(MailApp)


8 comentários:

Bruno Pio disse...

Olá! adorei suas dicas, mas se vc coloca-se num pdf ou mesmo um doc todas as dicas de uma vez seria perfeito! Abraços!

Anônimo disse...

Muito bom o seu curso Just. Quase não se acha material em português sobre wxWidgets, o que torna o seu curso um material muito importante.
Participo de um grupo de discussão sobre wx: http://br.groups.yahoo.com/group/wxwidgetsbr/
Já coloquei um link lá para o seu curso, mas gostaria que me desse permissão para colocar o seu material na sessão de arquivos do grupo, seria muito interessante.
Abraços,
Márcio Gil - Nova Friburgo - RJ

Saulo disse...

Just, realmente muito bom esse seu curso sobre wxWidgets. Só vim conhecer a wx e seu blog agora, mas estou achando muito bom!!!!
Um pedido, ou sugestão ... Será que você poderia falar um pouco sobre threads usando wx????

Abs

Anônimo disse...

Muito bom o curso mesmo. Agora uma sugestão podias fazer algo sobre wxListCtrl com multi colunas (multicolumn) pois é um bocado complicado funcionar com aquilo

Abraços

Anônimo disse...

Valeu just pelo curso, é bom pra quem tá começando e sente dificuldade com o livro. Estou estudando a biblioteca e contribuirei com material. Eu queria saber mais a respeito de sockets com protocolos. Você tem algum material que possa facilitar o estudo da biblioteca? Como se deve estudá-la?

Anônimo disse...

Valeu pelo tutorial, mas...

O exemplo não compila. Você faz uns casts ilegais e o GCC reclama.

Unknown disse...

Ola Prof. Just,

Estou tentando conectar meu programa em wxWidgets a uma base de dados MYSQL mas não estou tendo sucesso.
Cada vez que faço um registo no meu programa esse registo não fica gravado na BD. Será que me pode ajudar a perceber o que estou fazendo de errado?

Deixo aqui o código fonte que ainda não está concluído noutro post.

Obrigado.

Daniel Costa

Unknown disse...

#include
#include
#include
#include
#include
#include

enum
{
ID_BTN1_INSERIR,

};

class Janela : public wxApp
{
public : virtual bool OnInit(void);
};

class MinhaJanela : public wxFrame
{
public:
MinhaJanela(void);
void Button1(wxCommandEvent& event);


private:
wxButton *button1;

DECLARE_EVENT_TABLE()
};

bool Janela::OnInit(void)
{
MinhaJanela *frame= new MinhaJanela();
frame->Show();
SetTopWindow(frame);
return true;
}

BEGIN_EVENT_TABLE(MinhaJanela, wxFrame)
EVT_BUTTON(ID_BTN1_INSERIR, MinhaJanela::Button1)
END_EVENT_TABLE()

MinhaJanela::MinhaJanela(void)
:wxFrame(NULL, wxID_ANY, wxT("Janela"))
{
wxBoxSizer *sizer_v = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *sizer_h = new wxBoxSizer(wxHORIZONTAL);

button1 = new wxButton(this, ID_BTN1_INSERIR, wxT("INSERIR"));

sizer_h->Add(button1, 0, wxALL, 5);

sizer_v->Add(sizer_h, 0, wxALL | wxEXPAND, 5);

SetSizerAndFit(sizer_v);
}


void MinhaJanela:: Button1(wxCommandEvent& event)
{
static char* opt_host_name = "localhost";
static char* opt_user_name = "root";
static char* opt_password = "123";
static unsigned int opt_port_num = 3306;
static char* opt_socket_name = NULL;
static char* opt_db_name = "bd_angola";
static unsigned int opt_flags = 0;

MYSQL *conn;
MYSQL_RES *res;
MYSQL_ROW row;
MYSQL_FIELD *campos;


setlocale(LC_ALL, "Portuguese");

conn = mysql_init(NULL);

mysql_real_connect(conn, opt_host_name, opt_user_name, opt_password, opt_db_name, opt_port_num, opt_socket_name, opt_flags);

wxString cliente;
wxString proposta;
wxString adjudicado;
wxString sub_empreitada;
wxString SQL;

cliente=text1->GetValue();
proposta=text2->GetValue();
adjudicado=text3->GetValue();
sub_empreitada=text4->GetValue();

SQL="INSERT INTO `dados`(Cliente, Proposta, Adjudicado, Sub-Empreitada) VALUES ("+cliente+", "+proposta+", "+adjudicado+", "+sub_empreitada+");";

wxMessageBox(SQL);

wxMessageDialog *aMsg = new wxMessageDialog(this, wxT("Inserido com Sucesso"), wxT("Mensagem"), wxOK);
aMsg->ShowModal();
free(aMsg);



mysql_close(conn);

};

IMPLEMENT_APP(Janela)