Archive for the ‘Delphi’ Category

Windowsにはいくつか特別なフォルダが存在している。
そのパスを取得するって場面が結構ある。
たとえば、ログインしているユーザのデスクトップとか、Program Filesとか

  1.  
  2. function getFolderPath:string;
  3. var
  4.   Buf:array[0..512] of char;
  5. begin
  6.   //デスクトップのフォルダのパスを取得する
  7.   SHGetSpecialFolderPath(0, Buf, CSIDL_DESKTOPDIRECTORY,, FALSE);
  8.   Result:=String(Buf);
  9. end;
  10.  
  11.  

CSIDL_・・・は、デスクトップなどは通常で選択できるが、Program FilesなどはSHFolderに定義されているので
uses句にSHFolderを追加すると使用できる。

CSIDL_DESKTOP デスクトップ(ネームスペースのルートを表す仮想フォルダ)
CSIDL_INTERNET Internet Explorer(仮想フォルダ)
CSIDL_PROGRAMS プログラム
CSIDL_CONTROLS コントロールパネル
CSIDL_PRINTERS プリンタ
CSIDL_PERSONAL マイ ドキュメント
CSIDL_FAVORITES お気に入り
CSIDL_STARTUP スタートアップフォルダ
CSIDL_RECENT 最近使ったファイル
CSIDL_SENDTO SendToディレクトリ
CSIDL_BITBUCKET ゴミ箱
CSIDL_STARTMENU スタートメニュー
CSIDL_MYMUSIC マイ ミュージック
CSIDL_DESKTOPDIRECTORY デスクトップへのパス
CSIDL_DRIVES マイコンピュータ
CSIDL_NETWORK マイネットワーク
CSIDL_NETHOOD NetHood
CSIDL_FONTS Fonts
CSIDL_TEMPLATES ドキュメントのテンプレートが保存されているパス
CSIDL_COMMON_STARTMENU All Usersのスタートメニュー
CSIDL_COMMON_PROGRAMS All Usersのプログラム
CSIDL_COMMON_STARTUP All Usersのスタートアップ
CSIDL_COMMON_DESKTOPDIRECTORY All Usersのデスクトップ
CSIDL_WINDOWS WINDOWSフォルダ
CSIDL_PROGRAM_FILES Program Files
CSIDL_MYPICTURES マイピクチャ

クリップボードに文字列をコピーする方法。

uses節にClipbrdを追加する
Clipbord.AsText:=’aaaa’とすれば、aaaaがコピーされた状態になる。

フォームに1つのボタンを配置して、
ボタンを押したときに、文字列をコピーするようにするサンプル

  1. unit Unit1;
  2.  
  3. interface
  4.  
  5. uses
  6. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7. Dialogs,Clipbrd, StdCtrls;
  8.  
  9. type
  10. TForm1 = class(TForm)
  11. Button1: TButton;
  12. procedure Button1Click(Sender: TObject);
  13. private
  14. { Private declarations }
  15. public
  16. { Public declarations }
  17. end;
  18.  
  19. var
  20. Form1: TForm1;
  21.  
  22. implementation
  23.  
  24. {$R *.dfm}
  25.  
  26. procedure TForm1.Button1Click(Sender: TObject);
  27. begin
  28. Clipboard.AsText:=‘AAAAA’;
  29. end;
  30.  
  31. end.

DelphiでOSのバージョンを調べる方法。

今回は、ただ単に表示に使うためなので、文字列を返しているが、
使い方によってはいろいろ応用が利くかも。

  1. function CheckOSVersion:string;
  2. var
  3. OSVer:TOSVersionInfo;
  4. verInfo:String;
  5. begin
  6. OSVer.dwOSVersionInfoSize:=SizeOf(OSVer);
  7. GetVersionEx(OSVer);
  8. if OSVer.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS then
  9. begin
  10. verInfo:=‘Win95 or Win98′;
  11. end
  12. else
  13. begin
  14. if osVer.dwMajorVersion =4 then
  15. verInfo:=‘Windows NT’
  16. else if osVer.dwMajorVersion = 5 then
  17. begin
  18. if OsVer.dwMinorVersion = 1 then
  19. begin
  20. verInfo:=‘Windows XP’;
  21. end
  22. else
  23. begin
  24. verInfo:=‘Windows 2000′;
  25. end;
  26. end
  27. else
  28. verInfo:=‘Unknown Windows version’;
  29. end;
  30. Result:=verInfo;
  31.  
  32. end;

