Home New Help Edit

Delphi memo

2008年4月30日〜
Suns & Moon Laboratory

Delphi FireMonkey memo はこちら
https://github.com/mikekoma/DelphiLabo テストコード置き場

あちこち勘違いな所もあったりするので、ごめんなさい。と、逃げをうっておく。

https://docwiki.embarcadero.com/ 本家DocWiki
RAD Studio 11 Alexandria ヘルプ
RAD Studio 10.4 Sydney ヘルプ


Assign

Delphi Assign使い方


10.1Berlin 内部データ形式

符号付き
Int8,ShortInt符号付き8bit
Int16,SmallInt符号付き16bit
Int32,LongInt符号付き32bit
Int32,Integer符号付き32bit
Int64符号付き32bit

符号無し
UInt8,Byte符号無し8bit
UInt16,Word符号無し16bit
UInt32,LongWord符号無し32bit
UInt64符号無し32bit

要注意 Win64のExtendedは、Win32よりも精度が悪い
Win32Win64
Double8byte8byte
Extended10byte8byte

volatile

volatile 属性は、別のスレッドにより変更される可能性があるフィールドにマークを付けて、そのフィールドの値をレジスタまたは別の一時メモリ領域にコピーする操作がコード生成の際に最適化されないようにするために使用されます。
RADStudio⇒Alexandria⇒コンパイラ属性

type TMyClass = class(TObject) private [volatile] count:integer; end;

列挙型

CでいうところのEnum
type TGohan = (ghAsaGohan,ghHiruGohan,ghBanGohan);
この場合、Ord(ghAsaGohan)は0、Ord(ghHiruGohan)は1を返す。

名前衝突の回避
例えばghAsaGohanの名前衝突が起きた場合。
Gohan:=MyUnit.ghAsaGohan;


参照 単純型 → 列挙型

集合


type TGohan = set of (ghAsaGohan,ghHiruGohan,ghBanGohan); if ghAsaGohan in gohan then nattou.mazemaze;

空集合は[]で表す。


Grid.Options := Grid.Options + [goEditing];

構造体,Record

type PNS_HEAD = ^NOTE_TO_SH; NOTE_TO_SH = record lMitei2Size:Longint; lVerUpStartAddress:Longword; cYobi:array[0..31] of char; end;

レコードと、レコードを返す関数
type TIxSerial = record Used: boolean; Value: UInt64; end; function IxSerial(val: UInt64; Used: boolean): TIxSerial; begin Result.Value := val; Result.Used := Used; end;

Generics

ジェネリックスの概要

定義の例

type TIxBuffer<T> = class(TObject) protected FValue: T; public procedure Push(Value: T); function Pop: T; end; function TIxBuffer<T>.Pop: T; begin Result := FValue; end; procedure TIxBuffer<T>.Push(Value: T); begin FValue := Value; end;

使い方例

var buf_str: TIxBuffer<string>; buf_int: TIxBuffer<integer>; begin buf_str := TIxBuffer<string>.Create; try buf_str.Push('abcd'); memo(buf_str.Pop); finally buf_str.Free; end; buf_int := TIxBuffer<integer>.Create; try buf_int.Push(10); memo(buf_int.Pop.toString); finally buf_int.Free; end; end;

System.Generics.Collections

汎用コンテナユニット
http://docwiki.embarcadero.com/Libraries/Tokyo/ja/System.Generics.Collections

TObjectXXXは、リスト(スタック,キュー)から削除した時に、オブジェクトを自動的に開放する。

TArray
TEnumerable

TDicitionaryキー-値ペア
TListインデックスによってアクセス出来る順序付きリスト
TQueueFIFO
TStackFILO
TObjectListオブジェクトのメモリ管理してくれる。Delete,Remove,Clear,Freeでオブジェクト解放。
TObjectDictionary
TObjectQueue
TObjectStack

スレッド間データ受け渡し

uses Generics.Collections; var MemoQueue: TThreadedQueue<string>; begin MemoQueue := TThreadedQueue<string>.Create(100, 1, 1);//キュー深さ、Pushタイムアウト、Popタイムアウト。タイムアウト指定無しはタイムアウト無しになる。 MemoQueue.PushItem('hello'); //別スレッド var str:string; begin if MemoQueue.PopItem(str)=wrSignaled then Memo1.Lines.Add(str);//wrSignaledの時str有効。タイムアウト時間はCreate時指定。

uses Generics.Collections; var xlist:TList<integer>; begin xlist:=TList<integer>.Craete; try xlist.Add(1); xlist.Add(2); xlist.Add(3); for i=0 to xlist.Count-1 do begin memo(StrToInt(xlist[i])) end; finally xlist.Free; end;

TDictionaryで列挙してみる

var pairs: TDictionary<string, integer>; val: TPair<string, integer>; i:integer; begin pairs := TDictionary<string, integer>.Create; pairs.Add('name1', 10); pairs.Add('name2', 220); pairs.Add('name3', 11); pairs.Add('name4', 310); //ペアの列挙 for val in pairs do memo(val.Key + '=' + IntToStr(val.Value)); //値の列挙 for i in pairs.Values do memo(IntToStr(i)); end;

ソートTArray.Sort

動的配列をソートする
山本隆の開発日誌 - TArray.Sortで配列をソートする
System.Generics.Defaults.TComparer.Compare
uses System.Generics.Collections, System.Generics.Defaults; type TPointFComparer = class(TComparer<TPointF>) public function Compare(const Left, Right: TPointF): Integer; override; end; function TPointFComparer.Compare(const Left, Right: TPointF): Integer; begin if Left.X < Right.X then Result := -1 else if Left.X > Right.X then Result := +1 else if Left.Y < Right.Y then Result := -1 else if Left.Y > Right.Y then Result := +1 else Result := 0; end; procedure TForm1.Button1Click(Sender: TObject); var i: Integer; pt: TPointF; arr: array of TPointF; begin // ----------------------------------------- // init // ----------------------------------------- Randomize; SetLength(arr, 10); for i := Low(arr) to High(arr) do begin arr[i].X := Random(100); arr[i].Y := Random(100); end; // ----------------------------------------- // show // ----------------------------------------- ListBox1.Clear; for pt in arr do begin ListBox1.Items.Add(FloatToStr(pt.X) + ' ' + FloatToStr(pt.Y)); end; // ----------------------------------------- // sort // ----------------------------------------- TArray.Sort<TPointF>(arr, TPointFComparer.Create); // ----------------------------------------- // show // ----------------------------------------- ListBox2.Clear; for pt in arr do begin ListBox2.Items.Add(FloatToStr(pt.X) + ' ' + FloatToStr(pt.Y)); end; end;

ソートTObjectList.Sort

比較するクラスを作る

TObjectListをソートするには、比較するクラスを作ってやる。

TCompareはGenerics.Defaults

unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls, FMX.Controls.Presentation, FMX.ScrollBox, FMX.Memo, Generics.Collections, Generics.Defaults; type TIxPoint = class(TObject) public Text: string; X: Single; Y: Single; end; type TIxPointComparer = class(TComparer<TIxPoint>) public function Compare(const Left, Right: TIxPoint): Integer; override; end; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; procedure Button1Click(Sender: TObject); private { private 宣言 } public { public 宣言 } end; var Form1: TForm1; implementation {$R *.fmx} function TIxPointComparer.Compare(const Left, Right: TIxPoint): Integer; begin Result := Round(Left.Text.ToSingle - Right.Text.ToSingle); end; procedure TForm1.Button1Click(Sender: TObject); var objlist: TObjectList<TIxPoint>; obj: TIxPoint; begin objlist := TObjectList<TIxPoint>.Create; try obj := TIxPoint.Create; obj.Text := '2'; objlist.Add(obj); obj := TIxPoint.Create; obj.Text := '3'; objlist.Add(obj); obj := TIxPoint.Create; obj.Text := '1'; objlist.Add(obj); Memo1.Lines.Add('---- before sort ----'); for obj in objlist do begin Memo1.Lines.Add(obj.Text); end; objlist.Sort(TIxPointComparer.Create); Memo1.Lines.Add('---- after sort ----'); for obj in objlist do begin Memo1.Lines.Add(obj.Text); end; finally objlist.Free; end; end; end.

無名メソッド版1

クラスを宣言しなくても、無名メソッドでこうもかける。が、宣言した方が使うのは簡単だと思うのでうーん。
objlist.Sort(TComparer<TIxPoint>.Construct( function(const Item1, Item2: TIxPoint): Integer begin Result := Round(Item1.Text.ToSingle - Item2.Text.ToSingle); end));

無名メソッド版2

Delphi11.3
値が数値でなくても比較可能とかいいつつ、以下の例は数値比較だけど。
type TIxItem = class(TObject) public Value: Integer; end; uses System.Generics.Collections; procedure TForm1.Button1Click(Sender: TObject); var objlst: TObjectList<TIxItem>; item: TIxItem; begin // 生成 objlst := TObjectList<TIxItem>.Create; try for var i := 0 to 5 do begin item := TIxItem.Create; item.Value := Round(Random(100)); objlst.Add(item); end; // ソート前 Memo1.Lines.Add('before sort.'); for item in objlst do begin Memo1.Lines.Add(item.Value.ToString); end; // ソート objlst.Sort(System.Generics.Defaults.TComparer<TIxItem>.Construct( function(const L, R: TIxItem): Integer begin if L.Value = R.Value then Result := 0 else if L.Value < R.Value then Result := -1 else Result := 1; end)); // ソート後 Memo1.Lines.Add('after sort.'); for item in objlst do begin Memo1.Lines.Add(item.Value.ToString); end; finally objlst.Free; end; end;

インライン変数

変数の型は型推論で決定される。
for var i:=0 to 3 do
インライン変数

デフォルトパラメータ

引数を省略
制限いろいろあるみたい→パラメータ(Delphi)
procedure FillArray(A: array of Integer; Value: Integer = 0);

仮想メソッドと動的メソッド

仮想メソッド動的メソッド
virtualdynamic
実効速度最適化コードサイズ最適化

静的クラスメソッド


関数をクラス内に宣言出来る。
ただし、クラスメンバにアクセスできない。関数がクラス内に入れられるだけ。
あまりメリット無い?

