システム/DLL


DLLのエクスポート関数を表示する
デスクトップ上のアイコンの文字背景を透明にする
ドライブレターの一覧を取得する
標準入出力をパイプする
テンポラリファイルのファイル名を得る
Delphi 5のGetDiskFreeSpaceEx
システムアイコンの取得
mutexを使った二重起動の防止
標準のバージョン情報ダイアログ
マルチメディアタイマ
タスクトレイにアイコンを追加する
Windows2000の透明ウィンドウ


DLLのエクスポート関数を表示する

このプログラムを使えば, DLLのエクスポート関数の一覧を見ることができる. と言っても,Borland系言語製品に付属しているTDUMP.EXEの標準入出力をパイプしているだけ. 当然TDUMP.EXEが無いと動作しない.


デスクトップ上のアイコンの文字背景を透明にする

タイトルのことを実現するには,以下のようにする.

  procedure TMainForm.SetBackErase;
  var
    TheIndex, TheColor: Integer;
  begin
     TheIndex := COLOR_BACKGROUND;
     TheColor := -1;
     SetSysColors( 2, TheIndex, TheColor );
  end;

透明になったことでテキストが見にくくなるばあいは,LVM_SETTEXTCOLORメッセージを デスクトップに送る.

   SendMessage( HWnd, LVM_SETTEXTCOLOR, 0, AColor );

このメッセージのWPARAMおよびLPARAMはそれぞれ0および設定したい色である. デスクトップのハンドルは次のようにして取得する.

  function TMainForm.GetDesktopHWnd;
  var
    HWnd: THandle;
  begin
     HWnd := FindWindow( 'Progman', nil );
     HWnd := FindWindowEx( HWnd, 0, 'SHELLDLL_DefView', nil );
     HWnd := FindWindowEx( HWnd, 0, 'SysListView32', nil );
     Result := HWnd;
  end;

プログラムはここからダウンロードできる.


ドライブレターの一覧を取得する

タイトルのことを実現するにはGetLogicalDrives関数を用いる. この関数が戻すDWORD値は,現在使用可能なドライブの順序値に相当するビットがマスクされている. もし,A,C,Dドライブが使用可能なら,1番目と3番目と4番目のビットが1で,あとのビットは0になっている.

したがって,戻り値のビットを順に調べていけばよいのであるが,このような操作はPascalの集合型を 使うと大変要領良く行える. 以下のGetDriveString関数は,例えばA,C,D,Eドライブが使用可能なら,"ACDE"という文字列を返す.

function GetDriveString: String;
var
  R, Index: Integer;
  DriveBits: set of 0..25;
begin
   Result := '';
   R := GetLogicalDrives;
   if R <> 0 then
   begin
      Integer( DriveBits ) := R;
      for Index := 0 to 25 do
         if Index in DriveBits then
            Result := Result + Char( Index + Ord( 'A' ) );
   end;
end;


標準入出力をパイプする

以下の関数を使えばコンソールプログラムの標準出力をString型変数で得ることができる. どうしてそうなるのかの詳細は,実のところ良くわかっていない.

function RedirectExec( CmdLine: String;
             var StdIn, StdOut, StdErr: String ): Boolean;
var
  hPipe1Read, hPipe1Write: THandle;
  hPipe2Read, hPipe2Write: THandle;
  hPipe3Read, hPipe3Write: THandle;
  hStdin, hStdOut, hStdErr: THandle;
  SI: TStartupInfo;
  PI: TProcessInformation;
  Buf: array[ 0..1024 ] of Char;
  ReadCount: Integer;

