2009. február 15., vasárnap

Changing Position of Current Played Track in TMediaPlayer


Problem/Question/Abstract:

Difficulties in moving forward/backward (changing position) the current played track in TMediaPlayer ??

Answer:

To change the current position of current playing track, you just need to take the usefull (advance) of two event: 1) onTimer of TTimer and 2) onChange of TScrollbar. For full code, read below.

Here are the codes:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if (OpenDialog1.Execute) then
  begin
    Timer1.Enabled := false;
    MediaPlayer1.FileName := OpenDialog1.FileName;
    MediaPlayer1.Open;
    ScrollBar1.Max := MediaPlayer1.Length;
    ScrollBar1.Position := 0;
    Timer1.Enabled := true;
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  ScrollBar1.OnChange := nil; // disable the event handler
  ScrollBar1.Position := MediaPlayer1.Position;
  ScrollBar1.OnChange := ScrollBar1Change; // enable it again
end;

procedure TForm1.ScrollBar1Change(Sender: TObject);
begin
  MediaPlayer1.Pause;
  MediaPlayer1.Position := ScrollBar1.Position;
  MediaPlayer1.Play;
end;

First thing you must do here is initiate the MAX range of TScrollBar with the length of the current song track (look at Button1Click above).

Then add onTimer event code like above, and so the onChange event of TScrollBar.
The key is you must set TMediaPlayer's position with your selected scroolbar position for each of onTimer happen. Do this by calling onChange event of TScrollBar in onTimer event of TTimer.

2009. február 14., szombat

Changing the screen resolution


Problem/Question/Abstract:

How can I change the screen resolution?

Answer:

To change the screen resolution you can use the following function which is a wrapper for the Windows API ChangeDisplaySettings. The function takes the desired width and height as parameters and returns
the return value of ChangeDisplaySettings (see the documentation for more datails).

function SetScreenResolution(Width, Height: integer): Longint;
var
  DeviceMode: TDeviceMode;
begin
  with DeviceMode do
  begin
    dmSize := SizeOf(TDeviceMode);
    dmPelsWidth := Width;
    dmPelsHeight := Height;
    dmFields := DM_PELSWIDTH or DM_PELSHEIGHT;
  end;
  Result := ChangeDisplaySettings(DeviceMode, CDS_UPDATEREGISTRY);
end;

You can use ChangeDisplaySettings to change other properties of the display like the color depth and the display frequency.

Sample call

In the following example first we get the current screen resolution before setting it to 800x600, and then we restore it calling SetScreenResolution again.

var
  OldWidth, OldHeight: integer;

procedure TForm1.Button1Click(Sender: TObject);
begin
  OldWidth := GetSystemMetrics(SM_CXSCREEN);
  OldHeight := GetSystemMetrics(SM_CYSCREEN);
  SetScreenResolution(800, 600);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  SetScreenResolution(OldWidth, OldHeight);
end;

Copyright (c) 2001 Ernesto De Spirito
Visit: http://www.latiumsoftware.com/delphi-newsletter.php

2009. február 13., péntek

How to find a window by its (partial) title and close it (2)


Problem/Question/Abstract:

I am trying to figure out how I can get the handle of the main window of another application so I can terminate that program. I simply need to terminate one application from another application.

Answer:

This Code waits x seconds for a certain window to appear, tries to put it on top and send keystrokes to it. Pay special attention to the FindWindow Command. The Caption is the "EXACT" text in the window title bar. To do a partial match you have to have Windows ENUMERATE all it's open windows, then test the list that comes back.

function TMainForm.WaitForWindowAndType(WindowCaption, TextToSend: string;
  SecondsToWait: Cardinal): boolean;
var
  h: Hwnd;
  i: cardinal;
  vk: word;
