28 de fev. de 2007

Como enviar email em C# usando o GMail?

Complementando meu post anterior sobre como enviar email usando C#, eis um exemplo interessante que encontrei uns tempos atrás (adaptado para facilitar o entendimento) mostrando como enviar email pelo GMail...

1 using System; 2 using System.Net; 3 using System.Net.Mail; 4 5 namespace GMailSample { 6 7 class SimpleSmtpSend { 8 9 static void Main(string[] args) { 10 11 SmtpClient cliente = new SmtpClient("smtp.gmail.com", 587 /* TLS */); 12 cliente.EnableSsl = true; 13 14 MailAddress remetente = new MailAddress( 15 "sua.conta@gmail.com", "Seu Nome"); 16 MailAddress destinatario = new MailAddress( 17 "fulano@mailinator.com", "Fulano"); 18 19 MailMessage mensagem = new MailMessage(remetente, destinatario); 20 21 mensagem.Body = "Exemplo de mensagem via GMail"; 22 mensagem.Subject = "Teste do Gmail com SSL e Credenciais"; 23 24 NetworkCredential credenciais = new NetworkCredential( 25 "sua.conta@gmail.com", /* login */ 26 "sua.senha.secreta", /* senha */ 27 ""); 28 29 cliente.Credentials = credenciais; 30 31 Console.WriteLine("Enviando..."); 32 33 try { 34 cliente.Send(mensagem); 35 Console.WriteLine("OK"); 36 } 37 catch (Exception e) { 38 Console.WriteLine("Exceção:" + e.ToString()); 39 } 40 } 41 } 42 }
Toda a comunicação com os servidores do GMail (tanto SMTP como POP3) é tunelada através de uma conexão criptografada.
Esse exemplo usa a porta 587 do GMail. Quem usa o Outlook Express para acessar o GMail pode verificar que o OE usa a porta 465 enquanto quem tem o Thunderbird usa a porta 587 (como o exemplo).
No caso do GMail, a porta 465 é usada para comunicação via SSL enquanto a porta 587 é via TLS. TLS é uma versão mais moderna do SSL; uma explicação mais detalhada sobre SSL/TLS você encontra na Wikipedia (em inglês) ou no Google.
FONTE: Mariya Atanasova's Blog



21 de fev. de 2007

Como enviar email?

O exemplo seguinte usa .NET 2.0 e pode ser usado em projetos WinForms ou ASP.NET. Ele mostra o envio de email usando um cliente SMTP.

1 using System;
2 using System.Net;
3 using System.Net.Mail;
4 
5 ...
6 // monta a mensagem
7 MailAddress to = new MailAddress("email-do-destinatario");
8 MailAddress from = new MailAddress("email-do-remetente");
9 MailMessage mensagem = new MailMessage(from, to);
10 mensagem.Subject = "Teste";
11 mensagem.Body = "Mensagem de teste";
12 
13 SmtpClient cliente = new SmtpClient("seu-servidor-SMTP");
14 
15 // dados para autenticação
16 cliente.Credentials =
17 new NetworkCredential("seu-username", "sua-senha-secreta");
18 
19 // envia a mensagem
20 try {
21 cliente.Send(mensagem);
22 }
23 catch (Exception ex) {
24 // trata a exceção
25 }
26 ...

Sobre o código:

  • As linhas 7 a 11 montam a mensagem.
  • A linha 13 cria o cliente SMTP
  • A linha 16 define as credenciais (nome de usuário e senha) para a autenticação no servidor SMTP
  • A linha 21 faz o envio da mensagem
  • Na linha 24 deve ir o código para tratamento de eventuais erros no envio.

Pontos importantes:

  • Nem todo servidor SMTP requer autenticação: nesse caso, as linhas 16 e 17 devem ser omitidas.
  • Alguns servidores SMTP exigem que se conecte antes ao servidor POP3. Nesse caso, é preciso usar um cliente POP3, o que, infelizmente, o framework não disponibiliza; será preciso usar uma biblioteca de terceiros (assunto para um futuro tópico).
  • Este exemplo não funciona com o GMail e outros servidores que usam SSL.

