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.

4 de dez. de 2004

Como funcionam os Namespaces em Delphi?

Marc Rohloff publicou um artigo no Borland Developer Network sobre como o Delphi 2005 trata a questão dos namespaces.

3 de dez. de 2004

Como criar uma constante array em C#?

O pessoal do C# Team da Microsoft escreveu sobre como criar uma constante array. A sugestão é fazer:

1 static readonly int [] constIntArray = 2 new int[] {1, 2, 3};
para criar um array de constantes inteiras. Mas existe um pequeno problema... embora o array não possa ser modificado, cada elemento individual pode ser alterado!

Como desenhar texto rotacionado em VB.NET?

Duncan Mackenzie escreveu sobre como desenhar texto rotacionado. O exemplo, em VB.NET, é:

1 Public Enum Direction As Integer 2 N = 0 3 NE = 1 4 E = 2 5 SE = 3 6 S = 4 7 SW = 5 8 W = 6 9 NW = 7 10 End Enum 11 12 Protected Overrides Sub OnPaint(ByVal e As 13 System.Windows.Forms.PaintEventArgs) 14 15 e.Graphics.Clear(Me.BackColor) 16 17 Dim bounds As Rectangle 18 Dim g As Graphics 19 Dim rotation As Single = 0 20 21 g = e.Graphics 22 bounds = New Rectangle(50, 50, Me.Width - 100, 23 Me.Height - 100) 24 25 Dim rect As System.Drawing.RectangleF 26 27 g.DrawEllipse(Pens.Black, bounds) 28 29 Dim myMatrix As Drawing2D.Matrix 30 Dim sf As New StringFormat(StringFormatFlags.NoWrap) 31 32 sf.Alignment = StringAlignment.Center 33 myMatrix = g.Transform() 34 rect = New System.Drawing.RectangleF(bounds.X, 35 bounds.Y, bounds.Width, bounds.Height) 36 37 For i As Integer = 0 To 7 38 If i > 0 Then 39 myMatrix.RotateAt(45, 40 New PointF(Me.Width / 2, Me.Height / 2), 41 Drawing.Drawing2D.MatrixOrder.Append) 42 g.Transform = myMatrix 43 End If 44 45 Dim directionString As String 46 47 directionString = 48 System.Enum.GetName(GetType(Direction), i) 49 g.DrawString(directionString, 50 New Font("Arial", 12, FontStyle.Bold), 51 Brushes.Black, rect, sf) 52 Next 53 End Sub

23 de nov. de 2004

Qual o tamanho máximo de um identificador em C#?

Cultura inútil: o tamanho máximo de um identificador no compilador C# da Microsoft é 512 caracteres. O compilador Mono parece ter o mesmo limite (via Jon Skeet no NG ms.p.dn.languages.csharp).