begin
  i := SecondsToWait;
  repeat
    Application.ProcessMessages;
    sleep(1000);
    h := FindWindow(nil, pchar(WindowCaption));
    dec(i);
  until
    (i < 1) or (h > 0);
  result := not (h < 1);
  if h < 1 then
  begin
    {do nothing}
  end
  else
  begin
    memo1.Lines.Add(format('Found %s, after %d seconds.', [WindowCaption,
                 SecondsToWait - i]));
    sendmessage(h, WM_ACTIVATE, 0, 0);
    sendmessage(h, WM_SETFOCUS, 0, 0);
    i := 1;
    while integer(i) <= length(TextToSend) do
    begin
      if texttosend[i] = '@' then
      begin
        inc(i);
        vk := VkKeyScan(texttosend[i]);
        keybd_event(VK_MENU, MapVirtualKey(VK_MENU, 0), 0, 0); {this is the ALT key}
        keybd_event(vk, MapVirtualKey(VK, 0), 0, 0); {ALT Letter}
        keybd_event(vk, MapVirtualKey(VK, 0), KEYEVENTF_KEYUP, 0); {Letter UP}
        keybd_event(VK_MENU, MapVirtualKey(VK_MENU, 0), KEYEVENTF_KEYUP, 0); {ALT UP}
      end
      else
      begin
        vk := VkKeyScan(texttosend[i]);
        if vk shr 8 = 1 then
        begin
          keybd_event(VK_SHIFT, 0, 0, 0);
          keybd_event(vk, 0, 0, 0);
          keybd_event(vk, 0, KEYEVENTF_KEYUP, 0);
          keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
        end
        else
        begin
          keybd_event(vk, 0, 0, 0);
          keybd_event(vk, 0, KEYEVENTF_KEYUP, 0);
        end;
      end;
      sleep(100);
      inc(i);
    end;
  end;
end;

2009. február 12., csütörtök

Missing unit 'Proxies.pas'


Problem/Question/Abstract:

If your application or expert uses designtime information, you have to replace
uses DsgnIntf;
with
uses DesignIntf, DesignEditors;
But then you will run into an error message 'Cannot find unit Proxies.pas'

Answer:

The solution is to add DesignIde.dcp to your list of required packages.

You will have to ensure that the run-time package does not require the design-time package(s). This change in Delphi 6 enforces Borland's licence restrictions on designtime editors more strongly, which have been in the license documents since Delphi 3, I believe.

2009. február 11., szerda

Detect your own IP Address


Problem/Question/Abstract:

Detect your own IP Address

Answer:

uses
  WinSock; // type PHostEnt

function My_IP_Address: longint;
var
  buf: array[0..255] of char;
  RemoteHost: PHostEnt;
begin
  Winsock.GetHostName(@buf, 255);
  RemoteHost := Winsock.GetHostByName(buf);
  if RemoteHost = nil then
    My_IP_Address := winsock.htonl($07000001) { 127.0.0.1 }
  else
    My_IP_Address := longint(pointer(RemoteHost^.h_addr_list^)^);
  Result := Winsock.ntohl(Result);
end;

2009. február 9., hétfő

Extract and display version info from files


Problem/Question/Abstract:

Extract and display version info from files

Answer:

This routine shows how to retrieve version information from the Windows resources and displays it with a ShowMessage box:

procedure TForm1.GetVersionInfo;
const
  n_Info = 10;
  InfoStr: array[1..n_Info] of string =
  ('CompanyName', 'FileDescription', 'FileVersion', 'InternalName',
    'LegalCopyright', 'LegalTradeMarks', 'OriginalFilename',
    'ProductName', 'ProductVersion', 'Comments');
var
  Info: string;
  BuffSize,
    Len, i: Integer;
  Buff: PChar;
  Value: PChar;
begin
  Info := Application.ExeName;
  BuffSize := GetFileVersionInfoSize(PChar(Info), BuffSize);
  if BuffSize > 0 then
  begin
    Buff := AllocMem(BuffSize);
    Memo1.Lines.Add('FileVersionInfoSize=' + IntToStr(BuffSize));
    GetFileVersionInfo(PChar(Info), 0, BuffSize, Buff);
    Info := Info + ':';
    for i := 1 to n_Info do
      if VerQueryValue(Buff, PChar('StringFileInfo\040904E4\' +
        InfoStr[i]), Pointer(Value), Len) then
        Info := Info + #13 + InfoStr[i] + '=' + Value;
    FreeMem(Buff, BuffSize);
    ShowMessage(Info);
  end
  else
    ShowMessage('No FileVersionInfo found');
end;

2009. február 8., vasárnap

How to direct all keyboard events to a specific window


