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

Delphi壁の穴

その一:Delphiを覗く

ここではDelphiを使うときに問題になる事柄やコーディングのTIPSをご紹介します。

目次

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

・おすすめヘルプ

DelphiのCD-ROMには役に立つヘルプが入っています。使ってみて下さい。
○Delphi2の場合
  • Api32wh.hlp:Extras\Jphelp
    Delphi2のAPIヘルプは、あろう事か英語で書かれています。これはBorlandが悪いのではなく、Microsoftが日本語化しなかったためです。Api32wh.hlpは、WinNT/Win32用のヘルプですが、Win95と共通する部分が多くあって、しかも日本語で書かれています。
  • Techinfo.hlp:Info\Borland\Techinfo
    英語ですが、Borlandに寄せられた質問とその回答集です。結構たくさんの質問があるので、ひとつぐらいは「お、これは!」と思えるようなものもあるでしょう。
○Delphi3の場合
  • Win32.hlp:Extras\Jphelp
    Delphi3のAPIヘルプは、あろう事か英語で書かれています。これはBorlandが悪いのではなく、Microsoftが日本語化しなかったためです。Win32.hlpは、WinNT/Win32用のヘルプですが、Win95と共通する部分が多くあって、しかも日本語で書かれています。また、Delphi2を持っている場合は上記 Api32wh.hlp を同一フォルダに入れておけば、「グループ」ボタンが使えるようになります。

目次へ戻る


・「アドレス...に対する読み込み違反が起こりました」by Delphi2

 Delphi 2.0を終了させたとき、上記のようなエラーが出ることがあります。これは、Internet Explorer3.0をインストールしていると起こります。これを回避する方法は二通りあります。

ア)\Delphi 2.0\Binに、「_appdiet.exe」がない。

  1. ボーランドのサイトから、「dl201up.lzh」をダウンロードします。
  2. Readme.txtの指示に従って、インストールします。
    これで、Delphiのバージョンは2.01にアップデートされました。
  3. レジストリエディタ(REGEDIT.EXE)を呼び出し、次のレジストリキーを追加してください。

     キー: HKEY_LOCAL_MACHINE\SOFTWARE\Borland\Delphi\2.0
     名前:Debug stub
     値: "_APPDIET.EXE"

     自分でレジストリをいじりたくない方は、Setupプログラムソース(2KB)をダウンロードして下さい。

イ)\Delphi 2.0\Binに、「_appdiet.exe」がある。

 あなたの使っているDelphiのバージョンは、2.01にアップデートされています。上記ア)-3を実行して下さい。

目次へ戻る


・Delphi3で "Ole2" ユニットを使う

 Delphi3でフリーのコンポーネントを使おうとすると "Ole2" ユニットがないと怒られることがあります。Delphi3ではOLEの機能は全て "ActiveX" ユニットへ変わったためです。ではどうするか。実はOle2ユニットは Lib\Delphi2 フォルダの中にあるのですが、ライブラリパスが通っていないためにエラーになってしまったのです。ライブラリパスを通すには、メニューから[ツール-環境オプション-ライブラリ-ライブラリパス]に上記パスを書いておきます。なお、Delphi2のOle2ユニットを流用することは出来ません。

目次へ戻る


・アプリケーションからヘルプを実行させる

  1. ヘルプファイルの指定
     実行ファイル名とヘルプファイル名が同じで、同一の場所にあるならば、
     Application.HelpFile:=ChangeFileExt(ParamStr(0),'.hlp');
    
  2. CNTファイルを使った目次表示
     目次の表示には、Delphi 1.0で、HELP_CONTENTSを使っていましたが、CNTファイルを使うには、HELP_FINDERを使います。
     Application.HelpCommand(HELP_FINDER,0);
    
  3. トピックの検索を表示
     Application.HelpCommand(HELP_KEY,0);
    
  4. 特定のページを表示
     Application.HelpCommand(HELP_CONTEXT,10004);
    
     第二引数に表示したいページのコンテキスト番号を指定します。

  5. ウィンドウの右上にある「?」ボタンを使ったヘルプの表示
     フォームのBorderIconsプロパティで、biMaximizeとbiMinimizeをFalseに、biHelpをTrueにします。
     そうしたら、各コンポーネントにあるHelpContextプロパティに、表示したいヘルプのコンテキスト番号を割り当てます。

  6. 「?」ボタン以外でポップアップヘルプを表示させる
     Application.HelpCommand(HELP_CONTEXTPOPUP,10004);
    

  7. アプリケーションの終了と同時にヘルプを閉じる
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
     Application.HelpCommand(HELP_QUIT,0);
    end;
    

より詳しくは「WinHelp」をキーワードにしてDelphiヘルプで探して下さい。

目次へ戻る