Tópico atualizado em 09/05/2007: pequena correção na criação da mensagem.

18 de fev. de 2007

Como determinar onde gerou uma exceção?

Uma das coisas mais desagradáveis é testar um programa, instalar ele no cliente e ele cair com uma exceção no meio da demonstração. Na máquina de desenvolvimento, a IDE gentilmente mostra onde a exceção foi gerada. Mas no cliente, não.

Na JCL (Jedi Code Library) existe uma unit chamada JclDebug que deve ser usada em conjunto com o mecanismo do JCL para tratamento de exceções não tratadas. Esse mecanismo consiste basicamente em determinar onde a exceção foi gerada (em qual linha, função/procedure e unit) bem como gerar um dump da pilha do programa, o que permite ter uma idéia da sequência de processamento que resultou na exceção.

A documentação da JCL não é muito clara sobre como usar esse mecanismo. O problema maior é saber como preparar o executável para poder usar o mecanismo. Esta é a "receita do bolo":

  1. Na instalação do JCL, marque a opção IDE experts|Debug extension e marque a opção Sample exception dialogs.
  2. De volta a IDE, abra seu projeto
  3. File|New|Other...|Dialogs
  4. Dê um clique duplo no ícone do Exception Dialog
  5. Salve seu projeto dando um File|Save all
  6. Marque a opção Project|Insert JCL Debug Data
  7. Dê um Project|Build (um dialog box adicional do JCL irá aparecer se o build for bem sucedido)
Se você optar por não utilizar a janela de exceção do JCL, não precisa fazer os passos 3-4-5. Entretanto, vale a pena testar pelo menos uma vez essa caixa de diálogo e dar uma olhada como o relatório de erro é criado.
  • Para isto funcionar, nunca compacte o executável (com compactadores do tipo UPX). Os compactadores de executável costumam alterar os endereços de carga dos módulos, de modo que o endereço de uma função/procedure quando o executável é gerado fica diferente do endereço quando o executável é executado. Resultado: as informações capturadas na exceção estão todas erradas!
  • Exceções que ocorrem muito cedo (na inicialização das units) ou muito tarde (na finalização das units) costumam não ser capturadas por qualquer mecanismo de captura de exceções que você imaginar; para evitar isso, vá no fonte do seu DPR e garanta que a unit que inicializa seu mecanismo de captura de exceções seja a primeita unit da lista (difícil de fazer, especialmente se você usa a unit ShareMem).
  • Se você desabilitar a opção Project/Insert JCL Debug Data, terá de gerar um mapa completo de linkagem do executável e distribuir esse arquivo (.MAP) junto com seu programa. Mas é muito mais cômodo deixar a opção habilitada.
  • Se quiser um dump da pilha extremamento detalhado, vá em Project| Options, aba Compiler e marque a opção Use debug DCUs

Links relacionados

14 de fev. de 2007

MSDN Magazine Março/2007

A MSDN Magazine de Março  já está disponível no site da Microsoft. Destaque para o IIS 7.0 e o MS Build.

13 de fev. de 2007

Como fazer busca incremental num combobox?

Este exemplo mostra como fazer uma busca incremental num combobox.