Problem/Question/Abstract:

SetCapture directs all mouse events to a specific window. Is there a similar API for the keyboard? Can this be accomplished without using hooks? I want to track what the user presses on the keyboard and mouse and allow or reject the events.

Answer:

Look up TApplication.OnMessage and study the example given, then look at the example below:


procedure TMainForm.AppMessage(var Msg: TMsg; var Handled: Boolean);
const
  BLOCKEDKEYS = [VK_TAB, VK_UP, VK_DOWN, VK_LEFT, VK_RIGHT, VK_SPACE];
begin
  if (Msg.message = WM_KEYDOWN) then
  begin
    if (Msg.wParam in BLOCKEDKEYS) then
    begin
      Handled := True;
    end;
  end;
end;

2009. február 7., szombat

How to restore the original volume settings after changing the volume of a wave


Problem/Question/Abstract:

I'm trying to control the left/ right channel volume of a wave. I've checked with WaveOutGetDevCaps that volume and left/ right control is supported. When I try to read the volume with WaveOutGetVolume (using "word (Wave_Mapper)" as the device), I get MMSysErr_NotSupported. I need the original volume setting in order to restore it later. WaveOutSetVolume also returns the same error.

Answer:

The code below worked for me. You will see that it is in message handlers for WaveOutOpen etc. messages. The key might be that you need the handle to an open wave device rather than simply the constant for wave_mapper.


procedure TClockForm.mm_wom_open(var Msg: TMessage);
{This code handles the WaveOutOpen message by writing two buffers of data
to the wave device.  Plus other miscellaneous housekeeping.}
begin
  waveOutGetVolume(hWave_out, @saved_volumes);
  waveOutSetVolume(hWave_out, volumes);
  waveOutPrepareHeader(hWave_out, p_wave_hdr, SizeOf(TWaveHdr));
  waveOutWrite(hWave_out, p_wave_hdr, SizeOf(TWaveHdr));
end;

procedure TClockForm.mm_wom_done(var Msg: TMessage);
{Handle the wave out done message}
begin
  waveOutSetVolume(hWave_out, saved_volumes);
  waveOutReset(hWave_out);
  waveOutClose(hWave_out);
end;

2009. február 6., péntek

Protecting code the ... dizzy way


Problem/Question/Abstract:

Code crackers do not only use debuggers but also reverse dissassemblers, that allow viewing code in a more readable form.

Crackers set breakpoints (typically on windows calls or messages), and "peek" on the source code before actually single-stepping back.

This is a small example of how to insert VARIABLE dummy code between valid statements, which can make reverse-dissassembling and single-stepping a nightmare.

Answer:

Conditional ASM statements that actually insert dummy code are of this kind:

       JMP here
       DB byte,byte,byte,byte  ; garble data
  here:

so actually the garble data never get executed. One can find a LOT of combinations that actually drive reverse dissassemblers nuts. This include file, when used, includes variable length dummy statements

----- This is the Include file AsmDizzy.inc: ------

{$IFDEF DIZZY4}
{$UNDEF DIZZY1}
{$UNDEF DIZZY2}
{$UNDEF DIZZY3}
{$UNDEF DIZZY4}
{$ENDIF}

{$IFDEF DIZZY3}
{$IFNDEF DIZZY4}
asm
   DB $EB,$06,$55,$44,$55,$03,$a8,$09;
end;
{$DEFINE DIZZY4}
{$ENDIF}
{$ENDIF}

{$IFDEF DIZZY2}
{$IFNDEF DIZZY3}
{$IFNDEF DIZZY4}
asm
   DB $EB,$04,$75,$13,$a2,$14;
end;
{$DEFINE DIZZY3}
{$ENDIF}
{$ENDIF}
{$ENDIF}

{$IFNDEF DIZZY1}
{$IFNDEF DIZZY2}
{$IFNDEF DIZZY3}
{$IFNDEF DIZZY4}
asm
     DB $EB,$04,$55,$03,$a7,$44;
end;
{$DEFINE DIZZY2}
{$ENDIF}
{$ENDIF}
{$ENDIF}
{$ENDIF}
;

---- End of incude file ----

Having this include file, try putting this code in the event of a button click. The code executes normally, but a lot of VARIABLE garble code is between the statements, some times 4,5 or 6 bytes wide.

