shikaku anime トップページへ shikaku animeお気楽プログラム shikaku anime超ローテクDelphi講座 shikaku anime超低空飛行DelphiTip shikaku anime世界のVCL
shikaku anime復活畳の部屋 shikaku animeぶつぶつの部屋 shikaku animeおすすめLINK shikaku anime自己紹介 shikaku animeたたみやさんの伝言板
 

超低空飛行DelphiTips

 

 かなりローテクですが、よく使うものを初心者向けとして書いています。
みなさんのお役に立てるかどうか(^^;;)
もっともっとも〜〜っと高等な技術をお求めの方は、Delphi Acid Floorへどうぞ(^^;;)

 

 


ファイル名からディレクトリ名だけ取り出す

ExtractFileDirを使います。

Var DirName:String;
Begin
 DirName := ExtractFileDir('d:\temp\test\test.txt');
End;

とするとDirNameには、d:\temp\test\が入ります。
ExtractFilePathもありますが、ExtractFileDirはマルチバイト文字にも対応しているので
日本語を使ったディレクトリを扱う場合などは、こちらの方が良いでしょう。


ドラッグアンドドロップで受けたファイル名を取得

USE節にshellapiを追加します。
private宣言の中に以下を追加します。
procedure WMDropFiles(var Msg:TWMDropFiles);message WM_dropfiles;

その後
implementation

{$R *.DFM}

などの後に以下の文を追加します。
procedure tform1.WMDropFiles(var Msg:TWMDropFiles);
 var fname:array[0..255]of char;
    myfilename:string;
begin
 dragQueryfile(msg.drop,0,fname,256);
//fnameにファイル名を入れる
 dragfinish(msg.drop);
 myfilename:=strpas(fname); 
//string型の変数へ代入
end;

あとフォームのonshowイベントの中などに
 dragacceptfiles(handle,true);
も忘れずに追加しましょう。


コマンドライン引数(コマンドラインオプション)

何かのプログラムを実行するときに付けて、そのプログラムの動作を変えるものです。
例えばスクリーンセーバーなどでは、/Aはパスワードの変更、/Pはプレビュー、/Cは設定ダイアログの表示
などとなります。
DelphiではParamcountでコマンドラインパラメータの数を、
Paramstr()で、()内の数字番目のパラーメーターを文字で得られます。
ちなみに0番目はプログラム自身の名前が入っています。


アイコンやショートカットにドラッグアンドドロップされたファイル名を取得

 これも結局は上記のコマンドラインオプションと同じものです。
ドラッグアンドドロップといっても、見かけ上はアイコンにアイコンを落としているようには見えますが、
実際での動作はms-dos時代のプログラムの実行方法と同じことをやっています。
これをMS-DOSプロンプト上で同様の操作をするとこうなります。

c:¥>なんとか.exe c:¥test.txt c:¥abc.dat

のようにプログラム本体がなんとか.exeで、それにD&Dされたのが、c:¥test.txtc:¥abc.datです。
「送る」でプログラムを実行した場合も同様です。

この時のコマンドライン引数は、
0番・・・なんとか.exe(プログラム自身の名前)
1番・・・c:¥test.txt
2番・・・c:¥abc.dat
となります。

実際の使い方です。
フォームにmemoコンポを置き、下の文をFormのOnShowイベントのプロシージャ内に書いてください。
コンパイル後そのプログラムにD&Dしてみてください。

if paramcount >=1 then //何か引数があるか確認
begin
 for i:=1 to paramcount do
//その数だけの
 memo1.lines.add(paramstr(i));
end;


ファイルサイズの取得

ファイルサイズを取得して、結果を文字で返す関数です。
filesizeという関数もあるのですが、ヘルプを見ると。。。

function FileSize(var F): Integer;

説明

FileSize を呼び出すと,ファイルのサイズがわかります。
FileSizeを使うには,ファイルを開いておかなくてはなりません。
ファイルが空の場合,FileSize(F) は 0 を返します。F はファイル変数です。

注意

FileSize はテキストファイルに対しては使えません。

などなど面倒なので、findfirst関数を利用してファイルサイズを取得します。
Fnameにはフルパスでファイル名を指定します。

function GetfileSize(Fname:string):Cardinal;
 var
sr:tsearchrec;
begin
 if
findfirst(Fname,faanyfile,sr)=0 then
  begin
   result := sr.size;
   findclose(sr);
 end else result:=0;
