日本Delphi振興会 Delphiはじめて物語 Delphiつれづれ記 Delphiリンク互助会 Delphiアクセサリ

Delphi壁の穴

その二:システムを覗く

ここではWindowsシステムをDelphiで使う方法をご紹介します。

目次

Delphi壁の穴 Delphiを覗く レジストリを覗く アプリケーションを覗く

・Windows APIを知る。

 Windows APIは、Windowsを操るために使われる一般的な命令と考えればいいでしょう。しかし、Windows APIはC言語を念頭に置いて作られているため、PASCALを源流とするDelphiにはなじみにくく、使いづらいものです。Delphiはその使いづらいWindows APIをDelphi独自の「プロパティ」「メソッド」「イベント」「手続き」「関数」にカプセル化する事によって簡単に使えるようにしているのです。

 しかし、ちょっと難しいことや気の利いたことをしたい場合、必ずと言っていいほどWindows APIを直接使う必要に迫られます。また、Delphiがカプセル化したものを使うよりもWindows APIを直接使ったほうが、より思い通りの結果が得られることもあります。それは、やはりWindows APIがWindowsを直接的に操れるから、という理由に尽きるでしょう。特にシステム関係を操作するにはWindows APIの利用は必須です。

 私もそうですが、C言語を全く知らず、BASICやPASCALからDelphiにやって来た人にとっては、C言語を念頭に置いて作られたWindows APIの文法はちんぷんかんぷんだと思います。以下では、私が体験的に得たWindows APIの読みかえを一覧にしました。

Windows APIDelphi解説
NULLで終わる文字列(を指すポインタ)
lptstr
PChar型Delphiで使われる文字列型「String」はDelphi独自のもので、Windows APIではNULLで終わる文字列というものを一般的に使います。これを見た場合は、「PChar(Edit1.Text)」と型変換するか、直接に「'文字列'」のように「'」でくくって指定します。また、単に文字列と書いてあっても、NULLで終わる文字列のことと見て間違いありません。

例:MessageBox(Handle, PChar(Edit1.Text), 'メッセージ', MB_OK);

構造体(を指すポインタ)レコード型構造体という言葉が出てきた場合は、Delphiではすべてレコード型として扱います。例えばRECT構造体はDelphiでは「TRect」レコードですし、OSVERSIONINFO構造体は「TOsVersionInfo」レコードです。このように構造体名の頭に「T」を付けるだけです。
例:
procedure TForm1.Button1Click(Sender: TObject);
var
 Rect: TRect; {TRectレコードを宣言}
begin
 GetWindowRect(Handle,Rect); {Rectに情報を代入}
 Label1.Caption:=IntToStr(Rect.Top); {RectレコードのTopを参照する}
end;
DWORD/int/UINTInteger全部まとめてIntegerと宣言しても大丈夫です。
NULLnil「このパラメータにNULLを指定しなければなりません。」というようなフレーズを見たら、Delphiでは「nil」を指定します。

目次へ戻る


・現在の日付と時刻を得る。

 一般的な表示(97/03/04 3:05:20)をさせるには、DateTimeToStrを使います。
Label1.Caption:=DateTimeToStr(Now);

 日付だけなら「DateToStr」を、時刻だけなら「TimeToStr」を使います。

 また、いろいろな形式で表示させたいならば、FormatDateTimeを使います。
例えば「1997年3月4日 3時5分20秒」を表示するには、

Label1.Caption:=FormatDateTime('yyyy年m月d日 h時m分s秒',Now);

のようにします。他にも、曜日、元号、AM/PM表示などが可能です。

目次へ戻る


・ファイルの日付と時刻を得る(簡易版)。

 まずは、FileAgeを使います。例えば作成した実行ファイルの日付と時刻を得てみましょう。
Label1.Caption:=IntToStr(FileAge(ParamStr(0)));

 そうすると「577045466」のような単なる数字の羅列がでてきます。コンピュータ内部ではこのような数字の羅列でファイルの日付と時刻を一括して管理しているのです。
 数字が大きいほど新しく作られたファイルになります。このため、どちらのファイルが新しく作られたのかを調べるだけなら、FileAgeだけで十分出来ます。

 次に、これをDelphiで扱う日付時刻型(TDateTime型)に変換します。

var
 DateTime:TDateTime;
begin
 DateTime:=FileDateToDateTime(FileAge(ParamStr(0)));
end;

 最後に、人間が理解できる文字列に変換します。

var
 DateTime:TDateTime;
begin
 DateTime:=FileDateToDateTime(FileAge(ParamStr(0)));
 Label1.Caption:=DateTimeToStr(DateTime);
end;

 上では、DateTimeToStrを使いましたが、FormatDateTimeを使えばいろいろな形式で表示できます。

 Label1.Caption:=FormatDateTime('yyyy/mm/dd',DateTime);  //1999/12/03 の様に表示される

目次へ戻る


・ファイルの日付と時刻を得る(高度版)。

エクスプローラで見るファイルのプロパティには「作成日時」「更新日時」「アクセス日」が表示されますが、それを取得してみましょう。
{情報表示}
procedure TForm1.Button1Click(Sender: TObject);
var
 FileName: string;
 hFile: integer;

 ftCreateDate,
 ftModifyDate,
 ftAccessDate: TFileTime;
 lftCreateDate,
 lftModifyDate,
 lftAccessDate: TFileTime;
 stCreateDate,
 stModifyDate,
 stAccessDate: TSystemTime;
