サイトカレンダー

サイトカレンダーをスキップ

2018年 10月
«
»
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

記事カテゴリ

ユーザー機能


 2018年10月20日(土) 05:51 JST

[Delphi] 入力禁止中に発生したユーザによる入力の消去

  • 記事を友だちにメール
  • 印刷用ページ
  • 2004年2月18日(水) 12:42 JST
  • 投稿者:
  • 表示回数
    3,125

ある特定の処理実行中は、フォームやボタンの Enabled プロパティを False に設定し、2度同じ処理を実行されないようにしたりします。 しかし、Enabled が False でも True に変更する前にユーザがマウスクリックまたはEnterキーなどで処理をしていた場合、その操作に関連する処理は行われてしまいます。 今回はそのような問題への対策です。
以下の関数をボタンの Enabled を True にする際になどに実行します。

procedure PurgeMsgQueue(AHandle: HWND);
var
  Msg: TMsg;
begin
  // マウスのメッセージをキューから除去
  while PeekMessage(Msg, AHandle, WM_CLOSE, WM_QUIT, PM_NOREMOVE) do exit;
  while PeekMessage(Msg, AHandle, WM_DESTROY, WM_DESTROY, PM_NOREMOVE) do exit;
  while PeekMessage(Msg, AHandle, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE) do;
  // そのほかのメッセージをキューから除去
  while PeekMessage(Msg, AHandle, WM_CLOSE, WM_QUIT, PM_NOREMOVE) do exit;
  while PeekMessage(Msg, AHandle, WM_DESTROY, WM_DESTROY, PM_NOREMOVE) do exit;
  while PeekMessage(Msg, AHandle, WM_KEYDOWN, WM_KEYDOWN, PM_REMOVE) do;
end;

でも、この方法だと取り除きたいマウスとキーボードのメッセージの数だけ記述しないとダメなので、実用性はいかがなものだろうか?

別案として、別スレッドで処理するかマウスとキーボードの処理を別ウィンドウに処理させてしまう方法がある。

[Delphi] 一回で深い階層のディレクトリを作成する

  • 記事を友だちにメール
  • 印刷用ページ
  • 2003年12月18日(木) 14:42 JST
  • 投稿者:
  • 表示回数
    4,063
多階層のディレクトリを作成するには親ディレクトリから1つずつ作成していきます。
しかしこれでは面倒なので、ForceDirectories 関数を使って一発で作成することができます。
また、APIを使っても一発で作成することができます。
ForceDirectories は Delphi のヘルプに解説があるので、以下では API を使用する方法を解説します。

使用するAPIは、「MakeSureDirectoryPathExists」です。
この API と ForceDirectories との違いとしては、ForceDirectories は作成したいディレクトリを引数に指定するのに対し、 MakeSureDirectoryPathExists はファイル名を指定します。ですので、最後に「」が無いとファイル名と見なされディレクトリは作成されません。

このAPIを使用するには以下の定義が必要です。
interface
  function MakeSureDirectoryPathExists(DirPath: LPCSTR): Bool; stdcall;
implementation
  function MakeSureDirectoryPathExists; external 'IMAGEHLP.DLL' name 'MakeSureDirectoryPathExists';
DirPath にはファイル名を指定します。 パスの最後のコンポーネントがファイル名ではなくディレクトリである場合、文字列の最後に円記号(\)を記述しなければなりません。 このAPIは失敗すると False を返します。 こんな感じになります
procedure TForm1.Button1Click(Sender: TObject);
begin
  if not MakeSureDirectoryPathExists(PChar('c:temp')) then
    raise Exception.Create('Cannot create c:temp');
end;
補足
  1. 設定可能なファイル名の長さは200-260文字程度みたいです。
  2. APIなので String を PWideChar または PChar で型キャストする必要があります。
参照
  1. Microsoft Developer Network Library

[Delphi] Excel をアドイン付きで起動する

  • 記事を友だちにメール
  • 印刷用ページ
  • 2003年12月18日(木) 14:41 JST
  • 投稿者:
  • 表示回数
    4,356

通常 Excel を起動して OLE オートメーションとして使用したい場合には、CreateOleObject を使用します。
ただ、この方法ですと個人マクロブック(PERSONAL.XLS)が使えなかったり、アドインが読み込まれていなかったりするなどいろいろ不都合が多いのも事実です。

今回はそんなときに使える 「Excel をふつうに起動させる」方法です。

この方法は、ExcelをCreateProcessで非OLEアプリと同じように起動させて、その後GetActiveOleObjectでOLEを取得します。
Excel を起動するのは、CreateExcelProcess 関数です。ここでは「Excel.Application」という識別子から CLSID を取得し、さらにその CLSID から起動情報を取得した結果を利用してプロセスを作成しています。 また、起動後すぐには OLE のインスタンスを捕まえることができないので、最大 0.5 秒を 10 回計 5 秒を待つ設定にしています。 途中で SetForegroundWindow しているのは XP 上での Excel のバグ対策です。