end;


数値のファイルサイズを文字へ

上からの続きです。
iにはファイルサイズが入っているとします。
FsizeはString型とします。
そしてiをKbyteまたはMbyteで割り、結果が実数になるのでFormatFloatを使います。

Fsize := FormatFloat( '#,##0.00MB',i/1048576)// メガバイト単位
Fsize := FormatFloat( '#,##kB',i/1024);     
// キロバイト単位
Fsize := FormatFloat( '#,##byte',i);       
// バイト単位


chrの配列からString型へ代入

ドラッグアンドドロップで受けたファイル名を取得でも使っていますが、
StrPas関数を使います。

Var S:string;
Begin
 s := strpas(charの配列変数);
End;


Win32API(DLL)へ文字を渡す

Win32APIの関数へ文字列を渡す場合は、C言語ベースなので互換性は無く、
DelphiにあるString型の文字変数を直接渡すことはできません。
そこでPchar(string型の変数)と書いて文字列へのポインターを渡します。
またWin32api関数内で、' 'で囲んで直接文字を渡してもOKです。
他にもchrの配列を渡すことも可能です。
ただし文字の長さは255文字までです。
これらのことは、Unlha32.dllなどのDLLへ文字を渡すときも同様です。


DLLの使い方

DLLを使うときには静的リンクと動的リンクの方法があります。

静的リンク

これは使いたいDLLが絶対存在すると仮定した使い方です。
DLL の手続きや関数を使うには,external 宣言を使って手続きや関数をインポートします。

Unlha32.dllを使った例をあげます。Unlha32.dllのバージョン情報を取得する関数です。
Unlha32.dll付属のAPI.TXTには
WORD WINAPI UnlhaGetVersion(VOID)とあります。
UnlhaGetVersion関数は、引数無し(VOID)で戻り値はWord型(0〜255)の数値です。

external 宣言Unlha32.dll という DLLから UnlhaGetVersion という関数を読み込みます。
ここで気をつけなければならないのはname以降の関数名は1字一句間違えてはいけません。
大文字と小文字も区別されます



 新規フォームをにボタンをひとつ貼り付けて、そのイベントハンドラに記述します。
 赤い部分を自前で書きます。

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Classes,Graphics, Controls, Forms, Dialogs,
 StdCtrls;

type
 TForm1 = class(TForm)
 Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;

var
 Form1: TForm1;
 function GetVersion: Word; stdcall; external 'Unlha32.dll' name 'UnlhaGetVersion';

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
 begin
  showmessage(floattostr(GetVersion/100));
 end;

end.

これをそのまま書いて実行すると、Unlha32.dllのバージョンが表示されます。
ただしUnlha32.dllが存在し、かつパスの通った位置にあればの話ですが。。。
もしどちらかがかけていると、エラーが出てどっかーん!!です(^^;;)
この方法は簡便ですが、お勧めできません。
面倒でも次の動的リンクを使って書きましょう。

 

動的リンク

これはいったんLoadLibrary関数を使ってDLLがあるかないか確認してから使います。
LoadLibraryが0以外の有効なハンドルを返したなら、目的のDLLは存在し
メモリーに読み込まれます。
その後GetProcAddress関数を使い、DLL内の目的の関数のアドレスを取得し、
それを使って関数を実行します。


unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Classes,Graphics, Controls, Forms, Dialogs,
 StdCtrls;

type
 TGerVersion= function : word;

type
 TForm1 = class(TForm)
 Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;

var
 Form1: TForm1;
 GerVersion : TGerVersion;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
 var DLLHandle:THandle;
    Version : string;
begin
 DLLHandle := LoadLibrary('Unlha32.dll'); //DLLをロードしてハンドルを取得
 if DLLHandle <> 0 then
 begin
  @GerVersion := GetProcAddress(DLLHandle, 'UnlhaGetVersion'); 
//DLLのハンドルを使って
  if @GerVersion <> nil then                      
//関数のアドレスを取得
   begin
    Version := floattostr(GerVersion / 100);
   end else Version :='関数アドレスの取得に失敗しました';
   FreeLibrary(DLLHandle); 
//使ったら必ずDLLを開放する
 end else Version :='DLLがありません';
 showmessage(Version);
end;

end.

どうでしょうか?
ちょっと面倒ですが、こちらの使い方のほうが安全なので
普通はこちらを使うほうがよいでしょう。