begin
 FileName:='C:\Windows\Readme.txt';
 hFile:=FileOpen(FileName,fmOpenRead);  //読みとり専用でファイルを開く

 GetFileTime(hFile,@ftCreateDate,@ftAccessDate,@ftModifyDate);  //ファイルの日時を取得

 {ローカル日時に変更}
 FileTimeToLocalFileTime(ftCreateDate,lftCreateDate);
 FileTimeToLocalFileTime(ftModifyDate,lftModifyDate);
 FileTimeToLocalFileTime(ftAccessDate,lftAccessDate);
 {システム日時に変更}
 FileTimeToSystemTime(lftCreateDate,stCreateDate);
 FileTimeToSystemTime(lftModifyDate,stModifyDate);
 FileTimeToSystemTime(lftAccessDate,stAccessDate);
 
 {作成日時}
 CreateDate.Caption:=FormatDateTime('yyyy/mm/dd hh:nn:ss',SystemTimeToDateTime(stCreateDate));
 {修正日時}
 ModifyDate.Caption:=FormatDateTime('yyyy/mm/dd hh:nn:ss',SystemTimeToDateTime(stModifyDate));
 {アクセス日}
 AccessDate.Caption:=FormatDateTime('yyyy/mm/dd',SystemTimeToDateTime(stAccessDate));

 FileClose(hFile);  //ファイルを閉じる
end;

流れとしては

  1. FileOpenで目的のファイルを開きファイルハンドルを取得。
  2. GetFileTimeでファイルの日時を取得。
  3. 得られた日時は国際標準時なのでFileTimeToLocalFileTimeでローカル日時に変更。
  4. SystemTimeToDateTimeを使いたいので、FileTimeToSystemTimeでファイル日時をシステム日時に変更。
  5. SystemTimeToDateTimeでDelphiのTDateTime型に変換したあとFormatDateTimeでしかるべき表示形式で結果表示。
となります。

目次へ戻る


・パソコンの日付と時刻を変える。

 日付と時刻を変えるには、Windows APIの SetLocalTime を使います。以下の例ではボタンを押すと1997年11月21日13時0分0秒0ミリ秒に時計を合わせます。
procedure TForm1.Button1Click(Sender: TObject);
var
 SysTime:TSYSTEMTIME;
begin
 with SysTime do
 begin
  wYear:=1997;
  wMonth:=11;
  wDay:=21;
  wHour:=13;
  wMinute:=00;
  wSecond:=00;
  wMilliseconds:=00;
 end;
 SetLocalTime(SysTime);
end;

目次へ戻る


・EXEやDLLにアイコンを埋め込む。

  1. EXEにアイコンを埋め込む
     まず最初に ImageEditor で リソースファイル(res)を新規作成します。次に、メニューから[リソース-新規-アイコン]を選びます。そうするとICON1が作成されるので、ダブルクリックしてアイコンを描いていきます。

     で、ICON1をそのままの名前にしてはいけません。アイコンリソースの並ぶ順番は、アルファベット順になります。アプリケーションのデフォルトアイコンは、MAINICON という名前になっているため、このままだとICON1がMAINICONの前にきてしまい、アプリケーションのアイコンがICON1で表示されてしまいます。メニューから[リソース-名前の変更]を選んでMAINICONよりも後ろに来るような名前に変更します。

     次に、リソースファイルに名前を付けて保存します。リソースファイルの名前をプロジェクトと同じ名前にしてはいけません。エクスプローラなどで見ればわかりますが、project1.dprにはproject1.resが自動的に作成されます。このリソースファイルはDelphiが管理しているため、勝手に中身をいじってはいけないのです(いじってもいいですが、いじった結果は全く反映されません)。そのため、新しく作ったリソースファイルには新しい名前を付けてください。ユニット名と同じにすることはOKです。

     最後に、このリソースファイルをアプリケーションに埋め込みます。リソースファイル名をexample.resとすれば、

    unit Unit1;
    
    interface
    
    uses
      Windows,....;
    
    type
      TForm1 = class(TForm)
      private
        { Private 宣言 }
      public
        { Public 宣言 }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.DFM}
    {$R example.res} {<--- ここに追加}
    

     {$R}は、リソースを実行ファイルに埋め込むためのコンパイル指令です。ここにリソースファイル名を指定します。パスを指定しなければ、ユニットファイルがあるフォルダを検索します。

  2. DLLにアイコンを埋め込んで、アイコンDLLを作る
     リソースファイルを作る作業は、1.で説明したことと同じですが、埋め込む方法が違います。  Delphiのメニューから[新規作成-ダイナミックリンクライブラリ]を選びます。次に以下のようにコードを変更します。
    library Project1;
    
    uses
      Windows;
    
    {$R example.res}
    
    begin
    end.
    

     uses節をWindowsだけに変更します。DLLは単独では実行できないので、メニューからコンパイルを選んで、DLLにします。  これで、アイコンだけのDLLが出来ました。ちなみにWindows95に付属のアイコンDLLは、Shell32.dll、Moricons.dll、Pifmgr.dll などがあります。

目次へ戻る