1 procedure TSeuForm.SeuComboBoxKeyPress(Sender: TObject; var AKey: Char); 2 var 3 ComboBox: TComboBox; 4 TextoDigitado: string; 5 TextoSelecionado: string; 6 TextoDoItem: string; 7 Texto: string; 8 Achou: Boolean; 9 Inicio: Integer; 10 I: Integer; 11 begin 12 ComboBox := Sender as TComboBox; 13 14 // recupera o que foi digitado 15 16 Inicio := ComboBox.SelStart; 17 18 // trata a tecla 19 TextoDigitado := Copy(ComboBox.Text, 1, ComboBox.SelStart); 20 if AKey = Chr(VK_BACK) then begin 21 22 if ComboBox.SelLength = 0 then begin 23 24 Delete(TextoDigitado, Length(TextoDigitado), 1); 25 end; 26 end 27 else begin 28 TextoDigitado := TextoDigitado + AKey; 29 end; 30 31 TextoSelecionado := Copy(ComboBox.Text, 32 ComboBox.SelLength + ComboBox.SelStart + 1, MaxInt); 33 34 Texto := TextoDigitado + TextoSelecionado; 35 if Texto <> '' then begin 36 37 // atualiza a posição de início da seleção 38 if AKey = Chr(VK_BACK) then begin 39 40 if Inicio > 0 then begin 41 42 Dec(Inicio); 43 end; 44 end 45 else if AKey <> Chr(VK_BACK) then begin 46 47 Inc(Inicio); 48 end; 49 50 // descarta a tecla 51 AKey := #0; 52 53 if Inicio = 0 then begin 54 55 ComboBox.Text := ''; 56 ComboBox.ItemIndex := -1; 57 end 58 else begin 59 60 // busca o texto 61 62 Achou := False; 63 for I := 0 to Pred(ComboBox.Items.Count) do begin 64 65 TextoDoItem := Copy(ComboBox.Items[I], 1, Length(Texto)); 66 Achou := AnsiCompareText(TextoDoItem, Texto) = 0; 67 if Achou then begin 68 69 ComboBox.Text := ComboBox.Items[I]; 70 ComboBox.ItemIndex := I; 71 72 Break; 73 end; 74 end; 75 76 if Achou then begin 77 78 // destaca a parte não digitada da string 79 ComboBox.SelStart := Inicio; 80 ComboBox.SelLength := Length(ComboBox.Text) - Inicio; 81 end 82 else begin 83 84 // beepa para avisar que não encontrou 85 Beep; 86 end; 87 end; 88 end; 89 end;

8 de fev. de 2007

Como medir a memória utilizada por um aplicativo Delphi?

Com frequência vejo pessoas tentando medir o consumo de memória de um programa feito em Delphi usando o Gerenciador de Tarefas do Windows. Por motivos técnicos, essa não é a melhor forma de medir o consumo de memória do programa.

A grosso modo, o Gerenciador de Tarefas mede o máximo de memória seu programa alocou, não o quanto ele está usando; para piorar a memória que é liberada de volta para o Windows mas que ainda não foi utilizada por outro aplicativo é contabilizada nesse total pois essa memória disponível é considerada pelo Windows como disponível para uso pelo seu programa, embora ela possa ser usada por qualquer programa.

Gerenciador de Memória

Um dos motivos que a informação do Gerenciador de Tarefas estar errada é que um programa em Delphi aloca indiretamente memória do Windows, através de um gerenciador de memória do runtime do Delphi.

Quando o programa solicita memória para o gerenciador, ele aloca blocos de memória para o programa. Os blocos tem tamanho fixo de modo que para cada pedido de memória o gerenciador aloca a quantidade necessária de blocos contínuos para satisfazer o pedido do programa. Logo, um bloco é a menor quantidade de bytes que o gerenciador aloca. Por exemplo, imagine que cada bloco tem 32 bytes; se o programa pede 4 bytes, o gerenciador irá alocar um bloco; se o programa pedir 40 bytes, o gerenciador irá alocar dois blocos.

Pode parecer desperdício, mas isso é feito para evitar a fragmentação da memória. A fragmentação ocorre quando, apesar de haver memória livre para atender a um pedido de memória, não existem blocos contínuos suficientes para atender à solicitação. Caso o gerenciador fique sem blocos disponíveis para atender uma solicitação do programa, ele irá solicitar mais memória ao Windows.

Quando o programa devolve memória para o gerenciador, ele não devolve totalmente ao Windows a memória que você alocou porque ele imagina que o programa vai requisitar novamente mais memória. Isso é feito para otimizar os tempos de alocação de memória.

Vamos imaginar que inicialmente o programa alocou 100 blocos de memória do gerenciador. O programa cria algum form e precisa de mais 20 blocos de memória; o gerenciador irá providenciar 20 blocos de memória do Windows para poder atender a solicitação; nesse momento, o programa tem alocado 120 blocos de memória.