procedure TForm1.Button1Click(Sender: TObject);
begin
{$I AsmDizzy.inc}
  ShowMessage('1');
{$I AsmDizzy.inc}
  ShowMessage('2');
{$I AsmDizzy.inc}
  ShowMessage('3');
{$I AsmDizzy.inc}
  ShowMessage('4');
{$I AsmDizzy.inc}
  ShowMessage('1');
end;

This can make singlestepping a nightmare, even with simple statements (ShowMessage) in-between. The .inc file can be enhanced to produce real random code, but this is a task for you to do.

2009. február 5., csütörtök

Get the Processor Usage


Problem/Question/Abstract:

How to get the processor usage

Answer:

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ComCtrls, CommCtrl, StdCtrls, Menus, WinSpool, ExtCtrls, Buttons, Registry;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Label2: TLabel;
    Timer1: TTimer;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
    started: boolean;
    reg: TRegistry;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
  Dummy: array[0..1024] of byte;
begin
  // Stats started by Button1 hit
  Reg := TRegistry.Create;
  Reg.RootKey := HKEY_DYN_DATA; // Statistic data is saved under this topic
  { Before starting retrieving statistic data you have to query
    the appropiate key under 'PerfStats\StartStat'. }
  Reg.OpenKey('PerfStats\StartStat', false);
    // Open this key first to start collecting performance data
  Reg.ReadBinaryData('KERNEL\CPUUsage', Dummy, Sizeof(Dummy));
  Reg.CloseKey;
  started := true;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  CPUU: integer;
begin
  { After starting the collection of statistic data, you can retrieve the
    recent value under the 'PerfStats\StatData' key. This is done by a timer
    event in this example }
  if started then
  begin
    Reg.OpenKey('PerfStats\StatData', false); // Open extension kex for txt files
    Reg.ReadBinaryData('KERNEL\CPUUsage', CPUU, SizeOf(Integer));
    Reg.CloseKey;
    Label1.Caption := IntToStr(CPUU) + '%';
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  Dummy: array[0..1024] of byte;
begin
  // Button2 hit stops statistic collection
  { Collecting statistic data is stopped by a query under 'PerfStats/StopStat' }
  Reg.OpenKey('PerfStats\StopStat', false);
    // Open this key first to start collecting performance data
  Reg.ReadBinaryData('KERNEL\CPUUsage', Dummy, SizeOf(Dummy));
  Reg.Free;
  Started := false;
end;

end.

2009. február 4., szerda

Outlook Automation - Scaning Outlook's Folders


Problem/Question/Abstract:

How I can information from Outlook in my application

Answer:

Sample how to work with Outlook from Delphi application

unit UScanOutlook;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Grids, Outline;
const
  olByValue = 1;
  olByReference = 4;
  olEmbeddedItem = 5;
  olOLE = 6;

  olMailItem = 0;
  olAppointmentItem = 1;
  olContactItem = 2;
  olTaskItem = 3;
  olJournalItem = 4;
  olNoteItem = 5;
  olPostItem = 6;

  olFolderDeletedItems = 3;
  olFolderOutbox = 4;
  olFolderSentMail = 5;
  olFolderInbox = 6;
  olFolderCalendar = 9;
  olFolderContacts = 10;
  olFolderJournal = 11;
  olFolderNotes = 12;
  olFolderTasks = 13;

type
  TForm1 = class(TForm)
    oline_outlook: TOutline;
    Button8: TButton;
    procedure Button8Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    OlApp, NameSpace, root: OleVariant;
  end;

var
  Form1: TForm1;

implementation
uses ComObj;
{$R *.DFM}

procedure TForm1.Button8Click(Sender: TObject);
  procedure scan(ol: TOutline; root: OleVariant; s: string);
  var
    i, j, k: integer;
    bcount, rcount: integer;
    branch, MAPIFolder: olevariant;
    line: string;
  begin
    line := '';
    rcount := root.count;
    for i := 1 to rcount do
    begin
      line := s + root.item[i].name;
      ol.Lines.Add(line);
      branch := root.item[i].folders;
      bcount := branch.count;
      MAPIFolder := Namespace.GetFolderFromId(root.item[i].EntryID,
        root.item[i].StoreID);
      if MAPIFolder.Items.count > 0 then
        for j := 1 to MAPIFolder.Items.count do
          ol.Lines.Add(s + ' ' + MAPIFolder.Items[j].subject);
      if bcount > 0 then
      begin
        scan(ol, branch, s + ' ');
      end;
    end;
  end;