EXEやDLLからアイコンを取り出す。

     
  1. 大きなアイコン(32x32)のみでよければ、ExtractIconを使います。例として、Image1に描画します。
    uses Windows, ..... ShellAPI; {ShellAPIを追加}
    
    (略)
    {オープンダイアログで指定したファイルのアイコンを描画}
    procedure Form1.Button1Click(Sender: TObject);
    var
     Icon:TIcon;
    begin
     with OpenDialog1 do
     begin
      if Execute then
       begin
        Icon:=TIcon.Create;
        Icon.Handle:=ExtractIcon(hInstance,PChar(FileName),0);
        if Icon.Handle<>0 then
         Image1.Canvas.Draw(0,0,Icon)
        else ShowMessage('アイコンがありません');
        Icon.Free;
       end;
     end;
    end;
    

     ExtractIconで指定する第三引数は、アイコンの番号です。0は1つ目、1は2つ目のアイコンを指します。

     

  2. 小さいアイコン(16x16)も取り出したければ、ExtractIconExを使います。
    uses Windows, ..... ShellAPI; {ShellAPIを追加}
    
    (略)
    {オープンダイアログで指定したファイルのアイコンを描画}
    procedure Form1.Button1Click(Sender: TObject);
    var
     Icon:TIcon;
     hLarge,hSmall:THandle;
    begin
     with OpenDialog1 do
     begin
      if Execute then
       begin
        Icon:=TIcon.Create;
        ExtractIconEx(PChar(FileName),0,hLarge,hSmall,1);
        Icon.Handle:=hSmall; {小さいアイコンを指定}
        if Icon.Handle<>0 then
         Image1.Canvas.Draw(0,0,Icon)
        else ShowMessage('アイコンがありません');
        Icon.Free;
       end;
     end;
    end;
    
     ExtractIconExでは、アイコンの番号を第2引数に指定します。上記例では 0、つまり1つ目のアイコンが取得できます。

目次へ戻る


・メモリ情報を得る。

 Windows APIのGlobalMemoryStatusを使います。
{メモリ情報の取得}
procedure TForm1.Button1Click(Sender: TObject);
var
 MemStat:TMemoryStatus;
 TotalMemory,AvailMemory:string;
begin
 {メモリ情報を得る}
 MemStat.dwLength:=SizeOf(TMemoryStatus);
 GlobalMemoryStatus(MemStat);

 {メモリの計算}
 TotalMemory:= FormatFloat('#,##0KB',Round(MemStat.dwTotalPhys/1024));
 AvailMemory:= FormatFloat('#,##0KB',Round(MemStat.dwAvailPhys/1024));

 Label1.Caption:='全メモリ:'+TotalMemory;
 Label2.Caption:='空きメモリ:'+AvailMemory;
end;

※FormatFloat('#,##0')は、3ケタごとに「,」で区切るのに使います。
※Roundは、小数点以下を丸めた値を返します。

目次へ戻る


・ハードディスクの容量を得る。

ハードディスクの容量は「DiskSize」、空き容量は「DiskFree」を使います。

しかし、Delphi3までですとFAT32で2GBを越える容量に対して正しく動作しません。正しく動作させるには「GetFreeDiskSpaceEx」APIを使うことになります。ただし、このAPIはWindows95-OSR2以降からのサポートなので、それ以前のOSですとエラーになります。

Delphi4以降では正しく動作しますが(ヘルプにはできないと書いてあるができます)、やはりOSがWindows95-OSR2以降でないとエラーになります。つまり初期のWindows95をも対象にしたアプリの場合はこれらの関数は使えません。そのため、SysUtils.pasの中に「BackfillGetDiskFreeSpaceEx」という隠し関数があって、それを使うと初期のWin95でも使えるし、2GB超の容量も取得できます(ただしこの関数、なぜか末尾に「stdcall」が付いていて直接呼び出せません。ここの関数部分だけコピーして「stdcall」を消してください)。

2GBを超える場合(Delphi4)
OSWindows95初期版Windows95OSR2Windows98/2000
DiskSize×
DiskFree×
隠し関数

{ディスク容量}
procedure TForm1.Button1Click(Sender: TObject);
var
 Total,Free:string;