わかりやすい解説
関数ポインタとメソッドポインタの相互代入のおはなし。→クラスメソッド(静的な意味で

docwiki メソッド→静的クラスメソッド

手続き・関数・メソッド


手続きは戻り値の無いルーチン
関数は戻り値の有るルーチン
メソッドはクラスに関連付けられた手続き・関数


docwiki 手続きと関数


オーバーライドと隠蔽

オーバーライドした場合は、継承されたメソッドが呼び出される
隠蔽した場合は、宣言したクラスのメソッドが呼び出される。
program OverrideRedeclared; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils; const CRLF = #13#10; type T1 = class(TObject) procedure Act; virtual; end; T2 = class(T1) procedure Act; // Act is redeclared, but not overridden end; T3 = class(T1) procedure Act; override; // Act is redeclared, overridden end; procedure std_out(str: string); begin write(str + CRLF); end; var SomeObject1: T1; SomeObject2: T2; procedure T1.Act; begin std_out('T1.Act'); end; procedure T2.Act; begin std_out('T2.Act'); end; procedure T3.Act; begin std_out('T3.Act'); end; begin try SomeObject1 := T2.Create; try SomeObject1.Act; // calls T1.Act finally SomeObject1.Free; end; SomeObject2 := T2.Create; try SomeObject2.Act; // calls T2.Act finally SomeObject2.Free; end; SomeObject1 := T3.Create; try SomeObject1.Act; // calls T3.Act finally SomeObject1.Free; end; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end.

抽象メソッド

実装が無いメソッド
procedure DoSomething; virtual; abstract;

クラスフィールド

C言語の静的データメンバみたいなもの...

オブジェクト参照なしでアクセスできるクラス内のデータフィールド
クラスフィールドに保存されるデータは、クラスの全てのインスタンスで共有
クラスまたはクラスのインスタンス変数からアクセス
type TMyClass = class(TObject) public class var//静的クラスフィールドのブロック開始 Top:Integer; Bottom:Integer; var//ブロック終了 end;
http://docwiki.embarcadero.com/RADStudio/Seattle/ja/%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89

メッセージ

TMessagingManagerというのを使うと簡単にメッセージ処理を追加可能。
参考:TMessageManagerってこんなの。

以下はほぼサンプルまま。メッセージハンドラに、無名メソッドを登録している。

uses System.Messaging; var SubscriptionId: Integer; MessageManager: TMessageManager; procedure TForm1.FormCreate(Sender: TObject); begin MessageManager := TMessageManager.DefaultManager; SubscriptionId := MessageManager.SubscribeToMessage(TMessage<UnicodeString>, procedure(const Sender: TObject; const M: TMessage) begin ShowMessage((M as TMessage<UnicodeString>).Value); end); end; procedure TForm1.FormDestroy(Sender: TObject); begin MessageManager.Unsubscribe(TMessage<UnicodeString>,SubscriptionId); end; procedure TForm1.Button1Click(Sender: TObject); var msg: TMessage<UnicodeString>; begin MessageManager := TMessageManager.DefaultManager; msg := TMessage<UnicodeString>.Create('This is a string message.'); MessageManager.SendMessage(Sender, msg, True); end;

インターフェース


インターフェースを用いたSingletonの実装

Printerみたいなやつ
【Delphi】 Interface による Singleton

TImage画像読み込み


usesにJpeg,GIFImg,PNGImageを追加する。

参考 http://ht-deko.minim.ne.jp/tech054.html

PNG読み込み

10.2tokyo
FireMonkeyだとTBitmap.LoadFromFileでPNG読み込み出来る。
VCLだと、読み込めないのでこうする。
uses PngImage; var png: TPngImage; ext: string; begin ext := ExtractFileExt(opt_src_fname); if ext = '.bmp' then begin bmp_src.LoadFromFile(opt_src_fname); end else if ext = '.png' then begin png := TPngImage.Create; try png.LoadFromFile(opt_src_fname); bmp_src.SetSize(png.Width, png.Height); bmp_src.Canvas.Draw(0, 0, png); finally png.Free; end; end;

GDI+


GDI+DelphiXE2

こんな感じで拡大・縮小表示可能。便利。
implementation uses GDIPAPI, GDIPOBJ; {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var grp: TGPGraphics; img: TGPImage; rct: TGPRect; ratio: double; begin if OpenPictureDialog1.Execute then begin grp := TGPGraphics.Create(Image1.Canvas.Handle); img := TGPImage.Create(OpenPictureDialog1.FileName); try ratio := img.GetWidth / img.GetHeight; rct := MakeRect(0, 0, Trunc(Image1.Height * ratio), Image1.Height); grp.DrawImage(img, rct); finally grp.Free; img.Free; end; end; end;

GDI+Delphi2009

Delphi2009で使うには、下記からダウンロードする。
ID: 26950, Easy to use Delphi 2009 GDI+ 1.1 Library (version 1.2)

配列

配列、動的配列 わかりやすい
動的配列の実体はポインタなんだけど、逆参照演算子'^'使っちゃいけいないそうだ。→ObjectPascal言語ガイド


配列の型宣言


静的配列
TByteArray
type TByteArray = array [0..32767] of Byte;
動的配列
TBytes
TArray<T> = array of T;

動的配列の要素数

SetLength関数で要素数を設定
High関数で要素数-1を取得。要素数が0の時は、-1を返す。
Length関数で要素数を取得

要素数取得の例1

var colors: array of TColor; i: integer; begin SetLength(colors, 5); Memo1.Lines.Add('High(colors)=' + IntToStr(High(colors)));
実行時Memo1の表示
High(colors)=4

要素数取得の例2

var buf1: array [0 .. 9] of Byte; buf2: array [0 .. 9] of Word; buf3:array of Byte; buf4:array of Word; begin SetLength(buf3,10); SetLength(buf4,10); memo('SizeOf(buf1)='+IntToStr(SizeOf(buf1))); memo('SizeOf(buf2)='+IntToStr(SizeOf(buf2))); memo('SizeOf(buf3)='+IntToStr(SizeOf(buf3))); memo('SizeOf(buf4)='+IntToStr(SizeOf(buf4))); memo('Length(buf1)='+IntToStr(Length(buf1))); memo('Length(buf2)='+IntToStr(Length(buf2))); memo('Length(buf3)='+IntToStr(Length(buf3))); memo('Length(buf4)='+IntToStr(Length(buf4))); end;
実行結果
SizeOf(buf1)=10 SizeOf(buf2)=20 SizeOf(buf3)=4 SizeOf(buf4)=4 Length(buf1)=10 Length(buf2)=10 Length(buf3)=10 Length(buf4)=10


動的配列の引数渡し


procedure TForm1.test3; var buffer: TBytes; begin SetLength(buffer, 8); Memo1.Lines.Add('array of bytes : High(buffer)=' + IntToStr(High(buffer))); func_set_len(buffer); Memo1.Lines.Add('array of bytes : High(buffer)=' + IntToStr(High(buffer))); end; procedure TForm1.func_set_len(var buf: TBytes); begin SetLength(buf, 16); end;
実行時Memo1の表示
array of bytes : High(buffer)=7 array of bytes : High(buffer)=15

多次元動的配列

const SIZEH = 3; SIZEW = 8; procedure TForm1.Button1Click(Sender: TObject); var arr1: array [0 .. SIZEH - 1] of array [0 .. SIZEW - 1] of integer; arr2: array of array of integer; i, j, k: integer; str: string; begin SetLength(arr2, SIZEH, SIZEW); //( // (0,1,2,3,4,5,6,7), // (0,1,2,3,4,5,6,7), // (0,1,2,3,4,5,6,7) //) k := 0; for i := 0 to SIZEH - 1 do begin for j := 0 to SIZEW - 1 do begin arr1[i, j] := k; arr2[i, j] := k; inc(k); end; end; for i := 0 to SIZEH - 1 do begin str := ''; for j := 0 to SIZEW - 1 do begin str := str + ' ' + IntToStr(arr2[i, j]); end; memo(str); end;

動的メモリ

GetMemory→FreeMemory
GetMem→FreeMem
var ptr:pointer; begin try ptr:=GetMemory(600*1024*1024); //600MBytes!! finally FreeMemory(ptr); end;
GetMemとの差は、Delphi2010のヘルプ(System.GetMemory)によると、
メモ: GetMemory は、GetMem の C++ 互換バージョンです。

AllocMemとGetMem

AllocMemは、割り当てて0初期化する。GetMemは0初期化しない。

ポインタが指すメモリの初期化

Winapi.Windows.pasのFillMemory関数を使う→実体はFillCharなんだけど(XE8の時)
https://msdn.microsoft.com/ja-jp/library/cc430045.aspx

TMemoryStream

var ptr:PByte; ms:TMemoryStream; begin ms:=TMemoryStream.Create; ms.SetSize(256); ptr:=ms.Memory; ms.Free;

TByteArray

便利だけど要注意なやつ。サイズが固定。

実装はこうなっている。
http://docwiki.embarcadero.com/Libraries/Sydney/en/System.SysUtils.TByteArray
type TByteArray = array [0..32767] of Byte;

フィル

メモリの初期化
FillChar(m_Buffer^,total_size,$FF);
動的配列の場合
ちょっとわかりにくいが、こうみたい。
FillChar(Buffer[0],total_size,$FF); FillChar(Buffer[0],Length(Buffer),$FF); FillChar(PByte(Buffer)^,Length(Buffer),$FF);//これでもいける

コピー

System.Copy

var src:TBytes; dst:TBytes; begin dst := Copy(src);//動的配列はIndex,Size指定いらない
最後にNULを付加するので、MaxLen+1のDestが必要
SysUtils.StrLCopy(Dest,Source,MaxLen)

名前は移動だけどコピー
引数は、arr[0]の様に要素番号を書く。
procedure Move(const Source; var Dest; Count: Integer);
memcpyと方向逆なのと、var渡しのが要注意。ここらへんが判りづらい。

move例1

procedure TForm1.Button1Click(Sender: TObject); var Buffer: array of Byte; src_ptr,dst_ptr:PByte; i: integer; begin SetLength(Buffer, 256); for i := 0 to Length(Buffer) - 1 do Buffer[i] := i; memo('before'); for i := 0 to 10 - 1 do begin memo(IntToStr(i) + '=' + IntToStr(Buffer[i])); end; src_ptr:=PByte(Buffer); dst_ptr:=PByte(Buffer); inc(src_ptr,4); Move(src_ptr^, dst_ptr^, 3); memo('after'); for i := 0 to 10 - 1 do begin memo(IntToStr(i) + '=' + IntToStr(Buffer[i])); end; Move(Buffer[7], Buffer[0], 3); memo('after2'); for i := 0 to 10 - 1 do begin memo(IntToStr(i) + '=' + IntToStr(Buffer[i])); end; end;
出力
before 0=0 1=1 2=2 3=3 4=4 5=5 6=6 7=7 8=8 9=9 after 0=4 1=5 2=6 3=3 4=4 5=5 6=6 7=7 8=8 9=9 after2 0=7 1=8 2=9 3=3 4=4 5=5 6=6 7=7 8=8 9=9

move例2

procedure TForm1.Button2Click(Sender: TObject); var buf1: array of Byte; buf2: array of Byte; i: integer; begin SetLength(buf1, 8); SetLength(buf2, 8); for i := 0 to High(buf1) do buf1[i] := i; for i := 0 to High(buf2) do buf2[i] := 0; memo('--- before ---'); for i := 0 to High(buf1) do memo('buf1[' + IntToStr(i) + ']=' + IntToStr(buf1[i])); for i := 0 to High(buf2) do memo('buf2[' + IntToStr(i) + ']=' + IntToStr(buf2[i])); Move(PByte(buf1)^, PByte(buf2)^, Length(buf1)); memo('--- after ---'); for i := 0 to High(buf1) do memo('buf1[' + IntToStr(i) + ']=' + IntToStr(buf1[i])); for i := 0 to High(buf2) do memo('buf2[' + IntToStr(i) + ']=' + IntToStr(buf2[i])); end;
出力
--- before --- buf1[0]=0 buf1[1]=1 buf1[2]=2 buf1[3]=3 buf1[4]=4 buf1[5]=5 buf1[6]=6 buf1[7]=7 buf2[0]=0 buf2[1]=0 buf2[2]=0 buf2[3]=0 buf2[4]=0 buf2[5]=0 buf2[6]=0 buf2[7]=0 --- after --- buf1[0]=0 buf1[1]=1 buf1[2]=2 buf1[3]=3 buf1[4]=4 buf1[5]=5 buf1[6]=6 buf1[7]=7 buf2[0]=0 buf2[1]=1 buf2[2]=2 buf2[3]=3 buf2[4]=4 buf2[5]=5 buf2[6]=6 buf2[7]=7

配列のバイナリファイル書き込み

静的配列・動的配列・メモリーストリームをファイルに書き込む
var barr: TByteArray; darr: array of Integer; ms: TMemoryStream; fs: TFileStream; wptr: PWord; begin fs := TFileStream.Create('test.bin', fmCreate); try barr[0] := 1; barr[1] := 2; barr[2] := 3; barr[3] := 4; fs.Write(barr, 4); SetLength(darr, 4); darr[0] := $12345670 + 1; darr[1] := $12345670 + 2; darr[2] := $12345670 + 3; darr[3] := $12345670 + 4; fs.Write(@darr[0], 4 * sizeof(Integer));//バッファがTBytesの時は変数名をそのまま書く ms := TMemoryStream.Create; try ms.SetSize(4 * sizeof(Word)); wptr := ms.Memory; wptr^ := $89A0+ 1; inc(wptr); wptr^ := $89A0 + 2; inc(wptr); wptr^ := $89A0 + 3; inc(wptr); wptr^ := $89A0 + 4; fs.Write(ms.Memory, ms.Size) finally ms.Free; end; finally fs.Free; end;

スタックサイズ

配列が大きくてスタックオーバーフローする場合は、スタックを大きくする。という手も有る。
メモリ割り当てサイズ(Delphi) - RAD Studio XE2
{$M minstacksize,maxstacksize} {$MINSTACKSIZE number} {$MAXSTACKSIZE number} {$M 16384,1048576}

文字列・バイト配列


文字(配列)操作

FillChar Move

文字列からバイト配列(ANSI)に変換 DelphiXE7

「012」をTEncoding.ANSIに変換して、バイナリ表示

procedure TForm1.btnStringStreamDecodeClick(Sender: TObject); var ss: TStringStream; i: integer; ptr: PByte; begin ss:=TStringStream.Create('012', TEncoding.ANSI); try ptr := ss.Memory; for i := 0 to ss.Size - 1 do begin Memo1.Lines.Add('$' + IntToHex(ptr^, 2)); inc(ptr); end; finally ss.Free; end; end;

バイト配列(ANSI)から文字列に変換 DelphiXE7

Memo1.Lines.Add('ANSIバイナリ($82,$A0,$82,$A2,$82,$A4,$30,$31,$32)を、読み込んでstringに変換

procedure TForm1.btnStringStreamEncodeClick(Sender: TObject); var ss: TStringStream; arr: array of Byte; begin SetLength(arr, 9); arr[0] := $82; arr[1] := $A0; arr[2] := $82; arr[3] := $A2; arr[4] := $82; arr[5] := $A4; arr[6] := $30; arr[7] := $31; arr[8] := $32; ss := TStringStream.Create('', TEncoding.ANSI); try ss.WriteData(arr, High(arr) + 1); Memo1.Lines.Add(ss.DataString); finally ss.Free; end; end;

文字列からバイト列に変換 Delphi2010

静的配列・動的配列・動的メモリへ変換。StrLCopyを使う。※MaxLen+1のバッファサイズ必要。
AnsiStringにしているのは、アプリケーションの都合です。
procedure TForm1.Button1Click(Sender: TObject); var static_arr: array [0 .. 12] of Byte; dyn_arr: array of Byte; buffer: PByte; str: string; procedure dump(ptr: PByte; len: integer); var i: integer; tmp: string; begin tmp := ''; for i := 0 to len - 1 do begin tmp := tmp + IntToHex(ptr^, 2) + ' '; inc(ptr); end; Memo1.Lines.Add(tmp) end; begin // ----------------------------------------- Memo1.Lines.Add('文字列をバイト列に変換(静的配列)'); Memo1.Lines.Add('Length(static_arr)=' + IntToStr(Length(static_arr))); str := '01234567'; Memo1.Lines.Add(str); FillChar(static_arr, sizeof(static_arr), 1); dump(@static_arr, sizeof(static_arr)); StrLCopy(PAnsiChar(@static_arr), PAnsiChar(AnsiString(str)), Length(static_arr) - 1); // ok dump(@static_arr, sizeof(static_arr)); // ----------------------------------------- Memo1.Lines.Add('文字列をバイト列に変換(動的配列)'); str := '76543210'; Memo1.Lines.Add(str); SetLength(dyn_arr, 16); Memo1.Lines.Add('Length(dyn_arr)=' + IntToStr(Length(dyn_arr))); FillChar(dyn_arr[0], Length(dyn_arr), 2); dump(PByte(dyn_arr), Length(dyn_arr)); StrLCopy(PAnsiChar(dyn_arr), PAnsiChar(AnsiString(str)), Length(dyn_arr) - 1); dump(PByte(dyn_arr), Length(dyn_arr)); // ----------------------------------------- Memo1.Lines.Add('文字列をバイト列に変換(動的メモリ)'); buffer := GetMemory(20); try str := '01234567'; Memo1.Lines.Add(str); FillChar(buffer^, 20, 3); dump(PByte(buffer), 20); StrLCopy(PAnsiChar(buffer), PAnsiChar(AnsiString(str)), 22 - 1); dump(PByte(buffer), 20); finally FreeMemory(buffer); end; end;
実行結果
Memo1 文字列をバイト列に変換(静的配列) Length(static_arr)=13 01234567 01 01 01 01 01 01 01 01 01 01 01 01 01 30 31 32 33 34 35 36 37 00 01 01 01 01 文字列をバイト列に変換(動的配列) 76543210 Length(dyn_arr)=16 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 37 36 35 34 33 32 31 30 00 02 02 02 02 02 02 02 文字列をバイト列に変換(動的メモリ) 01234567 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 30 31 32 33 34 35 36 37 00 03 03 03 03 03 03 03 03 03 03 03

TBytesからStringに変換 DelphiXE10.1

TEncoding.GetString()を使う

var strarr: TBytes; str: string; begin SetLength(strarr, 8); strarr[0] := $30; str := TEncoding.ANSI.GetString(strarr, 0, 1);

strには'0'が入る

バイト配列からStringに変換 Delphi2010


例1

var byte_buf:array[0..15]of Byte; begin byte_buf[bytebuf_wr]:=0;//ヌル終端 str:=String(PAnsiChar(@byte_buf));

例2

var static_arr:array [0..8] of Byte; begin StrLCopy(@static_arr,AnsiString('1234'),Length(static_arr)-1); Memo1.Lines.Add(String(PAnsiChar(@static_arr))); Memo1.Lines.Add(String(PAnsiChar(@static_arr[1]))); end;
実行結果
1234 234

Pointerからstringに変換 Delphi2010

var buf:Pointer; ptr:PByte; begin buf:=AllocMem(16);//AllocMemは領域を0クリアする try ptr:=PByte(buf); ptr^:=$30; inc(ptr); ptr^:=$31; Memo1.Lines.Add(string(PAnsiChar(buf))); finally FreeMem(buf); end;

静的配列からstringに変換 Delphi10.1Seatle

TStringStreamを使った変換だと、配列内のゴミまで変換していたので、やり方を変えてみた。
var serialnumber_buf: array [0 .. 63] of AnsiChar; FillChar(serialnumber_buf[0], 64, 0); serialnumber_buf[0]:=適当な値 serialnumber_buf[1]:=適当な値 SerialNumber := String(AnsiString(serialnumber_buf));

文字列を数値に変換する

TryStrToIntDef を使うと例外を出さずに数値変換出来る。
Delphiは、'$'と'0x'に対応する!!

http://docwiki.embarcadero.com/Libraries/Tokyo/ja/System.SysUtils.TryStrToInt

文字列処理とUnicode(Delphi2009以降)

Delphi2009以降は、VCLがUnicode化されたため、既存ソースの移行は、いろいろとあります。
Delphi 2009 特集 ★必読。わかりやすいのでお勧め。

Delphi2007以前
String = AnsiString
Char = AnsiChar

Delphi2009以降
String = UnicodeString
Char = WideChar

Delphi2010でこんなコードを書いてみる
procedure TForm1.btnAnsiCharClick(Sender: TObject); var arr:array [0..2] of AnsiChar; str:String; begin arr[0]:=AnsiChar($82); arr[1]:=AnsiChar($A0); arr[2]:=AnsiChar(0); str:=arr; Memo1.Lines.Add('AnsiChar配列に「あ」$82$A0を入れた場合'); Memo1.Lines.Add(str); end; procedure TForm1.btnCharClick(Sender: TObject); var arr:array [0..2] of Char; str:String; begin arr[0]:=Char($82); arr[1]:=Char($A0); arr[2]:=Char(0); str:=arr; Memo1.Lines.Add('Char配列に「あ」$82$A0を入れた場合'); Memo1.Lines.Add(str); end;
結果はこう
AnsiChar配列に「あ」$82$A0を入れた場合 あ Char配列に「あ」$82$A0を入れた場合 □
□の部分は、見た目がそうなるという事で、□のコードに変換されたわけでは無いです。

数値文字列変換

Formatの使い方
FDelphi過去ログ:形式文字列を使うFormatは便利ですが、ヘルプの説明から形式文字列を作成 するのは骨が折れます。例示があると大変助かりますが。

上記URLの内容をコピー→Delphi Formatの使い方

Format
Format('%.2d',[value]); // 00 数値を2桁で0埋め有り

FormatFloatの例
FormatFloat('0##.0', value); // 000.0 必ず整数部3桁小数部1桁を表示

文字列処理

Ansi付き関数は、日本語使える
Ansi無しは日本語つかうと駄目な時有る

指定文字で分割


uses System.Types, System.StrUtils; var sda:TStringDynArray; begin sda:=SplitString('abc def ghi', ' ');

比較

uses SysUtils function SameStr(S1: string; S2: string): Boolean;//大文字小文字区別する function SameText(S1: string; S2: string): Boolean;//大文字小文字区別しない
CompareStr,CompareTextも同様だが、戻り値がIntegerになる。

検索

uses System function Pos(const Substr: string; const S: string): Integer;
SubstrがSに有る場合は、その位置を1〜nで返す。※1からというのが罠
無い場合は、0を返す。

切り出し

uses System function Copy(S:string;Index:Integer;Count:Integer):string;
Indexは1からというのが罠

置換

uses System.StrUtils ReplaceStr(const AText:string;const AFromText:string;const AToText:string):string; 大文字小文字区別 ReplaceText(const AText:string;const AFromText:string;const AToText:string):string; 大文字小文字区別しない StringReplace AnsiString用

正規表現


正規表現 検索

uses System.RegularExpressions; procedure TForm1.btnTestRegularexpressionClick(Sender: TObject); var match: TMatch; input_str: string; pattern_str: string; i: Integer; begin input_str := 'abcd123.scn'; pattern_str := '(\d+)\.'; input_str := 'aaabb(COM12)'; pattern_str := '\(COM(\d+)\)$'; match := TRegEx.match(input_str, pattern_str); if match.Success then begin Memo('success'); Memo('Input=' + input_str); Memo('Pattern=' + pattern_str); Memo('match.Value=' + match.Value); Memo('match.Groups.Count=' + match.Groups.Count.ToString); for i := 0 to match.Groups.Count - 1 do begin Memo('match.Groups.Item[' + IntToStr(i) + '].Value=' + match.Groups.Item[i].Value); end; end else begin Memo('fail'); end; end;
使用例1
success Input=aaabb(COM12) Pattern=\(COM(\d+)\)$ match.Value=(COM12) match.Groups.Count=2 match.Groups.Item[0].Value=(COM12) match.Groups.Item[1].Value=12
使用例2
success Input=abcd123.scn Pattern=(\d+)\. match.Value=123. match.Groups.Count=2 match.Groups.Item[0].Value=123. match.Groups.Item[1].Value=123
連番ファイル名の、先頭部分を取得
function get_file_head(fname: string): string; var head: string; match: TMatch; begin head := ExtractFileName(fname); head := ChangeFileExt(head, ''); match := TRegEx.match(head, '([^0123456789])+'); if match.Success then begin Result := match.Value; end else begin Result := ''; raise Exception.Create('ファイル名の解析に失敗 TIxViewObjectFrames.get_file_head'); end; end;

正規表現 置換

uses System.RegularExpressions, System.StrUtils; function TForm1.ReplaceCount(const Match: TMatch): string; var valstr: string; vallen: Integer; val: Integer; begin if Match.Success then begin valstr := Match.Groups.Item[1].Value; vallen := Length(valstr); val := StrToInt(valstr); inc(val); Result := RightStr('000000' + IntToStr(val), vallen) + Match.Groups.Item[2].Value; end else Result := ''; end; procedure TForm1.btnTestRegExReplaceClick(Sender: TObject); var regex: TRegEx; myEval: TMatchEvaluator; input_str: string; i: Integer; begin input_str := 'abcd000123.scn'; regex.Create('(\d+)(\.)'); myEval := ReplaceCount; for i := 0 to 4 do begin if regex.Match(input_str).Success then begin Memo('Match=' + input_str); input_str := regex.Replace(input_str, myEval); Memo('next=' + input_str); end else begin Memo('Match fail'); break; end; end; end;
出力
abcd000124.scn abcd000125.scn abcd000126.scn abcd000127.scn abcd000128.scn

文字列の繰り返し

文字や文字列の繰り返しを生成する。下記は「hello!hello!hello!」になる。
uses System.StrUtils; str = DupeString('hello!',3);

CSV


958_デリミタによる文字列の分割

TSTringList.CommaText

var csv:TStringList; begin csv:=TStringList.Create; csv.CommaText:='aa,bb,c' Memo1.Lines.Add(csv.Strings[0]);

SplitString

あくまで指定文字で分解するので、CSVのダブルクォーテーションとかには対応していない

uses System.Types, System.StrUtils; var sda: TStringDynArray; prms: TStringDynArray; begin sda := SplitString(line, ' '); if Length(sda) = 2 then begin memo('------> APPL ' + sda[1]); prms := SplitString(sda[1], ','); if Length(prms) >= 1 then begin voltage := StrToFloat(prms[0]);

BCD

ソース
変換結果は上位からMSB、Fraction[0]に収まる。
uses Data.FMTBcd; procedure TForm1.Button1Click(Sender: TObject); var hour, min, sec, msec: Word; year, month, day: Word; yyyy: TBcd; mm: TBcd; dd: TBcd; hh: TBcd; nn: TBcd; ss: TBcd; dt: TDateTime; str: string; begin dt := Now; DateTimeToString(str, 'yyyy-mm-dd hh:nn:ss', dt); Memo1.Lines.Add(str); DecodeDate(dt, year, month, day); DecodeTime(dt, hour, min, sec, msec); yyyy := CurrencyToBcd(year); mm := CurrencyToBcd(month); dd := CurrencyToBcd(day); hh := CurrencyToBcd(hour); nn := CurrencyToBcd(min); ss := CurrencyToBcd(sec); Memo1.Lines.Add('yyyy.Fraction[0]=' + IntToHex(yyyy.Fraction[0], 2)); Memo1.Lines.Add('yyyy.Fraction[1]=' + IntToHex(yyyy.Fraction[1], 2)); Memo1.Lines.Add('mm.Fraction[0]=' + IntToHex(mm.Fraction[0], 2)); Memo1.Lines.Add('dd.Fraction[0]=' + IntToHex(dd.Fraction[0], 2)); Memo1.Lines.Add('hh.Fraction[0]=' + IntToHex(hh.Fraction[0], 2)); Memo1.Lines.Add('nn.Fraction[0]=' + IntToHex(nn.Fraction[0], 2)); Memo1.Lines.Add('ss.Fraction[0]=' + IntToHex(ss.Fraction[0], 2)); end;
実行結果
2023-07-23 12:01:22 yyyy.Fraction[0]=20 yyyy.Fraction[1]=23 mm.Fraction[0]=70 ←1桁なのでMSBによっている dd.Fraction[0]=23 hh.Fraction[0]=12 nn.Fraction[0]=10 ss.Fraction[0]=22

初期化 initialization

var uid:integer; implementation : : initialization uid:=0; end.

終了時実行 finalization

http://docwiki.embarcadero.com/RADStudio/Seattle/ja/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%A8%E3%83%A6%E3%83%8B%E3%83%83%E3%83%88#finalization_.E3.82.BB.E3.82.AF.E3.82.B7.E3.83.A7.E3.83.B3
finalization

ラベルとgoto

procedure test; label exit_success; begin goto exit_success; : : exit_success: : end;

プロパティで配列

function Getvalues(index: integer): string; procedure Setvalues(index: integer; const Value: string); property values[index:integer]:string read Getvalues write Setvalues;

プロパティで関数

type TSampleEvent = procedure (const Value: string) of object; type TTestEventProperty = class(TObject) private FOnSample: TSampleEvent; public property OnSampleEvent: TSampleEvent read FOnSample write FOnSample; end; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private procedure testfunc(const Value: string); { Private 宣言 } public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.testfunc(const Value: string); begin ShowMessage(Value); end; procedure TForm1.Button1Click(Sender: TObject); var sample:TTestEventProperty; begin sample:=TTestEventProperty.Create; try sample.OnSampleEvent:=testfunc; sample.OnSampleEvent('syokun!'); finally sample.Free; end; end;

例外

uses SysUtils; raise Exception.Create('Error!!');

デフォルト例外処理

DLLはデフォルト例外処理を行わないので、デフォルト例外処理を行いたい時は、別途処理を追加する。

VCL でのデフォルト例外処理

FireMonkeyは、FMX.Forms.TApplication


浮動小数点(double,float)を文字列に変換

その1
uses SysUtils; Memo1.Lines.Add(FloatToStrF(12.3456, ffFixed, 15, 3));//15が精度、3が小数の桁数 Memo1.Lines.Add(FloatToStrF(12.345, ffFixed, 15, 3));//15が精度、3が小数の桁数 Memo1.Lines.Add(FloatToStrF(12.345, ffFixed, 15, 2));//15が精度、2が小数の桁数 //出力 12.346 12.345 12.35
その2
uses SysUtils; FormatFloat('00',12);
その3
f:=0.000012345678; memo(FloatToStrF(f,ffExponent,4,1)); memo(FloatToStrF(f,ffExponent,4,2)); memo(FloatToStrF(f,ffExponent,3,2)); //出力 1.235E-5 1.235E-05 1.23E-05

日付時刻

TDateTimeを年月日時分秒ミリ秒に分解


http://docwiki.embarcadero.com/Libraries/Tokyo/ja/System.SysUtils.DecodeDate
http://docwiki.embarcadero.com/Libraries/Tokyo/ja/System.SysUtils.DecodeTime

var hour, min, sec, msec: Word; year, month, day: Word; begin DecodeDate(dt, year, month, day); DecodeTime(dt, hour, min, sec, msec);

文字列から日付時刻を生成

現在のロケール形式で文字列に変換します。従って環境によって文字列変わります。
日本語環境ならば、下記の形式になると思います。
(なぜかStringToDateTimeみたいな関数が無い)
yyyy/mm/dd hh:nn:ss

StrToDateTimeDef(Value, 0.0);

EncodeDateTime 年月日から日付時刻を生成

引数は年、月、日、時、分、秒、msec
uses SysUtils; dt := EncodeDateTime(2012,8,29,0,0,0,0);

DateTimeToStr 日付時刻を文字列に変換

ソース
uses SysUtils; Memo1.Lines.Add(DateTimeToStr(Now));
結果
2009/01/29 20:28:12

DateTimeToString 日付時刻を文字列に変換

ソース
uses SysUtils; var str:string; fname:string; begin DateTimeToString(str,'yymmdd_hhnnss',Now); fname:=ExtractFilePath(Application.ExeName)+str+'.csv'; Memo1.Lines.Add(fname); end;
結果
070127_112913 070127_112914
ミリ秒まで表示する例
DatetimeToString(str, 'mm/dd hh:nn:ss zzz : ', Now);

TDateTimeから曜日を取得

Delphi2010
uses DateUtils; DayOfTheWeek(dt)
で、戻り値は
DayMonday
とかで比較。


TDateTime演算

Delphi2010
一日増やすのは単純に1足せばよい。
日付間の日数は、下記関数で求める。
uses DateUtils; DaysBetween(ANow:TDateTime,ATHen:TDateTime):integer;
DaySpan端数有り
DaysBetween端数無し
HoursBetween
MinuteBetween
SecondsBetween
WeeksBetween
YearsBetween
IncSecond年月日時分秒もある

TDateTime置き換え

RecodeXXXで、年、月、日、時、分、秒、ミリ秒(msec)を個別に置き換え
uses System.DateUtils; RecodeMilliSecond RecodeSecond RecodeHour RecodeDay RecodeMonth RecodeYear
ミリ秒を切り捨てる
uses System.DateUtils; cur_time := RecodeMilliSecond(Now, 0);

ファイルの日付取得

function get_file_date(fname: string): integer; var FileHnd: integer; begin { ファイルのハンドルを取得 } FileHnd := FileOpen(fname, fmOpenRead); try { ファイルのタイムスタンプを取得 } Result := FileGetDate(FileHnd); { タイムスタンプを日付型に変換 } finally FileClose(FileHnd); end; end;

ファイル読み書き


TFileStreamwrite,read使用してアクセス
TStreamWriterテキストファイルの書き込み。WriteLine使える。

ファイル読み書き TFileStream,ファイルサイズ,共有モード


read


GetMemして読み込み

var fs: TFileStream; ptr: PByte; begin GetMem(ptr, BUFFER_SIZE); try fs := TFileStream.Create(filename, fmOpenRead); try fs.Read(ptr^, BUFFER_SIZE); finally fs.Free end; finally FreeMem(ptr); end; end;

配列に読み込み

var fs:TFileStream; size:integer; chunk_size:integer; read_size:integer; fname:string; arr:TLongwordArray; i:integer; begin fname:='sample.bin'; fs:=TFileStream.Create(fname,fmOpenRead or fmShareDenyWrite); try size:=fs.Size; chunk_size:=4096; read_size:=chunk_size; while size>0 do begin if read_size>size then read_size:=size; fs.Read(arr,read_size); for i := 0 to (read_size div 4) - 1 do begin //arr[i] end; size:=size-read_size; end; finally fs.Free; end; end;

record(構造体)に読み込み

var fs: TFileStream; header: TIxANIPOVRomHeaderV5; begin fs := TFileStream.Create(fname, fmOpenRead); try fs.Read(header,sizeof(header)); finally fs.Free; end; end;

注意点

procedure TForm1.btnRefClick(Sender: TObject); var fs:TFileStream; fname:string; begin if OpenDialog1.Execute then begin fname:=OpenDialog1.FileName; edFname.Text:=fname; fs:=TFileStream.Create(fname,fmOpenRead or fmShareDenyNone); try edSize.Text:=IntToStr(fs.Size); finally fs.Free; end; end; end;

write


配列(array of Byte)をファイルに保存

procedure TIxAnipov2RomGen.SaveToFile(fname: string); var fs: TFileStream; slave_data_chunk: array of Byte; begin if EnalbeSlaveChunk then begin // Chunkヘッダ SetLength(slave_data_chunk, 8); slave_data_chunk[0] := $53; // S slave_data_chunk[1] := $4C; // L slave_data_chunk[2] := $41; // A slave_data_chunk[3] := $56; // V slave_data_chunk[4] := 0; slave_data_chunk[5] := 1; slave_data_chunk[6] := 2; slave_data_chunk[7] := 3; end; // ROMデータそのもの fs := TFileStream.Create(fname, fmCreate); try fs.Write(slave_data_chunk[0], 8); finally fs.Free; end; end;

配列(array of UInt16)をファイルに保存

Buffer: array of UInt16; SetLength(Buffer, 1024); fs.Write(Pointer(Buffer)^, 10); //write_sizeはバイト数

配列(TByteArray)をファイルに保存

procedure TIxAnipov2RomGen.gen_sd_file(frame_interval: integer; rom_fname: string); var fname: string; sd_file_header: TByteArray; begin num_of_slave := fnames.Count; sd_file_header[0] := $46; // F sd_file_header[1] := $4B; // K sd_file_header[2] := $42; // B sd_file_header[3] := $4D; // M sd_file_header[4] := 8; // chunk size(4byte) sd_file_header[5] := 0; sd_file_header[6] := 0; sd_file_header[7] := 0; sd_file_header[8] := num_of_slave; // num of slave(2byte) sd_file_header[9] := 0; sd_file_header[10] := frame_interval; // frame interval(2byte) sd_file_header[11] := 0; sd_file_header[12] := $FF; // reserved sd_file_header[13] := $FF; // reserved sd_file_header[14] := $FF; // reserved sd_file_header[15] := $FF; // reserved dst := TFileStream.Create(rom_fname, fmCreate); try // write header dst.Write(sd_file_header, 16); finally dst.Free; end; end;

Pointerで示しているバッファをファイルに保存

procedure WriteBuffer(buf: Pointer; write_size: integer); begin fs.Write(buf^, write_size);

PCharのバッファをファイルに保存

m_Buffer:PChar; GetMem(m_Buffer,LARGE_BUF_SIZE); var fs:TFileStream; begin acc_size:=READ_MEMORY_SIZE; remain:=readsize; fs:=TFileStream.Create(fname,fmCreate); try while remain>0 do begin fs.Write(PChar(m_Buffer + SizeOf(SH4USBIF_HEADER)+SizeOf(SH_TO_NOTE))^,acc_size); end; finally fs.Free end; end;

ファイル読み書き TextFile,AssignFile,Append,WriteLn,CloseFile

var fout: TextFile; begin FileMode := fmOpenWrite or fmShareDenyWrite; // 共有モード:他のアプリケーションは読み込み用に開く事が出来る。書き込み用に開く事は出来ない。 AssignFile(fout, file_name); try if FileExists(file_name) then Append(fout) else ReWrite(fout); WriteLn(fout, csv_val); finally CloseFile(fout) end;

テキストファイルから行読み込み TStreamReader

procedure TForm1.load_from_file(fname: string); var fs: TStreamReader; line: string; begin fs := TStreamReader.Create(fname); try while not fs.EndOfStream do begin line := fs.ReadLine; end; finally fs.Free; end; end;

ファイル操作


ファイルコピー

uses System.IOUtils; TFile.Copy(src,dst);

ZIPファイル

10.2Tokyo

uses System.Zip; procedure TForm1.btnDecompZipClick(Sender: TObject); var ZipFileName: string; ZippedFileIndex: Integer; ZipFile: TZipFile; DownloadedStream, DecompressionStream: TStream; LocalHeader: TZipHeader; bmp: TBitmap; strlist: TStringList; begin ZipFileName := 'genzip.zip'; ZippedFileIndex := 0; DownloadedStream := TFileStream.Create(ZipFileName, fmOpenRead); try ZipFile := TZipFile.Create; try ZipFile.Open(DownloadedStream, zmRead); ZipFile.Read(ZippedFileIndex, DecompressionStream, LocalHeader); try bmp := TBitmap.Create; try bmp.LoadFromStream(DecompressionStream); finally bmp.SaveToFile('zip1.bmp'); bmp.Free; Image1.Picture.LoadFromFile('zip1.bmp'); end; finally DecompressionStream.Free; end; ZipFile.Read('2/zip2.bmp', DecompressionStream, LocalHeader); try bmp := TBitmap.Create; try bmp.LoadFromStream(DecompressionStream); finally bmp.SaveToFile('2_zip2.bmp'); bmp.Free; Image2.Picture.LoadFromFile('2_zip2.bmp'); end; finally DecompressionStream.Free; end; ZipFile.Read('readme.txt', DecompressionStream, LocalHeader); try strlist := TStringList.Create; try strlist.LoadFromStream(DecompressionStream); finally strlist.SaveToFile('dst.txt'); strlist.Free; Memo1.Lines.LoadFromFile('dst.txt'); end; finally DecompressionStream.Free; end; finally ZipFile.Free; end; finally DownloadedStream.Free; end; end; procedure TForm1.btnGenZipClick(Sender: TObject); var ZipFile: TZipFile; UploadStream: TStream; fs: TFileStream; begin UploadStream := TFileStream.Create('genzip.zip', fmCreate); try ZipFile := TZipFile.Create; try // src1.bmpをzip1.bmpとして追加 ZipFile.Open(UploadStream, zmWrite); fs := TFileStream.Create('src1.BMP', fmOpenRead); try ZipFile.Add(fs, 'zip1.bmp'); finally fs.Free; end; // src2.bmpを2/zip2.bmpとして追加 fs := TFileStream.Create('src2.BMP', fmOpenRead); try ZipFile.Add(fs, '2/zip2.bmp'); finally fs.Free; end; // src.txtをreadme.txtとして追加 fs := TFileStream.Create('src.txt', fmOpenRead); try ZipFile.Add(fs, 'readme.txt'); finally fs.Free; end; finally ZipFile.Free; end; finally UploadStream.Free; end; end;

TImage上でマウスドラッグ

procedure TfrmMain.ImageViewMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin memo('mouse down'); MouseCapture:=True; end; procedure TfrmMain.FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin memo('main mouse move'); end; procedure TfrmMain.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin memo('main mouse up'); MouseCapture:=False; end;

マウスボタン

procedure TForm1.PaintBox1MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var tx, ty: Integer; begin tx := X div Zoom; ty := Y div Zoom; if Button = mbLeft then Image.Canvas.Pixels[tx, ty] := clBlack else if Button = mbRight then Image.Canvas.Pixels[tx, ty] := clWhite; PaintBox1.Refresh; end;

グローバルマウスフック

アプリにフォーカス無くてもマウスイベント取得
MouseHook GitHub

グローバルキーフック

アプリにフォーカス無くてもキーイベント取得
KeyHook GitHub

TChart(TeeChart)


サンプルソース

Delphi TChart Sample GitHub


サンプルソース2

UDPで送ったデータを受信して、TChartでグラフ表示
IndyUDPandTChart GitHub


TChartのヘルプ

なんか、どう探してもインストールされていない様な気がします(RAD Studio 2007)。
しかたないので、CODE GEARからTeeChartをダウンロード、インストールして、その中に有るヘルプを見ています。

2022-02-03
これまた仕方なくここみてる
http://teechart.net/docs/teechart/vclfmx/lib/html/]

TChartの情報

TeeChart Pro version 8 VCL / CLX
ニュートンQ&A(TeeChart Pro 7J VCL)

シリーズ動的生成


uses VclTee.Series; procedure TForm1.Button1Click(Sender: TObject); var t: integer; total_t: integer; series: TLineSeries; begin Chart1.SeriesList.Clear; series := TLineSeries.Create(Chart1); total_t := 48000; for t := 0 to total_t - 1 do begin series.AddXY(t, cos(2 * pi * (double(t) / double(total_t)))); end; Chart1.AddSeries(series); end;

シリーズの名前

右側にシリーズの一覧が表示される(Legend)、その名前の指定方法
var ser: TLineSeries; begin ser := TLineSeries.Create(Chart1); ser.Legend.Text:='cos';

Legendsのチェックボックス

シリーズ一覧(Legends)は、チェックボックスを表示して、シリーズの表示オン・オフ切り替えができる
下記は実行するたびに、チェックボックス自体の表示オン・オフ切り替え
Chart1.Legend.CheckBoxes := not Chart1.Legend.CheckBoxes;




PointerのStyle


↓こんな雰囲気
TLineSeries.Pointer.Style:=TSeriesPointerStyle(cmbPointerStyle.ItemIndex); TLineSeries.Pointer.Visible:=chkPointerVisible.Checked;
↓実際はこう。VisibleをTrueにしないと見えない。
Series1.Pointer.Style:=psCircle; Series1.Pointer.Visible:=True;

棒グラフ

これで棒3本
Series1.AddBar(10000, '1', clRed); Series1.AddBar(20000, '2', clRed); Series1.AddBar(30000, '3', clRed);
最初の棒を5000に変更する
Series1.YValue[0]:=5000;

縦右軸を使う(ソース)

Series1.VertAxis := aRightAxis;

縦右軸を使う(IDE)

IDEで設定する場合
Editing Chart1 -> Series -> Series2 -> General -> Options -> Vertical AxisをRightにする。

TCharHelperの使い方

Delphi10.1 Seatle
System.Character.IsDigit使おうとすると、非推奨で、TCharHelper使えと言われる。
c:TChar; c.IsDigit c.IsControl

スクロールするグラフ

TLineSeriesをスクロール表示する

procedure TForm1.add_series(val: double); begin Series1.AddXY(graph_x_count, val); inc(graph_x_count); if Series1.Count > 1000 then Series1.Delete(0); end;

TChartの画面をpngに保存する

uses Vcl.Imaging.pngimage; procedure TForm1.Button5Click(Sender: TObject); var fname: string; png: TPngImage; bmp:TBitmap; begin png := TPngImage.Create; try fname := 'test ChartTemp.bmp'; bmp:=ChartTemp.TeeCreateBitmap; png.Assign(bmp); png.SaveToFile(ChangeFileExt(fname, '.png')); finally bmp.Free; png.Free; end; end;

TChartの画面自動更新を止めて、描画速度を上げる

Chart1.AutoRepaint := false; // 自動更新を停止して描画速度を上げる : チャートの更新処理 : Chart1.AutoRepaint := true; // 自動更新再開 Chart1.Invalidate; // 自動更新を再開しただけだと今回の更新がきかないので明示的に再描画

TChartのX軸を90度回転して日付時刻にする

Delphi11.3

TChartの設定
Chart1.Axes.Bottom.LabelsAngle := 90; // ラベルの角度指定 Chart1.BottomAxis.DateTimeFormat := 'hh:mm:ss'; // 日付時刻の書式

シリーズ生成
var tgtser: TLineSeries; begin tgtser := TLineSeries.Create(Chart1); tgtser.Pointer.Visible := true; tgtser.Pointer.Style := psCircle; tgtser.XValues.DateTime := true; //これで日付時刻にする

参考。Delhpi11だとプロパティ違う
https://steema.com/docs/teechart/net/lib/html/SteemaTeeChartAxisLabelsDateTimeFormatProperty.htm

終了コード

System.ExitCodeで戻り値を返す事が可能。
戻り値はバッチファイルならば、ERRORLEVELで判定可能。

別のexe実行

ShellExecute

ShellExecute(Application.Handle,'open',PChar(cmd),PChar(option),nil,SW_NORMAL);

ShellExecuteEX

Windowsは、SW_xx,SHELLAPIは、APIいろいろの定義
バッチファイルから終了コードを返したい場合は、「exit 1」とかで指定可能です。

uses Windows,WinApi.SHELLAPI; function TForm1.shell_exec(cmd:string;option:string):DWORD; var sei:SHELLEXECUTEINFO; begin ZeroMemory(@sei, sizeof(SHELLEXECUTEINFO)); //構造体のサイズ sei.cbSize := sizeof(SHELLEXECUTEINFO); //起動側のウインドウハンドル sei.Wnd := Handle; //起動後の表示状態 sei.nShow := SW_SHOWNORMAL; //このパラメータが重要で、セットしないとSHELLEXECUTEINFO構造体のhProcessメンバがセットされない。 sei.fMask := SEE_MASK_NOCLOSEPROCESS; //起動プログラム sei.lpFile := PChar(cmd); sei.lpParameters := PChar(option); //プロセス起動 if not ShellExecuteEx(@sei) then//shell32.lib必須 exit; //エラー? if sei.hInstApp <= 32 then exit; //終了を待つ WaitForSingleObject( sei.hProcess, INFINITE ) ; //戻り値を取得 GetExitCodeProcess(sei.hProcess, Result); end;

CreateProcess

パイプ使った入出力のサンプルは↓から
http://www.autch.net/page/tips/delphi_anonymous_pipe.html

エクスプローラからドラッグドロップ


FMXコンポーネントのTDropTarget

TDropTargetをフォーム上に配置
Fileterに任意のフィルターを設定
*.txt
OnDragDropイベント記述
procedure TForm1.DropTarget1DragDrop(Sender: TObject; const Data: TDragObject; const Point: TPointF); begin memo(Data.Files[0]); end;
参考
DelphiXE3 [FMX]ドロップ先(DropTarget)

コードでドロップ受け入れ


Delphi11.3
type  TForm1 = class(TForm)  private { Private 宣言 }   procedure WMDropFiles(var Msg: TWMDropFiles); Message WM_DropFiles;  end; implementation uses WinApi.ShellApi;★忘れずに {$R *.dfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject); begin DragAcceptFiles(Handle,True); end; procedure TForm1.WMDropFiles(var Msg: TWMDropFiles); var num_files:integer; i:integer; FileName: Array[0..MAX_PATH] of Char; begin num_files := DragQueryFile(Msg.Drop, $FFFFFFFF, nil, 0); for i := 0 to num_files - 1 do begin DragQueryFile(Msg.Drop, i, FileName, SizeOf(FileName)); Memo1.Lines.Add(String(FileName)); end; DragFinish(Msg.Drop); end;
参考
https://www.blackcat.xyz/article.php/ProgramingFAQ_del0059
http://kwi.cocolog-nifty.com/blog/2005/12/delphi_drag__dr_e30d.html
http://www.geocities.co.jp/Milano/8000/delphi/dragdrop.html

コンポーネントを動的に生成

一般的な例ではないのですが、こんな感じで。
procedure TForm1.btnComm1CreateClick(Sender: TObject); begin comm1:=TComm.Create(self); comm1.BaudRate:=9600; comm1.ByteSize:=cbs8; comm1.ParityBits:=cpbNone; comm1.StopBits:=csb1; comm1.FlowControls:=[]; comm1.Port:=1; comm1.OnCommReceive:=Comm1CommReceive; comm1.Open; end; procedure TForm1.Comm1CommReceive(Sender: TObject; Size: Word); begin end;

タスクバーに表示しない

メインフォームをタスクバーで非表示にする方法。
ヘルプを見ると、Runメソッドの呼び出し前にとなっています。
とりあえずFormCreateとかで良いかと。
Application.ShowMainForm:=False;

環境変数

GetEnvironmentVariable('OS')

タイトルバーの無いフォームの移動(WM_NCHITTEST)

引用元失念。
procedure WMNCHITTEST(var Msg: TWMNCHITTEST); message WM_NCHITTEST; procedure WMNCLButtonDBLCLK(var msg :TWMNCHitMessage); message WM_NCLBUTTONDBLCLK; procedure TFormHusen.WMNCHITTEST(var Msg: TWMNCHITTEST); var Pt:TPoint; begin //マウス座標を取得 GetCursorPos(Pt); //フォーム上の座標に変換 Pt := ScreenToClient(Pt); if GetAsyncKeyState(VK_LBUTTON) < 0 then //ウィンドウズにタイトル バーで発生することを示すHTCAPTIONを返す Msg.Result := HTCAPTION else Msg.Result := HTCLIENT; end; //WMNCHITTESTで小細工しているので、ダブルクリックをFormのイベントで検出出来ない procedure TFormHusen.WMNCLButtonDBLCLK(var msg: TWMNCHitMessage); begin end;

TListView

選択されたアイテムを取得する方法。

TCustomListView.GetNextItem メソッド

TListを継承して独自のリストを作成。ついでにソートもしてみる。

TListはメモリの確保をしないので、メモリの確保と解放をするように実装。
TObjectListを参考に実装してみました。
とりあえず作ってみた物の、悩み中。

実装

unit IkCustomList; interface uses Classes; type TIkDoubleList = class(TList) Protected procedure Notify(Ptr: Pointer; Action: TListNotification); override; function GetItem(Index: Integer): Double; procedure SetItem(Index: Integer; const Value: Double); public procedure Clear; override; function Add(Value: Double): Integer; property Items[Index: Integer]: Double read GetItem write SetItem; default; end; function CompareDouble(item1,item2:Pointer):Integer; implementation //Sort用比較関数 function CompareDouble(item1, item2: Pointer): Integer; begin if PDouble(item1)^ < PDouble(item2)^ then Result:=-1 else if PDouble(item1)^ > PDouble(item2)^ then Result:=1 else Result:=0
end; { TIkDoubleList } function TIkDoubleList.Add(Value: Double): Integer; var p:PDouble; begin New(p); p^:=Value; Result:=Inherited Add(p) end; //この方法だと、Extractの戻り値が使えなくなる。が、実害はなさそう。 procedure TIkDoubleList.Notify(Ptr: Pointer; Action: TListNotification); begin if Action = lnDeleted then begin Dispose(Ptr); end; inherited Notify(Ptr, Action); end; procedure TIkDoubleList.Clear; var i:integer; begin for i := 0 to Count - 1 do Delete(0);//ClearはTListでは要素の解放をしない。ちなみにDestroyではClear呼出しが存在する。 inherited Clear; end; function TIkDoubleList.GetItem(Index: Integer): Double; begin Result:= PDouble(inherited Items[index])^; //Items[index]をアクセスすると、TList.Getが呼ばれる //TList.Getはポインタを返す。 //ポインタを逆参照して値を返す。 end; procedure TIkDoubleList.SetItem(Index: Integer; const Value: Double); begin PDouble(inherited Items[index])^:=Value; //Items[index]をアクセスすると、TList.Getが呼ばれる //TList.Getはポインタを返す。 //ポインタを逆参照して値を入れる。 end; end.

使い方

TMemoとTButtonをはりつけて、下記ソースみたくする。
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls,IkCustomList; type TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var dlist:TIkDoubleList; i:integer; begin dlist:=TIkDoubleList.Create; try for i := 0 to 100 do dlist.Add(Random()*100); dlist.Sort(@CompareDouble); for i := 0 to 100 do Memo1.Lines.Add( FloatToStr(dlist.Items[i])); finally dlist.Free; end; end; end.

Indy


UDPテキスト通信例

10.2Tokyo
TIdUDPClient,TIdUDPServer,TMemo,TEditをフォーム上にドロップ。

procedure TForm1.Button1Click(Sender: TObject); begin IdUDPClient1.Send(Edit1.Text); end; procedure TForm1.FormCreate(Sender: TObject); begin IdUDPClient1.Host := '127.0.0.1'; IdUDPClient1.Port := 55555; IdUDPClient1.Active := true; IdUDPServer1.DefaultPort := 55555; IdUDPServer1.Active := true; end; procedure TForm1.IdUDPServer1UDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle); var str: string; begin str := TEncoding.ASCII.GetString(AData); Memo1.Lines.Add('UDPRead = ' + str); end;

UDPバイナリ通信例

11.3 Alexandria
TIdUDPClient,TIdUDPServer,TMemo,TEditをフォーム上にドロップ。

procedure TForm1.btnStartClick(Sender: TObject); var portno:UInt16; begin portno:=5555; // Client IdUDPClient1.Host := '127.0.0.1'; IdUDPClient1.Port := portno; IdUDPClient1.Active := true; // Server IdUDPServer1.DefaultPort := portno; IdUDPServer1.Active := true; end; procedure TForm1.btnSendClick(Sender: TObject); var data: TIdBytes; begin SetLength(data, 4); for var i := Low(data) to High(data) do data[i] := i; IdUDPClient1.SendBuffer(data); end; procedure TForm1.IdUDPServer1UDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle); var str: string; begin Memo1.Lines.Add('Length(AData)=' + Length(AData).ToString); str := ''; for var i := Low(AData) to High(AData) do begin str := str + IntToHex(AData[i], 2) + ' '; end; Memo1.Lines.Add('AData = ' + str); end;

UDPバイナリ通信例2

UDPで送ったデータを受信して、TChartでグラフ表示
IndyUDPandTChart GitHub


TCP通信例


ボタン2個、メモ1個、IdTcpServer 1個、IdTcpClient 1個 をフォーム上に置く。

unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdContext, Vcl.StdCtrls, Vcl.ExtCtrls, IdCustomTCPServer, IdTCPServer, IdCmdTCPServer, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdCmdTCPClient; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; Button2: TButton; IdTCPClient1: TIdTCPClient; IdTCPServer1: TIdTCPServer; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure IdTCPServer1Execute(AContext: TIdContext); private { Private 宣言 } public { Public 宣言 } procedure memo(str: string); end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin IdTCPClient1.Host := '127.0.0.1'; IdTCPClient1.Port := 2268; IdTCPServer1.DefaultPort := 2268; IdTCPServer1.Active := true; memo('ServerPort = ' + IdTCPServer1.DefaultPort.ToString); IdTCPClient1.Connect; end; procedure TForm1.FormDestroy(Sender: TObject); begin IdTCPClient1.Disconnect; end; procedure TForm1.Button1Click(Sender: TObject); begin IdTCPClient1.IOHandler.WriteLn('hello'); // 送信する end; procedure TForm1.Button2Click(Sender: TObject); var text: String; begin text := IdTCPClient1.IOHandler.ReadLn; memo(text); end; procedure TForm1.IdTCPServer1Execute(AContext: TIdContext); var text: string; begin text := AContext.Connection.IOHandler.ReadLn; // 受信する memo(text); AContext.Connection.IOHandler.WriteLn('hi [' + text + ']'); // 応答を返す END; procedure TForm1.memo(str: string); begin Memo1.Lines.Add(str); end; end.

TCPとUDPの通信例

ソケットライブラリのIndyを利用したクリップボード共有ソフト Delphi7+Indy9
ソケットライブラリのIndy10を利用してみよう C++Builder2007+Indy10

Indy10 IdFTP

Indy
List→ListResultは、ファイル名とかになっていないので不便。

List→DirectoryListingは、便利なのだが、使おうとするといきなりエラー。
で、以下の追加が必須。
uses IdAllFTPListParsers
参考:FTPでlistがDirectoryListingに入らない。

TIdHTTPServer.AutoSession

INDY10 XE5
こうしないとAutoSession有効にならない?
AutoSession=true
SessionState=true
SessionTimeOut=1以上

TIdTCPServer.OnExecuteでポート番号

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext); begin memo(AContext.Binding.Port.toString);

TIdTCPServerの接続数

未確認だがこれっぽい?
memo(IntToStr(IDTCPServer1.Contexts.Count));

iOS,Indy,XML

http://blogs.embarcadero.com/teamj/2013/05/16/3877/

Indy
Delphi XE5 -> Indy 10.6.0.5122
Delphi XE6 -> Indy 10.6.0.5040
2014-05-10 release -> Indy 10.6.0.5138

TIdHttpServer

XE6
OnConnect,OnDisconnectにコンポーネントアクセスするような処理入れておくと、serverが終了しない。(例外が発生したりなんだりと...)
様な気がする。

Indy.TIdHttpでSSL通信が失敗する

Delphi11.3

IndyのTIdHttpでhttpsのポストをするとエラーが発生する。
「SSL routinessl3_read_bytes:tlsv1 allert protocol version.」


解決方法:Indy ネットワーク接続のセキュリティ保護

以下手順

libeay32.dllと、ssleay32.dllをダウンロード、DLLをexeと同じフォルダに置く
http://indy.fulgan.com/SSL/

フォームにTIdSSLIOHandlerSocketOpenSSL を配置

TIdHttpのIOHandlerに配置したTIdSSLIOHandlerSocketOpenSSLを割り当て

TIdSSLIOHandlerSocketOpenSSL.SSLOptions.SSLVersions=sslvTLSv1_2に変更

これで動く。

コンポーネントのインストール方法

参考:コンポーネントのインストール方法等

インストール

Delphi2007

CodeGear RAD Studio 2007 ISO (Dec 2007)
これを入れる。以下が入っている。
December 2007 Update
February 2008 Help Update

そんでもって、「スタート→CodeGear RAD Studio→アップデートの確認」で更新をかけるとMay08 Help Updateが入る。
注意:2009→2007入れるとはまるのでやめとけー

glyFXはどこー?

きれいなアイコンglyFXは
C:\Program Files\Common Files\CodeGear Shared\Images\glyFX C:\Program Files\Common Files\Borland Shared\Images\glyFX
引用:glyFX Embarcadero Special Edition Icon Set for Embarcadero Customers

グローバル変数、ローカルな変数

interface部に書けばグローバル。
implementationに書けばローカル。

色定数

Delphi2010
Delphi色定数の一覧

KeyPressイベント

Delphi2010
if Key = Char(VK_RETURN) then

DLL

DelphiXE2
http://www.s-m-l.org/csharp.html#DLLを呼ぶ
library testdll1; uses System.SysUtils, System.Classes; {$R *.res} procedure func1(path: PAnsiChar; opt: LongWord); stdcall; var strlist:TStringList; begin strlist:=TStringList.Create; try strlist.Add(path); strlist.SaveToFile('d:\testdll1.txt'); finally strlist.Free; end; end; exports func1; begin end.

DLL呼び出し

DelphiXE2
procedure func1(path: PAnsiChar; opt: LongWord); stdcall; external 'testdll1.dll'; procedure TForm1.Button1Click(Sender: TObject); var str: AnsiString; begin str := AnsiString(Edit1.Text); func1(PAnsiChar(str), 1); end;

DLLの遅延ロード

Delphi2010

dll呼び出し宣言に、delayedを追加。
参考:Team Japan ≫ Delphi 2010: 新機能 delayed ディレクティブ

ディレクトリ選択ダイアログ

uses FileCtrl; var path:string; begin if SelectDirectory('Caption','',path) then //真ん中は初期パス exec_proc(path); end;

第16回エンバカデロ・デベロッパーキャンプ

第16回エンバカデロ・デベロッパーキャンプ - 資料ダウンロード

景品当たった!わーいw


文字列処理とエンコーディング


Delphi2010での文字列処理

2010-03-10のセミナーメモ
SKRegExp
TIniFile→TMemoIniFileでEncoding指定
TStringListのLoad,Saveはエンコーディング指定を推奨
ExcelはUTF-16のCSV読み込み対応しない
SysUtils.StringOf(),WideStringOf()
MECSUtils

UTF-8エンコーディング指定して保存。BOM無し。

10.2tokyo
var strlist:TStringList; begin strlist:=TStringList.Create; strlist.Add('あいう'); strlist.WriteBOM:=false; strlist.SaveToFile('tmp.txt', TEncoding.UTF8);

TStringGrid


表示が従来と違う。サンプル例とかと違う。

DrawingStyleをgdsThemedからgdsClassicにする

値が変更できない、Edit出来ない


Options->goEditing=true にする。

選択範囲の取得

ClickやDoubleClickイベントで、クリックしたセルの特定にも使える。
var rc:TGridRect; begin rc:=StringGrid1.Selection;

TColorとRGB


uses Windows; color:TColor; r,g,b:Byte; color:=RGB(255,128,64); r = GetRValue(color); g = GetGValue(color); b = GetBValue(color); color := RGB(r,g,b);

ダイアログ XE10.1

ShowMessage を使うと簡単なメッセージ表示可能
SelectDirectory を使うと簡単にディレクトリ選択可能
MessageDlg を使うと簡単な問い合わせ可能。※FMXでは非推奨

定義
System.UITypes.TMsgDlgType.mtConfirmation
System.UITypes.TMsgDlgBtn.mbRetry

if MessageDlg('実行しますか?', TMsgDlgType.mtConfirmation, [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], 0) <> mrYes Exit; end;

FMXでは非推奨なのでこっちを使う
uses FMX.DialogService.Sync; if TDialogServiceSync.MessageDialog('ファイルの先頭から検索を始めますか', TmsgDlgType.mtConfirmation, [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], TMsgDlgBtn.mbYes, 0) = mrYes then begin // end;

乱数

Randomize
Random(Range)
0 <= X < Range

DUnit

とりあえずDelphi2010でやってみる。

テストユニット実行
uses GUITestRunner GUITestRunner.RunRegisteredTests;
テスト対象の記述(簡単なクラス)
delphi/Calc.pas

テストの記述
SetupとTearDownは、各テスト(procedure)毎に呼び出される。
delphi/TestCalc.pas

TFlowPanel

Delphiでフローレイアウト
ControlIndexで表示順序を変更可能。

Formの重なり順序

メインフォームと別のフォームの重なり順序が変更出来ない。
プロジェクトソースの以下をコメントアウトすると、順序が変更出来る。
Application.MainFormOnTaskbar := True;
参考:フォームの重なり順序を変更する方法は?

ファイル名操作

uses SysUtils
ExtractFilePath ファイル名からパスを取り出す
ChangeFileExt 拡張子を変更
IncludeTrailingPathDelimiter パス区切り文字を追加

uses System.IOUtils
TPath.Combine(path1,path2) 二つのパスを結合する

ファイルとフォルダの有無確認

ファイルの有無確認はFileExists
ディレクトリの有無確認はDirectoryExists
if not DirectoryExists('xyz') then MkDir('xyz');
ファイル操作
uses System.IOUtils TFile.Copy TFile.Move TFile.Delete TFile.Exists //FileExistsと同じ?

ファイルの列挙

深い階層まで検索したい場合は、再帰呼び出しを実装する必用有り。

procedure get_file_list(items: TStrings; find_str: string); // 1階層のみ var SearchRec: TSearchRec; begin // FindFirst が成功した場合のみ FindClose を呼ぶ必要がある if 0 = FindFirst(find_str, faAnyFile, SearchRec) then begin try repeat if (SearchRec.Attr and faDirectory) = 0 then begin // ファイル items.Add(SearchRec.Name) end; until 0 <> FindNext(SearchRec); finally FindClose(SearchRec); end; end; end;

ディレクトリ操作

ディレクトリ作成 SysUtils.ForceDirectories
ディレクトリ削除 SysUtils.RemoveDir
ディレクトリ存在確認 SysUtils.DirectoryExists
procedure TForm1.Button1Click(Sender: TObject); var path: string; begin path := tmp_path; if DirectoryExists(path) then begin memo('Exists ' + path); end else begin memo('not exists ' + path); ForceDirectories(path); // 絶対パス・複数ディレクトリ memo('ForceDirectories ' + path); end; if DirectoryExists(path) then begin memo('Exists ' + path); end else begin memo('not exists ' + path); ForceDirectories(path); end; end; procedure TForm1.Button2Click(Sender: TObject); begin RemoveDir(tmp_path); // SysUtils end; procedure TForm1.memo(str: string); begin Memo1.Lines.Add(str); end; function TForm1.tmp_path: string; begin Result := ExtractFilePath(Application.ExeName) + 'tmp'; end; // 似た様な処理を行う関数 // Sysutils.CreateDir // System.MkDir // System.RmDir

パス取得

特殊フォルダのパスを取得

サポートされているターゲット プラットフォームに適した標準の RTL パス関数

TPathは
System.IOUtils
にて定義。

torutkの日記→Windowsのディレクトリ構成ガイドライン

Mr.XRAY→アプリケーション固有のデータの置き場所

Delphi10.1 + Windows7
TPath.GetHomePathC:\Users\{ユーザー名}\AppData\Roaming設定データ Roaming/{CompanyName}/{AppName}
TPath.GetCachePathC:\Users\{ユーザー名}\AppData\Local消えても良いデータ
TPath.GetTempPathC:\Users\{ユーザー名}\AppData\Local\Temp\アプリケーション終了後に消えても良いデータ
TPath.GetLibraryPathD:\data\trunk\Lab\Delphi10\path\Win32\Debug\
TPath.GetDocumentsPathC:\Users\{ユーザー名}\Documents
TPath.GetSharedDocumentsPathC:\Users\Public\Documents
TPath.GetPicturesPathC:\Users\{ユーザー名}\Pictures
TPath.GetSharedPicturesPathC:\Users\Public\Pictures
TPath.GetPublicPathC:\ProgramData
TPath.GetCameraPathC:\Users\{ユーザー名}\Pictures
TPath.GetMusicPathC:\Users\{ユーザー名}\Music
TPath.GetSharedMusicPathC:\Users\Public\Music
TPath.GetMoviesPathC:\Users\{ユーザー名}\Videos
TPath.GetSharedMoviesPathC:\Users\Public\Videos
TPath.GetAlarmsPathC:\Users\{ユーザー名}\Music
TPath.GetSharedAlarmsPathC:\Users\Public\Music
TPath.GetRingTonesPathC:\Users\{ユーザー名}\Music
TPath.GetSharedRingTonesPathC:\Users\Public\Music
TPath.GetDownloadsPathC:\Users\{ユーザー名}\AppData\Local
TPath.GetSharedDownloadsPathC:\ProgramData

時間を計るTStopWatch


http://ht-deko.com/techf021.html

msec単位で計測


uses System.Diagnostics var sw:TStopWatch; begin sw:=TStopWatch.StartNew; //処理色々 //: //: sw.Stop; memo(sw.ElapsedMilliseconds.ToString); //swはrecordなので、Freeしなくても良い

高解像度で計測


uses System.Diagnostics; var sw: TStopWatch; tm: Double; begin sw := TStopWatch.StartNew; sleep(100); sw.Stop; Memo1.Lines.Add('sw.ElapsedMilliseconds=' + sw.ElapsedMilliseconds.ToString); Memo1.Lines.Add('sw.ElapsedTicks=' + sw.ElapsedTicks.ToString); Memo1.Lines.Add('sw.Frequency=' + sw.Frequency.ToString); tm := (1.0 / sw.Frequency) * sw.ElapsedTicks; Memo1.Lines.Add('高精度時間(sec)=' + FloatToStr(tm)); end;
実行結果
sw.ElapsedMilliseconds=111 sw.ElapsedTicks=1113758 sw.Frequency=10000000 高精度時間(sec)=0.1113758

プロパティで関数


type IxAddNewFunc = procedure (caption:String) of Object; type IxTest = class(TObject) protected FAddNew:IxNewFunc; public property AddNew:IxAddNewTabFunc read FAddNew write FAddNew; end; ---- procedure add_new(caption: string); ---- var tst:IxTest; begin tst:=IxTest.Create; tst.AddNew := add_new;

便利なTStringList

CSVをデコードしたり、Name=Value形式をデコードしたり出来る。
var strlist:TStringList; begin strlist:=TStringList.Create; try strlist.CommaText:='a=1,b=2,c=3'; memo('---- Strings[] ----'); memo(strlist.Strings[0]); memo(strlist.Strings[1]); memo(strlist.Strings[2]); memo('---- Values[] ----'); memo(strlist.Values['a']); memo(strlist.Values['b']); memo(strlist.Values['c']); memo(strlist.Values['d']);//エラーにはならない finally strlist.Free; end; end;

Editでキーイベント

procedure TfrmSettings.Edit2KeyPress(Sender: TObject; var Key: Char); begin if Ord(Key) = VK_RETURN then begin memo('hit return'); end; end;

スレッド


<1> スレッドオブジェクト (Delphi コンカレントプログラミング)

リンク切れ Delphi Tips - マルチスレッドアプリケーション スレッドの書き方。だいたいのこと書いてある。

System.Clases.TThreadd
スレッド管理ルーチン

System.SyncObjs


無名メソッドとスレッド

無名メソッドとスレッド 無名メソッドでスレッドのSynchronizeを書く
利点:引数を渡すために別途変数を定義する必要がなくなる
欠点:速度的に若干のペナルティ、構文知らないと読めない

Delphiで非同期プログラミング

TAnonymousThread

Samples\Delphi\RTL\CrossPlatform Utils\AnonThread.pas

  1. ジェネリクスを使っている為、スレッド処理の戻り値を指定可能
  2. 終了処理は、通常の終了と、例外の2種類を記述可能
  3. 終了処理は、TThread.OnTerminateから呼び出されるので、Synchronizeがいらない。System.Classes.TThread.OnTerminate
  4. メイン中に記述すれば、受け渡しの変数を作成しなくても済む とかかなー。便利そう。

TAnonymouseThread.Startの例


関数をスレッドとして呼び出し

type TForm1 = class(TForm) private master_counter: integer; run_model_thread: boolean; procedure ModelThread; { Private 宣言 } public { Public 宣言 } end; // ************************************************************************* procedure TForm1.FormCreate(Sender: TObject); begin master_counter := 0; end; // ************************************************************************* procedure TForm1.btnStartClick(Sender: TObject); begin run_model_thread := true; TThread.CreateAnonymousThread(ModelThread).Start; // スレッド開始 end; // ************************************************************************* procedure TForm1.btnStopClick(Sender: TObject); begin run_model_thread := false; // スレッド停止 end; // ************************************************************************* procedure TForm1.ModelThread; var i: integer; begin i := 0; while run_model_thread do begin sleep(10); inc(i); if i >= 10 then begin i := 0; inc(master_counter); end; end; end;

Cross platform anonymous threads and progress notification Samplesに入っているTAnonymousThreadの解説
TAnonymousThreadは、MAC,iOSではTNSAutoreleasePoolを実装しています。
iPhoneアプリ開発時のメモリ管理で気をつけること(マルチスレッド編) TNSAutoreleasePoolをスレッド毎に実装しないとメモリリークする。

スレッドの同期

スレッドの調整

タスクが完了するまで待つ

Eventオブジェクト

名前付きイベントを使ったスレッド例

名前付きイベントは、別アプリケーションとも名前を共有するので要注意。
名前はGUID使った方がよいと思います。

unit IxNamedEventThread; interface uses System.Classes, System.SyncObjs; type TIxNamedEventThread = class(TThread) protected event: TEvent; procedure Execute; override; end; implementation uses Unit1; procedure TIxNamedEventThread.Execute; begin event := TEvent.Create(nil, false, false, EVENT_NAME_SAMPLE); while not Terminated do begin if event.WaitFor(200) = wrSignaled then begin Break; end; end; end; end.

名前付きイベント発行例

(名前なくても同じだけど...)
unit Unit1; const EVENT_NAME_SAMPLE = '{BBCF957F-05C4-460C-9EE1-00FA6184E57F}'; var event_named: TEvent; event_thread: TIxEventThread; procedure TForm1.FormCreate(Sender: TObject); begin event_named := TEvent.Create(nil, false, false, EVENT_NAME_SAMPLE); named_event_thread := TIxNamedEventThread.Create; end; procedure TForm1.btnNamedEventEndClick(Sender: TObject); begin event_named.SetEvent; end;

クリティカルセクション

lock,unlock,ロック,アンロック
uses System.SyncObjs; var FLock:TCriticalSection; : FLock := TCriticalSection.Create; : FLock.Acquire; try //競合する処理 finally FLock.Release; end;
クリティカル セクションを使用する

ちなみに
Acquire = Enter
Release = Leave

SynchronizeとQueue

メインスレッドと同期を取るには、SynchronizeとQueueがある。
どちらも無名メソッドを利用して書くことが出来る。
http://www.gesource.jp/weblog/?p=538

スレッドの終了処理でリーク

OnTerminateに定義するのが正規のやりかたっぽいのですが、ReportMemoryLeaksOnShutdown=Trueにして、FormDestroyでスレッドをTerminateすると、リーク判定されてしまう。
type TIxDataThread = class(TThread) protected public ViewManager: TIxViewManager; constructor Create; procedure OnTerminateExecute(Sender: TObject); end; implementation { IxDataThread } constructor TIxDataThread.Create; begin ViewManager := TIxViewManager.Create; OnTerminate := OnTerminateExecute; inherited Create(true); // true:生成時停止(Startで開始) false:生成時即実行 FreeOnTerminate := true; // 終了時解放 end; procedure TIxDataThread.OnTerminateExecute(Sender: TObject); begin ViewManager.Free; end;

スレッドの終了処理

スレッドで例外しても、メイン側でちゃんと終了させる。
>EurekaLogメモ→スレッドの例外を補足する

スレッド間データ受け渡し

System.Generics.Collections
TThreadedQueue
var Queue: TThreadedQueue<TIxRawData>; // 実体 生データが入る。Executeで待つ constructor TIxThreadDataPacker.Create; begin Queue := TThreadedQueue<TIxRawData>.Create(1000, INFINITE, 100); // Depth, PushTimeout, PopTimeout end; procedure xyz; var data: TIxRawData; wr: TWaitResult; begin while true do begin // 待ち wr := Queue.PopItem(data); if (wr = TWaitResult.wrSignaled) and (data <> nil) then //★キューをdoShutdownした時、wrSignaledでdata=nilになる begin // Signaledの時はデータ有効 end else begin break; end;
TThreadList

スレッド同期


TCriticalSection

uses System.SyncObjs; var DataLock:TCriticalSection; procedure TFormMain.FormCreate(Sender: TObject); begin DataLock := TCriticalSection.Create; end; procedure TFormMain.FormDestroy(Sender: TObject); begin DataLock.Free; end; procedure TFormMain.lamp_red_on; var str: string; begin DataLock.Acquire; try memo('ランプ点灯'); finally DataLock.Release; end; end;

TThread,TThreadedQueueサンプル

Delphi11.3

スレッドのキューに文字列を渡す
スレッドの処理が終わるまでボタンダウンの関数を抜けない

フォーム作成時にスレッド作成、フォーム破棄時にスレッド停止
TThread
TThreadedQueue
IFMXCursorService
https://github.com/mikekoma/DelphiLabo/tree/master/Thread1

TThread,TQueueサンプル

Delphi11.3

スレッドのキューに文字列を渡す
スレッドの処理が終わるまでボタンダウンの関数を抜けない

処理毎にスレッド生成、処理終了時にスレッド破棄
TThread
https://github.com/mikekoma/DelphiLabo/tree/master/Thread2

デバッグ出力

表示→デバッグ→イベントログ

uses Windows; OutputDebugString(PWideChar(index+' '+IntToStr(ofs)+' = '+Result));
keyword dbgout

TActionList


別フォームのActionを使う

Actionを使いたいFormで、Actionを定義しているフォームをUsesする。
そうすると、ActionのプロパティでForm1.Action1見たいに表示される。

コンパイラ指令

DelphiC言語
{$DEFINE XYZ}#define XYZ
{$I xyz.inc}#include "xyz.inc"
{$IFDEF XYZ}
{$ENDIF}
#ifdef XYZ
#endif
{$IF exp}
{$ELSE}
{$IFEND}
#if exp
#else
#endif

条件付きコンパイル(Delphi)定義済みのシンボル

DebugとReleaseで条件コンパイルする
DEBUG,RELEASEは、プロジェクト→オプション→Delphiコンパイラ→条件定義で定義してある
{$IFDEF DEBUG} {$ENDIF} {$IFDEF RELEASE} {$ENDIF}
使いそうなの
MSWINDOWSWindows、WIN32,WIN64よりこっち使う
MACOSMac OS

ソースコードの折りたたみ

リージョン
{$REGION 'コメント'} {$ENDREGION}
REGION

ショートカットキー


ソース整形,フォーマッタ

CTRL+D

改行文字数は
ツール⇒オプション⇒言語⇒フォーマッタ⇒Delphi⇒改行⇒右マージン
で指定する


ソース コードを整形する

カーソル位置のクラス宣言補完(コード生成)

CTRL+SHIFT+C

同期編集(リファクタリングより軽くて便利)

CTRL+SHIFT+J

選択してTAB インデント→
選択してSHIFT+TAB インデント←

デフォルトのキーボード ショートカット

OSXで動的読み込み可能ライブラリ(dylib)を遅延読み込みできない

https://forums.embarcadero.com/thread.jspa?threadID=80777&tstart=15

DeleteFileでワーニング

usesの順序が重要。順序によってはH2443のワーニングが出る。
正しい順序
Windows,
SysUtils,
Classes,

タイプライブラリの取り込み

タイプライブラリの取り込み

JSONを使う

DelphiXE4

uses Data.DBXJSON, //これ追加 Data.DBXPlatform; //for in do 使うのにこれあった方が良い procedure TForm1.Button1Click(Sender: TObject); var json_obj: TJSONObject; begin json_obj := TJSONObject.Create; try json_obj.AddPair('key1', 'val1'); json_obj.AddPair('key2', 'val2'); Memo1.Lines.Add(json_obj.ToString); finally json_obj.Free; end; end; procedure TForm1.Button2Click(Sender: TObject); var json_obj: TJSONObject; pair: TJSONPair; begin json_obj := TJSONObject.ParseJSONValue('{"key1": "val1", "key2": 123}') as TJSONObject; // パース失敗するとnil try for pair in json_obj do begin Memo1.Lines.Add(pair.JsonString.Value + '->' + pair.JsonValue.Value); end; finally json_obj.Free; end; end;
Button1を押した結果
{"key1":"val1","key2":"val2"}
Button2を押した結果
key1->val1 key2->123
DBXPlatform無い時のヒント
[dcc32 ヒント] Unit1.pas(51): H2443 インライン関数 'TJSONPairEnumerator.GetCurrent' はユニット 'Data.DBXPlatform' が USES リストで指定されていないため展開されません
http://d.hatena.ne.jp/nullpobug/20121025/1351170571

リモートデバッグ(paserver)

クロスプラットフォーム アプリケーションのデバッグ(新しいリモート デバッグ方法)

  1. paserverをインストール
  2. paserverを実行
  3. 接続プロファイルを作成


paserverのインストール


C:\Program Files\Embarcadero\RAD Studio\n.n\PAServer\setup_paserver.exe n.n はリリース バージョン(XE5 では 12.0)を表します。
参考:Windows でのプラットフォーム アシスタントのインストール


paserverの実行

C:\Program Files\Embarcadero\RADPAServer\n.n\paserver.exe n.n はリリース バージョン(XE5 では 12.0)を表します。
参考:Windows でのプラットフォーム アシスタントの実行

接続プロファイルの作成

メニュー→ツール→オプション→環境オプション→接続プロファイルマネージャ

参考:接続プロファイル マネージャ

アプリケーションの配置

アプリケーションと、その他ファイルをリモートに配置するには、配置マネージャを使用します。
メニュー→プロジェクト→配置

配置マネージャで、配置(緑右矢印)をクリックすると、接続プロファイルを割り当てできる。

配置マネージャで、リモートマシンに接続(PCが2台)をクリックすると、リモートマシンに接続

F9を押すと、配置して実行される。

リモートデバッグからもとに戻す

プロジェクトマネージャ→ファイル→ProjectGroup1→XXX.exe→ターゲットプラットフォーム→32ビットウィンドウズ を右クリックして、「デフォルトの接続に戻す」を選択。

メモリリーク検出


FastMM Delphi11 + FastMM-4993

FastMMでメモリーリークを確認してみる
Delphi11 Update2

FastMM4


サンプルソースを参考にする
FastMM4\Replacement BorlndMM DLL\Delphi

手順


ソースフォルダにファイルこぴってくる

プロジェクトソース変更
program testFastMM; uses ShareMem,★追加 System.StartUpCopy, FMX.Forms, Unit1 in 'Unit1.pas', {Form1} FastMMDebugSupport in 'FastMMDebugSupport.pas';★追加 {$R *.res} begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end.
exeの実行フォルダにDLLをコピー
確認用にボタンにリークする処理追加
procedure TForm1.Button1Click(Sender: TObject); begin TObject.Create; end;
実行して、ボタンクリック後終了するとメモリーリーク検知してダイアログ表示される。
実行フォルダにはログが出力される。
今回の場合は「testFastMM_MemoryManager_EventLog.txt」

FastMM DelphiXE6 + FastMM-4991


リークの検出(Delphi2006以降)

プロジェクトソースに1行追加するだけ
begin ReportMemoryLeaksOnShutdown := True; Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end.
Delphiアプリケーションのメモリリーク検出法

リークの検出方法(詳細)

http://fastmm.sourceforge.net ダウンロードはここから。
FastMMDebugSupport.pasをプロジェクトフォルダにコピー
BorlndMM.dll,FastMM_FullDebugMode.dllをDebugフォルダ(exe生成フォルダ)にコピー

プロジェクトソースにShareMem追加
program test_fastmm; uses ShareMem, Vcl.Forms,
こうすると、終了時にリークを表示し、ログファイルも生成してくれる。
{プロジェクト名}_MemoryManager_EventLog.txt
ログから、自分のソースファイル名とか検索すれば、リークしているメモリを確保している場所がわかる。

メモリリーク検出(Delphi200x)

http://komish.com/delphi/memoryleak.htm
ReportMemoryLeaksOnShutdown := True

DLL,dylibをダイナミックロード

動的にロードする方法
http://d.hatena.ne.jp/nullpobug/20130616/1371314885

キー入力の仮想キーコード

http://docwiki.embarcadero.com/Libraries/Seattle/ja/System.UITypes

マウスカーソルを隠す(非表示)

ShowCursor関数の戻り値が、0未満の時にマウスカーソルが非表示になる。
非表示になるのは、自分のウィンドウ
uses Windows; ShowCursor(false);

Canvas.FillRect

四角塗りつぶし
Delphi11.3
bmp.Canvas.Brush.Color := clWhite; bmp.Canvas.Brush.Style := bsSolid; bmp.Canvas.FillRect(Rect(grad_hi, 0, bmp.Width, bmp.Height));

Canvas.CopyRect

CopyRectと、裏画面のサンプル
https://github.com/mikekoma/DelphiLabo/tree/master/CopyRect


TRectの当たり判定とか演算

色々あるんだ...http://docwiki.embarcadero.com/Libraries/Berlin/ja/System.Types.TRect_Methods

四角と点http://docwiki.embarcadero.com/Libraries/Berlin/ja/System.Types.TRectF.Contains
四角同士http://docwiki.embarcadero.com/Libraries/Berlin/ja/System.Types.TRectF.IntersectsWith

四角形の大きさを拡張
Inflate http://docwiki.embarcadero.com/Libraries/Berlin/ja/System.Types.TRect.Inflate

32bitのOSで印刷出来ない

Delphi2010,Delphi10.1
Win7 32bit,Win10 32bit

32ビットアプリを作成し、32ビットOSで印刷しようとすると例外が発生する。
印刷ダイアログを出そうとすると例外が発生する。
全てのプリンタではなく、プリンタによって発生する。
調べてみるとDocumentProperties(Win32API)の呼び出しで例外発生。


対策
浮動小数点の制御を変更する。

検索したらここへ
http://stackoverflow.com/questions/3090439/problems-running-a-32-bit-delphi-app-on-a-64-bit-windows-platform
↓ここからここへ
http://delphigroups.info/2/4/973047.html

Nard Moseley (Digital Metaphors)のソース
var lSaveCW: Word; begin lSaveCW := Default8087CW; Set8087CW(Default8087CW or $3f); try myReport.Print; finally Set8087CW(lSaveCW); end; end;
$3fをorすると、浮動小数点演算の例外を全てマスク出来る。

docwiki System.Set8087CW

GUIDの生成

GUID およびインターフェイス サポート ルーチン

var guid: TGUID; begin if CreateGUID(guid) = S_OK then begin memo(GUIDToString(guid)); end else begin memo('GUIDの生成に失敗。'); end;

コードの折りたたみ

REGION

{$REGION 'コメント'} : {$ENDREGION}

ブレークポイントがずれる

10.2.3 Tokyo
ブレークポイントが、ソースとずれる現象が発生。リビルドしようがDCU消そうが、何しようがダメ。
原因は、ソースファイル(pas)の改行コード異常だった。
改行コードに$0A$0Dが混ざっていて、それを$0D$0Aに修正したところ、正常にブレーク可能になった。

図:ブレーク位置異常


改行コードが異常なソース(address=$000B,$0238)


改行コードが正常なソース


なんか例外出る0xFEEEFEEE

ファイルをダイアログで開いて、スレッドで読み込みして少し経過後例外が出る。
ファイル読み込みに使用したfsは、fs.freeしている。

対策

fs.free後fs:=nilしたら出なくなった。ような気がしたがやっぱりたまに出てる。

なんか↓に似ている
http://rarara.cafe.coocan.jp/cgi-bin/lng/vc/vclng.cgi?print+201205/12050011.txt


リソースからファイル読み込み

メニューのプロジェクト→リソースと画像
テキストファイルを追加
追加すると、プロジェクトにも追加される。
下の例のResource_1は読み込んだ時の識別子にする
procedure TForm1.Button1Click(Sender: TObject); var List: TStringList; Stream: TResourceStream; begin Stream := TResourceStream.Create(HInstance, 'Resource_1', RT_RCDATA); try List := TStringList.Create; try List.LoadFromStream(Stream); Memo1.Text := List.Text; finally List.Free; end; finally Stream.Free; end; end;

バージョン管理対象

RAD Studio によって生成されるファイルの拡張子

必須
*.pasPascalソース
*.dfmVCLフォーム
*.fmxFMXフォーム
*.dprプロジェクトファイル
*.dprojプロジェクトファイル。コンパイル設定やリソース設定が入る
*.groupprojプロジェクトグループ。グループ依存無ければなくても良い

EurekaLog

EurekaLogでスレッド

EurekaLogメモ

DelphiとPython


手順はこちらの通り
Delphi のフォームを Python から使う方法

Pythonでコードを追加したいイベントは、Delphiで空のイベントを定義しておく。

Delphi11.3
Ubuntu22.04.2 LTS
Python 3.10.6
delphifmx 1.0.6



import os from delphifmx import * class Form1(Form): def __init__(self, owner): self.Panel1 = None self.Button1 = None self.Edit1 = None self.ListBox1 = None self.CheckBox1 = None self.GroupBox1 = None self.RadioButton1 = None self.RadioButton2 = None self.RadioButton3 = None self.CheckBox2 = None self.AniIndicator1 = None self.Button2 = None self.Button3 = None self.Splitter1 = None self.Memo1 = None self.OpenDialog1 = None self.SaveDialog1 = None self.Calendar1 = None self.LoadProps(os.path.join(os.path.dirname(os.path.abspath(__file__)), "Unit1.pyfmx")) def Button1Click(self, Sender): str = self.Edit1.Text self.Edit1.Text = "" self.ListBox1.Items.Add(str) self.Memo1.Lines.Add(str) def CheckBox1Change(self, Sender): self.AniIndicator1.Enabled = not self.AniIndicator1.Enabled def Button2Click(self, Sender): self.Memo1.Lines.SaveToFile('test.txt'); def Button3Click(self, Sender): self.Memo1.Lines.LoadFromFile('test.txt'); def main(): Application.Initialize() Application.Title = 'Form1' Application.MainForm = Form1(Application) Application.MainForm.Show() Application.Run() Application.MainForm.Destroy() if __name__ == '__main__': main()

出力ディレクトリの指定

dproj削除したりすると、出力ディレクトリ指定が無くなって、dprと同じディレクトリにexeが出来たりする。
プロジェクト⇒オプション⇒ビルド⇒Delphiコンパイラ⇒出力ディレクトリ(と、ユニットの出力ディレクトリ)を指定する
.\$(Platform)\$(Config)


高DPI対応

Delphi12

RAD Studio 11の新機能:「はっきり、くっきり」表示の高DPI対応IDEとフォームデザイン

RAD Studio 12 Athens アプリケーションマニフェスト
RAD Studio 12 Athens Application Manifest

ざっくりいうと、拡大時いまいちなアプリケーションは、GDIスケーリングに設定したほうがよいかも。
詳細は↓
DPI認識/DPI Awareness

リンク

Idera.Inc, Registered Products Portal 登録製品ポータル
CodeGear 旧Delphi FAQ
Delphi Library [Mr.XRAY] 高度なサンプル有ります。
Delphi 2009 特集 ★必読。漢字コードの問題に関してわかりやすく解説されています。
DEKOのアヤシいお部屋。 読みやすいので、ささっと目を通すだけでも役に立ちます。
CとDelphiの対比表
Delphi 7ユーザと初心者のためのDelphi 2010入門
ホワイトペーパー: モバイル開発のためのDelphi言語 Delphi言語仕様変わる
RAD Studio / Delphi / C++Builder 11.0 Alexandria スタートアップ FAQ
Delphi Community Edition→参考になるページ @pikさんのページ

end.

Home New Help Edit
2024-09-02 16:25:50 32400