begin
  oline_outlook.Lines.Clear;
  OlApp := CreateOleObject('Outlook.Application');
  Namespace := OlApp.GetNameSpace('MAPI');
  root := Namespace.folders;
  scan(oline_outlook, root, '');
end;

end.

2009. február 3., kedd

Opening and Closing a CD Tray better


Problem/Question/Abstract:

Opening and Closing a CD Tray?

Answer:

uses
  MMSystem;

// Open CD Tray

{Simple Way:}

mciSendstring('SET CDAUDIO DOOR OPEN WAIT', nil, 0, Self.Handle);

{More complex way:}

function OpenCD(Drive: Char): Boolean;
var
  Res: MciError;
  OpenParm: TMCI_Open_Parms;
  Flags: DWORD;
  S: string;
  DeviceID: Word;
begin
  Result := False;
  S := Drive + ':';
  Flags := MCI_OPEN_TYPE or MCI_OPEN_ELEMENT;
  with OpenParm do
  begin
    dwCallback := 0;
    lpstrDeviceType := 'CDAudio';
    lpstrElementName := PChar(S);
  end;
  Res := mciSendCommand(0, MCI_OPEN, Flags, Longint(@OpenParm));
  if Res <> 0 then
    Exit;
  DeviceID := OpenParm.wDeviceID;
  try
    Res := mciSendCommand(DeviceID, MCI_SET, MCI_SET_DOOR_OPEN, 0);
    if Res = 0 then
      Exit;
    Result := True;
  finally
    mciSendCommand(DeviceID, MCI_CLOSE, Flags, Longint(@OpenParm));
  end;
end;

//Close CD Tray

{Simple Way:}

mciSendstring('SET CDAUDIO DOOR CLOSED WAIT', nil, 0, Self.Handle);

{More complex way:}

function CloseCD(Drive: Char): Boolean;
var
  Res: MciError;
  OpenParm: TMCI_Open_Parms;
  Flags: DWORD;
  S: string;
  DeviceID: Word;
begin
  Result := False;
  S := Drive + ':';
  Flags := MCI_OPEN_TYPE or MCI_OPEN_ELEMENT;
  with OpenParm do
  begin
    dwCallback := 0;
    lpstrDeviceType := 'CDAudio';
    lpstrElementName := PChar(S);
  end;
  Res := mciSendCommand(0, MCI_OPEN, Flags, Longint(@OpenParm));
  if Res <> then
    Exit;
  DeviceID := OpenParm.wDeviceID;
  try
    Res := mciSendCommand(DeviceID, MCI_SET, MCI_SET_DOOR_CLOSED, 0);
    if Res = 0 then
      Exit;
    Result := True;
  finally
    mciSendCommand(DeviceID, MCI_CLOSE, Flags, Longint(@OpenParm));
  end;
end;

2009. február 2., hétfő

Deleting Registry Key from .Reg file


Problem/Question/Abstract:

Deleting Registry Key from .Reg file

Answer:

If you want to import a key but delete another key at the same time, use the syntax in your Reg file:

[-HKEY_CURRENT_USER\Software\Local Applications]

Note the minus sign after the bracket.

2009. február 1., vasárnap

Create a TAction and its OnExecute event at runtime


Problem/Question/Abstract:

How to create a TAction and its OnExecute event at runtime

Answer:

{ ... }
type
  TForm1 = class(TForm)
    ActionList1: TActionList;
    procedure FormCreate(Sender: TObject);
  private
    Action1, Action2: TAction;
    procedure Test(Sender: TObject);
  end;

procedure TForm1.Test(Sender: TObject);
begin
  Caption := IntToStr(Actionlist1.ActionCount);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Action1 := TAction.Create(Actionlist1);
  Action1.Actionlist := Actionlist1;
  Action1.OnExecute := Test;
  Form1.OnClick := Action1.OnExecute;
end;