begin
 {これはDelphiの王道パターンです。OSに注意すればこれでOK}
 {3はCドライブです(ヘルプ参照)。}
 Label1.Caption:=FormatFloat('#,##0KB',Trunc(DiskSize(3)/1024));
 Label2.Caption:=FormatFloat('#,##0KB',Trunc(DiskFree(3)/1024));

 {こちらはOSを選ばない裏技パターン。}
 {BackfillGetDiskFreeSpaceExをButton1Clickの上にコピーしておく。}
 {Delphi3でGetDiskFreeSpaceExを使う場合も同じように書けばよい}
 BackfillGetDiskFreeSpaceEx('C:\',Free,Total,nil);
 Label3.Caption:=FormatFloat('#,##0KB',Trunc(Total/1024));
 Label4.Caption:=FormatFloat('#,##0KB',Trunc(Free/1024));
end;

目次へ戻る


・Windows/System/Tempフォルダを得る。

 関数を作ってみました。
{Windowsフォルダを得る}
function WinDir:string;
var
 TempWin:array[0..MAX_PATH] of Char;
begin
 GetWindowsDirectory(TempWin,MAX_PATH);
 Result:=StrPas(TempWin)+'\';
end;

{Systemフォルダを得る}
function SysDir:string;
var
 TempSys:array[0..MAX_PATH] of Char;
begin
 GetSystemDirectory(TempSys,MAX_PATH);
 Result:=StrPas(TempSys)+'\';
end;

{Tempフォルダを得る}
function TempDir:string;
var
 TempTmp:array[0..MAX_PATH] of Char;
begin
 GetTemppath(MAX_PATH,TempTmp);
 Result:=StrPas(TempTmp)+'\';
end;

目次へ戻る


・ディレクトリの有無をチェックする。

 ディレクトリの有無をチェックするには、uses節にFileCtrlユニットを追加してDirectoryExistsを使います。
 しかし、フォームを使わない小さいアプリケーションにFileCtrlを追加すると100KBを越えてしまいます。そこでSysUtilsだけで使えるディレクトリチェック関数を作ってみました。
 ここで重要なのはFindFirst/FindNext/FindCloseです。これらを使うと、ファイル属性を指定して連続的にファイルを検索できます。そしてこのファイル属性の中に、ディレクトリが含まれており、今回はそれを利用します。
{フォルダ検索 -- DirectoryExistsを使えない場合}
function DirExists(DirName: string): Boolean;
var
 F: TSearchRec; {検索したファイルの情報が入る(今回は使わない)}
begin
 if Copy(DirName,Length(DirName),1)='\' then
  Delete(DirName,Length(DirName),1);
 if FindFirst(DirName+'\*.*', faDirectory, F)=0 then {フォルダがあれば}
  Result:=True
 else Result:=False;
 FindClose(F); {検索終了(メモリを解放する)}
end;

目次へ戻る


・ファイルのコピーと移動。

ファイルのコピーと移動はWindowsAPIを使って行います。APIだけでは使いにくいので、関数を作ってみました。
{ファイルのコピー}
function CopyFile(From,ToDir:string;OverWrite:Boolean):Boolean;
begin
 OverWrite:=not OverWrite;
 if Copy(ToDir,Length(ToDir),1)<>'\' then
  ToDir:=ToDir+'\';
 ToDir:=ToDir+ExtractFileName(From);
 Result:=Windows.CopyFile(PChar(From),PChar(ToDir),OverWrite);
end;

{ファイルの移動}
function MoveFile(From,ToDir:string;OverWrite:Boolean):Boolean;
begin
 if Copy(ToDir,Length(ToDir),1)<>'\' then
  ToDir:=ToDir+'\';
 ToDir:=ToDir+ExtractFileName(From);
 if FileExists(From) and OverWrite then DeleteFile(ToDir);
 Result:=Windows.MoveFile(PChar(From),PChar(ToDir));
end;

 例えば、「C:\Delphi 2.0\Readme.txt」を「C:\Delphi」にコピーするときは、

 CopyFile('C:\Delphi 2.0\Readme.txt','C:\Delphi',False);

 のようにします。第三引数は、コピー先に同じファイルがあった場合、上書きするか否かを決定します。Trueならば上書きし、Falseならば上書きしません。

 また、コピー/移動後にリネームさせることもできます。

{コピーとリネーム}
function CopyRenFile(const From,ToDir:string;OverWrite:Boolean):Boolean;
begin
 OverWrite:=not OverWrite;
 Result:=Windows.CopyFile(PChar(From),PChar(ToDir),OverWrite);
end;

{移動とリネーム}
function MoveRenFile(const From,ToDir:string;OverWrite:Boolean):Boolean;
begin
 if FileExists(From) and OverWrite then DeleteFile(ToDir);
 Result:=Windows.MoveFile(PChar(From),PChar(ToDir));
end;

 例えば、「C:\Delphi 2.0\Readme.txt」を「C:\Delphi」にコピーして「Read.txt」にリネームするには、

 CopyRenFile('C:\Delphi 2.0\Readme.txt','C:\Delphi\Read.txt',False);

 のようにします。第二引数のコピー先にリネームするファイル名まで書いてしまうところが、CopyFileとの違いです。

 これらの関数は、成功すると True を、失敗すると False を返します。

目次へ戻る


・DLLの利用方法。

 DLLを使うには、静的インポートと動的インポートがあります。静的インポートはアプリケーションを立ち上げるときにDLLをくっつける(インポートする)方法です。このため、起動時に必ず目的のDLLがなくてはなりません。動的インポートは、DLLを使うときになったらくっつける方法です。このため、目的のDLLがなくてもアプリケーションを起動できます。

 静的インポートはアプリケーションの起動が遅くなる代わりに、簡単にコードが書けます。また、DLLを頻繁に使うなら静的インポートがいいでしょう。動的インポートはコードが複雑になりますが、起動に影響を与えません。必要なときにだけ使えればいいのなら動的インポートがいいでしょう。

  1. 静的インポート
     implementation部のすぐ下に、使用するDLLのファイル名と関数名を指定します。例えば、example.dllの中にある関数 ExampleFunc(P: PChar; I: integer): PChar を使うには、
    implementation
    
    {$R *.DFM}
    
    function ExampleFunc(P:PChar;I:integer):PChar;stdcall;external 'example.dll';
    

     stdcallは、関数の呼び出し方の一種です。Delphi2/3では、stdcallが一般的です。Delphi1の場合は指定する必要はありません。
     externalは、どのDLLから関数を呼び出すかを決定します。パスを指定しない場合は、カレントフォルダ、Windowsフォルダ、Systemフォルダが検索されます。

  2. 動的インポート
     必要に応じてDLLを使うため、関数を使うときに呼び出せばOKです。例としてButton1を押したときに関数を呼び出してみましょう。
    procedure TForm1.Button1Click(Sender: TObject);
    type
     TExampleFunc=function(P:PChar;I:integer):PChar;
    const
     NoDLL='DLLがありません。';
     NoFunction='関数がありません。';
    var
     LibHd:THandle;
     ExampleFunc:TExampleFunc;
    begin
     {DLLをロードする}
     LibHd:=LoadLibrary('example'); {DLLを指定}
     if LibHd<HINSTANCE_ERROR then  {DLLがなければエラー}
      begin
       MessageBox(Handle,PChar(NoDLL),'エラー',MB_OK or MB_ICONHAND);
       Exit;
      end ;
     
     {関数をインポートする}
     @ExampleFunc:=GetProcAddress(LibHd,'ExampleFunc');{関数を指定}
     if @ExampleFunc=nil then  {関数がなければエラー}
      begin
       MessageBox(Handle,PChar(NoFunction),'エラー',MB_OK or MB_ICONHAND);
       Exit;
      end;
    
     {関数を使用する}
     ExampleFunc('aaaa');
     
     {DLLを解放する}
     FreeLibrary(LibHd);
    end;
    

 動的インポートはこのように長いコードを書いてやっと関数が使用できます。

目次へ戻る


・Windows標準のアバウトを使う。

 メモ帳やペイントなど、アクセサリのバージョン情報は、どれも同じものを使っています。これと同じものを出すには、WindowsAPIのShellAboutを使います。
uses Windows, .... ,ShellAPI; {ShellAPIを追加}

(略)

procedure TForm1.Button1Click(Sender: TObject);
const
 AppName='バージョン情報#Project1.exe ver 2.0';
 Info='製作者:わたし'+#13#10+
      '製作日:97/03/06'+#13#10+
      'Created with Borland Delphi 2.0';
begin
 ShellAbout(Handle,PChar(AppName),PChar(Info),Application.Icon.Handle);
end;


 第二引数にソフト名を指定します。「#」で区切ることで、タイトルとソフト名を分けて表示できます。第三引数にはソフトの簡単な情報を指定できます。最大3行までです。

 ただ、実行画面を見ればわかると思いますが、ソフト名の前に「Microsoft」が付くので、あたかもマイクロソフトの製品であるかのようになってしまうところが欠点です。

実行画面

目次へ戻る


・タイトルバーを点滅させる。

 ウィンドウのタイトルバーを点滅させるには、Windows APIのFlashWindowを使います。しかし、普通は点滅というと「点」と「滅」でひとそろいですが、FlashWindowは片方しか行いません。つまり、点滅させるにはFlashWindowを二回使う必要があります。また、点滅させる速度を制御するには、Timerコンポーネントを使います。
 例として、ボタンを押して5回タイトルバーを点滅させてみましょう。
(略)
implementation

{$R *.DFM}

var
 i:integer;

procedure TForm1.FormCreate(Sender: TObject);
begin
 Timer1.Enabled:=False; {Timerを動作させない}
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 Timer1.Interval:=500; {0.5秒に一回Timerを動作させる}
 Timer1.Enabled:=True;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
 Wnd:HWnd; {アクティブなウィンドウのハンドル}
begin
 Wnd:=GetForegroundWindow; {アクティブなウィンドウのハンドルを得る}
 FlashWindow(Wnd,True); {タイトルバーを点滅させる}
 Inc(i); {点滅させた回数を記録}
 if i=5*2 then {「点」と「滅」を5*2=10回したら}
  begin
   Timer1.Enabled:=False; {Timerストップ}
   i:=0; {点滅回数を初期化}
   FlashWindow(Wnd,False); {タイトルバーの状態を初期化}
  end;
end;

  1. Intervalを500(ミリ秒)にすることで、1秒に1回の周期で点滅します。
  2. GetForegroundWindowでアクティブなウィンドウを得ます。似たようなAPIにGetActiveWindowがありますが、Win32環境では無効です。
  3. FlashWindowで点滅させます。第二引数にはTrueを指定します。
  4. FlashWindowは「点」に1回、「滅」に1回必要なので、5回点滅させるには、「5*2=10回」FlashWindowさせる必要があります。
  5. 最後にFlashWindowの第二引数にFalseを指定して、タイトルバーを点滅させる前の状態に戻します。

目次へ戻る


・コントロールパネルを作る。

 コントロールパネル(*.CPL)を作るなんてさぞかし難しいように思われるかも知れませんが、案外簡単に作れてしまいます。その秘密は、コントロールパネルが実は単なるDLLの一種にすぎない、ということにあるのです。つまりあなたがDLLを作る方法をすでに知っているのなら、あとはそれの応用にすぎません。
 コントロールパネルの鍵ともなるべきものが「CPlApplet」です(CPlの"l"は小文字の"L")。コントロールパネル(*.CPL)は、CPlApplet関数をエクスポートする事でコントロールパネルたり得ます。コントロールパネルの本体であるControl.exeは、拡張子がCPLでかつ「CPlApplet」をエクスポートしているファイルをコントロールパネルと見なしてそれと通信を行います。Control.exeはCPlApplet内にあらかじめ定義しておいたコントロールパネル(*.CPL)の情報を見て、そのコントロールパネルの挙動を決定するのです。以下、具体的に見ていきましょう。
{programをlibraryに変えてDLLを作り、その拡張子をcplに変える}
library Cpluse; 

uses
  Cpl,  {CPLユニットを使う}
  Windows,
  Forms,
  Unit1 in 'Unit1.pas' {Form1};


{$E cpl}  //拡張子を「cpl」にする。

{$R *.RES}

(*
***解説***
 コントロールパネルに表示されるアプリケーションのことを
「アプレット」という。1つのCPLファイルの中に複数のアプレット
を埋め込むことができる。
 コントロールパネルを開こうとすると、Control.exeはSystemフォルダ
(NTではSystem32フォルダ)内にあるCPLを拡張子にもつファイルを調べ、
CPlApplet関数がエクスポートされているかを調べる。
 CPlApplet関数がエクスポートされていることが分かると、コントロール
パネルはCPLファイルと通信しあって、CPlAppletで指定したCPLファイルの
情報(アプレットの数/アイコン/アイコン名やダブルクリックしたときの処理)
を収集する。
*)

{コントロールパネルへエクスポートするコールバック関数}
{(注)必ず「CPlAppet」と綴ること。「CPL」「Cpl」などはだめ。}
function CPlApplet(hwndCPl: THandle; uMsg: DWORD;
                 lParam1, lParam2: LongInt):LongInt;stdcall;
var
 NewCplInfo:PNewCplInfo;
begin
 Result:=0;
 case uMsg of
  {初期化する。うまくいったら1を返すようにする。}
  CPL_INIT:
   begin
    hInst:=GetModuleHandle('Sample.cpl'); {CPLのハンドル取得}
   Result:=1;

  {アプレットの個数をコントロールパネルに送る。この例では1個でよい。}
  CPL_GETCOUNT: 
   Result:=1;

  {アプレットの情報をコントロールパネルに送る。}
  CPL_NEWINQUIRE: 
   begin
    NewCplInfo:=PNewCplInfo(lParam2);
    with NewCplInfo^ do
    begin
     dwSize:=SizeOf(TNewCplInfo);
     dwFlags:=0;
     dwHelpContext:=0;
     lData:=0;
     {アプレットに使うアイコン。}
     hIcon:=LoadIcon(hInst,'MAINICON');
     {アプレットの名前。つまりアイコン名。}
     szName:='Sample';
     {アプレットの説明。コントロールパネルのステータスバーに表示される}
     szInfo:='Sample Applet'; 
     szHelpFile:='';
    end;
   end;

  {アプレットをダブルクリックしたときの処理。}
  CPL_DBLCLK:
   begin
    Application.Initialize;
    Application.CreateForm(TForm1, Form1);
    Application.Run;
   end
  else Result:=0;
 end;
end;

{CPlApplet関数をエクスポートする。}
{(注)必ず「CPlAppet」と綴ること。「CPL」「Cpl」などはだめ。}
exports
 CPlApplet;

begin
  //Application.Initialize;
  //Application.CreateForm(TForm1, Form1);
  //Application.Run;
end.

目次へ戻る


・システムカラーを得る/設定する。

 ウィンドウタイトルバーの色やメニューの色などのシステムカラーを得るには「GetSysColor」を使います。GetSysColorで得られる色の値はRGB値なので、Delphiで使われるTColor型に型キャストします。例えばTShapeコンポーネントをアクティブウィンドウのタイトルバーの色にしてみましょう。
procedure TForm1.Button1Click(Sender: TObject);
begin
 Shape1.Brush.Color:=TColor(GetSysColor(COLOR_ACTIVECAPTION));
end;
 次にシステムカラーを設定しましょう。これには「SetSysColors」を使います。例としてデスクトップの色を変えてみましょう。
procedure TForm1.Button1Click(Sender: TObject);
var
 ColorIndex,Color:integer;
begin
 ColorIndex:=COLOR_BACKGROUND;
 Color:=0; //黒にする
 SetSysColors(2,ColorIndex,Color);
end;
おまけ:上の例で Color を -1 にすると、デスクトップにあるアイコンの文字背景色を透明に出来ます。

目次へ戻る


・システムアイコンをリフレッシュする。

 フォルダなどのシステムアイコンを変更した後で再起動せずにアイコンをリフレッシュするには、いったんアイコンの大きさを変更してまた元に戻す、という手法を使います。Tweak UIでも使われている方法です。アイコンの大きさを変えるには、まずアイコンの大きさをレジストリに登録し、次に「SystemParametersInfo」で大きさの変更を反映させています。
uses Windows....Registry; //Registry を追加

(省略)

{システムアイコン更新}
procedure RefreshSysIcon;
var
 Reg: TRegistry;
 IconMetrics: TIconMetrics;
 SizeStr:string;
 Size:integer;
begin
 Reg:=TRegistry.Create; //レジストリからアイコンの大きさを取得
 try
  Reg.OpenKey('Control Panel\Desktop\WindowMetrics', False);
  SizeStr:=Reg.ReadString('Shell Icon Size');
  try
   Size:=StrToInt(SizeStr);
  except
   Size:=32;
  end;

  Reg.WriteString('Shell Icon Size', IntToStr(Size-1)); //1ドット小さくする
  IconMetrics.cbSize := SizeOf(TIconMetrics);
  SystemParametersInfo(SPI_GETICONMETRICS,0,@IconMetrics,0);
  SystemParametersInfo(SPI_SETICONMETRICS,0,@IconMetrics,
                       SPIF_UPDATEINIFILE or SPIF_SENDWININICHANGE);

  Reg.WriteString('Shell Icon Size', IntToStr(Size)); //元に戻す
  IconMetrics.cbSize := SizeOf(TIconMetrics);
  SystemParametersInfo(SPI_GETICONMETRICS,0,@IconMetrics,0);
  SystemParametersInfo(SPI_SETICONMETRICS,0,@IconMetrics,
                       SPIF_UPDATEINIFILE or SPIF_SENDWININICHANGE);

 finally
  Reg.Free;
 end;
end;

目次へ戻る


・システムリソースを取得する。

 Windowsのバージョンに表示されるようなシステム・USER・GDIリソースを取得するには「GetFreeSystemResources」APIを使います。GetFreeSystemResources 関数は,システムリソースの中の空きメモリ空間の割合 (百分率) を返します。しかし、これは16ビットAPIなため、Delphi2/3からは直接使えません。そのため、「ThunkDown」という手法を使います。ただ、サンクダウンは私もよく分かってないので、サンプルソースをここから持っていって下さい。使い方は、

uses Windows .... FreeRes; {uses節に追加}
.
.
(略)
.
.
procedure TForm1.Button1Click(Sender: TObject);
begin
 if IsNT then  //NTの場合は退去。
  Exit;

 Label1.Caption:=Format('System: %d',[SystemResource]);
 Label2.Caption:=Format('GDI: %d',[GdiResource]);
 Label3.Caption:=Format('User: %d',[UserResource]);
end;

目次へ戻る


・「ファイルの検索」ダイアログを表示する。

 エクスプローラやスタートメニューから呼び出す「ファイルの検索」ダイアログを表示するにはダイナミックデータ交換(DDE)を使います。データ交換を行うアプリケーションはエクスプローラ(Explorer.exe)です。この場合、エクスプローラを「サーバー」、あなたのアプリケーションを「クライアント」と呼びます。クライアントはサーバーに対して作業を命令でき、サーバーは命令を実行します。作成には、まず System タブにある「TDdeClientConv」コンポーネントをフォームに乗せます。そして以下のコードを書いて下さい。
procedure TForm1.Button1Click(Sender: TObject);
var
 Macro:string;
begin
 DdeClientConv1.SetLink('Folders','AppProperties');
 DdeClientConv1.ServiceApplication:='Explorer';
 DdeClientConv1.OpenLink;
 Macro := Format('[FindFolder("%S")]', ['D:\Delphi 3']);
 DdeClientConv1.ExecuteMacro(PChar(Macro),False);
 DdeClientConv1.CloseLink;
end;
実行すると「D:\Delphi 3」フォルダを探す場所にしてダイアログが開きます。

目次へ戻る


・全てのウィンドウを最小化する

 これをするには「全てのウィンドウハンドルを取得してからそれらウィンドウに最小化のウィンドウメッセージを送る」のが王道ですが、Win95/NT4ではウィンドウズキー(ミ田)とMキーを押すと全てのウィンドウを最小化してくれます。このキーを押す操作をソフトで実現してやれば楽して最小化できるわけです。そのために使うAPIが「Keybd_event」です。
procedure TForm1.Button1Click(Sender: TObject);
begin
  Keybd_event(VK_LWIN, 0, 0, 0);  //Windowsキーを押す
  Keybd_event(Byte('M'), 0, 0, 0);  //Mキーを押す
  Keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, 0);  //Windowsキーを離す
