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

Limitações dos aplicativos Delphi no Windows 9x

Do fórum ClubeDelphi:

Aqui na empresa temos um sistema que atualmente está com 352 forms. O problema é que o programa roda no WinXP e Win2K, mas não roda em Win98. ... As mensagens [de erro] do Windows que aparecem [ao tentar executar o programa] são:
Impossivel executar C:\Programa\Programa.exe
e
Não há memória suficiente para iniciar C:\Programa\Programa.exe. Encerre alguns programas e tente novamente
Porém, se eu retirar dois forms ESPECÍFICOS o programa roda normalmente. Fazendo a depuração no Win98, nem chegar a executar a primeira linha do projeto. E, mais estranho ainda, é que se eu alterar o seu nome de um desses forms o programa roda; se eu voltar o nome original do form, não roda.

Pesquisei o NG da Borland e encontrei esta thread sobre esse problema; em particular, a mensagem 10 dessa thread explica porque mudar o nome do form resolve o problema.

Os pontos importantes dessa thread:

  • No Win98 existe um limite na quantidade e no tamanho total de forms contidos em um arquivo executável (mais precisamente, o limite se aplica aos recursos/resources nomeados contidos no executável). Esse limite não existe no WinNT/Win2K/WinXP.
  • A solução seria dividir o sistema em DLLs/BPLs e assim driblar o limite por arquivo imposto pelo Win98.

1 de fev. de 2005

Microsoft Ano XXX

Hoje completa 30 anos que a Microsoft, então uma empresa fundo de quintal formada pela dupla Gates/Allen, vendeu seu primeiro produto: um BASIC para o microcomputador Altair. Uma pequena história da Microsoft está aqui.

31 de jan. de 2005

Como evitar que a barra de tarefas oculte parte do form maximizado?

Do fórum ClubeDelphi:

Quando deixo o meu form principal com a propriedade WindowState com o valor wsMaximized, a parte inferior do form fica oculta pela barra de tarefas. O form não respeita o espaço livre da tela.

Como o problema não pode ser reproduzido, ao longo da discussão apareceu o causador do problema:

S[e] não me engano vc não pode desabilitar os botões de maximizar do form principal. Tenta lá... na opção BorderIcons deixa True para biMaximize.

Puxa... não ter o botão Maximizar e iniciar com o form maximizado causa o problema... Testei o tratamento da mensagem WM_GETMINMAXINFO e isso resolve o problema:

1 type 2 TSeuForm = class(... 3 private 4 procedure WMGetMinmaxInfo(var Msg: TWMGetMinmaxInfo); message WM_GETMINMAXINFO; 5 ... 6 7 procedure TSeuForm.WMGetMinmaxInfo(var Msg: TWMGetMinmaxInfo); 8 var 9 R: TRect; 10 begin 11 inherited; 12 13 // Obtem o retangulo com a area livre do desktop 14 SystemParametersInfo(SPI_GETWORKAREA, SizeOf(R), @R, 0); 15 16 Msg.MinMaxInfo^.ptMaxPosition := R.TopLeft; 17 OffsetRect(R, -R.Left, -R.Top); 18 Msg.MinMaxInfo^.ptMaxSize := R.BottomRight; 19 end;

Qual é o tamanho da barra de tarefas?

Do fórum ClubeDelphi:

Como conseguir a altura e a posição da barra de tarefas?

O código seguinte obtem o retângulo que contem a barra de tarefas do Windows:

1 var 2 TaskBarH: THandle; 3 TaskBarR: TRect; 4 ... 5 // obtem o retangulo com o taskbar 6 TaskBarH := FindWindow('Shell_TrayWnd', nil); 7 GetWindowRect(TaskBarH, TaskBarR); 8 9 // altura do taskbar = TaskBarR.Bottom - TaskBarR.Top

Como fazer alinhamento no DBGrid?

Do fórum ClubeDelphi:

Gostaria de mudar a cor da fonte de um valor negativo no meu DBGrid... a rotina eu já tenho.. só que minha coluna são todas centralizadas e quando coloco minha rotina, o registro que tem valor negativo fica todo desalinhado

Você está usando a procedure TCanvas.TextOut que não alinha texto... use DrawText da API do Windows para alinhar o texto dentro de um retângulo:

1 procedure TForm1.DBGridDrawColumnCell(Sender: TObject; const Rect: TRect; 2 DataCol: Integer; Column: TColumn; State: TGridDrawState); 3 var 4 S: string; 5 R: TRect; 6 begin 7 if Column.Field.AsDouble < 0 then begin 8 9 with DBGrid.Canvas do begin 10 11 Font.Color := clRed; 12 FillRect(Rect); 13 S := Column.Field.AsString; 14 R := Rect; 15 DrawText(Handle, PChar(S), Length(S), R, DT_CENTER or DT_VCENTER); 16 end; 17 end; 18 end;

No mesmo evento OnDrawColumnCell, você pode tratar individualmente cada coluna conforme o valor de DataCol ou de Column.

Para ter diferentes alinhamentos, basta mudar o último parametro de DrawText conforme sua necessidade; DrawText alinha tanto na horizontal (esquerda, centro, direita) como na vertical (topo, centro, fundo) além de fazer algumas outras mágicas... veja mais detalhes neste artigo no MSDN.

Como clonar um objeto?

Do fórum ClubeDelphi:

Como faço para clonar um objeto?

Você pode incluir seu próprio método de clonagem na sua classe:

1 type 2 TEstado = class 3 valor1: byte; 4 valor2: byte; 5 public 6 function Clone: TEstado; 7 end; 8 9 ... 10 11 function TEstado.Clone: TEstado; 12 begin 13 Result := TEstado.Create; 14 Result.valor1 := Self.valor1; 15 Result.valor2 := Self.valor2; 16 end;

O esquema usual de clonagem de objetos costuma se basear na idéia usada em TPersistent.Assign. Para um exemplo, dê uma olhada nesta discussão no NG da Borland.

Quais são as unidades de disco rígido da máquina?

Do fórum ClubeDelphi:

Como faço para saber quais são todas as unidades de disco rígido da máquina que o aplicativo está sendo executado?

Basta combinar as funções GetLogicalDrives e GetDriveType da API do Windows:

1 function ListaHDs: string; 2 var 3 Drives: DWord; 4 Mascara: DWord; 5 S: String; 6 I: Integer; 7 begin 8 Result := ''; 9 10 S := 'A:\'; 11 12 Drives := GetLogicalDrives; 13 14 Mascara := 1; 15 for I:= 1 to 32 do begin 16 17 if (Mascara and Drives) <> 0 then begin 18 19 if GetDriveType(PChar(S)) = DRIVE_FIXED then begin 20 21 Result := Result + S[1]; 22 end; 23 end; 24 25 Mascara := Mascara shl 1; 26 Inc(S[1]); 27 end; 28 end;

Se você tiver apenas um HD (normalmente o drive C), ListaHDs retorna 'C'; se você tiver dois HDs (ou duas partições num mesmo HD), retorna 'CD'.

Como ter somente um determinado componente por form?

Do fórum ClubeDelphi:

Como faço para que só possa existir um componente de determinado tipo em cada form?

Imaginando que esteja desenvolvendo um componente, pode-se testar no construtor do componente se o Owner do componente já possui uma instância do componente.

1 constructor TMeuComponente.Create(AOwner: TComponent); 2 var 3 I: Integer; 4 ... 5 begin 6 if AOwner <> nil and then begin 7 8 for I := 0 to AOwner.ComponentCount - 1 do begin 9 10 if AOwner.Components[I] is TMeuComponente then begin 11 12 raise Exception.Create('Só pode ter um TMeuComponente no form'); 13 end; 14 end; 15 end 16 else begin 17 18 raise Exception.Create('Precisa ter um Owner'); 19 end; 20 21 inherited Create(AOwner); 22 23 // resto da sua inicialização 24 ... 25 end;

Esse exemplo assume que não é permitido criar o componente sem ter um Owner válido.

É possível executar TForm.Close no evento TForm.OnShow?

Ao longo de uma discussão no fórum ClubeDelphi sobre como cancelar a exibição de um form no evento OnShow do próprio form, surgiu uma dúvida sobre se era ou não possível executar TForm.Close no evento TForm.OnShow. Para algumas pessoas, ocorre o erro...

Cannot change Visible in OnShow or OnHide

Fiz alguns testes... na verdade, chamar TForm.Close dentro do evento TForm.OnShow dá problema a não ser que você trate o evento TForm.OnClose e defina um valor para Action diferente de caHide (que é o default). Portanto, este código...

1 type 2 TForm1 = class(TForm) 3 procedure FormShow(Sender: TObject); 4 procedure FormClose(Sender: TObject; var Action: TCloseAction); 5 ... 6 end; 7 8 ... 9 10 procedure TForm1.FormShow(Sender: TObject); 11 begin 12 Close; 13 end; 14 15 procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); 16 begin 17 Action := caFree; 18 end;