procedure TForm1.Button1Click(Sender: TObject);

  // Excelプロセス生成
  function CreateExcelProcess(out hProcess: THandle): Bool;

    function GetExcelRegInfo: String;
    const
      KeyFormat = 'CLSID%sLocalServer32';
    var
      Key: String;
    begin
      Key := Format(KeyFormat, [GUIDToString(ProgIDToClassID('Excel.Application'))]);
      Result := GetRegStringValue(Key, '');
    end;

  var
    RegInfo: String;
    PROCESSINFO: TProcessInformation;
    STARTUPINFO: TStartupInfo;
  begin
    Result := false;

    RegInfo := GetExcelRegInfo;
    // Excel が導入されていない場合は、RegInfoが空文字列
    if (RegInfo = '') then exit;

    // 起動情報構造体
    with STARTUPINFO do
    begin
      cb := SizeOf(STARTUPINFO); // 構造体のサイズをセット
      lpReserved := nil;         // 上記以外は初期化
      lpDesktop  := nil;
      lpTitle    := nil;
      dwFlags := 0;
      cbReserved2 := 0;
      lpReserved2 := nil;
      dwysize := 0;
    end;

    // '/e' を付加すると初期のワークブックが抑制できる
    Result := CreateProcess(nil, PChar(RegInfo + ' /e'),
                nil, nil, false, 0, nil, nil, STARTUPINFO, PROCESSINFO);
    hProcess := PROCESSINFO.hProcess;
  end;

const
  RetryCount = 10;
  SErrorRunExcel = 'Excelの起動に失敗しました。';
var
  Excel: OleVariant;
  hProcess: THandle;      // Excel Process Handle
  i: Integer;
begin
  CreateExcelProcess(hProcess);
  try
    Excel := GetActiveOleObject('Excel.Application');
  except
    on E: EOleSysError do
    begin
      // Excelがまだ起動していない場合には、起動をかける
      if (E.ErrorCode = $FFFFFFFF800401E3) then
        if not CreateExcelProcess(hProcess) then
          raise Exception.Create(SErrorRunExcel);

      // Excel再取得
      for i := 0 to RetryCount do
      begin
        sleep(500);
        SetForegroundWindow(Handle);
        try Excel := GetActiveOleObject('Excel.Application') except end;
        if not VarIsEmpty(Excel) then break;
        // ループ最終回で取得できていなかった場合、
        if (RetryCount = i) then raise Exception.Create(SErrorRunExcel);
      end;
    end;
    on E: Exception do exit;
  end;  // Excel起動ループ

  Excel := unassigned;
end;

[Delphi] アプリケーションタスクをタスクバーに表示させない

  • 記事を友だちにメール
  • 印刷用ページ
  • 2003年1月13日(月) 22:47 JST
  • 投稿者:
  • 表示回数
    5,658
アプリケーションを切り替えるために使用するタスクバー。そこには起動中のアプリケーションが表示されています。
しかし、タスクトレイに表示するアプリケーションを作成した場合など何らかの理由で、タスクバーに表示させたくない場合もあるでしょう。

今回はそんなときに使える 「タスクバーにタスク表示させない」 方法です。

この方法は、アプリケーションのウィンドウ設定を変更します。
  • アプリケーションのウィンドウを非表示に(元々見えていません)する。
  • アプリケーションウィンドウの拡張設定の WS_EX_TOOLWINDOW というフラグをたてる。

この方法はプロジェクトのソースに対し修正を行います。
  1. デルファイの開発環境でプロジェクトマネージャを表示します。
  2. 次にプロジェクトを選択、右クリックポップアップメニューを表示します。
  3. ソースの表示をクリックすると表示されます。

実際のコードは以下のようになります。(斜体部分のみ入力します。)

program Project1;
uses
  Windows,
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  ShowWindow(Application.Handle, SW_HIDE);
  SetWindowLong(Application.Handle, GWL_EXSTYLE,
    GetWindowLong(Application.Handle, GWL_EXSTYLE) or WS_EX_TOOLWINDOW);
  Application.Run;
end.

[Delphi] ユーザーの画面操作禁止

  • 記事を友だちにメール
  • 印刷用ページ
  • 2003年1月13日(月) 22:47 JST
  • 投稿者:
  • 表示回数
    3,352
フォームでボタンをクリックしたとき、メニューをクリックしたときの操作がある程度以上時間がかかる場合ありますよね。こんな時にもう一度、ボタンをクリックされたら...。同じ処理が平行して同時に行われることになってしまいます。
たいていこんな時には、処理中にもう一度ボタンをクリックされないように、Enabled プロパティを使用してクリックできないようにされていると思います。でも複数のオブジェクトを使用不可にするのは面倒な作業です。
そこで今回は、アプリケーション中のすべてのフォームとフォームに張り付いたすべてのオブジェクトが使用不可能になる方法をご紹介します。
この方法は、Forms ユニットにある ShowModal や MessageDlg も使用している関数を使用します。
  • ほかのフォームを触れなくする場合、function DisableTaskWindows(ActiveWindow: HWnd): Pointer; を使用します。
  • 逆に使用可能に戻す場合には procedure EnableTaskWindows(WindowList: Pointer); を使用します。
で使い方ですが...
  1. DisableTaskWindows(0) を実行します。(ちなみに特定のフォームのウィンドウハンドルを渡すと、特定のフォーム以外を使用不可にできます。)
    戻り値のポインタは、適当な変数に保存しておいてください。後で使用します。
  2. 特定の処理を書きます
  3. 元に戻すタイミングで、EnableTaskWindows(保存しておいたポインタ) を実行します。

実際のコードは以下のようになります。(斜体部分のみ入力します。)
procedure TForm1.Button1Click(Sender: TObject);
var
  pDTW: pointer;
begin
  pDTW := DisableTaskWindows(0);  // 使用不可にして
  try
    Sleep(1000);                           // 処理をして
  finally
    EnableTaskWindows(pDTW);      // 終わったら戻す
  end;
end;

新着情報

記事 新着(24時間)

-

コメント 新着(2日)

-

トラックバック 新着(2日)

-

リンク 新着(2週)

新しいリンクはありません

ファイル (14日)