拡張子に頼らず何のファイルかを判断

通常Windowsでは、拡張子を見てファイルの判断をしますが、これが当てにならない場合が多々あります。
こんな時は直接ファイルを読み込んで、そのファイルヘッダから何のファイルか判断します。
といってもそれぞれファイルフォーマットが違うので、それなりに調べなければなりませんが

よくあるもので

Bitmap 先頭の2バイトがBMになっている
EXE     〃     がMZ
ZIP         〃     がPK

などがあります。
興味のある方は、The Programmer's File Format Collectionへ行ってみてください。
大抵の資料は揃っています。

例文
Procedure CheckHeader(Const FileName:string);
 Var TF:TfileStream;
   Buf:array[0..2]of char;
begin
 TF:=TFileStream.create(FileName,fmOpenRead); 
 TF.Position := 0;
 TF.Read(Buf,2);
 If (buf[0]='B')and(buf[1]='M') then showmessage('Bitmap');
 If (buf[0]='M')and(buf[1]='Z') then showmessage('Exe');
 If (buf[0]='P')and(buf[1]='K') then showmessage('Zip');
 TF.Free;
end;


IMEの制御

いやいや、最近これにはまってネット上のいろいろな資料を漁りました。 
しかしながらDelphi関連は少なくVC++ばかりで、少しCも読めるようになりました(^^;;)
IMEを制御するときのミソというか、直接IMEのハンドルを取得してそれに対して
メッセージを投げるか、APIを実行するのかと思ったら違うんですね〜。
IMEは「文字入力可能なコントロールに対して個別にハンドルを割り当てる」らしいです。
ということで、ここから書くサンプルでは、TWinControlから派生したコンポのハンドルを
使用するものとして書いていきます。
自分が試したときはTeditを使いました。
またUSE節には、IMMを追加しておきます。

 

IME ウィンドウ(ツールバー)の表示・非表示

procedure ShowIme(const winctr :TWinControl;const valeu:boolean);
 var Imc:HIMC;
begin
  winctr.SetFocus; //対象のコントロールにフォーカスを当てる
  Imc := ImmGetDefaultIMEWnd(winctr.Handle);  //winctrに対するIMEのハンドルを取得
  if valeu then SendMessage(Imc, WM_IME_CONTROL, IMC_OPENSTATUSWINDOW, 0)
  else SendMessage(Imc, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0);
      //SendMessage()で表示、非表示を切り替える
end;

valeu
にはtrue(表示)false(非表示)を渡します。

しかしながら、WinXPのIME2002では非表示にならず、表示サイズが小さくなるだけでした。
その他のバージョンのIMEやAtokでは効くみたいです。

 

IMEのOn Off

procedure ImeOnOff(const winctr :TWinControl; const status:boolean);
 var
Imc:HIMC;
begin
 winctr.SetFocus;
 if status then Win32NLSEnableIME(winctr.Handle,true) else Win32NLSEnableIME(winctr.Handle,false);
                            // MS-IMEでは上記のようにします。win98以降のもの?
 Imc := ImmGetContext(winctr.Handle);
 if status then ImmSetOpenStatus(imc,true) else ImmSetOpenStatus(imc,false);
 ImmReleaseContext(winctr.Handle, Imc);
//その他のIME(Atokなど)では、上の3行でOKみたいです。
end;

statusにはtrue(On)false(Off)を渡します。

 

入力文字種の切り替え

Type
 TImeCharMode = (ZenHiragana,ZenKatakana,ZenEisu,HanKatakana,HanEisu,Kigou);
//事前に定義しておきます。

procedure SetInputCharMode(const winctr :TWinControl;const ImeCharMode:TImeCharMode);
var
  Imc          : HIMC;
  dwConversion : DWORD;
  dwSentence   : DWORD;