end;
第一引数に押したいキー(仮想キーコード)を指定します。ShiftキーはVK_SHIFT、CtrlキーはVK_CONTROL、ALTキーはVK_MENUです。これらのキーは「押しながら」操作するので、キーを離す操作が必要です。第三引数に「KEYEVENTF_KEYUP」を指定するとキーを離したことになります。また、アルファベットや数字は Byte型で指定します。本当は、VK_A とか VK_1 があるのですが、なぜかDelphiはわざと使えなくしています。その他の仮想キーコードは、Windows.pas に書いてあるのでソースコードを持ってる場合は覗いてみて下さい。

目次へ戻る


・各種のシェルフォルダを得る(APIバージョン)

 お気に入りフォルダ・My Documentフォルダなどのシェルフォルダを得るには、シェルAPIの「SHGetSpecialFolderLocation」を使います。
例としてお気に入りフォルダを得てみましょう。

uses ShlObj; //追加

{お気に入りフォルダ取得}
function GetFavoritesPath: string;
var
 pidlPath: PItemIDList;
 Path: string;
begin
 SHGetSpecialFolderLocation(Application.Handle, CSIDL_FAVORITES, pidlPath);
 SetLength(Path, MAX_PATH);
 SHGetPathFromIDList(pidlPath, PChar(Path));
 Result:=Path;