Quando o form é destruído e a memória devolvida ao gerenciador, ele irá marcar os 20 blocos de memória que foram usados pelo form como sendo disponíveis. O programa continua usando 120 blocos. Imagine que o programa crie outro form que precise de 10 blocos; como tem 20 disponíveis, o gerenciador não pede memória para o Windows, ele usa 10 dos 20 disponíveis.

Delphi 2006 e FastMM

Até o Delphi 2005, todas as versões do Delphi utilizaram o mesmo gerenciador de memória (eventualmente, com pequenas alterações). Mas o modo como é implementado o gerenciador de memória sempre permitiu o surgimento de ferramentas de terceiros para depurar a alocação de memória (localizar vazamentos/leaks, sobrescrita, etc) e até mesmo gerenciadores de memória alternativos.

Um desses gerenciadores de memória alternativos é o FastMM. Esse gerenciador é um dos mais rápidos e baseia-se na idéia de que um programa costuma alocar com mais frequencia pequenas quantidades de memória do que gransdes quantidades. Partindo dessa idéia, o FastMM usa três diferentes estratégias de gerenciamento de memória, dependendo da quantidade de memória que o programa pede. O resultado é que programas que criam/destroem muitos objetos tem desempenho melhor com o FastMM.

A Borland reconheceu a qualidade do FastMM e adotou o FastMM como gerenciador padrão de memória do Delphi 2006. Para quem não tem o Delphi 2006 mas gostaria de experimentar, visite a página do FastMM no SourceForge.net (baixe a versão 4.x mais recente, as versões antigas 2.x e 3.x estão lá apenas para quem mantem programas que usam essas versões)

Medir a memória utilizada

Para quem usa Delphi 2005 ou anterior é simples saber quantos blocos o gerenciador de memória pegou do Windows e quanto de memória seu programa está consumindo através do gerenciador de memória:

1 var
2   Blocos: Integer;
3   Bytes: Integer;
4 ...
5   Blocos := AllocMemCount;
6   Bytes := AllocMemSize;

Para quem usa Delphi 2006 ou usa o FastMM ao invés do gerenciador de memória padrão, o cálculo é um pouco mais trabalhoso:

1 var 2 Estado: TMemoryManagerState; 3 Blocos: Integer; 4 Bytes: Integer; 5 I: Integer; 6 ... 7 Blocos := 0; 8 Bytes := 0; 9 10 GetMemoryManagerState(Estado); 11 12 for I := 0 to High(Estado.SmallBlockTypeStates) do begin 13 14 Inc(Blocos, Estado.SmallBlockTypeStates[I].AllocatedBlockCount); 15 Inc(Bytes, Estado.SmallBlockTypeStates[I].AllocatedBlockCount 16 * Estado.SmallBlockTypeStates[I].UseableBlockSize); 17 end; 18 19 Inc(Blocos, Estado.AllocatedMediumBlockCount); 20 Inc(Bytes, Estado.TotalAllocatedMediumBlockSize); 21 22 Inc(Blocos, Estado.AllocatedLargeBlockCount); 23 Inc(Bytes, Estado.TotalAllocatedLargeBlockSize); 24 ...

Quem estiver usando o FastMM, deve incluir no uses da unit que contem esse código a unit FastMM4 (ou FastMM3, se for o caso).

Uma diferença do FastMM para o gerenciador de memória usado até o Delphi 2005 é que o FastMM tem diferentes estratégias de gerenciamento da memória conforme o tamanho do bloco de memória a ser alocado. Por isso, o código para calcular o consumo de memória é um pouco mais complicado.

Em ambos os casos, Blocos é o total de blocos de memória alocados pelo gerenciador e Bytes é a quantidade de memória que o programa está usando.

Links relacionados

7 de fev. de 2007

Como desabilitar o ghosting do Windows?

Desde o Windows 2000 o Windows é capaz de detectar aplicações que estejam congeladas/travadas. Essa detecção se baseia na idéia de que um aplicativo está congelado/travado se...

  • não está inicializando E
  • não está esperando por entrada de dados do usuário E
  • está a cinco segundos sem processar a fila de mensagens (via PeekMessage)