begin
 // Make StdOut Pipe
   CreatePipe( hPipe1Read, hPipe1Write, nil, 0 );
   DuplicateHandle( GetCurrentProcess, hPipe1Write,
         GetCurrentProcess, @hStdOut, 0, True,
         DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS );

 // Make StdIn Pipe
   CreatePipe( hPipe2Read, hPipe2Write, nil, 0 );
   DuplicateHandle( GetCurrentProcess, hPipe2Read,
         GetCurrentProcess, @hStdIn, 0, True,
         DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS );

 // Make StdErr Pipe
   CreatePipe( hPipe3Read, hPipe3Write, nil, 0 );
   DuplicateHandle( GetCurrentProcess, hPipe3Write,
         GetCurrentProcess, @hStdErr,0, True,
         DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS );

 // Intialize StartupInfo
   FillChar( SI, SizeOf( SI ), 0 );
   SI.cb := SizeOf( SI );
   SI.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
   SI.wShowWindow := SW_HIDE;
   SI.hStdInput  := hStdIn;
   SI.hStdOutput  := hStdOut;
   SI.hStdError  := hStdErr;

   StdIn := StdIn + #$1a; // EOF
   StdOut := '';
   StdErr := '';
   try
     Result := CreateProcess( nil, PChar( CmdLine ),nil, nil, True,
                          NORMAL_PRIORITY_CLASS or CREATE_NO_WINDOW,
                          nil, nil, SI, PI );
     CloseHandle( PI.hThread );

     CloseHandle( hStdOut ); // Close StdOutPut
     Closehandle( hStdIn );  // Close StdInPut
     CloseHandle( hStdErr ); // Close StdError

      // Write StdInPut
     FileWrite( hPipe2Write, PChar( StdIn )^, Length( StdIn ) );

      // Read StdOutPut
     repeat
        Application.ProcessMessages;
        FillChar( Buf, SizeOf( Buf ), 0 );
        ReadCount := FileRead( hPipe1Read, Buf, SizeOf( Buf ) - 1 );
        StdOut := StdOut + Buf;
     until ReadCount <<= 0;

      // Read StdError
     repeat
         Application.ProcessMessages;
         FillChar( Buf, SizeOf( Buf ), 0 );
         ReadCount := FileRead( hPipe3Read,Buf, SizeOf( Buf ) - 1 );
         StdErr := StdErr + Buf;
     until ReadCount <= 0;

     WaitForSingleObject( PI.hProcess, INFINITE );
     CloseHandle( PI.hProcess );
   finally
     // CloseHandle( hStdOut ); // Close StdOutPut
     // Closehandle( hStdIn );  // Close StdInPut
     // CloseHandle( hStdErr ); // Close StdError
      CloseHandle( hPipe1Read );    // Close StdOut Pipe
      CloseHandle( hPipe2Write );  // Close StdIn Pipe
      Closehandle( hPipe3Read );    // Close StdError Pipe
   end;
end;

テンポラリファイルのファイル名を得る

タイトルのことを実現するには,まずGetTempPath関数を使ってテンポラリディレクトリの パスを取得し,次にGetTempFileName関数を使って一意的なファイル名を作成すればよい. 例えば以下のような関数を用いる.

function GetTemporaryFileName: String;
var
  Path: array[ 0..255 ] of Char;
  TempFile: array[ 0..255 ] of Char;
begin
   GetTempPath( SizeOf( Path ), Path );
   GetTempFileName( Path, '$', 0, TempFile );
   Result := TempFile;
end;

GetTempFileName関数の第2引数にはテンポラリファイル名に付けるプレフィクスを指定する. 第3引数に0を指定すれば,システムは現在時刻から数値を作成し,ファイル名に付加する.


Delphi 5のGetDiskFreeSpaceEx

APIのGetDiskFreeSpaceEx関数を使えば2GB超のドライブ容量が取得できるが,この関数は Windows 95 OSR2以降かNT4.0以降でしかサポートされていない. これ以前のOSでは,旧来のGetDiskFreeSpace関数を使って1クラスタあたりのセクタ数, 1セクタあたりのバイト数,空きクラスタ数および総クラスタ数を取得し,これらを乗ずることで 空き容量および総容量を求めるが,2GB超のドライブ容量は正しく求められない. このようなことのため,ディスク容量を取得する場合は前もってOSのバージョンを調べておく必要がある.

しかしながら,Delphi 5ではGetDiskFreeSpaceExが関数ポインタとしてSysUtilsユニット に宣言されており,ユニットの初期化時に,もしAPIのGetDiskFreeSpaceEx関数がサポート されているならそのアドレスが代入され,さもなければDelphiの内部関数のアドレスが代入されるように なっている. Delphiの内部関数はGetDiskFreeSpace関数を呼び出しているだけなので,依然2GBの制限は残っているが, 少なくともOSのバージョンを気にかけずにGetDiskFreeSpaceExを呼び出すことができる.

以下はGetDiskFreeSpaceExを使用した例である. エクスプローラに表示される値と同じ値がリストボックスに表示される.

procedure TForm1.Button1Click(Sender: TObject);
var
  Avail, Total, Free: Int64;