end;
「SHGetSpecialFolderLocation」の第2引数にいろいろな定数を入れてやると他のフォルダも取得できます。

CSIDL_BITBUCKETごみ箱
CSIDL_DESKTOPDIRECTORYデスクトップ
CSIDL_FAVORITESお気に入り
CSIDL_PERSONALマイドキュメント
CSIDL_PROGRAMSProgram Files
CSIDL_RECENT最近使ったファイル
CSIDL_SENDTO送る
CSIDL_STARTMENUスタートメニュー
CCSIDL_STARTUPスタートアップ

他にもありますので、Win32ヘルプか、ShlObj.pasを見てください。
また、取得には
レジストリバージョンもあります。

目次へ戻る


・サブフォルダまで一気にコピー・削除する

サブフォルダも含めて一気にコピー・削除するには、シェルAPIの「SHFileOperation」を使います。
例として削除の方法を示します。

uses ShellAPI; //追加

procedure DeleteFolders(FolderName: string);
var
 foStruct:  TSHFileOpStruct;
begin
 with foStruct do
 begin
  wnd:=Handle;
  wFunc:=FO_DELETE;  //削除フラグ(コピーの場合はFO_COPY)
  pFrom:=PChar(FolderName+#0);  //削除するフォルダ
  pTo:=#0;
  fFlags:=FOF_SILENT or FOF_NOCONFIRMATION;  //削除ダイアログ非表示
  fAnyOperationsAborted:=False;
  hNameMappings:=nil;
  lpszProgressTitle:=nil;
 end;

 if SHFileOperation(foStruct)<>0 then  //削除
  ShowMessage('フォルダの削除に失敗しました。');
end;

目次へ戻る


・フォルダの参照ダイアログを表示させる

フォルダの参照ダイアログを表示するには「SHBrowseForFolder」を使います。
Delphi4でしたら「SelectDirectory」でも出せますが、バグがあるのと機能が弱いので出来がよくないです。

uses Windows, ..... ShlObj, ActiveX;  //追加


{フォルダの参照ダイアログ用コールバック関数}
function BrowseCallback(hWnd: HWND; uMsg: UINT; lParam: LPARAM; lpData: LPARAM): integer; stdcall; export;

var
  Form1: TForm1;

implementation

{$R *.DFM}

{フォルダの参照ダイアログ用コールバック関数}
function BrowseCallback(hWnd: HWND; uMsg: UINT; lParam: LPARAM; lpData: LPARAM): integer;
var
 PathName: array[0..MAX_PATH] of Char;
begin
 Result:=0;

 case uMsg of
  {最初に表示するフォルダ}
  BFFM_INITIALIZED:
   SendMessage(hwnd,BFFM_SETSELECTION,1,LongInt(lpData));
  {フォルダ参照時にパスを表示}
  BFFM_SELCHANGED:
   begin
    SHGetPathFromIDList(PItemIDList(lParam), PathName);
    SendMessage(hWnd, BFFM_SETSTATUSTEXT, 0, LongInt(@PathName));
   end;
 end;
end;

{フォルダの参照ダイアログを開く}
function OpenFolderDialog(var FolderPath: string): boolean;
var
 Malloc: IMalloc;
 BrowseInfo: TBrowseInfo;
 DisplayPath: array[0..MAX_PATH] of Char;
 IDList: PItemIdList;
 Buffer,pFolderPath: PChar;
begin
 Result:=False;

 if Succeeded(SHGetMalloc(Malloc)) then  //IMallocのポインタを取得できたら
  begin
   pFolderPath:=PChar(FolderPath);       //初期フォルダ指定用

   {BrowseInfo構造体を初期化}
   with BrowseInfo do
   begin
    hwndOwner      := GetActiveWindow();  //D4のSelectDirectoryでは、ここがApplication.Handleになっているので、表示位置がおかしくなる
    pidlRoot       := nil;
    pszDisplayName := DisplayPath;                             //表示名用バッファ
    lpszTitle      := 'フォルダを選択してください。';
    ulFlags        := BIF_RETURNONLYFSDIRS or BIF_STATUSTEXT;  //通常のフォルダのみ参照可能(特殊フォルダは選択できない)
    lpfn           := @BrowseCallback;                         //コールバック関数指定
    lParam         := LongInt(pFolderPath);                    //初期フォルダ指定
    iImage         := 0;
   end;

   IDlist := SHBrowseForFolder(BrowseInfo); //フォルダ参照ダイアログを表示
   if IDlist<>nil then                      //値が返ってきたら
    begin
     Buffer:=Malloc.Alloc(MAX_PATH);        //フォルダパス取得用バッファ
     try
      SHGetPathFromIDList(IDlist, Buffer);  //フォルダパスを取得
      FolderPath:=string(Buffer);
     finally
      Malloc.Free(Buffer);
     end;

     Malloc.Free(IDlist);
     Result:=True;
    end;
  end;
end;

{フォルダの参照}
procedure TForm1.Button1Click(Sender: TObject);
var
 Dir: string;
begin
 Dir:=Edit1.Text;
 if OpenFolderDialog(Dir) then
  Edit1.Text:=Dir;
end;
目次へ戻る

一番上へ
Delphi壁の穴 Delphiを覗く レジストリを覗く アプリケーションを覗く

リンクは日本Delphi振興会から張ってください。