・タスクトレイに常駐させるときフォームとタスクバーを表示させない。

 タスクトレイにアイコンを描画するコンポーネントを手に入れるには、

 常駐させているときは、フォームとタスクバーを表示させないのが普通なので、その方法を以下に示します。

 Application.ShowMainForm:=False; {フォーム非表示}
 ShowWindow(Application.Handle,SW_HIDE); {タスクバー非表示}

 Delphi2では立ち上げたときに一瞬タスクバーが見えてしまいますが、Delphi3では改善されて見えなくなりました。

目次へ戻る


・半角カナを全角カナにしたり、アルファベット小文字を大文字にする。

 Delphi 2.0 CD-ROMにあるJstrm.pas(作:村松 真氏)を使います。場所は、Extras\Programs\Jstrmです。ない場合は作者のホームページからダウンロードして下さい。その中にある関数「JUniCase」は、小文字を大文字にそろえる、全角アルファベットや記号,数字を半角にする、半角カタカナを全角カタカナにする、という機能があります。

 この関数を応用すれば、文字をいろいろと変換できます。以下の例は、全角カタカナをひらがなにします。

function ConvKanaHira( const source: string ): string;
var
  P: PChar;
  i, len: integer;
  wc : WORD;
begin
  P := PChar( source );
  len := length( source );
  i := 1;
  Result := '';
  repeat
    {全角文字ならば}
    if IsDBCSLeadByte( BYTE( p^ ) ) then begin
      wc := ( BYTE(p^) shl 8 ) or Byte( (p + 1)^ );
      case wc of
        {ァ-ミ --> ぁ-み}
        $8340..$837E: Result := Result + #$82+Char(wc-$A1);
        {ム-ワ --> む-わ}
        $8380..$838F: Result := Result + #$82+Char(wc-$A2);
        {ヰ --> うぃ}
        $8390: Result := Result + #$82+#$A4+#$82+#$A1;
        {ヱ --> うぇ}
        $8391: Result := Result + #$82+#$A4+#$82+#$A5;
        {ヲ-ン --> を-ん}
        $8392..$8393: Result := Result + #$82+Char(wc-$A2);
        {ヴ --> う゛}
        $8394: Result := Result + #$82+#$A4+#$81+#$8B;
        else  Result := Result + p^ + (p + 1)^;
      end;
      Inc( p );
      Inc( i );
    end
    {半角文字}
    else begin
      Result := Result + p^;
    end;
    Inc( p );
    Inc( i );
  until i > len;