begin
   GetDiskFreeSpaceEx( 'D:\', Avail, Total, @Free );
   ListBox1.Items.Add( Format( 'Avail = %8.3f', [ Avail / IntPower( 1024, 3 ) ] ) );
   ListBox1.Items.Add( Format( 'Total = %8.3f', [ Total / IntPower( 1024, 3 )] ) );
   ListBox1.Items.Add( Format( 'Free  = %8.3f', [ Free / IntPower( 1024, 3 ) ] ) );
end;


システムアイコンの取得

APIのSHGetFileInfo関数を使えば,ファイル,フォルダおよびドライブに関連付けられた システムアイコンのハンドルを取得することができる. 取得したアイコンを描画する場合は,DrawIconEx関数を使う.DrawIcon関数は使わない.


mutexを使った二重起動の防止

Win32環境でアプリケーションの二重起動を防止するには,mutexを使う方法が最も簡単である. アプリケーションを最初に起動するときに ユニークな名前のmutexオブジェクトを作成しておき, 次に起動する際にその名前のmutexが存在するかを調べ,あれば起動を中止する.

以下はプロジェクトのソースコードであり,すでにmutexオブジェクトが存在すれば, すなわち,すでにアプリケーションが起動済みであれば,そのアプリケーションを アクティブにし,アイコン化されていれば元の大きさに復元する. 起動済みアプリケーションのハンドルを検索する際には,クラス名が必要である. Delphiで作成したアプリケーションでは,メインフォームの名前にTをつけたものが アプリケーションのクラス名になる.

var
  hMutex, Wnd, AppWnd: THandle;

begin
  hMutex := OpenMutex( MUTEX_ALL_ACCESS, False, 'hPrevCalendar' );
  if hMutex = 0 then
  begin
     hMutex := CreateMutex( nil, False, 'hPrevCalendar' );
     Application.Initialize;
     Application.CreateForm(TCalendarMainForm, CalendarMainForm);
     Application.CreateForm(TMemoDlg, MemoDlg);
     Application.Run;
     ReleaseMutex( hMutex );
  end
  else begin
    Wnd := FindWindow( 'TCalendarMainForm', nil );
    if Wnd <> 0 then
    begin
      AppWnd := GetWindowLong( Wnd, GWL_HWNDPARENT );
      ShowWindow( AppWnd, SW_SHOW );
      ShowWindow( Wnd, SW_SHOW );
      if IsIconic( AppWnd ) then
      begin
         SendMessage( AppWnd, WM_SYSCOMMAND, SC_RESTORE, -1 );
      end;
      SetForegroundWindow( AppWnd );
    end;
  end;
end.


標準のバージョン情報ダイアログ

Windows標準のバージョン情報ダイアログを表示するには,ShellAbout関数を使う. この関数を使うときは,ShellAPIユニットをuses節に追加する.

procedure TForm1.Button1Click(Sender: TObject);
begin
   ShellAbout( Handle, '九州大学',
                       '工学研究院付属環境科学システム研究センター',
                       Application.Icon.Handle );          
end;

このコードをWindows2000で実行した場合,下の図のようになる.


マルチメディアタイマ

ここ に移動した.


タスクトレイにアイコンを追加する

タイトルのことを実現するにはShell_NotifyIcon APIを用いる. このAPIのプロトタイプは以下の通り.

function Shell_NotifyIcon(dwMessage: DWORD; lpData: PNotifyIconData): BOOL; stdcall;

dwMessageはシステムに送るメッセージで,アイコンをトレイに追加するときはNIM_ADD, トレイから削除するときはNIM_DELETE,アイコンを変更するときはNIM_MODIFYをそれぞれ送る. lpDataはアイコンの情報を格納したTNotifyIconData構造体へのポインタである. TNotifyIconData構造体と等価な_NOTIFYICONDATAA構造体は以下のように定義されている.

 _NOTIFYICONDATAA = record
    cbSize: DWORD;
    Wnd: HWND;
    uID: UINT;
    uFlags: UINT;
    uCallbackMessage: UINT;
    hIcon: HICON;
    szTip: array [0..63] of AnsiChar;
 end;

cbSizeはこの構造体のサイズを指定する.Wndはシステムからのメッセージを受け取る ウィンドウのハンドルであり,uIDはアプリケーション定義の アイコンIDである.アイコンを1個しか使わない場合,このメンバーの値は何でもよい. uFlagsはhIcon,uCallbackMessageおよびszTipの各メンバーが有効か否かを 指定するものである(使い方は以下の例を参照). uCallbackMessageはシステムから送られるメッセージのIDであり,アプリケーション定義の メッセージIDを指定する. hIconはトレイに追加するアイコンのハンドルであり, szTipはマウスカーソルを近づけたときに表示されるチップヘルプの文字列である.

アプリケーションのアイコンをトレイに追加するには, メインフォームのOnCreateイベントに以下のように書く.

const
  WM_TRAYICONCLICKED = WM_USER + 1000;

var
  IconData: TNotifyIconData;

procedure TMainForm.FormCreate(Sender: TObject);
begin
   with IconData do
   begin
      cbSize := SizeOf( TNotifyIconData );
      Wnd := Self.Handle;
      uID := 1;
      uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
      uCallbackMessage := WM_TRAYICONCLICKED;
      hIcon := Application.Icon.Handle;
      szTip := 'Use Shell_NotifyIcon';
   end;
   Shell_NotifyIcon( NIM_ADD, @IconData );
end;

上の例では,トレイのアイコンがクリックされたときに メインフォームにWM_TRAYICONCLICKEDメッセージが送られてくる. アプリケーションの終了時にトレイからアイコンを削除するには,メインフォームのOnCloseイベントに

procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
   Shell_NotifyIcon( NIM_DELETE, @IconData );
end;

のように書く. トレイのアイコンが左クリックされたときにメニューを表示するには, メインフォームにPopupMenuを追加し, WM_TRAYICONCLICKEDメッセージに対するメインフォームのメッセージハンドラを

procedure TMainForm.WMTrayIconClicked( var Mes: TMessage );
var
  Pos: TPoint;
begin
   if Mes.LParam = WM_LBUTTONDOWN then
   begin
      GetCursorPos( Pos );
      PopupMenu.Popup( Pos.X, Pos.Y );
   end;
end;

のように書けばよい.以下は実行例である(カキ氷のアイコン).

なお,この例ではメニューのオーナードローを使っている. やり方についてはここにまとめた.


Windows2000の透明ウィンドウ

Windows2000で新たに追加されたSetLayeredWindowAttributesを使えば簡単に(半)透明ウィンドウを 作成することができる. このAPIのプロトタイプは以下の通り.

function SetLayeredWindowAttributes( hwnd: HWND; crKey: COLORREF;
         bAlpha: BYTE; dwFlags: DWORD ): BOOL; stdcall;

hwndは透明にするウィンドウのハンドル,crKeyは透過色,bAlphaは 透過度合(0: 完全透過,255: 完全不透過)である. また,dwFlagsはアクションフラグであり,

LWA_COLORKEY:透過色としてcrKeyを使う.
LWA_ALPHA:bAlphaによって透過度合を指定する.

を指定できる. SetLayeredWindowAttributesを使うためにはウィンドウがWS_EX_LAYEREDスタイルを 持っている必要があるため,フォームのCreateParamsメソッドをオーバーライドする.

なお,ここで述べたAPIおよび定数はDelphi5のwindows.pasには宣言されていないので, 自前で宣言する必要がある.

以下は半透明のウィンドウを作成した例である.

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
  protected
  public
    procedure CreateParams( var Params: TCreateParams ); override;
  end;

var
  Form1: TForm1;

function SetLayeredWindowAttributes( hwnd: HWND; crKey: COLORREF;
         bAlpha: BYTE; dwFlags: DWORD ): BOOL; stdcall;

implementation

{$R *.DFM}

const
  WS_EX_LAYERED = $80000;
  LWA_COLORKEY  = 1;
  LWA_ALPHA     = 2;

function SetLayeredWindowAttributes; 
         external 'user32.dll' name 'SetLayeredWindowAttributes';

procedure TForm1.CreateParams( var Params: TCreateParams );
begin
   inherited CreateParams( Params );
   Params.Exstyle := Params.Exstyle or WS_EX_LAYERED;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
    SetLayeredWindowAttributes( Handle, ColorToRGB( clWindow ),
          200, LWA_ALPHA or LWA_COLORKEY );
end;

実行結果は以下の通り.


お問い合わせはメールにて: akasaka@klc.ac.jp

戻る
SEO [PR] 爆速!無料ブログ 無料ホームページ開設 無料ライブ放送