É graças a essa capacidade que o Windows consegue dar o aviso de que "tal aplicativo não está respondendo".

O Windows XP SP1 adicionou a capacidade de ghosting: ao detectar que um aplicativo não está respondendo (está congelado/travado), o Windows cria uma janela fantasma na mesma posição, com o mesmo tamanho e os mesmos atributos da janela do aplicativo que não está respondendo. Isso permite ao usuário minimizar, maximizar e mover a janela ou até mesmo fechar o aplicativo.

Caso o aplicativo volte a responder, o Windows restaura a janela do aplicativo na mesma posição, com o mesmo tamanho e os mesmos atributos da janela fantasma que foi criada enquanto o aplicativo não respondia.

Sem a janela fantasma, o que o usuário vê na maior parte dos casos é uma janela em branco ou contendo partes de outras janelas que eventualmente tenham sidos postas sobre a janela do aplicativo que não está respondendo. Teoricamente, a janela fantasma dá ao usuário a impressão que o aplicativo está funcionando "normalmente".

Infelizmente, no caso do Delphi, isso causa problemas com forms exibidos usando ShowModal quando o Windows restaura a janela do aplicativo. O form mostrado com ShowModal pode ficar atrás do form principal do aplicativo.

Se estiver tendo esse tipo de problema, inclua a procedure seguinte na unit do seu form principal (nem precisa ser método do form) e execute-a no evento OnCreate do form principal:

1 procedure DesabilitarGhostingDoWindows; 2 var 3 User32: HMODULE; 4 DisableProcessWindowsGhosting: TProcedure; 5 begin 6 User32 := GetModuleHandle('USER32'); 7 if User32 <> 0 then begin 8 DisableProcessWindowsGhosting := 9 GetProcAddress(User32, 'DisableProcessWindowsGhosting'); 10 if Assigned(DisableProcessWindowsGhosting) then begin 11 DisableProcessWindowsGhosting; 12 end; 13 end; 14 end;

Tópicos relacionados no MSDN...

6 de fev. de 2007

Como percorrer as pastas do sistema de arquivos?

Este exemplo mostra como percorrer as pastas do sistema de arquivos:

1 using System; 2 using System.IO; 3 4 public class Exemplo { 5 6 public static void PercorrerPasta(string oNomeDaPasta) { 7 8 DirectoryInfo dir = new DirectoryInfo(oNomeDaPasta); 9 10 // processa eventuais subdiretórios 11 string[] subdirs = dir.GetDirectories(oNomeDaPasta); 12 foreach (string subdir in subdirs) { 13 14 PercorrerPasta(subdir); 15 } 16 17 FileInfo[] files = dir.GetFiles(); 18 foreach (FileInfo file in files) { 19 20 // processa o arquivo encontrado 21 } 22 } 23 24 [STAThread] 25 public static void Main() { 26 PercorrerPasta('c:\'); 27 } 28 }

1 de fev. de 2007

Floating Point Overflow ao Imprimir com Delphi

Existem relatos frequentes sobre a ocorrência de erros de floating point overflow ao imprimir a partir de programas feitos em Delphi. Algumas pessoas chegam a afirmar que existe incompatibilidade entre Delphi e determinada marca de impressora.

Esse problema resulta de uma "incompatibilidade" no modo como o Delphi trata a FPU e os outros compiladores. O padrão do Delphi gera exceção no caso de problemas em cálculos envolvendo ponto flutuante enquanto os outros compiladores ignoram esses problemas (os cálculos resultam em NaN, INF, etc).

Quando se faz uma impressão, o programa em Delphi precisa usar uma DLL fornecida pelo fabricante da impressora (o driver da impressora). Embora drivers de impressão possam ser feitos em Delphi, é pouco provável que essa DLL tenha sido feita em Delphi. O resultado é que a DLL feita assumindo que erros envolvendo ponto flutuante podem ser ignorados é executada por um programa que assume o oposto. Assim, erros que normalmente a DLL ignoraria passam a não ser mais ignorados.

Se estiver fazendo impressão usando o objeto Printer, a solução seria algo assim...

1 var 2 Sav8087CW: Word; 3 ... 4 Sav8087CW := Default8087CW; 5 Set8087CW(0x133F); 6 try 7 Printer.EndDoc; 8 finally 9 Set8087CW(Sav8087CW); 10 end; 11 ...

Note que o driver da impressora geralmente só é usado quando TPrinter.EndDoc é executado. Não sei dizer se esse comportamento pode ser alterado por alguma configuração do Windows.

21 de jan. de 2007

Como criar emails em formato HTML usando Indy 9?

Exemplo de envio de email em formato HTML usando Delphi e Indy 9:

 1 var
 2   Email: TIdMessage;
 3   Texto: TIdText;
 4   Html: TIdText;
 5   Anexo: TIdAttachment;
 6 begin
 7   Email := TIdMessage.Create(nil);
 8   try
 9     // ...ajusta remetente, destinatário, etc...
10 
11     // define o tipo do conteúdo da mensagem
12     Email.ContentType := 'multipart/mixed';
13 
14     // cria a parte texto - pode estar em branco
15     Texto := TIdText.Create(Email.MessageParts);
16     Texto.Body.Text :=
17       'This message contains HTML and images.';
18     Texto.ContentType := 'text/plain';
19 
20     // cria a parte HTML
21     Html := TIdText.Create(Email.MessageParts);
22     Html.Body.Text := '<html><body>'
23       + 'Mensagem de <b>Teste</b><br />'
24       + '<img src="cid:imagem.jpg" />'
25       + '</body></html>';
26     Html.ContentType := 'text/html';
27 
28     // inclui no email a imagem usada na parte HTML
29     Anexo := TIdAttachment.Create(
30       Email.MessageParts, 'c:\pasta\imagem.jpg');
31     Anexo.ContentType := 'image/jpg';
32     Anexo.Headers.Add('Content-ID: <imagem.jpg>');
33 
34     // ... envia o email...
35   finally
36     Email.Free;
37   end;
38 end;
Pontos importantes:
  • A parte em formato text/plain deve estar presente embora não precise conter texto. Existe um bug no Indy que, na ausência dessa parte, faz com que o email seja gerado vazio.
  • Para ter imagens embutidas no email, inclua o header Content-ID e use na tag img o modificador cid: ao especificar a origem da imagem.

12 de jan. de 2007

Como enviar emails usando Indy 9?

Exemplo básico de envio de email usando Delphi e Indy 9:

1 var 2 Email: TIdMessage; 3 begin 4 // SMTP é um TIdSMTP 5 // POP3 é um TIdPOP3; 6 7 Email := TIdMessage.Create(nil); 8 try 9 10 // ...ajusta remetente, destinatário, etc da mensagem... 11 12 POP3.Host := 'seu-servidor-pop3'; 13 POP3.Username := 'seu-username'; 14 POP3.Password := 'sua-senha-secreta'; 15 16 POP3.Connect; 17 18 try 19 SMTP.Host := 'seu-servidor-smtp'; 20 SMTP.AuthType := atLogin; 21 SMTP.Username := 'seu-username'; 22 SMTP.Password := 'sua-senha-secreta'; 23 24 SMTP.Connect; 25 try 26 SMTP.Send(Email); 27 except 28 on E: Exception do begin 29 // envio falhou 30 end; 31 end; 32 SMTP.Disconnect; 33 finally 34 POP3.Disconnect; 35 end; 36 finally 37 Email.Free; 38 end; 39 end;

Pontos importantes:
  • Nem todo servidor SMTP requer autenticação: nesse caso, TIdSMTP.AuthType, TIdSMTP.Username e TIdSMTP.Password podem ser omitidos.
  • Nem todo servidor SMTP exige que antes se conecte ao servidor POP3: nesse caso, tudo que é relacionado com o servidor POP3 pode ser omitido. Note que quando isso é necessário, deve se permanecer conectado ao servidor POP3 enquanto estiver usando o servidor SMTP.
  • Permamenecer conectado aos servidores POP3 e SMTP o mínimo de tempo possível. A maioria desses servidores irá desconectar o cliente por inatividade ou por tempo.
  • Este exemplo não funciona com o GMail e outros servidores que usam SSL

5 de mai. de 2005

Qual a porta da impressora?

No fórum ClubeDelphi apareceu uma dúvida hoje sobre como determinar em qual porta está ligada uma impressora. Não tem um jeito fácil de determinar isso...

1 uses 2 Printers, 3 WinSpool, 4 ... 5 6 function GetCurrentPrinterHandle: THandle; 7 var 8 Device, Driver, Port: array[0..255] of Char; 9 Mode: THandle; 10 begin 11 Printer.GetPrinter(Device, Driver, Port, Mode); 12 if not OpenPrinter(@Device, Result, nil) then 13 RaiseLastWin32Error; 14 end; 15 16 function GetCurrentPrinterPort: string; 17 var 18 Buffer: array of Char; 19 Size: DWORD; 20 H: THandle; 21 begin 22 H := GetCurrentPrinterHandle; 23 try 24 GetPrinter(H, 2, nil, 0, @Size); 25 SetLength(Buffer, Size); 26 try 27 GetPrinter(H, 2, @Buffer[0], Size, @Size); 28 Result := PPrinterInfo2(@Buffer[0])^.pPortName; 29 finally 30 Buffer := nil; 31 end; 32 finally 33 ClosePrinter(H); 34 end; 35 end;

A função GetCurrentPrinterPort retorna o nome da porta onde a impressora corrente está conectada. Por exemplo, se a impressora corrente estiver conectada na porta LPT1, GetCurrentPrinterPort retorna 'LPT1:'.

23 de mar. de 2005

Lista dos erros de I/O

Esta é uma lista dos códigos de erro de I/O gerados pelo Delphi (obtida desta página):

  1  Invalid function number
  2  File not found
  3  Path not found
  4  Too many open files
  5  File access denied
  6  Invalid file handle
 12  Invalid file access code
 15  Invalid drive number
 16  Cannot remove current directory
 17  Cannot rename across drives
 18  No more files
100  Disk read error
101  Disk write error
102  File not assigned
103  File not open
104  File not open for input
105  File not open for output
106  Invalid numeric format
150  Disk is write-protected
151  Bad drive request struct length
152  Drive not ready
154  CRC error in data
156  Disk seek error
157  Unknown media type
158  Sector Not Found
159  Printer out of paper
160  Device write fault
161  Device read fault
162  Hardware failure
200  Division by zero
201  Range check error
202  Stack overflow error
203  Heap overflow error
204  Invalid pointer operation
205  Floating point overflow
206  Floating point underflow
207  Invalid floating point operation
208  Overlay manager not installed
209  Overlay file read error
210  Object not initialized
211  Call to abstract method
212  Stream registration error
213  Collection index out of range
214  Collection overflow error
215  Arithmetic overflow error
216  General Protection Fault

10 de fev. de 2005

Delphi Ano X

Allen Bauer, o responsável pela IDE do Delphi, postou em seu blog uma nota sobre o aniversário de 10 anos do Delphi. Inclusive, tem uma foto do CD com a versão pré-release do Delphi. O lançamento oficial do Delphi foi em 14 de Fevereiro de 1995, no evento Software Development organizado pela revista de mesmo nome. Lembro que na época eu estava avaliando o Visual Basic e que meu primeiro contato com o Delphi foi através de um artigo da revista Microsoft Systems Journal (hoje MSDN Magazine) de junho de 1995. Depois de ler aquele artigo, abandonei o VB e providenciei a compra do Delphi para a empresa onde trabalhava na época.

3 de fev. de 2005

Como impedir a movimentação do form MDI filho?

Do fórum ClubeDelphi:

usando o código mostrado no post sobre ocultar as barras de rolagem em aplicativo MDI... tem como impedir a movimentação do form MDI filho para fora da área do MDI pai?

Pesquisei o NG da Borland e encontrei esta thread com o seguinte código:

1 type 2 TForm2 = class... 3 private 4 procedure WMWindowPosChanging(var Msg: TWMWindowPosChanging); 5 message WM_WINDOWPOSCHANGING; 6 ... 7 8 procedure TForm2.WMWindowPosChanging(var Msg: TWMWindowPosChanging); 9 var 10 R: TRect; 11 begin 12 with Msg.WindowPos^ do begin 13 14 if (flags and SWP_NOMOVE) = 0 then begin 15 16 Windows.GetClientRect(Application.MainForm.ClientHandle, R); 17 18 if x < 0 then 19 x := 0; 20 21 if y < 0 then 22 y := 0; 23 24 if (x + cx) > R.Right then 25 x := R.Right - cx; 26 27 if (y + cy) > R.Bottom then 28 y := R.Bottom - cy; 29 end; 30 end; 31 32 inherited; 33 end;

Como ocultar as barras de rolagem em um aplicativo MDI?

No fórum ClubeDelphi apareceu esta rotina para ocultar as barras de rolagem de um form MDI pai:

1 type 2 TForm1 = class(TForm) 3 procedure FormCreate(Sender: TObject); 4 ... 5 6 implementation 7 8 ... 9 10 function ClientWindowProc(wnd: HWND; Msg: Cardinal; 11 wParam, lParam: Integer): Integer; stdcall; 12 var 13 f: Pointer; 14 begin 15 f := Pointer(GetWindowLong(wnd, GWL_USERDATA)); 16 17 case Msg of 18 WM_NCCALCSIZE: begin 19 20 if (GetWindowLong(wnd, GWL_STYLE) 21 and (WS_HSCROLL or WS_VSCROLL)) <> 0 then 22 23 SetWindowLong(wnd, GWL_STYLE, 24 GetWindowLong(wnd, GWL_STYLE) and 25 not (WS_HSCROLL or WS_VSCROLL)); 26 end; 27 end; 28 29 Result := 30 CallWindowProc(f, wnd, Msg, wparam, lparam); 31 end; 32 33 procedure TForm1.FormCreate(Sender: TObject); 34 begin 35 if ClientHandle <> 0 then begin 36 37 if (not (GetWindowLong(ClientHandle, 38 GWL_USERDATA) <> 0)) then begin 39 40 SetWindowLong(ClientHandle, GWL_USERDATA, 41 SetWindowLong(ClientHandle, GWL_WNDPROC, 42 Integer(@ClientWindowProc))); 43 end; 44 end; 45 46 // ... 47 end;

Como mostrar propriedades/eventos de forms personalizados no Object Inspector?

Do fórum ClubeDelphi:

... criei um form base para rotinas genéricas de cadastramento. O que eu preciso agora é definir algumas propriedades neste form base, que apareçam no Object Inspector, para que eu possa configurar algumas coisas. Porém não estou conseguindo isso. Já tentei criar uma propriedade na sessão published no form base, mas a propriedade não aparece nos forms herdeiros. ... como posso fazer isso?

Pesquisei o NG da Borland e encontrei esta thread que explica como fazer isso. Com base no que foi discutido lá:

  1. Inclua sua propriedade/evento personalizada na seção published do form.
  2. Adicione seu form personalizado ao Object Repository (clique com o botão direito no form e execute Add to repository)
  3. Inclua seu form personalizado em um design-time package (um bom candidato é o package dclusrXx, mas pode ser qualquer design time package)
  4. Inclua no fonte do package o seguinte:
    1 interface 2 ... 3 4 procedure Register; 5 6 implementation 7 8 uses 9 dsgnIntf; 10 // ou DesignIntf se D6+, inclua DesignIDE na 11 // seção requires do package 12 ... 13 14 procedure Register; 15 begin 16 // TMyForm é o form personalizado 17 RegisterCustomModule(TMyForm, TCustomModule); 18 end;
  5. Crie um form derivado do seu form personalizado