end;
{#ENDIF}

目次へ戻る


・Posを使って大文字と小文字の区別なしに文字列検索。

 Posは大文字と小文字を区別して文字列を検索します。大文字と小文字を区別しないで検索させるには、簡単にいえば、大文字を小文字に変えてやればいいのです。以下に作成した関数を示します。
{大文字と小文字の区別なしに文字列検索}
function PosText(SubStr,S:string):integer;
begin
 SubStr:=AnsiLowerCase(SubStr);
 S:=AnsiLowerCase(S);
 Result:=Pos(SubStr,S);
end;

目次へ戻る


・フォントの種類と大きさをコンボボックスで変える。

 ワープロソフトのように、コンボボックスでフォントの種類と大きさを変えられるようにします。例としてフォントの種類の方をcobFontName、大きさをcobFontSizeとし、Memoを使います。
{コンボボックスの初期値}
procedure TForm1.FormCreate(Sender: TObject);
begin
 {使えるフォント名をコンボボックスに入れる}
 cobFontName.Items:=Screen.Fonts;

 {Memo1のフォント情報をコンボボックスに指定する。}
 cobFontName.Text:=Memo1.Font.Name;
 cobFontSize.Text:=IntToStr(Memo.Font.Size);
end;

{フォント名変更-ドロップダウンリストを使う場合}
procedure TfrmMemo.cobFontNameClick(Sender: TObject);
begin
 Memo.Font.Name:=cobFontName.Text;
end;

{フォント名変更-直接フォント名を書く場合}
procedure TfrmMemo.cobFontNameKeyPress(Sender: TObject; var Key: Char);
begin
 if key=#13 then
  Memo.Font.Name:=cobFontName.Text;
end;

{フォントサイズ変更-ドロップダウンリストを使う場合}
procedure TfrmMemo.cobFontSizeClick(Sender: TObject);
begin
 Memo.Font.Size:=StrToInt(cobFontSize.Text);
end;

{フォントサイズ変更-直接フォントサイズを書く場合}
procedure TfrmMemo.cobFontSizeKeyPress(Sender: TObject; var Key: Char);
begin
 if key=#13 then
  Memo.Font.Size:=StrToInt(cobFontSize.Text);
end;

 使えるフォント名は、Screen.Fontで得ることが出来ます。フォントサイズはあらかじめItemsプロパティに入れておいてください。
 フォントの指定には、ドロップダウンリストを使う場合とコンボボックスに直接書く場合があるので、イベントも2種類必要です。OnChangeイベントではないことに注意してください。

目次へ戻る


・Memoで、先頭行/最終行/指定行へ移動する。

 Delphi1と2/3で方法が違います。

  1. Delphi1の場合
    • 先頭行へ
      {先頭行へ}
      procedure TForm1.Button1Click(Sender: TObject);
      begin
       with Memo1 do
       begin
        {1行目にスクロール}
        Perform(WM_VSCROLL,SB_THUMBTRACK,0); {0が一行目を指す}
        SelStart:=0; {カーソルを一番先頭に移動}
        SetFocus;
       end;
      end;
      
    • 最終行へ
      procedure TForm1.Button2Click(Sender: TObject);
      begin
       with Memo1 do
       begin
        LastLine:=Perform(EM_GETLINECOUNT,0,0); {全行数を得る}
        Perform(WM_VSCROLL,SB_THUMBTRACK,LastLine-1); {最終行にスクロール}
        SelStart:=Perform(EM_LINEINDEX,LastLine-1,0); {カーソルを最終行に移動}
        SetFocus;
       end;
      end;
      

    • 指定行へ
       行の指定にEdit1を使います。
      procedure TForm1.Button3Click(Sender: TObject);
      var
       ToLine:integer;
      begin
       with Memo1 do
       begin
        ToLine:=IntToStr(Edit1.Text);
        Perform(WM_VSCROLL,SB_THUMBTRACK,ToLine-1); {指定行にスクロール}
        SelStart:=Perform(EM_LINEINDEX,ToLine,0); {カーソルを指定行に移動}
        SetFocus;
       end;
      end;
      

  2. Delphi2/3の場合
    • 先頭行へ
      procedure TForm1.Button1Click(Sender: TObject);
      var
       NowLine:integer;
      begin
       with Memo1 do
       begin
        NowLine:=Perform(EM_LINEFROMCHAR,SelStart,0); {カーソルのある行数を得る}
        {先頭行へスクロール}
        Perform(EM_LINESCROLL,0,-NowLine); {マイナスが重要}
        SelStart:=0;
        SetFocus;
       end;
      end;
      

    • 最終行へ
      procedure TForm1.Button2Click(Sender: TObject);
      var
       NowLine,LastLine:integer;
      begin
       with Memo do
       begin
        NowLine:=Perform(EM_LINEFROMCHAR,SelStart,0);
        LastLine:=Perform(EM_GETLINECOUNT,0,0);
        Perform(EM_LINESCROLL,0,LastLine-NowLine);
        SelStart:=Perform(EM_LINEINDEX,LastLine-1,0);
       end;
      end;
      

    • 指定行へ
       指定行をEdit1で指定します。
      procedure TForm1.Button3Click(Sender: TObject);
      var
       NowLine,ToLine:integer;
      begin
       with Memo do
       begin
        NowLine:=Perform(EM_LINEFROMCHAR,SelStart,0);
        ToLine:=StrToInt(Edit1.Text);
        Perform(EM_LINESCROLL,0,ToLine-NowLine);
        SelStart:=Perform(EM_LINEINDEX,ToLine-1,0);
       end;
      end;
      

目次へ戻る


・Memoで、現在行数/列数を得る。

 まず、現在行数と列数を計算する手続きを作成し、StatusBar1に表示させるようにします。
{行数と列数を計算}
procedure TForm1.GetLinePos;
var
 CurPos,Line,LinePos,LineCount:Integer;
begin
 with Memo1 do
 begin
  CurPos:=SelStart;
  Line:=Perform(EM_LINEFROMCHAR, CurPos, 0);
  LinePos:=Perform(EM_LINEINDEX, Line, 0);
  LineCount:=Perform(EM_GETLINECOUNT,0,0);
  StatusBar.Panels[0].Text:=Format('%d行%d列目 (全%d行)',
                    [Line+1,(CurPos-LinePos)+1,LineCount]);
 end;
end;

 次に、private宣言のところに

 private
    { Private 宣言 }
     procedure GetLinePos;

 と書き加えます。

 最後に、Memo1のOnKeyDown/OnKeyUp/OnMouseDown/OnMemoChangeの各イベントハンドラに GetLinePos を書きます。例えば、

procedure TfrmMemo.MemoKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
 GetLinePos;
end;

 OnKeyDownはキーを連続して押したときに、OnKeyUpはキーを一回押したときに、OnMouseDownはマウスでMemoのカーソルを動かしたときに、OnMemoChangeはファイルを読み込んだときに、それぞれ発生して行数と列数を表示します。

目次へ戻る


・Memoで、アンドゥさせる。

 WindowsAPIの EM_UNDO を使います。
procedure TForm1.Button1Click(Sender: TObject);
begin
 Memo.Perform(EM_UNDO,0,0);
end;

 ※ちなみに、Undoさせた後、もう一度UndoさせるとRedoになります。

目次へ戻る


・フォームを使わない小さなアプリケーションを作る。

 Delphiは、フォームだけのアプリケーションでも150KB程度の大きさになってしまいますが、フォームを使わなければ、10〜50KB前後にサイズを押さえられます。また、フォームの代わりにダイアログリソースを使う方法がTezzorieAppで紹介されています。

  1. 新規アプリケーションを作成する。
  2. Unit1.pasを閉じる。この際、保存するか聞いてくるが、保存しない。
  3. [表示-プロジェクトソース]を選んで、プロジェクトファイル(DPR)を開く。
  4. USES節にあるFormsを削除し、代わりにWindowsを書く。
  5. begin end.の間にある文を全て削除する。 こんな感じになります。
    program Project1;
    
    uses
      Windows;
    
    {$R *.RES}
    
    begin
      
    end.
         
  6. 基本的に begin と end. の間にプログラムを書いていきます。varやconstはbeginの上に書けばいいです。試しに、以下のコードを実行してみてください。
    program Project1;
    
    uses
      Windows;
    
    {$R *.RES}
    
    const
     Msg='ダイアログが音と一緒に出ます。';
     Title='ダイアログの実験'
    begin
     MessageBox(GetForegroundWindow,PChar(Msg),PChar(Title),MB_OK or MB_ICONINFORMATION);
    end.
        

    ダイアログが出ると思います。実行ファイルのサイズもとても小さくなっています。

  7. 少し凝ったものを作ろうとするとbegin end.の間には書ききれなくなるので、procedureやfunctionとして独立させる必要性が出てくるでしょう。その場合、必ずbegin end.の上に書きます。
    function Test:integer;
    begin
     {コード}
    end;
    
    procedure Test2;
    begin
     {コード}
    end;
    
    var
     Result:integer;
    begin
     Test2;
     Result:=Test;
    end.
         
    こんな感じです。また、Test2からTestを呼ぶことは出来ますが、逆にTestからTest2を呼ぶことは出来ません。つまり、下から上は呼び出せるが、上から下は呼び出せません。
 あと、uses節にはWindowsしかありませんが、IntToStrを使いたかったら SysUtilsを追加する必要がありますし、レジストリ操作にはRegistry, TStringListにはClasses、SendMessageにはMessagesなど、やりたいこと によってuses節へ追加してください。ただし、Dialogsを加えてはいけません。サイズが100KBを越えてしまいます。ダイアログはあくまでも MessageBox を使うようにしてください。

目次へ戻る


・メニューに独自のショートカットを割り当てる。

 TextToShortCutを使います。例えば、Menu1に「Ctrl+Shift+A」を割り当てるには
Menu1.ShortCut:=TextToShortCut('Ctrl+Shift+A');

目次へ戻る


・SetFocusの注意事項。

 SetFocusは、あるコンポーネントへフォーカスを当てるときに使いますが、使い方を間違えるとエラーが出ます。

  1. SetFocusは、画面に表示されているコンポーネントにしかフォーカスを当てられません。そのため、例えば PageControlを使っているときに、TabSheet1が表示されているにもかかわらず、TabSheet2にあるコンポーネントへフォーカスを当てようとするとエラーになります。

  2. FormのOnCreateイベントにもSetFocusを使ってはいけません。OnCreateはその名の通り、フォームを作るイベントなので、この段階では未だコンポーネントも画面に表示されないのです。このため、たとえSetFocusをOnCreateの一番下に書いたとしてもエラーになります。この場合は、OnActivateかOnShowイベントに書くのが正解です。

  3. ListBoxにSetFocusしても反転表示されないことがあります。しかし、よく見ると一番上の項目が点線で囲まれていて、フォーカスが当たっていることが分かります。反転表示されないのは、単に項目が選択されていないからです。この場合は、ItemIndexを使って、反転表示させたい項目を指定する必要があります。一番上の項目ならば、
    ListBox1.SetForcus;
    ListBox1.ItemIndex:=0;
    

    とすれば、一番上の項目が反転表示されます。

目次へ戻る


・SenderのないprocedureからSenderを持つprocedureを呼ぶ。

 自作の手続き(procedure)を作った場合に、そこからButton1Clickなどを呼び出そうとすると、Senderを引数に渡す必要があります( Button1Click(Sender); のように)。
 しかし、自作の手続きにわざわざ使う必要のないSender引数を付けるのもスマートではありません。これを回避するには、引数に「nil」を使うとうまくいきます。
(略)

  private
      { Private 宣言 }
      NoSender;

(略)

{Senderを引数に持つ手続き}
procedure TForm1.Button1Click(Sender: TObject);
begin
 Form1.Color:=clRed;
end;

{Senderを引数に持たない自作の手続き}
procedure TForm1.NoSender;
begin
 {Button1Clickを呼び出す}
 ButtonlClick(nil); {Senderの代わりにnilを引数に渡せばよい}
end;

調子が悪い場合はnilの代わりに「Self」を使ってみてください。

目次へ戻る


・2バイトの16進数を10進数に変換する

 Delphiには16進数を10進数にする関数「StrToInt」がありますが、これは漢字などの2バイト16進数を変換してくれません。というわけで汎用の 16進数 -> 10進数関数を作ってみました。
{16進数を10進数に変換}
function HexToInt(Hex:string):integer;
const
 ErrorMsg='桁数が2桁か4桁ではありません。';
var
 High,Low:string;
begin
 Result:=-1;

 if Copy(Hex,1,1)='$' then Delete(Hex,1,1);

 High:='';
 Low:='';
 case Length(Hex) of
  0: Exit;
  2: Low:=Hex;
  4: begin
      High:=Copy(Hex,1,2);
      Low:=Copy(Hex,3,2);
     end;
  else
   raise EConvertError.Create(ErrorMsg); {例外を生成}
 end;
  Result:=StrToInt('$'+High)*256+StrToInt('$'+Low);
end;

目次へ戻る


・フォームが表示し終わった時のイベントを取得する。

 フォームを表示しているときに起こるイベントには「OnCreate」「OnShow」「OnActivate」などがありますが、表示し終わったときのイベントがありません。これを取得するには「CMShowingChanged」メッセージをトラップします。
(略)
private
  { Private 宣言 }
  procedure CMShowingChanged(var Msg:TMessage);message CM_SHOWINGCHANGED;

(略)

procedure TForm1.CMShowingChanged(var Msg:TMessage);
begin
 inherited; {通常の CMShowingChagenedをまず実行}

 if Visible then
  begin
   Update; {完全に描画}

   //この中にやりたいことを書いていく
  end;
end;

目次へ戻る


・特定のコンポーネントを使ってフォームをドラッグする。

 例えば Imageコンポーネントをドラッグするとフォームもドラッグするように動作させるには、OnMouseDownに以下のように書きます。
{ドラッグしてフォームを移動}
procedure TForm1.Image1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
 if ssLeft in Shift then
  begin
   ReleaseCapture;
   SendMessage (Handle, WM_SYSCOMMAND, SC_MOVE or 2, 0);
   Image1.Perform(WM_LBUTTONUP,0,MakeLParam(X,Y));
  end;
end;

目次へ戻る


・コンボボックスに項目を重複せずに追加する。

コンボボックスに項目を追加するとき、すでに追加した項目については再度追加したくないことはありませんか。例えばコンボボックスに履歴機能を付けるときに必要になると思います。
with Combobox1 do
  if Items.IndexOf(Text)=-1 then
   Items.Add(Text);
「IndexOf」は、かっこ内の文字列が項目の何番目にあるのかを調べるメソッドです。もし該当する項目がなければ「-1」を返します。この機能を利用してすでに項目があるのかを調べているわけです。

目次へ戻る


・任意のキーを押したらTabキーと同じ挙動にする。

普通はTabキーでコンポーネント間を移動するのですが、これと同じ動作をさせるには、
   //その1 SendMessage(Handle, WM_NEXTDLGCTL, 0, 0);
   //その2 SelectNext((Sender as TWinControl),True,True);
   //その3 Keybd_event(VK_TAB, 0, 0, 0);
方法は3通りあります。その1はウィンドウハンドルを利用して次または前に進みます。その2はDelphiの手続きで、進む先のコンポーネントの種類を制限できたりします。その3はずばりTabキーをソフトウェア的に押す方法で、「Shift+Enter」での逆移動が何のコードも書かずに出来ます。一番きちんと動くやつをどうぞ。

<使用例>

{Enterキーでタブ移動}
procedure TForm1.DBEdit1KeyPress(Sender: TObject; var Key: Char);
begin
 if Key=#13 then  //Enterキーが押されたら
  begin
   Key:=#0; //キー入力を無効にし
   Keybd_event(VK_TAB,0,0,0) //Tabキーを発生させる。
  end;
end;

{上下キーでタブ移動}
procedure TForm1.DBEdit1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
 case Key of
  //下矢印キー
  VK_DOWN   : begin
               Key:=0;
               SelectNext((Sender as TWinControl),True,True);
              end;
  //上矢印キー
  VK_UP     : begin
               Key:=0;
               SelectNext((Sender as TWinControl),False,True);
              end;
 end;
end;

目次へ戻る


・PageControlで実行時にTabを作り、さらにその上にRichEditを作る

DelphiのIDEにあるエディタはタブ管理されていて、しかもアプリケーションの実行時にタブとエディタ部分を作成しています。それと同じ事をする方法をご紹介します。ちょっとここでは書ききれないのでサンプルプログラムを作りました。ダウンロード(pagecrt.lzh/3KB)して参考にして下さい。

目次へ戻る


・IEのURLを入力するコンボボックスのように自動的にアドレス入力をさせたい

IEのアドレス入力欄に入力すると、今までの履歴を参照して自動的に入力してくれます。それを行う方法をご紹介します。TComboBoxのOnKeyUpイベントに書くだけなので結構簡単です。
procedure TForm1.ComboBox1KeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
const
 DefUrl: array[0..3] of string =
        ('http://','file:///','ftp://','res://');
var
 i,k,j: integer;
 TempLength: integer;
 TypedUrl,Address: string;
 DefFlag: boolean;
begin
 //スキップさせるキー
 case Key of
  VK_LEFT..VK_DOWN: Exit;   //矢印キー
  VK_BACK..VK_CLEAR: Exit;  //削除キー
  VK_SHIFT..VK_MENU: Exit;  //Ctrl,Shift,Alt
 end;

 //小文字にしておく
 TypedUrl:=AnsiLowerCase(Combobox1.Text);

 {http:// などの場合}
 //IEは、一番最初に「h」と入れると「http://」を、
 //「f」と入れると「files:///」を返すので、まずは
 //それを調べる。「DefUrl」に定数を追加すれば
 //好きなだけ自動入力する項目を増やせる。
 DefFlag:=False;
 for j:=0 to High(DefUrl) do
 begin
  if AnsiPos(TypedUrl,DefUrl[j])=1 then
   begin
    TempLength:=Length(TypedUrl);
    Combobox1.Text:=DefUrl[j];
    Combobox1.SelStart:=TempLength;
    Combobox1.SelLength:=Length(Combobox1.Text)-TempLength;
    DefFlag:=True;
    Break;
   end;
 end;
 if DefFlag then Exit;

 {それ以外}
 //ここが自動入力のメイン。
 //IEは自動入力をとりあえずスラッシュまで行う。
 //例えば「http://home.att.ne.jp/red/takeone/」の
 //場合は、まず「home.att.ne.jp/」まで、次に「red/」を
 //自動入力していく。
 //また、大文字と小文字の区別は行わない。履歴の方が小文字
 //なのに大文字で入力した場合は、実際の入力が採用される。
 for i:=0 to Combobox1.Items.Count-1 do
 begin
  if AnsiPos(TypedUrl,AnsiLowerCase(Combobox1.Items[i]))=1 then
   begin
    Address:=Combobox1.Items[i];
    TempLength:=Length(TypedUrl);
    //スラッシュごとに自動入力
    for k:=TempLength to Length(Address) do
    begin
     if (Copy(Address,k,1)='/') or
        (Copy(Address,k,1)='\')  then  //\はローカル用
      begin
       Address:=Copy(Combobox1.Items[i],1,k);
       Break;
      end;
    end;

    //IEは、履歴が小文字なのに大文字で入力したとき
    //(または反対)は、実際の入力を採用する。
    //もし、履歴の方を採用したい場合は、下記の2行を
    //削って3行目を使えばよい。
    Delete(Address,1,Length(TypedUrl));
    Combobox1.Text:=Combobox1.Text+Address;
    //Combobox1.Text:=Address;  //履歴の方を採用する
    
    Combobox1.SelStart:=TempLength;
    Combobox1.SelLength:=Length(Combobox1.Text)-TempLength;
    Break;
   end;
 end;
end;
この方法はURL入力以外でも流用できますので、広く使ってみて下さい。

目次へ戻る


・DDEでブラウザにHTMLファイルを開かせる

DDEを使うにはブラウザをサーバー、自分のアプリをクライアントにします。そのためには、DDEClientConvコンポーネントを使用します。下記例では分かりやすいように動的に作成してますが、設計時にコンポーネントを利用した方が簡単です。IEでの例を示します。
procedure TForm1.Button1Click(Sender: TObject);
const
 HtmlFileName='D:\index.htm';
 HtmlApplication='C:\Program Files\Internet Explorer\iexplore.exe';
var
 DdeClientConv: TDdeClientConv;
 Macro: string;
begin
 DdeClientConv:=TDdeClientConv.Create(Self);
 with DdeClientConv do
 begin
  try
   //ブラウザが起動していないときはここで指定したアプリを起動します。
   ServiceApplication:=HtmlApplication;
   
   //IEだと、'IExplore'です。
   //ネスケだと、3.xでは 'Netscape'、4.xだと 'NSShell'です。
   if SetLink('IExplore','WWW_OpenURL') then
    begin
     //IEだと '"file://%s",,-1,,,,,'です。
     //ネスケだと '"%s"'ですが、どっちでも大丈夫みたいです。
     Macro:=Format('"file://%s",,-1,,,,,',[HtmlFileName]);
     if not ExecuteMacro(PChar(Macro),True) then
      ShowMessage('表示できませんでした。');
     CloseLink;
    end
   else
    ShowMessage('接続できませんでした。');
  finally
   Free;
  end; 
 end;
end;

SetLinkでネスケのバージョンに注意して下さい。とりあえず'NSShell'を試して、だめだったら'Netscape'を試せばよいでしょう。

目次へ戻る


・タイトルバーをダブルクリックしたときにイベントを発生させたい

 これはFormのOnDblClickでは取得できません。なぜならタイトルバーは「非クライアント(Non-Client)領域」だからです。フォームをよく見て下さい。格子状の点々が打ってある領域がクライアント領域です。フォームのプロパティで「ClientHeight/ClientWidth」で指定される領域ですね。しかし、タイトルバーやフォームの大きさを変えられる枠の部分、メニューには格子が付いてません。そこが非クライアント領域です。OnDblClickが受け入れるイベントはクライアント領域内だけです。
非クライアント領域でイベントを発生させたい場合、WindowsAPIの「WM_NCLBUTTONDBLCLK」ウィンドウメッセージをトラップしてこちらで捕まえ、イベントの代わりとします。

(略)
  private
    { Private 宣言 }
    procedure WMNCLBUTTONDBLCLK(var msg: TWMNCLBUTTONDBLCLK); message WM_NCLBUTTONDBLCLK;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

{非クライアント領域で起こったダブルクリックを捕捉}
procedure TForm1.WMNCLBUTTONDBLCLK(var msg:TWMNCLBUTTONDBLCLK);
begin
 if msg.HitTest=HTCAPTION then  //タイトルバーで起こったならば
  ShowMessage('タイトルバーがダブルクリックされました。');

 inherited;  //トラップ終了
end;

目次へ戻る


・サブフォルダにあるファイルまで検索したい

 あるフォルダ以下にあるファイル全てをサブフォルダも含めて検索したい場合があります。こういうときは、普通なら再帰処理という方法を使って検索するのですが、今回はよりDelphiらしくTStringListを2つ使った検索ルーチンをご紹介しましょう。
フォームに、Button,ListBox,DirectoryListBox,Labelを一つずつ配置して下さい。
{メインルーチン}
procedure TForm1.Button1Click(Sender: TObject);
 {検索ルーチン}
 procedure FindFiles(FindPath:string; var DirList, FileList: TStringList);
 var
  SearchRec: TSearchRec;
  Found: integer;
 begin
  if Copy(FindPath,Length(FindPath),1)='\' then
   Delete(FindPath,Length(FindPath),1);
  Found:=FindFirst(FindPath+'\'+'*.*',faAnyFile,SearchRec);
  while Found=0 do
  begin
   if (SearchRec.Name<>'.') and (SearchRec.Name<>'..') then
    begin
     if (SearchRec.Attr and faDirectory>0) then
      DirList.Add(FindPath+'\'+SearchRec.Name)
     else
      FileList.Add(FindPath+'\'+SearchRec.Name);
    end;
   Found:=FindNext(SearchRec);  
  end;
  FindClose(SearchRec);
 end;

var
 i: integer;
 DirList,FileList: TStringList;
begin
 {いろいろ初期化}
 ListBox1.Items.Clear;

 //検索用テンポラリStringList
 DirList:=TStringList.Create;    //ディレクトリ用
 FileList:=TStringList.Create;   //ファイル用
 try

  {ファイルの検索開始}
  Label1.Caption:='検索中...';
  Application.ProcessMessages;

  //第一階層の検索
  FindFiles(DirectoryListBox1.Directory,DirList,FileList);
  //サブフォルダありの場合
  if DirList.Count>0 then
   begin
    i:=0;
    repeat
     FindFiles(DirList[i],DirList,FileList);
     Inc(i);
    until i=DirList.Count;
   end;

  {結果表示}
  Label1.Caption:='ソート中...';
  Application.ProcessMessages;
  FileList.Sort;                      //検索結果のソート
  ListBox1.Items.Assign(FileList);
  Label1.Caption:=Format('ファイル数:%d  フォルダ数:%d',[FileList.Count,DirList.Count]);

  {StringList開放}
 finally
  DirList.Free;
  FileList.Free;
 end;
end;
一番の特徴は、StringListを2つ使っていることです。DirListにはサブフォルダのリストが蓄積され、FileListにはファイル名がフルパスで蓄積されていきます。これの良いところは、TStringListを使っているのでコンポーネントとの親和性が高い(TListBox,TListView,TMemoなど)、DirListとFileListの2つのリストが得られるため、フォルダの処理とファイルの処理を別個に行うことが出来る、という点です。
FindFilesルーチンでDirListに収集されたサブフォルダの情報が、再度FindFilesルーチンに送られてサブフォルダ内のファイルを検索します。本来の再帰ではないのですが、DirListを使うことで再帰風の処理をしています。

上記の全ソース(findfile.lzh)を置いておきますので、使って下さい(Delphi3で作成)。

目次へ戻る


・TListViewをReportにした時にサブ項目の実行時追加方法

TListViewのViewStyleプロパティをvsReport、つまり詳細表示にした場合、項目をプログラム的に追加する方法をご紹介します。
フォームにListViewとボタンを用意し、Columnsに「ファイル名」「サイズ」「日時」を追加し、ボタンのOnClickイベントに以下のように記述してください。
procedure TForm1.Button1Click(Sender: TObject);
var
 SearchRec: TSearchRec;
 Found: integer;
 ListItem: TListItem;
begin
 //ファイル検索開始
 Found:=FindFirst(ExtractFilePath(Application.ExeName)+'*.*',faAnyFile,SearchRec);
 if Found=0 then
  begin
   try
    while Found=0 do
    begin
     {ListViewに項目を追加}
     with ListView1 do
     begin
      //項目追加
      ListItem:=Items.Add;
      //新規項目のキャプション
      ListItem.Caption:=SearchRec.Name;
      //項目のサブ項目を追加していく
      ListItem.SubItems.Add(IntToStr(SearchRec.Size));
      ListItem.SubItems.Add(DateTimeToStr(FileDateToDateTime(SearchRec.Time)));
     end;
     //次を検索
     Found:=FindNext(SearchRec);
    end;
   finally
    //検索終了
    FindClose(SearchRec);
   end; 
  end;
end;
いかがでしょう。実行ファイルのあるフォルダ内のファイルがすべてListViewに表示されたと思います。検索方法に関してはここでは触れません。
  1. Items.Addで項目を追加しています。ファイルが1つ見つかるたびに1項目増やしています。増やした項目をListItem変数に格納します。
  2. 増やした項目(ListItem)のCaptionを設定します。これはちょうど「ファイル名」カラムの内容に相当します。
  3. SubItemsプロパティが今回の重要点です。ここがサブ項目、つまり「サイズ」「日時」カラムなどの項目内容です。このプロパティはTStringsなので、TMemoのLinesやTListBoxのItemsと同じ扱いが可能です。
  4. SubItems.Addでどんどんサブ項目を追加していくことができます。
  5. ViewStyleプロパティをvsReport以外にすれば、当然「ファイル名」項目の内容のみが表示されます。

ListView
<図説>vsReportのListView

目次へ戻る


・TF1BookのFomula One Workbook Desinerだけを呼び出すアプリを作る方法

 Delphi3から付いてくるActiveXコンポーネントに「TF1Book」があります。このコンポーネントは、Excelのような表計算機能を提供しています。そして、TF1Bookには「Fomula One Workbook Desiner」が内蔵されています。本来の使い方は設計時にF1Bookのデザインを変更するためのものですが、その出来映えはサンプルアプリケーションとして立派に機能するものです。
Workbook Desinerは、TF1Bookを右クリックして出てくるメニューから起動するのですが、今回はそれを呼び出すだけのアプリケーションを作ってみます。

  1. まず、新規アプリケーションを作成。
  2. 出来たUnit1.pasを閉じる。このとき保存はしない。
  3. プロジェクトファイルを表示する。
  4. プロジェクトソースを以下のように書き換える。
    program Project1;
    
    uses
      Windows,
      Vcf1;
    
    {$R *.RES}
    
    var
     F1Book: TF1Book;
    begin
     F1Book:=TF1Book.Create(nil);
     with F1Book do
     begin
      try
       LaunchDesigner; //デザイナ起動
      finally
       Free;
      end;
     end;
    end.
    
  5. あとは保存して実行すれば、Workbook Desinerだけがすぐに起動する。

あまりにも簡単に出来たので作った本人も驚いてしまいました。つまり、ActiveXもDelphiにかかればただのコンポーネントと同じ、普通のクラスと同様に扱えると言うことです。だからわざわざF1Bookをフォームに乗せなくとも、プロジェクトファイル内でCreateすることが可能なわけですね。

<配布方法>
F1BookはActiveXなので、他のパソコンで使うには本体であるOCXを一緒に配布しなければなりません。さらに、ActiveXなので、レジストリに登録しなければなりません。

  1. 配布するファイル
    ヘルプによれば以下の通りである。
    • Vcf132.ocx
    • Oc30.dll
    • Mfcans32.dll
    • Msvcrt20.dll
    これらはすべてSystemフォルダへ投入する。DLLに関してはすでに存在している可能性があるので、そのときはコピーしなくていいでしょう。
    ライセンスフリーだそうなので、ガンガン配りましょう!
  2. レジストリへの登録
    1. スタートメニューから「ファイル名を指定して実行」を選択。
    2. 「regsvr32 c:\windows\system\vcf132.ocx」と入力して実行。
      Systemフォルダへのパスは適宜変えること。
    3. レジストリへの登録が完了するとダイアログが表示される。

Fomula Oneについては、販売元サイトにより詳しく載っています。 

目次へ戻る


一番上へ

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

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