XMLを扱うことが多くなってきているが、
XMLを簡単に表示する方法を模索していた。

まず、考えたことがIEでXMLを表示することを考える。
編集を考えず、表示だけなら一番手軽に扱うことができそうである。
木構造もきれいに表示してくれるし。

ってことで、TWebBrowserをGUIに配置してXMLを表示しちゃえばいいのかなと
単純に思った。

まず、ネットにあるXMLをTWebBrowserのNavigateメソッドで呼んでみる。
確かに、IEの表示と同じになる。

しかし、今回はやりたいことは、XMLを内部で解析した後、
TWebBrowserで表示したいということ。

ってことなので、TXMLDocumentからTWebBrowserにXMLを流し込む
コードを書いてみた。

  1. procedure WBLoadXML(WebBrowser: TWebBrowser; EditedXMLDoc: TXMLDocument) ;
  2. var
  3. HTMLDoc : Variant;
  4. XMLDoc : IXMLDOMDocument;
  5. XSLDoc : IXMLDOMDocument;
  6.  
  7. begin
  8. WebBrowser.Navigate(‘about:blank’);
  9. HTMLDoc := WebBrowser.Document;
  10.  
  11. //ここ↓がないと1回目はうまく行っても、次からうまくいかず。。。
  12. while WebBrowser.ReadyState < READYSTATE_INTERACTIVE do
  13. Application.ProcessMessages;
  14.  
  15.  
  16. if not VarIsNull(HTMLDoc) then
  17. begin
  18. XSLDoc := CoDOMDocument.Create; //uses MSXML
  19. if XSLDoc.load(‘res://msxml.dll/defaultss.xsl’) then
  20. begin
  21. XMLDoc := CoDOMDocument.Create;
  22. EditedXMLDoc.Active := true;
  23. if XMLDoc.loadXML(EditedXMLDoc.xml.Text) then HTMLDoc.write(XMLDoc.transformNode(XSLDoc));
  24. HTMLDoc.Close;
  25. end;
  26. end;
  27. end;

これでできた。

GUI部品でブラウザ機能を持たせようとしたときに
まず、考えるのはTWebBrowserではないでしょうか。

URLさえ渡してしまえば、そのページが表示できらク。
かなり簡単♪

しかし、プログラム内部で作成したHTMLコードを流し込もうとしても
そのようなメソッドがパっと見なさそうである。

そんなわけで、流し込むコードを書いてみた。

  1. procedure WBLoadHTML(WebBrowser: TWebBrowser; HTMLCode: string) ;
  2. var
  3. stringList: TStringList;
  4. ms: TMemoryStream;
  5. webDoc:OleVariant;
  6. begin
  7. WebBrowser.Navigate(‘about:blank’);
  8.  
  9. while WebBrowser.ReadyState < READYSTATE_INTERACTIVE do
  10. Application.ProcessMessages;
  11.  
  12. if Assigned(WebBrowser.Document) then begin
  13. stringList := TStringList.Create;
  14. memoryStream := TMemoryStream.Create;
  15. try
  16. stringList.Text :=HTMLCode;
  17. stringList.SaveToStream(ms);
  18. memoryStream.Seek(0, 0);
  19. (WebBrowser.Document as IPersistStreamInit).Load(TStreamAdapter.Create(memoryStream));
  20. finally
  21. memoryStream.Free;
  22. stringList.Free;
  23. end;
  24. end;
  25. end;

これで、プログラム内部で書いたHTMLを流し込むことができる。

最近、Windowsでの作業がちょこっと増えて、
またまたDelphiを触っています。
#いい加減.NETに移行しなさいって感じですが。。。

今回行ったこと。

TListViewに動的にカラムを追加し表示する。
列の項目が毎回変わり、どうしても動的に追加する必要があった。

  1. ///////////////////////////////
  2. /// 複数のTStringList(Name,Valueで管理されている)が入ったTObjectListから
  3. /// keyStrList(ただの文字列リスト)に入っている順でカラムを追加して
  4. /// そのキーにマッチした項目をカラムに表示する
  5. ///////////////////////////////
  6. procedure ShowListViewAddColumn(objectList:TObjectList;listView:TListView;keyStrList:TStringList);
  7. var
  8. I,J: Integer;
  9. ListItem:TListItem;
  10. Column:TListColumn;
  11. begin
  12. //リストビューを空にする  
  13. listView.Clear;
  14. //リストビューに対して、全カラムを削除する  
  15. if listView.Columns.Count > 0 then
  16. begin
  17. for I := listView.Columns.Count - 1 downto 0 do
  18. begin
  19. listView.Columns.Delete(I);
  20. end;
  21. end;
  22.  
  23. //keyStrListを元にカラムを追加  
  24. for I := 0 to keyStrList.Count - 1 do
  25. begin
  26. Column:=listView.Columns.Add;
  27. Column.Caption:=keyStrList.Strings[I];
  28. Column.Width:=ColumnTextWidth; //←項目の文字列の長さに合わせる ※1  
  29. end;
  30.  
  31. //各値をカラムに設定  
  32. for I := 0 to objectList.count - 1 do
  33. begin
  34. ListItem:=listView.Items.Add;
  35. for J := 0 to keyStrList.Count - 1 do
  36. begin
  37. if J=0 then
  38. ListItem.Caption:=TStringList(objectList.Items[I]).Values[keyStrList.Strings[J]]
  39. else
  40. ListItem.SubItems.Add(TStringList(objectList.Items[I]).Values[keyStrList.Strings[J]]);
  41. end;
  42. end;
  43. end;

※1
このなかで、一番はまったのは、自動で追加したTListColumnの横幅。
文字列が固定ならなんとかなるけど、基本異なるので。。。
TListColumnにAutoSizeというプロパティがあって設定したけど、うまくいかない。。。

Googleで検索してみると、同じ現象になっている人がいて、質問掲示板に書き込まれていた。
その質問に対する答えが「TListCoumn.Widthをヘルプで調べてみなさい」だった。

さっそく調べてみたが(BDS2006 Professional)、これと思われる部分がないぞ。。。
で、ふとDelphi6のヘルプかと思って調べてみたら、ビンゴ!
目的の回答がありました。

listColumn.Width:=-1 //項目のテキストの長さにより幅が決定
listColumn.Width:=-2 //見出しに合わせて幅が決定

次の書き方でも等価
listColumn.Width := ColumnTextWidth
listColumn.Width := ColumnHeaderWidth

これで実現できた。

Formに対して、Explorerからファイルおよびフォルダをドラッグアンドドロップで受け取れるようにする

  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3. //ドラッグアンドドロップの受付を許可する
  4. DragAcceptFiles(Handle,true);
  5. end;
  6.  
  7. //ドロップしたというメッセージをハックする
  8. procedure TForm1.WMDropFiles(var Msg: TWMDROPFILES);
  9. var
  10. DropCount:integer;
  11. pFileName:array[0..255] of char;
  12. I: Integer;
  13. begin
  14. //ドロップしたファイルの個数を取得する
  15. DropCount:=DragQueryFile(msg.Drop,$ffffffff,nil,0);
  16. ShowMessage(IntToSTr(DropCount));
  17.  
  18. try
  19. for I := 0 to DropCount - 1 do
  20. begin
  21. //ドロップしたファイルをすべてダイアログで表示
  22. DragQueryFile(msg.Drop,i,pFileName,SizeOf(pFileName));
  23. ShowMessage(pFileName);
  24. end;
  25. finally
  26.  
  27. end;
  28. end;

上記二つのメソッドを実装することによって
フォームにドラッグアンドドロップを受け付けるようになる。

Delphiである処理時間を計測するツールを作ってみた。
そのときのメモ。

使用する関数は、以下の通り

function GetTickCount:DWORD;

この関数は、Windowsを起動してからの経過時間を返す単純なもの。
実装は以下の通り。
(ボタンが押されると計測開始と想定)

  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3. StartTime, EndTime:integer;
  4. ResultTime:double;
  5. begin
  6. //スタート時間取得
  7. StartTime := GetTickCount;
  8.  
  9. {計測したい処理}
  10. //終了した時間取得
  11. EndTime:=GetTickCount;
  12.  
  13. //スタートと終了時間の差が処理時間
  14. //msecオーダーのため、secオーダーへ変更
  15. ResultTime:=( EndTime - StartTime ) / 1000;
  16.  
  17. {処理時間を表示する記述}
  18. end;

TListViewで遊んでみた。

Macで開発するのが多いため、DelphiではあまりViewに凝ったアプリを作っていなかった。
頭がCocoaのNSTableViewだったため、いろいろなところではまった。

一番、はまったのはプロパティの設定。
デフォルトでは、まったくっていっていいほど使えなかった。

やったこと。

TStringListを用意して、NameとValueで値を管理
(NSDictionaryのように管理している)

で、各値をTListViewに表示するといった内容である。

まずは以下のように設定変更
viewStyle:=vsReport・・・テーブルっぽく
rowSelect:=true・・・これで行すべてが選択可能に
readOnly:=false・・・リスト上で内容を編集したっかったので。。。

が、ここに落とし穴。
リスト上でsubItem(2つ目以降のカラム)の編集ができない・・・
ググッた結果、subItemはリスト上で編集するのは無理らしい。どんなにがんばってもTListViewだけでは無理だそうだ。

で、代替案として2つ目以降のカラムにeditを貼り付けて、それっぽく動かすんだそうな。。。
#なんとスマートじゃないんだ。。。(あくまでも私の主観です)

しかたないので、
CocoaのCoreDataが作るViewを作ってみた。
つまり、リストで選択した項目をリストのほかに配置したTEditで編集するというもの。

これで、大体できた。

それから、アプリをいじっていて気づいたこと。
「あれ、TEditにフォーカスが移ると、リストの選択外れてる。。。」

まじっすか。。。

で、探し当てたプロパティがこれ
HideSelection:=false;

これで、フォーカス外れても、灰色で選択状況がわかるようになった。
ふぅ、やっぱ慣れていない環境は大変だ。

久々にDelphi触っていて、基本的なところで躓いたので、メモ

【やりたいこと】
フォルダを削除したい(フォルダにファイル、フォルダを含んでいても削除)

【実装】
SHFileOpStructを使用して、実装を行う。このAPIを使用すると
簡単に削除できると調べたら見つかったので。。。

  1. procedure TForm1.AllFileDeleteInDirectory(folderPath:string);
  2. var
  3. SHFileOpStruct: TSHFileOpStruct;
  4. begin
  5. with SHFileOpStruct do
  6. begin
  7. Wnd:=0;
  8. wFunc:=FO_DELETE;
  9. pFrom:=PAnsiChar(folderPath+#0);
  10. pTo:=nil;
  11. fFlags := FOF_SILENT or FOF_NOCONFIRMATION or FOF_NOCONFIRMMKDIR or FOF_NOERRORUI;
  12.  
  13. end;
  14.  
  15. SHFileOperation(SHFileOpStruct);  // APIを呼び出す。
  16.  
  17. end;

【ポイント】
pFrom:=PAnsiChar(folderPath+#0);
この行に尽きました。
はじめは、ここをPChar(folderPath)と書いていました。
うまくいく場合もあれば、失敗するときもあり。

うまくいかない場合のエラーは1026となる。

ここらへんのキーワードから調べてみると、
SHFileOpStructは、「¥0¥0」で文字列を終わられせないと正しく認識しないらしい。
PCharはnull文字で終わるポインタだったので、¥0がひとつしかついていないのではないかと考えた。
なので、明示的に¥0をつけてあげる事で解決した。

もう少し、動かしてみて経過を見てみることにしよう。

Delphi参考資料
DELPHI2009HANDBOOK―Delphi最新プログラミングエッセンス