begin
  winctr.SetFocus;
  Imc := ImmGetContext(winctr.Handle);
  ImmGetConversionStatus(Imc,dwConversion,dwSentence);
  If (dwConversion And IME_CMODE_ROMAN) = IME_CMODE_ROMAN Then
    dwConversion := IME_CMODE_ROMAN else  dwConversion := 0;

  case ImeCharMode of
    ZenHiragana: dwConversion := dwConversion or
                       IME_CMODE_JAPANESE or     //日本語
                       IME_CMODE_FULLSHAPE;      //全角
    ZenKatakana: dwConversion := dwConversion or
                       IME_CMODE_JAPANESE or     //日本語
                       IME_CMODE_FULLSHAPE or    //全角
                       IME_CMODE_KATAKANA;       //カタカナ
    ZenEisu: dwConversion := dwConversion or
                       IME_CMODE_FULLSHAPE;      //全角英数字
    HanKatakana: dwConversion := dwConversion or
                       IME_CMODE_JAPANESE or     //日本語
                       IME_CMODE_KATAKANA;       //カタカナ
    HanEisu: dwConversion := dwConversion or
                       IME_CMODE_ALPHANUMERIC;   //英数字
    Kigou :  dwConversion := dwConversion or
                       IME_CMODE_CHARCODE;   //  記号 = &H20
  end;

  ImmSetConversionStatus(Imc,dwConversion,dwSentence);
  ImmReleaseContext(winctr.Handle, Imc);
end;


現在の入力文字種判定

Type
 TImeCharMode = (ZenHiragana,ZenKatakana,ZenEisu,HanKatakana,HanEisu,Kigou);
//事前に定義しておきます。

Function  GetInputCharMode(const winctr :TWinControl):TImeCharMode;
var
Imc:HIMC;
  dwConversion : DWORD;
  dwSentence   : DWORD;
begin
  winctr.SetFocus;
  Imc := ImmGetContext(winctr.Handle);
  ImmGetConversionStatus(Imc,dwConversion,dwSentence);
 case  dwConversion of
  IME_CMODE_JAPANESE or IME_CMODE_FULLSHAPE:result:= ZenHiragana;
  IME_CMODE_JAPANESE or IME_CMODE_FULLSHAPE or IME_CMODE_KATAKANA:result:= ZenKatakana;
  IME_CMODE_FULLSHAPE:result:= ZenEisu;
  IME_CMODE_JAPANESE or IME_CMODE_KATAKANA :result:= HanKatakana;
  IME_CMODE_ALPHANUMERIC:result:= HanEisu;
  IME_CMODE_CHARCODE:result:= Kigou;
 end;

  ImmReleaseContext(winctr.Handle, Imc);
end;


 現在の入力モードの判定(ローマ字・かな入力)

type
 TImeInputMode = (ROMAN,HIRA);
//事前に定義しておきます。

Function  GetInputMode(const winctr :TWinControl):TImeInputMode;
var
Imc:HIMC;
  dwConversion : DWORD;
  dwSentence   : DWORD;
begin
  winctr.SetFocus;
  Imc := ImmGetContext(winctr.Handle);
  ImmGetConversionStatus(Imc,dwConversion,dwSentence);
  If (dwConversion And IME_CMODE_ROMAN) = IME_CMODE_ROMAN Then  result:=ROMAN  else result:=HIRA;
  ImmReleaseContext(winctr.Handle, Imc);
end;


すでにIMEがオープンしているか

Function IsImeOpened(const winctr :TWinControl):boolean;
 var
Imc:HIMC;
begin
  winctr.SetFocus;
  Imc := ImmGetContext(winctr.Handle);
  result:= ImmGetOpenStatus(imc);
  ImmReleaseContext(winctr.Handle, Imc);
end;


現在のIMEの名前を取得

Function  GetImeName(const winctr :TWinControl):string;
var
kl:HKL;
  Imc:HIMC;
  imename:array[0..255]of char;
begin
  winctr.SetFocus;
  Imc := ImmGetContext(winctr.Handle);
  if not ImmGetOpenStatus(Imc)then ImmSetOpenStatus(Imc, TRUE);
  Kl :=  GetKeyboardLayout(0);
  if not ImmIsIME(Kl)then result:='' else
  ImmGetDescription(Kl,imename, sizeof(imename));
  result:= strpas(imename);
  ImmReleaseContext(winctr.Handle, Imc);
end;

 

使用可能な全てのIMEの名前を取得してComboBoxへ入れる 

これは手抜きで、Delphiの関数を使ってます(^^;;)

procedure Tform1.Button1Click(Sender: TObject);
begin
 ComboBox1.Items.AddStrings(screen.Imes);
end;

上記で取得した複数のIMEを切り替える。

対象はForm上にあるEdit1とします。 

procedure Tctrl_f.ComboBox1Click(Sender: TObject);
begin
 edit1.ImeName:= ComboBox1.Items.Strings[ComboBox1.itemindex];
end;


2008 SugaharaTatamiten AllrightRecived