...funciona sem problemas enquanto este código...

1 type 2 TForm1 = class(TForm) 3 procedure FormShow(Sender: TObject); 4 // ... 5 end; 6 7 ... 8 9 procedure TForm1.FormShow(Sender: TObject); 10 begin 11 Close; 12 end;

... dá problema se o form não for o form principal do programa. Moral da estória: é possível para chamar TForm.Close no evento TForm.OnShow se...

  • no evento TForm.OnClose você atribuir a Action qualquer valor diferente de caHide OU
  • o form for o form principal do programa.

Como usar arquivos de ajuda em formato CHM em aplicativos Delphi?

Do fórum ClubeDelphi:

Gostaria de saber se existe algum forma de abrir um arquivo de ajuda .CHM direto em algum tópico

Usando a API do HtmlHelp:

1 const 2 HelpFile = 'c:\Alguma\Pasta\MeuArquivo.chm'; 3 var 4 H: HWND; 5 begin 6 // abre a ajuda no tópico intro.htm 7 H := HtmlHelp(0, PChar(HelpFile + '::/intro.htm'), HH_DISPLAY_TOPIC, 0);

Links relacionados

Como fazer carga e vínculo dinâmico de DLLs?

Do fórum:

Como faço para verificar em qual Windows a aplicação está rodando, para poder chamar esta função

function RegisterServiceProcess(dwProcessID, dwType: integer):integer; stdcall; external 'KERNEL32.DLL';

pois quando utilizo ela no Windows NT ele diz que não existe esta função na biblioteca.

A variável global Win32Platform declarada na unit SysUtils permite determinar qual a versão do Windows. Neste caso, se Win32Platform <> VER_PLATFORM_WIN32_NT, então pode chamar a função RegisterServiceProcess. Um $IFDEF não serve, porque o teste da versão do Windows precisa ser feito em tempo de execução.

Ao executar o programa aparece essa mensagem:

Não foi possivel localizar o ponto de entrada do procedimento RegisterServiceProcess na biblioteca de vinculo dinâmico 'KERNEL32.DLL'

O teste da versão do Windows é parte da solução. A mensagem de erro aparece por causa da declaração

function RegisterServiceProcess(dwProcessID,
  dwType: integer):integer; stdcall;
  external 'KERNEL32.DLL'; 

Essa declaração cria um link estático entre o programa e a rotina RegisterServiceProcess; isso significa que quando programa o programa é iniciado, o código de inicialização (gerado pelo Delphi) do programa irá tentar localizar essa rotina na DLL (no caso, kernel32.dll) e irá falhar se não encontrar. O que resolve é criar um link dinâmico com a rotina RegisterServiceProcess. Um jeito de fazer isso (não testado) seria:

1 type 2 TRegisterServiceProcessProc = function (dwProcessID, 3 dwType: integer):integer; stdcall; 4 5 var 6 RegisterServiceProcess: TRegisterServiceProcessProc; 7 H: THandle; 8 begin 9 H := GetModuleHandle('kernel32'); 10 if H <> 0 then begin 11 12 RegisterServiceProcess := GetProcAddress(H, 'RegisterServiceProcess'); 13 if @RegisterServiceProcess <> nil then begin 14 15 RegisterServiceProcess(GetCurrentProcessID, 1); 16 end; 17 end; 18 end;

No exemplo não usei LoadLibrary e nem FreeLibrary porque sei que kernel32.dll é uma DLL que já está carregada pelo programa, por isso uso GetModuleHandle, mas no caso mais geral deveria usar LoadLibrary/FreeLibrary.

Links relacionados

30 de jan. de 2005

Como usar o Mozilla FireFox em aplicativos Delphi?

Do fórum ActiveDelphi:

alguem sabe o nome da classe do Mozila Firefox (ex: a do IE é 'IEFrame' )
Assim como o IE, o Mozilla FireFox é um servidor de automação. O artigo Taming the Lizard with Delphi do site delphi.mozdev.org fala sobre o controle ActiveX Mozilla e faz uma comparação com o TWebBrowser do Internet Explorer.