Delphi Tips
>> Index
● 01/12 ファイルを作らずメモリ上で動作する TTable コンポーネント
● 10/02 DataSet を指定していないときに QuickReport の進捗状況を表示する
● 01/22 短いファイル名を長いファイル名に変換したい(COM版)。
● 12/31 リンクファイルから参照先のファイル名を得る
● 12/08 SubDetailが印刷できない
● 10/15 EXE ファイルのスリム化
● 07/05 デバッグ中にコード特定部分で発生する例外を無視する
● 02/08 リソースにあるバージョン情報を取得したい。
● 06/20 システムメニューに項目を追加/削除したい
● 03/04 自作アプリにスペルチェックの機能をつけたい
● 03/04 マウスがコントロールから出たことを検知する
● 03/04 正規表現の使える検索・置換えライブラリ
● 03/04 ブレークポイントで停止したときに IDE がアクティブにならない
● 03/04 エディットコントロールで右寄せ表示
● 03/04 フォームの最小化時のアニメーション効果を出す(BCB)
● 03/04 コンパイラを識別するための定義済みシンボル
● 03/04 春分、秋分、夏至、冬至の求め方
● 11/21 子プロセス(DOS)の標準出力をパイプする
● 10/02 二重起動防止措置で既存プロセスを最前面に送る
● 09/24 Delphi7 のコンボボックスの空文字列でエラー
● 08/22 非ビジュアルコンポーネントの Left, Top 座標を取得
● 08/22 プロパティ値を文字列に変換/逆変換
● 07/24 集合型変数の内部構造が知りたい/数値として処理したい。
● 07/18 スクリーンセーバーの作り方
● 07/10 浮動小数点数を整数に丸めるときの注意
● 06/27 Delphi によるレジストリの操作方法
● 06/27 ファイルの更新日時を得る
● 06/27 システムアイコンをリフレッシュする
● 06/27 コントロールパネルを作る
● 06/27 全てのウィンドウを最小化する
● 06/27 コンボボックスにブラウザのURL入力のような自動補完機能を付けたい
● 06/27 TMemoで、先頭行/最終行/指定行へ移動する。
● 06/25 「アドレス????????でエラーが発生」からエラー位置を知る
● 06/25 Delphi の格言
● 06/25 OnDblClick発生時にOnMouseDownはいらない!
● 06/25 ComboBoxのDropDownListの幅を変更する
● 06/25 自己実行形式の動画
● 06/25 フォルダもファイルも選べるダイアログ
● 06/25 Ini ファイルに published プロパティを保存する方法
● 06/25 ボタン間を矢印キーで移動させるロジック
● 06/25 RichEdit を印刷したい
● 06/25 Install Shield Express でアンインストール時にレジストリを削除できない
● 06/25 ActiveX でショートカットキーが使えない
● 06/25 DLL にブレークポイントが設定できない
● 06/25 FindFirst でアイコンを変更したディレクトリを検索できない
● 06/25 FindFirst による4文字以上の拡張子の判別
● 05/17 Photoshop のようなツールウィンドウを実現したい
● 05/17 HTMLタグ表記の大文字・小文字変換を行う
● 05/17 文字列から括弧の中のみを削除する
● 05/17 列挙型(Enum)の値と文字列との変換
● 05/17 TIdentToInt型とTIntToIdent型
● 05/17 2つの TMemo のスクロールを同期させる
● 05/17 2つの TRichEdit のスクロールを同期させる
● 05/17 文字列や画像データをリソースに埋め込むためのコンポーネント
● 05/17 ActiveX 内部から自身の親ウィンドウのハンドルを得る
● 05/17 OnShow イベント中で SetFocus すると不具合
● 05/17 異なるバージョンのDelphiを1つのPCに共存させる
● 05/17 TreeView で D&D 中の描画処理
● 04/04 国際化アプリケーションの作り方
● 02/09 TListView の ... 表示をなくす
● 12/27 VBのMIDステートメント
● 06/11 ListView をスクロールする
● 06/06 <Delphi Install Dir>\BINディレクトリの中のファイル
● 06/02 フォームの新規作成のデフォルトを変更したい
● 06/02 トークンの切り出し
● 05/14 ディレクトリのファイルを列挙する
● 04/05 Oracle の Number 型で入力値が化ける
● 04/05 メモリマネージャのマルチスレッド対応 -- IsMultiThread
● 04/05 スレッドローカル変数 -- threadvar の使い方
● 09/08 整数値が奇数かどうかの判定
● 08/25 ComCtl32.DLL を配布するには
● 08/12 Dual CPU 機でフォームのフォントに不具合
● 06/01 色表現について(RGB, HSV, YUV など)
● 05/26 DLLの初期化・終了処理
● 04/06 Currency型の小数演算結果が不正
● 03/28 C++Builder でwith文の代用
● 03/28 あるアドレスを基点とした、変位のアドレスの参照
● 01/02 TField.EditMask の y2k 回避
● 01/02 StringGrid の内容更新を高速に行う
● 12/28 Windows2000の新APIを使った半透明ウィンドウ
● 12/22 フォームを1つずつ表示する
● 12/21 ランダムなレコードの移動
● 11/30 スクリーンセーバーの名前を変更する。
● 11/16 INI ファイルを扱うもう1つのクラス
● 10/14 終了処理の中の例外を見逃すな !
● 10/12 Pascal 版 auto_ptr -- Free の必要の無い高機能ポインタ
● 10/07 System ユニットの _ で始まる特殊ルーチンの呼び方
● 10/07 文字列リソースの編集ツール - 文字列テーブルエディタ
● 10/07 OnExit ハンドラで次にフォーカスを受け取るコントロールを知る
● 10/07 大きなソースブロックのコメントアウト
● 09/30 アクティブデスクトップを使って壁紙を変更
● 09/27 短いファイル名を長いファイル名に変換したい。
● 09/27 デスクトップにあるアイコンの数と位置を知りたい。
● 09/27 アップリケーションにサウンドリソースを埋め込んで使いたい。
● 09/27 入力された漢字のひらがなを取り出す
● 09/27 エクスプローラからファイルをドラッグ&ドロップする
● 09/27 アプリケーションを常駐させてタスクトレイに登録したい
● 09/26 ファイルを削除してゴミ箱に移動させたい。
● 09/25 TSaveDialogでファイルタイプが変更されたらファイル拡張子を変更したい
● 09/24 Windows 特殊フォルダの Class ID List
● 09/24 TOpenDialog(TSaveDialog) の初期フォルダをシステムフォルダ(デスクトップなど)に設計する
● 09/24 TOpenDialogで、Executeメソッド実行時に表示位置を変えたい。
● 09/23 シェルのデスクトップやマイコンピュータをプログラムから開きたい。
● 09/23 TOpenDialogが表示された時のフォーカスを変えたい。
● 09/22 長いファイル名を短いファイル名に変換したい
● 09/21 Delphi4 で TBitmap を pf1bit で使うときの注意点(不具合回避)
● 09/21 TListを特定のクラスのListにする方法
● 09/20 エクスプローラのように、アプリケーションにブラウザページをつくりたい。
● 09/20 フォームのアイコンをアニメーションにしたい
● 09/20 MDI等で二重起動を防止して新しいファイルを開く
● 09/19 二重起動の判定
● 09/19 簡易アプリケーション間通信
● 09/19 インターネット エクスプローラを起動したい/その情報を取得したい
● 09/19 Sqr()の結果が負になる!?
● 09/17 起動中のブラウザからURLを取得する/ブラウザにURLをセットする方法
● 09/16 StringGrid でセル編集終了のイベントを得たい
● 09/16 TStringGrid の列の ReSize イベントの取得
● 09/16 Pascal で文字列を効率良く扱う(例:文字列を逆順にする)。
● 09/15 漢数字で位取り表示
● 09/15 Win98 でタイトルバーのグラデーション色を得る
● 09/15 子に override されたメソッドを孫クラスから呼び出す
● 09/15 TRichEdit.Add でリソース不足
● 09/15 TPageControl に OnDblClick を追加する
● 09/15 TTreeView で5つ以上のオーバレイ・イメージを使う
● 09/15 Install Shield でアンインストール時にレジストリが掃除されない
● 09/13 TFontDialog で標準以外のサイズを選択肢に表示したい
● 09/11 Editコントロールで入力を数値専用にする
● 09/11 TMemo のキャレットを非表示にする
● 09/11 エディットコントロールにポップアップウィンドウをつけたい
● 09/09 文字列を TDateTime に変換する
● 09/09 経過日数を取得する
● 09/09 1時間後を取得する
● 09/09 Metafile が Draw で1ピクセル小さく描画される
● 09/09 TWebBrowser を使って HTML の描画イメージを取得する
● 09/09 DLL 内で TDBCtrlGrid を使うときの注意事項
● 09/09 サブネットマスク値など TCP/IP 関連パラメータを取得する
● 09/09 UDP 通信で NoParam ???
● 09/09 デフォルトプリンタの設定
● 09/09 TPageControl でタブをドラッグして順序入れ替え
● 09/08 任意のドライブをセクタ単位で読み書きする
● 09/08 IME に未確定文字列を入力
● 09/08 フォルダのタイムスタンプを変更する
● 09/08 Delphi3 でコンポーネントパレットを複数行にする
● 09/08 ショートカットキーに'+'を使う。
● 09/07 右寄せ・数値入力可能なEditコンポーネントを作りたい。
● 09/06 メッセージボックスを独自にカスタマイズしたものを使いたい。
● 09/06 Delphi 2.0/3.0でCPUウィンドウを出す
● 09/06 メインフォーム以外のフォームをタスクバーに入れたい。
● 09/06 APPのフォームを最小化して起動したい
● 09/06 メッセージボックスのデフォルトボタンを変えたい
● 09/06 ButtonのCaptionで改行を使って文字を複数段で表示したい
● 09/06 キーボードでボタンを押したとき、ボタンをちゃんと沈ませたい
● 09/06 カーソルキーでボタン(TButton)のフォーカス移動をやめさせたい
● 09/06 エディットコントロールにコンボボックスのようなボタンをつけたい
● 09/05 数値の2進数への変換で効率的な方法
● 09/05 ドッカブルメニュー(ツールバー形式のメニュー)を実現したい。
● 09/02 Windowsの「ファイルの検索」ダイアログをプログラムから使いたい
● 09/01 フォームの最小化、最大化をアニメーションでやりたい。(DELPHI)
● 09/01 RichEditでHTMLタグを色・書式付き表示をする
● 08/31 LinesプロパティエディタでTabを入力する
● 08/31 全角文字が半角文字の2倍幅になるフォントの選び方
● 08/31 RichEdit の全パラグラフのタブ幅を設定する
● 08/31 TRichEdit.Paragraph.Tab[] の設定(Tab位置の設定)
● 08/30 メインフォーム以外のフォームを最小化した時もAPP全体を最小化したい
● 08/30 Pascal 版の yacc & lex
● 08/28 データコントロールのDataLinkオブジェクトを取得する方法(ReadOnlyプロパティのバグの解決法)
● 08/26 ダイヤラを呼び出す。(TAPI32.DLLの使用)
● 08/26 他のアプリの起動パスを取得する
● 08/26 セルのテキストをドラッグイメ−ジにしてグリッド(TStringGrid)でドラッグ&ドロップを行う
● 08/26 ツリービューのノードをドラッグ&ドロップで移動させる
● 08/26 書式付きメモ型項目へアクセスしたい
● 08/23 デスクトップのウィンドウのタイトルとハンドルを取得する
● 08/23 プロセスの実行ファイル名を列挙する
● 08/23 Win95/98で、ウィンドゥハンドルから、実行アプリ名を知る
● 08/23 WinNT でウィンドウハンドルから実行アプリ名を知る
● 08/21 クリップボードのフォーマットを知る
● 08/21 クリップボードが更新された時のイベントを取得する
● 08/19 インターネットエクスプローラのアドレス帳を呼び出す
● 08/17 PASCAL ソースの整形ツール
● 08/14 Delphi2/3.x における Cardinal 型の妙な定義
● 08/14 MS-Word 97 で作成した rtf ファイルがヘルプコンパイラでエラーを起こす
● 08/14 カスタムコントロールの子コントロールをオブジェクトインスペクタに表示させない
● 08/14 ショートカットキーのキー名を独自に設定する
● 08/14 Object Pascal のコーディングスタイル
● 08/13 TColor 値を文字列に変換する
● 07/28 HTML-Help を作りたい
● 07/14 基本的な階層プロパティ定義の例
● 07/09 @ 演算子の働き
● 07/07 CapsLock の状態を変更する
● 06/08 LhasaのようなUIを持つアプリケーションの作成方法
● 05/28 TFileListBox にファイル名が重複して表示されてしまう
● 05/19 コンボボックスのリスト部分の幅を指定する
● 05/19 独自メッセージとして自由に使える値の範囲
● 05/19 「送る」の 516 文字制限について
● 05/19 TRichEdit の内容を高速に変更する
● 05/14 文字数のカウント方法
● 05/14 システムカラーを得る、設定する
● 05/12 コードエディタで「キーボードマクロ」を使う
● 05/12 コードエディタで選択部分をまとめてインデントする
● 05/12 コードエディタで矩形領域を選択する
● 05/08 改行コードの違いについて
● 05/01 右の項目の幅が固定されるTStatusBar
● 04/29 TCoolBarを用いたツールバーの位置保存
● 03/21 アプリケーション間でデータの送受信(WM_COPYDATAを使う)
● 02/11 BCB で実行ファイルのサイズを小さくするには
● 02/11 IME 入力で読み仮名を取得する
● 02/11 デバッグ中に関数評価の戻り値をチェックしたい
● 02/11 フォームの破棄、生成を続けて行うときの注意事項
● 02/11 DBGrid のスクロールバーを非表示に
● 02/11 プログラムからプリンタのスプーラ表示を開く
● 02/11 カレントユーザのデスクトップディレクトリを得る
● 02/11 IniFile に書き込みを行った後にはバッファのクリアが必要
● 02/11 TFileStream で標準出力に表示
● 02/11 半角カナを確定無しで直接入力させる
● 02/11 CreateProcess を使うときは CloseHandle で後始末
● 02/11 ディスパッチインターフェイスとデュアルインターフェイスについて
● 02/11 デバッグ時にライブラリ内もステップ実行したい
● 02/11 マウスがクリックされた正確な時刻が知りたい
● 02/11 Delphi2 以上で Form の枠を黒線一本にするには
● 02/11 エクスプローラで使われるアイコンを取得・変更する
● 02/11 和暦を西暦に直したい
● 02/11 可変長レコードの扱い方
● 02/11 アプリケーションが最小化されているかどうかを判定する
● 02/11 クリップボードにコピーされたファイル・ディレクトリ
● 02/11 Form をスクロールして特定のコントロールを表示させる
● 02/11 プリンタのスプールファイルを TMetafile に読みこむ
● 02/11 長すぎる TreeView アイテムを補完するチップヘルプを出さなくする
● 02/11 リムーバブルドライブの種類を判別する
● 02/11 下の図柄がすける透明パネル
● 02/11 Bitmap のパレットに使いたい色を追加する
● 02/11 API を使って縦書きなどのフォントを指定する
● 02/11 表示中のポップアップメニューを消す
● 02/11 TSplitter をドラッグ中にヒント文字列が表示された時の不具合
● 02/08 リードオンリーファイルを Assign/Reset で読もうとするとエラーになる
● 02/08 Fortran のソースを C に変換するツール
● 02/08 TColor を RGB 値へ変換する
● 02/08 Delphi で DOS のアプリを書く方法
● 02/08 Delphi/CBuilder で作った DLL から VB に文字列を返す
● 02/08 コントロールパネルのスクリーンセーバの設定画面を表示させる
● 02/08 Windows95 のみを再起動する
● 02/08 ダイヤルアップネットワークで設定されている接続先を取得する
● 02/08 他プロセスの Window を GUI で選ばせたい
● 02/08 *.h ファイルから *.pas ファイルを作るコンバータ
● 02/08 任意の実行ファイルが DLL などからインポートしている関数の一覧を得る
● 02/08 実行終了後、自分自身(実行ファイル)を削除したい
● 02/08 メタファイルを wmf 形式でコピーするときの注意
● 02/08 超高速 ListView
● 02/08 ステータスバーにプログレスバーを置きたい
● 02/08 Glyphに張ったビットマップの背景がおかしくなる
● 02/08 中村さんありがとうシリーズ:ビットマップ・パレット編
● 02/08 デスクトップフォルダのパスを得る
● 02/08 Scaled/AutoScrollプロパティと実行時のフォームサイズ
● 02/08 起動時にフォームの表示・非表示を決める
● 02/08 TBitmap の Width/Height に 0 を代入すると例外が発生する
● 02/08 Bitmap から Icon を作る
● 02/08 CD-DA のデータを Wav ファイルに落としたい
● 02/08 複数プログラムから同一内容のメモリを参照/更新する
● 02/08 プログラムからWindowsのスタートメニューを表示する
● 02/08 SetWindowsExt/SetViewportExtを使うときの注意点
● 02/08 PopupMenu に MainMenu のサブ項目をそのまま表示する
● 02/08 親クラスのプライベートフィールドにアクセスする
● 02/08 OpenDialogでたくさんファイルを選択するとエラー
● 02/08 C++ の関数の親子関係をレポートするツール
● 02/08 スクリーンセーバーをプログラムから停止する
● 02/08 Delphi3のコード補完機能で選択肢をソートして表示させる
● 02/08 コードエディタで「しおり」を使う
● 02/08 Delphiアプリをマウスのホイールに対応させたい
● 02/08 ヘルプ作成用のホットスポットエディタ(SHED.EXE)の入手方法
● 02/08 「システムエラー 読み出せません ドライブ X:」ダイアログを回避する
● 02/08 StringGrid で マウスのある Cell 内容に応じた Hint を出したい
● 02/08 自作コントロールで IME 入力時の変換候補をキャレット位置に表示したい
● 02/08 TDDEClientConvで最初の行しか実行されない?
● 02/08 TUpDown の不具合
● 02/08 Delphi1/2で状況依存型のコンポーネントヘルプを作るときの注意
● 02/08 time_t を TDateTime に変換する
● 02/08 TPageControl を TabPosition=tpBottom にすると不具合
● 02/08 TBitmap をきれいに印刷する
● 02/08 アプリケーションアイコンの変更が反映されない
● 02/08 クイックレポート関連記事
● 02/08 BCB3 のバージョン情報でキー名が日本語だと不具合
● 02/08 起動したアプリケーションの終了待ち
● 02/08 BCB1 から BCB3 へ移動したプロジェクトでのプリコンパイルヘッダ利用
● 02/08 最新版クイックレポート(英語)の日本語化
● 02/08 親の published プロパティを子クラスで隠蔽する
● 02/08 NT のタスクマネージャにアプリケーションのアイコンが表示されない
● 02/08 QuickReport で Canvas プロパティを使う
● 02/08 DDeClientConv を使ってスタートメニューにアイコンを登録
● 02/08 DLL のロード・アンロードでメモリリーク?
● 02/08 サブフォームがアクティブな時はメインフォームのアクセラレータキー・ショートカットキーを無効にしたい
● 02/08 用紙の印刷可能範囲を求める
● 02/08 超高速タイマーコンポーネント(サブミリ秒)
● 02/08 スタートメニューを任意の位置にポップアップさせる
● 02/08 MS-Access97 に接続できない
● 02/08 Delphi1.0でHugeポインタを使う
● 02/08 IDEのコードエディタのデフォルトサイズを変更する
● 02/08 アイコン状態で起動するアプリ
● 02/08 Windowsの「ファイルの検索ダイアログ」を表示させる
● 02/08 StringGridで選択セルのハイライト表示を無くしたい
● 02/08 関数のパラメータを省略可能にしたい
● 02/08 TLabelのFontがイタリックだと、AutoSize=Trueで右端が切れる
● 02/08 Delphi1.0のDirectoryListBoxが全角英文字のディレクトリ名で動作不良
● 02/08 Delphi3 の TStringList.CommaText の不具合
● 02/08 TIniFileにクオートを含む文字列を与えるときの注意点
● 02/08 Internetコンポーネントが他のマシンで動かない
● 02/08 自作アプリで作ったオブジェクトを他のアプリに貼り付けたい
● 02/08 Visual C++ と Delphi で Exe/DLL のやり取り
● 02/08 フォームの印刷時にComboBoxの内容が印刷されない
● 02/08 Delphiのインラインアセンブラで MMX コードを使いたい
● 02/08 COMCTL32.DLLのバージョンについて
● 02/08 Delphi3.1でオブジェクトインスペクタの表示がおかしい
● 02/08 DelphiのバグをBorlandに報告する
● 02/08 TrueTypeフォントからベクタ情報を得る
● 02/08 oDelphi1.0とDelphi2.0/3.0でコンポーネントのソースを共有したい
● 02/08 TDriveComboBoxの内容の更新
● 02/08 TMemoで入力*行数*を制限したい
● 02/08 設計時にフォームがエラーで読み込めず変更もできない
● 02/08 TListViewで列を指定してソート
● 02/08 文字列の切り分け
● 02/08 TStringGridのソート
● 02/08 StringGrid/DBGrid でのセル編集の動作を細かく指定する
● 02/08 32bitアプリから16bit DLLを呼び出す
● 02/08 complib.dllが壊れた!また全部のコンポーネントをインストールするの?!
● 02/08 New/Dispose に Pointer 型のポインタを渡すと。。。
● 02/08 Delphi3.0でDLLにバージョン情報が入らない
● 02/08 Delphi 1.0 (16bit)で、物理メモリアドレスに直接アクセスする
● 02/08 NTで他のアプリが開いているファイルを知る
最終更新: 6525 日前
0129 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2007/01/12 RAN 編集
ファイルを作らずメモリ上で動作する TTable コンポーネント
>前どこかで TTable 互換でメモリ上で動作するコンポーネントを見かけたのです
>が、ご存知な方いらっしゃいませんか? (その時にダウンロードしておけばよかっ
>た...)
[1]
Delphi Super Pageに昔からあるコンポーネントです。
BDEのインメモリテーブルと一時テーブルの機能を使ったものです。
http://sunsite.icm.edu.pl/delphi/
(2007年現在はhttp://delphi.icm.edu.pl/authors/a0000561.htm に移動)
ファイル名:inmemory.zip および inmem32.zip
※サンプル付きです。
Delphi1.0用のほうを、Delphi3で使用するときの修正点は以下のとおりです。32ビット版が出ているのでもう必要無いのかな?
#Inmemユニット:
# TypeMapで配列の最後に「, fldCURSOR」を追加。
# SubTypeMapで配列の最後に「, 0」を追加。
#TempTblユニット:
# TypeMapで配列の最後に「, fldCURSOR」を追加。
# SubTypeMapで配列の最後に「, 0」を追加。
※Register手続きがありませんので、追加してください。
>これがあればテンポラリのテーブルをいちいちディスクに作成しなくても済んで
>しまうため(結果的に作られるとしても)、便利なのですが...
そうなんですが、デバッグのとき、値を確認しにくいと思いますよ。
[2]
> ひょっとして http://delphideli.com/dlnbde.htm にある TMemTable のことでしょうか。
僕が前見たのは確かシェアウエアだったのですが、これはフリーでしかもソース付き。嬉しい限りです。
[3]
最近のDelphi では標準でMyBase が利用できます。
http://hp.vector.co.jp/authors/VA028375/delphi/db.html が分かりやすいです。
参照: [Delphi-ML:22981] [Delphi-ML:22983] <データベース> <DataAccess> <コンポーネント >
0150 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2006/10/02 223 編集
DataSet を指定していないときに QuickReport の進捗状況を表示する
> 今回作ったレポートだと TQuickRep.DataSet:=nil で、 OnNeedDataイベ
> ントですべてのデータを供給しているので、プログレスフォームに進捗
> を表示してくれません。
> 実行時には出力すべきレコード数と現在出力中のレコード番号は分かっ
> ています。こういうときに、うまくプログレスフォームで進捗を表示し
> てあげる方法は用意されていないのでしょうか?
QuickRep.QRPrinter.Progress プロパティに 0..100 の範囲の値を指定すると良さそうです。
procedure QuickRep1.DetailBand1AfterPrint(Sender: TQRCustomBand;
BandPrinted: Boolean);
begin
QrPrinter.Progress:=Trunc((100/MaxBandCount)*BandCount);
end;
MaxBandCount: 最大レコード数
BandCount: 現在のレコード
参照: [Delphi-ML:25304] <印刷>
0273 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/24 西坂良幸 rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2006/01/22 <> 編集
短いファイル名を長いファイル名に変換したい(COM版)。
エクスプローラは内部的には、短い名前を使っているようです。
ということは、シェルのCOMインターフェースのメソッドが使えるということです。
[Tips:233]のCOM版を考えてみましょう。
uses ShlObj,Comobj,ActiveX;
function ShortToLongFileName(const ShortName: string): string;
var
Desktop: IShellFolder;
pIDList: PITEMIDLIST;
NameS: String;
NameW: WideString;
Len : integer;
Buffer: array[0..MAX_PATH] of Char;
// 以下は値は使わない
pDummy: PCHAR;
pchEaten, Attributes: ULONG;
begin
pIDList := Nil;
// フルパス化
Len := GetFullPathName(PChar(ShortName), 0, PChar(result), pDummy);
SetLength(NameS, Len);
GetFullPathName(PChar(ShortName), Len, PChar(NameS), pDummy);
// ワイド文字列に転換
NameW := NameS;
// IShellFolderを生成(解放は自動)
OleCheck(SHGetDesktopFolder(Desktop));
// IDリストをえる
OleCheck(Desktop.ParseDisplayName(0, Nil, PWideChar(NameW), pchEaten, pIDList, Attributes));
if not SHGetPathFromIDList(pIDList, Buffer) then
raise EConvertError.Create('ファイルを変換できません。');
Result := StrPas(Buffer);
end;
WideStringを使うことを覗けば、IShellFolderのParseDisplayNameメソッドとSHGetPathFromIDList
を使うだけです。[Tips:233]よりすっきりしましたか。
※ 使用できるOS、バァージョンに注意して下さい。
参照: [Delphi-ML:7322] <その他Windows関連> <Windows> <ファイル> <PASCAL>
0099 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2005/12/31 長山 編集
リンクファイルから参照先のファイル名を得る
IShellLinkを使用すれば出来ます。
CoInitialize(NULL);
HRESULT HR;
IShellLink *psl;
IPersistFile *ppf;
WideChar wsz[MAX_PATH];
char szGotPath[MAX_PATH];
WIN32_FIND_DATA wfd;
HR = CoCreateInstance(CLSID_ShellLink, NULL,
CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
if(SUCCEEDED(HR)){
HR = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
if(SUCCEEDED(HR)){
MultiByteToWideChar(CP_ACP, 0, "ショートカット.lnk", -1,
wsz, MAX_PATH );
HR = ppf->Load( wsz, STGM_READ );
if(SUCCEEDED(HR)){
HR = psl->Resolve(Application->Handle, SLR_ANY_MATCH);
if(SUCCEEDED(HR)){
HR = psl->GetPath(szGotPath, MAX_PATH,
(WIN32_FIND_DATA *)&wfd, SLGP_SHORTPATH );
Edit2->Text = szGotPath;
}
}
}
ppf->Release();
}
psl->Release();
}
CoUninitialize();
Delphi翻訳版 (関数です)
uses
Windows, SysUtils, ShlObj, ActiveX, ComObj;
const IID_IPersistFile: TGUID= (D1:$0000010B;D2:$0000;D3:$0000;D4:($C0,$00,$00,$00,$00,$00,$00,$46));
function ExtractStringFromLinkFile( Target:TFileName ): String;
var
R : HRESULT;
SL : IShellLink;
PF : IPersistFile;
Wrk : array[0..MAX_PATH] of WideChar;
Path: array[0..MAX_PATH] of Char;
WFD : WIN32_FIND_DATA;
begin
Result := '';
CoInitialize( NIL );
R := CoCreateInstance( CLSID_ShellLink, NIL, CLSCTX_INPROC_SERVER, IID_IShellLinkA, SL );
if Succeeded( R ) then
begin
R := SL.QueryInterface( IID_IPersistFile, PF );
if Succeeded( R ) then
begin
MultiByteToWideChar( CP_ACP, 0, PChar(Target), -1, Wrk, MAX_PATH );
R := PF.Load( Wrk, STGM_READ );
if Succeeded( R ) then
begin
R := SL.Resolve( 0{Application.Handle}, SLR_ANY_MATCH );
if Succeeded( R ) then
begin
SL.GetPath( Path, MAX_PATH, WFD, SLGP_SHORTPATH );
Result := Path;
end;
end;
end;
end;
CoUninitialize;
end;
参照: [builder:5475] <ShellApi> <Windows> <ファイル>
0357 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2005/12/08 三輪一雄 rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2005/12/08 <> 編集
SubDetailが印刷できない
QRBandでBandTypeをrbSubDetailに設定しても、テーブルのフィールドが印刷されません。この方法で作成するとDataSetを指定するプロパティーがありません。
必ずQRSubDetailでバンドを作成します。
参照: <QReport> <コンポーネント > [d DelphiML]
0302 D1D2 D3 D4 D5 D6 D7 3.195 98 作成: 2000/06/01 osamu rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2005/10/15 nameless <> 編集
EXE ファイルのスリム化
.exe ファイルを圧縮しておき、実行時に自動的に解凍することで、.exe ファイルを小型化することの出来るソフトがいくつか出回っています。フリーソフトでは ezip, upx、商用ソフトでは aspack というものが有名なようです。
ezip>
http://web.archive.org/*/http://www.jonathanclark.com/ezip/ezip.exe
upx>
http://upx.sourceforge.net/
aspack>
http://www.entechtaiwan.com/aspack.htm
圧縮ではなく、W32 PE EXE ファイルのの 'reloc' セクションを削除してしまう方法もあります。reloc が無くても EXE はメモリー空間に Windows のローダがチャンと割り付けてくれます。(DLL は削除してはいけません)
StripReloc>
http://www.jrsoftware.org/striprlc.htm
参照: <開発環境> <アプリケーション> <配布>
0333 D1 D2 D3 D4D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2005/07/05 <> 編集
デバッグ中にコード特定部分で発生する例外を無視する
例外をデバッガが受け取るようにした場合、try except でハンドルしている既知の例外で頻繁に実行が止まってしまい困る場合があります。
コードの特定部分で発生する例外をデバッガに無視させるには、その範囲の前後にブレークポイントを仕掛けて、右クリック-[ブレークポイントの設定]-[拡張]で[ブレーク]のチェックを外し、それぞれ[この後の例外を無視する][この後の例外を処理する]のチェックを入れると良いです。
参照: [Delphi-ML:73061] <開発環境> <デバッグ>
0226 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/29 西坂良幸 rev 1.5 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2005/02/08 <> 編集
リソースにあるバージョン情報を取得したい。
Delphiのメニュー[プロジェクト|オプション]のバージョン情報ページのデータはリソースとして製作したアプリケーションの EXE ファイルの中にあります。バージョン情報ダイアログのバージョン番号は、是非このリソースの内容を反映させたいですね。
この方法は、多くの書物に書かれているようですが、ML でも多い話題です。
自作のダイアログをつくり、リポジトリに登録しておきましょう。
コンポーネント化するのもひとつの方法です。
VerQueryValue 関数の第二パラメータに、 '\' をつかって バージョン番号を取得する方法は [Delphi-ML:31478] で紹介しています。
ここでは、すべてのバージョン情報を取得する関数を紹介します。
type
TVerResourceKey = (
vrComments, // コメント
vrCompanyName, // 会社名
vrFileDescription, // 説明
vrFileVersion, // ファイルバージョン
vrInternalName, // 内部名
vrLegalCopyright, // 著作権
vrLegalTrademarks, // 商標
vrOriginalFilename, // 正式ファイル名
vrPrivateBuild, // プライベートビルド情報
vrProductName, // 製品名
vrProductVersion, // 製品バージョン
vrSpecialBuild); // スペシャルビルド情報
const
KeyWordStr: array [TVerResourceKey] of String = (
'Comments',
'CompanyName',
'FileDescription',
'FileVersion',
'InternalName',
'LegalCopyright',
'LegalTrademarks',
'OriginalFilename',
'PrivateBuild',
'ProductName',
'ProductVersion',
'SpecialBuild');
// バージョン情報を取得
function GetVersionInfo(KeyWord: TVerResourceKey): string;
const
Translation = '\VarFileInfo\Translation';
FileInfo = '\StringFileInfo\%0.4s%0.4s\';
var
BufSize, HWnd: DWORD;
VerInfoBuf: Pointer;
VerData: Pointer;
VerDataLen: Longword;
PathLocale: String;
begin
// 必要なバッファのサイズを取得
BufSize := GetFileVersionInfoSize(PChar(Application.ExeName), HWnd);
if BufSize <> 0 then
begin
// メモリを確保
GetMem(VerInfoBuf, BufSize);
try
GetFileVersionInfo(PChar(Application.ExeName), 0, BufSize, VerInfoBuf);
// 変数情報ブロック内の変換テーブルを指定
VerQueryValue(VerInfoBuf, PChar(Translation), VerData, VerDataLen);
if not (VerDataLen > 0) then
raise Exception.Create('情報の取得に失敗しました');
// 8桁の16進数に変換
// →'\StringFileInfo\027382\FileDescription'
PathLocale := Format(FileInfo + KeyWordStr[KeyWord],
[IntToHex(Integer(VerData^) and $FFFF, 4),
IntToHex((Integer(VerData^) shr 16) and $FFFF, 4)]);
VerQueryValue(VerInfoBuf, PChar(PathLocale), VerData, VerDataLen);
if VerDataLen > 0 then
begin
// VerDataはゼロで終わる文字列ではないことに注意
result := '';
SetLength(result, VerDataLen);
StrLCopy(PChar(result), VerData, VerDataLen);
end;
finally
// 解放
FreeMem(VerInfoBuf);
end;
end;
end;
// テスト
procedure TForm1.Button1Click(Sender: TObject);
begin
Label1.Caption := GetVersionInfo(vrFileVersion);
Label2.Caption := GetVersionInfo(vrLegalCopyright);
end;
参照: [Delphi-ML:31478] [Delphi-ML:37794] <アプリケーション> <開発環境> <ダイアログ>
0065 D1D2 D3 D4 D5 D6 D7 3.195 98 作成: 1999/02/08 osamu rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2004/06/20 kakken 編集
システムメニューに項目を追加/削除したい
海外製のコンポーネントですがこういうのがあるそうです(ソース付だそうだ)
http://www.torry.ru/vcl/menus/ksysmenu.zip
実際にやってみましょう。まず削除です。
デファウルトでシステムメニューはこのようになっています
+−−−−−−−−−+
|元のサイズに戻す |
|移動 |
|サイズ変更 |
|最小化 |
|最大化 |
|−−−−−−−−−|
|終了 |
+−−−−−−−−−+
// 上の例から移動と、終了だけ残します。
procedure TForm1.Button1Click(Sender: TObject);
var
hSysMenu:integer;
begin
hSysMenu := GetSystemMenu(Handle,False);
DeleteMenu(hSysMenu, 0, MF_BYPOSITION);
DeleteMenu(hSysMenu, 1, MF_BYPOSITION);
DeleteMenu(hSysMenu, 1, MF_BYPOSITION);
DeleteMenu(hSysMenu, 1, MF_BYPOSITION);
end;
MF_BYPOSITION を指定した時は、第二パラメータで位置を0..で指定します。
削除ごとに位置が変わることに注意して下さい。
MF_BYCOMMAND を指定した時は SC_SIZE、SC_MOVE、SC_MINIMIZE、SC_MAXIMIZE、SC_CLOSE などでメニューを特定します。
次に追加です。
const
NewMenu1_Command = 10; // この値は重複しないように任意
NewMenu2_Command = 11;
procedure TForm2.Button1Click(Sender: TObject);
var
hSysMenu:integer;
begin
hSysMenu := GetSystemMenu(Handle,False);
// 底に(最後に)追加する
// セパレータを追加
AppendMenu(hSysMenu, MF_SEPARATOR,0,nil);
// 'NewMenu_1'を追加
AppendMenu(hSysMenu, MF_STRING, NewMenu1_Command, 'NewMenu_1');
// セパレータ(位置=1)の上の挿入
InsertMenu(hSysMenu, 1, MF_BYPOSITION or MF_STRING, NewMenu2_Command, 'NewMenu_2');
end;
Const で定義した値が、TMenuItemで、Commandプロパティにあたるものです。
でも、これだけでは、表示されるだけです。追加した項目をクリックしたらイベントハンドラを呼び出さねばなりません。
(ここでは、イベントハンドラではなく、メッセージボックスで代用)それには、WM_COMMANDメッセージを捕まえます。
procedure WMCOMMAND(var msg: TWMSysCommand);message WM_COMMAND;
を、フォームのPrivateに定義して、
procedure TForm1.WMCOMMAND(var msg: TWMSysCommand);
begin
case Msg.CmdType of
NewMenu1_Command: ShowMessage('NewMenu_1が押されました');
NewMenu2_Command: ShowMessage('NewMenu_2が押されました');
end;
inherited;
end;
ですね。
たくさん作るときは、TPopupMenuを貼り付け、TMenuItemを作成してこのCaption、Command、OnClickなどのプロパティを利用した方が簡単です。
参照: [Delphi-ML:19712] [Delphi-ML:42000] <メニュー>
0046 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2004/03/04 osamu 編集
自作アプリにスペルチェックの機能をつけたい
Spell Checker(辞書付き)というコンポーネントが、http://www.cs.monash.edu.au/~vtran/にあります。
Delphian World MLの過去ログにこれに関する話題があるそうです。
http://www.delphianworld.com/
参照: [Delphi-ML:19276] <文字列> <PASCAL>
0101 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2004/03/04 osamu 編集
マウスがコントロールから出たことを検知する
マウスカーソルがコントロールに入ったり、出たりする瞬間を捕らえる方法です。
CM_MOUSEENTER/CM_MOUSELEAVE メッセージを使う方法
[Delphi-ML:9631]
マウスの出入りを検出できるコンポーネントを作ってしまう方法
[Delphi-ML:9645]
アイドル処理で、検出する方法
[Delphi-ML:9820]
等が有ります。
9631 はフォームに直接貼り付けられたコントロールにしか使えないので注意して下さい。
コントロールをサブクラス化するコンポーネントを使っても簡単にできるかもしれません。
http://www.delphianworld.com/direct.html?id=SY0054
SubClassVCL というやつです。
参照: [Delphi-ML:14031] <コンポーネント > <その他コンポーネント関連>
0119 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2004/03/04 osamu 編集
正規表現の使える検索・置換えライブラリ
正規表現を使った文字列探索/操作 : Rexp016.lzh
Perl 互換正規表現ユニット : BRegExp.lzh
DLは Delphian World で、
http://www.delphianworld.com/direct.html?id=MI0036
それか、この手のDLLがいろいろありますからそれらを使うという手がラクだと思います。
VWXW.DLL(Wz Editorで使われている)
作者のM.Ishidaさんのページ
http://www2h.meshnet.or.jp/~ishida/
jre.dll(秀丸エディタで使われている)
http://www.yamada-labs.com/software/spec/jre/
basp21.dll
http://www.hi-ho.ne.jp/babaq/
参照: [Delphi-ML:22272] <文字列> <PASCAL>
0146 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2004/03/04 osamu 編集
ブレークポイントで停止したときに IDE がアクティブにならない
Windows98 に移行してから、ブレークポイントで停止したときの挙動が変わりました。
フォーカスがDelphi IDE に移らず、デバッグ中のプログラムに残ったままなのです。
これを改善するためのエキスパート focusmgr.zip が、Delphian World
http://www.delphianworld.com/ で入手可能です。
(別件)
win.ini ファイルを編集することでも対応できます。
このファイルの[Compatibility95] セクションに(なければ追加し)、
DELPHI32=0x00000002
と記述するだけです。
こちらについての詳細は、以下をご覧ください。
Microsoft のサポート技術情報
W98:メッセージボックスがデスクトップの背後に隠れる
文書番号: J044951
参照: [Delphi-ML:24961] [Delphi-ML:25050] <開発環境> <バグ>
0204 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/16 osamu rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2004/03/04 osamu 編集
エディットコントロールで右寄せ表示
>TEditで表示したい内容が数字なため右寄せにしたいと
>思っています。しかし、調べた限りでは右に寄ってく
>れません。
Delphian World などで既存のコンポーネントを入手するのが一番楽チンですね。( http://www.delphianworld.com/ )
自分で作る場合:
Win98とそれ以前のWinとでは作り方が異なります。
これはWindows98が右寄せEditをサポートしたからです。
『Win98で右寄せ・数値入力可能なEditコンポーネントを作る』
というTipsを参照ください。
こちらでは簡単に完璧に右寄せEditが作れませす。
Win98以前のWinでは
Editの代わりにTMemoを使い、1LineのTMemoをEditに見せかけます。
TMemoを配置して
1、Alignment Property を taRightJustify にする。
2、WordWrap Property を False にする。
WantReturns Property を False にして改行を入力させないようにしても
Ctrl+JやCtrl+Returnを押すと改行コードが入力されてしまいます。
そこで改行された時に改行を解除する動作をコーディングします。
procedure TForm1.Memo1KeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
SelStartBuf: Integer;
S: string;
begin
if Memo1.SelLength>0 then exit;
SelStartBuf := Memo1.SelStart;
S := Memo1.Text;
if pos(#13,S)<>0 then//改行コードのCRを削除
begin
Delete(S,pos(#13,S),1);
Dec( SelStartBuf );
end;
if pos(#10,S)<>0 then
begin
Delete(S,pos(#10,S),1);//改行コードLFを削除
Dec( SelStartBuf );
end;
Memo1.Text := S;
Memo1.SelStart := SelStartBuf;
(*
また、TMemoを右寄せにしているとWordWrap:=falseが効果ありません。
文字が増えるとどうしても改行されてしまいます。
それに対応して改行されたらその時の文字数を保持して左寄せに変えます。
LengthShowはFormのメンバーとして変数宣言してください。
*)
if Memo1.Lines[1]<>'' then//改行されたなら
begin
SelStartBuf := Memo1.SelStart ;
Memo1.Alignment := taLeftJustify;
Memo1.SelStart := SelStartBuf;
LengthShow := Length(Memo1.Lines[0]);//LengthShow変数に
end;//表示可能文字数を保持
(*
文字数が減ったら元に戻さなければいけません。
*)
if LengthShow<>0 then//LengthShowに保持数があるなら
if Length(Memo1.Lines[0])<LengthShow then//文字が減ったら
begin
SelStartBuf := Memo1.SelStart;
Memo1.Alignment := taRightJustify;
Memo1.SelStart := SelStartBuf;
end;
end;//KeyUpイベント終わり
単純に、改行された時の文字数=表示の限界、とみなして
その文字数以下になれば右寄せに変えるという動作ですので
プロポーショナルフォントの場合では文字幅が異なりますのでちょっと変な動作になりそう。
また、左寄せの状況で文字を削除していくと左寄せという事がばれる動作をします。
更に、キー入力以外でテキストを代入するような動作は考慮していません。
その辺は、自分で作ってね。出来たら、このTipsを更新してくだされ。
参照: [Delphi-ML:9195] [Delphi-ML:41622] <コンポーネント > <Standard>
0057 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2004/03/04 osamu 編集
フォームの最小化時のアニメーション効果を出す(BCB)
BCBで作成したアプリだけは、他のアプリと異なり、Win95で「アイコン化」したときなどのタイトルバーのアニメーション(タイトルバーがひゅるるっとタスクバーに吸い込まれるようなアレ)が出ないようなのですが、これはBCBの仕様なのでしょうか?あるいは出す方法があるとか??
Delphiでもそうですよ!!
これは、BCB が使用しているライブラリ VCL の仕様のようです。
アニメーションを実現するには、プログラムを組む必要がありますが、(別項でDELPHIでのコード例を見て下さい)
既にこのようなコンポーネントが発表されています。
http://www.delphianworld.com/direct.html?id=SY0042
TAnimateWindowというのがそれのようです。
参照: [Delphi-ML:34432] [builder:4884] <Windows> <フォーム>
0086 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.7 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2004/03/04 osamu 編集
コンパイラを識別するための定義済みシンボル
Pascal コンパイラの バージョン識別子です。
http://3932.rapidforum.com/topic=100288647371
http://www.adug.org.au/Reference/VersionSymbols.htm
http://www.blong.com/Tips/KylixTips.htm
及び[Delphi-ML:67458]より。
Compiler Symbol System Symbol BCB?
Turbo Pascal 4 Ver40 DOS -
Turbo Pascal 5.0 Ver50 DOS -
Turbo Pascal 5.5 Ver55 DOS -
Turbo Pascal 6.0 Ver60 DOS -
Turbo Pascal for Win 1.0 Ver10 Win16 ???
Turbo Pascal for Win 1.5 Ver15 Win16 ???
Borland Pascal 7 Ver70 ??? ???
Delphi 1.0 Ver80 Win16 Windows
Delphi 2.0x Ver90 Win32 Win32
C++ Builder 1 Ver93 Win32 Win32 BCB
Delphi 3 Ver100 Win32 Win32
C++ Builder 3 Ver110 Win32 Win32 BCB
Delphi 4 Ver120 Win32 Win32
C++ Builder 4 Ver125 Win32 Win32 BCB
Delphi 5 Ver130 Win32 Win32
C++ Builder 5 Ver130 Win32 Win32 BCB
Kylix1 Ver140 Linux LINUX
Delphi 6 Ver140 Win32 Win32
C++ Builder 6 Ver140 Win32 Win32 BCB
Kylix2 Ver140 Linux LINUX
その他、CPU386、LINUX32、POSIX(32)、PIC、ELF なども状況により利用可能です。
また、Kylix からは不等号を使った条件分岐ができます。
条件コンパイルするには、[Delphi-ML:67458], [Delphi-ML:67474], [Delphi-ML:67489] を参考にして、
{$IFDEF VER80} {Delphi1} {$ENDIF}
{$IFDEF VER90} {Delphi2} {$ENDIF}
{$IFDEF VER93} {Builder1} {$ENDIF}
{$IFDEF VER100} {Delphi3} {$ENDIF}
{$IFDEF VER110} {Builder3} {$ENDIF}
{$IFDEF VER120} {Delphi4} {$ENDIF}
{$IFDEF VER125} {Builder4} {$ENDIF}
{$IFDEF VER130}
{$IFDEF BCB} {Builder5}
{$ELSE} {Delphi5} {$ENDIF}
{$ENDIF}
{$IFDEF CONDITIONALEXPRESSIONS}
// Kylix1 以上は RTLVersion を持っているので
{$IF RTLVersion < 14.20 - 0.001}
// こういう形で処理できる
// "- 0.001" の部分は2進小数と10進小数との変換誤差を
// 吸収するためのダミー定数 → [Delphi-ML:67489] を参照
{$IFEND}
{$IF Declared(CompilerVersion)}
// Delphi6 以上では CompilerVersion も利用可能
{$IF CompilerVersion < 14.10 - 0.001}
// Kylix1 から Builder6 までに対する処理
{$IFEND}
{$IFEND}
{$ENDIF}
などとします。
ちなみに、中村さんの [Delphi-ML:67486] によれば、
RTLVersion CompilerVersion
Kylix1 14.00 なし
Delphi6 14.10 14.01
Delphi6 Update Pack 1 14.11 14.01
Delphi6 Update Pack 2 14.20 14.01
C++Builder6 14.20 14.01
Kylix 2 14.20 14.10
とのことですが、Nobuyuki Nakashima さんの [Delphi-ML:67489] によれば、2進小数と10進小数との変換誤差で、上記の値ぴったりにはなりません。コード例のように、誤差を考慮した対処が必要です。
http://www.delphianworld.com/direct.html?id=MI0062
のようなインクルードファイルを用意すると楽ができます。
参照: [builder:5430] [Delphi-ML:57660] [Delphi-ML:67458] [Delphi-ML:67474] [Delphi-ML:67486] [Delphi-ML:67489] <開発環境>
0221 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/26 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2004/03/04 osamu 編集
春分、秋分、夏至、冬至の求め方
水野さんが明快なアイデアをだしています。[Delphi-ML:38594]
春分、秋分、夏至、冬至の求め方は、単純に1997年の春分、夏至、秋分、冬至の日時に、地球が太陽の周りを回る周期(365.2422日)を足したり引いたりするだけのものです。
ただ、ここ20〜30年くらいの春分、秋分の日は、上の式で求めたものと同じだそうです。
実際は、計算して人間(政府)が決めるらしいですね。
カレンダーなどの作成に大変便利です。
下記のコードは、時刻部を省略し、日付のみに変えてあります。
type
TSeason = (seVernalEquinox, seSummerSolstice, seAutummnalEquinox, seWinterSolstice);
function JapanHoliday4(myYear: Integer; mySeason: TSeason): TDateTime;
var
myDate: TDateTime;
begin
case Integer(mySeason) of
0: myDate:= StrToDateTime('97/03/20 22:55') + 365.2422 * (myYear - 1997); //春分
1: myDate:= StrToDateTime('97/06/22 03:55') + 365.2422 * (myYear - 1997); //夏至
2: myDate:= StrToDateTime('97/09/23 08:56') + 365.2422 * (myYear - 1997); //秋分
3: myDate:= StrToDateTime('97/12/21 18:49') + 365.2422 * (myYear - 1997); //冬至
end;
Result:= Int(myDate);
end;
またコンポーネントでは、
Delphian World < http://www.delphianworld.com/ >;
に「祝祭日カレンダー」というのがあります。
秋分・春分計算もしてくれる様です。
参照: [Delphi-ML:38539] [Delphi-ML:38594] <日時> <PASCAL>
0115 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/11/21 osamu 編集
子プロセス(DOS)の標準出力をパイプする
こんなんでどうでしょう?(↓)
CreatChildProcess('cmd.exe /c dir');
とやると、Memo1に"dir"の結果が表示されます(NT4.0では動きました)。
ただし、標準出力をリダイレクトしてしまっているので、DOS窓(コンソール)には、なにも表示されません(この例では、コンソールを表示しないようにしていますが、表示しても空のコンソールが表示されるだけです)。
どうしてもコンソールにも表示したい場合は、AllocateConnsoleAPIで自前でコンソールを作成して、Memo1に書く内容を自前で書くのかなぁ?
あとエラー処理等まるっきり省いていますのでご注意を。
function TForm1.CreatChildProcess(exec_name: AnsiString): Integer;
var
buf: array[0..4095] of Char;
hRead, hWrite: THandle;
SecAttrib: TSecurityAttributes;
StartInfo: TStartupInfo;
dwRead: DWORD;
begin
//
// 名前なしパイプの作成
//
FillChar(SecAttrib, SizeOf(SecAttrib), 0);
SecAttrib.nLength := SizeOf(SecAttrib);
SecAttrib.bInheritHandle := TRUE;
CreatePipe(hRead, hWrite, @SecAttrib, 0);
//
// 子プロセスを作成
//
FillChar(StartInfo, SizeOf(StartInfo), 0);
StartInfo.cb := SizeOf(StartInfo);
StartInfo.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
StartInfo.hStdOutput := hWrite;
StartInfo.wShowWindow := SW_HIDE;
if not CreateProcess(Pointer(0),
PChar(exec_name),
Pointer(0), Pointer(0),
TRUE,
0,
Pointer(0), Pointer(0),
StartInfo, ProcInfo) then
begin
Result := -1;
Exit;
end;
WaitForInputIdle(GetCurrentProcess(), INFINITE);
CloseHandle(hWrite);
//
// Windows95ではこの部分で制御を返してくれなくなります。
// もうちょっとしたら対処法を書くつもり。
// いつになることやら。。。(--;
//
While ReadFile(hRead, buf, SizeOf(buf) - 1, dwRead, Nil) do
begin
if dwRead = 0 then
Break;
buf[dwRead] := #00;
Memo1.Lines.Add(buf);
Application.HandleMessage;
end;
CloseHandle(hRead);
Result := 0;
end;
[追記] Autch.net さんの tips にもソースが挙げられているとの情報を得ました。[Delphi-ML:79357]
Autch.net
http://hp.vector.co.jp/authors/VA026252/
GUI アプリからコンソールアプリを実行するには
http://hp.vector.co.jp/authors/VA026252/tips/delphi_anonymous_pipe.html
参照: [Delphi-ML:21973] [Delphi-ML:79357] <Windows>
0349 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/10/02 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/10/02 osamu 編集
二重起動防止措置で既存プロセスを最前面に送る
以下のコードでうまく動くそうです。
定数宣言部分を必要に応じて書き換えて使います。
program Project1;
uses
Windows,
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$R *.RES}
const
MutexName = 'TEST_MUTEX';
ClassName = 'TForm1';
WindowName = 'Form1';
var
Mutex: THandle;
Handle: THandle;
begin
Mutex := OpenMutex(MUTEX_ALL_ACCESS, False, MutexName);
if Mutex <> 0 then
begin
Handle := FindWindow(ClassName, WindowName);
SetForegroundWindow(Handle);
Exit;
end;
CreateMutex(nil, False, MutexName);
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
参照: [Tips:269] [Delphi-ML:78270] <アプリケーション>
0344 D1D2D3D4D5D6D7 3.195 98 作成: 2003/07/03 osamu rev 1.4 B1B3B4B5B6 B7 NT3 NT4 2K XP 更新: 2003/09/24 osamu 編集
Delphi7 のコンボボックスの空文字列でエラー
Delphi7 では、コンボボックスに空文字列を入れておくと、
ComboBox1.Items.Add('');
この Item を Items[0] などとして参照した時にアクセスバイオレーションエラーが発生してしまいます。
これは、以前の Delphi が Item 保持領域として 4096 などの固定値を使っていたのに対して、Delphi7 からは CB_GETLBTEXTLEN を使ってバッファを動的に確保するようになったためで、それ自体は正しいことなのですが、Item の長さが 0 の時に正しくバッファが確保されないためにエラーが発生してしまいます。
エラーの詳しい内容は、[Delphi-ML:77352][Delphi-ML:77360][Delphi-ML:77366]を見てみてください。回避方法としては、Amaito さんの [Delphi-ML:77357] のように、
StdCtrls.pasをプロジェクトと同じフォルダにコピーして、2325行を
//if Len <> CB_ERR then
if Len > 0 then
とするのが良いようです。(CB_ERR は負の値なので、Len > 0 で Len=0 と Len=CB_ERR との両方の場合に対応しています)
#[Delphi-ML:72969] でもすでに同じ問題が報告されていました。
## 実は編集後のコードにもちょっとした仕様バグが含まれています。
## TListBox.Items などと動作を合わせるのであれば、CB_ERR
## の場合には空文字列を返すのではなく例外を投げるべきだと
## 思います。
参照: [Delphi-ML:72969] [Delphi-ML:77352] [Delphi-ML:77357] [Delphi-ML:77360] [Delphi-ML:77366] <バグ> <Standard> <コンポーネント >
0342 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/27 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/08/22 osamu 編集
非ビジュアルコンポーネントの Left, Top 座標を取得
> 設計時にフォーム上に貼り付けた
> 非ビジュアルコンポーネント(TTable,TQuery)の
> フォーム上の位置を取得したいんですけれどもできるんでしょうか?
procedure TComponent.WriteLeft(Writer: TWriter);
procedure TComponent.WriteTop(Writer: TWriter);
の2つのメソッドを見てみると、DesignInfo プロパティの上半分、
下半分がそれぞれ Top, Left に対応するみたいですね。
TheLeft:= LongRec(ACompo.DesignInfo).Lo;
TheTop := LongRec(ACompo.DesignInfo).Hi;
で取り出せるようです。
参照: [Delphi-ML:77269] <その他コンポーネント関連> <コンポーネント >
0346 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/08/22 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/08/22 osamu 編集
プロパティ値を文字列に変換/逆変換
published なプロパティを Ini に書き込みたい・から読み出したいというような時、プロパティ値を文字列に変換する必要が生じます。
Font プロパティを Ini/Registry に保存したい という投稿はFAQに近いですが、この記事はそういった要望への回答となります。
Enum/Set だけではなく、TColor のように「Enum ではないけど文字で書きこまれることがあるプロパティ」にも対応しています。
uses Classes, TypInfo;
function PropertyToString(obj: TPersistent; PropName: string): string;
var IntToIdent: TIntToIdent;
Ident: string;
begin
Result:= GetPropValue(obj, PropName);
if PropType(obj, PropName)=tkInteger then begin
IntToIdent := FindIntToIdent(GetPropInfo(obj, PropName).PropType^);
if Assigned(IntToIdent) then
IntToIdent(GetPropValue(obj, PropName), Result);
end;
end;
procedure StringToProperty(s: string; obj: TPersistent; PropName);
var IdentToInt: TIdentToInt;
i: Integer;
begin
if PropType(obj, PropName)==tkInteger then begin
IdentToInt:= FindIdentToInt(GetPropInfo(obj, PropName).PropType^);
if Assigned(IdentToInt) and IdentToInt(s, i) then begin
SetPropValue(obj, PropName, i);
end else begin
SetPropValue(obj, PropName, s);
end;
end else begin
SetPropValue(obj, PropName, s);
end;
end;
参照: [Delphi-ML:75958] <文字列> <PASCAL>
0277 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/28 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/07/24 西坂良幸 編集
集合型変数の内部構造が知りたい/数値として処理したい。
集合型の変数は、フラッグの集まりです。
例えば、
type
TDbit = 0..25;
TDbits = set of TDbit;
と定義し、
Var D:TDbit;
D := [1,3,4,6]; の場合
内部的には,00000000 00000000 00000000 01011010B
とビット表現されています ~~~~~~~~~
したがって、Integer(D)とキャスト可能で、この値は90です
集合で使うバイト数は次の式で計算します。
ByteSize = (Max div 8) - (Min div 8) + 1
※ 最大255要素ですから、最大32バイト
Min と Max は集合の基本型の下限と上限です。
特定の要素 E が何バイト目かは次の式でわかります。
ByteNumber = (E div 8) - (Min div 8)
そのバイト内で何ビット目かは次の式でわかります。
BitNumber = E mod 8
※ E は要素の順序値を表します。
GetLogicalDrives関数は、 現在利用可能なディスクドライブを表すビットマスクを返しますので、これで、試してみましょう。返り値のビット位置0 (最下位ビット) はドライブAに対応します。
バイトサイズ → (25 div 8) - (0 div 8) + 1 = 4
で、4バイト、DWord型やLongint型が使えます。
TDBitsの最小値は → 0
0 mod 8 → 0 ですから、そのまま対応できます
procedure TForm1.Button1Click(Sender: TObject);
var
d: TDbit;
drvs: TDbits;
DName:string;
begin
ListBox1.Items.Clear;
// マシンのドライブをえる
DWORD(drvs) := GetLogicalDrives;
for d := Low(TDbit) to High(TDbit) do
begin
if d in drvs then
begin
DName := Chr(Integer('A') + d) + ':\';
// ドライブタイプをえる
case GetDriveType(PChar(Char(Ord('A')+d)+':\')) of
DRIVE_REMOVABLE: DName := DName + 'リマーブルデスク';
DRIVE_FIXED: DName := DName + 'ハードデスク';
DRIVE_REMOTE: DName := DName + 'ネットワークデスク';
DRIVE_CDROM: DName := DName + 'CDROMデスク';
DRIVE_RAMDISK: DName := DName + 'ラムデスク';
end;
ListBox1.Items.Add(DName);
end;
end;
end;
次に、
Type
TDrive = 'A'..'Z';
TDrives = Set of TDrive; を使えばどうでしょうか。
TDrivesの最小値は → 'A' 順序値 65
65 mod 8 → 1 ですから、
返り値の0ビットは、TDravesの1ビット目に対応します。
procedure TForm1.Button1Click(Sender: TObject);
var
d: TDrive;
drvs: TDrives;
DName:string;
begin
ListBox1.Items.Clear;
// マシンのドライブをえる−−左に1ビットシフトする
DWORD(drvs) := GetLogicalDrives shl 1;
for d := Low(TDrive) to High(TDrive) do
begin
if d in drvs then
begin
DName := Char(d) + ':\';
// ドライブタイプをえる
case GetDriveType(PChar(d +':\')) of
DRIVE_REMOVABLE: DName := DName + 'リマーブルデスク';
DRIVE_FIXED: DName := DName + 'ハードデスク';
DRIVE_REMOTE: DName := DName + 'ネットワークデスク';
DRIVE_CDROM: DName := DName + 'CDROMデスク';
DRIVE_RAMDISK: DName := DName + 'ラムデスク';
end;
ListBox1.Items.Add(DName);
end;
end;
end;
集合型を内部表現で使うのは、注意が必要です。
なるべく集合型として使いましょう。
なお、集合型の演算子として +,*,-,IN が定義されていますので、
not は、 全定義 - 値
xor は、 和集合 - 積集合で代用できます。
要素数は、4バイトまでの集合なら以下のようなぐあいですか?
function GetElements(Drvs: TDrives):DWORD;
var
i:TDrive;
begin
result := 0;
for i := Low(TDrive) to High(TDrive) do
begin
inc(result,DWORD(Drvs) and 1);
DWORD(Drvs) := DWORD(drvs) shr 1;
end;
end;
*リマーブルデスクって・・・(ぷ
参照: [Delphi-ML:9105] [Delphi-ML:42690] <計算> <PASCAL>
0345 D1D2 D3 D4 D5 D6 D7 3.195 98 作成: 2003/07/18 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/07/18 osamu 編集
スクリーンセーバーの作り方
Ryota Ando さんの、「Delphi で作るスクリーンセーバー制作講座」
http://www02.so-net.ne.jp/~rando/delphi/how2ss/index-f.html
に詳しいです。
参考として、以下の URL も紹介されています。
HowToScr - technical documentation
http://www.wischik.com/scr/resources.html
How to write a 32bit screen saver
http://www.wischik.com/scr/howtoscr.html
screensavergallery.com: Developers Corner
http://www.screensavergallery.com/Developers_Corner/
Borland Technical Information
http://www.borland.com/devsupport/delphi/ti_list/TI4534D.html
Microsoft Visual J++ 6.0 Developer's Workshop
http://mspress.microsoft.com/vstudio/books/sampchap/2275.htm#94
参照: [Delphi-ML:31792] <その他Windows関連> <Windows>
0073 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/07/10 yamamoto 編集
浮動小数点数を整数に丸めるときの注意
Delphiには四捨五入用の関数として Round() がありますが、この関数は「Banker's Rounding」(銀行家の丸め)をするため、普通の四捨五入とは結果が異なることがあるため注意が必要です。
具体的には、引数がちょうど ???.5 となるとき、通常の四捨五入では答は 1 繰り上げた数になりますが、Round() では結果の一の位が偶数になるように切り上げ/切り捨てが行われます。(例: 0.5 は 0,1.5 は 2 になる)[Delphi-ML:4727][Delphi-ML:12693]から始まるスレッドも参考になります。
これに対して Trunc() はいつも小数部分を切り捨てて返しますので、普通の四捨五入をするには、
function Roundoff(X: Extended): Longint;
begin
if x >= 0 then Result := Trunc(x + 0.5)
else Result := Trunc(x - 0.5);
end;
のような関数を作って使用します。
Professional Edition 以上では、Math ライブラリに Floor()/Ceil() の関数もあります。
Int() 関数は Trunc() と同じ答を Extended 型で返します。
参照: [Delphi-ML:4727] [Delphi-ML:12693] [Delphi-ML:20194] [Delphi-ML:20205] <計算> <PASCAL>
0170 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/03/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/27 osamu 編集
Delphi によるレジストリの操作方法
ひきさんのページで詳しい解説をしてくださっているので、参考にすると良いでしょう。
[Delphi 壁の穴]-[その三:レジストリを覗く]
http://hp.vector.co.jp/authors/VA009712/take/delphi/kabereg.htm
以下は内容:
レジストリ操作の基本を知る
レジストリにアプリケーションの設定を保存する
レジストリにファイルの関連づけを設定する
レジストリにDWORD値を記録させる
レジストリにバイナリ値を記録させる
各種のシェルフォルダを得る(レジストリ版)
REGファイルを作る
TRegistryでDeleteKeyできない
TRegIniFileでキーを丸ごと削除したい
キーの指定で「\」付きとなしではどう違うのか
参照: <その他Windows関連> <Windows>
0173 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/03/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/27 osamu 編集
ファイルの更新日時を得る
Windows 的な方法としては、FindFirst/FindClose を使って、得られた TSerchRec を読みます。
別解として、PASCAL の FileAge 関数を使った方法を、ひきさんが以下のホームページで解説してくださっています。
[Delphi壁の穴]-[その二:システムを覗く]
http://hp.vector.co.jp/authors/VA009712/take/delphi/kabesys.htm#datetimefile1
http://hp.vector.co.jp/authors/VA009712/take/delphi/kabesys.htm#datetimefile2
参照: <日時> <ファイル> <PASCAL>
0175 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/03/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/27 osamu 編集
システムアイコンをリフレッシュする
フォルダのアイコンなど、システムで使われるアイコンを変更した後は、ちょっとしたトリックを使わないと画面に反映されません。
具体的には、一旦アイコンの大きさを変更してまた元に戻すのだそうです。ひきさんのページでサンプルコードを見ることができます。
[Delphi壁の穴]-[その二:システムを覗く]
http://hp.vector.co.jp/authors/VA009712/take/delphi/kabesys.htm#refreshsysicon
参照: <Windows> <バグ> <アイコン>
0176 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/03/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/27 osamu 編集
コントロールパネルを作る
コントロールパネル(*.CPL)を作るなんてさぞかし難しいように思われるかも知れませんが、案外簡単に作れてしまいます。とのことです。
ひきさんのページへGo!
[Delphi壁の穴]-[その二:システムを覗く]
http://hp.vector.co.jp/authors/VA009712/take/delphi/kabesys.htm#controlpanel
参照: <その他Windows関連> <Windows>
0174 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/03/08 osamu rev 1.6 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/27 osamu 編集
全てのウィンドウを最小化する
Keybd_event を使って、[WindowsKey(ミ田)]+[M] を押してやると言う手法が、ひきさんの日本Delphi振興会のページで紹介されています。
[Delphi壁の穴]-[その2:システムを覗く]
http://hp.vector.co.jp/authors/VA009712/take/delphi/kabesys.htm#allminimize
Windows+Mを押すと全起動APPが最小化するいうこと知っていましたか?
知らなかったらこのコードは書けませんね。
procedure TForm1.Button1Click(Sender: TObject);
begin
Keybd_event(VK_LWIN, 0, 0, 0); //Windowsキーを押す
Keybd_event(Byte('M'), 0, 0, 0); //Mキーを押す
Keybd_event(Byte('M'), 0, KEYEVENTF_KEYUP, 0); //Mキーを離す{この行:必要ないかも知れない}
Keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, 0); //Windowsキーを離す
end;
Byte('M')の代わりにOrd('M')を用いてもよいでしょう。
そして、この操作で最小化したAPPを元に戻すには、
Windows+Shift+Mキーを押します。
Keybd_eventの書き方は分りますね。Shiftキーの仮想キーコードはVK_SHIFTです。
また、Win98やIE4以降インストールでデスクトップ統合を行った
Windowsでは
Windows+Dキーで
『全ウィンドウの最小化』←→『元に戻す-すべて最小化』
を切りかえられます。
参照: <Windows>
0171 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/03/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/27 osamu 編集
コンボボックスにブラウザのURL入力のような自動補完機能を付けたい
ひきさんのページにサンプルコードを見つけることができます。ひきさんに感謝しつつ、ご利用下さい。
[Delphi壁の穴]-[その一:Delphiを覗く]
http://hp.vector.co.jp/authors/VA009712/take/delphi/kabedel.htm#autocomplete
参照: <Standard> <コンポーネント >
0172 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/03/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/27 osamu 編集
TMemoで、先頭行/最終行/指定行へ移動する。
ひきさんのページにサンプルソースを見つけることができます。ひきさんに感謝しつつ活用しましょう。
[Delphi壁の穴]-[その1:Delphiを覗く]
http://hp.vector.co.jp/authors/VA009712/take/delphi/kabedel.htm#memomove
参照: <Standard> <コンポーネント >
0184 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/11 花井@自宅 rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
「アドレス????????でエラーが発生」からエラー位置を知る
「EAccessViolationがモジュールHOGEHOGE.EXEの0005E1D9で発生しました。モジュール'HOGEHOGE.EXE'のアドレス0045E1D9でアドレスFFFFFFFFに対する読み込み違反がおきました。」
などというメッセージから、エラー発生のソース中での位置を調べることができます。
(IDE 上から)
Delphi の統合環境上のメニュー [検索][エラーの検索] でアドレスを指定します。エラーの起きた場所が見つけられると、その位置にカーソルが移動します。
(コマンドラインから)
コマンドラインコンパイラ(dcc32.exe)が使えます。
(delphi 3の場所)\bin\dcc32 -f5E1D9 hogehoge.dpr
とかすると
Delphi for Win32 Version 10.0 Copyright (c) 1983,97 Borland International
hogehoge.dpr(20)
UNIT1.PAS(775) Target address found.
strcopy(p, title);
^
のように表示されます。ソースのない部分とかWindowsの中で起きている場合は 'target address not found' になります。
ただし、これを使うには、ライブラリパスを指定しておく必要があります。
コマンドラインコンパイラの場合、ライブラリパスは -u オプションで指定します。引数なしで dcc32 を起動すると、オプションの種類が分かります。
常時指定するオプションは dcc32.exe と同じディレクトリにある dcc32.cfg に書き込んでしまっても OK です。こんな感じです。
----------^ dcc32.cfg
-aWinTypes=Windows;WinProcs=Windows;DbiProcs=BDE;DbiTypes=BDE;DbiErrs=BDE
-uE:\DELPHI3\Lib\User;E:\DELPHI3\Lib;E:\DELPHI3\Lib\Delphi2
----------$
追記:コンパイル時に map ファイルを作成しておけば IDE が無くても対応行が分かるそうです。
[Delphi-ML:68565][Delphi-ML:68566][Delphi-ML:68574][Delphi-ML:68592]
参照: [Delphi-ML:19322] [Delphi-ML:4975] [Delphi-ML:68565] [Delphi-ML:68566] [Delphi-ML:68574] [Delphi-ML:68592] <開発環境>
0341 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
Delphi の格言
[Delphi-ML:68385] から始まったツリーなんですが、まとめるに当たって編集者の個人的な好みが反映されています。ご容赦。
1) リソースの取得と開放は try〜finally〜end を使うべし。[Delphi-ML:68385]
2) Assert を使いまくれ。[Delphi-ML:68385]
3) メソッドを override するとき、override キーワードが抜けていないか常にチェックせよ。[Delphi-ML:68385]
4) Except で例外を処理するとき、raise で例外を再生成する必要がないか常にチェックせよ。[Delphi-ML:68385]
5) with文を使用するのは控えること[Delphi-ML:68385][Delphi-ML:68402]
6) フォームは自動生成ではなく自分で明示的に生成しよう![Delphi-ML:68386][Delphi-ML:68402]
7) AnsiString, WideString, Interface, Variant, 動的配列を含むレコードを動的に確保するときは New, Dispose を使うか、動的配列でメモリを確保せよ[Delphi-ML:68387][Delphi-ML:68423]
8) Application.ProcessMessages はその危険性を充分認識して使え。[Delphi-ML:68387]
9) VCL のソースをよく読もう。[Delphi-ML:68387]
10) RECORD に AnsiString/WideString/DynArray を不用意に使うな。[Delphi-ML:68391]
11) RECORD や ARRAY は PACKED が必要か考えろ。[Delphi-ML:68391]
12) 1行処理でも begin 〜 end は必ず付けよう。[Delphi-ML:68401]
13) 「名は体を表す」 変数名やクラス名、コンポーネント名は意味のわかる単語で。[Delphi-ML:68401]
14) バリアント変数の型変換はなるべく関数経由で。[Delphi-ML:68402]
15) try 〜 exceptはエラー情報の損失を最小に。[Delphi-ML:68402]
16) プロパティと同一名をメソッドの仮引数名に使わない。[Delphi-ML:68402]
17) イベントハンドラ内に細かい処理を書かない。[Delphi-ML:68402]
18) 他のフォーム内のコンポーネントは直接触らず、メソッドかプロパティ経由で。[Delphi-ML:68402]
13) 状態変化を伴うメソッド(C++での non-const method)は、なるべく関数でなく手続きに[Delphi-ML:68402]
14) プロジェクトで開発するときは基底モジュールを用意してフォームの継承を活用する[Delphi-ML:68408]
15) TThreadからVCL(TFormとか)を参照するときは必ずSynchonize! [Delphi-ML:68411]
16) public で 非virtual なメソッドは極力再定義するな。[Delphi-ML:68420]
17) オブジェクトが上位から継承する仮想メソッドを呼び出すときは inherited をつけて呼び出すか、そうしないかをよく考えよ。[Delphi-ML:68420]
18) デストラクタは、必ず destroy という名にして、必ず仮想メソッドにする。[Delphi-ML:68420]
19) コンストラクタは protected にしてはいけない[Delphi-ML:68420]
20) New で確保したメモリを破棄するときは、New に渡したポインタと同じ型のポインタを Dispose に渡せ[Delphi-ML:68423]
21) コメントは必要最小限にして、極力コードの可読性を高めよ[Delphi-ML:68429]
22) デフォルト引数は使わず、オーバーロードを使え。[Delphi-ML:68433]
23) {ObjExportsAll On} を忘れずに。[Delphi-ML:68433]
24) 名前だけが異なる複数のコンストラクタは作らない。[Delphi-ML:68433]
IDEの格言
1) アプリケーションをリリースするときは、map ファイルを作るのを忘れずに。[Delphi-ML:68385]
1b) 外に出したようなリリース版は、*.dcu をすべて保存しておいて Delphi を起動して「エラーの検索」を活用する[Delphi-ML:68572]
2) たくさんウィンドウを開いているときは ALT+0 を活用せよ。[Delphi-ML:68385]
3) コンパイルオプションは Ctrl+O+O でソースに埋め込むとIDE のコンパイルオプションの設定に左右されないソースが作れる。[Delphi-ML:68385]
4) デフォルトのコンパイルオプションの変更はトラブルの元。[Delphi-ML:68385]
5) Delphi をインストールしたら「環境オプション]→[設定]→[自動保存の設定]→[エディタファイル]をチェックしよう。[Delphi-ML:68420]
コンポーネントの格言
1) OnExit イベントでダイアログボックスを表示するな。[Delphi-ML:68385]
2) コンポーネントの Loaded メソッドが呼ばれることを仮定したコンポーネントを作ってはいけない。[Delphi-ML:68385]
3)自動生成されるコンポーネント名をそのままでほったらかすな。意味のわかりやすいコンポーネント名に変えろ。[Delphi-ML:68398][Delphi-ML:68402]
番外編
1) 『プログラミング作法』を読もう。[Delphi-ML:68404]
http://www.ascii.co.jp/books/detail/4-7561/4-7561-3649-4.html
人間関係の格言???[Delphi-ML:68543]
1.感情的に物事を判断しない。
2.例え正しい事でも、言い方や伝え方には気を使う。
3.コーディングスタイルには、ある種の宗教的要素が存在する事を認めて無駄な議論にならないように。
(みんな、自分のスタイルが正しいと信じている。)
4.ユーザーがどのように使い運用するのかを常に考える。
(ユーザーとのコミュニケーション無しに役立つソフトはできない。)
5.最初から完全なソフトウェアを目指そうとしてはいけない。
(実際に運用してみないと分からないもんです。
役立つソフトであれば仕様変更は必ずあります。必要最低限の機能を早くリリースするように心がけましょう。)
参照: [Delphi-ML:68385] <開発環境> <PASCAL>
0019 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
OnDblClick発生時にOnMouseDownはいらない!
ダブルクリック発生をOnDblClickで捕まえたいのに、1度目のクリックでOnMouseDownが発生してしまう。
ダブルクリックとシングルクリックの両方を捕まえるにはどうするか?
procedure TForm1.FormMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var t:LongInt;
begin
//
// シングル/ダブルクリックの判定
//
if ssDouble in Shift then begin
DoubleClickOccurred[Button]:=True;
end else begin
DoubleClickOccurred[Button]:=False;
t:=GetTickCount+GetDoubleClickTime;
while GetTickCount<t do begin
Application.ProcessMessages;
if DoubleClickOccurred[Button] then
Exit;
end;
end;
if ssDouble in Shift then begin
case Button of
mbLeft: ;
mbMiddle: ;
mbRight: ;
end;
end else begin
case Button of
mbLeft: ;
mbMiddle: ;
mbRight: ;
end;
end;
end;
ここで、
DoubleClickOccurred:array [TMouseButton] of Boolean;
は、TForm1のメンバ変数です。
シングルクリックの際の動作でチョット遅れた感じがするのはしかたないですね。
参照: [Delphi-ML:7386] [Delphi-ML:7392] [Delphi-ML:7457] <コンポーネント > <その他コンポーネント関連>
0043 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
ComboBoxのDropDownListの幅を変更する
エディット部分はそのままで、ドロップダウンする部分の幅だけを変更したければ、
ComboBox1.Perform(CB_SETDROPPEDWIDTH, 幅, 0);
とします。
「幅」はピクセル単位となります。
参照: [Delphi-ML:19165] <Standard> <コンポーネント >
0340 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
自己実行形式の動画
Exe 内部に動画データをバイナリデータのリソースにしてコンパイルします。D5でのリソースファイルの作り方について:
http://halbow.cool.ne.jp/Notes/N008.html
例えば、'clock.avi' をリソースにする場合、リソーススクリプトファイル(MyRes.rc) は以下のようになります。
MyAVI AVI "c:\WINNT\clock.avi"
このリソースを使って以下のようにして、うまくいきました。
procedure TForm1.Button1Click(Sender: TObject);
var
RS:TResourceStream;
begin
RS := TResourceStream.Create(hInstance,'MyAVI','AVI');
try
RS.SaveToFile(ExtractFilePath(ParamStr(0))+'MyAVI.avi');
finally
RS.Free;
end;
if FileExists(ExtractFilePath(ParamStr(0))+'MyAVI.avi') then
with MediaPlayer1 do begin
Filename := ExtractFilePath(ParamStr(0))+'MyAVI.avi';
DeviceType := dtAVIVideo;
Notify := false;
Open;
Notify := true;
Play;
end;
end;
procedure TForm1.MediaPlayer1Notify(Sender: TObject);
begin
MediaPlayer1.Close;
if FileExists(ExtractFilePath(ParamStr(0))+'MyAVI.avi') then
begin
DeleteFile(ExtractFilePath(ParamStr(0))+'MyAVI.avi');
ShowMessage('AviFile has Deleted!');
end;
end;
参照: [Delphi-ML:76119] <その他Windows関連> <System> <Windows> <コンポーネント >
0339 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
フォルダもファイルも選べるダイアログ
Q.デスクトップ 〜 右クリック 〜 新規作成 〜 ショートカットの時に「参照」ボタンを押しますと、Caption は「フォルダの参照」とかになっていながらフォルダもファイルも選べるダイアログが出てきます。こいつを使用したいのです。
A.ShBrowseForFolder で BIF_BROWSEINCLUDEFILES を指定すると良いようです。
with BrowseInfo do begin
hwndOwner := FhParent;
pidlRoot := nil;
pszDisplayName := BDisplayName;
lpszTitle := 'フォルダを選択してください';
ulFlags := BIF_STATUSTEXT or BIF_RETURNONLYFSDIRS or
BIF_BROWSEINCLUDEFILES; // ここを追加
lpfn := @BrowseFolderProc;
lParam := integer(@BFFR);
end;
参照: [Delphi-ML:76043] <Dialogs> <コンポーネント >
0338 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
Ini ファイルに published プロパティを保存する方法
published プロパティを Ini ファイルなどに保存するためにテキストに/テキストから変換する方法を [Delphi-ML:75958] に載せました。
例えば、Font.Style プロパティを [fsBold] などとして保存することができます。
参照: [Delphi-ML:75958] <アプリケーション> <PASCAL>
0337 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
ボタン間を矢印キーで移動させるロジック
Q.フォーム上にボタンがたくさんあり、上下左右の矢印キーによって、実際のボタンの配置から期待される順序でフォーカスを動かしたいのですが。
A.設計時の手間はかかるのですが、各ボタンを個別にパネルの上に乗せてしまえば、矢印キーによる通常のフォーカス移動を無効にできるので、ボタンの OnKeyUp イベント内で移動先を設定できると思うのですがいかがでしょうか。
参照: [Delphi-ML:75689] <コンポーネント > <Standard>
0336 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
RichEdit を印刷したい
Delphi の Demo にサンプルコードが含まれています。Delphi5 であれば、
Program Files\Borland\Delphi5\Demos\Richedit\richedit.dpr
に RichEdit の印刷ルーチンがあります。
参照: [Delphi-ML:54315] <印刷> <Win95> <コンポーネント >
0335 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
Install Shield Express でアンインストール時にレジストリを削除できない
> Delphi3.1に付属の InstallShield Express にてインストール時
> に設定したキーをアンインストール時に掃除させようとしているの
> ですがうまくいきません。(キーが全く削除されない)
私もキーが削除できなくて困った経験があり、何かのショー(忘れました)で、インストールシールドをデモしているブースがあったので、聞いてみたところ、Delphi付属のInstallShieldでは「出来ません」という簡単な回答でした。
では、どうすればよいかと尋ねたところ、Professional版(有料)を購入し、自分でプログラミングしてください、ということでした。(ほかにもいろいろな機能を持っているそうです)
参照: [Delphi-ML:32275] <開発環境> <配布>
0334 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
ActiveX でショートカットキーが使えない
ActiveXをd3・d4で取り込むと ActiveX に組み込まれているショートカットキーが使えないものがあります。
どうも、TOLEControlの親であるTWinControlでショートカットのメッセージが止まっていて、activeXに渡されてないようなのです。
ちょっとうまい対処の方法が思いつかないのですが、とりあえずApplication.OnMessage をつかってOleInPlaceActiveObject.TranslateAccelerator(Msg); で、activeXに直接伝えれば、症状を回避できるようです。
由木尾@蛸薬師倶楽部さんが具体的なコードを [Delphi-ML:32242] に示してくださっています。
参照: [Delphi-ML:32242] <その他Windows関連> <Windows> <バグ>
0332 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
DLL にブレークポイントが設定できない
今までWin2000 + Del5Entの環境で開発していました。OSをWinXPに変えたところ、DLLのデバッグができなくて困っています。
D5 + WinXP では DLL のデバッグができないようです。
D7 では正常に動作するとの報告があります。
参照: [Delphi-ML:72312] [Delphi-ML:73399] <開発環境> <バグ> <デバッグ> <DLL>
0331 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
FindFirst でアイコンを変更したディレクトリを検索できない
Delphi7, WinXPです。XPにはフォルダのアイコンイメージを変更することができるのですが、これを適用したフォルダをFindFirstは認識しません。
この投稿に簡単な解決法は投稿されませんでした。FindFirstFileEx API や Shell 関連の API を使うことで回避できるかもしれないとの意見がありました。
参照: [Delphi-ML:73372] <Windows> <ファイル>
0330 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2003/06/25 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2003/06/25 osamu 編集
FindFirst による4文字以上の拡張子の判別
Q.FindFirst,FindNextを使用してファイル名を取得しているのですが、PATH定数を "C:\TEST\*.jpg" と指定した場合、拡張子が「.jpg」のファイルだけでなく退避用にリネームした”XXXX.Jpg_”といったファイルまでヒットしてしまいます。
A.WinXPには4文字以上の拡張子を正しく認識させるためのレジストリが用意されています。以下の方法を試してみてはいかがでしょうか。Win2000でも同じことができるかどうかはわかりません。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem の Win95TruncatedExtensions を 0 にして再起動。
参照: [Delphi-ML:73374] <Windows> <ファイル>
0327 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
Photoshop のようなツールウィンドウを実現したい
1.細いタイトルバーでクローズボタンのみが付いている
2.アプリケーションがアクティブであればツールウィンドウのタイトルバーもアクティブ色
3.常にアプリケーションのフォームよりも手前にある
というようなツールウィンドウを実現しようと思うと、フォーカス関連のイベントが貧弱な Delphi ではなかなか難しいようです。
[Delphi-ML:66799]、[Delphi-ML:66801] で書かれているように、アプリケーションに飛んでくるすべてのメッセージの中からフォーカスの移動に関係するものをチェックしてやることで実現が可能です。実際のツールウィンドウはコード中の TToolWindow を継承して作成することになります。
参照: [Delphi-ML:66799] <アプリケーション> <フォーム>
0326 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
HTMLタグ表記の大文字・小文字変換を行う
コードはSJISを仮定します。
HTMLのコメント部分、""などで囲まれたパラメータ部分などは変換しません。
速度を気にするのであれば IsDBCSLeadByte は避けるべきです。自分で文字テーブルを作れば速くなります。ByteType の方は最適化されていたと思います。通常の用途では速度が気になることは無いかもしれませんね。
function ChangeHTMLTagCase(s:string; Lower:Boolean):string;
const
sNormal = 0;
sInComment= 1;
sInBracket= 2;
sInQuote = 3;
sInDQuote = 4;
sAfterEq = 5;
var
p: PChar;
state: Integer;
begin
state:= 0;
Result:= s;
UniqueString(Result);
p:= PChar(Result);
while p^<>#0 do begin
if IsDBCSLeadByte(Ord(p^)) and ((p+1)^<>#0) then begin
Inc(p, 2);
end else begin
case state of
sNormal:
if p^='<' then
if StrLComp(p, '<!--', 4)=0
then state:= sInComment
else state:= sInBracket;
sInComment:
if p^='-' then
if StrLComp(p, '-->', 3)=0 then
state:= sNormal;
sInBracket:
case p^ of
'''': state:= sInQuote;
'"': state:= sInDQuote;
'=': state:= sAfterEq;
'>': state:= sNormal;
'A'..'Z': if Lower then p^:= Chr(Ord(p^)+32);
'a'..'z': if not Lower then p^:= Chr(Ord(p^)-32);
end;
sInQuote:
case p^ of
'''': state:= sInBracket;
end;
sInDQuote:
case p^ of
'"': state:= sInBracket;
end;
sAfterEq:
case p^ of
'''': state:= sInQuote;
'"': state:= sInDQuote;
' ': state:= sInBracket;
'>': state:= sNormal;
end;
end;
Inc(p);
end;
end;
end;
参照: [Delphi-ML:66840] <WWW> <文字列> <通信> <PASCAL>
0325 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
文字列から括弧の中のみを削除する
特に、全角の括弧と半角の括弧に対応した方法を紹介します。一旦WideStringに直しているところが味噌で、こうすることによってコードの見通しが非常に良くなります。
function DeleteInBracket(s:string):string;
const
ZenAki = $FF08;
ZenToji= $FF09;
HanAki = $0028;
HanToji= $0029;
var
PrevPos,Len,BTimes,i:integer;
ss:WideString;
begin
ss := s;
i := 1; PrevPos := 1;Len := Length(ss);BTimes := 0;
result := '';
while i < Len+1 do begin
case Ord(ss[i]) of
ZenAki,HanAki:begin
Inc(BTimes);
if BTimes = 1 then result := result+Copy(ss,PrevPos,i-PrevPos);
end;
ZenToji,HanToji:begin
Dec(BTimes);
if BTimes = 0 then PrevPos := i+1;
end;
end;
inc(i);
If BTimes < 0 then Break;
end;
if BTimes = 0 then
result := result+Copy(ss,PrevPos,i-PrevPos)
else if BTimes < 0 then begin
ShowMessage('カッコが閉じていません-- ( が足りません' );
result := result+Copy(ss,PrevPos,i-PrevPos);
end else begin
ShowMessage('カッコが閉じていません-- ) が足りません' );
end;
end;
また、こういった文字列処理には正規表現を使うとかなり楽ができます。たとえば、
http://www2.big.or.jp/~osamu/Delphi/MyLibrary.htm
の BRegExp ユニットを使えば、
uses BRegExp;
function RemoveParentheses(text: string): string;
begin
Result:= text;
while brx.Subst('s/(\(|()[^()()]{3,1024}(\)|))//gk', Result) do;
end;
とするだけでできてしまいます。
s/// の k というオプションは、BREGEXP.DLL に固有のもので、正規表現中の SJIS を SJIS として認識してくれるので、2バイト文字にも特殊な処理はいらないです。
参照: [Delphi-ML:66865] <文字列> <PASCAL>
0324 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
列挙型(Enum)の値と文字列との変換
TBrushStyle や TPenStyle などの値を文字列として取り出したい、またはそのような文字列からもとの値に戻したい場合、TypInfoユニットにあるGetEnumNameやGetEnumValueを使用します。
uses TypInfo;
function AlignmentToString(Value: TAlignment): String;
begin
Result := GetEnumName(TypeInfo(TAlignment),Ord(Value));
end;
こんな感じですね。
参照: [Delphi-ML:66981] <文字列> <PASCAL>
0323 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
TIdentToInt型とTIntToIdent型
IdentToColor は TIdentToInt 型の変換関数で、TIntToIdent型 とともに、整数型のプロパティを見やすくするために登録される関数です。
TColorのような特定の整数型に対し、TIdentToInt 型の関数とTIntToIdent型の関数を RegisterIntegerConsts で登録しておくとコンポーネントの整数型プロパティの特定の値を見やすい文字表現でオブジェクトインスペクタに表示することができます。
また、dfm ファイルも読みやすくなります。
また、実行時に任意の整数型に対して
function FindIntToIdent(AIntegerType: Pointer): TIntToIdent;
function FindIdentToInt(AIntegerType: Pointer): TIdentToInt;
で変換関数が存在するか調べることもでき、整数を見やすい文字表現に変換したり、その逆も可能です。
参照: [Delphi-ML:66991] <コンポーネント開発>
0321 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
2つの TMemo のスクロールを同期させる
Q:
memo1 のスクロールバーを動かすと、memo2 も同じようにスクロールさせたいです。
A:[Delphi-ML:67004] Halbow さん
垂直スクロールバーについてだけ、回答します。Memo1 と Memo2 は Width や Height 、Font.Size など同一のプロパティーであると仮定しています。
TRichEdit で同様のことをする方法が[Tips:322]にあります。
type
TForm1 = class(TForm)
Memo1: TMemo;
Memo2: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
public
Memo1Proc:TWndMethod;
MKEY:WORD;
procedure Memo1SuclassProc(var Msg:TMessage);
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1Proc := Memo1.WindowProc;
Memo1.WindowProc := Memo1SuclassProc;
end;
procedure TForm1.Memo1SuclassProc(var Msg: TMessage);
begin
Memo1Proc(Msg);
case Msg.Msg of
WM_VSCROLL:Memo2.Dispatch(Msg);
CN_COMMAND:
if (Msg.WParamHi = EN_VSCROLL) then begin
case MKEY of
VK_DOWN:Memo2.Perform(EM_LINESCROLL,0,1);
VK_UP :Memo2.Perform(EM_LINESCROLL,0,-1);
VK_HOME:if (GetAsyncKeyState(VK_CONTROL) and $8000) <> 0 then begin
Memo2.SelStart := 0;
Memo2.Perform(EM_SCROLLCARET,0,0);
end;
VK_END:if (GetAsyncKeyState(VK_CONTROL) and $8000) <> 0 then begin
Memo2.SelStart := Length(Memo2.Text);
Memo2.Perform(EM_SCROLLCARET,0,0);
end;
end;
MKEY := 0;
end;
CN_KEYDOWN:MKEY := Msg.WParam;
end;
end;
参照: [Delphi-ML:67004] <Standard> <コンポーネント > [Tips:322]
0322 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
2つの TRichEdit のスクロールを同期させる
2つのTMemo のスクロールを同期させるやり方が[Tips:321]にありますが、実は TRichEdit だと同じようにしてもうまく行きません。
解決方法は、やはり Halbow さんの[Delphi-ML:67313]にあります。
うまく行かないのは、TRichEdit では CN_COMMAND を通じたスクロール通知メッセージ EN_VSCROLL が発行されないためです。EM_SETEVENTMASK というメッセージを送って、LParam に ENM_SCROLL を設定すると、親に EN_VSCROLL を通知するようになります。
親にイベントを通知するかどうかをプログラムで設定するためのこのようなマスクについて、[Delphi-ML:67328]にさらに詳しい説明があります。
public
RichEdit1Proc:TWndMethod;
MKEY:WORD;
procedure RichEdit1SuclassProc(var Msg:TMessage);
procedure SyncVScroll;
procedure PageDown;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses
richedit;
procedure TForm1.FormCreate(Sender: TObject);
begin
RichEdit1Proc := RichEdit1.WindowProc;
RichEdit1.WindowProc := RichEdit1SuclassProc;
end;
procedure TForm1.FormShow(Sender: TObject);
var
MASK:LPARAM;
begin
MASK := RichEdit1.Perform(EM_GETEVENTMASK,0,0);
RichEdit1.Perform(EM_SETEVENTMASK,0,MASK or ENM_SCROLL);
end;
procedure TForm1.RichEdit1SuclassProc(var Msg: TMessage);
begin
RichEdit1Proc(Msg);
case Msg.Msg of
WM_VSCROLL:RichEdit2.Dispatch(Msg);
CN_COMMAND:begin
if (Msg.WParamHi = EN_VSCROLL) then
case MKEY of
VK_DOWN,VK_UP,VK_PRIOR:SyncVScroll;
VK_NEXT:PageDown;
VK_HOME,VK_END:
if (GetAsyncKeyState(VK_CONTROL) and $8000) <> 0 then
SyncVScroll;
end;
end;
CN_KEYDOWN:MKEY := Msg.WParam;
end;
end;
procedure TForm1.SyncVScroll;
var
Msg:TWMScroll;
begin
Msg.Msg := WM_VSCROLL;
Msg.Pos := GetScrollPos(RichEdit1.Handle,SB_VERT);
Msg.ScrollBar := 0;
Msg.ScrollCode := SB_THUMBTRACK;
RichEdit2.Dispatch(Msg);
Msg.ScrollCode := SB_THUMBPOSITION;
RichEdit2.Dispatch(Msg);
Msg.ScrollCode := SB_ENDSCROLL;
RichEdit2.Dispatch(Msg);
end;
procedure TForm1.PageDown;
var
Msg:TWMScroll;
begin
Msg.Msg := WM_VSCROLL;
Msg.Pos := 0;
Msg.ScrollBar := 0;
Msg.ScrollCode := SB_PAGEDOWN;
RichEdit2.Dispatch(Msg);
end;
参照: [Delphi-ML:67313] [Delphi-ML:67328] <Win95> <Standard> <コンポーネント > [Tips:321]
0320 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
文字列や画像データをリソースに埋め込むためのコンポーネント
プログラム内部で使いたい文字列や画像のデータを、フォーム上に置いた非ビジュアルコンポーネントに保存しておくと、アプリケーションと別にファイルを配布する必要が無くなり、便利な場合があります。
そういった用途に使えるコードが[Delphi-ML:67285]のスレッドに紹介されています。ただし、画像データなどは文字列にエンコードされますので、そのままファイルを持つ場合に比べ、ファイルサイズは大きくなってしまいます。
[Delphi-ML:50664]で中村さんが紹介されたコンポーネントは任意のバイナリデータを内部に持てるようです。
参照: [Delphi-ML:67285] <画像> <文字列> <PASCAL>
0319 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
ActiveX 内部から自身の親ウィンドウのハンドルを得る
Q:
ActiveXコントロールに関する事です。親ウインドウをサブクラス化しようとする時に必要なウインドウハンドルをparent等で、取得することができませんでした。(nilが返される)
A:
IE4 以降の ActiveX コントロールでは、ウィンドウハンドルを持たないことが多い ( ウィンドウレスコントロール ) ので、親ウィンドウのウィンドウハンドルは自分自身のウィンドウハンドルであることが多いです。
Internet Explorer のようなアプリケーションを見て貰えるとわかるかもしれませんが、Windows でいう「ウィンドウ」は一番親にあたるコンテナ1つ以外には一切作成されなかったりします。
それで、ActiveX コントロールが乗っかっているコンテナのウィンドウハンドルはは、InPlaceSite プロパティの GetWindow メソッドを利用すれば得られます。
参照: [Delphi-ML:67312] <その他Windows関連> <Windows>
0318 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
OnShow イベント中で SetFocus すると不具合
フォームにMemoを貼り付けて実験してみたのですが
procedure TForm1.FormShow(Sender: TObject);
begin
Memo1.SetFocus;
end;
とすると「ソフトを起動したとき、それがアクティブウィンドウになるにもかかわらず、そのソフトを示すタスクバーのボタンが押されない状態で表示される」という症状が出るようです。
Memo1 の TabOrder をゼロにするなど、別の方法でフォーカスをコントロールする必要があるようです。
参照: [Delphi-ML:67321] <アプリケーション> <フォーム>
0317 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
異なるバージョンのDelphiを1つのPCに共存させる
基本的に問題は起こりません。ただし、古いものから順に入れていったほうが良いと思われます。気をつけるべき点としては、
最終的に *.dpr などのファイルがどのバージョンのDelphiに関連付けられるか。
BDE のバージョンに依存するコードが無いか。
delphimm.dll など、同じ名前でバージョンの異なるDLLで問題が起こらないか。
といったところでしょうか。
*.dpr などのファイルを複数のバージョンから簡単に開けるようにするには、レジストリの HKEY_CLASSES_ROOT\DelphiProject\Shell 以下に
[HKEY_CLASSES_ROOT\DelphiProject\Shell\Open3]
@="Delphi&3で開く"
[HKEY_CLASSES_ROOT\DelphiProject\Shell\Open3\command]
@="\"D:\\Programs\\Borland\\Delphi 3\\BIN\\Delphi32.exe\" \"%1\""
みたいなそれぞれに対応するキーを作っておくと、Explorer から右クリックで任意のバージョンの Delphi でそれを開くことができます。
参照: [Delphi-ML:67330] [Delphi-ML:67329] <開発環境>
0316 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/05/17 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/05/17 osamu 編集
TreeView で D&D 中の描画処理
TreeView のアイテムをドラッグ・ドロップする際に、挿入位置をグラフィカルに表示するためのサンプルコードです。[Delphi-ML:67348] Halbow さん作。
procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
var
AnItem: TTreeNode;
r:TRect;
begin
AnItem := TreeView1.GetNodeAt(X, Y);
if not Assigned(AnItem) then exit;
r := AnItem.DisplayRect(true);
with TreeView1.Canvas do begin
Brush.Color := TreeView1.Color;
Pen.Color := clRed;
Font := TreeView1.Font;
TextRect(r,r.Left+2,r.Top+1,AnItem.Text);
if (GetAsyncKeyState(VK_CONTROL) and $8000) = 0 then begin
Brush.Style := bsClear;
Rectangle(r.Left,r.Top,r.Right,r.Bottom)
end else begin
MoveTo(r.Left+1,r.Bottom-1);
LineTo(r.Right-1,r.Bottom-1);
end;
end;
end;
参照: [Delphi-ML:67348] <Win95> <コンポーネント >
0315 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2002/04/01 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/04/04 osamu 編集
国際化アプリケーションの作り方
日本語フォントを使わない、など基本的なこと以外にも、ダイアログ関連を修正する必要があります。
詳細は過去ログにいくつか存在します。
[Delphi-ML:44083], [Delphi-ML:53963]
「英語版」「国際化」などで検索すると、他にも見つかるかも知れません。
また、Microsoft のサイトに国際化対応ソフトウェア開発のためのページがあります。そこの情報も一読されることをお薦めします。
Top ページ
http://www.microsoft.com/globaldev/
Guidelines
http://www.microsoft.com/globaldev/guides/guides.asp
その他の注意点としては、
・小数点は "." 、リストの区切りは ","、日付は"年月日の順"など
とは仮定してはいけない。
・そのため 数値←→数値文字列、日付←→日付文字列の変換はロケー
ルを適切に使用する Delphi の関数/手続きを使用する。
・でも HTML や XML などの属性値における小数点は必ず "."。
あたりは常に意識しておいたほうが良いでしょう。
ちょっと古い本ですが国際化対応のアプリケーションを作ろうとする場合とても参考になる本として、
Developing International Software For Windows 95 and Windows NT
Microsoft Press 発行 Nadine Kano 著
http://www.microsoft.com/globaldev/dis_v1/disv1.asp
があります。オンラインで内容すべてを閲覧できます。
参照: [Delphi-ML:44083] [Delphi-ML:53963] [Delphi-ML:66183] <アプリケーション>
0314 D1 D2 D3 D4 D5 D6 D7 3.195 98 作成: 2002/02/09 鈴木充 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2002/02/09 鈴木充 編集
TListView の ... 表示をなくす
> 自作ソフトにTListViewを使っているのですが、ViewStyleをvsListにした場
> 合、実行時にListItemのCaptionを編集して元のCaptionより長くなった場合、
> 入りきらない部分が"..."表示になるのですが、これをすべて表示するように
> するにはどうすればよいのでしょうか?
ListView1.Perform(CM_RECREATEWND, 0, 0);
で表示されるはずですが、選択状態・スクロール状態等は解除されるので必要に
応じて状態を復元する処理を行ってください。
参照: [Delphi-ML:64465] <Win95> <コンポーネント >
0313 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2001/12/05 濱野 rev 1.5 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2001/12/27 濱野 編集
VBのMIDステートメント
Delphi6でVB互換の関数などが追加されました。
Mid関数もそのひとつですが、部分文字列の挿入
であるMidステートメントはありません。
S = "1234567890"
Mid(S, 3, 5) = "ABC"
そこで逆Mid関数ともいうべきMidステートメントを
シミュレートする手続きを作ってみました。
procedure MidInsert(var S1: String; P, N: Integer; S2: String);
var
L: Integer;
begin
L := Length(S1);
if N > (L - P + 1) then
N := (L - P + 1);
if N > Length(S2) then
N := Length(S2);
Delete(S1, P, N);
Insert(Copy(S2, 1, N), S1, P);
end;
{ テスト例: }
procedure TForm1.Button1Click(Sender: TObject);
var
S: String;
begin
S := '1234567890';
MidInsert(S, 5, 3, 'ABC');
ShowMessage(S);
end;
Delphiの文字列型は配列と同様の扱いも出来るのでDelete, Insert, Copyを
使わない別の方法もあります。一時オブジェクトが作成されない分、
こちらの方が高速に動作するかもしれません。
procedure MidInsert(var S1: String; P, N: Integer; S2: String);
var
L, I: Integer;
begin
L := Length(S1);
if N > (L - P + 1) then
N := (L - P + 1);
if N > Length(S2) then
N := Length(S2);
StrMove(Pchar(@S1[P]), PChar(S2), N);
end;
参照: <文字列> <PASCAL>
0312 D1D2 D3 D4 D5 D6 D7 3.195 98 作成: 2001/06/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2001/06/11 osamu 編集
ListView をスクロールする
> データセット後に任意の行を表示させたいのですが、
> TopItem プロパティだと read のみで変更できません。
> TStringGrid では TopRow プロパティにて行う事ができますが、
> TListView では何か良い方法はあるのでしょうか?
ListView1->Selected->MakeVisible(false or true);
でスクロールすることができます。
参照: [builder:31260] <Win95> <コンポーネント >
0311 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2001/06/05 濱野 rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2001/06/06 濱野 編集
<Delphi Install Dir>\BINディレクトリの中のファイル
DelphiのインストールされているBINディレクトリには
アプリケーション以外にも様々なファイルが存在します。
その中でIDE(Delphi32.EXE)と同名のファイルがいくつか
存在し、これらはレジストリ以外のIDEの設定を保存する
のに使用されているようです。
[Delphi 3/4]
.dci : コードテンプレート(テキスト)
.dct : コンポーネントテンプレート(バイナリ)
.din : メニューのキャプションで和名を使ったときの
オブジェクト命名表(テキスト)
.dmt : メニューテンプレート(バイナリ)
.dro : オブジェクトリポジトリのプロパティ(テキスト)
.dsk : IDEの表示状態(テキスト)
Delphi32.dciやDelphi32.dinはテキストエディタで編集
出来ますがその他は不整合が出る恐れがあるためIDE以外
では弄らないほうが無難です。
参照: <開発環境>
0310 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2001/06/02 濱野 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2001/06/02 濱野 編集
フォームの新規作成のデフォルトを変更したい
例えばはじめからScaled=Falseにしたい、
文字は固定ピッチで大きめのフォントにしたい、
フォームの位置、大きさ、表示状態を最大化で標準化したい、etc....
こういう時、元になるフォームを[プロジェクト]-[リポジトリに追加]
でリポジトリに登録しておき、[ツール]-[リポジトリ]で登録した
フォームを選択して、[フォームの新規作成時に使用]にチェックを入れて
おけばスピードバーや[ファイル]-[フォームの新規作成]を選んだときに
リポジトリに登録したものと同じ内容でフォームを作成することが出来ます。
ただしあくまでもコピーが作成されるだけなので元のフォームを変更しても
その変更は反映されません。そのような変更が予想される場合は[ファイル]
-[新規作成]で[継承]を選んで使用すると後々便利です。
コントロールのデフォルト変更には[コンポーネント]-
[コンポーネントテンプレートの作成]を使用するといいでしょう。
Delphi 5からはさらにフレーム機能という便利な機能もあります。
参照: <開発環境> <フォーム>
0309 D1D2D3 D4 D5 D6 D7 3.1 95 98 作成: 2001/05/25 濱野 rev 1.6 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2001/06/02 K.Takaoka 編集
トークンの切り出し
Delphiのライブラリにはstrtokのようなトークンの切り出し
を行なう関数や手続きが無い為、自作することになります。
以下のコードはCopyとIsDelimiter関数を使ったトークンの
切り出し例です。
{$APPTYPE CONSOLE}
program Typing;
uses
SysUtils;
procedure TypingToken(S, Delims: String);
var
TokenIn : Boolean; // 進行中であるかのフラグ
TokenS, TokenLen, // トークンの開始位置と長さ
I: Integer;
begin
TokenIn := False;
TokenS := 0;
TokenLen := 0;
for I := 1 to Length(S) do
begin
if not IsDelimiter(Delims, S, I) then begin
if TokenIn then
Inc(TokenLen)
else begin
TokenS := I;
TokenLen := 1;
TokenIn := True
end
end else begin
if TokenIn then begin
Writeln(Copy(S, TokenS, TokenLen));
TokenIn := False;
TokenLen := 0;
end
end
end;
if Tokenlen > 0 then
Writeln(Copy(S, TokenS, TokenLen));
end; { TypingToken }
const
Delims = #9#10#13'[](){};, ';
var
Buf: String;
begin
while not EOF(Input) do begin
Readln(Buf);
TypingToken(Buf, Delims)
end
end.
※リダイレクトしない場合、最後に[Ctrl]+[Z]を入力
して下さい。
参照: <文字列> <PASCAL>
0274 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/24 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2001/05/14 jkaneko@fujita.co.jp 編集
ディレクトリのファイルを列挙する
以下のようにするのが定石です。
ポイントは,
2バイト文字の2バイト目に '\' が来る可能性を考慮する
-> IsPathDelimiter 関数
FindFirst が成功したときのみ FindClose を呼ぶ。
-> [Delphi-ML:17508]
repeat until を使うとループがスマートに書ける
'.' や '..' といった特殊ディレクトリも列挙される
といったところです。
SearchRec.FindData には見つかったファイルに関するさらに多くの情報が詰まっています。ヘルプを参照しましょう。
procedure Form1.EnumFiles(DirectoryName: string);
var
SearchRec: TSearchRec;
begin
// 一番後ろに '\' がついていなければ付ける
DirectoryName:= IncludeTrailingBackslash(DirectoryName);
// FindFirst が成功した場合のみ FindClose を呼ぶ必要がある
if 0=FindFirst(DirectoryName+'*.*', faAnyFile, SearchRec) then try
repeat
if SearchRec.Attr and faDirectory <> 0 then begin
// カレントディレクトリや親ディレクトリをスキップ
if (SearchRec.Name='.') and (SearchRec.Name='..') then
Continue;
// ディレクトリに対する処理
// SearchRec.Name にディレクトリ名が入っている
// たとえば、Memo1.Lines.Add('Dir :'+DirectoryName+SearchRec.Name);
end else begin
// ファイルに対する処理
// SearchRec.Name にファイル名が入っている
// たとえば、Memo1.Lines.Add('File:'+DirectoryName+SearchRec.Name);
end;
until 0<>FindNext(SearchRec);
finally
FindClose(SearchRec);
end;
end;
参照: <ファイル>
0202 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/13 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2001/04/05 濱野 編集
Oracle の Number 型で入力値が化ける
BDE の 5.01 よりも前のバージョンで、Oracle 8.0.4 のテーブルのNUMBER型で桁数が7,2の項目に 99.999 と入力すると 10.00 になってしまいます。これは、BDE のバグです。
http://www.borland.co.jp/download/other.html
で BDE を 5.01 以上にバージョンアップすることで回避されます。詳しくは [Delphi-ML:41554] から始まるスレッドを参照してください。
参照: [Delphi-ML:41554] <データベース> <バグ>
0284 D1 D2 D3 D4 D5 D6 D7 3.195 98 作成: 1999/10/12 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2001/04/05 濱野 編集
メモリマネージャのマルチスレッド対応 -- IsMultiThread
以下,
http://www.borland.co.jp/interbase/tech/ib_udf/delphi_udf_2.html
からの抜粋です。Delphi のメモリマネージャをマルチスレッドで利用する際に IsMultiThread グローバル変数を使わなければならないことを解説しています。
>>>>>>>>>>>>>>>> ここから
スレッドセーフティの問題を解決するために、Inpriseはメモリ管理ルーチンをクリティカル・セクションにラップすることによってメモリ・マネージャをスレッドセーフにしました。クリティカル・セクションについて説明することは本稿の範囲を外れているので詳しくは述べませんが、共用リソースへ秩序正しくアクセスできるようにするものであるとだけ考えておいてください。
Inpriseはその際に、クリティカル・セクションはあまりよく知られていない変数IsMultiThreadがTrueに設定されている場合しか使用されないという規則を設定しました。(IsMultiThreadは、すべてのDelphiユニットによって暗黙的に使用されるユニット 'System.pas' で定義されます。)
つまりこういうことなのです。Delphiは確かにスレッドセーフですが、それは開発者がそのように指示した場合だけです。アプリケーションやライブラリで複数のスレッドを扱う可能性がある場合は、必ずIsMultiThreadをTrueに設定しなければなりません。そうでないと、そのアプリケーションやライブラリはスレッドセーフになりません。(重要:IsMultiThreadはTThreadオブジェクトを使用した場合は暗黙的に設定されます。)
マルチスレッド環境でIsMultiThreadをTrueに設定することの必要性は、いくら強調してもしすぎることはありません。
参照: [Delphi-ML:43304] <PASCAL>
0285 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/10/12 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2001/04/05 濱野 編集
スレッドローカル変数 -- threadvar の使い方
http://www.borland.co.jp/interbase/tech/ib_udf/delphi_udf_3.html
に解説が載っています。
参照: <PASCAL>
0306 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2000/08/25 ITO, Sakuya rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2000/09/08 ITO, Sakuya 編集
整数値が奇数かどうかの判定
Delphiで整数値が奇数かどうかの判定をするには Pascal の標準関数 Odd を使用して次のようにします。
if Odd( int ) then
奇数のときの処理
else
偶数のときの処理
参照: <PASCAL>
0158 D1D2 D3 D4 D5 D6 D7 3.195 98 作成: 1999/02/11 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3NT4 2K XP 更新: 2000/08/25 ITO, Sakuya 編集
ComCtl32.DLL を配布するには
Delphi4 で作成した ComCtrl32.DLL のバージョンに依存するプログラムに ComCtl32.DLL を添付して配付すると MS の EUL に違反します。CDに入ってる 40comupd.exe を添付すれば問題はないです。
インストーラを利用する場合は /q をオプションにつけて呼び出すとMicrosoft の承諾文章が出ませんがその場合の注意点なんてのもあるので詳しくは Microsoft のサイトの EUL を読んでください。
# まあ、Win95/Win98, WinNT で ComCtl32.DLL に互換性ないし
# ComCtl32.DLL を含めて配付できたとしても面倒ですね。
//2000.08.25 by Sakuya ITO
その時点で最新のComCtl32.DLLの配布用インストーラ ??comupd.exe は、Microsoftの次のURLからダウンロードできます。これ1本で日本語を含む各国言語に対応しています。
http://www.microsoft.com/msdownload/ieplatform/ie/comctrlx86.asp
参照: [Delphi-ML:30766] <Windows> <配布>
0300 D1D2 D3 D4 D5 D6 D7 3.19598作成: 2000/04/06 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3NT4 2K XP 更新: 2000/08/12 ITO, Sakuya 編集
Dual CPU 機でフォームのフォントに不具合
開発環境がNT4 + Dual CPUだと、IDEで Code Explorer を表示させていると、 Object Inspector や作ったフォームなどのフォントが勝手に変わるというおぞましい現象がでます。Delphi 4で経験し、Delphi 5でも解消していませんでした。ちょっと使用に耐えなかったので、泣く泣く1個 CPU を外しました。
Delphi 5 Update Pack #1 では治っているそうです。
//追記 2000-08-12
日本語版 Delphi 5 は Update Pack #1 で修正されますが、US版では UP1 をあてても解消されません。
参照: [Delphi-ML:47436] <バグ>
0303 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2000/06/01 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2000/06/01 osamu 編集
色表現について(RGB, HSV, YUV など)
色を数値で表現する有名な方法として、RGB, HSV, YUV などがありますが、このような色表現同士の相互変換およびこれらを用いた画像処理に関してよくまとめてあるページを紹介します。
カラーのお話 -詳細- :
http://robotics.me.es.osaka-u.ac.jp/~koji/html/color_detail.html
参照: <画像>
0301 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2000/05/26 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2000/05/26 osamu 編集
DLLの初期化・終了処理
D3 のヘルプでは DLL の終了処理に ExitProc を使うように書かれていますが、ExitProc のヘルプには ExitProc が下位互換性のためだけに残してあるものである旨の記述があります。実際(?)、ExitProc はパッケージの使用時に正常に動作しないようです。
このような問題を回避するためには、System ユニットにある DLLProc という変数を使います。
library MyLibrary;
uses Windows, SysUtils;
...
procedure DLLEntry(ul_reason_for_call: DWORD);
begin
case ul_reason_for_call of
// これらの値の意味については、Win32SDK の
// DLLEntryPoint の HELP を参照してください。
DLL_PROCESS_ATTACH:
;
DLL_PROCESS_DETACH:
;
DLL_THREAD_ATTACH:
;
DLL_THREAD_DETACH:
;
end;
end;
begin
DllProc := @DLLEntry;
DLLEntry(DLL_PROCESS_ATTACH);
end.
なお、初期化部分でエラーを発生させて DLL のロードを失敗させたい場合には、
(1) 初期化コードで System.ExitCode を 0以外に設定する。
(2) 初期化コード内で例外を起こす。
のいずれかを行います。
参照: [Delphi-ML:39301] [Delphi-ML:39147] <バグ> <DLL> <PASCAL>
0299 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2000/04/06 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2000/04/06 osamu 編集
Currency型の小数演算結果が不正
Currency型で小数点以下の計算をすると、環境によっては結果が期待した値にならない場合があるそうです。
[例]
procedure TForm1.Button1Click(Sender: TObject);
var
num1, num2: Currency;
ret: string;
begin
num1 := StrToCurr('888888888888888.8888');
num2 := StrToCurr('0.8888');
ret := FormatCurr('#,##0.0000', num1 - num2);
ShowMessage(ret);
end;
この結果が『888888888888888.0128』になります。
この問題を解決するには、Currency や Extended 型の演算を行う前に、Set8087CW(Default8087CW); なるおまじないを入れておけばよいらしい。プロジェクトソースの一行目にでも書いておきましょう。
未確認ながら Delphi4 では治っていると言う報告があります。
[例]
begin
Set8087CW(Default8087CW);
num1 := StrToCurr('888888888888888.8888');
参照: [Delphi-ML:47431] <バグ> <PASCAL>
0297 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2000/03/24 濱野 rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2000/03/28 濱野 編集
C++Builder でwith文の代用
BCBには(というかC++には)Pascalのwith文に相当するものが
無い為、クラスのメンバの階層が深いとコード1行あたりの文字数
が無茶苦茶長くなってしまいます。
こんな時はポインタか参照変数を使えば、コード量を減らす事が
出来ます。
void __fastcall TForm1::StringGrid1DrawCell(TObject *Sender, long Col, long Row,
TRect &Rect, TGridDrawState State)
{ char *s;
TCanvas &cv = *StringGrid1->Canvas;
s = StringGrid1->Cells[Col][Row].c_str();
if (strchr(s, '?') != NULL)
cv.Font->Color = clRed;
else
cv.Font->Color = clBlack;
cv.TextOut(Rect.Left + 2, Rect.Top + 2,
StringGrid1->Cells[Col][Row]);
}
参照: <C++>
0298 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 2000/03/25 濱野 rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2000/03/28 濱野 編集
あるアドレスを基点とした、変位のアドレスの参照
バイト列を処理する場合、あるアドレスを基点に
そこから+nバイト目を数バイトずつ処理したい場合があります。
特に文字処理や画像処理ではこの様なことは頻繁に行われます。
Cであれば(p+1)* = '\0'; といったアドレッシングを行えますが、
Pascalでは強引な型キャストでも行わない限りこのような書き方は
行えません。
Pchar(Integer(p) + n)^ := #0;
のようになってしまいます。Integerとポインタのサイズが同じ
であるので、このような書き方でも通ります。
しかし、WORD,DWORDなどもっと大きな型の場合SizeOfなどを使っ
て型のサイズを意識した書き方を行わなければなりません。
PWORD(Integer(pw) + Sizeof(WORD) * n)^ := 0;
このような場合、必要なサイズ分の配列のポインタ型を定義して
やり、その変数に代入することでかなり近い事が行える様になり
ます。
例では、'Delphi 'という文字を、10回繰り返し配列
にセットしています。
type
CharArray = array[0..6] of char;
PCharArray = ^CharArray;
var
p: array[0..100] of Char;
C: PCharArray;
I : Integer;
begin
C := @p;
for I := 0 to 9 do
begin
C^[0] := 'D';
C^[1] := 'e';
C^[2] := 'l';
C^[3] := 'p';
C^[4] := 'h';
C^[5] := 'i';
C^[6] := ' ';
Inc(C);
end;
C^[0] := #0;
Edit1.Text := StrPas(p);
参照: <PASCAL>
0296 D1D2D3D4 D5 D6 D7 3.195 98 作成: 2000/01/02 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2000/01/02 osamu 編集
TField.EditMask の y2k 回避
> DBEditで日付入力させるのですが、関連付けているTTable側の
> 日付フィールドのTFieldのEditMaskプロパティで
>
> !99/99/00;1;_
>
> ・・とマスクをかけています。
> すると、西暦を下2桁で打つマスクが有効になるのですが
> 2000/05/05 の意で 00/05/05 とか打つと、期待した
> 2000/05/05にならずに、1900/05/05と表示されてしまいます。
OnCreate イベントハンドラ内で
TwoDigitYearCenturyWindow := 50;
ShortDateFormat := 'yyyy/mm/dd';
の設定を行うと,現在の年が1999年のとき,1999年の前後50年である
1949/1/1 から 2048/12/31 の100年を変換の対象として扱います.
ただし D4 からの機能のようです。
詳しくはヘルプか以下のサイトをご覧になられるとよいと思います.
http://www.inprise.com/devsupport/y2000/
【追記】
Delphi 5 になってから TwoDigitYearCenturyWindow グローバル変数の初期値が「0」から「50」に変更されました。UI 簡略化等で西暦2桁年を使っているような場合、TwoDigitYearCenturyWindow 変数を設定しないまま日付型に変換しているような処理では、処理結果がD4以前と変わる場合があります。
ヘルプの記述(初期値は0)は誤っていますので、ご注意ください。
参照: [Delphi-ML:37908] [Delphi-ML:43093] <データベース> <コンポーネント > <DataControls>
0190 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/19 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 2000/01/02 osamu 編集
StringGrid の内容更新を高速に行う
| 少々データ量が大きいので時間を稼ぐため、画面表示の Update を
| 停止したいのですが TStringGrid に BeginUpdate/EndUpdate は
| 無いようです。
【解法1】
描画更新の一次停止
SendMessage(StringGrid1.Handle, WM_SETREDRAW, 0, 0);
描画更新の再開
SendMessage(StringGrid1.Handle, WM_SETREDRAW, 1, 0);
StringGrid1.Refresh;
この方法は、すべての TWinControl に対して有効です。
【解法2】
描画更新の一次停止
StringGrid1.Rows[0].BeginUpdate;
描画更新の再開
StringGrid1.Rows[0].EndUpdate;
Rows[0] は Rows[1] でも何でも構いませんが、停止と開始で同じ添字を使わなければなりません。Cols[x] も使えます。一つの Rows[x] または Cols[x] に対して BeginUpdate すると StringGrid のすべての表示更新が停止になります。
BeginUpdate は TStrings の仮想メンバ関数なので、TMemo.Lines とか TListBox.Items などで BeginUpdate を使って TMemo/TListBox などの変更を高速に行うこともできます。
【メモ】
描画更新の再開を必ず行うために、BeginUpdate/EndUpdate は try/finally で保護した方が良いです。
StringGrid1.Rows[0].BeginUpdate;
try
// 更新処理
finally
StringGrid1.Rows[0].EndUpdate;
end;
また、BeginUpdate を繰り返し呼んだ場合には EndUpdate を同じ回数呼ばなければ描画は再開されません。さらに、BeginUpdate よりも多く EndUpdate を呼んでしまうとおかしなことになってしまうので注意が必要です。
参照: [Delphi-ML:38146] [builder:19539] <Additional> <コンポーネント >
0289 D1D2 D3 D4 D5 D6 D7 3.19598作成: 1999/12/19 Atsushi Shinoda rev 1.3 B1B3B4B5 B6 B7 NT3NT42K XP 更新: 1999/12/28 osamu 編集
Windows2000の新APIを使った半透明ウィンドウ
最近、雑誌の付録についていたWin2kRC2をインストールして「半透明ウィンドウを作ってみよう」と思い立ち、TClock(KAZUBON氏作のフリーソフト)のソースファイルを参考に作ってみたので紹介します。
ウィンドウの拡張スタイルにWS_EX_LAYEREDを指定して、SetLayeredWindowAttributesというAPIを使います。このAPIの詳細は http://msdn.microsoft.com/library/psdk/winui/windows_1p6b.htm を参照してください。(英語)
Win2kを使っている方は、ちょっとした遊びのつもりで試してみてください。(^^;;;
FormにScrollBarを1つ置いて、FormCreate, FormDestroy, ScrollBar1Changeのイベントを使っています。ScrollBarを動かすと、Formの透明度が変化します。
{API宣言}
type
TSetLayeredWindowAttributes
= function(wnd: HWND; crKey: DWORD;
bAlpha: BYTE; dwFlag: DWORD): Boolean; stdcall;
const
WS_EX_LAYERED = $80000;
LWA_ALPHA = 2;
var
hLibUser32: THandle;
MySetLayeredWindowAttributes: TSetLayeredWindowAttributes;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var
p: Pointer;
begin
hLibUser32 := LoadLibraryA('user32.dll');
MySetLayeredWindowAttributes := nil;
if hLibUser32 <> 0 then begin
p := GetProcAddress(hLibUser32, 'SetLayeredWindowAttributes');
if p = nil then begin
FreeLibrary(hLibUser32);
hLibUser32 := 0;
end else begin
MySetLayeredWindowAttributes := TSetLayeredWindowAttributes(p);
end;
end;
if hLibUser32 <> 0 then begin
SetWindowLong(Handle, GWL_EXSTYLE,
GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_LAYERED);
ScrollBar1.Position := ScrollBar1.Max;
ScrollBar1Change(Self);
end else begin
ShowMessage('OSが半透明ウィンドウに対応していません!');
Application.Terminate;
end;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if hLibUser32 <> 0 then begin
FreeLibrary(hLibUser32);
hLibUser32 := 0;
end;
end;
procedure TForm1.ScrollBar1Change(Sender: TObject);
var
alpha: Integer;
begin
if hLibUser32 <> 0 then begin
alpha := ScrollBar1.Position;
alpha := alpha * 255 div (ScrollBar1.Max - ScrollBar1.Min);
if alpha < 8 then alpha := 8;
if alpha > 255 then alpha := 255;
MySetLayeredWindowAttributes(Handle, 0, Byte(alpha), LWA_ALPHA);
end;
end;
参照: [Delphi-ML:44902] <その他Windows関連> <Windows>
0295 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/12/22 濱野 rev 1.1 B1B3B4B5 B6 B7 NT3 NT4 2K XP 更新: 1999/12/22 濱野 編集
フォームを1つずつ表示する
TFormの配列をスタック風に管理し、呼び出し元は
呼び出されたフォームを表示後、非表示にします。
これでフォームが1つずつ表示されるようになります。
前のフォームへの復帰も再表示するだけなので速いです。
この手続きにちょっと手を加える事で位置やサイズを
揃える事も出来ます。
unit FmDsp;
interface
uses
Windows, Messages, SysUtils, Classes, Forms;
procedure SetMainForm(MainForm: TForm);
procedure ShowNextForm(FormClass: TFormClass; Form: TForm);
procedure ShowPrevForm;
implementation
type
TShowType = (stWithCreate, stShowOnly);
TFormRec = record
Form: TForm;
ShowType: TShowType;
end;
var
FormAry: array[0..100] of TFormRec;
FormTop: Integer = 0;
{ 最上階のフォームの登録、プログラムのスタートアップ時
に一回だけ呼び出す。 }
procedure SetMainForm(MainForm: TForm);
begin
FormAry[0].Form := MainForm;
FormAry[0].ShowType := stShowOnly;
FormTop := 0;
end;
{ 次のフォームの表示
FormClass: フォームのクラス名
Form: フォーム変数名、フォームが自動作成でない場合 nilを指定 }
procedure ShowNextForm(FormClass: TFormClass; Form: TForm);
begin
Inc(FormTop);
if Form = nil then
begin
FormAry[FormTop].Form := FormClass.Create(Application);
FormAry[FormTop].ShowType := stWithCreate;
end else
begin
FormAry[FormTop].Form := Form;
FormAry[FormTop].ShowType := stShowOnly;
end;
FormAry[FormTop].Form.Show;
FormAry[FormTop - 1].Form.Hide;
end;
{ 前のフォームの表示、呼び出された側のフォームのCloseイベントで呼び出し }
procedure ShowPrevForm();
begin
FormAry[FormTop - 1].Form.Show;
if FormAry[FormTop].ShowType = stWithCreate then
FormAry[FormTop].Form.Release
else
FormAry[FormTop].Form.Hide;
Dec(FormTop);
end;
end. { unit FmDsp }
参照: <フォーム>
0294 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/12/21 濱野 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/12/21 濱野 編集
ランダムなレコードの移動
Random関数とMoveByメソッドを使います。
MaxRecordはあらかじめRecordCountプロパティやSELECT * FROM文などで
求めておきます。
利用目的としてテストデータの作成や練習問題の出題などに活用
出来ると思います。
procedure RandomMove(Dataset: TDataSet; MaxRecord: Integer);
var
X : Integer;
begin
X := Random(MaxRecord);
DataSet.First;
DataSet.MoveBy(X);
end;
参照: <データベース>
0252 D1D2 D3 D4 D5 D6 D7 3.195 98 作成: 1999/09/09 濱野 rev 1.3 B1 B3 B4 B5 B6 B7 NT3NT4 2K XP 更新: 1999/11/30 K.Takaoka 編集
スクリーンセーバーの名前を変更する。
スクリーンセーバーに名前を付ける場合、{$D }コンパイラ指令でリソース文字列を埋め込んでもコントロールパネルの画面の設定には反映されません。これはWin95/WinNT4.0からの新GUIに移行した際に、コントロールパネルで表示されるスクリーンセーバーの名前はファイル名そのものに変更されたためです。
しかし、 Delphi のプロジェクト名には日本語を使う事は出来ないので、日本語名を付けたい場合はバッチなどで変更するようにします。
COPY C:\MyProj\MySaver.exe "C:\Windows\まい せーばー.scr"
ただし、ファイル名が 8 バイトまでの場合に、先頭が SS で始まっていると、SS は削除されます。
例) sstest.scr → test
Microsoft の技術文章によると Win16/Win32 ともに、スクリーンセーバーの説明は {$D } にて 25 文字までで指定しておくことになっています。
この値を有効にするためには、ファイル名を 8 文字までにし、すべてを大文字にする必要があります。
ex) SSTEST.SCR
このようにすることで {$D} にて設定した 25 文字までの内容をコントロールパネルのスクリーンセーバーの選択肢として表示できるようになります。
参照: <その他Windows関連> <Windows>
0287 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/11/16 K.Takaoka rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/11/16 K.Takaoka 編集
INI ファイルを扱うもう1つのクラス
みなさん TIniFile を利用して Windows の .ini というファイルを扱われていると思いますが、Inifiles Unit には TMemIniFile というクラスがあります。
TIniFile は Windows API をカプセル化しているので以下のような制限・仕様があります。
・ファイルサイズは 64k bytes までしか認識しない
・1つのセクションの大きさは 32k bytes までしか扱えない
・コメントの概念が存在しない(無意味なキーを使って実現)
・クオーテーションを自動除去する
・Win95/98 では TAB 文字が扱えない
・WInNT では読み書きキャッシングが行われない
最初の2つは容量の制限。
最後の2つは重要で、Win95/98 では TAB 文字を扱えないために
[Section]
Key=Value タブで区切って Key の意味を説明書き
なんて記述ができてしまい、WinNT でヒドイ目を見ますし、 WinNT では INI ファイルへの読み書きをレジストリに読み書きするためのマッピング処理のため、キャッシュが効かずに低速です。
また、キャッシングの有無に違いがあるため、キャッシュの操作を忘れると INI ファイルを頻繁に読み書きする際に挙動不審になるという弊害もあります。
TMemIniFile は上のような点をすべて解決します。
・ファイルサイズの制限はシステムによってのみ制限される。
・セクションの大きさはファイルサイズによってのみ制限される。
・行頭が ; で開始する行をコメントとして扱う
( ';hoge' のようなキー名は扱えないということです )
・クオーテーションの自動削除は行わない
・OS に関係なく行末までを読み出すことができる
・すべての変更は常にメモリ上にキャッシュされる
( UpdateFile を呼ばずに Free すると変更は無効になります )
と、なります。
TMemIniFile の保存処理で String 型の変数を介すため、2G Bytes までということになりますが、すべての Win32 環境で利用可能な最大メモリ量が 1.995G Bytes ですので、この制限は問題にならないでしょう。
参照: <Windows>
0286 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/10/14 K.Takaoka rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/10/14 K.Takaoka 編集
終了処理の中の例外を見逃すな !
Object Pascal の構造化例外処理の 1 つである try 〜 finally には思わぬメモリリークの可能性があります。
ObjectPascal言語ガイドには次のように記述されています。
-begin-
生成された例外が finally で処理されない場合,その例外は try...finally 文を越えて伝わり,try 節で既に生成されている例外は失われます。したがって,ほかの例外の伝播の妨げにならないよう,ローカルに生成された例外はすべて finally 節で処理するようにしてください。
-end-
つまり、
try
保護ブロック
finally
終了ブロック
end;
という記述の中で、保護ブロックの中で例外が発生した場合に、さらに終了ブロックで補足されない例外が発生してはいけないことになります。
A = TA.Create;
try
A.Start;
A.MethodCall; // ここで例外が起こる可能性がある !
finally
A.Stop; // ここでも例外が起こる可能性がある !
A.Free;
end;
ではなく
A = TA.Create;
try
A.Start;
A.MethodCall; // ここで例外が起こる可能性がある !
finally
try
A.Stop; // ここでも例外が起こる可能背がある !
except
// A.Stop が失敗したときに必要な処理があればココへ
end;
A.Free;
end;
などと、finally 節内での例外を補足し処理しなければなりません。
# 補足し処理するので try 〜 except 構文を用います。
参照: [Delphi-ML:41492] [Delphi-ML:41495] <PASCAL> []
0283 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/10/12 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/10/12 osamu 編集
Pascal 版 auto_ptr -- Free の必要の無い高機能ポインタ
[Delphi-ML:41732] で、C++ での auto_ptr にあたる高機能ポインタを Delphi 上で実現する方法を中村@NECさんが紹介してくださっています。使用後に Free することに煩わされること無く、TObject/record/MemoryBlock/string/自動配列 を動的に確保して利用することができます。
これを使うと、さまざまなリソースを変数と同じ寿命にしたり、レコードやオブジェクトが破棄されるときに、レコードやオブジェクトからポイントされているさまざまな資源を自動破棄できたりします。
finally より強力です。
技術的には、Interface の参照カウントと、System._Dispose を有効活用しています。_Dispose に付いては [Tips:282] も参照。
例:使用した レコードを自動破棄する
uses AutoPointer;
type
TXXRecord = record
str: string;
:
:
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var pRecord: ^TXXRecord;
AutoPtr: IAutoPtr;
begin
New(pRecord);
AutoPtr := TAutoPtr.Create(pRecord, TypeInfo(TXXRecord));
:
:
end; // ここで レコードが自動破棄される
参照: [Delphi-ML:41732] [Tips:282] <PASCAL>
0282 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/10/07 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/10/07 osamu 編集
System ユニットの _ で始まる特殊ルーチンの呼び方
_Dispose など、System ユニットには _ で始まる特殊なルーチンがいくつかある。これを無理やり自分で呼び出したいときには,
procedure __Dispose(p: Pointer; typeInfo: Pointer);
asm
call System.@Dispose
end;
のようにする。
そんなん何につかうん?
と言う人は [Tips:283] を見てみよう。
参照: [Delphi-ML:41727] <PASCAL>
0281 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/10/07 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/10/07 osamu 編集
文字列リソースの編集ツール - 文字列テーブルエディタ
> .RCファイルは手でも書けるということですが、ツールがあれば使
> いたいと思いましたのでヘルプを探してみました。
> すると、Delphi4のヘルプに、「文字列テーブルエディタ」という
> ものがあったのですが使い方が分かりません。
これの表示の仕方ですが、
1.適当なプロジェクトを開く
もちろん、いじりたいリソースが resourcestring で定義されている
プロジェクトです。エラーメッセージだけ編集するなら resourcestring
がなくても OKAY(エラーメッセージもこれでいぢれるんですね!)
2.新規作成 → リソースDLL
対象になる dfm を追加し、リソースDLLのプロジェクトを作成して
ください。
3.表示 → プロジェクトグループ
プロジェクトグループの一覧が出ます。(すでに出てないなら)
ここから、2 で作成した言語のプロジェクトを開いてください。
4.言語.rc をダブルクリック
これでOK!
参照: [Delphi-ML:34134] <開発環境> <その他Windows関連> <Windows>
0280 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/10/07 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/10/07 osamu 編集
OnExit ハンドラで次にフォーカスを受け取るコントロールを知る
> OnExit 内で、この後フォーカスが移動するコンポーネントを
> 知ることは出来るのでしょうか?
私はこうやってます。
procedure XXXXXExit(Sender:TObject);
begin
:
If ActiveControl = 該当のコンポーネント then
:
end;
参照: [Delphi-ML:34067] <コンポーネント > <その他コンポーネント関連>
0279 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/10/07 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/10/07 osamu 編集
大きなソースブロックのコメントアウト
コーディング中に数十行にわたるコードを一気にコメントアウトしたくなること、ありません?そんな時、どうしましょ?
コード中で {} を使っていなければ、{} でくくればおしまいですが,{} が入っていると、コメントがそこで途切れてしまいます。
以下のような方法を紹介します。
1) {$IFDEF} を使う
{$IFDEF DUMM_DUMMY_DUMMY}
ちょっとコメントアウト
{ コメントアウトしたいコード }
..
{$ENDIF}
2) (* *) を使う
(* や *) は、{ や } と同等に使えますが,コンパイラはこれらを区別するため,コード中に { } を使ってあっても、 (* や *) を使っていなければ,これが便利です。
(* ちょっとコメントアウト
{ コメントアウトしたいコード }
...
*)
参照: [Delphi-ML:34048] <PASCAL>
0278 D1 D2 D3 D4 D5 D6 D7 3.195 98 作成: 1999/09/30 篠田 rev 1.2 B1 B3 B4 B5 B6 B7 NT3NT4 2K XP 更新: 1999/09/30 篠田 編集
アクティブデスクトップを使って壁紙を変更
アクティブデスクトップを使うには、IActiveDesktop を使います。
Microsoftが提供しているリファレンスはhttp://msdn.microsoft.com/library/sdkdoc/shellcc/Shell/IFaces/IActiveDesktop/IActiveDesktop.htmにあります。
uses ShlObj, ComObj;
// IActiveDesktop を使って壁紙を変更する
procedure TForm1.Button1Click(Sender: TObject);
var
hObj : IUnknown;
ADesktop : IActiveDesktop;
str : String;
wstr : PWideChar;
begin
hObj := CreateComObject(StringToGUID('{75048700-EF1F-11D0-9888-006097DEACF9}'));
ADesktop := hObj as IActiveDesktop;
if OpenDialog1.Execute then begin
wstr := AllocMem(MAX_PATH);
StringToWideChar(OpenDialog1.FileName, wstr, MAX_PATH);
ADesktop.SetWallpaper(wstr, 0);
ADesktop.ApplyChanges(AD_APPLY_ALL);
FreeMem(wstr);
end;
end;
ここでActiveDesktopのクラスIDとして {75048700-EF1F-11D0-9888-006097DEACF9} というマジックナンバーが使われていますが、もしかしたら将来Windowsのバージョンが変わるとこの値も変わる可能性があります。
しかし[Delphi-ML:42104]に書かれているように、アクティブデスクトップはWindowsの準(?)標準コンポーネントですので、恐らく問題ないと思います。
一応、\\HKEY_CLASSES_ROOT\CLSID\{75048700-EF1F-11D0-9888-006097DEACF9}\(標準) に "ActiveDesktop" という文字列が登録されているのを見つけることができます。
参照: [Delphi-ML:42096] <Windows> <デスクトップ>
0233 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/03 西坂良幸 rev 1.6 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/27 西坂良幸 編集
短いファイル名を長いファイル名に変換したい。
長いファイル名を短いファイル名にするのは、APIにGetShortPathNameという関数があるで簡単ですが([Tips:68])、逆の長いファイル名にするのはちょっと工夫が必要です。
APIのFindFirstFile関数を使うとパラメータのWIN32_FIND_DATA構造体のcAlternateFileNameに短いファイル名あり、長いファイル名はcFileNameにあります。
したがって、長い→短い、短い→長いの変換は基本的には、ファイル名を使ってもう一度FindFirstしてやればいいわけです。
効率を無視すれば次のようものでしょうか。
この関数ではドライブレターの大文字小文字は変換されませんので、必要であれば後から大文字に変換してください。
function ShortToLongFileName(ShortName: String):String;
var
SearchRec: TSearchRec;
begin
result:= '';
// フルパス化
ShortName:= ExpandFileName(ShortName);
// 長い名前に変換(ディレクトリも)
while LastDelimiter('\', ShortName) >= 3 do begin
if FindFirst(ShortName, faAnyFile, SearchRec) = 0 then
try
result := '\' + SearchRec.Name + result;
finally
// 見つかったときだけ Close -> [Delphi-ML:17508] を参照
FindClose(SearchRec);
end
else
// ファイルが見つからなければそのまま
result := '\' + ExtractFileName(ShortName) + result;
ShortName := ExtractFilePath(ShortName);
SetLength(ShortName, Length(ShortName)-1); // 最後の '\' を削除
end;
result := ShortName + result;
end;
※ [Tips:273]に同様の結果を得るCOM版があります。
参照: [Delphi-ML:3333] [Delphi-ML:7320] [Delphi-ML:7322] [Delphi-ML:17508] [Tips:68] [Tips:273] <ファイル>
0108 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/27 西坂良幸 編集
デスクトップにあるアイコンの数と位置を知りたい。
通常デスクトップには色々なアイコンがありますが、それらの数とそれらが置かれている位置(X,Y座標)をアプリケーション中で把握したいのです。
デスクトップを覆っているリストビューコントロールに対して、メッセージを投げるなりAPIを発行するなりしてください。
Commctrl.pas の ListView_XXXXXX関数は、結構豊富です。テストすることをお勧めします。
以下サンプルプログラムです。
uses Commctrl;
// デスクトップリストビューのハンドル所得
function GetDesktopListView: HWnd;
var
Hdl:HWnd;
begin
Hdl := FindWindow('Progman', 'Program Manager');
if not (Hdl = 0) then
begin
Hdl := FindWindowEx(Hdl, 0, 'SHELLDLL_DefView', '');
if not (Hdl = 0) then
begin
result := FindWindowEx(Hdl, 0, 'SysListView32', '');
exit;
end;
end;
result := 0;
end;
// アイコンの表示数をえる
procedure TForm1.Button1Click(Sender: TObject);
var
hLView:Hwnd;
begin
hLView := GetDesktopListView;
if not (hLView = 0) then
Label1.Caption := AnsiString('Count: ')
+ IntToStr(ListView_GetItemCount(hLView))
else
Label1.Caption := 'ハンドルの取得に失敗しました';
end;
詳細は、Win32 SDK Referenceを調べてください。
また、IE4&Active Desktopをインストールしている環境では動かないかもしれません。
参照: [builder:5968] <Windows> <アイコン>
0276 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/27 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/27 西坂良幸 編集
アップリケーションにサウンドリソースを埋め込んで使いたい。
リソースファイルには、*.WAVファイルを埋め込むことが出来ます。しかし、これは手作業で行わなければなりません。
たとえば、TEST.WAV というファイルがあるとします。
・このファイルをproject1.dprと同じディレクトリに起きます。
・メニューから[ファイル|新規作成−テキスト]を選びます。
・このテキストに、
MYSOUND WAVE TEST.WAV
と1行を書き込み、PROJTEST.rc(適当でよい)という*.rcファイルとしてセーブします。
※ TEST.WAVが同じディレクトリでないときはフルパスで指定
※ リソースは、原則、大文字です。
・binディレクトリにあるbrcc32.exeで、*.resファイルを作成します。
brcc32.exe PROJTEST.rc
※ コマンドラインEXE であることに注意
・project1.dprを開き作成したリソースファイルを書き込みます
・・省略・・・
{$R *.RES} // デフォルトである
{$R projtest.res} // ←ここを書き加える
begin
Application.Initialize;
・・省略・・・
以上で、プロジェクトの再構築を行えば、EXEの中にTEST.WAVのデータが埋め込まれます。
Demosディレクトリの中にあるリソースエクスプローラの見本をコンパイルして、このプロジェクトのEXEファイルを読み込むと、WAVEリソースが出来ていることがわかります。
以下は、簡単な使用方法です。
// 音を鳴らす
procedure TForm1.Button1Click(Sender: TObject);
begin
PlaySound('MYSOUND',HInstance, SND_RESOURCE or SND_ASYNC);
end;
// 停止させる
procedure TForm1.Button2Click(Sender: TObject);
begin
PlaySound(nil, 0, SND_RESOURCE);
end;
上記の場合、SND_RESOURCE は必ず必要です。
また、SND_ASYNCをSND_SYNCにすると終わるまで制御が戻りません。
参照: [Delphi-ML:8787] [Delphi-ML:12338] <アプリケーション> <その他Windows関連> <開発環境> <Windows>
0201 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/13 西坂良幸 rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/27 K.Takaoka 編集
入力された漢字のひらがなを取り出す
WM_IME_COMPOSITIONメッセージをとらえて、ImmGetCompositionString関数を処理するのですが、受け取るのはコントロールの場合が多いので、少し工夫が必要です。
この例は、フォームに2つのTEditがあり、
TEdit1で漢字入力すると、TEdit2で入力に利用した平仮名がとれます。
ふりがなを取得するには別の方法を利用します.
// interface部
type
TForm1 = class(TForm)
Edit1: TEdit; // 漢字変換を行う
Edit2: TEdit; // フリガナを受け取る
procedure FormDestroy(Sender: TObject);
procedure FormShow(Sender: TObject);
private
FDefEditProc: TWndMethod;
procedure EditWndProc(var Message: TMessage);
public
end;
// implementation部
uses imm;
procedure TForm1.FormCreate(Sender: TObject);
begin
// 本来のWndProcを待避する
FDefEditProc := Edit1.WindowProc;
// 新しいWndProcを設定
Edit1.WindowProc := EditWndProc;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
// 恐いので元に戻す
Edit1.WindowProc := FDefEditProc;
end;
// 新しいEdit1のWndProc--ここでメッセーージを捕らえる
procedure TForm1.EditWndProc(var Message: TMessage);
var
IMC: HIMC;
Len: integer;
Str: string;
begin
with Message do
begin
if (Msg = WM_IME_COMPOSITION)
and ((LParam and GCS_RESULTREADSTR) <> 0) then
begin
IMC := ImmGetContext(Edit1.Handle);
Len := ImmGetCompositionString(IMC, GCS_RESULTREADSTR, nil, 0);
SetLength(Str, Len + 1);
ImmGetCompositionString(IMC, GCS_RESULTREADSTR, PChar(Str), Len + 1);
ImmReleaseContext(Edit1.Handle, IMC);
SetLength(Str, Len);
Edit2.Text := Str;
// Edit2.Text := Edit2.Text + Str; // でもよい
// イベントを作成してもよい
end;
FDefEditProc(Message);
end;
end;
実際は、メッセージを捕まえるコンポーネントのイベントなどで、必要なコントロールに渡すのがいいでしょうか。
参照: [Delphi-ML:41353] [Delphi-ML:41355] <System> <その他コンポーネント関連> <コンポーネント >
0218 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/26 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/27 西坂良幸 編集
エクスプローラからファイルをドラッグ&ドロップする
一度はやってみたいんですよね。MLでも多いスレッドです。
「Delphi2.0Q&A120選」(大野元久著)にもあります。
WM_DropFilesメッセージを捕まえて、DragQueryFile関数で処理するわけですが、受け取る側のスタイルを準備しなければなりません。
これは、一般的なフォームのOnCreateイベントで
DragAcceptFiles(Handle,True);
を使うよりも、CreateParamsをオーバーライドするのがベターだそうです。
// 定義部
type
TForm1 = class(TForm)
・・省略・・
private
procedure CreateParams(var Params: TCreateParams);override;
procedure WMDropFiles(Var Msg: TWMDropFiles); Message WM_DropFiles;
public
end;
// 実装部
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
// 受け取れる準備をする
Params.ExStyle := Params.EXStyle or WS_EX_ACCEPTFILES;
end;
// ファイル1個の場合−−メモに読み込ませる。
procedure TForm1.WMDropFiles(Var Msg: TWMDropFiles);
Var
FileName: Array[0..MAX_PATH] of Char;
Begin
Count := DragQueryFile(Msg.Drop, 0, FileName, SizeOf(FileName));
Memo1.Lines.LoadFromFile(String(FileName));
DragFinish(Msg.Drop);
End;
//今度は複数の場合−−リストボックスにファイル名
procedure TForm1.WMDropFiles(Var Msg: TWMDropFiles);
Var
FileName: Array[0..MAX_PATH] of Char;
i, Count: integer;
Begin
ListBox1.Items.clear;
// 第二パラメータを-1にするとドロップ数が取得できる
Count := DragQueryFile(Msg.Drop, DWord(-1), FileName, SizeOf(FileName));
for i := 0 to count - 1 do
begin
DragQueryFile(Msg.Drop, i, FileName, SizeOf(FileName));
ListBox1.items.add(String(FileName));
end;
DragFinish(Msg.Drop);
End;
最後のDragFinishを忘れないように。
参照: [Delphi-ML:3137] [Delphi-ML:8536] [Delphi-ML:31274] <ShellApi> <Windows> <ファイル>
0207 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/19 西坂良幸 rev 1.10 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/27 西坂良幸 編集
アプリケーションを常駐させてタスクトレイに登録したい
コンポーネントをつかう人が多いんでしょうね。MLでは少ない話題です。このようなAPLを何十本も作るんでなければ、直接コーディングしてみませんか。
要領は、APIのShell_NotifyIcon関数を使うことです。TaskTrayへの登録と削除は簡単にできます。
頭を悩ますのは、どんなアイコンを使うのかと、TaskTrayに送られてくるメッセージを自分で決めなければならないことです。
const
WM_MY_TRAYICON = WM_APP + $300; // 適当です
// ProcessMesageに配慮−−無くても良い
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
// WM_QUIT メッセージを受け取る時
if Application.Terminated then
begin
ShowWindow(Application.Handle, SW_HIDE);
Visible := False;
Action := caNone;
end;
end;
// アイコンをトレイに登録−−ここではメインアイコンを使用
procedure TForm1.CreateTaskBarIcon;
var
NotifyData: TNotifyIconData;
begin
with NotifyData do
begin
cbSize := SizeOf(TNotifyIconData);
Wnd := hWndTrayIcon;
uID := 0;
uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
szTip := 'Traybar Tip';
hIcon := Application.Icon.Handle;
uCallbackMessage := WM_MY_TRAYICON;// 独自に定義
end;
Shell_NotifyIcon( NIM_ADD, @NotifyData );
end;
// アイコンを削除
procedure TForm1.DeleteTaskBarIcon;
var
NotifyData: TNotifyIconData;
begin
with NotifyData do
begin
cbSize := SizeOf(TNotifyIconData);
Wnd := hWndTrayIcon;
uID := 0;
end;
Shell_NotifyIcon( NIM_DELETE, @NotifyData );
end;
// 起動直後はトレイにアイコン表示のみ
procedure TForm1.FormCreate(Sender: TObject);
begin
// uTaskBarRecrate は TForm1 の private に UINT 型で宣言
// 新しい UtilWindow を作成し、ブロードキャストメッセージのみを受け取らせる
uTaskBarRecreate := RegisterWindowMessage('TaskbarCreated');
// hWndTrayIcon は TForm1 の private に HWND 型で宣言
// メッセージ送信先を指定
hWndTrayIcon := AllocateHWnd(TaskTrayWndProc);
CreateTaskBarIcon;
ShowWindow(Application.Handle,SW_HIDE);
Application.ShowMainForm := False;
end;
// 終了した時にトレイのアイコンを削除
procedure TForm1.FormDestroy(Sender: TObject);
begin
DeleteTaskBarIcon;
DeallocateHWnd(hWndTrayIcon);
end;
// タスクトレイに来るWM_MY_TRAYICONメッセージを受信
procedure TForm1.TaskTrayWndProc(var Msg: TMessage);
var
ps: TPoint;
begin
Case Msg.LParam of
WM_LBUTTONDBLCLK:
begin
Visible := true;
ShowWindow(Application.Handle,SW_SHOW);
//ShowWindow(Application.Handle,SW_RESTORE);
end;
WM_RBUTTONUP:
begin
GetCursorPos(ps);
SetForegroundWindow(Handle);
// フォームにポップアップメニューがあるとする
PopupMenu1.Popup(ps.x,ps.y);
PostMessage(Handle, WM_NULL, 0,0);
end;
else
// タスクバーが移動・再構築された場合に消えたアイコンを再生成
if (Msg.LParam = LongInt(uTaskBarRecreate)) then
CreateTaskBarIcon;
end;
end;
AllocateHWnd 関数の戻り値のウィンドウハンドルを利用すれば他のメッセージ受信先/送信先として利用できるので、TrayIcon のメッセージ送信先に利用しました。
参照: [Delphi-ML:2536] [Delphi-ML:11821] [Delphi-ML:35250] <System> <Windows> <フォーム> <コンポーネント >
0275 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/26 西坂良幸 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/26 西坂良幸 編集
ファイルを削除してゴミ箱に移動させたい。
SHFileOperation関数を使えば、ファイル単位でもフォルダーごとでもゴミ箱に移動させることができます。
注意事項としては、必ずフルパスでなければならないということです。
ちょっと変な関数名ですが・・・
Uses Shellapi;
procedure SendToRecycleBin(FileList: TStrings; Dlg: boolean);
var
LpFileOp:TSHFILEOPSTRUCT;
FileNames: string;
i: integer;
begin
if FileList.Count = 0 then exit;
// #0をデリミタとした文字列を作る
for i := 0 to FileList.Count - 1 do
FileNames := FileNames + FileList[i] + #0;
// ターミネイタにもう一度#0をつける
FileNames := FileNames + #0;
with LpFileOp do
begin
Wnd := Application.Handle;
wFunc := FO_DELETE;
pFrom := PChar(FileNames);
pTo:= nil;
fFlags := FOF_ALLOWUNDO;
if not Dlg then fFlags := fFlags or FOF_SILENT;
hNameMappings := nil;
lpszProgressTitle := nil;
end;
SHFileOperation(LpFileOp);
end;
// テスト
procedure TForm1.Button1Click(Sender: TObject);
var
Flist :TStrings;
begin
Flist := TStringlist.Create;
try
Flist.Add('C:\Program Files\Borland\Delphi 4\Projects\*.~df');
Flist.Add('C:\Program Files\Borland\Delphi 4\Projects\*.~pa');
SendToRecycleBin(FList, true);
finally
Flist.free;
end;
end;
参照: [Delphi-ML:3443] [Delphi-ML:5005] <ShellApi> <Windows> <ファイル>
0122 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/25 西坂良幸 編集
TSaveDialogでファイルタイプが変更されたらファイル拡張子を変更したい
Excelなどの「名前を付けて保存」でしているように 選択されたファイルタイプに応じて拡張子の部分を変更したいのですが。
TSaveDialogをはりつけて、Filterプロパティを以下のようにしたとします.
Filter -> 'テキストファイル(*.txt)|*.txt|アイコンファイル(*.ico)|*.ico|カンマテキスト(*.csv)|*.csv|'
uses Dlgs,Commdlg;
procedure TForm1.SaveDialog1TypeChange(Sender: TObject);
var
DlgParent: HWND;
StrFileName, StrExt: string;
begin
DlgParent := GetParent(TSaveDialog(Sender).Handle);
Case SaveDialog1.FilterIndex of
2: StrExt := '*.ico';
3: StrExt := '*.csv';
else StrExt := '*.txt';
end;
StrFileName := ChangeFileExt(TSaveDialog(Sender).FileName, StrExt);
SendMessage(DlgParent, CDM_SETCONTROLTEXT, Edt1, Longint(PChar(StrFileName)));
end;
// テスト
procedure TForm1.Button1Click(Sender: TObject);
begin
SaveDialog1.Execute;
Label1.Caption := SaveDialog1.FileName;
end;
Filterを切り分けて、Filterindexに拡張子をセットする処理ルーチンは省略していますが、やり方はわかると思います。
参照: [Delphi-ML:38586] [builder:7802] <ダイアログ>
0196 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/07/07 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/24 K.Takaoka 編集
Windows 特殊フォルダの Class ID List
SHGetFolderPath, SHGetFolderLocation, SHGetSpecialFolderPath, SHGetSpecialFolderPath といった API で Windows が特別に扱うフォルダのパスを得ることができます.
これらのフォルダはユーザ環境によって異なるため, 常に API を利用して取得するようにすると問題のないアプリケーションが開発できます.
フォルダ名:CSIDL名(プログラムで利用できる定数名)
{CLSIDL}
ディレクトリ名
マイコンピュータ: CSIDL_DRIVES
{20D04FE0-3AEA-1069-A2D8-08002B30309D}
マイコンピュータはディレクトリにリンクしていません
ネットワークコンピュータ: CLSIDL_NETWORK, CSIDL_NETHOOD
{208D2C60-3AEA-1069-A2D7-08002B30309D}
ネットワークコンピュータはディレクトリにリンクしていません
マイドキュメント: CSIDL_PERSONAL
{450D8FBA-AD25-11D0-98A8-0800361B1103}
C:\My Document
C:\WinNT\Users\UserName\Personal
ごみ箱: CSIDL_BITBUCKET
{645FF040-5081-101B-9F08-00AA002F954E}
C:\Recycled.Bin
コントロールパネル: CSIDL_CONTROLS
{21EC2020-3AEA-1069-A2DD-08002B30309D}
コントロールパネルはディレクトリにリンクしていません
プリンタ: CSIDL_PRINTERS, CSIDL_PRINTHOOD
{2227A280-3AEA-1069-A2DE-08002B30309D}
プリンタフォルダはディレクトリにリンクしていません
他にも
CSIDL_COOKIES, CSIDL_DESKTOP, CSIDL_DESKTOPDIRECTORY, CSIDL_FAVORITES, CSIDL_FONTS, CSIDL_HISTORY, CSIDL_INTERNET, CSIDL_INTERNET_CACHE, CSIDL_PROGRAMS, CSIDL_RECENT, CSIDL_SENDTO, CSIDL_STARTMENU, CSIDL_STARTUP, CSIDL_TEMPLATES
などといったものが Win95/98/NT4/2K で共通に利用できます.
WinNT/2K においては CSIDL_COMMON_*** という形式で全ユーザ共通のフォルダを指定できるものが多くあり, Win2K にはさらに多くの定数が新設されています.
基本的には MSDN などの Microsoft の公式な文書を読むことになりますが, HKEY_CLASSES_ROOT\CLSID\ 以下を探すことで, アプリケーション固有のものを発見することができます.
たとえば、
「受信トレイ」
{00020D75-0000-0000-C000-000000000046}
といったものです.
これらを利用して 'プリンタ.{2227A280-3AEA-1069-A2DE-08002B30309D}' という名前のフォルダを作ることで、任意の場所にプリンタフォルダを追加できます。
参考: http://msdn.microsoft.com/library/sdkdoc/Shell/Functions/CSIDL.htm
参照: [Delphi-ML:10601] <Windows> <ファイル>
0271 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/23 西坂良幸 rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/24 K.Takaoka 編集
TOpenDialog(TSaveDialog) の初期フォルダをシステムフォルダ(デスクトップなど)に設計する
TOpenDialog はデフォルトで Windows95 であればカレントディレクトリ, WindowsNT であればホームディレクトリ, Windows98 であればパーソナルディレクトリを表示します.
InitialDir プロパティにディレクトリ名を設定すれば変更できますが, システムフォルダの場所は一定ではないので実行時に指定する必要が出てしまいます.
# システムフォルダの位置を知るには API と CLSID 定数を利用します.
# その方法については [Tips:196] を参照してください.
これらのフォルダを設計時に設定することができます.
CLSID の値に :: をつけたもの, たとえば
::{20D04FE0-3AEA-1069-A2D8-08002B30309D}
などを InitialDir プロパティに設定します.
この値の一覧は Delphi の ShellAPI Unit や [Tips:196] にあります.
参照: [Delphi-ML:9689] [Delphi-ML:39628] [Tips:196] <ダイアログ>
0141 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/24 西坂良幸 編集
TOpenDialogで、Executeメソッド実行時に表示位置を変えたい。
TOpenDialogは、OnShowイベントの段階では、位置がすでに決まっているようです。調べてみると、何回かWM_NOTIFYが送られますが、NMHDR構造体のCodeメンバーがCDN_INITDONEになるダイアログの初期化時を捕まえるとうまくいくようです。(ただし、これでも最初のExecute時しか効果ありません。D4のみ?)
以下のような拡張ダイアログコンポが紹介されています。
// 定義部
type
TQueryDlgPosEvent = procedure(Sender: TObject; var pt: TPoint) of object;
ThtOpenDialogEx = class(TOpenDialog)
private
FQueryDlgPos: TQueryDlgPosEvent;
protected
procedure DefaultHandler(var Message); override;
procedure DoQueryDlgPos; virtual;
published
property OnQueryDlgPos: TQueryDlgPosEvent read FQueryDlgPos write FQueryDlgPos;
end;
// 実装部n
procedure ThtOpenDialogEx.DefaultHandler(var Message);
begin
inherited;
with TWMNotify(Message) do
if (Msg = WM_NOTIFY) then
if (NMHdr^.Code = CDN_INITDONE) then
DoQueryDlgPos;
end;
procedure ThtOpenDialogEx.DoQueryDlgPos;
var
DlgHandle: THandle;
Rect: TRect;
pt: TPoint;
begin
if Assigned(FQueryDlgPos) then
begin
DlgHandle := GetParent(Handle);
GetWindowRect(DlgHandle, Rect);
pt.x := (GetSystemMetrics(SM_CXSCREEN) - Rect.Right + Rect.Left) div 2;
pt.y := (GetSystemMetrics(SM_CYSCREEN) - Rect.Bottom + Rect.Top) div 3;
FQueryDlgPos(Self, pt);
SetWindowPos(DlgHandle, HWND_TOP, pt.X, pt.Y, 0, 0,
SWP_NOSIZE or SWP_NOZORDER);
end;
end;
// テスト
procedure TForm1.Button1Click(Sender: TObject);
var
OpenDialogEx:ThtOpenDialogEx;
begin
OpenDialogEx:=ThtOpenDialogEx.Create(self);
try
with OpenDialogEx do
begin
OnQueryDlgPos := QueryDlgPos;
Execute;
end;
finally
OpenDialogEx.Free;
end;
end;
procedure TForm1.QueryDlgPos(Sender: TObject; var pt: TPoint);
begin
Pt.x := 800;
pt.y := 600;
end;
追記:
DefaultHandlerメソッドのかわりに、WndProcメソッドをオーバーライドしてもほぼ同じ結果が得られます。
以下は、イベントの代わりに、InitialLeft, InitialTop、というプロパティを設定しています。
procedure ThtOpenDialogEx.WndProc(var Message: TMessage);
var
DlgHandle: THandle;
begin
inherited WndProc(Message);
if (Message.Msg = WM_NOTIFY) then
if (TWMNOTIFY(Message).NMHdr^.Code = CDN_INITDONE)
and not ((FInitialLeft < 0) or (FInitialTop < 0)) then
begin // 負数のときは処理しない
DlgHandle := GetParent(Handle);
SetWindowPos(DlgHandle, 0, FInitialLeft, FInitialTop, 0, 0, SWP_NOSIZE or SWP_NOZORDER);
end;
end;
参照: [Delphi-ML:12600] [Delphi-ML:24640] <ダイアログ> <Dialogs> <コンポーネント >
0272 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/23 西坂良幸 rev 1.1.1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/23 西坂良幸 編集
シェルのデスクトップやマイコンピュータをプログラムから開きたい。
デスクトップは、
HKEY_CURRENT_USER\SoftwareMicrosoft\Windows\CurrentVersion\
Explorer\Shell Folders の Desktopを参照すればOKです。
たいていの場合 'C:\Windows\デスクトップ'(実際は半角です)
procedure TForm1.Button1Click(Sender: TObject);
begin
ShellExecute(Handle, 'Open', 'EXPLORER.EXE', 'C:\Windows\デスクトップ', '', SW_SHOW);
// 'デスクトップ'は半角です。
end;
また、主要なシステムフォルダーは
HKEY_CURRENT_USER\SoftWare\Classes\CLSIDの値を使えばOKです
たとえば、マイコンピュータは。
procedure TForm1.Button4Click(Sender: TObject);
begin
ShellExecute(Handle,'Open','EXPLORER.EXE','::{20D04FE0-3AEA-1069-A2D8-08002B30309D}','',SW_SHOW);
end;
という具合です。CLSIDについては、詳しくはCOMの知識が必要になります。
・ネットワークコンピュータ
・マイドキュメント
・ゴミ箱
などのCLSIDの値は、[Tips:196]にあります。
コントロールパネルは。アプリケーションですので。
procedure TForm1.Button2Click(Sender: TObject);
begin
ShellExecute(Handle,'Open','Control.exe','','',SW_SHOW);
end;
// たとえばコントロールパネルの中の'アプリケーションの追加と削除'なら
procedure TForm1.Button3Click(Sender: TObject);
begin
ShellExecute(Handle,'Open','Control.exe','Appwiz.cpl','',SW_SHOW);
end;
参照: [Delphi-ML:9689] [Delphi-ML:16991] [Delphi-ML:37909] [Delphi-ML:39661] <Windows>
0016 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/23 西坂良幸 編集
TOpenDialogが表示された時のフォーカスを変えたい。
TOpenDialogは、ダイアログを開いたとき最初のフォーカスは、ファイル名になっています。
TabOrderは、ファイル名(Edit)->ファイルの種類(ComboBox)->
>それを、上下キーで指定のフォルダから選択できるように、開く(Button)->キャンセル(Button)->ファイル場所(ComboBox)->選択リスト(ListView)の順です。
たとえば、選択リスト(ListView)にフォーカスをおく場合は、
OpenDialogのOnShowイベントで以下のようにすればどうでしょう。
procedure TForm1.OpenDialog1Show(Sender: TObject);
begin
// ひとつ前のコントロールにフォーカスを戻す
PostMessage(OpenDialog1.Handle, WM_NEXTDLGCTL, -1, 0);
// リバースしないので、何かキーを押してリバースさせる
keybd_event(VK_SPACE, 0, 0, 0);
keybd_event(VK_SPACE, 0, KEYEVENTF_KEYUP, 0);
end;
参照: [Delphi-ML:33851] <ダイアログ>
0068 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/22 osamu 編集
長いファイル名を短いファイル名に変換したい
GetShortPathName APIを使いましょう。
function LongToShortFileName(Const LongName: String):String;
var
Len: integer;
begin
Len := GetShortPathName(PChar(LongName), PChar(result), 0);
SetLength(result, len);
if GetShortPathName(PChar(LongName), PChar(result), Len) = 0 then
Raise EConvertError.Create('ファイルが見つかりません。');
end;
ファイルが実在しないと例外が発生します。
逆に、短いファイル名を長いファイル名にするには [Tips:233] を見てください。
参照: [builder:5092] [Tips:233] <Windows> <ファイル>
0270 D1D2 D3 D4 D5 D6 D7 3.195 98 作成: 1999/09/21 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/21 osamu 編集
Delphi4 で TBitmap を pf1bit で使うときの注意点(不具合回避)
TBitmap は pf1bit で Width/Height が共に 0 以上になると 1bpp DIB を作成しますが、2色のカラーテーブルを初期化していません。
さらに、Width/Height が 0 以上の TBitmap を PixelFormat で pf1bit へ形式変換する場合
DDB -> 1bpp DIB 変換なら 黒白2色のカラーテーブルが作られます。 -> OK
DIB -> 1bpp DIB 変換なら 古いカラーテーブルが引き継がれます。 -> バグ
となります。
結局、PixelFormat := pf1bit; がまともに動くのは、古い PixelFormat の値が pfDevice で Width > 0, Height > 0 の場合だけです。たとえば,以下のようなコードで問題が発生します。
Bitmap1 := TBitmap.Create; // スクリーン互換 DDB を作成
Bitmap1.PixelFormat := pf1bit; // Size がゼロのまま 1bit DIB に変換
Bitmap1.Width := 1000;
Bitmap1.Height := 1000;
これを、
Bitmap1 := TBitmap.Create; // スクリーン互換 DDB を作成
Bitmap1.Width := 1000;
Bitmap1.Height := 1000;
Bitmap1.PixelFormat := pf1bit; // Size を設定してから 1bit DIB に変換
とすれば、正常に動きます。ただし、一旦ばかでかい DDB ができてしまうので,リソースの消費が問題になります。この場合には、以下のようにするのがベストでしょう。
Bitmap1 := TBitmap.Create; // スクリーン互換 DDB を作成
Bitmap1.Width := 1;
Bitmap1.Height := 1;
Bitmap1.PixelFormat := pf1bit; // 小さな Size を設定してから 1bit DIB に変換
Bitmap1.Width := 1000;
Bitmap1.Height := 1000; // 大きくする
基礎知識:
TBitmap.Monochrome=True; は 1bit DDB
TBitmap.PixelFormat:=pf1bit; は 1bit DIB
を作成します。
この辺は、Delphi Magazine 3/4/5号で中村@NECさんが詳しく説明して下さっています。
参照: [Delphi-ML:42732] <画像> <バグ>
0254 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/15 おばQ rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/21 おばQ 編集
TListを特定のクラスのListにする方法
みなさん、TListって使ってます?
TListはItemsプロパティで、いろんなオブジェクトにアクセスできる
とても便利なコレクションクラス(リスト)です。
これを利用すればコンポーネント配列なんて特に必要なくなりますし、
自分で作ったクラスが保持できます。
しかし、こういうコーディングしていませんか?
TMyClass( TList.Items[i] ).MyClassProperty
実際には特定のクラスのみItemsに保持したい場合が多いのに
TListはPointerを保持する汎用的なListなので
キャストしないとプロパティやメソッドが使えません。
TListのItemにアクセスするたびに
毎回キャストしなくてはいけないのであれば、大変めんどうですし
コーディング量も増えて綺麗ではありません。
そういう場合はTListを継承してしまいましょう。
とてもあっさりと可能です。
type節に追加します
TMyClassList = class(TList)
private
function Get(Index: Integer): TMyClass;
procedure Put(Index: Integer; const Value: TMyClass);
public
property Items[Index: Integer]: TMyClass read Get write Put; default;
end;
実装部には
{ TMyClassList }
function TMyClassList.Get(Index: Integer): TMyClass;
begin
Result := TMyClass( inherited Get(Index) );
end;
procedure TMyClassList.Put(Index: Integer; const Value: TMyClass);
begin
inherited Put( Index, Value );
end;
TMyClassは適当に変えてください。
これだけで、TListを使うのとほとんど同様に
TMyClassList.Items[i].MyClassProperty
という風にスッキリと使えます。
また、TListを継承する時ついでに
TMyClassList = class(TList)
public
destructor Destroy; override;
…
destructor TMyClassList.Destroy;
var
i: Integer;
begin
for i := 0 to Self.Count - 1 do
Self.Items[i].Free;
inherited Destroy;
end;
と実装しておくとListが破棄される時に
所有するItemも一緒に破棄されるので
結構便利に使えるでしょう。
参照: <PASCAL>
0266 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/19 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/20 西坂良幸 編集
エクスプローラのように、アプリケーションにブラウザページをつくりたい。
本来なら、DelphiにあるHRMLコンポーネント等を使うんでしょうが、これも、D4まででは、結局AktiveXなので、オフィス97をお持ちの方は、Webブラウザコントロール(WebBrowser)を使うととても簡単です。
これは、オフィス97のCD-ROM
\VALUPACK\ACCESS\WEBHELP\Webrowse.hlpに
Web ブラウザ コントロールとして、詳しい解説があります。
この実体は、Shdocvw.dll というDLLファイルです。
メニューで[コンポーネント|ActiveXコントロールの取り込み]を開いて下さい。
ダイアログのリストに、Microsoft Internet Contorols(version 1.??) というのがあります。
無かったら、Windows\Systemディレクトリの、Shdocvw.dllを探して追加して下さい。
クラス名の欄に、TWebBrowser_V1、TWebBrowser と表示されたら、インストール実行で、パレットのActiveXページにコンポーネントがインストールされます。
ページ切り替えは省略しますが、
フォームにこのコンポーネントを貼り付け、Alignを決めます。
procedure TForm1.FormShow(Sender: TObject);
begin
WebBrowser1.GoHome;
end;
とすれば、Web表示が出来上がりです。
URLを指定するときは、Navigate、やNavigate2 メソッドを使います
たとえば、URLがファイル(*.htm)なら、
procedure TForm1.Button1Click(Sender: TObject);
var
url: WideString;
flg,Tmp: OleVariant;
begin
if OpenDialog1.Execute then
begin
url := OpenDialog1.FileName;
flg := 0;
WebBrowser1.Navigate(url, flg, Tmp, Tmp, Tmp);
end;
end;
メソッドや、プロパティの詳細は、上記のヘルプファイルを見て下さい。
264番のインターネットエクスプローラオブジェクトと似通っているようです。
参照: [Delphi-ML:25232] [Delphi-ML:37104] <その他Windows関連> <WWW> <ShellApi> <Windows> <通信>
0265 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/19 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/20 西坂良幸 編集
フォームのアイコンをアニメーションにしたい
タイマーイベントを使って、AppliCationのアイコンをきりかえてやればアニメーションGIFのような効果ができます。
プロジェクトの*.resファイルに、アイコン'ICON1'-'ICON5'をアニメ風に作成して下さい。
フォームにStartBtn、StopBtn の2個のボタンとTTimer(Intervalプロパティを100程度)を貼り付けます。
private 部に
IconStat: integer;
ICon1,ICon2,ICon3,ICon4,ICon5: TIcon;
と書き加えます。
// あらかじめリソースを参照するアイコンを作成
procedure TForm1.FormCreate(Sender: TObject);
begin
ICon1 := TIcon.create;
ICon1.Handle := LoadIcon(HInstance,'ICON1');
ICon2 := TIcon.create;
ICon2.Handle := LoadIcon(HInstance,'ICON2');
ICon3 := TIcon.create;
ICon3.Handle := LoadIcon(HInstance,'ICON3');
ICon4 := TIcon.create;
ICon4.Handle := LoadIcon(HInstance,'ICON4');
ICon5 := TIcon.create;
ICon5.Handle := LoadIcon(HInstance,'ICON5');
Application.Icon.Handle := ICon1.Handle;
IconStat := 1;
end;
// 破棄
procedure TForm1.FormDestroy(Sender: TObject);
begin
ICon1.Free;
ICon2.Free;
ICon3.Free;
ICon4.Free;
ICon5.Free;
end;
// スタート
procedure TForm1.StartBtnClick(Sender: TObject);
begin
Timer1.Enabled := true;
end;
// ストップ
procedure TForm1.StopBtnClick(Sender: TObject);
begin
Timer1.Enabled := false;
IconStat := 1;
Application.Icon.Handle := ICon1.Handle;
end;
// タイマーイベントでアイコンの切り替え
procedure TForm1.Timer1Timer(Sender: TObject);
begin
If IconStat = 5 then IconStat :=1
else IconStat := IconStat + 1;
Case IconStat of
1: Application.Icon.Handle := ICon1.Handle;
2: Application.Icon.Handle := ICon2.Handle;
3: Application.Icon.Handle := ICon3.Handle;
4: Application.Icon.Handle := ICon4.Handle;
else Application.Icon.Handle := ICon5.Handle;
end;
end;
4−5個アイコンを作るとスムースです。
注意事項として。resファイルを書き直した時は、[プロジェクトの再構築]を行ってから実行して下さい。resの変更が反映されません。また、アイコンの名称はイメージエディタでは、デフォルトでIcon?となりますので、オリジナルな名称に変えて下さい。
また、イメージリストを使えば、以下のようにすれば、ほぼ同じ結果が得られます。
var
IconCount : integer;
procedure TForm1.Button1Click(Sender: TObject); // start
begin
Timer1.Interval := 100;
Timer1.Enabled := True;
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
if IconCount >=ImageList1.Count then IconCount := 0;
ImageList1.GetIcon(IconCount, Application.Icon);
inc(IconCount);
end;
この場合は、使用するアイコンイメージは、リソースに入りませんので、プログラムアイコンとして使用出来ません。必要ならば、リソースからイメージリストへロードする処理を加えて下さい。
参照: [Delphi-ML:42725] [Delphi-ML:42748] <アイコン> <フォーム>
0267 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/19 おばQ rev 1.1.1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/20 K.Takaoka 編集
MDI等で二重起動を防止して新しいファイルを開く
なんか変なタイトルですいません。
Exe本体やExeのショートカットにファイルをドラッグ&ドロップ(以下D&D)した時にアプリケーションが起動してファイルが開く処理を実現したとします。
アプリケーションが起動している最中にもう一度、Exe本体やショートカットにファイルを D&D するともう一つアプリケーションが起動しませんか?あまり素敵じゃないですよね?
貴方がお望みの動作は、たぶんアプリケーションは一つだけ立ち上がっていて MDI 子ウィンドウで新しく D&D されたファイルを開きたいというものでしょう?そこで以下のTipsを利用します。
・二重起動の判定(t269)
・簡易アプリケーション間通信(t268)
t269 のままでは Halt 手続きによってアプリケーションが終了してしまうので、引数に渡されたファイル名を t268 を利用して送信します.
halt 手続きの変わりに呼び出される CopyDataToOld 手続きを作成します.
procedure CopyDataToOld;
var
wnd: HWND;
begin
{ 既に存在している TForm1 を探す }
Wnd := FindWindow('TForm1', nil);
if Wnd<>0 then
begin
// SIGNATURE_FILEOPEN 定数で ParamStr の内容を送信するとする
// SendMessage(wnd, WM_COPYDATA, ...); // t268 参照
end;
end;
そして、送信された WM_COPYDATA を受け取るメッセージハンドラを実装します。
インターフェス部
type
TForm1 = class(TForm)
// …省略…
private
procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;
public
end;
実装部
procedure TForm1.WMCopyData(var Msg: TWMCopyData);
var
ArriveStr: String;
begin
if Msg.CopyDataStruct.dwData=SIGNATURE_FILEOPEN then
begin
// 受信処理 : t268 参照
// ArriveStr に受け取った文字列が入るとする
// ※ 受け取ったファイル名で開く
FileOpen(String(pcData)); //例です
end;
end;
以上のソースでは※印部分では D&D されるファイルは一つだとしか考慮していません。
参照: [Delphi-ML:42587] [Tips:268] [Tips:269] <その他Windows関連> <アプリケーション> <Windows>
0269 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/19 K.Takaoka rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/19 K.Takaoka 編集
二重起動の判定
該当記事で以下のようなユニットが紹介されています
unit Unique;
interface
uses
Windows, Sysutils;
implementation
var
UniqueName : string;
hMutex: THandle;
initialization
UniqueName := ExtractFileName(ParamStr(0));
hMutex := OpenMutex(MUTEX_ALL_ACCESS, false, pchar(UniqueName));
if hMutex <> 0 then
begin
CloseHandle(hMutex);
Halt;
end;
hMutex := CreateMutex(nil, false, pchar(UniqueName));
finalization
ReleaseMutex(hMutex);
end.
Mutex は同じ名前のものは 2 つ存在することはできません.
ここでは ExtractFileName(ParamStr(0)) を mutex の名前にしています.
複数のアプリケーションで(意図的な場合は除いて)同じ名前の mutex を作成しようとすると問題になる可能性が高いため、できるだけユニークな名前を割り当てるようにしなければなりません.
また、このユニットではすでに mutex が存在していた場合に Halt 手続きを実行しています.
該当部分を書きかえることによって二重起動時に任意のアクションを起こすことができるでしょう.
参照: [Delphi-ML:6240] <その他Windows関連> <Windows>
0268 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/19 K.Takaoka rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/19 K.Takaoka 編集
簡易アプリケーション間通信
Win32 環境で 2 つのアプリケーションがお互いに通信する方法は非常に多くあります.
同じコンピュータ上の 2 つのフォーム(ウィンドウ)の間で簡単に通信を行うには WM_COPYDATA というメッセージを使うという方法があります.
これは、2 つのフォームが同じアプリケーションであっても違うアプリケーションであっても問題ありません.
WM_COPYDATA を利用するには, 相手の WindowHandle が分かっている必要があります. Form1 の WindowHandle は Form1.Handle で取得できます.
画面内にあるフォーム(ウィンドウ)の検索は FindWindow API を用いて行うことができます.
ここでは相手の WindowHandle の得方については言及しません.
受信側では TForm1 のインターフェス部にメッセージハンドラを追加します. private でかまわないでしょう.
private
procedure WMCopyData(var msg: TWMCopyData);
message WM_COPYDATA;
送信側では、CopyDataStruct 構造体に送信するデータを格納してから、WM_COPYDATA を送信します.
procedure TForm1.Button1Click(Sender: TObject);
var
buf: PChar;
i: Integer;
cd: TCopyDataStruct;
begin
cd.dwData := SIGNATURE_STRING;
cd.cbData := Length(Memo1.Lines.Text)+1;
cd.lpData := StrAlloc(cd.cbData);
try
StrCopy(cd.lpData, PChar(Memo1.Lines.Text));
SendMessage(Handle, WM_COPYDATA, WPARAM(Handle), LPARAM(@cd));
finally
StrDispose(cd.lpData);
end;
end;
この例では, Memo1 の内容を自分自身に送信しています.
このメッセージの受信側実装は
procedure TForm1.WMCopyData(var msg: TWMCopyData);
var
i: Integer;
buf: PChar;
begin
if msg.CopyDataStruct.dwData=SIGNATURE_STRING then
begin
buf := StrALloc(msg.CopyDataStruct.cbData);
try
StrCopy(buf, msg.CopyDataStruct.lpData);
Memo2.Lines.Text := buf;
finally
StrDispose(buf);
end;
end
else
inherited;
end;
このような感じになります. この例では受信した文字列を Memo2 に設定しています.
受信した CopyDataStruct の内容は読み出し専用になっていますので注意してください.
双方で使われている SIGNATURE_STRING は適当な DWORD 値を指定します.たとえば
cosnt SIGNATURE_STRING: DWORD = $00000001;
のような値でも良いでしょう, 重要なのは送信側と受信側で同じ値を使うことです.
CopyDataStruct には 3 つの要素 dwData と cbData と lpData があります.
lpData に目的のデータを格納し cbData には lpData に保持されたデータのバイト数を格納します.
dwData は独立して利用できますが, 一般的には lpData の内容を示すユニークな値を格納しておきます.
参照: [Delphi-ML:4527] [Tips:90] <通信>
0264 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/19 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/19 西坂良幸 編集
インターネット エクスプローラを起動したい/その情報を取得したい
オフィス97のCD-ROM
\VALUPACK\ACCESS\WEBHELP\Webrowse.hlpに
InternetExPlorerオブジェクトの詳しい解説があります。
これを使うと、OLE オートメーションを利用してインターネット エクスプローラのインスタンスを作成し、操作することができます。
このオブジェクトはComObj.pas で定義されているCreateOleObject関数で使えます。
変数とメソッドを定義し、
private
IExp:Olevariant;
public
procedure ExecuteIE(Url: String = '');
uses ActiveX,ComObj;
procedure TForm1.ExecuteIE(Url: String = '');
begin
if IUnKnown(IExp) = nil then
begin
IExp := CreateOleObject('InternetExplorer.Application');
IExp.Height:=400;
IExp.Width:=600;
IExp.MenuBar:=1;
IExp.StatusBar:=1;
IExp.ToolBar:=1;
IExp.Visible:=true;
end;
if Url = '' then
IExp.Gohome
else
IExp.Navigate(Url);
end;
// ホームで起動
procedure TForm1.Button1Click(Sender: TObject);
begin
ExecuteIE;
end;
// URLを指定して起動
procedure TForm1.Button2Click(Sender: TObject);
begin
ExecuteIE('http://www2.big.or.jp/~osamu/Delphi/Tips');
end;
// インターネット エクスプローラを終了するには、Quit メソッドを使用します。
procedure TForm1.Button3Click(Sender: TObject);
begin
if IUnKnown(IExp) <> nil then
begin
IExp.Quit;
IExp:= Unassigned;
end;
end;
便利なプロパティやメソッドがたくさんあります。
参照: <その他Windows関連> <ShellApi> <Windows>
0024 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.5 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/19 西坂良幸 編集
Sqr()の結果が負になる!?
Sqr()関数は引数にIntegerを渡すと結果にIntegerが返ってきます。
ヘルプで
function Sqr(X: Real): Real;
と書いてあるにもかかわらずです。
Sqr()関数は引数が整数型だと結果は整数型で返り、
引数が実数型だと結果は実数型で返ります。
これが仕様です。
なお、整数型を渡した場合、オーバーフローのチェックは行われません。そのため渡す値によっては
sqr(x)+sqr(y)
の結果が負になる場合があり、これを実行時に検出することはできません。自乗した場合の結果が Integer の制限を越えることが予想できる場合には、引数を実数型にキャストして渡す必要があります。
参照: [Delphi-ML:7250] <バグ> <計算> <PASCAL>
0044 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.5 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/17 おばQ 編集
起動中のブラウザからURLを取得する/ブラウザにURLをセットする方法
DDE を用いれば、ブラウザが現在表示している URL を取得したり、逆に URL をブラウザにセットすることができます。
Form1 に DdeClientConv1 を配置し、ConnectMode プロパティを ddeManual にします。
type TBrowserType = (btIE, btNN);
const BrowserServices : array [TBrowserType] of string =
('Iexplore', 'netscape');
を用意して、
function TForm1.GetBrowserURL(BrowserType: TBrowserType): string;
var
ServiceStr, TopicStr, ItemStr, UrlStr: String;
UrlPch: PChar;
begin
ServiceStr := BrowserServices[BrowserType];
TopicStr := 'WWW_GetWindowInfo';
UrlStr := '';
with DdeClientConv1 do
begin
if SetLink(ServiceStr, TopicStr) then
if OpenLink then
begin
ItemStr:= '0xFFFFFFFF';
UrlPch := RequestData(ItemStr);
UrlStr := UrlPch;
StrDispose(UrlPch)
CloseLink;
end;
end; {with}
Result := copy( UrlStr, 2, Pos('",',UrlStr) - 2);
end;
GetBrowserURL(btIE) とすると起動中のインターネットエクスプローラの URL を文字列として取得出来ます。btNN とするとネットスケープから URL を取得します。
ブラウザは起動しておいてください。
URL をセットするには以下の関数を使います。
function TForm1.SetBrowserURL(BrowserType: TBrowserType; UrlStr: String): Boolean;
var
ServiceStr, TopicStr: String;
Pch: PChar;
begin
Result := false;
ServiceStr := BrowserServices[BrowserType];
TopicStr := 'WWW_OpenURL';
with DdeClientConv1 do
begin
if SetLink(ServiceStr, TopicStr) then
if OpenLink then
begin
Pch := RequestData( PChar(UrlStr) );
CloseLink;
StrDispose(Pch);
Result := true; {成功すれば戻り値がTrue}
end;
end; {with}
end;
IE の場合は起動中のブラウザ画面にセットされた URL のページが表示されます。NN の場合は新しく Window を開いてページを表示するようです。
参照: [Delphi-ML:42589] [Delphi-ML:42621] <その他Windows関連> <WWW> <Windows> <通信>
0263 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/16 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/16 osamu 編集
StringGrid でセル編集終了のイベントを得たい
StringGrid で表計算のような動作をさせようとすると、セル編集終了のイベントで、関係し合うセルの再計算を行いたくなります。OnSetEditText というイベントが利用できそうなのですが、このイベントはユーザのキー入力の一文字ごとに発生するため使えません。
で、少なくとも Delphi3 では、以下のようにすると、セル編集終了時を検出することができます。
これは、セル編集終了時に、OnSetEditText が2度続けて同じ Value 値で呼び出されるという現象を利用しています。Delphi の今後のバージョンで動作が保証されるわけではありませんので、注意が必要です。また、Options に goAlwaysShowEditor が含まれている場合には、Enter キーで値を確定することができないという不具合(?)が生じます。
procedure TForm1.StringGrid1SetEditText(Sender: TObject; ACol, ARow: Integer;
const Value: String);
const
MagicValue = 'd0308|ybh<_lfds$t083q()'#1#5; // 入力値としてありえない値
PreviousEditorValue: string = MagicValue; // C でいう static 変数の代り
begin
if Value<>PreviousEditorValue then begin // まだ編集中
PreviousEditorValue:= Value;
Exit;
end;
// 編集終了時には、同じ値が二度続けて送られてくる
case ACol of
0: ;
1: ; // ここで入力後の処理
2: ;
end;
// 次回のために絶対にありえない文字列を代入
PreviousEditorValue:= MagicValue;
end;
参照: [Delphi-ML:42663] <Additional> <コンポーネント >
0262 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/16 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/16 osamu 編集
TStringGrid の列の ReSize イベントの取得
ColWidthsChanged を overrideすることで、実現できます。
■interface
TStringGrid_Ex = class(TStringGrid)
private
FOnColWidthsChange:TNotifyEvent;
ptotected
procedure ColWidthsChanged; override;
published
property OnColWidthsChange:TNotifyEvent
read FOnColWidthsChange
write FOnColWidthsChange;
end;
■implementation
procedure TStringGrid_Ex.ColWidthsChange;
begin
inherited;
if Assigned(FOnColWIdthsChange) then
FOnColWidthsChange(Self);
end;
■利用方法
procedure TForm1.Grid1OnColWidthsChange(Sender:TObject);
var
idx :Longint;
begin
//条件
// Grid1.ColCount = Grid2.ColCountであること
if Sender = Grid1 then begin
for idx:=0 to Grid1.ColCount-1 do begin
Grid2.ColWidths[idx] := Grid1.ColWidths[idx];
end;
end else begin
for idx:=0 to Grid2.ColCount-1 do begin
Grid1.ColWidths[idx] := Grid2.ColWidths[idx];
end;
end;
end;
end;
同様に、RowHeighsChangedもoverrideできます。
参照: [Delphi-ML:31921] <Additional> <コンポーネント >
0234 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/04 西坂良幸 rev 1.8 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/16 osamu 編集
Pascal で文字列を効率良く扱う(例:文字列を逆順にする)。
MLの全体がコーディングの勉強ですが、[Delphi-ML:42129] から始まる一連のスレッド「文の逆」は、Pascal で文字列(文)を効率良く扱う様々なコーディングの例が示されています。いろいろな方法があることがわかりますね。勉強になります。
実装で気をつけるのは、2バイト文字と、CR/LF の順を入れ替えないようにしなければならないというところです。
(1) 何も考えないで作る
function StrReverse(s: string): string;
var i: Integer;
LeadBytes: set of Char;
begin
i:= 1;
Result:= '';
LeadBytes:= SysUtils.LeadBytes + [#13];
while i<=Length(s) do begin
if s[i] in LeadBytes then begin
Result:= Copy(s,i,2) + Result;
i:= i+2;
end else begin
Result:= Copy(s,i,1) + Result;
i:= i+1;
end;
end;
end;
この関数が、ループ中で何百回も一度に呼ばれるとは考えにくいので、実はこれで十分かもしれませんが、後学のために工夫の余地を考えてみることにしましょう。
(2) [Delphi-ML:42142] 基本的な形です。(ただし、処理速度を考慮しています。)
function StrReverse(const s: string): string;
var
i, L: integer;
begin
L := Length(s);
SetLength(result, L);
i := 1;
while i <= L do
begin
if (s[i] in SysUtils.LeadBytes) or
((s[i] = #13) and (S[i+1] = #10))
then
begin
result[L-i] := s[i];
result[L-i+1] := s[i+1];
Inc(i, 2);
end
else
begin
result[L-i+1] := s[i];
Inc(i);
end;
end;
end;
コード(1)も同じなんですが、実はこれデータによっては s[Length(s)+1] を参照してしまいます。不正な文字列の場合は無視するにしても、改行を常に #13#10 と仮定して(1)のように比較部分を単純化するか、i=Length(s) のときには s[i+1] を参照しないか、どちらかにしないと意味が無いです。
ま、それは置いといて、、、
文字列の生成回数を減らす(Copy 関数は値を返すのに string を生成する)
文字列の足し算の回数を減らす(足し算も内部で新しい文字列を作る)
関数の呼び出しを最小限にする(Length の値を L にキャッシュ)
i:=i+1 の代わりに Inc 関数を使う(C での ++ 演算子と同等)
引数に const をつける(これも文字列の生成を押さえられる)
などが高速化部分。効果が大きい順に並べてみました。今回の目的には後ろの3つはたいした違いは生みません。特に、Length 関数のコストはそれほど大きくないことは覚えておいても良いと思います。逆に文字列の連結は時間がかかるので注意。
(3) [Delphi-ML:42157] ポインタの使い方の見本のような例ですね。
function StrReverse(const s: string): string;
var
p, pn, pr: PChar;
begin
SetLength(Result, Length(s));
p := PChar(s);
pr := PChar(Result) + Length(s);
while p^ <> #0 do begin
if (p^ = #13) and ((p+1)^ = #10) then
pn := p + 2
else
pn := CharNext(p);
System.Move(p^, (pr-(pn-p))^, pn-p); Dec(pr, pn-p);
p := pn;
end;
end;
コード(2)から、さらに [ ] の使用を無くして高速化を図ったものです。Pascal の [ ] は C の [ ] に比べてかなり時間がかかります。CharNext という関数も覚えていてよさそうですね。でも高々2バイトの転送に System.Move を使うよりは、(2) のように SysUtils.LeadBytes を使って判定して1バイトずつ ^ で転送した方がずっと速いでしょう。
2バイト文字の判断で2バイト目が #0 でないことを確認しないと、不正なデータが与えられた時に、メモリを破壊してシステムをハングアップさせる危険性があります。p_end:=PStr(s)+Length(s); としておいて、while の比較を、p<=p_end とすればハングアップを防ぎつつさらに高速化にもなりますね。
(4) [Delphi-ML:42148] ポイントは再帰ですね。
function StrReverse(const s: string): string;
var
L: integer;
begin
result := '';
L := Length(s);
if L = 0 then
exit;
if IsDBCSLeadByte(Byte(s[1])) then
result := StrReverse(copy(s, 3, L-2)) + s[1] + s[2]
else
result := StrReverse(copy(s, 2, L-1)) + s[1];
end;
コードはすっきりしますが、速度的には(1)よりずっと悪いです。なにより、長〜い文字列を渡すと再帰が深くなりすぎて、スタックオーバーフローを起こしかねないので、この目的には使えません。えっと、この例だけ改行文字を考慮していないですね。
(5) [Delphi-ML:42155] WideStringを使う発想です。
function StrReverse(s: WideString): WideString;
const
WCharCR : WideChar = #$0d;
WCharLF : WideChar = #$0a;
var
i : Integer;
begin
SetLength(Result, Length(s));
for i:=1 to Length(s) do begin
if (s[i] = WCharCR) and (s[i+1] = WCharLF) then begin
s[i] := WCharLF; s[i+1] := WCharCR;
end;
Result[Length(s)-i+1] := s[i];
end;
end;
WideString を使うことで、2バイト文字を考えなくても良くなります。もし、改行文字が含まれないことが分かっていれば、if 文は必要無くなりますから、非常にすっきりしたコードになります。WideString <-> string の変換はキャストの必要無く自動で行われます。変換は時間がかかりますが、初めと終わりの2回だけなので、この場合には気にする必要は無いでしょう。美しいコードです。
(6) [Delphi-ML:42173] 文字列の両端から交換していこうというアイディア
function StrReverse(const s: string): string;
var
len, i : integer;
L, R : pchar;
tmp : char;
p : pchar;
begin
Result := s;
len := length(s); if len = 0 then exit ;
p := PChar(Result);
L := p; R := L + len - 1;
while L < R do
begin
if (L^ in Sysutils.LeadBytes) or
((L^ = #13) and ((L+1)^ = #10)) then
begin
tmp := L^; L^ := (L+1)^; (L+1)^ := tmp;
inc(L, 2);
end
else
inc(L);
end;
L := p; R := L + len - 1;
for i := 1 to (len div 2) do
begin
tmp := L^; L^ := R^; R^ := tmp;
inc(L); Dec(R);
end;
end;
うーん何やら複雑。改行文字や2バイト文字が無くて、なおかつ関数が
procedure StrReverse(var s: string): string;
のように、直接引数を書きかえるものなら、メリットはあるかもしれません。。。この場合には、ほぼ最後の for 文だけになってしまいます。
基本的に、ループの中で何度も繰り返し呼ばれるので無ければ、実行速度を求めて読みにくいコードを書くのは愚とされます。したがって、この問題に限って言えば、(1) や (5) のように分かりやすく書けるものがお勧めです。
ただし、実行効率を考慮しないといっても限度があり、(4) のように、長い文字列を渡すとスタックオーバーフローを起こすようなコードは、いかに見やすくても不適当でしょう。
あなたならどう書きますか? アイデアある方はどんどん加筆して下さい。
参照: [Delphi-ML:42134] <文字列> <PASCAL>
0261 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/15 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/15 osamu 編集
漢数字で位取り表示
数字を漢数字で位取り表示(4桁単位)する関数を作ってみました。
function RhKnjNumber(const Num: Currency): string;
begin
Result := FormatFloat('####"兆"####"億"####"万"####円', Num);
while True do {余計な文字を取り去る}
if ByteType(Result, 1) = mbLeadByte then
Result := Copy(Result, 3, Length(Result) - 2)
else
Break;
end;
参照: [Delphi-ML:34500] <文字列> <PASCAL>
0260 D1D2 D3 D4 D5 D6 D7 3.195 98 作成: 1999/09/15 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3NT4 2K XP 更新: 1999/09/15 osamu 編集
Win98 でタイトルバーのグラデーション色を得る
const
COLOR_GRADIENTACTIVECAPTION = 27;
COLOR_GRADIENTINACTIVECAPTION = 28;
という前提で、GetSysColor() API を使用してください。
DWORD の色がかえります。( TColor に代入できます )
どーせやるなら、
const
COLOR_GRADIENTACTIVECAPTION = 27;
COLOR_GRADIENTINACTIVECAPTION = 28;
clGradientActiveCaption = $80000000 + COLOR_GRADIENTACTIVECAPTION;
clGradientInactiveCaption = $80000000 + COLOR_GRADIENTINACTIVECAPTION;
としておくと便利かもしれません。
この状態で、 Delphi4 の ColorToRGB のソースを見るかぎりでは
ColorToRGB(clGradientActiveCaption)
は正常に動作します。
ところで…この定数ですが、、、、Delphi4 にも定義されていないようです。
# たんに、私が未確認なだけでしょうか?
勝手に Graphic.pas (Desktop 版では Graphic.int)に書き足しておいても問題はないと思うのですが…。。。
参照: [Delphi-ML:34285] <描画>
0259 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/15 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/15 osamu 編集
子に override されたメソッドを孫クラスから呼び出す
TClassA というクラスがあって、TClassB がその子供 TClassC がさらにその子供であるとします。
TClassB が override した TClassA の手続き AProc を、TClassB 内から呼び出すには、inherited AProc; と書けば良いのは皆さんご存知だと思いますが、TClassC の中から呼び出すにはちょっとしたテクニックが必要になります。
type
TClassA = class
protected
procedure AProc; virtual;
end;
TClassB = class(TClassA)
protected
procedure AProc; override;
end;
とし、
type
TClassC = class(TClassB)
protected
procedure AProc; override;
end;
という場合です。これには、ダミーのクラス TClassD を使って、以下のようにします。
procedure TClassC.AProc;
type
TClassD = class(TClassA) end; {4番目のダミークラス}
TAProc = procedure of object; { AProc の型 }
var
method: TMethod;
begin
with method do begin
Data := Self;
Code := @TClassD.AProc;
end;
TAProc(method);
// TClassC 特有の処理
end;
スコープをうまく操作して「Delphi を騙してる」感じです。
参照: [Delphi-ML:2958] [Delphi-ML:34246] <PASCAL>
0258 D1D2 D3 D4 D5 D6 D7 3.195 98 作成: 1999/09/15 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3NT4 2K XP 更新: 1999/09/15 osamu 編集
TRichEdit.Add でリソース不足
> 現在、通信関係のソフトを開発しているのですが、RichEdit の Add メソッドで
> EOutOfResources(リソース不足)の例外が発生するようになってしまい
> ました。
>
> RichEdit の MaxLength には、20000000 の値を入れており、まだそんなに
> Add していない状態で発生します。
Lines.Add メソッドで読み込んでいるということですね。
私も同じトラブルを経験しましたが、Lines.Add の代わりに、一旦 TMemoryStream に書き出してから、LoadFromStream メソッドを使うという方法で回避しました。
参照: [Delphi-ML:34075] <Win95> <コンポーネント >
0257 D1 D2 D3 D4 D5 D6 D7 3.195 98 作成: 1999/09/15 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3NT4 2K XP 更新: 1999/09/15 osamu 編集
TPageControl に OnDblClick を追加する
> PageControl や TabControl にクリックイベントやダブルクリックイベントを
> 付けたいのですがどのようにしたらよろしいのでしょうか?
以下の派生クラスでお望みのイベントが生成されます。
# ただ、PageControl の仕様をよく調査しないで作ったので、
# もしかしたら Click関係で弊害があるかもしれません。
uses
Classes, Controls, ComCtrls;
type
TxxPageControl = class(TPageControl)
public
constructor Create(AOwner: TComponent); override;
published
property OnDblClick;
end;
implementation
constructor TxxPageControl.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := ControlStyle + [csClickEvents];
end;
参照: [Delphi-ML:34074] <Win95> <コンポーネント >
0256 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/15 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/15 osamu 編集
TTreeView で5つ以上のオーバレイ・イメージを使う
>TreeView で同時に指定可能なオーバーレイ・イメージの最大数は
>使用する ImageList のメソッド Overlay で設定可能な最大数
>(4つ、TOverlay の範囲内で指定) になると解釈しています。
>現在、この制限を越えた数のオーバーレイ・イメージを使いたいの
>ですが、何か良い方法は無いでしょうか?
Overlay は ComCtl32.dll が 4.70 以前は4個まで、4.71 以降で 15個までに拡張されたようです。4.70 以前の場合は、同時に4つまでしか使用できませんが、4.71 以降では、
ImageList_SetOverlayImage(ImageList1.Handle, 8, 5);
のように直接設定すれば OK です。(^^)/
参照: [Delphi-ML:32333] <Win95> <コンポーネント >
0255 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/15 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/15 osamu 編集
Install Shield でアンインストール時にレジストリが掃除されない
Install Shield で作ったアンインストーラは、元々有ったレジストリのキーは削除しないようにできています。
したがって、同じソフトをアンインストールしないで、2度続けてインストールすると、
1 最初のインストールで、キーを作る。
2 2回目のインストール時に、キーが既に有るので、
「これは、元々有ったキーに値を設定するのだな」と認識される。
3 Unistall Shield が起動したときに、1で作ったキーは
2で「元々有ったキー」と認識されているので、削除されない。
という風になってレジストリの情報が残ってしまうみたいです。バージョンアップの配布などでは何か策を講じる必要がありそうですね。
#[Delphi-ML:32250] の質問の原因がこれかどうかはちょっと疑問
参照: [Delphi-ML:32315] <配布>
0253 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/13 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/13 osamu 編集
TFontDialog で標準以外のサイズを選択肢に表示したい
> ルビをうつフォントを指定しようと思い、TFontDialogで
> 片付けようと思いましたが、TFontDialogで、MSゴシック
> など8Pt以下のポイントが出てきません。指定できるように
> する方法をどなたかご存知でしょうか。
思いっきり強引ですけど
unit FontDialogEx;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs;
type
TFontDialogEx = class(TFontDialog)
protected
function TaskModalDialog(DialogFunc: Pointer;
var DialogData): Bool; override;
end;
procedure Register;
implementation
uses Dlgs, CommDlg;
const
WM_FONTDIALOGEX = WM_USER + 256;
str1: string = '4';
str2: string = '5';
str3: string = '6';
str4: string = '7';
var
OldHook: function(Wnd: HWnd; Msg: UINT; WParam: WPARAM;
LParam: LPARAM): UINT; stdcall;
function FontDialogHookEx(Wnd: HWnd; Msg: UINT; WParam: WPARAM;
LParam: LPARAM): UINT; stdcall;
begin
if (Msg = WM_COMMAND) and (LOWORD(WParam) = cmb1) then begin
PostMessage(Wnd, WM_FONTDIALOGEX, 0, 0);
end;
if Msg = WM_FONTDIALOGEX then begin
SendDlgItemMessage(Wnd, cmb3, CB_RESETCONTENT, 0, 0);
SendDlgItemMessage(Wnd, cmb3, CB_ADDSTRING, 0, LongInt(PCHAR(str1)));
SendDlgItemMessage(Wnd, cmb3, CB_ADDSTRING, 0, LongInt(PCHAR(str2)));
SendDlgItemMessage(Wnd, cmb3, CB_ADDSTRING, 0, LongInt(PCHAR(str3)));
SendDlgItemMessage(Wnd, cmb3, CB_ADDSTRING, 0, LongInt(PCHAR(str4)));
Result := 1;
exit;
end;
Result := OldHook(Wnd, Msg, WParam, LParam);
end;
function TFOntDialogEx.TaskModalDialog(DialogFunc: Pointer; var DialogData): Bool;
begin
OldHook := TChooseFont(DialogData).lpfnHook;
TChooseFont(DialogData).lpfnHook := FontDialogHookEx;
inherited TaskModalDialog(DialogFunc, DialogData);
end;
procedure Register;
begin
RegisterComponents('NkCtrls', [TFontDialogEx]);
end;
end.
一応動いてます(^^ フォント名毎に表示するサイズリストを変えたい場合は、cmb1 に選択されているフォント名を確認するコードが必要になるでしょう。
一瞬元のポイントサイズがコンボボックスに表示されてしまいますが今のところ他に良い手を思いつきません(^^;
参照: [Delphi-ML:42562] <ダイアログ> <Dialogs> <描画> <コンポーネント >
0223 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/28 おばQ rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/11 K.Takaoka 編集
Editコントロールで入力を数値専用にする
入力を数値のみにする場合は、Handleの生成を確認した後
SetWindowLong(Edit1.Handle, GWL_STYLE,
GetWindowLong(Edit1.Handle,GWL_STYLE) or ES_NUMBER);
でできます。
SetWindowLong による指定はウィンドウの再生成が行われた時に無効になりますので、Delphi4 のドッキングウィンドウや、Toolbar97 などのコンポーネントを同時に利用される場合には CreateParams をオーバーライドしたコンポーネントを作成するかフローティング状態の変更通知を受け取るたびに SetWindowLong を行う必要があります。
参照: [Delphi-ML:9195] [Delphi-ML:41622] <コンポーネント > <Standard>
0243 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/09 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/11 K.Takaoka 編集
TMemo のキャレットを非表示にする
> メモコンポーネントでキャレットを消したいのです。
以下のコードでうまくいっています
// TExMemo=class(TMemo)
// TExMemo.WMSetFocusはWM_SETFOCUSのメッセージハンドラ
Procedure TExMemo.WMSetFocus(var Msg:TWMKeyDown);
Begin
Inherited;
HideCaret(Handle);
End;
参照: [Delphi-ML:33802] <Standard> <コンポーネント >
0235 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/06 西坂良幸 rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/11 K.Takaoka 編集
エディットコントロールにポップアップウィンドウをつけたい
エディットコントロールにボタンをつけた、コンボボックスもどきは、カスタムコントロールでよく見かけます。ボタンを押すと小ウィンドウが開くヤツです。これはどのように作るのでしょうか。
このような小ウィンドウをインプレースコントロールと呼びます。
ポイントは、小ウィンドウの親をコントロールにすることと、独自のフォーカスを与えないことです。これさえ理解できれば後は、小ウィンドウのVisbleの切り替えで開いたり閉じたりします。
ここでは、プロパティを受け渡すことを無視し、ただ開閉だけをやってみます。(マウス右ボタンで開く)
// 定義部
TxEdit = class;
// インプレースコントロール ここではリストボックス
TinPlaceList = class(TListbox)
private
FEdit: TxEdit;
protected
procedure CreateWnd; override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
public
constructor CreateList(AOwner: TComponent; Edit: TxEdit);
end;
// ここでは、TEditから継承する
TxEdit = class(TEdit)
private
FInplaceList:TListbox;
protected
procedure Mousedown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
public
constructor Create(AOwner: TComponent);override;
destructor Destroy;override;
procedure DropDown;
end;
// 実装部
//コンストラクタでコントロールに接続させる
constructor TInplaceList.CreateList(AOwner: TComponent; Edit: TxEdit);
begin
inherited Create(AOwner);
// 小ウィンドウに親のポインタを持たせて接続する
FEdit := TxEdit(Edit);
Visible := false;
end;
// インプレースコントロールの生成とフォーカス制御
procedure TInplaceList.CreateWnd;
begin
inherited CreateWnd;
//親の変更を行う
if not (csDesigning in ComponentState) then
Windows.SetParent(Handle, 0);
//独自のフォカスメッセージを避ける
CallWindowProc(DefWndProc, Handle, WM_SETFOCUS, 0, 0);
end;
//とりあえずマウス(左右)で閉じることにする
procedure TInplaceList.MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
inherited MouseUp(Button, Shift, X, Y);
if Button = mbLeft then
begin
// プロパティの受け渡しをここらで行う
Hide;
end;
if Button = mbRight then
Hide;
end;
// コンストラクタで、インプレースコントロールを生成
constructor TxEdit.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FInplaceList := TInplaceList.CreateList(Self, Self);
with FInplaceList do
begin
Parent := Self;
TabStop := false;
Visible := false;
Top := FInplaceList.Top + Self.Height;
end;
end;
// デストラクタで念のため明示的に解放する
destructor TxEdit.Destroy;
begin
FInplaceList.free;
inherited Destroy;
end;
// ドロップダウンリストの開閉のメソッドを作成する
procedure TxEdit.DropDown;
var
xyPos: TPoint;
begin
if (FInplaceList <> nil) and not FInplaceList.Visible then
with FInplaceList do
begin
xyPos := Self.ClientToScreen(Point(0 + Self.Width - Width, Self.Height));
SetWindowPos(Handle, 0, xyPos.X, xyPos.Y, 0, 0, SWP_NOSIZE or SWP_NOACTIVATE);
Windows.SetFocus(Handle);
Visible := not Visible;
// プロパティの受け渡しをここらで行う
end;
Invalidate;
end;
// マウス右ボタンで開く
procedure TxEdit.Mousedown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
if Button = mbRight then
DropDown;
inherited Mousedown(Button, Shift, X, Y);
end;
TListBoxのかわりに、何でも使えます。TPanelなら、電卓、カレンダなどになりますね。TGridでもいいですね。
実際にカスタムコントロールを作るときは
・やはりボタンをつける
・キーボード入力にも対応させる
・必要なプロパティの受け渡しを行う
・ドロップダウンなどイベントを記述する
・ロストフォーカスで開きっぱなしにしない
などが必要でしょうか。
参照: [Delphi-ML:40241] <コンポーネント開発> <コンポーネント > <Standard>
0250 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/09 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/09 osamu 編集
文字列を TDateTime に変換する
文字通り StrToDateTime という関数もあるんですが、これは yyyy/mm/dd などの形式にしか対応していません。ちょっとひねって VarToDateTime を使えば、ほぼどんな形式でもうまく変換できます。string -> Variant は自動で変換されるので、そのまま文字列を渡すだけで使えます。
MyDateTime := VarToDateTime('平成1年1月1日午後1時1分1秒');
全角数字もOKですが、さすがに '平成九年九月九日' は無理でした。
VarToDateTime('1999/1/2') = VarToDateTime('1/2/1999');
の結果は、True になりました。
注意点として、VerToDateTime は StrToDateTime と違い、ShortDateFormat グローバル変数を見ていないようです。
ShortDateFormat:='DD/MM/YY';
ShowMessage(FloatToStr(VarToDateTime('2/1/1999')-VarToDateTime('平成11年2月1日')));
の結果は 0 になります。
参照: <文字列> <日時> <PASCAL>
0251 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/09 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/09 osamu 編集
経過日数を取得する
> 1998年12月3日 − 1998年11月29日 = 4日
> という答えが欲しいのですが、年・月をまたがっても、
> 日数が帰って来るようにするにはどうしたらよいでしょう?
TDateTime 型は日数を単位とする double 値 (1899/12/30 0:0:0 からの経過日数) なので、Trunc して引き算すれば望みの結果が得られます。
Days := Trunc(Now) - VarToDateTime('昭和20年8月15日');
あるいは、D3 以前ならば、
Days := Trunc(Now) - EncodeDate(1945,8,15);
参照: [Delphi-ML:33577] <日時> <PASCAL>
0249 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/09 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/09 osamu 編集
1時間後を取得する
TDateTime 型は日数を単位とする double 値 (1899/12/30 0:0:0 からの経過日数) なので、一時間後の時刻を得るには 1/24 を足してやれば良いです。1時間20分後などを得るには
a := Now + StrToTime('1:20:00'); // 現時刻の1時間20分後
b := Now + EncodeTime(1, 20, 0, 0); // これも1時間20分後
EncodeDate、DecodeDate、EncodeTime、DecodeTimeを使うと環境に依存しないので、a より b がおすすめです。
参照: [Delphi-ML:33577] <日時> <PASCAL>
0248 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/09 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/09 osamu 編集
Metafile が Draw で1ピクセル小さく描画される
>拡張メタファイルを作成するツールを作ろうとしています。
>そこで、メタファイルに編集を加えるたびに以前編集した像が
>1ドット(?)ずつ縮小してしまう問題があるのです。
TMetafile.Draw を見ると
R := Rect;
Dec(R.Right); // Metafile rect includes right and bottom coords
Dec(R.Bottom);
PlayEnhMetaFile(ACanvas.Handle, FImage.FHandle, R);
となっていて、描画先矩形を 1ドット分縮小しているのが原因のようです。
Win32 Programmer's Ref. で PlayEnhMetafile を見ると、
Points along the edges of the rectangle are included in the picture.
なんて微妙なことが書いてあるのですが、実際には Win98 で試してみた限り矩形領域を小さくするのは正しくないようです。Dec をコメントにすると完全に同じ大きさの図形が得られます。
対処としては graphics.pas を修正するか、Right, Bottom を一つ大きめに指定して StretchDraw するしかないようです。
ただ、ディスプレイデバイスとメタファイルの参照デバイスが異なると多少の誤差は避けられないようです。この辺を考慮して高精度のメタファイルを作るにはメタファイルを直接編集するしかないと思います。
参照: [Delphi-ML:33593] <画像> <バグ> <描画>
0247 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/09 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/09 osamu 編集
TWebBrowser を使って HTML の描画イメージを取得する
WebBrowser1.ControlInterface.Width と 同.Height を保存し、ページのサイズにして OleDraw の後で復元したところ、イメージが取得できました。
IWebBrowser の Width, Height は表示しているコントロールの Width/Height にイコールであるため、TWebBrowser(TOleContainer)の大きさが可変であることが必要なのがちょっと困者ですが、なんとかなった、、、ことにします。
web: TWebBrowser;
var
bmp: TBitmap;
body: OleVariant;
w, h: Integer;
begin
body := (Web.Document as IHTMLDocument2).Body;
bmp := TBitmap.Create;
try
// 軽くマージンをつけておかないとスクロールバーがついちゃいます。
bmp.Width := body.scrollWidth + 16;
bmp.Height := body.scrollHeight + 16;
(* ここで WindowLock(Web.Handle) すべきでしょうか?
CPU などの性能によってはチラつきそうです。 *)
// 現在値を保存してリサイズします。
w := Web.ControllInterface.Width;
h := Web.ControllInterface.Height;
Web.ControllInterface.Width := bmp.Width;
Web.ControllInterface.Height := bmp.Height;
// 描画します。
OleDraw(Web.ControllInterface, DVASPECT_DOCPRINT, bmp.Canvas.Handle,
Rect(0, 0, bmp.Width, bmp.Height));
// 元の大きさに戻します。
Web.ControllInterface.Width := w;
Web.ControllInterface.Height := h;
(* ここで WindowLock() してるなら戻す *)
(* ここでいろいろ bmp を使って遊べます *)
finally
bmp.Free;
end;
end;
一応、上記のようなかんじなのですが、dislable なスクロールバーがつくものの、全体をイメージにできました。scrollbar の幅と高さを GetSystemMetrics などで手にいれてカットすれば okay っぽいのですが、常に scrollbar がついているか…など検証が必要そうです。
# IWebBrowser 関連を操作して、強制的に消せましたっけ?
参照: [Delphi-ML:33701] <WWW> <通信>
0246 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/09 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/09 osamu 編集
DLL 内で TDBCtrlGrid を使うときの注意事項
>DLL化したアプリのフォームにDBCtrlGrid,DBCheckBoxを置いてローカルDB
>の内容を表示させているのですが、DLL化した途端にカレントレコードしか
>表示されなくなってしまいました。
この現象は以下の理由で発生します。
1.DLLの中では、何もしなければApplication.Handle=0です。
2.DBCtrlGridに配置されるコンポーネントの一部(DBCheckBoxを含む)はカレントレコード以外へ複製表示を行うために、Application.Handleを利用しています。
具体的にはDBCtrls.pasのTPaintControl.GetHandleで、次のようなコードで表示用の別ウインドウを生成しています。
with Params do
FHandle := CreateWindowEx(ExStyle, PChar(FClassName),
PChar(TWinControlAccess(FOwner).Text), Style or WS_VISIBLE,
X, Y, Width, Height, Application.Handle, 0, HInstance, nil);
Application.Handle=0 だと CreateWindowEXは失敗し、FHandle=0となるので複製表示が正常に行われないのです。
対処方法としては、Delphiヘルプの TApplication.Handle に書いてありますが、EXE ホストのメインウィンドウのウィンドウハンドルを DLL の Application.Handleに割り当てます。
別の方法としては、上記の TPaintControl.GetHandle の Application.Handle を FOwner.Handle に書き換えてもいいでしょう。
このスレッドではさらに、TApplication.MainForm.Handle を渡すべきか、TApplication.Handle を渡すべきか。また、TApplication.Handle を渡す際には、DLL 側の uses から Forms を削除しなければ、など、使い方に関する細かい注意点が議論されています。
参照: [Delphi-ML:33636] [Delphi-ML:33791] <データベース> <コンポーネント > <DataControls>
0245 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/09 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/09 osamu 編集
サブネットマスク値など TCP/IP 関連パラメータを取得する
>ネットワークコンピュータに設定されているTCP/IPのアドレスは
>gethostbynameで取得できると思いますが、サブネットマスクの
>アドレスはどうやって取得すればよいのでしょうか。
こういうことは MS で検索しましょう。
http://support.microsoft.com/support/
で WindowsNT workStation を SUBNETMASK で検索すると
http://support.microsoft.com/support/kb/articles/q120/6/42.asp?FR=0
が見つかります。
TCP/IP 関連パラメータの格納場所が公開されています。
参照: [Delphi-ML:33813] <通信>
0244 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/09 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/09 osamu 編集
UDP 通信で NoParam ???
Q:
UDPで受信するコードを書こうとしています。
ヘルプで調べてみたのですが、以下のNoParamの部分が分かりません。
procedure TForm1.UDP1DataArrival(Sender: TObject; bytesTotal: Integer);
begin
UDP1.GetData(data, NoParam); //データを取得する
Memo1.Lines.Append(data); //表示を追加する
end;
A:
function NoParam: Variant;
begin
TVarData(Result).VType := varError;
TVarData(Result).VError:= DISP_E_PARAMNOTFOUND;
end;
わかりにくいのですが、一応載っています。「パラメータの省略」というページです。
参照: [Delphi-ML:33778] <通信>
0242 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/09 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/09 osamu 編集
デフォルトプリンタの設定
>「通常使うプリンタ」を変更したいのですが、よい方法が見つかりません。
最近 SetPrinter API の使い方を覚えました。これで出来ます(^^
procedure SetDefaultPrinter(Index: Integer);
begin
var Device, Driver, Port: array[0..511] of Char;
hDevMode: THandle;
hPrinter: THandle;
pInfo2: ^TPrinterInfo2;
Size: DWORD;
begin
Printer.PrinterIndex := Index;
// デバイス名を得る
Printer.GetPrinter(Device, Driver, Port, hDevMode);
// プリンタハンドルを作る。
Win32Check(OpenPrinter(Device, hPrinter, Nil));
try
// Printer_Info_2 に必要なバッファサイズを取得
WinSpool.GetPrinter(hPrinter, 2, pInfo2, 0, @size);
// バッファ確保
GetMem(pInfo2, size);
try
// プリンタ情報を取得
Win32Check(WinSpool.GetPrinter(hPrinter, 2, pInfo2, Size, @size));
// デフォルトプリンタにする
pInfo2.Attributes := pInfo2.Attributes or
PRINTER_ATTRIBUTE_DEFAULT;
// プリンタ情報をプリンタへセットする
Win32Check(WinSpool.SetPrinter(hPrinter, 2, pInfo2,
PRINTER_CONTROL_SET_STATUS));
finally
FreeMem(pInfo2);
end;
finally
ClosePrinter(hPrinter);
end;
end;
Win98 でしか確認していません。あしからず。
参照: [Delphi-ML:33386] <印刷>
0241 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/09 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/09 osamu 編集
TPageControl でタブをドラッグして順序入れ替え
>マウスでTabSheet上のTabをドラッグして
>Tabの順序を交換するにはどうしたらよいのでしょうか?
面白そうなので、D3 でやってみました。
--- 以下 例文 ---
procedure TForm1.PageControl1MouseDown(Sender: TObject;...);
begin
PageControl1.BeginDrag(false);
end;
procedure TForm1.PageControl1DragOver(Sender, ...);
begin
if Sender is TPageControl then
Accept := true;
end;
procedure TForm1.PageControl1DragDrop(Sender, ...);
var
i: Integer;
r: TRect;
begin
if not (Sender is TPageControl) then
Exit;
with PageControl1 do
begin
for i := 0 to PageCount - 1 do
begin
Perform(TCM_GETITEMRECT, i, LPARAM(@r));
if PtInRect(r, Point(X, Y)) then
begin
if i <> ActivePage.PageIndex then
ActivePage.PageIndex := i;
Exit;
end;
end;
end;
end;
参照: [Delphi-ML:33411] <Win95> <コンポーネント >
0240 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/08 osamu 編集
任意のドライブをセクタ単位で読み書きする
Q:
MS-DOS Ver.2.1でフォーマットされたFDをWin95で読込みたいのですが、どのようにすればよいのでしょうか?
セクタ単位で読込む方法が解りません。
A:
'\\.\A:' で該当のドライブ( A: の部分を適宜変更)に対して CreateFile() すればアクセスできます。
# You can use the CreateFile function to open a disk drive
# or a partition on a disk drive. The function returns a
# handle to the disk device; that handle can be used with
# the DeviceIOControl function. The following requirements
# must be met in order for such a call to succeed:
#
# The caller must have administrative privileges for the
# operation to succeed on a hard disk drive.
# The lpFileName string should be of the form \\.\PHYSICALDRIVEx
# to open the hard disk x. Hard disk numbers start
注意として GetDiskFreeSpace() でセクタサイズを調べて FILE_FLAG_NO_BUFFERING でアクセスしてください。
参照: [Delphi-ML:32482] <ファイル>
0239 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/08 osamu 編集
IME に未確定文字列を入力
> プログラム中からIMEに未変換・未確定文字列として送りたいのですが。。。
>
> 例
> プログラムで渡す文字列 へいせい10ねん
> ↓
> IME 「へいせい10ねん」(未変換・未確定)
方法1
「へいせい10ねん」を「heisei10nenn」もしくは、
「ヘイセイ10ネン」(ほんとは半角)に変換し、キーボードイベントとして
sendkeysや、keybd_event APIでIMEに送る。
ローマ字変換か、カナ変換かは、ImmGetConversionStatus APIで
判断出来ます。
例
Edit1.SetFocus;
SendKeys('heisei10nenn',true);
方法2
ImmSetCompositionString APIでIMEに直接セットする。
例
var
IMC:HIMC;
BufLen:longint;
Buf:string;
begin
Edit1.SetFocus;
IMC:=ImmGetContext(Edit1.Handle);
Buf:='へいせい10ねん';
BufLen:=length(Buf);
//MS-IME98
ImmSetCompositionStringA(IMC,SCS_SETSTR,PChar(Buf),BufLen,nil,0);
//ATOK12
ImmSetCompositionStringW(IMC,SCS_SETSTR,PChar(Buf),BufLen,nil,0);
ImmReleaseContext(Edit1.Handle,IMC);
end;
MS-IME98とATOK12でテストしましたが、ATOK12の場合、UniCodeの方のAPIで実行しないとちゃんと動作しませんでした。
なぜなのかは、よくわかりません(^^;
#詳しくは、英語のヘルプやAPIの解説書等で調べてください。
参照: [Delphi-ML:32185] <その他Windows関連> <Windows> <コンポーネント > <Standard>
0143 D1 D2 D3 D4 D5 D6 D7 3.1 95 98作成: 1999/02/11 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/08 osamu 編集
フォルダのタイムスタンプを変更する
ファイルのタイムスタンプを変更するのはできるのですがフォルダのタイムスタンプはどのようにして変更したらよいのでしょうか?
procedure ChangeDirTime(DirName : String;Date : TDateTime);
var
FHandle : integer;
Begin
FHandle := CreateFile(Pchar(DirName), { ディレクトリ名最後に\はいらない }
GENERIC_WRITE, { 書きこみ許可しないと変えられない }
FILE_SHARE_Read, { 最低他からアクセスできないとハンドルがもらえない }
nil, { セキュリティは無視(いいのかな?) }
OPEN_EXISTING, { ディレクトリは存在していることを前提 }
FILE_FLAG_BACKUP_SEMANTICS, { APIのヘルプをみてね }
0);
FileSetDate(FHandle,DateTimeToFileDate(Date));
CloseHandle(FHandle);
End;
タイムスタンプの設定にAPIのSetFileTimeを使う手もありますがフォルダならこれで十分です。
D3+Win98 では動きませんでした。NT では動くのかな???
参照: [Delphi-ML:24889] <Windows> <ファイル>
0013 D1 D2 D3 D4D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.9 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/08 osamu 編集
Delphi3 でコンポーネントパレットを複数行にする
RegEdit.exeで
キー :"HKEY_CURRENT_USERS\Software\Borland\Delphi\?.0\Desktop"
名前 :"TabMultiLine"
文字列:"1"
を追加する。
参照: <開発環境>
0003 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/08 osamu 編集
ショートカットキーに'+'を使う。
Delphi2.0 以上では、『!"#$%&'()=~|^{}`*+_?><』など、101のフルキー側(テンキーを除いた部分)で、Shiftと同時にキーを押さなければ入力できない文字はショートカットに使えない。
テンキーがついてたり、JISキーボードならばワンキーで押せるものもあるのに。
こういったキーを使うには、ShortCut プロパティにキーコードを直接セットすればいい。
たとえば、
Test2.ShortCut:=$6B; // <- + キーのキーコード
でも、これだとメニューの右端にキー名が表示されない。 (;_;)
対処方法は [Tips:4]。
参照: [Delphi-ML:17954] [Tips:4] <コンポーネント > <Standard> <メニュー>
0224 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/28 おばQ rev 1.1.1.5 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/07 西坂良幸 編集
右寄せ・数値入力可能なEditコンポーネントを作りたい。
Win98 以降では、次のようにすれば完璧な右寄せエディタが作成できます。
WindowsNT4 では SP3 (User32.Dll が 03/11/97 07:04p Intel, 03/11/97 06:14p Alpha) から利用できます。
type
TRgEdit = class(TEdit)
protected
procedure CreateParams(var params: TCreateParams); override;
end;
procedure TRgEdit.CreateParams(var params: TCreateParams);
begin
inherited CreateParams(params);
with Params do
Style := Style or ES_RIGHT;
end;
追記のバージョン以前、またDelhi3以前は、
Style := Style or WS_MULTILINE or ES_RIGHT and not WS_VSCROLL and not WS_AUTOVSCROLL ;
とします。
また、入力を数字専用される場合は、上記にES_NUMBERを論理和
させます。
以下は、Alignment、NumOnly の各プロパティを作成する例です
(FAlignment、FNumOnlyやプロパティ定義部分は省略しています)
procedure TRgEdit.CreateParams(var params: TCreateParams);
const
Alignments: array[TAlignment]of Word = (ES_LEFT, ES_RIGHT, ES_CENTER);
NumlOnlies: array[Boolean]of Word = (0, ES_NUMBER);
begin
inherited CreateParams(Params);
Params.Style := Params.Style
or Alignments[FAlignment]
or NumlOnlies[FNumlonly];
end;
procedure TRgEdit.SetAlignment(NewValue: TAlignment);
begin
if FAlignment <> NewValue then
begin
FAlignment := NewValue;
RecreateWnd;// CreateParamsを呼び出すのではないことに注意
end;
end;
procedure TRgEdit.SetNumlOnly(NewValue: Boolean);
begin
if FNumlOnly <> NewValue then
begin
FNumlOnly := NewValue;
RecreateWnd;
end;
end;
参照: [Delphi-ML:9195] [Delphi-ML:41622] <コンポーネント > <Standard>
0011 D1D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/06 西坂良幸 編集
メッセージボックスを独自にカスタマイズしたものを使いたい。
メッセージボックスを使ってたのですが、動きはほとんど同じで表示のアイコンやボタンをカスタマイズする方法って言うのはないものでしょうか? 決まりきった疑問符や感嘆符などではなく、ユーザが自由にビットマップを貼り付けられるようにしたいのですが。
CreateMessageDialog関数を使えば簡単にできます。
以下の例を試して見て下さい。あなた好みのメッセージボックスのイメージが沸いてきますよ。
CreateMessageDialogは、メッセージダイアログのインスタンスを返します。
このダイアログ(TForm)の上のコンポーネントは
クラス 名前
Components[0]が、TImage 'Image'
Components[1]が、TLabel 'Message'
Components[2]が、TButton たとえば 'Yes','No' など
Components[3] ・・・ボタンがつづく
となっています。
function MyMessageBox(Const Msg: String): integer;
var
Dlg:TForm;
begin
Dlg:=CreateMessageDialog(Msg, mtError, [mbYes,mbNo]);
with Dlg do
try
Dlg.Caption := '登録確認';
TImage(Components[0]).Picture.Icon.LoadFromFile('C:\Program Files\Borland\Delphi 4\IMAGES\ICONS\Technlgy.ico');
TButton(Components[2]).Caption := '登録';
TButton(Components[3]).Caption := '中止';
ret := ShowModal;
finally
Free;
end;
end;
// テスト
procedure TForm1.Button1Click(Sender: TObject);
begin
if MyMessageBox('データを登録しますか?') = mrYes then
// 登録処理
else
; // 無視
end;
てな具合で絵(BitmapやIcon)を変える事ができます。
LeftやTopを使って配置を換えることも可能です。
HelpContextなども設定できます。
絵は32x32サイズに作っておいた方がいいです。
大きさが違う場合には位置の調整をしなければならなくなりますが、ダイアログ上のコントロールに名前がついていないので苦労します。(D4以降は上記のように名前も付いています)
ボタンについても同様です。
参照: [Delphi-ML:5100] <ダイアログ>
0001 D1D2 D3 D4 D5 D6 D7 3.195 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/06 osamu 編集
Delphi 2.0/3.0でCPUウィンドウを出す
RegEdit.exeで
キー :"HKEY_USERS\.Default\Software\Borland\Delphi\?.0\Debugging"
名前 :"EnableCPU"
文字列:"1"
を追加する。
参照: [Delphi-ML:6088] <開発環境>
0005 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/06 西坂良幸 編集
メインフォーム以外のフォームをタスクバーに入れたい。
フォームのウィンドウスタイルにWS_EX_APPWINDOWを加えます。
TForm2 = class(TForm)
:
procedure FormClose(Sender: TObject; var Action: TCloseAction);
protected
procedure CreateParams(var Params: TCreateParams);
override;
:
end;
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := caFree; // これを忘れないこと
Form2 := nil;
end;
procedure TForm2.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW;
end;
とします。[Delphi-ML:7921]
また、タスクバーにも入れたくないときは、
Application.OnMinimize := AppMinimize;
を設定し、
procedure TForm2.AppMinimize(Sender: TObject);
begin
ShowWindow(Form1.Handle, SW_HIDE);
end;
ですね。
参照: [Delphi-ML:3429] [Delphi-ML:6234] [Delphi-ML:17682] <タスクバー> <Windows> <フォーム>
0232 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/01 西坂良幸 rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/06 西坂良幸 編集
APPのフォームを最小化して起動したい
メインフォームのWindowStyleをwsMinimizedに設定しておくと自分で起動するときは、最小化されタスクバーに入ります。
しかし、他のAPPから起動する時やAPIのShowWindowやショートカットやタスクトレイなどで起動する時はちょっとやっかいです。
これは、Application(変数)のフォームとメインフォームの同期がとれないからです。これはメインフォームに送られてくるスタイルフラッグに合わせて、Applicationのスタイルを合わせてやる必要があります。
この方法は、MLではいくつも紹介されています。[Delphi-ML:13173] [Delphi-ML:13185]
(混乱ということでは、古いバージョンでは、GetStartupInfoで取得できるwShowWindow値が、CmdShowと必ずしも一致しないという問題や、D2→D3で仕様が変わっているということもあります。)
以下のコードはどうでしょうか。
procedure TForm1.FormCreate(Sender: TObject);
var
SI : TStartupInfo ;
begin
// D4では、常にCmdShow = SI.wShowWindowですが念のため
GetStartupInfo( SI ) ;
CmdShow := SI.wShowWindow ;
// 最小化に関係するスタイルフラッグにすべて対応
case cmdShow of
SW_SHOWMINIMIZED,
SW_MINIMIZE,
SW_SHOWMINNOACTIVE :
Application.Minimize ;// 同期させる
end ;
end;
なお、子フォームの場合をDeskTopでなく、タスクバーに入れるには、0005番「メインフォーム以外のフォームをタスクバーに入れたい。」を参照して下さい。
参照: [Delphi-ML:13173] [Delphi-ML:3426] [Delphi-ML:23218] [Delphi-ML:25557] <タスクバー> <Windows> <フォーム>
0238 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/06 西坂良幸 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/06 西坂良幸 編集
メッセージボックスのデフォルトボタンを変えたい
ボタンが、[はい、いいえ]ならNoの方に、デフォルトが、[はい、いいえ、キャンセル]ならキャンセルにデフォルトを置きたい場合があります。
APIのMessageBoxのヘルプから、スタイルフラッグを調べて下さい。ApplicationのMessageBox メソッドを使えば簡単です。
Application.MessageBox('デフォルトボタンを変えています', PChar(Application.Title), MB_ICONQUESTION + MB_YESNO + MB_DEFBUTTON2);
Application.MessageBox('デフォルトボタンを変えています', PChar(Application.Title), MB_ICONQUESTION + MB_YESNOCancel + MB_DEFBUTTON3);
がわかりやすいでしょう。
CreateMessageDialog関数を使われるなら、次のような自作関数はどうでしょうか。
Defaultパラメータを、2とか3にすれば、デフォルトボタンを変えることができます。
function MessageDlgEx(const Msg: string; AType: TMsgDlgType; AButtons: TMsgDlgButtons; HelpCtx: Longint = 0; default: Word = 1): Word;
var
Dlg: TForm;
begin
Dlg := CreateMessageDialog(Msg, AType, AButtons);
try
// 最初のボタンが Components[2]である
if (Default <1) or (Default >= Dlg.ComponentCount - 1) then Default := 1;
Dlg.HelpContext := HelpCtx;
Dlg.ActiveControl := TWinControl(Dlg.Components[default + 1]);
result := Dlg.ShowModal;
finally
Dlg.Free;
end;
end;
参照: [Delphi-ML:5944] [Delphi-ML:32505] <ダイアログ>
0123 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/06 西坂良幸 編集
ButtonのCaptionで改行を使って文字を複数段で表示したい
Windows95では #13#10 挿入することにより思った通り表示できるのですが
WindowsNTの場合は改行されません。
procedure TForm1.Button1Click(Sender: TObject);
begin
SetWindowLong(Button1.Handle, GWL_STYLE, GetWindowLong(Button1.Handle, GWL_STYLE) or BS_MULTILINE);
Button1.Caption := 'ABC' + #13#10 + 'DEF';
end;
なお、コンポ−ネント化する場合はCreateParamsをオーバーライドして下さい。
この方法は、TButtonControl系(TRadioButton,TCheckBox)でほとんど使えますが、オーナードロー系(TBitBtn,TSpeedButtonなど)のボタンではできません。
また、以下のプロパティエディッタをインストールすれば、オブジェクトインスペクタで,改行コードを入力を'\n'ですることが出来るようになります。
// 定義部
type
// 複数行の入力を\nで受け入れるプロパティエディッタ
TMultCapProperty = Class(TCaptionProperty)
Public
Function GetValue: string; Override;
Procedure SetValue(const Value: string); Override;
End;
procedure Register;
// 実装部
// 置き換える関数
procedure ReplaceStr(var Source : string; Search, Replace : string);
function XPos(Source, Search : string):integer;
begin
if StrPos(PChar(Source), PCHar(Search)) = nil then
result := 0
else
result := StrPos(PChar(Source), PCHar(Search)) - PChar(Source) + 1;
end;
var
p, L1, L2 : Integer;
begin
L2 := Length(Search);
p := XPos(Source, Search);
while p <> 0 do
begin
L1 := Length(Source);
if p = 1 then
Source := Replace + Copy(Source, L2 + 1, L1)
else
Source := Copy(Source, 1, p - 1) + Replace +
Copy(Source, p + L2, L1);
p := XPos(Source, Search);
end;
end;
function TMultCapProperty.GetValue: string;
begin
Result := GetStrValue;
// 以下3つのパターンがある
ReplaceStr(Result, #13 + #10, '\n');
ReplaceStr(Result, #10, '\n');
ReplaceStr(Result, #13, '\n');
end;
procedure TMultCapProperty.SetValue(const Value: string);
var
Caption : string;
begin
Caption := Value;
ReplaceStr(Caption, '\n', #13);
SetStrValue(Caption);
end;
// プロパティエディタとして登録する
procedure Register;
begin
// TLabelのCaptionプロパティエディタの登録--'\n'で改行入力
RegisterPropertyEditor(TypeInfo(TCaption), TLabel; , 'Caption', TMultCapProperty);
// TButtonのCaptionプロパティエディタの登録--'\n'で改行入力
// ただし上記BS_MULTILINEが設定されていないとダメ
RegisterPropertyEditor(TypeInfo(TCaption), TButton; , 'Caption', TMultCapProperty);
end;
参照: [Delphi-ML:17735] [Delphi-ML:39679] [builder:6270] <コンポーネント開発> <コンポーネント > <Standard>
0063 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/06 西坂良幸 編集
キーボードでボタンを押したとき、ボタンをちゃんと沈ませたい
BM_SETSTATE メッセージを使うとできます。
コンポーネント化は、ちょっと長くなるので、詳細は [Delphi-ML:19607] を見てください。
以下、スペースキーでの例です。
procedure TForm1.Button1KeyPress(Sender: TObject; var Key: Char);
begin
if Key = #32 then
SendMessage(Button1.Handle,BM_SETSTATE,1,0);
end;
procedure TForm1.Button1KeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = 32 then
SendMessage(Button1.Handle,BM_SETSTATE,0,0);
end;
コーディングだけで呼ぶ場合は
SendMessage(Button1.Handle,BM_SETSTATE,1,0);
Button1.Click;
Sleep(100); // 適当に好みで決める
PostMessage(Button1.Handle,BM_SETSTATE,0,0);
参照: [Delphi-ML:19607] <コンポーネント > <Standard>
0237 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/06 西坂良幸 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/06 西坂良幸 編集
カーソルキーでボタン(TButton)のフォーカス移動をやめさせたい
TButtonのOnKeyPress、OnKeyDownなどでやってみてもダメですね。これは、Windowsのボタンの仕様です。
フォームへのCM_DialogKeyメッセージをとらえて処理すると可能です。
procedure TForm1.CMDialogKey(var Msg:TCMDialogKey);
begin
case Msg.CharCode of
VK_UP,VK_DOWN,VK_LEFT,VK_RIGHT : Msg.CharCode:=0;
end;
inherited;
end;
このメッセ−ジはタブキー(VK_TAB)もとらえることが出来ます。
参照: [Delphi-ML:3691] [Delphi-ML:33541] <フォーム> <コンポーネント > <Standard>
0236 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/09/06 西坂良幸 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/06 西坂良幸 編集
エディットコントロールにコンボボックスのようなボタンをつけたい
エディットをParentとするフォーカスを持たないTSpeedButtonを、貼り付けてやれば簡単です。
注意するのは、編集領域がボタンに重ならないようにEM_SETRECTNPを送ることですが、このメッセージが有効になるには、TEditのスタイルフラッグにES_MULTILINEを加えなければなりません。
以下の例は、ボタンを押せば単にメッセージボックスがでるだけのものです。
// 定義部
TxEdit = class(TEdit)
private
FButton: TSpeedButton;
FOnButtonClick: TNotifyEvent;
procedure SetEditRect;
procedure WMSize(var Message: TWMSize); message WM_SIZE;
protected
procedure ButtonClick (Sender: TObject);
procedure CreateParams(var Params: TCreateParams); override;
procedure CreateWnd; override;
public
constructor Create(AOwner: TComponent); override;
published
property OnButtonClick: TNotifyEvent read FOnButtonClick write FOnButtonClick;
end;
// 実装部
constructor TxEdit.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
// ボタンを作成する
FButton := TSpeedButton.Create (Self);
with FButton do
begin
Parent := Self;
Width := 18;
Height := Height - 4;
// リソースから ▼ のビットマップ(10×8程度×2)を読むのは省略
// NumGlyphs := 2;
// result.Glyph.LoadFromResourceName(HInstance,'????');
CurSor := crArrow;
OnClick := ButtonClick;
end;
end;
// スタイルフラッグにES_MULTILINEが必要です。
procedure TxEdit.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.Style := Params.Style or ES_MULTILINE;
end;
// ハンドルが生成されてからSetEditRectを呼ぶ
procedure TxEdit.CreateWnd;
begin
inherited CreateWnd;
SetEditRect;
end;
// エディット(編集)領域を再設定する(ボタンの部分を排除)
procedure TxEdit.SetEditRect;
var
Loc: TRect;
begin
SendMessage(Handle, EM_GETRECT, 0, LongInt(@Loc));
Loc.Right := ClientWidth - FButton.Width - 2;
SendMessage(Handle, EM_SETRECTNP, 0, LongInt(@Loc));
end;
// 常に左端にアジャストさせる
procedure TxEdit.WMSize(var Message: TWMSize);
begin
inherited;
if FButton <> nil then
begin
if NewStyleControls and Ctl3D then
FButton.SetBounds(Width - FButton.Width - 4, 0, FButton.Width, Height - 4)
else FButton.SetBounds (Width - FButton.Width, 1, FButton.Width, Height - 2);
SetEditRect;
end;
end;
// ボタンのクリックに対応するイベントを設定する
procedure TxEdit.ButtonClick (Sender: TObject);
begin
if Assigned(FOnButtonClick) then FOnButtonClick(self);
ShowMessage('ボタンが押されました')
// ここに必要な処理を書く
end;
この他、CM_EnabledChangeを捕まえて、EditとボタンのEnabledを同期させることが必要でしょうか。
参照: <コンポーネント開発> <コンポーネント > <Standard>
0225 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/28 西坂良幸 rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/05 藤中理香 編集
数値の2進数への変換で効率的な方法
ML[Delphi-ML:32617]〜[Delphi-ML:32720]のスレッドにいろいろな変換関数例が示されています。
ベンチワークしたわけではありませんが、効率ということでは、中川さんのコードがお勧めでは?
「10進から2進数への変換に標準関数はありませんが、デルファイではTBitsという
似て非なるクラスがあります。(定義はclassesの中)
そこを見てみますと、インラインアセンブラが使われています。ということで、題材的に
インラインアセンブラが向いているのではないかと思い作ってみました。」[Delphi-ML:32720]中川
以下、中川さんの例示コードをIntToHex型パラメータの関数にしたものです。(4桁ごとのスペースはありません)
function IntToBin(Int: LongInt; Digits: Integer = 32): string;
// 変換の関数
procedure ToBin(Int: LongInt; x: pointer);
asm
push ebx; // ebxは使用不可なので保存
mov edx,x; // 文字列の先頭アドレス
mov eax,int; // 変換する整数
mov ecx,32; // 32回のループのセット
@MAWASU:
mov bl,'0'; // とりあえず文字「0」とする
sal eax,1; // 整数を左シフトして、1ビットを取り出す
jnc @TOBU; // ビットが0ならそのまま
inc bx; // ビットが1ならば文字を「1」にする
@TOBU:
mov [edx],bl; // 文字を書き込む
inc edx; // 文字列のポインタを進める。
loop @MAWASU; // これを繰り返す
mov bl,0; // 文字列の最後は空文字
mov [edx],bl; // 最後の空文字の書き込み
pop ebx; // ebxを呼び戻す
end;
var
Buff: array[0..63] of Char;
begin
if (Digits> 1) and (Digits <= 32) then
begin
ToBin(Int, @Buff);
result := StrPas(Buff + 32 - Digits);
exit;
end;
raise EConvertError.Create('第二パラメータが範囲外です。');
end;
// テスト
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text := IntToBin(16,8);
Edit2.Text := IntToBin(-1);
Edit3.text := IntToBin(Integer($F0F0F0F0));
end;
参照: [Delphi-ML:32720] <計算> <PASCAL>
0198 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/07/09 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/05 西坂良幸 編集
ドッカブルメニュー(ツールバー形式のメニュー)を実現したい。
Delphi4でドッカブルメニューを実現するにはどうしたら良いのでしょうか?
IDEでは実現されているので、出来るとは思うのですが...。
アメリカのインプライズのサイトで、ドッカブルメニューのコンポが配られていますよ。使用した感じ日本語環境でも大丈夫そうです。まぁ、多分かなりの問い合わせがあったんでしょうね。
TMenubar -> http://www.borland.com/devsupport/delphi/downloads/index.html
このコンポーネントを使わない場合は、以下の手順で行います。[Delphi-ML:35243][Delphi-ML:35223] Oyats、fumika
・フォームにTCoolBarを配置し、そのTCoolBarの上にTToolBarを配置
・TToolBarのShowCaptionプロパティをTrue、FlatプロパティをTrue、EdgeBordersのedTopプロパティをFalseにする
・フォームにTMainMenuを配置
・TMainMenuのメニューエディッタで、作りたいメニューを作る(MenuItemの作成)
・TToolBarの上にTToolButtonを作成し、各TToolButtonのMenuItemプロパティにTMainMenuの各MenuItem(Parent)を対応させる
・各TToolButtonのGroupedプロパティをTrue、AutoSizeプロパティをTrueにする
・フォームのMenuプロパティをnilにする(メニュー表示を消す)
・フォームのOnShortCutイベントに次のハンドラを設定する
procedure TForm1.FormShortCut(var Msg: TWMKey; var Handled: Boolean);
begin
Handled := Handled or MainMenu1.IsShortCut(Msg);
end;
TMenuBarを使う人も
http://member.nifty.ne.jp/cosmic/delphi/tips_vcl.html#tmenubar
は覗いておきましょう。
参照: [Delphi-ML:32742] [Delphi-ML:35223] <Win95> <コンポーネント > <Standard>
0079 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/02 西坂良幸 編集
Windowsの「ファイルの検索」ダイアログをプログラムから使いたい
「ファイルの検索」ダイアログを出すには、DDEを使います。
フォームに TDdeClientConv コンポーネント(Systemタブ)を乗っけて、
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 フォルダがカレントになります。
ちなみにどこで見つけたかというと、レジストリの
HKEY_CLASSES_ROOT\Directory\shell\find\ddeexec
です。
なお、終了させる場合は
procedure TForm1.Button2Click(Sender: TObject);
var
hDialog;
begin
hDialog:=FindWindow('#32770',nil); {ダイヤログのハンドル}
SendMessage(hDialog, WM_CLOSE, 0, 0); {終了}
end;
が簡単でいいでしょう。
参照: [Delphi-ML:20377] <その他Windows関連> <ShellApi> <Windows> <ダイアログ>
0228 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/29 西坂良幸 rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/01 西坂良幸 編集
フォームの最小化、最大化をアニメーションでやりたい。(DELPHI)
過去のMLを検索してみましたが案外少ないようです。「最小化」の話題は結構あるのに。このような機能をもつコンポーネントがたくさんあるし、あまり重要な機能でもないということですか。
これは当然ですが、SetBoundsメソッドなんかをいじっていてもダメですね。
また、WM_SYSCOMMANDメッセージをとらえて
procedure TForm1.WMSysCommand(var msg: TWMSysCommand);
begin
if msg.CMdType = SC_MINIMIZE then
WindowState := wsMinimized;
inherited;
end;
としてもだめですね。[Delphi-ML:939] 原
結論的に言うと、MovWindowというAPIを使うんですが、このためのちょっとした工夫が、メッセージの理解やその処理の仕方について大変勉強になります。(コンポーネント貼り付けるだけが能ではない!)
ということで、今回はちょっと解説を。
・フォーム(メインも含めて)とApplicationの同期を考慮しなければならないので、双方のWindProcをオーバーライドしなければならない。
・自分のメソッドのWindProcは、オーバーライドは簡単ですよね、
・では、ApplicationのWndProcはどうすればオーバーライドのようにできるのか。
というようなことを考えて下さい。
TForm1 = class(TForm)
PopupMenu1: TPopupMenu;
N1: TMenuItem;
N2: TMenuItem;
N3: TMenuItem; // ボタンではなく、メニューでテストがミソ
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure N1Click(Sender: TObject);
procedure N2Click(Sender: TObject);
private
NewAppWndProc: Pointer;
OldAppWndProc: Pointer;
protected
procedure WndProc(var Msg: TMessage); override;
procedure AppWndProc(var Msg: TMessage); virtual;
public
end;
// 途中省略
uses MMSystem;
{$R *.DFM}
// AppliCationのWndProcをすり替えている
procedure TForm1.FormCreate(Sender: TObject);
begin
if Application.MainForm = nil then
begin
// WndProcのインスタンスを作成
NewAppWndProc := MakeObjectInstance(AppWndProc);
// 元のApplicationのWndProcアドレスを保存
OldAppWndProc := Pointer(GetWindowLong(Application.Handle, GWL_WNDPROC));
// ApplicationのWndProcアドレスをNewAppWndProcに変更
SetWindowLong(Application.Handle, GWL_WNDPROC, Longint(NewAppWndProc));
end;
end;
// 終わる時は後始末をする
procedure TForm1.FormDestroy(Sender: TObject);
begin
if (Application.Handle <> 0) and (OldAppWndProc <> nil) then
begin
// 元のWndProcに戻す
SetWindowLong(Application.Handle, GWL_WNDPROC, Longint(OldAppWndProc));
// インスタンスを解放
FreeObjectInstance(NewAppWndProc);
end;
end;
//ウィンドウプロシージャを書き直す
procedure TForm1.WndProc(var Msg: TMessage);
var
SaveTitle: string;
begin
with Msg do // ここでメッセージを横取り
begin
if (Msg = WM_SYSCOMMAND) and (WParam = SC_MINIMIZE) then
with Application do
begin
SaveTitle := Title;
Title := Caption;
// 再び手前に表示−−おまじない
NormalizeTopMosts;
// トップ レベルでアクティブ ウィンドウ化
SetActiveWindow(Handle);
// ウィンドウの位置と寸法を変更
MoveWindow(Handle, Left, Top, Width, Height, True);
// サウンドを再生 ふろくだから無視してください
PlaySound('Minimize', 0, Snd_Alias or Snd_NoDefault or Snd_ASync);
// 表示状態を設定
ShowWindow(Handle, SW_MINIMIZE);
Title := SaveTitle;
end
end;
inherited WndProc(Msg);
end;
//アップリケーションプロシージャに置き換えられた偽のWndProc
procedure TForm1.AppWndProc(var Msg: TMessage);
begin
with Msg do // ここでも横取りする
begin
if (Msg = WM_ERASEBKGND) then
Result := 1
else if (Msg = WM_SYSCOMMAND) and (WParam = SC_RESTORE) then
with Application do
begin // 戻す
PlaySound('RestoreUp', 0, Snd_Alias or Snd_NoDefault or Snd_ASync);
// トップ レベルでアクティブ ウィンドウ化
SetActiveWindow(Handle);
ShowWindow(Handle, SW_RESTORE);
// わり算を大きくするとスム−ス
MoveWindow(Handle, Screen.Width div 4, Screen.Width div 4, 0, 0, False);
Result := 1;
end
else if (Msg = WM_SYSCOMMAND) and (WParam = SC_MINIMIZE) then
begin // 最小化
PlaySound('Minimize', 0, Snd_Alias or Snd_NoDefault or Snd_ASync);
// フォーム
Perform(WM_SYSCOMMAND, SC_MINIMIZE, 0);
end
else
Result := CallWindowProc(OldAppWndProc, Application.Handle, Msg, WParam, LParam);
end;
end;
これをヒントに自分でコンポーネント化するのは簡単でしょう。
もう一つ、フォームへのメッセージを横取りするWndProcをコンポーネントで書くということです。
MLで話題になっていたので、ひとこと。ボタンやメニューで最小化する時は
procedure TForm1.N1Click(Sender: TObject);
begin
Application.minimize; // これはダメ アニメーションしない(当然)
end;
procedure TForm1.N2Click(Sender: TObject);
begin
Perform(WM_SYSCOMMAND, SC_MINIMIZE, 0); // これでうまくいく
end;
逆のRestoreの場合は、
Application.minimize;
Perform(WM_SYSCOMMAND, SC_RESTORE, 0);
でも似たようなものになります。
なぜか? → MLを再読されたし。
参照: [Delphi-ML:939] [Delphi-ML:34432] <Windows> <フォーム>
0216 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/25 おばQ rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/09/01 おばQ 編集
RichEditでHTMLタグを色・書式付き表示をする
HTMLソースを表示する時タグだけ色分けや書式付きで表示されると便利です。
以下のソースを入力して、RichEditにHTMLソースを表示させて
ボタンを押すと、なかなか高速にタグだけに色がつきます。
テキストを検索してSelAttributesで色付けしてもいいのですが、
あまりにも遅いのでほとんどAPIを使います。
タグを入力してリアルタイムに色をつけることは少々ムズカシそうだったので
実装していません。こちらと組み合わせてみるのもよいでしょう。
そちらの方法は、自分で考えてね(^^;;;
//関数部
procedure RichEditHTMLTagPickUp(RichEdit: TRichEdit; TagColor: TColor);
function ColorToStringHex(Value: TColor): string;
begin
Result := '$00' + IntToHex(ColorToRGB(Value),6)
end;
var
mask: Longint;
CFmt: TCharFormat;
Str: String;
PTop, PPos, PEnd, PNext: PChar;
CR: TCharRange;
begin
try
RichEdit.Lines.BeginUpdate;//高速化、1
mask := SendMessage(RichEdit.Handle, EM_GETEVENTMASK, 0, 0);//高速化、2
SendMessage(RichEdit.Handle, EM_SETEVENTMASK, 0, 0);
with RichEdit do
begin
SelStart := length(Text);//高速化、3
Perform(EM_SCROLLCARET, 0, 0);
case 1 of
0:
begin
CFmt.cbSize := sizeof(CFmt);
CFmt.dwMask := CFM_BOLD;
CFmt.dwEffects := CFE_BOLD;
end;
1:
begin
CFmt.cbSize := sizeof(CFmt);
CFmt.dwMask := CFM_COLOR;
CFmt.crTextColor := ColorToRGB(TagColor);
end;
end;
Str := RichEdit.Text;
PTop := PChar(Str);
PPos := PTop;
while (AnsiStrScan(PPos, '<') <> nil) do//高速化、4
begin
PPos := AnsiStrScan(PPos, '<');
PEnd := PPos;
while (AnsiStrScan(PEnd +1 , '>') <> nil) do
begin
PNext := AnsiStrScan(PEnd +1 , '>');
PEnd := PNext +1;
while (PEnd = PChar(#13)) or (PEnd = PChar(#10)) do
Inc(PEnd);
if PEnd <> PChar('<') then Break;
end;
CR.cpMin := PPos - PTop;
CR.cpMax := PEnd - PTop;
RichEdit.Perform(EM_EXSETSEL, 0, lParam(@CR));
RichEdit.Perform(EM_SETCHARFORMAT, 1, lParam(@CFmt));//書式決定
PPos := PEnd;
end;
end;
finally
SendMessage(RichEdit.Handle, EM_SETEVENTMASK, 0, mask);//高速化、2終
RichEdit.Lines.EndUpdate;//高速化、1終
end;
end;
//実装部
procedure TForm1.Button2Click(Sender: TObject);
begin
RichEditHTMLTagPickUp(RichEdit1, Form1.Color);
end;
注意:タグが綺麗に閉じられていないと無限ループに
はまってしまう事が考えられます。気をつけてください
内部でcase文に1を設定している所があります。
ここを0にしますとHTMLタグがBold属性になります。
1の場合ですとForm1.Colorになります。
適当に書きなおしてください。
RichEditのUndoバッファは影響を受けますので注意です。
元々はC++BuilderMLでのC++Builder用のTipsでしたが、
Delphiに書き換えました。
MLのC++BuilderでのサンプルソースはMLを参照してください。
Delphi版とほぼ同じ内容です。
参照: [builder:8463] <WWW> <通信> <Win95> <コンポーネント >
0231 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/31 おばQ rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/31 おばQ 編集
LinesプロパティエディタでTabを入力する
TMemoやTRichEditでは設計時にLinesプロパティを
設定することが出来ます。
文字列リストの設定ダイアログ(という名前か?)
というLinesのプロパティエディタが表示されて編集します。
常識ですね。
さて、ここでTabキーを押してもTabが入力出来ません!
さあ、困った困ったどうしよう。
そういう時にはCtrl+Tabキーを押します。
参照: <Win95> <Standard> <コンポーネント >
0230 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/31 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/31 osamu 編集
全角文字が半角文字の2倍幅になるフォントの選び方
等幅フォントを使っても、全角文字と半角文字を混ぜて使うと、桁の位置がずれて見づらくなってしまう場合があります。これは、特定のフォントサイズ以外では、全角文字が半角文字の2倍の幅にならないことに起因しています。
ピクセル単位とポイント単位の変換は以下のようになっています。
Point = 72 * ( Pixels / GetDeviceCaps(hDC, LOGPIXELS_) )
Pixels = Point / 72 * GetDeviceCaps(hDC, LOGPIXELS_)
ここで、LOGPIXELS_ は LOGPIXELSX または LOGPIXELSY ですが、幅を問題にする場合には、LOGPIXELSX を使います。
Pixels が整数になるような Point 数の等幅フォントを使えば、全角文字がちょうど半角文字二つ分の幅になり、エディタのように使った場合に、「ずれ」が生じません。
たとえば、画面の設定が 96dpi の時、第2式は
Pixels = 4 * Point / 3
となりますので、適するサイズは、3の倍数ポイントのフォントということになります。
参照: <描画>
0215 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/25 花井@自宅 rev 1.5 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/31 花井@自宅 編集
RichEdit の全パラグラフのタブ幅を設定する
RichEdit では、各パラグラフごとにタブ位置を設定できます。
[DelphiML:41963]にて、全パラグラフに一定間隔のタブ位置を
設定する関数を紹介しています。等幅フォント専用です。
(補足)
Tab の幅は書式なので書式付きテキストの OLE D&D と
ペースト処理時にはTabの幅が変化すると予想されます。
ペースト時の書式消しは可能ですが OLE D&D を防ぐ事は
まず無理ですので、そのあたりはご容赦ください。
参照: [Delphi-ML:41963] <Win95> <コンポーネント >
0211 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/21 おばQ rev 1.7 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/31 花井@自宅 編集
TRichEdit.Paragraph.Tab[] の設定(Tab位置の設定)
<書式>
RichEdit1.Paragraph.Tab[iIndex] := ptsAbsolute
<値>
iIndex: Byte
タブインデックス。段落の何番目のタブ位置を設定するかを示す。
範囲は 0 から MAX_TAB_STOPS - 1 まで。
(定数 MAX_TAB_STOPS は RichEdit.pasで定義)
ptsAbsolute: LongInt
タブ位置。コントロール水平端からの絶対位置で単位は論理ポイント。
(1論理ポイントは 1/72論理インチ)
<解説>
・現在選択範囲のパラグラフのタブ位置設定を行います。範囲選択がされて
いないときは、キャレット(文字カーソル)のあるパラグラフになります。
・ptsAbsoluteにスクリーンのピクセル値を元にした値を設定
したい場合には、次のように変換します。
(論理ポイント) =
72 * ( ピクセル値 / 水平方向1論理インチ当りのピクセル数 )
<備考>
・Delphi のバージョンによっては、バグのために Tab[] プロパティの設定
が行われないパターンが発生します。詳細は[Delphi-ML:41966] をご覧下さい。
(Delphi 4.0 Update 3 には存在しています。)
<サンプル>
テキスト選択範囲内にあるパラグラフの最初のタブ位置をコントロール
中央に設定します
// 四捨五入します
function RoundOff(X: Extended): LongInt;
begin
if X >= 0 then
Result := Trunc(0.5 + X)
else
Result := Trunc(-0.5 + X);
end;
// テキスト選択範囲内にあるパラグラフの最初のタブ位置を
// コントロール中央に設定します
procedure TForm1.RichEdit1SetFirstTabAtCenter;
var
DC: HDC;
PixelsPerInch: Integer;
CenterByPixels: Currency;
CenterByPoints: Currency;
begin
// 水平方向1論理インチ当りのピクセル数を取得します。
DC := GetDC(0);
PixelsPerInch := GetDeviceCaps(DC, LOGPIXELSX);
ReleaseDC(0, DC);
// RichEdit1 の中央を得ます(ピクセル値)
CenterByPixels := RichEdit1.ClientWidth / 2;
// スクリーンのピクセル値から論理ポイント値に変換します
CenterByPoints := 72 * (CenterByPixels / PixelsPerInch);
// タブ位置を設定します。
RichEdit1.Paragraph.Tab[1] := 0; // バグ回避のため
RichEdit1.Paragraph.Tab[0] := RoundOff(CenterByPoints);
end;
// RichEdit1SetFirstTabAtCenter を呼び出します。
procedure TForm1.Button1Click(Sender: TObject);
begin
RichEdit1SetFirstTabAtCenter;
end;
参照: [Delphi-ML:41923] [Delphi-ML:41966] <Win95> <コンポーネント >
0227 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/29 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/30 osamu 編集
メインフォーム以外のフォームを最小化した時もAPP全体を最小化したい
複数のフォームを持つAPPで、メインフォームでないフォームのMiniMizeボタンを押すとこのフォームは左下にタイトルバー形式にアイコン化され、メインフォームはそのまま残っています。これはこれで意味があるのですが、どのフォームを最小化してもAPP全体を最小化したい時があるものです。
これは、WM_SYSCOMMANDメッセージをとらえて、Appicationを最小化すればできます。
// Form2は、メインフォームでないフォームです。
type
TForm2 = class(TForm)
// コントロールいろいろ
private
procedure WMSysCommand(var Message: TWMSysCommand);message WM_SYSCOMMAND;
public
{ Public 宣言 }
end;
var
Form2: TForm2;
implementation
{$R *.DFM}
procedure TForm2.WMSysCommand(var Message: TWMSysCommand);
begin
if (Message.CmdType and $FFF0 = SC_MINIMIZE) then
Application.Minimize
else
inherited;
end;
最小化された状態の判定は、
IsIconic(Application.Handle); で行います
「元のサイズに戻す」のは、
Application.Restore; ですね。
TCustomFormのWM_SYSCOMMANDメッセージ処理は
if (Message.CmdType and $FFF0 = SC_MINIMIZE) and
(Application.MainForm = Self) then
Application.Minimize
else
inherited;
のようになっていることからもわかるように、本来メインフォームとApplicationフォームとは別のものです。
この違いからWindowStyleプロパティの独特の仕様が生まれているのでしょうか?
左下にアイコン化するときは、WindowStyleプロパティは変化しないようです。フォームのWindowStyleプロパティは、メインフォームでないときは注意が必要です。
参照: [Delphi-ML:25286] [Delphi-ML:40312] <Windows> <フォーム>
0229 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/30 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/30 osamu 編集
Pascal 版の yacc & lex
Turbo PASCAL 版の yacc と lex のセットです。
http://www.idiom.com/free-compilers/LANG/Lex-1.html
http://www.filelibrary.com/Contents/DOS/77/17.html
のあたりに、lyprg.zip とか、 lex_yacc.zip とかいう名前で、
転がっています。
数式評価ルーチンくらいならこれで簡単に書けますね。
どっかに Object Pascal のパーサ転がってませんかね?
参照: <開発環境> <PASCAL>
0222 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/28 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/28 西坂良幸 編集
データコントロールのDataLinkオブジェクトを取得する方法(ReadOnlyプロパティのバグの解決法)
VCLでは、DataControlのFDataLinkは、プロパティになっていないので、アクセスできませんが、
DataControlにCM_GETDATALINKメッセージを投げる事で取得(参照)できます。[Delphi-ML:41387]田原
多重継承ができないので、こうなっているそうです。
これによって、MLでも多い質問ですが、いわゆる「極東仕様」問題、すなわち、TDBEdit の ReadOnly と継承元の TCustomEdit のReadOnly の同期がとれないバグを解決することができます。[Delphi-ML:41051]藤中
以下は、藤中さんの例示コード(省略部あり)です。
// このデータコントロール(フィールド対応)は編集可能か?
function IsCanModifyFieldDataControl(Control: TControl) : Boolean;
var
DataLink : TFieldDataLink;
begin
// データリンクを取得
DataLink := TFieldDataLink(Control.Perform(CM_GETDATALINK,0,0));
if (DataLink = nil) then
DatabaseError('CM_GETDATALINK は無効', Control);
// データリンクが編集可能かどうか調べる
Result := DataLink.CanModify and
( DataLink.DataSource.AutoEdit or
(DataLink.DataSource.State in [dsInsert, dsEdit]) );
end;
// たとえば、Table1のAfterScrollイベントなどで動的にReadOnlyを変更する
type TCustomEditCracker = class(TCustomEdit);
procedure TForm1.Table1AfterScroll(DataSet: TDataSet);
var
Flg :Boolean;
begin
// 条件
if DataSet.FieldByName('制御フラグ').AsString = '1' then
Flg := True
else
Flg := False;
// 以下でReadOnlyを変更
DBEdit1.ReadOnly := Flg;
if (Flg = False) then
begin
TCustomEditCracker(DBEdit1).ReadOnly :=
not IsCanModifyFieldDataControl(DBEdit1);
end
else begin
TCustomEditCracker(DBEdit1).ReadOnly := True;
end;
end;
参照: [Delphi-ML:41387] [Delphi-ML:17611] [Delphi-ML:41051] <データベース> <バグ> <コンポーネント > <DataControls>
0206 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/18 西坂良幸 rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/26 西坂良幸 編集
ダイヤラを呼び出す。(TAPI32.DLLの使用)
TAPI32.DLLのtapiRequestMakeCall関数を呼び出します。
function tapiRequestMakeCall(pDestAddress,pAppName,pCalledParty,pComment: PChar): LongInt; stdcall; external 'TAPI32.DLL';
procedure CallDialar(Number, DName , Comment : string);
begin
tapiRequestMakeCall(pChar(Number),
PChar(Application.Title), pChar(DName), PChar(Comment));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
CallDialar('06-5556-1234', '山田太郎', '');
end;
で、ダイアラが起動します。
本格的にTAPIを使う場合は、Tapi32.dll用の定義ユニットが必要です。
なお、TAPIの定義ユニットは、
外国製ですが、
d_tapi.zip (28K)のTapi.pas
tapid3.zip (28K)のTapih.pasなどがあります。
これらは、桜島子宮さんの ページ
http://www.kobira.co.jp/sakura/d_net_RasAPI.htm
からダウンロードできます。
参照: [Delphi-ML:6833] [Delphi-ML:17807] [Delphi-ML:41356] <ShellApi> <Windows>
0182 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/26 西坂良幸 編集
他のアプリの起動パスを取得する
> (ウィンドウハンドルの場合)
> GetWindowLong(hwnd, GWL_HINSTANCE) でモジュールハンドルを取り出す。
> (モジュールハンドルの場合)
> GetModuleFileName(hModule, strBuffer, nBufferSize) でファイル名を得る。
残念ですがこの方法では、他のアプリの起動パス は取得できません。自分自身のExe名しか取得できません。(私も最初はこれではまった。)(^.^)
モジュールハンドルはプロセス毎に独立して管理されるので、他のアプリ(他のプロセス)のモジュールハンドルを持ってきても意味がありません。
http://www.microsoft.com/japan/support/kb/articles/J041/6/32.htm
にシステム内で稼働中のプロセスを列挙する方法がでてますから、参考にしてください。ウィンドウハンドルが判っているのでしたら、
GetWindowthreadProcessID でプロセスIDを求めて、列挙中にこれと合致するプロセスを探せばよいでしょう。
たとえば、Win95/98用(WinNTではダメ)ですが、
uses TLHelp32;
// ハンドルからファイル名を得る
function GetProcesFileNameFrom(Handle: hWnd):string;
var
PID: DWORD;
SnapShot: THandle;
ProcessEntry32: TProcessEntry32;
begin
// ハンドルから作成スレッドを調べてプロセスIDを得る
GetWindowThreadProcessId(Handle, @PID);
// TProcessEntry32構造体の初期化
ProcessEntry32.dwSize := SizeOf(TProcessEntry32);
// システム中の情報のスナップショットをとる
SnapShot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
try
// 最初のプロセスの検索
if Process32First(SnapShot, ProcessEntry32) then
begin
repeat
// IDが一致したら
if ProcessEntry32.th32ProcessID = PID then
begin
Result := string(ProcessEntry32.szExeFile);
break;
end;
// 次のプロセスの検索
until Process32Next(SnapShot, ProcessEntry32) = False;
end;
finally
CloseHandle(SnapShot);
end;
end;
などです。
参照: [Delphi-ML:30032] <アプリケーション> <Windows> <ファイル>
0219 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/26 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/26 西坂良幸 編集
セルのテキストをドラッグイメ−ジにしてグリッド(TStringGrid)でドラッグ&ドロップを行う
やり方は2つあって、Drag Object をすりかえる方法と
コントロール側で DoStartDrag, DoEndDrag, GetDragImages 等を
override する方法があります。[d40216]中村
以下は、[d40948]で須賀さんが書いたコードを若干省略したものです。
Drag&Dropのやり方として大変参考になりますね。
type
TForm1 = class(TForm)
//<省略>
・・・・・
public
CursorSave:TCursor;
end;
type
TCustomDragObject = class(TDragControlObject)
public
function GetDragImages: TDragImageList; override;
procedure HideDragImage; override;
procedure ShowDragImage; override;
function GetDragCursor(Accepted: Boolean; X, Y: Integer)
: TCursor; override;
procedure Finished(Target: TObject; X, Y: Integer;
Accepted: Boolean); override;
end;
//<省略>
・・・・・
var // ローカルに宣言
w_x,w_y:integer; // もとのcellの位置を取っておく
Images: TImageList; // Image作成用
drag_sizex,drag_sizey:integer; // 作成したimageのサイズ
drag_enter_sw:smallint; // drag start時かどうかのsw
function TCustomDragObject.GetDragImages: TDragImageList;
begin
Result := Images;
end;
procedure TCustomDragObject.HideDragImage;
begin
GetDragImages.HideDragImage;
end;
procedure TCustomDragObject.ShowDragImage;
begin
GetDragImages.ShowDragImage;
end;
procedure TCustomDragObject.Finished;
begin
inherited;
Free;
end;
function TCustomDragObject.GetDragCursor;
begin
Result := crDeFault;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
drag_enter_sw := 0; // drag start時かどうかのSwをクリア
Grid1.ControlStyle := ControlStyle + [csDisplayDragImage];
// 適当にセルに文字列を・・・
Grid1.cells[1,1]:='AAA';
end;
procedure TForm1.Grid1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
var
acol,arow:integer;
begin
if source is TCustomDragObject then
begin
Accept := True;
Grid1.mousetocell(x, y, acol, arow);
if (acol<0) or (arow<0) then //
begin
Grid1.DragCursor := crNoDrop;
exit;
end
else begin
Grid1.DragCursor := crDrag;
end;
if State=dsDragMove then
begin
if drag_enter_sw = 0 then // start drag時以外のとき
begin
if (y-drag_sizey) <= Grid1.rowheights[1] then // 上にscroll
begin
if Grid1.toprow > 1 then
begin
Grid1.toprow := Grid1.toprow - 1 ;
Grid1.repaint;
end;
end;
if (y >= (Grid1.height - 5)) then // 下にscroll
begin
if (Grid1.rowcount - Grid1.visiblerowcount)>Grid1.toprow then
begin
Grid1.toprow := Grid1.toprow + 1;
Grid1.repaint;
end;
end;
end
else
drag_enter_sw := 0;
end;
end;
end;
procedure TForm1.Grid1StartDrag(Sender: TObject;
var DragObject: TDragObject);
var Size: TSize;
bm: TBitmap;
acol,arow:integer;
begin
drag_enter_sw := 1;
// drag start時にswをたてる
Grid1.mousetocell(Grid1.screentoclient(mouse.CursorPos).x,Grid1.screentoclient(mouse.CursorPos).y, acol, arow);
w_x := acol; // drag開始時のcellの位置を取っておく
w_y := arow; // drag開始時のcellの位置を取っておく
// textのimageを作成
bm := TBitmap.Create;
bm.Canvas.Font := Font;
Size := bm.Canvas.TextExtent(Grid1.cells[acol, arow]);
bm.Width := Size.cx;
bm.Height := Size.cy;
drag_sizex := Size.cx;
drag_sizey := Size.cy;
bm.Canvas.TextOut(0, 0, Grid1.cells[acol, arow]);
Images := TImageList.Create(Self);
Images.Width := Size.cx;
Images.Height := Size.cy;
Images.Add(bm, Nil);
bm.Free;
Images.SetDragImage(0, Size.cx, Size.cy);
Images.EndDrag;
// カーソル処理
CursorSave := Screen.Cursor;
Screen.Cursor := crDefault;
// DragImageを作成
DragObject := TCustomDragObject.Create(Grid1); // dragobjectの差し替え
end;
procedure TForm1.Grid1EndDrag(Sender, Target: TObject; X, Y: Integer);
begin
Screen.Cursor := CursorSave;
Grid1.repaint;
images.free; // drag終了時にTImageListの開放 これを忘れていました !!
end;
procedure TForm1.Grid1DragDrop(Sender, Source: TObject; X, Y: Integer);
var
acol,arow:integer;
begin
Grid1.mousetocell(x, y, acol, arow);
if (acol < 0) or (arow < 0) then // scroll barや最後の行より下のところにdropしたときは何もしない
begin
Grid1.DragCursor := crNoDrop;
exit;
end;
if (Sender = Grid1) then // drag開始時のcellの内容をdropしたcellにcopy
begin
Grid1.mousetocell(x, y, acol, arow);
Grid1.cells[acol, arow] := Grid1.cells[w_x, w_y];
Grid1.repaint;
end;
end;
なお、落とし穴があります。
IDEの中では、ドラッグカーソルとドラッグイメージの合成処理の過程で(TCustomImageList.CombineDragCursor)
Win95/98 が落ちることが前は良く起きました。Video Driver の問題らしいのですが、いまだに原因が良く判らないそうです。
EXEレベルではこの障害はでません。
参照: [Delphi-ML:22670] [Delphi-ML:40662] [Delphi-ML:40977] <Windows> <コンポーネント > <その他コンポーネント関連>
0220 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/26 西坂良幸 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/26 西坂良幸 編集
ツリービューのノードをドラッグ&ドロップで移動させる
TTreeViewでは、DragImageにこだわらなければ,DragModeプロパティを dmAutomatic に設定すれば、OnDragOver、OnDragDropイベントを使うだけで簡単にできます。
OnDragOverイベントのAcceptは,ユーザーがドロップするオブジェクトをそのコントロールが受け入れることを示すだけです。
ツリー構造ですので、移動が無限ルーチンに陥らないように工夫します。
下記では、
IsFamilyメソッドで、ファミリー内にその上位ノードを移動するのか
を判定し、MoveNodeメソッドですべての子ノードが移動できるように
しています。
type
TForm1 = class(TForm)
// <省略>
・・・・・
・・・・・
private
procedure MoveNode(Parent, Source: TTreeNode);
function IsFamily(Parent, Source: TTreeNode):boolean;
public
end;
// <省略>
・・・・・
・・・・・
procedure TForm1.FormCreate(Sender: TObject);
var
i:integer;
Node:TTreeNode;
begin
// 適当にtreeを作成
for i := 1 to 20 do
begin
node := TreeView1.Items.Add(nil,'A00'+IntToStr(i));
node.ImageIndex := 0;
node.SelectedIndex := 1;
end;
end;
// 移動と削除−−すべての子ノードを移動する
procedure TForm1.MoveNode(Parent, Source: TTreeNode);
var
node: TTreeNode;
begin
node := TreeView1.Items.AddChild(Parent,Source.Text);
node.ImageIndex := Source.ImageIndex;
node.SelectedIndex := Source.SelectedIndex;
while Source.Count > 0 do
// 再帰させる
MoveNode(Node, TTreeNode(Source.Item[0]));
Source.Delete;
end;
// Sourceの一族にParentがあれば true の判定
function TForm1.IsFamily(Parent, Source: TTreeNode):boolean;
var
i:integer;
node: TTreeNode;
begin
result := false;
for i := 0 to Source.Count - 1 do
begin
node := Source.Item[i];
if Node.AbsoluteIndex = Parent.AbsoluteIndex then
result := true
else
// 再帰させる
result := IsFamily(Parent, Node);
if result then break;
end;
end;
// ドロップ後の処理
procedure TForm1.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer);
Var
Node:TTreeNode;
begin
if (Sender is TTreeView) and (Source is TTreeView) then
begin //ツリーからツリー
with Sender, Source as TTreeView do
begin
//新親の親
Node := GetNodeAt(X,Y);
if Node = Selected then
begin
MessageDlg('同じ所に移動はできません。', mtInformation, [mbOk], 0);
Exit;
end;
//ツリーのアイテムを移動し削除
Items.BeginUpdate;
if IsFamily(Node, Selected) then
//無限循環を避ける
MessageDlg('この移動はできません。', mtInformation, [mbOk], 0)
else
MoveNode(Node, Selected);
Items.EndUpdate;
end;
end;
end;
// ソースを確認する
procedure TForm1.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer;
State: TDragState; var Accept: Boolean);
begin
Accept := (Source as TTreeView) = TreeView1;
end;
参照: [Delphi-ML:3727] [Delphi-ML:22691] [Delphi-ML:30411] <Win95> <コンポーネント >
0217 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/26 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/26 西坂良幸 編集
書式付きメモ型項目へアクセスしたい
ヘルプなどでは、Streamをつかった方法が紹介されています。
DataSet.FieldByName('Field1').AsString := RichEdit1.Text;
や
DataSet.FieldByName('Field1').AsVariant := RichEdit1.Text;
では、PlainText となり、書式が保存されません。
Field1 書式付きメモ型とし、
DataSetは開かれているとします。
// リッチエディットからフィールドへの書き込み
procedure TForm1.Button8Click(Sender: TObject);
var
Stream1 : TMemoryStream;
begin
Stream1 := TMemoryStream.Create;
try
RichEdit1.Lines.SavetoStream(Stream1);
Query1.Edit;
TBlobField(Query1.FieldByName('Field1')).LoadFromStream(Stream1);
Query1.Post;
finally
Stream1.Free;
end;
end;
// フィールドからリッチエヂットへの読み出し
procedure TForm1.Button4Click(Sender: TObject);
var
Stream1 : TBlobStream;
begin
Stream1 := TBlobStream.Create(TBlobField(Query1.FieldByName('Field1')), bmRead);
try
RichEdit1.Lines.LoadFromStream(Stream1);
finally
Stream1.Free;
end;
end;
TRichEdit系は、いくつかのバグが報告されています。
データが大きくなるとヤバイという噂です。
D4では、大部改善されたようですが、気をつけて下さい。
バグ情報は
http://www.dataweb.nl/~r.p.sterkenburg/indexpag.htm
参照: [Delphi-ML:5484] [Delphi-ML:41858] <データベース> <DataAccess> <コンポーネント >
0214 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/23 西坂良幸 rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/23 西坂良幸 編集
デスクトップのウィンドウのタイトルとハンドルを取得する
稼働中のウィンドウのタイトルとハンドルを取得するには、
APIのENumWindows関数を使います。
第一パラメータのコールバック関数は、タイトルとハンドル
をどのように処理するかを工夫します。
次の例はListBoxに列挙し、Labelにその数を示します。
function CallBackTest(WH: HWND; LP: LParam): BOOL stdcall;
var
Buf: array [0..255] of Char;
begin
// スタイル ビットがセットされているもののみ
if IsWindowVisible(hwnd) then
// タイトル バーのテキストをBufにコピー
if GetWindowText(WH, Buf, 255) <> 0 then
TListBox(LP).Items.AddObject(Buff, Pointer(hwnd));
Result := True;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Clear;
EnumWindows(@CallBackTest, Longint(ListBox1));
Label1.Caption := IntToStr(ListBox1.Items.Count);
end;
参照: [Delphi-ML:2378] [Delphi-ML:23203] <Windows> <デスクトップ>
0213 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/23 西坂良幸 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/23 西坂良幸 編集
プロセスの実行ファイル名を列挙する
Tipsの別項、[Win95/98で、ウィンドゥハンドルから、実行アプリ名を知る]および、[WinNTで、ウィンドゥハンドルから、実行アプリ名を知る]の例にある
EnumProcesses95、EnumProcessesNTを使えば
汎用関数のようなものができます。
同名の関数がありますので、OverrLoad指定を忘れないで下さい。
procedure EnumProcesses(lpEnumFunc: TEnumProcs; lParam: longint);overload
begin
case Win32Platform of
VER_PLATFORM_WIN32_NT: EnumProcessesNT(lpEnumFunc, lParam);
VER_PLATFORM_WIN32_WINDOWS: EnumProcesses95(lpEnumFunc, lParam);
else
raise Exception.Create('未対応のOSかバージョンです。');
end;
end;
// 以下の例は、リストボックスに列挙しています。
function CallBackTest(ID: DWord; Str: PChar; LP: LParam):bool;stdcall;
begin
TListBox(LP).Items.Addobject(Str,Pointer(ID));
Result := True;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Clear;
EnumProcesses(@CallBackTest,Longint(ListBox1));
end;
参照: [Delphi-ML:30064] <その他Windows関連> <Windows>
0212 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/23 西坂良幸 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/23 西坂良幸 編集
Win95/98で、ウィンドゥハンドルから、実行アプリ名を知る
Win95 では、ToolHelp32 を使って取得できます。WinNT ではこの方法では使えません。
http://www.microsoft.com/japan/support/kb/articles/J041/6/32.htm
に、Win95/NT 両方で使える方法が C 言語で書かれてます。
ここでは、上記の部分的丸写しですが、ToolHelp32を使った例を示します。
コールバック関数を適切に作れば、プロセスIDとファイル名が列挙できますので、ウィンドゥハンドルからID(ProcessID)を
GetWindowThreadProcessID(WH: hWnd; @ID); 関数
で取得し、一致するものを探せば実行アプリ名を取得できます。
uses TLHelp32;
type
TEnumProcs = function(ID: DWord; Str: PChar; LP: LParam):bool; stdcall;
procedure EnumProcesses95(lpEnumFunc: TEnumProcs; lParam: longint);
var
// 関数ポインタ
CreateToolhelp32Snapshot: TCreateToolhelp32Snapshot;
Process32First: TProcess32First;
Process32Next: TProcess32Next;
// 変数
hLib: THandle;
Snapshot: THandle;
ProcEntry: TProcessEntry32;
Flag: boolean;
begin
// Kernel32.DLL のロード
hLib := LoadLibraryA('Kernel32.dll');
if hLib = 0 then
raise Exception.Create('Kernel32.dll がロードできません。');
// API のアドレス取得(使用するのは以下の3個)
CreateToolhelp32Snapshot := TCreateToolhelp32Snapshot(
GetProcAddress(hLib, 'CreateToolhelp32Snapshot'));
Process32First := TProcess32First(GetProcAddress(hLib, 'Process32First'));
Process32Next := TProcess32Next(GetProcAddress(hLib, 'Process32Next'));
// ロードをチェック
if (not Assigned(CreateToolhelp32Snapshot))
or (not Assigned(Process32First))
or (not Assigned(Process32Next)) then
begin
FreeLibrary(hLib);
raise Exception.Create('API のアドレスを取得できません。');
end;
// スナップショットを行う
Snapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if Snapshot = INVALID_HANDLE_VALUE then
begin
FreeLibrary(hLib);
raise Exception.Create('Toolhelp32 スナップショットが取得できません。');
end;
// TProcessEntry32構造体の初期化
ProcEntry.dwSize := SizeOf(TProcessEntry32);
// 最初のプロセスの取得
Flag := Process32First(Snapshot, ProcEntry);
while Flag do
begin
// コールバック関数呼び出し
if not lpEnumFunc(ProcEntry.th32ProcessID, ProcEntry.szExeFile, lParam) then
break;
// 次のプロセスの取得
Flag := Process32Next(Snapshot, ProcEntry);
end;
// 解放
CloseHandle(Snapshot);
FreeLibrary(hLib);
end;
// コールバック関数の例
function CallBackTest(ID: DWord; Str: PChar; LP: LParam): bool; stdcall;
begin
TListBox(LP).Items.Addobject(Str,Pointer(ID));
Result := True;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Clear;
EnumProcesses95(@CallBackTest,Longint(ListBox1));
end;
参照: [Delphi-ML:8519] [Delphi-ML:19427] <Windows>
0051 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.4 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/23 西坂良幸 編集
WinNT でウィンドウハンドルから実行アプリ名を知る
WinNT では、ToolHelp32 が使えないので、Win95 で良く知られた方法(ToolHelp32)は使えません。
http://www.microsoft.com/japan/support/kb/articles/J041/6/32.htm
に、Win95/NT 両方で使える方法が C 言語で書かれてます。
ここでは、PSAPIを使っています。
上記の部分的丸写しですが。
uses PsApi;
type
TEnumProcs = function(ID: DWord; Str: PChar; LP: LParam):bool; stdcall;
procedure EnumProcessesNT(lpEnumFunc: TEnumProcs; lParam: longint);
var
// 関数ポインタ
EnumProcesses: TEnumProcesses;
EnumProcessModules: TEnumProcessModules;
GetModuleFileNameEx: TGetModuleFileNameEx;
// 変数
hLib: THandle;
Process: THandle;
hMod: DWORD;
PIDs: array[0..2047] of DWORD; // 動的配列ポインタの扱いを簡素化(^^;
Size, Size2, Index: DWORD;
ProcessName: array[0..MAX_PATH] of char;
begin
hMod := 0;
// PSAPI.DLL のロード
hLib := LoadLibraryA('PSAPI.DLL');
if hLib = 0 then
raise Exception.Create('PSAPI.DLL がロードできません。');
// API のアドレス取得(以下の3個を使う)
EnumProcesses := TEnumProcesses(GetProcAddress(hLib, 'EnumProcesses'));
EnumProcessModules := TEnumProcessModules(GetProcAddress(hLib, 'EnumProcessModules'));
GetModuleFileNameEx := TGetModuleFileNameEx(GetProcAddress(hLib, 'GetModuleFileNameExA'));
// ロードのチェック
if (not Assigned(EnumProcesses))
or(not Assigned(EnumProcessModules))
or(not Assigned(GetModuleFileNameEx)) then
begin
FreeLibrary(hLib);
raise Exception.Create('API のアドレスを取得できません。');
end;
// プロセスの数は2048まで
Size2 := 2048 * SizeOf(DWORD);
if not EnumProcesses(@PIDs, Size2, Size) then
begin
FreeLibrary(hLib);
raise Exception.Create(SysErrorMessage(GetLastError));
end;
if Size >= Size2 then
begin
FreeLibrary(hLib);
raise Exception.Create('2048 個以上のプロセスがあるみたいです。');
end;
Size := Size div SizeOf(DWORD);
for Index := 0 to Size - 1 do
begin
// プロセスを読み出す
Process := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,
False, PIDs[Index]);
if Process <> 0 then
begin
// プロセスのモジュールをチェック
if EnumProcessModules(Process, PDWORD(hMod), SizeOf(hMod), Size2) then
begin
// ファイル名を取得
if GetModuleFileNameEx(Process, hMod, ProcessName, SizeOf(ProcessName)) = 0 then
begin
ProcessName:='';
end;
end;
CloseHandle(Process);
end
else
ProcessName:='';
// コールバック関数を呼び出し
if not lpEnumFunc(PIDs[Index], ProcessName, lParam) then
else Break;
end;
// 解放
FreeLibrary(hLib);
end;
// 使用例
function CallBackTest(ID: DWord; Str: PChar; LP: LParam):bool;stdcall;
begin
TListBox(LP).Items.Addobject(Str,Pointer(ID));
Result := True;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Clear;
EnumProcessesNT(@CallBackTest,Longint(ListBox1));
// リストボックスに列挙する
end;
参照: [Delphi-ML:19427] <Windows>
0210 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/21 おばQ rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/21 おばQ 編集
クリップボードのフォーマットを知る
クリップボードのフォーマットは
ヘルプを見ると幾つか定義されています。
プロフェッショナル版以上のDelphiに付属する
VCLのソースをみると
windows.pasに定義されていることが分ります。
CF_TEXTは単に1という値で定義されています
こちらの定義を用いれば現在のクリップボードのフォーマットが分ります。
定義されていないクリップボードフォーマットもありますが
その場合には単に数値として取得してみます。
procedure ClipFormat;
function ShowMessageCF(Value: Integer): String;
case Value of
CF_TEXT:
Result := 'CF_TEXT';
CF_BITMAP:
Result := 'CF_BITMAP';
CF_METAFILEPICT:
Result := 'CF_METAFILEPICT';
CF_SYLK:
Result := 'CF_SYLK';
CF_DIF:
Result := 'CF_DIF';
CF_TIFF:
Result := 'CF_TIFF';
CF_OEMTEXT:
Result := 'CF_OEMTEXT';
CF_DIB:
Result := 'CF_DIB';
CF_PALETTE:
Result := 'CF_PALETTE';
CF_PENDATA:
Result := 'CF_PENDATA';
CF_RIFF:
Result := 'CF_RIFF';
CF_WAVE:
Result := 'CF_WAVE';
CF_UNICODETEXT:
Result := 'CF_UNICODETEXT';
CF_ENHMETAFILE:
Result := 'CF_ENHMETAFILE';
CF_HDROP:
Result := 'CF_HDROP';
CF_LOCALE:
Result := 'CF_LOCALE';
CF_MAX:
Result := 'CF_MAX';
CF_OWNERDISPLAY:
Result := 'CF_OWNERDISPLAY';
CF_DSPTEXT:
Result := 'CF_DSPTEXT';
CF_DSPBITMAP:
Result := 'CF_DSPBITMAP';
CF_DSPMETAFILEPICT:
Result := 'CF_DSPMETAFILEPICT';
CF_DSPENHMETAFILE:
Result := 'CF_DSPENHMETAFILE';
CF_PRIVATEFIRST:
Result := 'CF_PRIVATEFIRST';
CF_PRIVATELAST:
Result := 'CF_PRIVATELAST';
CF_GDIOBJFIRST:
Result := 'CF_GDIOBJFIRST';
CF_GDIOBJLAST:
Result := 'CF_GDIOBJLAST';
else
Result := IntToStr(Value);
end;//case
ShowMessage(Result);
end;
var
i: Integer;
begin
for i := 0 to 1100 do
if Clipboard.HasFormat(i) then
ShowMessageCF(i);
end;
1100までループさせているのは最後のCF_GDIOBJLASTが
1023で定義されていたからです。
FormにButton1を貼り付けて以下のように実装してみてください。
procedure TForm1.Button1Click(Sender: TObject);
begin
ClipFormat;
end;
ボタンを押すと現在のクリップボードデータのフォーマットがShowMessageで表示されます
参照: <その他Windows関連> <Windows>
0209 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/21 おばQ rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/21 おばQ 編集
クリップボードが更新された時のイベントを取得する
クリップボードが更新された時、いろいろしたいことがあると思います。
少々ややこしいですが以下のようにすれば
クリップボードの更新を監視する事が出来ます。
そうするとクリップボード履歴機能を持つソフトが作れますね。
Formのメンバーに以下のように定義します
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
FClipNextHandle: HWND;
procedure WMDrawClipboard(var Msg: TWMDrawClipboard);
message WM_DRAWCLIPBOARD;
procedure WMChangeCBChain(var Msg: TWMChangeCBChain);
message WM_CHANGECBCHAIN;
public
end;
↑適当に省略しています
implimention部に以下のように実装します。
procedure TForm1.WMDrawClipboard(var Msg: TWMDrawClipboard);
//クリップボード更新フック
//更新された後に流れてくる。
//アプリが立ち上がった瞬間も流れる
begin
inherited;
//------------------------------
{ここにテキストバックアップ処理等を行えば。
クリップボード履歴機能が実装できます。}
ShowMessage('クリップボード更新されたよ');
//------------------------------
if FClipNextHandle<>0 then
SendMessage(FClipNextHandle,WM_DRAWCLIPBOARD, 0, 0);
end;
procedure TForm1.WMChangeCBChain(var Msg: TWMChangeCBChain);
begin
if Msg.Remove = FClipNextHandle then
FClipNextHandle := Msg.Next;
if FClipNextHandle <> 0 then
SendMessage(FClipNextHandle, WM_CHANGECBCHAIN, Msg.Remove,Msg.Next);
end;
procedure TForm1.FormCreate(Sender: TObject);
procedure ClipboardSetHandle;//クリップボード履歴の為に必要な初期化
begin
FClipNextHandle := SetClipboardViewer(Form1.Handle);
if FClipNextHandle = 0 then
if GetLastError <> 0 then {本当にエラーだったら}
ShowMessage('なぜかクリップボードのフックに失敗しました(T_T)');
end;
begin
ClipboardSetHandle;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
ChangeClipboardChain(Handle, FClipNextHandle);
//クリップボード監視処理の破棄
end;
参照: [Delphi-ML:5296] <その他Windows関連> <Windows>
0208 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/19 西坂良幸 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/19 西坂良幸 編集
インターネットエクスプローラのアドレス帳を呼び出す
アドレス帳(IE4以上)の起動は、使用しているアドレスデータとともに呼び出すことが重要です。
このデータは、レジストリの
HKEY_CURRENT_USER\Software\Microsoft\WAB\WAB4\Wab File Name
に示される 拡張子 wab のファイルにあり、
これを「ファイルの関連づけ」機能(IEをインストールすれば自動設定される)をつかって呼び出すのが簡単です。
procedure TForm1.Button1Click(Sender: TObject);
var
RegIniFile: TRegIniFile;
Wab:String;
Comm: array[0..128] of Char;
Ret:Integer;
begin
Wab := '';
Ret := 0;
// レジストリからwabファイルを探す。
RegIniFile := TRegIniFile.Create('Software\Microsoft\WAB\WAB4');
try
Wab := RegIniFile.ReadString('Wab File Name', '', '');
finally
RegIniFile.Free;
end;
if Wab <> '' then
if FileExists(Wab) then
begin
StrPCopy(Comm, Wab);
Ret := Shellexecute(Handle, 'Open', Comm, '', '', SW_SHOWNORMAL);
end;
if Ret < 32 then
ShowMessage('呼び出しに失敗しました。');
end;
参照: [Delphi-ML:39621] <その他Windows関連> <Windows>
0205 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/17 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/17 osamu 編集
PASCAL ソースの整形ツール
Tips Lounge で花井さんが紹介してくれました。Delphi Super Page
http://sunsite.icm.edu.pl/delphi/
にある delforex.zip というファイルです。D2-D4 にインストールして使えるエキスパートで、他人の書いた汚い(?)ソースをワンキーで自分好みに変換することが出来ます。便利々々。
参照: <PASCAL>
0089 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/14 osamu 編集
Delphi2/3.x における Cardinal 型の妙な定義
>Delphi3.1のWindows.pasの中で、
> DWORD = Integer; と定義されていますが、
> DWORD = Cardinal; ではないのでしょうか。
>
>また、ヘルプ及びマニュアルに
> Cardinal 0〜2147483647 符号なし 32 ビットと書かれていますが、
> 0〜4294967295 ではないのでしょうか.。
残念ながらマニュアルの通りです。
また Cardinal 型の変数には マイナス値や 2147483647 を超える定数値を代入することは出来ませんが、「オプション」の「範囲チェックエラー」をオフにしておくと 計算の結果負数を持つことは出来ます。
例えば
var i: Cardinal;
:
:
i := 10;
i := i - 20;
if i >= 0 then ShowMessage('Plus')
else ShowMessage('Minus');
は Minus と表示されます。つまり Integer とほとんど変わりません。
[Delphi-ML:23120]によれば、Delphi4 ではちゃんと Cardinal=(0..2^32-1) になっているようです。
参照: [Delphi-ML:20567] [Delphi-ML:23120] <バグ> <PASCAL>
0035 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/14 osamu 編集
MS-Word 97 で作成した rtf ファイルがヘルプコンパイラでエラーを起こす
Delphi付属のヘルプコンパイラではMS-Word97で作成したrtfファイルをコンパイルできません。
http://support.microsoft.com/support/downloads/dp2677.asp
から、Word97対応のヘルプコンパイラがダウンロードできます。
参照: [Delphi-ML:6924] [Delphi-ML:16043] [Delphi-ML:18838] <バグ> <ヘルプ>
0012 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/14 osamu 編集
カスタムコントロールの子コントロールをオブジェクトインスペクタに表示させない
オブジェクトインスペクタには、OwnerがFormであるコントロールだけが表示されるので、カスタムコントロールの内部で作成するコンポーネントならば、OwnerにSelfを与えればよいです。
サンプルコンポーネントのTSpinEditなどを参照しましょう。
参照: [Delphi-ML:5112] <コンポーネント開発>
0004 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/14 osamu 編集
ショートカットキーのキー名を独自に設定する
例えば、テンキーの + キーをショートカットにした場合に、'Num +'とか表示させたい。
TMenuItem.AppendTo()を参照すると、
TestMenu1.Caption:=TestMenu1.Caption+#9+'Num +'+#0;
とすればよいことが分かる。
キャプションとショートカット名との間にはさんだタブ文字と、自動で追加されるショートカットを隠すために最後にくっつけられた #0 とが味噌。
参照: [Delphi-ML:17989] <コンポーネント > <Standard> <メニュー>
0180 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/10 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/14 osamu 編集
Object Pascal のコーディングスタイル
> 最近 Delphi でプログラムを書いているのですが、インデントの仕方や
> begin, end をどこに書いたらいいのか迷っています。
以下の書籍にコーディングスタンダードが記載されています。購入していないので何章かは忘れました。
「Delphi 4 プログラミング技法 Vol.1 Win32プログラミングテクニック 」
ザビエル・パチェコ+スティーブ・テイクセラ(日向 俊二 訳)
ボーランドの開発者も使用しているらしく、VCLやサンプルプログラムでは、大半がこのガイドにしたがっています。
また、以下のサイト(著者のサイト)に同様の内容があります。(但し英語)
Delphi 4 Developer's Guide
http://www.xapware.com/ddg/
また、[Delphi-Doc:00906]で中村@NECさんが、Borland の Delphi チームが実践しているコーディングスタイルを紹介したページを報告してくださいました。
http://www.borland.com/techvoyage/articles/DelphiStyle/StyleGuide.html
参照: [Delphi-ML:38485] <PASCAL>
0203 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/08/13 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/08/13 osamu 編集
TColor 値を文字列に変換する
レジストリに TColor 型の値を格納したいような場合、ColorToString 関数を使えば TColor から AnsiString に変換できるので、あとは TRegistry の WriteString で書き出してやればいいと思います。
反対に AnsiString から TColor に戻すのは StringToColor 関数を使えばいいです。
詳しくはヘルプを見てください。
参照: [builder:17839] <文字列> <PASCAL> <描画>
0095 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/07/28 osamu 編集
HTML-Help を作りたい
HTML-Help に関する情報ソースです。
Microsoftから
HTML Help Web site
http://msdn.microsoft.com/workshop/author/htmlhelp/default.asp
latest version Microsoft HTML Help Workshop
http://msdn.microsoft.com/workshop/author/htmlhelp/download.asp
国内では
http://www.takenet.or.jp/~ryuuji/delphi/
またDelphi用のUNITが、
http://www.mygale.org/~jlucm/
からダウンロードできます。
参照: [Delphi-ML:20899] <Windows> <ヘルプ>
0200 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/07/14 久保田 宏光 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/07/14 久保田 宏光 編集
基本的な階層プロパティ定義の例
// 子にするクラスの宣言
// ・TPersistentから継承する
// ・プロパティ内に、さらに他のクラスを定義する場合はAssignでコピーする。
// 直接代入を許さないように留意する(SetListメソッドを参照)
// メソッドをprotectedにする場合はvirtualにする。
// また、隠蔽したい場合はprivateに置く。
// ※protected/privateが良く分からない場合はprotected/virtualが無難
// ・クラスにAssignを実装する必要がある。
type
TChild = class(TPersistent)
private
FList:TStrings;
public
constructor Create;
destructor Destroy; override;
procedure Assign(Source:TPersistent); override ;
protected
procedure SetList(Value:TStrings); virtual;
published
property List :TStrings read FList write SetList;
end;
//親側のクラス宣言
//・こちらでも、Childプロパティの書き込みにSetChildを実装する。
type
TParent = class(TComponent)
Private
FChild:TChild;
public
constructor Create(AOwner:TComponent); override;
destructor Destroy; override;
protected
procedure SetChild(Value:TChild); override;
published
property Child :TChild read FChild write SetChild;
end;
//実現部(子)
//・抽象クラスで宣言したプロパティは、こちらで実装クラスとして
// create/freeする。
//・プロパティのwriteはメソッドになっている。間違っても
// List := TStringList.Create
// ~~~~ などとしないように。
//・コンポーネントがTStrings内でObjectsを使用している場合のみ、
// free時に解放する。
//
// 例 )
// for i:=0 to FList.count - 1 to
// FList.Objects[i].Free;
// FList.Free;
constructor TChild.Create;
begin
inherited Create;
FList := TStringList.Create;
end;
destructor TChild.Destroy;
begin
FList.Free;
inherited Destroy;
end;
procedure TChildSetList(Value:TStrings);
begin
if Value <> FList then
FList.Assign(Value);
end;
Procedure TChild.Assign(Source:TPersistent);
begin
if Source is TChild then
FList.Assign(TChild(Source).List);
inherited Assign(Source);
end;
//実現部(親)
//・FChildをcreate/freeするのを忘れないように。
constructor THyperText.Create(AOwner:TComponent);
begin
inherited Create(AOWner);
FChild := TChild.Create;
end;
destructor THyperText.Destroy;
begin
FChild.Free;
inherited Destroy;
end;
参照: <コンポーネント開発> <PASCAL>
0197 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/07/09 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/07/09 osamu 編集
@ 演算子の働き
Hidetoshi NAKAI さんは書きました:
> すごく初歩的な質問だと想うのですが、変数の前に
> "@" (アットマーク) を付加すると、どういう効果が
> あるのでしょうか?
初歩的でもないですよ。結構知らない人がいます。
手続き(メソッド)型でない変数の場合:
その変数を指すポインタを返します。ポインタの型は
{$T-} の状態では Pointer 型に、{$T+} の状態では
^[変数の型] になります。
手続き型(メソッド型)の変数の場合:
変数が手続きの呼び出しと解釈されることを抑止し、
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
手続き(メソッド)へのポインタが入っている変数として
扱うことを強制します。従って変数のポインタを 取得
したい場合は @@A という具合に @ を2個書きます。
手続き名、関数名、メソッド名の場合:
手続き、関数、メソッドもエントリポイントを指す
ポインタ(メソッドポインタ)が返ります。
詳しくは @ 演算子のヘルプを見てください。
参照: [Delphi-ML:32704] <PASCAL>
0195 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/07/07 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/07/07 osamu 編集
CapsLock の状態を変更する
> ある入力コントロールにフォーカスが移った時に、CapsLockを
> プログラムにて On/Off することは出来ないでしょうか?
keybd_event で CAPS キーを押したことにしてあげれば良いです。この方法は、いろいろ応用がききそうです。
// CapsLock を Off にする場合
procedure TForm1.Edit1Enter(Sender: TObject);
begin
if GetKeyState(VK_CAPITAL) and 1 = 1 then begin
keybd_event(VK_CAPITAL, 0, 0, 0);
keybd_event(VK_CAPITAL, 0, KEYEVENTF_KEYUP, 0);
end;
end;
// CapsLock を On にする場合
procedure TForm1.Edit2Enter(Sender: TObject);
begin
if GetKeyState(VK_CAPITAL) and 1 <> 1 then begin
keybd_event(VK_CAPITAL, 0, 0, 0);
keybd_event(VK_CAPITAL, 0, KEYEVENTF_KEYUP, 0);
end;
end;
参照: [Delphi-ML:33786] <Windows>
0193 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/06/08 おばQ rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/06/08 おばQ 編集
LhasaのようなUIを持つアプリケーションの作成方法
Lhasaという有名な圧縮解凍ソフトをご存知だと思います。
そのUIはファイルをexeにDrag&Dropした時は解凍を行い、
普通に起動の場合、Formが開きます。
つまり最小化起動して処理をして終了するアプリケーションです。
その基本的なUIを実現します。
普通にFormを作り以下のコードを書きます。
procedure TForm1.FormShow(Sender: TObject);
var
i:Integer;
begin
if ParamCount=0 then
//Drag&Dropされていない場合はParamCount=0
begin
Application.Title := 'サンプルプログラム';
//普通に起動します。
end else
begin
Application.Title := '処理中';
Application.Minimize;//最小化起動
for i:=1 to ParamCount do
begin
{
ここでDrag&Dropされたファイルに対しての処理を行う
ParamStr(i)でファイル名がパス名付きで取得できるので
読込み、処理を行い、書きこむと良いです。
}
end;
Application.Terminate;
//最小化起動したアプリを終了させる
end;
end;
参照: <アプリケーション>
0192 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/28 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/28 osamu 編集
TFileListBox にファイル名が重複して表示されてしまう
TFileListBox.Mask に、'*.txt;*.?x?' などと入力した場合、もし複数の Mask に合致するファイル(例えば、example.txt)があると、そのファイルはダブって表示されてしまいます。これを回避する単純な方法は、TDirectoryListBox.OnChange にて、重複しているファイル名を削除してやることです。
procedure Form1.DirectoryListBox1Change(Sender: TObject);
var i: Integer;
begin
for i:=FileListBox1.Items.Count-2 downto 0 do
if FileListBox1.Items[i]=FileListBox1.Items[i+1] then
FileListBox1.Items.Delete(i+1);
end;
この位のことは、TFileListBox 自身で面倒を見てもらいたいものですが。。。
参照: <System> <コンポーネント >
0191 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/19 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/19 osamu 編集
コンボボックスのリスト部分の幅を指定する
>コンボボックスをクリックして表示されるリストの幅を
>コンボボックスコントロール自体の幅よりも広くして
>表示することはできないでしょうか?
ComboBox1->Perform(CB_SETDROPPEDWIDTH, 幅, 0);
です。詳細は Win32.hlp(English) を参照してください。
参照: [builder:12689] <Standard> <コンポーネント >
0136 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/19 osamu 編集
独自メッセージとして自由に使える値の範囲
MSDN (99/01 版)をみた感じでは、
$0000 - $03FF (WM_USER-1) システム用予約領域
WM_USER ($0400)- $7FFF (WM_APP -1) WindowClass 用
WM_APP ($8000)- $BFFF アプリケーション用
$C000 - $FFFF RegisterWindowMessage 用
$10000 - last 今後のために予約
で、自由に使用できるのは、WM_USER から $BFFF であると読み取れます。ただし、WM_USER 〜 $7FFF はアプリケーションの制御ではなく、その部品になっているウィンドウコントロールのために予約しておくほうがよいので、コード中から独自メッセージを PostMessage するような場合には、WM_APP〜$FFFF までを使うとよいようです。
参照: [Delphi-ML:24086] <その他Windows関連> <Windows>
0189 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/19 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/19 osamu 編集
「送る」の 516 文字制限について
> エクスプローラー等の右クリックの「送る」に機能を
> 追加するソフトを開発しています。SendToフォルダに
> ショートカットを置くことで、上の機能を実現してい
> るのですが、コマンドラインからの起動(?)の516文字
> 制限に困っています。
おそらく、「送る」に頼らず自力でシェルエクステンションを登録するという解決策が一番実現しやすいかなと思っています。
言い換えれば、右クリックしたとき独自のメニューを表示させ、あとはそのメニューに関連付けられたdllがファイル名を処理するというわけです。
参照: [Delphi-ML:37241] <Windows>
0188 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/19 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/19 osamu 編集
TRichEdit の内容を高速に変更する
> RichEdit の Lines の内容を、次の様に変更しているんですが、
> 3000行で10秒もかかってしまいます。
>
> var sl: TStringList;
>
> for i:=1 to 3000 do
> sl.Add(IntToStr(i));
> RichEdit1.Lines.Assign(sl);
ストリームを経由すると速いですよ。
var ms: TMemoryStream;
ms := TMemoryStream.Create;
try
sl.SaveToStream(ms);
ms.Position := 0;
RichEdit1.Lines.LoadFromStream(ms);
finally
ms.Free;
end;
3000行で一秒以下でした。(Pentium 150MHz)
参照: [builder:16379] <Win95> <コンポーネント >
0186 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/14 おばQ rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/14 おばQ 編集
文字数のカウント方法
普通にLength関数で文字数をカウントすると半角文字の数になります。例えばLength('あいうえおABCDE')は15です。
全角文字を1文字としてカウントするにはWideStringを使います。Length(WideString('あいうえおABCDE'))は10です。
WideStringにキャストするより高速な手法としてByteToCharLenを使う方法もあります。
ByteToCharLen('あいうえおABCDE', Length('あいうえおABCDE'))は10です。
参照: <文字列> <PASCAL>
0187 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/14 おばQ rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/14 おばQ 編集
システムカラーを得る、設定する
ひきさんのページ『日本Delphi振興会』にも載っていますが
更に情報を加えておきます。
WindowsAPIでWindowsのシステムカラーを得たり設定したりする事が出来ます。それぞれGetSysColor、SetSysColorというAPI関数です。
サンプルとしてButton1を押すと色の設定情報を読込み
Button2を押すと読込んだ情報を設定するというものを作りました。
Button1を押して
その後にコントロールパネルでWindowsの色設定を変更しても
Button2を押す事で変更前の色設定になります。
const
SysColorMaxNumber=28;
type
TForm1 = class(TForm)
…省略…
private
FList: array[0..SysColorMaxNumber] of Integer;
FColorList: array[0..SysColorMaxNumber] of TColor;
FRGBList: array[0..SysColorMaxNumber] of Longint;
end;
…省略…
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
for i:=0 to SysColorMaxNumber do
begin
FList[i] := i;
FColorList[i] := TColor(GetSysColor(i));
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
i: Integer;
begin
for i:=0 to SysColorMaxNumber do
FRGBList[i] := ColorToRGB(FColorList[i]);
SetSysColors(SysColorMaxNumber,FList,FRGBList);
end;
GetSysColor(COLOR_ACTIVECAPTION)という定数も指定出来ます。この定数はwindows.pasに定義されています。
Delphiで定義されている色のclBtnFaceやclActiveCaptionとAPIでのGetSysColor(COLOR_ACTIVECAPTION)との違いは以下のようなコードで理解できるでしょう。
ShowMessage(IntToHex((clRed),8));
ShowMessage(IntToHex((clBtnFace),8));
ShowMessage(IntToHex(GetSysColor(COLOR_BTNFACE),8));
ShowMessage(IntToHex(ColorToRGB(clRed),8));
ShowMessage(IntToHex(ColorToRGB(clBtnFace),8));
ShowMessage(IntToHex(ColorToRGB(GetSysColor(COLOR_BTNFACE)),8));
参照: <描画>
0185 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/12 花井@自宅 rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/12 花井@自宅 編集
コードエディタで「キーボードマクロ」を使う
[Ctrl] + [Shift] + R で、動作の記録 On/Off
[Ctrl] + [Shift] + P で、記録した動作の再生
参照: [Delphi-ML:24437] <開発環境>
0076 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.3 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/12 花井@自宅 編集
コードエディタで選択部分をまとめてインデントする
デフォルトのキーマップなら、次のショートカットが使えます。
[Ctrl] + [K] + [I] インデント
[Ctrl] + [K] + [U] アンインデント
もしくは
[Ctrl] + [Shift] + [I] インデント
[Ctrl] + [Shift] + [U] アンインデント
ヘルプの「キーボードショートカット」を見ると、他にも便利な機能を発見することができるかもしれません。
参照: <開発環境>
0075 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/12 花井@自宅 編集
コードエディタで矩形領域を選択する
(キーボード)
デフォルトキーマップでしたら、
[Alt] + [Shift] + 矢印キー
で、できます。ただ、この後は普通に選択しても矩形になってしまいます。
元の選択モードに戻すのは、
[Ctrl] + [O] + [K]
です。ちなみに、矩形選択モードへは
[Ctrl] + [O] + [C]
で行けます。
(マウス)
Altキーを押しながら、エディタ領域をマウスでドラッグすればよいです。
こちらはキーボードと違って一時的に矩形選択モードになるだけなので便利です。
参照: [Delphi-ML:24425] [Delphi-ML:24478] <開発環境>
0181 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/08 osamu 編集
改行コードの違いについて
テキストを扱っていると、何かと改行コードについて気になります。
改行コードは Windows では CRLF、Macでは CR のみ、Unix 系では LF のみで示されます。制御コードとして CR が復帰 LF が改行と呼ばれます。Delphiでは CR=Char(13) または #13、LF=Char(10) または #10 として指定できます。
Label 等ではテキストの改行を行う場合、CRLF, CRのみ, LFのみ、どの場合でも改行が行えますので
Label.caption := '1行目'#10'2行目'という表現も可能です。
また、改行コードが、CR のみ、LF のみ、LFCR になっているテキストを CRLF シーケンス(つまりWindows標準形式)に変換する関数としてAdjustLineBreaks というものがあります。逆に CRLF から、CR のみLF のみ、に変換するには
function DeleteCR(S: string): string;
begin
while pos(#13,S)<>0 do
Delete(S,pos(#13,S),1);
Result := S;
end;
このようなものを使ってください。DeleteLFも作れるでしょう
(D4以降ではStringReplace関数が用意されていますのでそちらを使いましょう)
参照: [Delphi-ML:9035] <文字列> <PASCAL>
0179 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/05/01 おばQ rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/05/01 おばQ 編集
右の項目の幅が固定されるTStatusBar
Delphiで普通にTStatusBarを使用してスターテスバーを作り
複数のTStatusPanelを設定すると
Formをリサイズする時、右の項目の幅が変化します。
IEエクスプローラやワードパッドなどでは
WindowをResizeした時に
StatusBarの項目の右の方のものが幅が固定されていて
左の項目の幅が変化するものがあります。
推定ですが、VC++アプリケーションウィザードを普通に
用いるとこのようなものが自動で生成されるようです。
これをDelphiで実現します。
StatusBarのResizeイベントに以下のようなコードを書きます。
procedure TForm1.StatusBarResize(Sender: TObject);
const
ResizePanelNumber=0; //リサイズするStatusbarのパネル番号を指定
MinSize=0; //リサイズされても維持したい最小のWidthを指定
var
BarWidth,i: Integer;
begin
with StatusBar do
begin
BarWidth := 0;
for i:=0 to Panels.Count-1 do
begin
if not(i=ResizePanelNumber) then
BarWidth := BarWidth + Panels[i].Width;
end;
if (Width-BarWidth)<=MinSize then
Panels[ResizePanelNumber].Width := MinSize
else
Panels[ResizePanelNumber].Width := Width - BarWidth;
end;
end;
これで項目のうちResizePanelNumberで指定したパネルのWidthが
StatusBarのResize、つまりFormのリサイズに合わせてリサイズします。
また、
すべての項目において右の項目の幅が優先されて維持される
IEブラウザのような動作をさせるには工夫が必要です。
設計時のPanelのWidthを保持して
Panels.Widthを変化させます。
TForm1 = class(TForm)
…
private
FBarWidths: array of Integer;
end;
procedure TForm1.FormCreate(Sender: TObject);
procedure SetStatusBarWidth;
var //右項目固定StatusBarの為に設計時のpanelのWidthを保存
i: Integer;
begin
with StatusBar do
begin
SetLength(FBarWidths,Panels.Count);
for i:=0 to Panels.Count-1 do
FBarWidths[i]:= Panels[i].Width;
end;
end;
begin
SetStatusBarWidth;
end;
procedure TForm1.StatusBarResize(Sender: TObject);
var
BarWidth,i,PointPanel,k: Integer;
begin
BarWidth:=0;
with StatusBar do
begin
PointPanel := Panels.Count;
repeat
dec(PointPanel); //何番のパネルをリサイズするべきか決定する
BarWidth := BarWidth + FBarWidths[PointPanel];
until (Width < BarWidth)or(PointPanel=0) ;
for i:=0 to PointPanel-1 do //消えるべきパネルをWidth:=0にする
Panels[i].Width := 0;
BarWidth := 0;
for i:=PointPanel+1 to Panels.Count-1 do
begin
Panels[i].Width := FBarWidths[i];
BarWidth := BarWidth + FBarWidths[i];
end;
Panels[PointPanel].Width := StatusBar.Width - BarWidth;
end;
end;
分かりにくい説明ですが、プログラムを実行すると分かります。
VCL化してみるのも面白いでしょう。
参照: <Win95> <コンポーネント >
0178 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/04/29 おばQ rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/04/29 おばQ 編集
TCoolBarを用いたツールバーの位置保存
クールバーでツールバーを作った場合、
その位置を起動時に前回の状態に戻す為の処理です。
この場合Delphi4で採用されているドッカブルツールバーには
対応していません。もう少し、工夫が必要になるでしょう。
今回はIniファイルにに保存すると言う前提で解説します。
クールバー上のツールバーの数をBandCountとして
const
BandCount=5;
後はFormのクリエイトイベントとクローズイベントに
以下の様なコードを書きます。
procedure TForm1.FormCreate(Sender: TObject);
procedure BandPositionRead;
var
ini: TIniFile;
BandIDNumber,BandWidth: array[0..BandCount-1] of Integer;
BandBreak: array[0..BandCount-1] of Boolean;
i: Integer;
begin
ini := TIniFile.Create(
ExtractFilePath(Application.ExeName)+'BandPos.ini');
with ini do
try
for i:=0 to BandCount-1 do
begin //Bandの位置とBreakを読込む
BandIDNumber[i]:=
ReadInteger('Bar','Band'+IntToStr(i)+'ID',i);
BandWidth[i] :=
ReadInteger('Bar','Band'+IntToStr(i)+'Width',200);
BandBreak[i] :=
ReadBool('Bar','Band'+IntToStr(i)+'Break',true);
end;
finally
Free;
end;
CoolBar1.Bands.BeginUpdate;
for i:=0 to BandCount-1 do
begin //Bandの位置とBreakを復元します。
CoolBar1.Bands.FindItemID(BandIDNumber[i]).Index := i;
CoolBar1.Bands[i].Break := BandBreak[i];
CoolBar1.Bands[i].Width := BandWidth[i];
end;
CoolBar1.Bands.EndUpdate;
end;
begin
BandPositionRead;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
procedure BandPositionWrite;
var
ini: TIniFile;
i: Integer;
begin
ini := TIniFile.Create(
ExtractFilePath(Application.ExeName)+'BandPos.ini');
with ini do
try
for i:=0 to BandCount-1 do
begin //Bandの位置とBreakを書きこむ
WriteInteger('Bar','Band'+IntToStr(i)
+'ID',CoolBar1.Bands[i].ID);
WriteInteger('Bar','Band'+IntToStr(i)
+'Width',CoolBar1.Bands[i].Width);
WriteBool('Bar','Band'+IntToStr(i)
+'Break',CoolBar1.Bands[i].Break);
end;
finally
Free;
end;
end;
begin
BandPositionWrite;
end;
参照: [Delphi-ML:32427] <Win95> <コンポーネント >
0090 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.2 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/03/21 osamu 編集
アプリケーション間でデータの送受信(WM_COPYDATAを使う)
BCB1で記述してます。3用に手直しがあるかもしれません。
送り側アプリ
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
HANDLE handle;
WPARAM wParam;
LPARAM lParam;
char msgbuf[]="hogehoge";
COPYDATASTRUCT cds;
cds.dwData = 1;
cds.cbData = strlen(msgbuf);
cds.lpData = (LPVOID)msgbuf;
handle = ::FindWindow(NULL,"受けアプリ");
wParam = (WPARAM)Application->Handle;
lParam = (LPARAM)&cds;
::SendMessage(handle,WM_COPYDATA,wParam,lParam);
}
//---------------------------------------------------------------------------
受け側アプリのヘッダ
public: // ユーザー宣言
void __fastcall EvWmCOPYDATA( TMessage message );
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_COPYDATA,TMessage, EvWmCOPYDATA)
END_MESSAGE_MAP(TForm)
浮け側アプリのソース
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Form1->Caption = "受けアプリ";
}
//---------------------------------------------------------------------------
void __fastcall TForm1::EvWmCOPYDATA( TMessage message )
{
COPYDATASTRUCT *cds;
char buf[80];
cds = (COPYDATASTRUCT *)message.LParam;
::ZeroMemory(buf,sizeof(buf));
memmove(buf,cds->lpData,cds->cbData);
::MessageBox(0,buf,"Oh!",MB_OK);
}
参照: [builder:5252] <Windows>
0169 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
BCB で実行ファイルのサイズを小さくするには
> 後、妙にファイルサイズの大きい dll が出来てしまうのが困り
> ものかも知れません (^^;; 。
VCLを使っていない場合、VCLをリンクしないようにすればファイルサイズが小さくなります。
・BCB3でVCLをリンクしないDLLの作り方
プロジェクトメイクファイルのALLOBJ行からsysinit.objを削除し、ALLLIB行のcp32mt.libをcw32mt.libに書き換える。
マルチスレッドを使っていないなら、cw32mt.libではなく、cw32.libをリンクすると、もう少し小さくなります。
VCLをリンクしない場合、空のDLLで50kバイト程度になります。
# まだ大きいですけど...
参照: [builder:6917] <C++>
0168 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
IME 入力で読み仮名を取得する
以下のコードを含んだ関数を作成し、追跡したいところで
Application->OnMessage = AppMessage;
と書けばよいのではないでしょうか? はずしていたらすみません。
void __fastcall TForm1::AppMessage(TMessage &Msg, bool &Handled)
{
if (Msg.message == WM_IME_ENDCOMPOSITION)
{
int nRetVal; //APIの戻り値を格納
HIMC hImcIMEHandle; //IMEのコンテキストを格納
char cBuff[256];
hImcIMEHandle = ImmGetContext(Handle);
//変換結果の「読み」を取得
nRetVal = ImmGetCompositionString(hImcIMEHandle,
GCS_RESULTREADSTR,
cBuff, sizeof(cBuff));
cBuff[nRetVal] = '\0';
AnsiString strTemp = cBuff;
//IMEのコンテキストを開放する
nRetVal = ImmReleaseContext(Handle, hImcIMEHandle);
//cBuffに対して処理をする(入力された文字列)
}
}
でcBuffに入力された文字が入ってきていると思います。
このコードを実行するにはimm.hが必要です。
参照: [builder:6815] <その他Windows関連> <Windows> <コンポーネント > <Standard>
0167 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
デバッグ中に関数評価の戻り値をチェックしたい
> VB では ?関数名(引数) とか打ってエンターすると値が表示されます。
> でもこればかりはインタプリタ言語の特権ですね。
Delphi でも CTRL+F7 で 関数名(引数) とやって結果が表示されます。
・・・プログラムが一時停止中でないとできませんが。
参照: [Delphi-ML:32133] <開発環境> <デバッグ>
0166 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
フォームの破棄、生成を続けて行うときの注意事項
> プログラム中でフォームを破棄し、直後に生成するとイベントが妙な順で
> 発生してしまい困っています。
>
> Form1.Release;
> :
> Form1 := TForm1.Create( Application );
> :
> Form1.Show;
> 処理A
>
> と、こんな感じで記述すると、Form1 の Create イベントや処理 A の後に、
> Form1のDestroy イベントが走っているようなのです。
Release の後で Application.ProcessMessages してみるとどうですか。Release のヘルプを見てみてください。
Form1 がアクセスされていないことが分かっているのなら、Free でも良いです(場合によって推奨されない方法です)。
参照: [Delphi-ML:31893] <フォーム>
0165 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
DBGrid のスクロールバーを非表示に
この DBGrid のスクロールバーを力で消す方法,大変参考になりました。じっと見ているとちらりと映りますが,言われなければたぶん誰も気づかないでしょう(^^)
> private
> { Private 宣言 }
> FCellBottom : Integer;
>
> procedure TForm1.DBGrid1DrawColumnCell
> (Sender: TObject; const Rect: TRect;
> DataCol: Integer; Column: TColumn; State: TGridDrawState);
> begin
> if (FCellBottom < Rect.bottom) then FCellBottom := Rect.bottom;
> end;
>
> procedure TForm1.Query1BeforeInsert(DataSet: TDataSet);
> begin
> Abort;
> end;
>
> procedure TForm1.Query1AfterScroll(DataSet: TDataSet);
> begin
> ShowScrollBar(DBGrid1.Handle, SB_VERT, False);
> end;
>
> procedure TForm1.Button1Click(Sender: TObject);
> begin
> DBGrid1.Height := FCellBottom - DBGrid1.Top + 6;
> end;
参照: [Delphi-ML:31864] <データベース> <コンポーネント > <DataControls>
0164 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
プログラムからプリンタのスプーラ表示を開く
Delphiで『スタート』→『設定』→『プリンタ』にある複数のプリンタの中から,指定された特定の名前のプリンタ(たとえばEPSON LP-8000S)などを開いてスプーラの表示にしたいのですがどうすればいいでしょうか。
参照: [Delphi-ML:31750] <印刷>
0163 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
カレントユーザのデスクトップディレクトリを得る
>実行中に現在の(ユーザの)「デスクトップ」ディレクトリのパスを取得したい
>のですが、どうすれば良いのでしょうか?
こんなの有ります。
uses ShellAPI, SHlObj, ComObj, ActiveX;
:
:
function GetDesktopFolder: string;
function GetDispName(shi: IShellFolder; pidl: PItemIDList): string;
var DispName: TStrRet;
begin
shi.GetDisplayNameOf(pidl, SHGDN_FORPARSING, DispName);
if DispName.uType = STRRET_CSTR then
Result := DispName.cStr
else if DispName.uType = STRRET_OFFSET then
Result := PCHAR(LongInt(pidl) + DispName.uOffset)
else
Result := WideCharToString(DispName.pOleStr);
end;
var Pidl: PItemIDList;
DesktopFolderI: IShellFolder;
begin
OleCheck(SHGetSpecialFolderLocation(0, CSIDL_DESKTOPDIRECTORY, Pidl));
try
OleCheck(SHGetDesktopFolder(DesktopFolderI));
Result := GetDispName(DesktopFolderI, Pidl);
finally
CoTaskMemFree(Pidl);
end;
end;
参照: [Delphi-ML:31606] <Windows> <ファイル>
0162 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
IniFile に書き込みを行った後にはバッファのクリアが必要
Delphi4 になって追加されたメソッドに、TIniFile.UpdateFile というのがあります。
TIniFile.UpdateFile メソッドは,バッファリングされた INI ファイルのデータをディスクにフラッシュします。
function UpdateFile; override;
説明
UpdateFile メソッドを呼び出すと,バッファリングされた,INI ファイルからの読み取りや INI ファイルへの書き込みをディスクにフラッシュできます。UpdateFile は,Windows95 では便利ですが,Windows NT では INI ファイルの読み取りや書き込みをバッファリングしないので、効果がありません。
Delphi3 以前では自分で
WritePrivateProfileString(nil, nil, nil, PChar(FileName));
などとして IniFile のキャッシュを書き込む必要があります。
> Ini := TIniFile.Create(Filename);
> with Ini do begin
> WriteString(aaaa, bbbb, cccc);
> WriteString(dddd, eeee, ffff);
> Free;
> // 以下の処理で、キャッシュをフラッシュ
> WritePrivateProfileString(nil, nil, nil, PChar(Filename));
> end;
参照: [Delphi-ML:31512] <アプリケーション> <Windows> <バグ>
0161 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
TFileStream で標準出力に表示
> テキストファイルの入出力は TStringList でやると便利です。
> そこで、コンソールアプリケーションの標準出力でも使いたいなと、
> SaveToFile('con') を実行したところ例外で失敗しました。
> VCLソースを追いかけてみたところ、TFileStream.Create() で例外が起きていました。
>
> ところが、SaveToFile('nul') を実行したところ、なにも出力されないとはいえ
> 例外が発生することなく実行できました。
>
> 簡単に使えないのはしかたないのですが、この違いはなんでしょうね?
これ、'con'の代わりに'conout$'だと行けるみたいですね。
ところで、ちょっと横道にそれますけど、'con'だと仮にopen出来ても
リダイレクトした場合にまでコンソールに表示されて場合によっては
不便じゃありませんか?
その場合は以下のようにした方が良いかと。
uses
windows, classes, sysutils;
var
s : TStringList;
hs : THandleStream;
begin
s := TStringList.Create;
try
hs := THandleStream.Create(GetStdHandle(STD_OUTPUT_HANDLE));
try
s.SetText('一行目'#10'二行目'#10'三行目'#10);
s.SaveToStream(hs);
finally
hs.free;
end;
finally
s.free;
end;
end.
参照: [Delphi-ML:31323] <Windows> <ファイル>
0160 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
半角カナを確定無しで直接入力させる
> IMEの制御についてお聞きしたいのですが、例えばTEditのIMEMode
> プロパティを半角カナに設定した時、「アイウエオ(半角)」と打
> ちこんだ後リターンキーで確定をしなければなりませんが、ここで
> 入力される文字(半角カナ)を随時確定していきたいのですが、どの
> ようにすればよろしいでしょうか?
[スマートな解法]
Edit1の ImeMode プロパティを imSKata にして
uses Imm;
procedure TForm1.Edit1Enter(Sender: TObject);
var
Imc: HIMC;
Conversion, Sentence: DWORD;
begin
Imc := ImmGetContext(Handle);
ImmGetConversionStatus(Imc, Conversion, Sentence);
ImmSetConversionStatus(Imc, Conversion, IME_SMODE_NONE);
ImmReleaseContext(Handle, Imc);
end;
[スマートではないがいろいろ応用が利きそう]
procedure TForm1.Edit1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
type
TLetters = set of 'A'..'z';
const
Vowels: TLetters = ['A', 'E', 'I', 'O', 'U', 'a', 'e', 'i', 'o', 'u'];
begin
if (Key<>VK_RETURN) and (Chr(Key) in Vowels) then
begin
Keybd_event(VK_RETURN,0,0,0);
Keybd_event(VK_RETURN,0,KEYEVENTF_KEYUP,0);
end;
end;
参照: [Delphi-ML:31097] <その他Windows関連> <Windows> <コンポーネント > <Standard>
0159 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
CreateProcess を使うときは CloseHandle で後始末
CreateProcess で Process_Infomation に返されるプロセス/スレッドハンドルは、明示的に CloseHandle でクローズしないとまずいです。
Win32 Ref. より抜粋
The created process remains in the system until all threads within the process have terminated and all handles to the process and any of its threads have been closed through calls to CloseHandle. The handles for both the process and the main thread must be closed through calls to CloseHandle. If these handles are not needed, it is best to close them immediately after the process is created.
作成されたプロセスは、プロセス内の全てのスレッドが終了し、かつ全てのプロセスとスレッドのハンドルが CloseHandle でクローズされるまでシステム内に残る。プロセスとメインスレッドのハンドルは必ず CloseHandle でクローズしなければならない。もしこれらのハンドルを使わないならばプロセスを作成した直後にクローズするのが最も良い。
参照: [Delphi-ML:30914] <Windows>
0157 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
ディスパッチインターフェイスとデュアルインターフェイスについて
> さて、マニュアルを読んでみました。ですが、結構読みづらいです(@ @)。
> そこで、ちょっとほぐしてみました。
> (解釈に間違いがあればご指摘お願いいたします)
とのことですので、とりあえず覗いてみましょう。
参照: [Delphi-ML:30627] [Delphi-ML:30649] [Delphi-ML:30684] <Windows> <PASCAL>
0156 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
デバッグ時にライブラリ内もステップ実行したい
>標準VCL内のステップ実行の様子を、IDEのデバッグ環境
>で見れるようにしたいのですが、方法はあるのでしょう
>か?
標準VCLソース内をステップ実行するやり方であれば、少なくとも2通りあります。
1) ライブラリパスの設定変更による方法
a. メニュー「ツール」-「環境オプション」の[ライブラリ]-<ライブラリパス>に
標準VCLソースへのパス(〜\Source\VCL)を追加。
<〜\Source\Rtl\Winも加えるとなお良し。Rtl\Sysはやめるが良し。(--;) >
b. メニュー「プロジェクト」-「オプション」の[パッケージ]-<実行時パッケー
ジを使って構築>のチェックを外す。
# b.は、標準VCL内のステップ実行をしたい時にだけ行います。
# a.は常にこの状態にしておいても問題はないでしょう。(私は障害未経験です。)
# そして、こうしておくととても便利です。CTRL + ENTER のショートカットで
# 標準VCLを読み出せるようになります。(^^)
2) VCLソースをプロジェクトに加える方法
<つい最近、このMLで見かけた方法です。どなたの方法かは...失念。>
a. メニュー「プロジェクト」-「プロジェクトに追加」で、
デバッグしたい標準VCLソースをプロジェクトに追加する。
[注]
1)のやり方は、ソースとDCUとのパッチ状況が食い違っている環境(Release-1等) ではパッチが無に帰すかもしれません。何らかの対策を行いましょう。
参照: [Delphi-ML:30414] <開発環境> <デバッグ>
0155 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
マウスがクリックされた正確な時刻が知りたい
> 現在は、ふつうに OnMouseDown イベントから、GetCurrentTimeMillis()
> しているのですが、 Windows のイベントスプールにマウスイベントが
> たまってしまった場合、正確に マウスイベントの時間を計測しているか
> どうか、疑わしいと思います。
GetMessageTime API はいかがでしょう? Thread Queue に置かれたときの時刻が手に入るみたいです。
参照: [Delphi-ML:30236] <その他Windows関連> <Windows>
0154 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
Delphi2 以上で Form の枠を黒線一本にするには
Form の枠を一本線にするために Ctrl3D = True, BorderStyle = bsSingle としても、望みの結果が得られません。問題は、ControlStyle に csFrame が無いとき CTRL3D := False は無視されてしまうということです。
constructor を再定義して
# constructor TForm1.Create(AOwner: TComponent);
# begin
# inhelited;
# ControlStyle := ControlStyle + [csFramed] ;
# end;
を加えれば望みの結果が得られます。
参照: [Delphi-ML:30137] <バグ> <フォーム>
0153 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
エクスプローラで使われるアイコンを取得・変更する
> Windowsでディレクトリなどに使われるアイコンを変更するなどの
> プログラムもあるみたいですが、そういったシステムで使われる
> アイコンを取得したいと思っています。
レジストリの HKEY_LOCAL_MACHINE の下に
Software\Microsoft\Windows\CurrentVersion\explorer\Shell Icons
というキーがあり、ここに番号の文字列を作り、アイコンのファイル名とインデックスを設定することによりアイコンが設定可能です。
参照: [Delphi-ML:26403] <Windows> <アイコン> <ファイル>
0151 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
和暦を西暦に直したい
こんなのはどうでしょう?
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if Key<>#13 then
Exit;
Key:= #0;
with Edit1 do
try
Text:= FormatDateTime('yyyy/m/d',VarToDateTime(Text));
SelectAll;
except
on EVariantError do begin
SelectAll;
raise Exception.Create('入力が不正です');
end;
end;
end;
98/8/21 h10/8/21 s24-10-2 m1/1/1 8/21 8/21/98 98/5 1996-8/21
等 ほとんど何でもありです。半角、全角関係なしです。
参照: [Delphi-ML:25313] [Delphi-ML:25315] <文字列> <計算> <日時> <PASCAL>
0149 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
可変長レコードの扱い方
> 今、DirectPlayの送信部分を制作していますが、
>
> HRESULT Send(DPID idFrom, DPID idTo, DWORD dwFlags,
> LPVOID lpData, DWORD dwDataSize);
>
> 上記のようになっていて、送信データ部分を構造体のポインタで渡す
> ようになっています。現在は、
>
> TTextMessage = record
> dwType: DWORD; { dwType は絶対に必要 }
> Msg :array[0..255]of char;
> end;
>
> としてstring型からchar型に変換してから送受信をしていますが、
> 255文字以上送信できませんし、短い場合は無駄が生じるように思います。
> このmsgの部分を可変サイズにする方法はないでしょうか?
必要な量だけヒープから確保するのがよろしいかと思います。
具体的な案としては、以下のような手順が考えられます。
1. 送信用に AllocMem等で 必要量だけ確保
2. 確保した領域を編集しやすいように 予め用意しておいた型をかぶせ、編集
3. 送信
4. 確保領域の開放
例)
const
Giga = (1024*1024*1024);
type
// 編集用の型
PTextMessage = ^TTextMessage;
TTextMessage = record
dwType: DWORD;
Msg: array[0..1*Giga -1] of Char;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
pBuf: Pointer;
pMsg: PTextMessage;
begin
pBuf := AllocMem(Sizeof(pMsg^.dwType) + 4092);
// 型をかぶせて編集
pMsg := pBuf;
with pMsg^ do
begin
dwType := $12345678;
StrCopy(Msg, PChar(StringOfChar('A', 4091)));
end;
... // 送信等
FreeMem(pBuf);
end;
参照: [Delphi-ML:25091] <PASCAL> <メモリ>
0148 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
アプリケーションが最小化されているかどうかを判定する
最小化された状態の判定:
IsIconic(Application.Handle)
「元のサイズに戻す」
Application.Restore;
で良いと思います。
参照: [Delphi-ML:25293] <アプリケーション> <フォーム>
0147 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
クリップボードにコピーされたファイル・ディレクトリ
> クリップボードからペーストなのですが、どのようにしたらファイル名
> なのか確認できますか?その取り出し方も教えていただけないでしょうか?
プログラム上、考慮することが2点(だけかな?)、あります。
1) Explorer上で「コピー」した場合は、クリップボード内にCF_HDROPフォーマットのデータが入るようになってます。この場合、ファイルの有無はこのフォーマットの有無で判断できます。例は下のほうに置いておきます。
2) IE4が出てから、Explorerに「アドレス」なるコンボボックスが付きやがりまして、ここでディレクトリ名の「コピー」、できちまうんスよ、ったく。こいつ、テキスト形式(CF_TEXT)なんでね、これも考慮するとくりゃ、テキストがファイル名として正しいのか、白黒つけなきゃならないんスね。あ〜こりゃこりゃ。ちと面倒なコトをするハメになりそうっスよ、ったく。くぁ〜、まぃったねぇ。あっしぁ、面倒みきれませんぜ。なんせ あっし、ほれ、バカなんで。
// uses Clipbrd, ShellAPI;
procedure TForm1.Button1Click(Sender: TObject);
var
i,
n: integer;
hDrop: THandle;
szFile: array[0..MAX_PATH-1] of Char;
begin
if HasFormat(CF_HDROP) then
begin
hDrop := GetAsHandle(CF_HDROP);
n := DragQueryFile(hDrop, $FFFFFFFF, szFile, MAX_PATH); // -1かな?(64bit用)
Caption := Format('FileCount : %d', [n]);
for i := 0 to n - 1 do
begin
DragQueryFile(hDrop, i, szFile, MAX_PATH);
ListBox1.Items.Add(szFile);
end;
end;
end;
参照: [Delphi-ML:25073] <その他Windows関連> <Windows> <ファイル>
0145 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
Form をスクロールして特定のコントロールを表示させる
TForm には ScrollInView / AutoScrollInView などという便利なメソッドがあります。
参照: [Delphi-ML:25041] <フォーム>
0144 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
プリンタのスプールファイルを TMetafile に読みこむ
プリンタを一時停止にしておいて印刷すると、windows\temp の下にファイルができますが、それをコピーして拡張子を emf に変えて TImage で覗いたらちゃんと表示されました。
これなら、どんなソフトを使っても印刷さえできれば簡単に emf が作れますね。
参照: [Delphi-ML:14852] <印刷>
0142 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
長すぎる TreeView アイテムを補完するチップヘルプを出さなくする
TreeView からはみだすアイテムをマウスでポイントしたときに出るチップヘルプを出さないようにしたい。
const
TVS_NOTOOLTIPS = $0080; // comctl32.dll ver4.70(IE3) 以上で使用可
var
Style: DWORD;
begin
Style := GetWindowLong(TreeView1.Handle, GWL_STYLE);
Style := Style or TVS_NOTOOLTIPS;
SetWindowLong(TreeView1.Handle, GWL_STYLE, Style);
end;
参照: [Delphi-ML:24847] [Delphi-ML:24861] <Win95> <コンポーネント >
0140 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
リムーバブルドライブの種類を判別する
結局結論は出たのでしょうか???
参照: [Delphi-ML:24577] <Windows> <ファイル>
0139 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
下の図柄がすける透明パネル
実行時に透明になるパネルコンポーネントです。
unit NkTransparent;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs;
type
// 透明で 背景消去を行わない コンテナウィンドウコントロール
TNkTransparent = class(TCustomControl)
private
procedure WMEraseBkGnd(var Msg: TWMEraseBkGnd);
message WM_ERASEBKGND;
protected
procedure CreateParams(var Params: TCreateParams); override;
public
constructor Create(AOwner: TComponent); override;
end;
procedure Register;
implementation
constructor TNkTransparent.Create;
begin
inherited;
Width := 100; Height := 100;
// 上にコントロールが貼りつけられるようにする
ControlStyle := ControlStyle + [csAcceptsControls];
end;
procedure TNkTransparent.WMEraseBkGnd;
begin
// 実行時は背景消去をしない
if csDesigning in ComponentState then inherited;
end;
procedure TNkTransParent.CreateParams(var Params: TCreateParams);
begin
inherited;
// 実行時は「透明」なウィンドウ
if not (csDesigning in ComponentState) then
Params.ExStyle := Params.Exstyle + WS_EX_TRANSPARENT;
end;
procedure Register;
begin
RegisterComponents('NakCtrl', [TNkTransparent]);
end;
end.
参照: [Delphi-ML:24485] <コンポーネント > <Standard>
0138 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
Bitmap のパレットに使いたい色を追加する
>どうやれば どんなBMP にでも 画像を悪化 せずに
>自分で指定した色で文字を描画出来るようになるのでしょうか
取りあえず、こんな処理はどうでしょう?
var bm: TBitmap;
pal: TMaxLogPalette;
PalSize: WORD;
i: Integer;
const Colors: array[0..15] of TColor =
(clBlack, clMaroon, clGreen, clOlive,
clNavy, clPurple, clTeal, clSilver,
clGray, clRed, clLime, clYellow,
clBlue, clFuchsia, clAqua, clWhite); // VGA Colors
:
:
bm := TBitmap.Create;
bm.LoadFromFile('c:\windows\雲.bmp');
// パレットエントリ数を得る
GetObject(bm.Palette, 2, @PalSize);
// パレットの色を取得
GetPaletteEntries(bm.Palette, 0, PalSize-1, pal.palPalEntry);
// パレットのエントリを16色分ずらす
for i := PalSize-1 + 16 downto 16 do
if i < 256 then
pal.palPalEntry[i] := pal.palPalEntry[i-16];
// ずらして空いた部分に VGA カラーを埋め込む
for i := 0 to 15 do begin
pal.palPalEntry[i].peRed := GetRValue(Colors[i]);
pal.palPalEntry[i].peGreen := GetGValue(Colors[i]);
pal.palPalEntry[i].peBlue := GetBValue(Colors[i]);
pal.palPalEntry[i].peFlags := 0;
end;
if Palsize + 16 > 256 then
pal.palNumEntries := 256
else
pal.palNumEntries := PalSize + 16;
pal.palVersion := $0300;
// VGA カラーを足したパレットをビットマップにセット
// TBitmap は新しいパレットで色の劣化が最小限に
// なるように自動的にカラーマッチをやり直してくれます。
bm.Palette := CreatePalette(PLogPalette(@pal)^);
これで VGA Color が描けるビットマップになると思います。
ほとんど色は劣化しません。ただ、パレットの後半に重要な色が有ると多少劣化するかも。そこまでやるには VGA Color を挿入する前にパレットの色を明るさの降順に並べ替える処理を入れた方がいいと思います。
また、VGA Colors と同色の色が有った場合、その色を挿入しないようにすれば、より品質が向上すると思います。
参照: [Delphi-ML:24045] <その他Windows関連> <画像> <Windows>
0137 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
API を使って縦書きなどのフォントを指定する
var
lbl :TLogFont;
begin
// ここをいろいろ変えれば縦横比を変えることができる
// (日本語の場合、幅:高さ=1:2で横倍率が100%)
lbl.lfWidth := 10;
lbl.lfHeight := 20;
// 文字の太さ(0〜1000)
// 「標準」の太さは400、「太字」の太さは700
lbl.lfWeight := 400;
// 反時計回りの角度(単位は1/10度)
lbl.lfEscapement := 2700;
// 下線なし(デフォルトでは「あり」)
lbl.lfUnderline := 0;
// 打ち消し線なし(デフォルトでは「あり」)
lbl.lfStrikeOut := 0;
// 斜体無効(デフォルトでは有効)
lbl.lfItalic := 0;
// 縦書き用の「@」がつくフォントを使用する
lbl.lfFaceName := '@MS ゴシック';
// フォントを作成
Canvas.Font.Handle := CreateFontIndirect(lbl);
// キャンバスのブラシスタイルを変えることで
// 背景を透明にして描画します
Canvas.Brush.Style := bsClear;
// 文字列を描画
Canvas.TextOut(300, 300, '縦書き文字');
end;
参照: [Delphi-ML:24027] <描画> <その他コンポーネント関連> <コンポーネント >
0135 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
表示中のポップアップメニューを消す
>プログラム上でポップアップメニューを表示するにはPopup メソッドを
>使えば出来るのですが、逆に非表示させるにはどうしたらよいのでしょう。
>よろしくお願いします。
キーボードイベントを送って非表示にする、というのはどうでしょう。
procedure TForm1.FormCreate(Sender: TObject);
begin
Timer1.Interval := 1000;
Timer1.Enabled := False;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Timer1.Enabled := True;
PopupMenu1.Popup(Button1.ClientOrigin.x,
Button1.ClientOrigin.y + Button1.Height);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
keybd_event(VK_ESCAPE, 0, 0, 0);
keybd_event(VK_ESCAPE, 0, KEYEVENTF_KEYUP, 0);
Timer1.Enabled := False;
end;
以上のコードで実行すると、ボタンを押すとポップアップしますが、約1秒後にポップアップが非表示になります。
参照: [Delphi-ML:24269] <Standard> <メニュー> <コンポーネント >
0134 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/11 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/11 osamu 編集
TSplitter をドラッグ中にヒント文字列が表示された時の不具合
TSplitter をドラッグ中に他のコントロール上にてヒントボックスを出現させると、TSpliter の境界線がその場に残ってしまいます。
TSplitter のドラッグ中は Application.ShowHint を False にしてしまうという回避方が [Delphi-ML:24019] にて紹介されています。
参照: [Delphi-ML:24019] <Additional> <バグ> <その他コンポーネント関連> <コンポーネント >
0133 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
リードオンリーファイルを Assign/Reset で読もうとするとエラーになる
Reset する前に FileMode 変数をセットしましょう。
参照: [Delphi-ML:23966] <ファイル>
0114 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Fortran のソースを C に変換するツール
F2C という FORTRAN to C translator を BCB1 用に移植したものがあります。
BCB3 でそのまま使えるかは未確認です。
ただ、ソースも丸ごとはいっているので、libF77.lib と libI77.lib、心配なら f2c.exe も BCB3 でリコンパイルすれば使えるかもしれません。
Makefile.BCB というので、コマンドラインからリコンパイルするようにしてあります。
BCB1 なら、make -f Makefile.BCB で問題なくコンパイルできます。
http://www.annie.ne.jp/~shimaden/
参照: [builder:6207] <開発環境> <C++>
0116 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TColor を RGB 値へ変換する
下のような関数を作ればよいです。
procedure ColorToRGB(c: TColor; var r,g,b: Byte);
var IntColor: Integer;
begin
IntColor:= ColorToRGB(c);
r:= GetRValue(IntColor);
g:= GetGValue(IntColor);
b:= GetBValue(IntColor);
end;
参照: [builder:6369] <画像>
0117 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Delphi で DOS のアプリを書く方法
[A1]
http://www.geocities.com/SiliconValley/Park/4493/
にある 32 ビット DOS エクステンダで Win32 Console 実行ファイルをMS-DOS 実行ファイルに変換できます。
どの API が使用できるのかは未確認ですが・・・・
[A2]
Delphi1の場合、DOS用Turbo Pascalの最終バージョンであるBorland Pascal 7.0付属のライブラリソースを元にしてDOS用ライブラリをでっちあげ、かつコマンドラインコンパイラでオプション-cdをつけてコンパイルすれば、DOS プログラム用のコンパイラとして動かすことができます。
ライブラリの構築にBP7が必要なので、「Delphiだけで」というわけにはいきませんが、一度ライブラリを作ってしまえばOKということです。
ただ、このライブラリでっち上げが「すげー手間」なのでお勧めしません。
昔はCompuServeにライブラリソースの差分が上がってたんですけど、さすがにもう消えているはずです。
現実的には、どうせライブラリ作成過程で必要になるBP7を素直に使うのが正常な判断というものでしょう。
#私?BP7も持ってたから試しましたとも、D1日本語版すら出てない頃に(^^;
参照: [Delphi-ML:22238] [Delphi-ML:22240] <開発環境> <Windows>
0118 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Delphi/CBuilder で作った DLL から VB に文字列を返す
> VB(VBA含む)は、ポインタが使えませんので、DLLで文字列を返す方法
> が知りたいです。 だれか、ご存知のかたおしえてください。
一番手軽なのは,SysAllocStringByteLen で文字列を確保して,そのまま返す方法です.
例)
return SysAllocStringByteLen( "Hello", 5 );
内容は ShiftJIS で Ok.
ここで確保したメモリーは VB 側で開放されるそうです.
参照: [builder:6429] <Windows> <DLL> <メモリ>
0120 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
コントロールパネルのスクリーンセーバの設定画面を表示させる
> 画面のプロパティの設定ウィンドウを自作のアプリから
> 起動するにはどうすれば良いのでしょう?
---------------8<---------8<-------------------------------
> できれば、プロパティ設定画面の"スクリーンセーバー"のページが
> デフォルトで表示される方法をご教示頂ければ有り難いのですが。
コマンドラインだと
Rundll32 Shell32.dll,Control_RunDLL Desk.cpl,,1
アプリからでは
::ShellExecute( Handle ,"open","Rundll.exe",
"Shell32.dll,Control_RunDLL Desk.cpl,,1",
"",SW_SHOWNORMAL);
で"スクリーンセーバー"のページが表示されます。
(コンマの前後に空白は入れないこと。)
パラメータの最後の数値が 2 なら"デザイン"、3 では"ディスプレイの詳細"が表示されます。終端の数字が 0 或いは、コンマ・数字無しだとデフォルトの"背景"です。
参照: [builder:6442] <その他Windows関連> <Windows>
0121 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Windows95 のみを再起動する
> Windowsの再起動ってどうやればいいんでしょうか?
> マシンごと再起動ではなく、Windowsのみの再起動を
> したいのですが・・・。
> Windowsの終了->コンピューターを再起動する
> をシフトキーを押しながらしたときの動作がしたいのです。
これ、16BitAPIコードじゃないとできないらしいです。
だからWin95では使えても、NTでは無理なのでは?
と思いますが……どうなんでしょう?
ちょっと裏技として、RUNDLLを利用する手段があります。
RUNDLL.EXE に
user.exe,exitwindowsexec
というオプションをつけて起動させてみてください。
ショートカットでは正常動作したので、多分使えると思います。
プログラムでは95でもNTでも試していませんが……。
できなかったらごめんなさい。
ではまた。
参照: [builder:6513] <Windows>
0124 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
ダイヤルアップネットワークで設定されている接続先を取得する
> ダイヤルアップネットワークで設定されている複数の接続先を
> すべて取得して、選択した接続先に接続するには
> どうすればよいのでしょうか?
http://www.kh.rim.or.jp/~maruoka/Builder/TIPS/tips14.html
に説明があります、参考にしてください。
参照: [builder:6282] <通信>
0125 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
他プロセスの Window を GUI で選ばせたい
TImage を基本として作ったVCLを紹介します。
当該コントロール上でマウスの左ボタンを押下し、そのまま目的のウィンドウ上までドラッグ、ボタンを離す事によって、ウィンドウハンドルを得ることができます。
ThWndCapture Ver. 1.00
http://w3ma.kcom.ne.jp/~sammy/
レポートお待ちしております。
参照: [Delphi-ML:22176] <Windows>
0126 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
*.h ファイルから *.pas ファイルを作るコンバータ
最終的にご自分で変換するときの為に、サポートツールをご紹介します。
これらは、*.h -> *.pas への自動変換を行ってくれます。
それぞれに得手不得手がありますので、おいしいとこだけ つまんでください。(^^)
1. 熊木さんがこのMLで配付なさっていた H to Pas Converter
[Delphi-ML:11748] が最新版?
おっと、最新版は黒田 Dycoon さんのページにあるそうです。
http://www.ceres.dti.ne.jp/~dycoon/
2. Command-line HeadConv C DLL header converter (HeadConv.exe [HeadConv.zip])
Dr.Bob's Delphi Clinic
http://www.drbob42.com/delphi/default.htm
(ページ左のリンク "Files to Download" から取得できます。)
3. Header Translator (htrans.exe [htr034b.zip])
Alexander Staubo's Web Page --Technical stuff
http://www.mop.no/~alex/
参照: [Delphi-ML:23153] [Delphi-ML:22638] [Delphi-ML:11748] <開発環境> <C++> <PASCAL>
0127 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
任意の実行ファイルが DLL などからインポートしている関数の一覧を得る
>ある実行ファイルについて、それが DLL などからインポートしている関数の
>一覧を得るには、どうすれば良いでしょうか。
Delphi3ならtdump.exeというのが付属していると思います。
ちなみに、手元のPCでは以下のファイルがそれです。
C:\Program Files\Borland\Delphi 3\BIN\TDUMP.EXE
引き数なしで起動すると簡単な使用方法が表示されます。
参照: [Delphi-ML:22690] <開発環境>
0128 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
実行終了後、自分自身(実行ファイル)を削除したい
> 実行終了後、自分自身を削除したいのですが、
> なにか良い方法はありませんか?
こんなのはどうでしょう?
MoveFileEx(lpszExisting,NULL,MOVEFILE_DELAY_UNTIL_REBOOT);
NTではこれで再起動時に削除されます。
Windows95 ではこのAPIはサポートされないので
以下の方法をとるように、MoveFileEx のヘルプで示されています。
Windows 95: The MoveFileEx function is not supported on Windows 95.
To rename or delete a file at reboot on a Windows 95 system, use the following procedure.
・To Rename or Delete a File on Windows 95
Check for the existence of the WININIT.INI file in the Windows directory. If WININIT.INI exists, open it and add new entries to the existing [rename] section. If the file does not exist, create the file and create a [rename] section. Add lines of the following format to the [rename] section:
DestinationFileName=SourceFileName
Both DestinationFileName and SourceFileName must be short filenames. To delete a file, use NUL as the value for DestinationFileName.
The system processes WININIT.INI during system boot. After WININIT.INI has been processed, the system names it WININIT.BAK.
ちなみに、いずれも再起動が必要です。また私自身は、Windows95 で上記の方法は試したことがありませんが、WindowsNTで自分自身を消去するのは確認済みです。
参照: [Delphi-ML:22795] <Windows>
0130 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
メタファイルを wmf 形式でコピーするときの注意
Word 95 などのように旧メタファイル形式にしか対応しないアプリにメタファイルをコピーするときの注意点です。
Windows はクリップボードにエンハンストメタファイルが有って CF_METAFILEPICT 形式のメタファイルを要求されるとメタファイルを自動的に変換(emf->wmf)します。
ところが Windows がメタファイルをエンハンストから旧メタファイルに変換するとき境界枠の大きさやアスペクト比が失われてしまいます。
以下のように、はじめから wmf でクリップボードにコピーしておくといいようです。
procedure SaveToClipAsWMF(mf: TMetafile);
var
hMetafilePict: THandle;
pMFPict: PMetafilePict;
DC: THandle;
Length: Integer;
Bits: Pointer;
h: HMETAFILE;
begin
DC := GetDC(0);
try
Length := GetWinMetaFileBits(mf.Handle, 0, nil,
MM_ANISOTROPIC, DC);
Assert(Length > 0);
GetMem(Bits, Length);
try
GetWinMetaFileBits(mf.Handle, Length, Bits,
MM_ANISOTROPIC, DC);
h := SetMetafileBitsEx(Length, Bits);
Assert(h <> 0);
try
hMetafilePict := GlobalAlloc(GMEM_MOVEABLE or
GMEM_DDESHARE,
Length);
Assert(hMetafilePict <> 0);
try
pMFPict := GlobalLock(hMetafilePict);
pMFPict^.mm := MM_ANISOTROPIC;
pMFPict^.xExt := mf.Width;
pMfPict^.yExt := mf.Height;
pMfPict^.hMF := h;
GlobalUnlock(hMetafilePict);
Clipboard.SetAsHandle(CF_METAFILEPICT, hMetafilePict);
except
GlobalFree(hMetafilePict);
raise;
end;
except
DeleteObject(h);
raise;
end;
finally
FreeMem(Bits);
end;
finally
ReleaseDC(0, DC);
end;
end;
参照: [Delphi-ML:23065] <その他Windows関連> <画像> <Windows>
0131 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
超高速 ListView
> 推測ではありますが、Explorer等は ListView コモンコントロールが備えている
> コールバック機構を利用して、初期化処理の遅延を図っているのだと思います。
> ここでいうコールバックの機能は、画面に表示するハメになった ListItem について
> 「Caption はなんですか?」「ImageIndex は何番ですか?」「SubItem(n)は?」
> と聞いてくる、というものです。
この処理を実現しているコンポーネントが[Delphi-ML:23838]で紹介されています。
参照: [Delphi-ML:23838] <Win95> <コンポーネント >
0132 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
ステータスバーにプログレスバーを置きたい
>ステータスバーを二つのパネルに分割して一方に文字、もう一方にprogress barを
>表示したい。
動的に作って置けばできます。Panels[0]に文字列、Panels[1]にProgressBarを置くとします。
ProgressBar1 := TProgressBar.Create(StatusBar1);
try
with ProgressBar1 do begin
Top := 2;
Height := StatusBar1.Height -2 ;
Width := StatusBar1.Panels[1].Width -2;
Left := StatusBar1.Panels[0].Width +2;
Max := ***.Count;
Step := 1;
end;
for i := 0 to ***.Count -1 do begin
//何らかの処理
ProgressBar1.StepIt;
Progressbar1.Update;
end;
finally
ProgressBar.Free;
end;
参照: [Delphi-ML:23558] <Win95> <コンポーネント >
0049 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Glyphに張ったビットマップの背景がおかしくなる
Glyphでは、左下隅のピクセルの色を透過色として扱うので、背景となる色をちゃんと表示するには、ビットマップを一回り大きく作って表示したい絵の中に使われていない色で「縁」を作ってやればよいです。
参照: [Delphi-ML:6213] <画像> <バグ>
0050 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
中村さんありがとうシリーズ:ビットマップ・パレット編
ビットマップやパレットについて分からないとき、中村さんのホームページ、
http://www.asahi-net.or.jp/~HA3T-NKMR/
を見に行くのは当然として、Delphi-MLでの中村さんの発言をまとめてみましたので、こちらもご利用ください。
[Delphi-ML:01572] Re: Palette
[Delphi-ML:01690] Re: How To SystemColor ?
[Delphi-ML:01865] Re: How to Use Palette ???
[Delphi-ML:01969] Re: About TMediaPlayer and TImage
[Delphi-ML:02075] The Bug of FreeMem
[Delphi-ML:02365] Re: Palette 98とATで機種依存?
[Delphi-ML:03293] How Does Delphi Copy Palette?
[Delphi-ML:03376] Re: 16-Color Palette Bitmap
[Delphi-ML:04017] Re: BITMAPの色がまともに出ない
[Delphi-ML:04344] Re: [Q]:Strech Image Save ToFile
[Delphi-ML:04494] Re: クリップボードからの画像貼付けについて
[Delphi-ML:05140] Re: Help! Bitmap Handle to Address
[Delphi-ML:05271] Re: 256 color Bitmap を作りたい
[Delphi-ML:05312] Re: 256 color Bitmap
[Delphi-ML:05343] Re: 256 color Bitmap
[Delphi-ML:05711] Re: パレットの合成
[Delphi-ML:05343] Re: FloodFill
[Delphi-ML:06583] Re: rectangleについて(Canvasの秘密)
[Delphi-ML:06587] Re: rectangleについて(Canvasの秘密)
[Delphi-ML:07096] Re: モノクロビットマップについて
[Delphi-ML:07199] Re: クリップボード経由でグラフィックを印刷する方法?
[Delphi-ML:08388] Re: Delphi3がやってきた。
[Delphi-ML:08518] Re: チョー初歩的、スプライト
[Delphi-ML:08810] Re: パレット破壊?
[Delphi-ML:10676] Re: ビットマップをだんだん暗くしたい
[Delphi-ML:13018] Re: ScanLineの使用方法?
[Delphi-ML:13230] The Oracle of Delphi
[Delphi-ML:14252] Re: はじめまして&質問です
[Delphi-ML:14915] Re: TBitmapで DIBのビット値へのポインタを得る方法
[Delphi-ML:15271] Re: RE: Re: グラフィックの扱いについて
現在まだまだ増加中
近々、中村さんありがとうシリーズ:TImage編、Printer編なども追加していくつもりです。
参照: <画像>
0052 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
デスクトップフォルダのパスを得る
uses Shellapi, ShlObj, ActiveX, ComObj;
function GetDesktop: string;
var pidl: PItemIDList;
buf : array [0..MAX_PATH] of Char;
m : IMalloc;
const DesktopID = CSIDL_DESKTOPDIRECTORY;
begin
OleCheck(CoGetMalloc(1, m));
OleCheck(SHGetSpecialFolderLocation(0, DesktopID, pidl));
try
Assert(SHGetPathFromIDList(pidl, buf));
Result := buf;
finally
m.Free(pidl);
end;
end;
ネットワークコンピュータ、マイコンピュータは仮想フォルダなので
パスは有りません。マイコンピュータの定義は CSIDL_DRIVES です。
参照: [Delphi-ML:19392] <Windows>
0054 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Scaled/AutoScrollプロパティと実行時のフォームサイズ
中村@NECさんの、[builder-ML:4674] からの引用です。
(1) Scaled = False, AutoScroll = False
フォームのフォントの大きさ、フォームのクライアント領域、
コントロールの大きさは設計時のピクセル単位のサイズが
そのまま使われる。
(2) Scaled = False, AutoScroll = True
(1) とほとんど同じだが、フォームは Width と Height で大きさが
設定されるので、タイトルバーの高さ、枠の幅、メニューの高さ
によりクライアント領域の大きさが若干かわるので、場合によっては
コントロールがはみ出したりする。
(3) Scaled = True, AutoScroll = False
フォームのフォントの大きさ、フォームのクライアント領域、
コントロールの大きさは設計時のピクセル単位のサイズに
フォントの大きさの変化に応じたスケール比を掛けたものが
使われる。
(4) Scaled = True, AutoScroll = True
フォームのフォントの大きさ、コントロールの大きさは設計時の
ピクセル単位のサイズにフォントの大きさの変化に応じた
スケール比を掛けたものが使われる。しかし、フォームの
大きさは設計時のピクセル単位の大きさがそのまま使われるので
コントロールがはみ出したりする。使わない方がよいでしょう(^^
Delphi-MLで詳しく解説してくださったのが [Delphi-ML:19661]。
参照: [Delphi-ML:19661] <フォーム>
0056 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
起動時にフォームの表示・非表示を決める
Main Form の表示は Application の ShowMainForm と Main Form の Visible Property で決まります。
Application.Run が呼ばれる前に ShowMainForm を適切に設定して下さい。
参照: [Delphi-ML:19468] <フォーム>
0058 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TBitmap の Width/Height に 0 を代入すると例外が発生する
TBitmap のバグか仕様か以下のようなコードで例外が発生する。
void __fastcall TClass1::Method1( void )
{
Graphics::TBitmap *Bmp;
Bmp = new Graphics::TBitmap();
Bmp->LoadFromFile( "BMPファイル名" );
Bmp->Width = 0; // <-ここでEOutOfResource例外が発生
delete Bmp;
}
このコードは BCB1 では問題なかったが BCB3 では例外が発生する。
内部ビットマップ形式が DDB から DIB に変わったことが関連している。
Bmp->Width = 0;
の前に、
Bmp->ReleaseHandle();
とすることで回避が可能。
参照: [builder:4889] <画像> <バグ>
0059 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Bitmap から Icon を作る
Windows 標準の 16 色パレットを使った BMP から 16 色アイコンを作る方法が、[Delphi-ML:19514] に紹介されている。
参照: [Delphi-ML:19514] <画像>
0060 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
CD-DA のデータを Wav ファイルに落としたい
ここにあるcd2wavが大いに参考になるでしょう。ソース付きです。
http://www2s.biglobe.ne.jp/~elfin/
参照: [builder:4912] <Windows>
0061 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
複数プログラムから同一内容のメモリを参照/更新する
Win32用に、中村@NECさんが、File Mapping を利用して実現したクラスを [Delphi-ML:19603] で紹介されています。
ただ、添付ファイルは Web からは取れません。
どこか他からダウンロードしないと。。。
参照: [Delphi-ML:19603] <Windows> <ファイル> <メモリ>
0062 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
プログラムからWindowsのスタートメニューを表示する
以下の方法でメニューを出すことができるようです。
HWND taskbar=FindWindow("Shell_TrayWnd","");
if(taskbar) PostMessage(taskbar,WM_USER+260,0,0);
参照: [builder:4947] <Windows> <メニュー>
0064 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
SetWindowsExt/SetViewportExtを使うときの注意点
TCanvas.Handle に対してこれらの関数を呼んで使う際の注意点。
(1) Font.Height と Font.Size の関係はマッピングモードが MM_TEXT
で有ることを仮定しているので SetMapMode API でマッピングモードを
変えた場合は Font.Height のみを使った方が良いでしょう。
(2) API の効果の寿命はイベントハンドラ毎です。
引き継がれません。
参照: [Delphi-ML:19671] <Windows> <描画>
0066 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
PopupMenu に MainMenu のサブ項目をそのまま表示する
一つのメニューアイテムを、同時に二つのメニューに結び付ける事は出来ないようなので、実行時に、MainMenu と PopupMenu 、それぞれが開く時点で、共有するアイテムをそのメニューに結びつけ直す、という方法をとりました。ある位置から下を全部共有アイテムとする仕様です。
結び付け直すタイミングですが、PopupMenu の方は、OnPopup イベントで良いのですが、MainMenu の方は、(エディットメニュー以下をすべて使う場合に) Edit1 のOnClick だと、位置がずれたりしてうまくないので、WndProc をオーバーライドして、WM_INITMENU メッセージに応答させました。
以下は PASCAL 翻訳版
const COMMON_ITEM_START = 3; // 共有アイテムの始まる位置
procedure TForm1.PopupMenu1Popup(Sender: TObject);
var Item: TMenuItem;
begin
while Edit1.Count > COMMON_ITEM_START do begin
Item:= Edit1.Items[COMMON_ITEM_START];
Edit1.Remove(Item);
PopupMenu1.Items.Add(Item);
end;
end;
procedure TForm1.WndProc(var Msg: TMessage);
var Item: TMenuItem;
begin
if (Msg.Msg=WM_INITMENU) and (Msg.WParam=Menu.Handle) then begin
while PopupMenu1.Items.Count > COMMON_ITEM_START do begin
Item:= PopupMenu1.Items[COMMON_ITEM_START];
PopupMenu1.Items.Remove(Item);
Edit1.Add(Item);
end;
end;
inherited WndProc(Msg);
end;
参照: [builder:5012] <コンポーネント > <Standard> <メニュー>
0069 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
親クラスのプライベートフィールドにアクセスする
中田さんが「邪悪な」方法を紹介してくれてます。
利用する方はくれぐれも気を付けて下さい。
参照: [Delphi-ML:20017] <PASCAL>
0070 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
OpenDialogでたくさんファイルを選択するとエラー
ファイル名を保存するバッファが 8KB 固定のために起こります。
VCL に手を入れるか、自分で GetOpenFileName API を呼ぶしか回避できません。
参照: [builder:5117] <バグ> <ダイアログ> <ファイル>
0071 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
C++ の関数の親子関係をレポートするツール
以下の場所に「PBrows32」というフリーソフトがあります。
http://www01.tcp-ip.or.jp/~world/free/othprogram.html
参照: [builder:4912] <開発環境> <C++>
0072 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
スクリーンセーバーをプログラムから停止する
API の SetCursorPos() で強制的にマウスの位置を動かしてやることでスクリーンセーバを止めることができます。
95ではちょっと動かしてやれば止まりますが、NT4ではある程度以上大きく動かさないと止まってくれないそうです。
最近の報告では、この方法でNT4のスクリーンセーバを停止すると、その後いつまで経ってもスクリーンセーバが起動してくれないとのことです。
ちなみに、スクリーンセーバを開始させるには、uses 節に Messages を加えて、
SendMessage(Handle, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
でできます。
参照: [Delphi-ML:5062] [Delphi-ML:17211] [Delphi-ML:19836] <その他Windows関連> <Windows>
0074 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Delphi3のコード補完機能で選択肢をソートして表示させる
ポップアップウィンドウが開いた時点で、マウスをその中に合わせて右ボタンをクリックすると表示順をスコープ順からアルファベット順に変更する事ができます。
参照: [Delphi-ML:20349] <開発環境>
0077 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
コードエディタで「しおり」を使う
デフォルトのキーマップならば[Shift]-[Ctrl]+[数字キー]でしおりの設定、[Ctrl]+[数字キー]でしおりにジャンプができます。
ヘルプの「キーボードショートカット」を見ると他にも便利な機能を発見することができるかもしれません。
参照: <開発環境>
0078 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Delphiアプリをマウスのホイールに対応させたい
TMouseWheel コンポーネントってのがあります。
これが、参考になるのではないでしょうか。
以下、readme.txtより抜粋
---------ここから
(概要)
このコンポーネントはフォームに貼り付ける事によってそのフォー
ムに貼り付けてある全てのコントロールに対してホイールによるス
クロールを可能にする事ができます。
---------ここまで
Delphi広場にありました。ファイル名:mwheel.exe
http://www.sakaki.keiaikai.or.jp/
参照: [Delphi-ML:20365] <Windows>
0080 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
ヘルプ作成用のホットスポットエディタ(SHED.EXE)の入手方法
以下のURLよりダウンロードが可能です。
ftp://ftp.microsoft.com/Softlib/MSLFILES/SHED.EXE
参照: [Delphi-ML:20436] <開発環境> <Windows> <ヘルプ>
0081 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
「システムエラー 読み出せません ドライブ X:」ダイアログを回避する
Q:
Delphiで、フロッピーのようなリムーバブルメディアがちゃんとセットされているかを、「静かに」知る方法はないものでしょうか。FileExists関数を使っても、フロッピーの入っていないドライブを見に行くと、システムエラーダイアログが表示されてしまいます。
A:
Windows API の SetErrorMode を使用すればいいでしょう。
ここ一年使っているドライブのチェックルーチンはこんな感じです。
# WIN32の部分はまだあまり実績がないですけど...
function IsDriveReady(drive : char) : boolean;
var
oldmode : word;
searchrec : tSearchRec;
begin
drive := upcase(drive);
oldmode := SetErrorMode(SEM_FAILCRITICALERRORS); (* ここと *)
result := DiskSize(ord(drive)-$40) <> -1;
{$IFDEF WIN32}
if result and (GetDriveType(pchar(drive+':\')) in [DRIVE_REMOTE, DRIVE_CDROM]) then begin
{$ELSE}
if result and (GetDriveType(ord(drive)-$41) = DRIVE_REMOTE) then begin
{$ENDIF}
result := FindFirst(drive+':\*.*',$3f, searchrec) = 0;
FindClose(searchrec);
end;
SetErrorMode(oldmode); (* ここね *)
end;
http://slis.flet.mita.keio.ac.jp/~anakata/delphi/myfaq.html#Q12
にも載せてあります。
参照: [Delphi-ML:184] <Windows> <ダイアログ>
0082 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
StringGrid で マウスのある Cell 内容に応じた Hint を出したい
{Application.OnShowHint イベントハンドラの設定}
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnShowHint := DoShowHint;
end;
{ヒントの表示ルーチン}
{TForm1 の Private で宣言してあります。}
procedure TForm1.DoShowHint( var HintStr: string; var CanShow: Boolean;
var HintInfo: THintInfo);
var
ACol,ARow: Integer;
ARect: TRect;
begin
{ストリンググリッドならば}
if HintInfo.HintControl = StringGrid1 then begin
with HintInfo do begin
{ヒントの色の指定}
HintColor := clAqua;
{セルの位置を取得}
StringGrid1.MouseToCell( CursorPos.x, CursorPos.Y, ACol, ARow );
{セルの範囲の取得}
ARect := StringGrid1.CellRect( ACol, ARow );
{ヒントの表示位置}
HintPos := StringGrid1.ClientToScreen( Point(ARect.Left,ARect.Bottom));
{ヒントの内容}
HintStr := 'このセルは('+IntToStr(ACol)+','+IntToStr(ARow)+')';
{ヒントの有効範囲の設定}
CursorRect := ARect;
end;
end;
end;
参照: [Delphi-ML:19686] <Additional> <その他コンポーネント関連> <コンポーネント >
0083 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
自作コントロールで IME 入力時の変換候補をキャレット位置に表示したい
IMEが編集を開始する直前にWM_IMESTARTCOMPOSITION というメッセージを送って来るので、そのメッセージを捕らえて設定してやります。
class TCustom : public TCustomControl
{
・・・・・・・・・・・・
void __fastcall IMEStart(TMessage& Message);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER( WM_IME_STARTCOMPOSITION ,TMessage,IMEStart)
END_MESSAGE_MAP(TCustomControl)
};
void __fastcall TCustom::IMEStart(TMessage& Message)
{
// IMEの位置をキャレットのポジションに設定
COMPOSITIONFORM CompForm;
POINT pt;
LOGFONT lf;
HIMC hImc=ImmGetContext(Handle);
//キャンバスのフォントと同じに設定する
GetObject(Canvas->Font->Handle,sizeof(LOGFONT),&lf);
ImmSetCompositionFont(hImc,&lf);
//キャレットのポジションに設定する
ImmGetCompositionWindow(hImc,&CompForm);
CompForm.dwStyle=CFS_POINT;
GetCaretPos(&pt);
CompForm.ptCurrentPos=pt;
ImmSetCompositionWindow(hImc,&CompForm);
ImmReleaseContext(Handle, hImc);
// その他の処理
・・・・・・・・・・・・・
}
参照: [builder:5269] <その他Windows関連> <コンポーネント開発> <Windows> <コンポーネント > <Standard>
0084 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TDDEClientConvで最初の行しか実行されない?
TDDEClientConv を使って、ステップ実行を行うとちゃんとマクロは実行されるのですが、そのまま実行すると、最初のコマンドのみ実行してその後が実行されません。
これは2.0からのバグです。3.0/3.1用のパッチを当てると直ると思います。
http://www.dataweb.nl/~r.p.sterkenburg/bugsall.htmが詳しいです。
日本語版でも通用する個所が多いです。参考にしてみて下さい。
1)必要なったらTDDEClientConvをCreate
2)マクロを実行
3)用が済んだらFree
で逃げられます。
参照: [Delphi-ML:19657] <その他Windows関連> <System> <ShellApi> <Windows> <バグ> <コンポーネント >
0085 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TUpDown の不具合
TUpDown コントロールは OnChange/OnClick イベントがらみで、様々な問題を抱えているようです。
詳しくは、まとめとして投げられた、[Delphi-ML:20127]からたどれるスレッドを参照してください。
参照: [Delphi-ML:20127] <バグ> <Win95> <コンポーネント >
0087 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Delphi1/2で状況依存型のコンポーネントヘルプを作るときの注意
[delphi-cw 127]より。
マニュアルによれば、ヘルプトピックに、決められた形式の B 脚注を入れれば良いことになっているのですが、この時、脚注の書式に気を付けないといけません。
#{\footnote hlp_TNkDIB}
${\footnote TNkDIB}
K{\footnote TNkDIB;NkDIB;DIB}
B{\footnote class_TNkDIB}
などとする変わりに、
#{\footnote # hlp_TNkDIB}
${\footnote $ TNkDIB}
K{\footnote K TNkDIB;NkDIB;DIB}
B{\footnote B class_TNkDIB}
のようにしないと、kwgen.exe がキーワードの読み取りに失敗してしまいます。
参照: <開発環境> <コンポーネント開発> <バグ> <ヘルプ>
0088 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
time_t を TDateTime に変換する
time_t は、 int (4バイト)で、GMT の 1970年1月1日0時0分0秒からの経過時間を秒単位で表したもので、どうやら2038年1月19日3時14分07秒で破綻するようです。
function time_tToDateTime(vtime_t : Integer) : TDateTime;
const
cDeltaDate : Integer = 25569; // 1970/01/01 の TDateTime値
cSecPerMint : Integer = 60;
cSecPerHour : Integer = (60*60);
cSecPerDay : Integer = (60*60*24);
var
hh, mm, ss : Word;
n : Integer;
begin
vtime_t := vtime_t - cSecPerHour * 9; // JST=+9
n := vtime_t mod cSecPerDay;
hh := n div cSecPerHour;
n := n mod cSecPerHour;
mm := n div cSecPerMint;
ss := n mod cSecPerMint;
Result := (vtime_t div cSecPerDay) +
cDeltaDate + EncodeTime(hh, mm, ss, 0);
end;
参照: [Delphi-ML:18520] <計算> <日時> <PASCAL>
0091 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TPageControl を TabPosition=tpBottom にすると不具合
Windowsのバージョンによって、tpBottom での利用に制限がある場合があるようです。[Delphi-ML:20099]からのスレッドに、いくつか報告例があるので、参考にしてください。
参照: [Delphi-ML:20099] <バグ> <Win95> <コンポーネント >
0092 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TBitmap をきれいに印刷する
TBitmap 印刷用手続きの中村@NECさんバージョンです。
StretchDIBits を使うところがミソだそうです。
procedure StretchDrawBitmap(Canvas: TCanvas; // 描画先キャンバス
r: TRect; // 描画先の範囲
Bitmap: TBitmap // ビットマップ
);
var OldMode: Integer; // StretchMode のセーブ
Info: PBitmapInfo; // DIB ヘッダ + カラーテーブル
InfoSize: Integer; // DIB ヘッダ + カラーテーブル
Image: Pointer; // DIB のピクセルデータ
ImageSize: Integer; // DIB のピクセルデータのサイズ。
begin
// DIB の「ヘッダ+カラーテーブル」の大きさと ピクセル
// データの大きさを求める
GetDIBSizes(Bitmap.Handle, InfoSize, ImageSize);
Info := Nil;
Image := Nil;
try
// DIB 用のメモリーを確保
Info := AllocMem(InfoSize);
Image := Allocmem(ImageSize);
// DIB を取り出す
GetDIB(Bitmap.Handle, Bitmap.Palette, Info^, Image^);
// 描画!!
OldMode := SetStretchBltMode(Canvas.Handle, COLORONCOLOR);
StretchDIBits(Canvas.Handle,
r.Left, r.Top,
r.Right - r.Left, r.Bottom - r.Top,
0, 0, Info^.bmiHeader.biWidth,
Info^.bmiHeader.biHeight,
Image, Info^, DIB_RGB_COLORS, SRCCOPY);
SetStretchBltMode(Canvas.Handle, OldMode);
finally
if Info <> Nil then FreeMem(Info);
if Image <> Nil then FreeMem(Image);
end;
end;
参照: [Delphi-ML:20572] <印刷> <画像>
0093 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
アプリケーションアイコンの変更が反映されない
IDEでアプリケーションアイコンを変更しても、95のエクスプローラからプロパティで見たときのアイコンを変更することができず、はまることがあります。
これは、Windows 95 のアイコンキャッシュのせいです。何故か、Windows 95 環境では、再起動しようがどうしようが変わってくれない事があります。ファイルをリネームすると変わるので、Windows 95 のバグみたいです。
TWEAK UI に含まれるツールに、アイコンのキャッシュを飛ばすツールが有るらしいので、それを使って見てはいかがでしょうか。
普段、Windows NT 4.0 を使っているのですが、こちらでは普通起こりません。希に起こっても、再起動で必ず解消します。
参照: [builder:5426] <開発環境> <バグ>
0096 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
BCB3 のバージョン情報でキー名が日本語だと不具合
BCB3は標準でバージョン情報が表示できるようになりましたが、キーの名前がが2バイト文字で終わるとバグるみたいです。(標準のキー名(会社、製品名など)は大丈夫)
とりあえず終わりに「.」を打つことで対処しています。
参照: [builder:4937] <開発環境> <バグ>
0097 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
起動したアプリケーションの終了待ち
WaitForSingleObject という API を使います。
これをキーに過去ログを検索すると、コード例も見つかります。
http://www2.big.or.jp/~osamu/Delphi/search.cgi?key1=WaitForSingleObject
参照: <Windows>
0098 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
BCB1 から BCB3 へ移動したプロジェクトでのプリコンパイルヘッダ利用
BCB1 のプロジェクトをそのまま BCB3 でコンパイルするととても時間がかかります。これは、BCBのプリコンパイルヘッダ機能が生かされていないためです。
Builder1と Builder3でヘッダ情報のパスが違っています。
具体的には、
#include<vcl\header.hpp> // Builder 1
#include<header.hpp> // Builder 3
というように先頭のvcl\が無くなっています。
実際に読み込むファイル自体は同じなのでコンパイル自体は問題なく行うことができます。
結果として大きなヘッダを毎回読んでいる事になります。
次の要領でヘッダを修正してください。
1.フォーム付き(*.dfm)のユニット
(1) ヘッダファイルを開きます。
(2) 先頭の#includeの塊を#includeを1行だけ残して後は全て削除します。
ただし,手作業で意図的に追加した分は残しておきます。
(3) Ctrl+Sでそのヘッダファイルを保存します。
(4) Builderが自動的に必要なヘッダファイルを挿入してくれます。
(5) (2)で残った行で意図的に追加していないヘッダであれば削除します。
(6) Ctrl+Sで(2)で消した分が挿入されます。
2.ファームがないユニット(コンポーネントなど)
(1) ヘッダファイルを開きます。
(2) #include<vcl\〜.h>となっている行が在ればvcl\の部分を削除します。
(3) Ctrl+S等で保存します。
こっちの場合には,自動的に付加してくれないのでヘッダファイル名の大文
字と小文字も同じにする必要があります。プリコンパイルヘッダは相変わら
ず大文字と小文字を区別しているので,db.hppとDB.hppは別物として認識し
てくれます。
※例でも上げていますが,DB.hppからDb.hppにヘッダ名が変わっているの
でDBアプリを作っている人は注意してください。
なお,BCB3で新しくアプリケーションを作る場合には関係ありませんが,C++で書かれたコンポーネントをインストールする際には注意して下さい。
参照: [builder:5373] <開発環境> <C++>
0100 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
最新版クイックレポート(英語)の日本語化
クイックレポートの最新版を、
http://www.qusoft.com/scripts/download.dll
から落としてきて、Delphiにインストールすることができます。
但し、プレビューのボタンなどが英語になります。
以前のバージョン(2.0g)のdfmファイルをlibディレクトリにコピーするだけで、実行時パッケージを使用しない場合に限り、実行時に日本語で表示できます。
これを、設計時や実行時パッケージを使ったときにも日本語で表示させる方法が、[Delphi-ML:20984]に紹介されています。
参照: [Delphi-ML:20984] <データベース> <印刷>
0102 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
親の published プロパティを子クラスで隠蔽する
Object Pascal では、クラスの継承時に親クラスで指定されたアクセス指定子よりも厳しいアクセス指定子をメンバに指定しても無視されてしまいます。従って、親クラスで public に指定されたメンバを子クラスで protected にするようなことは出来ません。
これは、published も同じで、一度公開されたものを元に戻すことはできないのですが、単に「オブジェクト・インスペクタに表示させない」だけならば、「読み込み専用のプロパティ」として再宣言することで、オブジェクトインスペクタに表示しないようにすることができます。
例:AutoScrollプロパティを読み込み専用にする。
THogeScrollBox = class(TScrollBox)
private
function GetAutoScroll: Boolean;
published
property AutoScroll: Boolean read GetAutoScroll;
end;
function THogeScrollBox.GetAutoScroll: Boolean;
begin
Result := inherited AutoScroll;
end;
※当然、イベントも同じ方法で隠すことが出来ます。
参照: [Delphi-ML:21207] <コンポーネント開発>
0103 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
NT のタスクマネージャにアプリケーションのアイコンが表示されない
Delphi で作られるソフトすべてでこの問題が発生します。ちなみに、Delphi 自身も同じ問題を抱えています。
アプリケーションのクラスアイコンをセットしたら表示されるようになると思います。
プロジェクトソースかメインフォームの OnCreate イベントで、
SetClassLong(Application.Handle,
GCL_HICON,
Application.Icon.Handle);
を実行して下さい。
参照: [Delphi-ML:21168] <その他Windows関連> <アプリケーション> <Windows> <バグ> <アイコン>
0104 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
QuickReport で Canvas プロパティを使う
QRPrinter.NewPage は、Windows API とか Printer の NewPage とは若干意味合いが異なって、「新しいページの前に」指定する必要があるようです。座標指定は、0.1mm 単位の座標指定を QRPrinter.XPos()、QRPrinter.YPos()でプリンタの物理座標に変換して指定すれば印刷できると思います。
procedure TForm1.Button1Click(Sender: TObject);
begin
with QuickRep1.QRPrinter do begin
BeginDoc;
NewPage;
Canvas.TextOut(XPos(300), YPos(300), 'test');
EndDoc;
end;
end;
参照: [Delphi-ML:21024] <印刷>
0105 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
DDeClientConv を使ってスタートメニューにアイコンを登録
3.1 のプログラムマネージャのときと同じですね。
DDEClientConv をフォームに置いて以下のようにします。
var strGroup:String;
strExeName:String;
strPrgName:String;
Temp:String;
begin
strGroup := 'グループ名';
strPrgName := 'ソフト名';
strExeName := Application.ExeName;
if not DDEClientConv1.SetLink('PROGMAN','PROGMAN') then begin
ShowMessage('ショートカットの制作に失敗しました。');
end else try
{グループの作成}
Temp := '[CreateGroup(' + strGroup + ')]';
DDEClientConv1.ExecuteMacro(PChar(Temp),False);
{グループの表示}
Temp := '[ShowGroup(' + strGroup + ',1)]';
DDEClientConv1.ExecuteMacro(PChar(Temp),False);
{アイテムの作成}
Temp := '[AddItem("' + strExeName + '","' + strPrgName + '")]';
DDEClientConv1.ExecuteMacro(PChar(Temp),False);
ShowMessage('ショートカットを作成しました。');
finally
DDEClientConv1.CloseLink;
end;
end;
参照: [Delphi-ML:21012] <その他Windows関連> <ShellApi> <Windows> <スタートメニュー> <配布>
0106 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
DLL のロード・アンロードでメモリリーク?
プログラム中でDLLの呼び出し、開放を何度も行っていると、システムモニターの「アロケート済みメモリ」がどんどん増えていきます。
簡単なDLL呼出しプログラムを作って試しましたが、呼出・開放を2018回繰り返すとプログラムがとまります。DLLの内容には関係ありません。
また、NT4/BCB3を使っても同様の現象が発生します。
以下の方法で回避できます。
・インポートライブラリを使ってDLLをリンクする
または
・DLLを作成するときにVCLをリンクしない(DLLでVCLを使っていない場合のみ)
参照: [builder:5705] <バグ> <DLL> <メモリ>
0109 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
サブフォームがアクティブな時はメインフォームのアクセラレータキー・ショートカットキーを無効にしたい
メインフォームのメニューに指定されたアクセラレータキー([File(&F)]とした時の 'F')は、サブフォームがアクティブであっても、そのサブフォームがメインメニューを持たない場合には、メインフォームのメニューを起動してしまいます。
また、メインフォームのメニューに指定されたショートカットキー([Open(&O)... Ctrl+O] とした時の 'Ctrl+O')は、サブフォームがアクティブである場合にも有効です。
これらを回避するには以下のようにします。
function TForm1.Hook(var Message: TMessage): Boolean;
begin
case Message.Msg of
CM_APPKEYDOWN:
Result := True; // ショートカットを無効にする
CM_APPSYSCOMMAND:
Result := True; // アクセラレータを無効にする
else
Result := False;
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.HookMainWindow(Hook);
end;
参照: [Delphi-ML:21685] <フォーム> <メニュー>
0110 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
用紙の印刷可能範囲を求める
Windows API の GetDeviceCaps 関数を使います。
A--I----------------B
| |
J E-------------F |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| G-------------H |
| |
C-------------------D
用紙サイズ ABCD ・印刷可能範囲 EFGH とした場合GetDeviceCaps関数の引数は
A-B が PHYSICALWIDTH
A-C が PHYSICALHEIGHT
E-F が HORZRES
E-G が VERTRES
A-I が PHYSICALOFFSETX
A-J が PHYSICALOFFSETY
となります。
ピクセル単位で値が返されます。
参照: [builder:6063] [builder:6064] <印刷>
0111 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
超高速タイマーコンポーネント(サブミリ秒)
DSPで見つけたのは「TZTimer」コンポーネントでした。
以下 Readme からの抜粋。
----------
Similar to the vcl Ttimer component, but with a resolution of 0.1ms and accurate to 0.015ms.
(略)
Resolution could be improved considerably when the RDTSC (pentium) instruction is utilized, however I had to get this timer working on a 486 also.
It was tested on D2 and D3 for W95, it should also work on D1 and W3.1.
However it will not work in Windows NT (?).
Some observed timing measurements of Tztimer, compared with the (vcl) Ttimer
:
{ evaluation of 60 runs on W95, P133, D3}
interval (vcl) Ttimer Tztimer
100 ms 108 +/- 4 ms 99.3 +/- 0.14 ms
10 ms 59 +/- 5 ms 10.1 +/- 0.07 ms
1 ms 59 +/- 6 ms (!) 1.00 +/- 0.02 ms
0.1ms Not available 0.098+/- 0.015 ms
----------
参照: [Delphi-ML:21828] <System> <コンポーネント >
0112 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
スタートメニューを任意の位置にポップアップさせる
スタートメニューのポップアップは、
procedure PopupStartMenu;
var hTaskBar: Integer;
begin
hTaskBar:= FindWindow(PChar('Shell_TrayWnd'), nil);
SetForegroundWindow(hTaskBar);
PostMessage(hTaskBar, WM_USER + $0104, 0, 0);
end;
でOK。
表示されたメニューを MoveWindow で強制的に動かすには、
procedure MoveStartMenu(Point: TPoint);
var hStartMenu: Integer;
OldRect: TRect;
begin
hStartMenu:=FindWindow('#32768',nil);
GetWindowRect(hStartMenu, OldRect);
MoveWindow(hStartMenu,
Point.X, Point.Y,
OldRect.Right-OldRect.Left, OldRect.Bottom-OldRect.Top,
True);
end;
詳しくはスレッドを追って下さい。
参照: [Delphi-ML:21886] <Windows> <スタートメニュー>
0113 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
MS-Access97 に接続できない
Delphi3.1についているBDE4.5だとまだACCESS97に対応していなかったと思います。
BDE4.51にバージョンアップすると大丈夫だったと思いいます。
詳しくはインプライズのページを見てみてください。
バージョンアップデータが配布されているはずです。
http://www.inprise.co.jp/bde/
参照: [Delphi-ML:21943] <データベース>
0025 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Delphi1.0でHugeポインタを使う
Delphi1.0用のHugeポインタルーチンです。
もう過去の遺物かな?
function HpSubOne( pointer:Pointer ):Pointer;
inline(
$58/ { POP AX }
$5A/ { POP DX }
$2d/$01/$00/ { SUB AX,1 }
$73/$03/ { JNB @@ }
$83/$EA/$08 { SUB DX,8 }
); {@@: }
function HpAddOne( pointer:Pointer ):Pointer;
inline(
$58/ { POP AX }
$5A/ { POP DX }
$40/ { INC AX }
$75/$03/ { JNZ @@ }
$83/$C2/$08 { ADD DX,8 }
); {@@: }
function HpAddWord( pointer:Pointer ; offset:Word ):Pointer;
inline(
$59/ { POP CX }
$58/ { POP AX }
$5A/ { POP DX }
$01/$c8/ { ADD AX,CX }
$73/$03/ { JNC @@ }
$83/$C2/$08 { ADD DX,8 }
); {@@: }
function HpSubWord( pointer:Pointer ; offset:Word ):Pointer;
inline(
$59/ { POP CX }
$58/ { POP AX }
$5A/ { POP DX }
$29/$c8/ { SUB AX,CX }
$73/$03/ { JNB @@ }
$83/$EA/$08 { SUB DX,8 }
); {@@: }
function HpAddLong( pointer:Pointer ; offset:LongInt ):Pointer;
inline(
$59/ { POP CX }
$5B/ { POP BX }
$58/ { POP AX }
$5A/ { POP DX }
$01/$c8/ { ADD AX,CX }
$83/$D3/$00/{ ADC BX,0 }
$B1/$03/ { MOV CL,3 }
$D3/$E3/ { SHL BX,CL }
$01/$DA { ADD DX,BX }
);
function HpSubLong( pointer:Pointer ; offset:LongInt ):Pointer;
inline(
$59/ { POP CX }
$5B/ { POP BX }
$58/ { POP AX }
$5A/ { POP DX }
$29/$c8/ { SUB AX,CX }
$83/$D3/$00/{ ADC BX,0 }
$B1/$03/ { MOV CL,3 }
$D3/$E3/ { SHL BX,CL }
$29/$DA { SUB DX,BX }
);
参照: [Delphi-ML:13257] <メモリ>
0026 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
IDEのコードエディタのデフォルトサイズを変更する
http://www.imasy.or.jp/~i16/soft/
にある dopt100.lzh (DelphiOpt.exe) を使うと簡単にできます。
参照: [Delphi-ML:6104] <開発環境>
0027 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
アイコン状態で起動するアプリ
参照: [Delphi-ML:13173] [Delphi-ML:13185] <バグ> <フォーム>
0028 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Windowsの「ファイルの検索ダイアログ」を表示させる
ファイルの検索ダイアログを出すには、DDEを使います。
フォームに TDdeClientConv コンポーネント(Systemタブ)を乗っけて、
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 フォルダがカレントになります。
ちなみにどこで見つけたかというと、レジストリの
HKEY_CLASSES_ROOT\Directory\shell\find\ddeexec
です。
参照: [Delphi-ML:20377] <その他Windows関連> <ShellApi> <Windows> <ダイアログ>
0029 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
StringGridで選択セルのハイライト表示を無くしたい
>> TStringGridについてなんですが、それにFocusが当たってない場合に一つのセルの色
>> が青で塗りつぶされています。これを塗りつぶされていなくしたいのですが、やり方
>> をご存知の方、ご教授願います。
以下のような方法でできます。
// TForm1のメンバ
StringGrid1Selection:TGridRect;
// implementation
procedure TForm1.FormCreate(Sender: TObject);
begin
StringGrid1Selection:=TGridRect(Rect(-1,-1,-1,-1));
end;
procedure TForm1.StringGrid1Exit(Sender: TObject);
begin
StringGrid1Selection:=StringGrid1.Selection;
StringGrid1.Selection:=TGridRect(Rect(-1,-1,-1,-1));
end;
procedure TForm1.StringGrid1Enter(Sender: TObject);
begin
StringGrid1.Selection:=StringGrid1Selection;
end;
ただし、
TGridRect(Rect(-1,-1,-1,-1))は、ちょっと無理をしているので、
Delphi1.0や、将来のバージョンでは動かないことも考えられます。
> function GridRect(l,t,r,b:LongInt):TGridRect;
> begin
> with Result do begin
> Left:=l;
> Top:=t;
> Right:=r;
> Bottom:=b;
> end;
> end;
を作っておいて
StringGrid1.Selection:=GridRect(-1,-1,-1,-1);
とするのが正攻法です。
参照: [Delphi-ML:17189] <Additional> <コンポーネント >
0030 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
関数のパラメータを省略可能にしたい
R> 関数のパラメータを省略可能なものにしたいのですが、どうしたら良いのでしょうか。
R> また、パラメータの個数を動的に決められるものにするには、どうしたら良いのでしょうか。
Delphiで可変数パラメータを使うには、array of constを使うのが一般的だと思います。
function Test(Values: array of const): string;
var i:Integer;
begin
Result:='';
for i:=Low(Values) to High(Values) do
with Value do
case VType of
vtInteger:
Result:=Result+IntToStr(VInteger);
vtChar:
Result:=Result+VChar;
vtString:
Result:=Result+VString^;
vtObject:
if VObject is TComponent then
Result:=Result+(VObject as TComponent).Name;
...
end;
end;
Test(['Testing ',123,' ',Self);
あってるかな?
ちなみに、C++Builder3/Delphi4 以降では、
procedure AProcedure(a: Integer; b: Integer= 3);
のような形で省略可能なパラメータを定義できるそうです。
参照: [Delphi-ML:17729] [Delphi-ML:19255] <PASCAL>
0031 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TLabelのFontがイタリックだと、AutoSize=Trueで右端が切れる
困ったもんですね。
コードを見るとDelphiのせいではなく、WinAPI側の問題のようですが。。。
とりあえず、Captionの後ろにダミーの空白文字を入れておけばちゃんと表示されます。
Caption:=Caption+' ';
参照: [Delphi-ML:18567] <バグ>
0032 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Delphi1.0のDirectoryListBoxが全角英文字のディレクトリ名で動作不良
参照: [Delphi-ML:11616] <バグ>
0033 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Delphi3 の TStringList.CommaText の不具合
個々のアイテムが空白などを含む場合にのみダブルクオートで括るはずが、必要の無いときまでダブルクオートが使われる。
[対応策]
class.pas の 2253 行目から4行だけ示します。"" で包む必要があるなら
包む処理をする部分です。
S := Get(I);
P := PChar(S);
while not (P^ in [#0..' ','"',',']) do P := CharNext(P);
if (P <> #0) then S := AnsiQuotedStr(S, '"');
// ^^ ここ正しくは P^ 。
// ポインタと文字を比べるんじゃなーい!!
参照: [Delphi-ML:8468] <文字列> <バグ> <PASCAL>
0034 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TIniFileにクオートを含む文字列を与えるときの注意点
APIのGetPrivateProfileStringの仕様として、値文字列の両端がクォート( " or ' )で囲まれていると、これを外してから返すことになっています。
これは、次のような問題を引き起こしかねません。
[Iniファイルの内容]
SomeItem="a bd","de fg","h ijk"
これをTIniFileを使って読み出すと、結果は、
Result='a bd","de fg","h ijk';
となって、両端のクォートだけが取り払われ、おかしなことになってしまいます。
これは、特に、TStringList.CommaTextをTInifileを使って読み書きするときに注意すべき点です。
これを防ぐには、クォートで括られた文字列群をさらに一段、クォートで囲ってしまいます。
[Iniファイルの内容]
SomeItem=""a bd","de fg","h ijk""
これをTIniFileを使って読み出すと、結果は、
Result='"a bd","de fg","h ijk"';
となって、OKです。
コード的には、
IniFile.WriteString('TEST', 'ZZZ', TmpLst.CommaText);
を
IniFile.WriteString('TEST', 'ZZZ', '"'+TmpLst.CommaText+'"');
とします。
ちなみに、TRegIniFileを使う場合には、このようなことは起こりません。
参照: [Delphi-ML:8412] [Delphi-ML:8417] <アプリケーション> <バグ>
0036 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Internetコンポーネントが他のマシンで動かない
Delphiに付属のftpなどのInternetコンポーネントはOCXとして実装されているので、アプリケーションの配布時にOCXもいっしょに配布してやる必要がある。
必要なファイルは、http://www.niftyserve.or.jp/forum/fdelphi/faq/00025.htmにまとめられているので、これらを Install Shield を使って配布することになる。
やり方は、Nifty Serve の FDELPHI、12番会議室の発言番号 01382 に詳しく書かれたとのことで、濱野@MICさんが[Delphi-ML:18991]で引用して解説なさってます。
ispとinstallをキーにして過去ログを検索するといろいろな情報を集めることができる。
http://leed.t.u-tokyo.ac.jp/~takeuchi/delphi/search.cgi
これを嫌って、DSPなどにあるTCI/IP Component Pack(ファイル名:tcpip.zip)などのフリーのコンポーネントを使う方を好む人も多い。
参照: [Delphi-ML:7327] [Delphi-ML:18991] <配布>
0037 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
自作アプリで作ったオブジェクトを他のアプリに貼り付けたい
自作アプリを OLE サーバにして他のアプリに貼り付けられるデータを作成する方法が Inside Windows の、98年4月号「Delphi の神託」に出ているそうです。
参照: [Delphi-ML:18525] <その他Windows関連> <ShellApi> <Windows>
0038 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Visual C++ と Delphi で Exe/DLL のやり取り
Visual C++ のアプリから Delphi のDLLを呼ぶ方法。
Delphi のアプリから Visual C++ のDLLを呼ぶ方法。
両方とも、[Delphi-ML:18743]で関川@数理システムさんが解説して下さってます。
参照: [Delphi-ML:18743] <DLL>
0040 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
フォームの印刷時にComboBoxの内容が印刷されない
Delphi3 になって、フォームの印刷時に TComboBox の内容(テキスト)が印刷表示されなくなりました。
Delphi2 ではちゃんと印刷されます。
kohiroさんが[Delphi-ML:19070]で解法を示してくださっています。
参照: [Delphi-ML:19070] <印刷> <バグ> <フォーム> <Standard> <コンポーネント >
0041 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Delphiのインラインアセンブラで MMX コードを使いたい
堀 浩行さんが、MMX コードを db 擬似命令に変換してくれる Delphi3.1 用アドインを公開してくださっています。
http://www.ingjapan.or.jp/hori/
参照: [Delphi-ML:19070] <開発環境>
0042 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
COMCTL32.DLLのバージョンについて
Delphi3のクールバーなどは、Windows の comctl32.dll のバージョンによって動作したりしなかったりします。
このため、Delphi3.1 では comctl32.dll のバージョンを手軽にチェックするための関数が Comctrls.pas に追加されています。
この関数はヘルプには載っていません。
使い方は、
if (GetComCtlVersion >= ComCtlVersionIE4) then
などとします。
ComCtlVersionIE3 なども定義されています。
参照: [Delphi-ML:19113] <バグ>
0045 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Delphi3.1でオブジェクトインスペクタの表示がおかしい
PCにインストールされているグラフィックカードによって、この現象が起こることがあるようです。
コントロールパネルの[システム]-[パフォーマンス]のグラフィックスのハードウェアアクセラレータを1つ2つ下げてやることで改善されることが多いようです。
参照: [Delphi-ML:19277] <開発環境> <バグ>
0047 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
DelphiのバグをBorlandに報告する
Borlandへのバグ報告は質問扱いにはならず、この場合のインシデントの消費は無いそうです。
電子メールmailto:user@borland.co.jpでも受け付けるそうです。
参照: [Delphi-ML:11475] <バグ>
0048 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TrueTypeフォントからベクタ情報を得る
中村@NECさんのサンプルコードが[Delphi-ML:6963]にあります。
参照: [Delphi-ML:06963] <Windows> <その他コンポーネント関連> <コンポーネント >
0015 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
oDelphi1.0とDelphi2.0/3.0でコンポーネントのソースを共有したい
普通にやると、*.dcr のフォーマットが違うのと、*.dcu の形式が違うという2つの問題が生じます。
この前者を解決する方法が[Delphi-ML:6212]に説明されています。
味噌となるのは、コンポーネントを含むユニットと、コンポーネントを登録するユニット( RegisterComponentsを行うRegister手続きを含むユニット )を分離して記述することです。
実はこれは、VCL自身が採用しているやり方です。
後者については、手ですべての *.dcu ファイルを削除してやるしか方法を思い付きません。
参照: [Delphi-ML:6212] <開発環境> <コンポーネント開発>
0017 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TDriveComboBoxの内容の更新
ネットワークドライブの割当てをした後、TDriveComboBoxにて割当てたドライブを反映させたいのですが、方法が解りません。
DriveComboBox1.TextCase:=DriveComboBox1.TextCase;
たぶんこれが一番楽な方法だと思います。
非常に裏わざ臭いですが。。。
参照: [Delphi-ML:7162] <System> <バグ> <コンポーネント > <ファイル>
0018 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TMemoで入力*行数*を制限したい
アンドゥを犠牲にすれば、
procedure TForm1.Memo1Change(Sender: TObject);
const MaxLineCount=5;
begin
if Memo1.Lines.Count>MaxLineCount then
Memo1.Perform(EM_UNDO,0,0);
Memo1.Perform(EM_EMPTYUNDOBUFFER,0,0);
end;
というのがうまく動きそうです。
参照: [Delphi-ML:5738] <Win95> <Standard> <コンポーネント >
0020 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
設計時にフォームがエラーで読み込めず変更もできない
> フォームのActiveControlプロパティにタブシードで
> 隠れている(つまりアクティブシートでない)コントロール
> にのせてあるものをせっていしてしまい、その後セーブ
> はできたのですが、プロジェクトをロードすると以下のような
> エラーがでて、フォームが二度と表示されなくなってしまいました。
.dfmファイルをDelphiのエディタに放り込んでみましょう。
もしくは[ファイルを開く]で.dfmファイルを読み込みます。
すると、.dfmファイルがテキストとして編集できますので、ActiveControlプロパティの設定部分を書き換えられます。
参照: [Delphi-ML:5705] <開発環境> <フォーム>
0021 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TListViewで列を指定してソート
以下のようにすると簡単にできます。
// ソートに使うColumnを表す。
var ListViewSort:Integer;
// Formのメンバ変数にするのがいいとおもいます。
// 0でソートしない。
// ゼロ以外ならば、Abs(ListViewSort)コラムをキーにする
// 正負で正・逆順ソート
// 汎用的に使える比較ルーチン
procedure TMainForm.ListViewCompare(Sender: TObject; Item1, Item2:
TListItem; Data: Integer; var Compare: Integer);
begin
case ListViewSort of
0: Compare:=0;
1,-1: Compare:=AnsiCompareStr(Item1.Caption,Item2.Caption);
else Compare:=AnsiCompareStr(Item1.SubItems[Abs(ListViewSort)-2],
Item2.SubItems[Abs(ListViewSort)-2]);
end;
if ListViewSort<0 then Compare:=-Compare;
end;
// コラムヘッダがクリックされたらソートしなおす。
procedure TMainForm.ListViewColumnClick(Sender: TObject; Column:
TListColumn);
begin
if Abs(ListViewSort)=Column.Index+1 then begin
ListViewSort:=-ListViewSort;
end else begin
ListViewSort:=Column.Index+1;
end;
ListView.SortType:=stNone;
ListView.SortType:=stData;
end;
参照: [Delphi-ML:11526] <Win95> <コンポーネント >
0022 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
文字列の切り分け
>> 以下のような文字列dumStringにおいて、その中に含まれている引数term3だけを得たい。
>>
>> dumString := 'program term1 term2 term3 term4 term5';
GetTokenIndex(dumString,' ',4)でterm3を返します。
{---------------------------------------------------------------------}
function RemoveToken(var s:string;delimiter:string):string;
var p:Integer;
begin
p:=Pos(delimiter,s);
if p=0 then Result:=s
else Result:=Copy(s,1,p-1);
s:=Copy(s,Length(Result)+Length(delimiter)+1,Length(s));
end;
{---------------------------------------------------------------------}
function GetTokenIndex(s:string;delimiter:string;index:Integer):string;
var i:Integer;
begin
Result:='';
for i:=0 to index do
Result:=RemoveToken(s,delimiter);
end;
{---------------------------------------------------------------------}
RemoveTokenはsをdelimiterで切り分けたときの一番始めの部分を返し、sから取り除きます。sにdelimiterが含まれないときにはs全体を返し、sは空文字列になります。
GetTokenIndexは、sをdelimiterで切り分けたときのindex番目の部分を返します。一番始めの部分はindex=0です。sは変更されません。
indexが範囲外の場合には空文字列が返ります。
delimiterはstring型になっているので、#13#10や' : 'などで切り分けるのもOKです。
参照: [Delphi-ML:12110] <文字列> <PASCAL>
0023 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
TStringGridのソート
> StringGrid上に表示したデータを、ある列(Col)をKEYとしてソートし、
> 再表示させたいのですが、どのようにしたらよいのでしょうか?
エレガントさを求めず、力わざで良ければ、
procedure TForm1.FormCreate(Sender: TObject);
var i,j:Integer;
begin
for i:=0 to StringGrid1.ColCount-1 do
for j:=0 to StringGrid1.RowCount-1 do
StringGrid1.Cells[i,j]:=IntToStr(Random(100));
end;
procedure TForm1.StringGrid1DblClick(Sender: TObject);
var c,min,i,j:Integer;
s:string;
begin
c:=StringGrid1.Col;
for i:=0 to StringGrid1.RowCount-2 do begin
min:=i;
for j:=i+1 to StringGrid1.RowCount-1 do
if StrToInt(StringGrid1.Cells[c, j ]) <
StrToInt(StringGrid1.Cells[c,min]) then
min:=j;
if min<>i then
for j:=0 to StringGrid1.ColCount-1 do begin
s:=StringGrid1.Cells[j,i];
StringGrid1.Cells[j,i]:=StringGrid1.Cells[j,min];
StringGrid1.Cells[j,min]:=s;
end;
end;
end;
としてできますが、セル数が1万を超えると速度的にチョットきつくなってきます。大きなグリッドに対してこのようなことをやりたければTListViewを使うことをお勧めします。
参照: [Delphi-ML:12259] <Additional> <コンポーネント >
0009 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
StringGrid/DBGrid でのセル編集の動作を細かく指定する
下のような関数を使って、TCustomGridで定義されているインプレースエディタを取得すれば、
GetGridEditor(StringGrid1).SelStart:=0;
などとできます。
function GetGridEditor(Grid: TCustomGrid): TCustomEdit;
var i: Integer;
begin
Result:=nil;
for i:=0 to Grid.ControlCount-1 do
if Grid.Controls[i] is TCustomEdit then begin
Result:=TCustomEdit(Grid.Controls[i]);
Exit;
end;
end;
参照: [Delphi-ML:18726] <データベース> <Additional> <コンポーネント > <DataControls>
0010 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
32bitアプリから16bit DLLを呼び出す
英語ですが、
http://www.itecuk.com/delmag/thunk95.htm
に詳しく説明があり、サンプルソースも落としてこれます。
ただし、Delphi Magagine で紹介されている方法を解読するには遙かなる時間がかかるでしょう。まあ、これは参考文献にとどめて置いて、やはり実践的なサンプルコードが必要かと思います。
DSP(http://ring.asahi-net.or.jp/archives/pc/delphi/)の、ThunkDownを使ったサンプル「freres.zip」と「zthunk.zip」や、ひきさんの「TExitWinコンポーネント」(http://www.vector.co.jp/authors/VA009712/take/)が参考になると思います。
また、UserリソースやGDIリソースを得るにはGetFreeSystemResourcesを使うのですが、これは16ビットAPIなため、ThunkDownが必要です。
サンプルまでひきさんが書いてくれました。
#でも Web からは添付文書取れないんだよね。。。(^^;;;
参照: [Delphi-ML:18742] <Windows> <DLL>
0014 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
complib.dllが壊れた!また全部のコンポーネントをインストールするの?!
以下のようなファイルを一つ作っておくと、コンポーネントの再インストールの手間がかなり省けます。
この方法はDelphi1.0/2.0のどちらでも使えます。
----------------------------------------------------
unit MyReg;
interface
{インストールしたいコンポーネント}
{ユニットを列挙する }
uses
Stdreg,
Sysreg,
Sampreg,
Ddereg;
{上記のコンポーネントユニットに }
{対応するリソースファイルがあれば}
{インクルード?する }
{$R D:\DELPHI\LIB\STDREG.DCR}
{$R D:\DELPHI\LIB\SYSREG.DCR}
{$R D:\DELPHI\LIB\SAMPREG.DCR}
{$R D:\DELPHI\LIB\DDEREG.DCR}
procedure Register;
implementation
{個々のユニットのRegister手続きを}
{呼び出す }
procedure Register;
begin
Stdreg.Register;
Sysreg.Register;
Sampreg.Register;
Ddereg.Register;
end;
end.
----------------------------------------------------
他のコンポーネントファイルをすべてアンインストールして、あるいは、まったく新しくライブラリを作って、このMyRegのみをインストールすると、中で呼び出した全てのコンポーネントユニットをすべてインストールしたのと同じ効果が得られます。
御自分で作った/ダウンロードしたユニットもここに加えれば、最インストールの手間はほとんどなくなります。
ただし、個々のコンポーネントがライブラリパス以外に含まれている場合には手動でライブラリパスを入力しなければならなくなるので、MyReg.Pas のようなユニットは、中で参照するユニットと同じディレクトリに置いておくのが良いでしょう。
御参考までに。
参照: [Delphi-ML:7402] <開発環境> <コンポーネント開発>
0006 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
New/Dispose に Pointer 型のポインタを渡すと。。。
New や Dispose は与えられたポインタの型によって、正しく初期化/解放の処理をしますが、これは、Delphi がコンパイル時にポインタの型情報を隠れた引数としてこれらの関数に与えているからです。
従って、差している先のデータがなんであれ、Dispose はポインタの型を信じ込んでデータの解放を試みます。
すると、次のコードはあっという間にメモリを使い果たします。
var pString1:pString;
pUnknown1:pointer;
i:Integer;
begin
for i:=0 to 2000 do begin
New(pString1);
SetLength(pString1^,10000000);
pUnknown1:=pString1;
Dispose(pUnknown1);
end;
end;
ただし、MemGet/MemFree と同様、ポインタの直接差すメモリブロックのサイズに関しては、Dispose が実行時に取得するため、次のコードは、メモリリークを起こしません。
type
TTest = array [0..1000000] of Double;
pTest = ^TTest;
var
pTest1: pTest;
pUnknown1: pointer;
begin
for i:=0 to 2000 do begin
New(pTest1);
pUnknown1:=pTest1;
Dispose(pUnknown1);
end;
end;
参照: [Delphi-ML:18664] <PASCAL> <メモリ>
0007 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Delphi3.0でDLLにバージョン情報が入らない
Delphi3.0で、「新規作成」から DLL を作ると、「バージョン情報を含める」のオプションを指定しても、効果が無い。
これは、.dpr ファイルに {$R*.RES} の一文が入らないために、リソースファイルはできるのに、リンクされないためだ。
uses節の後ろに手動で{$R*.RES} を書いてやればよい。
参照: [Delphi-ML:19012] <その他Windows関連> <アプリケーション> <開発環境> <Windows> <バグ> <DLL>
0000 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
Delphi 1.0 (16bit)で、物理メモリアドレスに直接アクセスする
procedure TForm1.Button1Click(Sender: TObject);
var
TestSelector: word; {新しいセレクタ}
P : ^byte; {メモリマップアクセス用ポインタ}
begin
TestSelector := AllocSelector(DSeg); {新しいセレクタを作成}
SetSelectorBase(TestSelector, $C8000);{ベースアドレスを$C8000に設定}
SetSelectorLimit(TestSelector, $2000);{8KB($2000)確保}
P := Ptr(TestSelector, $0100); {$0100 アドレスにポインタセット}
P^ := $00; {$C8000+$0100=$C8100に$00を書込}
FreeSelector(TestSelector); {セレクタの解放}
end;
参照: [Delphi-ML:18551] <Windows> <メモリ>
0002 D1 D2 D3 D4 D5 D6 D7 3.1 95 98 作成: 1999/02/08 osamu rev 1.1 B1 B3 B4 B5 B6 B7 NT3 NT4 2K XP 更新: 1999/02/08 osamu 編集
NTで他のアプリが開いているファイルを知る
WindowsNT上ならば、
http://www.ntinternals.com/
に NTFilemon があります。
ソースが公開されているので、参考になるかもしれません。
参照: [Delphi-ML:18558] <Windows> <ファイル>
[新規作成] [最新の情報に更新]
How To
Lounge
KeyWords
Osamu Takeuchi osamu@big.or.jp
Tips
Delphi
Home