日本Delphi振興会 | Delphiはじめて物語 | Delphiつれづれ記 | Delphiリンク互助会 | Delphiアクセサリ |
Delphi壁の穴
その二:システムを覗く
ここではWindowsシステムをDelphiで使う方法をご紹介します。
目次
しかし、ちょっと難しいことや気の利いたことをしたい場合、必ずと言っていいほどWindows APIを直接使う必要に迫られます。また、Delphiがカプセル化したものを使うよりもWindows APIを直接使ったほうが、より思い通りの結果が得られることもあります。それは、やはりWindows APIがWindowsを直接的に操れるから、という理由に尽きるでしょう。特にシステム関係を操作するにはWindows APIの利用は必須です。
私もそうですが、C言語を全く知らず、BASICやPASCALからDelphiにやって来た人にとっては、C言語を念頭に置いて作られたWindows APIの文法はちんぷんかんぷんだと思います。以下では、私が体験的に得たWindows APIの読みかえを一覧にしました。
例:MessageBox(Handle, PChar(Edit1.Text), 'メッセージ', MB_OK);
日付だけなら「DateToStr」を、時刻だけなら「TimeToStr」を使います。
また、いろいろな形式で表示させたいならば、FormatDateTimeを使います。
のようにします。他にも、曜日、元号、AM/PM表示などが可能です。
そうすると「577045466」のような単なる数字の羅列がでてきます。コンピュータ内部ではこのような数字の羅列でファイルの日付と時刻を一括して管理しているのです。
次に、これをDelphiで扱う日付時刻型(TDateTime型)に変換します。
最後に、人間が理解できる文字列に変換します。
上では、DateTimeToStrを使いましたが、FormatDateTimeを使えばいろいろな形式で表示できます。
流れとしては
で、ICON1をそのままの名前にしてはいけません。アイコンリソースの並ぶ順番は、アルファベット順になります。アプリケーションのデフォルトアイコンは、MAINICON という名前になっているため、このままだとICON1がMAINICONの前にきてしまい、アプリケーションのアイコンがICON1で表示されてしまいます。メニューから[リソース-名前の変更]を選んでMAINICONよりも後ろに来るような名前に変更します。
次に、リソースファイルに名前を付けて保存します。リソースファイルの名前をプロジェクトと同じ名前にしてはいけません。エクスプローラなどで見ればわかりますが、project1.dprにはproject1.resが自動的に作成されます。このリソースファイルはDelphiが管理しているため、勝手に中身をいじってはいけないのです(いじってもいいですが、いじった結果は全く反映されません)。そのため、新しく作ったリソースファイルには新しい名前を付けてください。ユニット名と同じにすることはOKです。
最後に、このリソースファイルをアプリケーションに埋め込みます。リソースファイル名をexample.resとすれば、
{$R}は、リソースを実行ファイルに埋め込むためのコンパイル指令です。ここにリソースファイル名を指定します。パスを指定しなければ、ユニットファイルがあるフォルダを検索します。
uses節をWindowsだけに変更します。DLLは単独では実行できないので、メニューからコンパイルを選んで、DLLにします。
これで、アイコンだけのDLLが出来ました。ちなみにWindows95に付属のアイコンDLLは、Shell32.dll、Moricons.dll、Pifmgr.dll などがあります。
ExtractIconで指定する第三引数は、アイコンの番号です。0は1つ目、1は2つ目のアイコンを指します。
例えば、「C:\Delphi 2.0\Readme.txt」を「C:\Delphi」にコピーするときは、
のようにします。第三引数は、コピー先に同じファイルがあった場合、上書きするか否かを決定します。Trueならば上書きし、Falseならば上書きしません。
また、コピー/移動後にリネームさせることもできます。
例えば、「C:\Delphi 2.0\Readme.txt」を「C:\Delphi」にコピーして「Read.txt」にリネームするには、
のようにします。第二引数のコピー先にリネームするファイル名まで書いてしまうところが、CopyFileとの違いです。
これらの関数は、成功すると True を、失敗すると False を返します。
静的インポートはアプリケーションの起動が遅くなる代わりに、簡単にコードが書けます。また、DLLを頻繁に使うなら静的インポートがいいでしょう。動的インポートはコードが複雑になりますが、起動に影響を与えません。必要なときにだけ使えればいいのなら動的インポートがいいでしょう。
stdcallは、関数の呼び出し方の一種です。Delphi2/3では、stdcallが一般的です。Delphi1の場合は指定する必要はありません。
動的インポートはこのように長いコードを書いてやっと関数が使用できます。
ただ、実行画面を見ればわかると思いますが、ソフト名の前に「Microsoft」が付くので、あたかもマイクロソフトの製品であるかのようになってしまうところが欠点です。
他にもありますので、Win32ヘルプか、ShlObj.pasを見てください。
Delphi壁の穴
Delphiを覗く
レジストリを覗く
アプリケーションを覗く
一番上へ
・Windows APIを知る。
Windows APIは、Windowsを操るために使われる一般的な命令と考えればいいでしょう。しかし、Windows APIはC言語を念頭に置いて作られているため、PASCALを源流とするDelphiにはなじみにくく、使いづらいものです。Delphiはその使いづらいWindows APIをDelphi独自の「プロパティ」「メソッド」「イベント」「手続き」「関数」にカプセル化する事によって簡単に使えるようにしているのです。
Windows API Delphi 解説
NULLで終わる文字列(を指すポインタ)
lptstrPChar型 Delphiで使われる文字列型「String」はDelphi独自のもので、Windows APIではNULLで終わる文字列というものを一般的に使います。これを見た場合は、「PChar(Edit1.Text)」と型変換するか、直接に「'文字列'」のように「'」でくくって指定します。また、単に文字列と書いてあっても、NULLで終わる文字列のことと見て間違いありません。
構造体(を指すポインタ) レコード型 構造体という言葉が出てきた場合は、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/UINT Integer 全部まとめてIntegerと宣言しても大丈夫です。
NULL nil 「このパラメータにNULLを指定しなければなりません。」というようなフレーズを見たら、Delphiでは「nil」を指定します。
・現在の日付と時刻を得る。
一般的な表示(97/03/04 3:05:20)をさせるには、DateTimeToStrを使います。
Label1.Caption:=DateTimeToStr(Now);
例えば「1997年3月4日 3時5分20秒」を表示するには、
Label1.Caption:=FormatDateTime('yyyy年m月d日 h時m分s秒',Now);
・ファイルの日付と時刻を得る(簡易版)。
まずは、FileAgeを使います。例えば作成した実行ファイルの日付と時刻を得てみましょう。
Label1.Caption:=IntToStr(FileAge(ParamStr(0)));
数字が大きいほど新しく作られたファイルになります。このため、どちらのファイルが新しく作られたのかを調べるだけなら、FileAgeだけで十分出来ます。
var
DateTime:TDateTime;
begin
DateTime:=FileDateToDateTime(FileAge(ParamStr(0)));
end;
var
DateTime:TDateTime;
begin
DateTime:=FileDateToDateTime(FileAge(ParamStr(0)));
Label1.Caption:=DateTimeToStr(DateTime);
end;
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;
となります。
・パソコンの日付と時刻を変える。
日付と時刻を変えるには、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にアイコンを埋め込む。
まず最初に ImageEditor で リソースファイル(res)を新規作成します。次に、メニューから[リソース-新規-アイコン]を選びます。そうするとICON1が作成されるので、ダブルクリックしてアイコンを描いていきます。
unit Unit1;
interface
uses
Windows,....;
type
TForm1 = class(TForm)
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{$R example.res} {<--- ここに追加}
リソースファイルを作る作業は、1.で説明したことと同じですが、埋め込む方法が違います。
Delphiのメニューから[新規作成-ダイナミックリンクライブラリ]を選びます。次に以下のようにコードを変更します。
library Project1;
uses
Windows;
{$R example.res}
begin
end.
・EXEやDLLからアイコンを取り出す。
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;
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つ目のアイコンが取得できます。
OS Windows95初期版 Windows95OSR2 Windows98/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;
CopyFile('C:\Delphi 2.0\Readme.txt','C:\Delphi',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;
CopyRenFile('C:\Delphi 2.0\Readme.txt','C:\Delphi\Read.txt',False);
・DLLの利用方法。
DLLを使うには、静的インポートと動的インポートがあります。静的インポートはアプリケーションを立ち上げるときにDLLをくっつける(インポートする)方法です。このため、起動時に必ず目的のDLLがなくてはなりません。動的インポートは、DLLを使うときになったらくっつける方法です。このため、目的のDLLがなくてもアプリケーションを起動できます。
implementation部のすぐ下に、使用するDLLのファイル名と関数名を指定します。例えば、example.dllの中にある関数 ExampleFunc(P: PChar; I: integer): PChar を使うには、
implementation
{$R *.DFM}
function ExampleFunc(P:PChar;I:integer):PChar;stdcall;external 'example.dll';
externalは、どのDLLから関数を呼び出すかを決定します。パスを指定しない場合は、カレントフォルダ、Windowsフォルダ、Systemフォルダが検索されます。
必要に応じて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行までです。
・タイトルバーを点滅させる。
ウィンドウのタイトルバーを点滅させるには、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;
・コントロールパネルを作る。
コントロールパネル(*.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_PROGRAMS Program Files
CSIDL_RECENT 最近使ったファイル
CSIDL_SENDTO 送る
CSIDL_STARTMENU スタートメニュー
CCSIDL_STARTUP スタートアップ
また、取得にはレジストリバージョンもあります。
・サブフォルダまで一気にコピー・削除する
サブフォルダも含めて一気にコピー・削除するには、シェル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振興会から張ってください。