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よりも精度が悪い
| Win32 | Win64 |
Double | 8byte | 8byte |
Extended | 10byte | 8byte |
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 | インデックスによってアクセス出来る順序付きリスト |
TQueue | FIFO |
TStack | FILO |
TObjectList | オブジェクトのメモリ管理してくれる。Delete,Remove,Clear,Freeでオブジェクト解放。 |
TObjectDictionary
TObjectQueue
TObjectStack
スレッド間データ受け渡し
- TThreadedQueue 受け渡しにロック操作不要
- TThreadList 追加・削除にロック操作不要、Listアクセス時ロック
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);
仮想メソッドと動的メソッド
仮想メソッド | 動的メソッド |
virtual | dynamic |
実効速度最適化 | コードサイズ最適化 |
静的クラスメソッド
関数をクラス内に宣言出来る。
ただし、クラスメンバにアクセスできない。関数がクラス内に入れられるだけ。
あまりメリット無い?
わかりやすい解説
関数ポインタとメソッドポインタの相互代入のおはなし。→クラスメソッド(静的な意味で
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;
ファイル読み書き
TFileStream | write,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.GetHomePath | C:\Users\{ユーザー名}\AppData\Roaming | 設定データ Roaming/{CompanyName}/{AppName} |
TPath.GetCachePath | C:\Users\{ユーザー名}\AppData\Local | 消えても良いデータ |
TPath.GetTempPath | C:\Users\{ユーザー名}\AppData\Local\Temp\ | アプリケーション終了後に消えても良いデータ |
TPath.GetLibraryPath | D:\data\trunk\Lab\Delphi10\path\Win32\Debug\ | |
TPath.GetDocumentsPath | C:\Users\{ユーザー名}\Documents | |
TPath.GetSharedDocumentsPath | C:\Users\Public\Documents | |
TPath.GetPicturesPath | C:\Users\{ユーザー名}\Pictures | |
TPath.GetSharedPicturesPath | C:\Users\Public\Pictures | |
TPath.GetPublicPath | C:\ProgramData | |
TPath.GetCameraPath | C:\Users\{ユーザー名}\Pictures | |
TPath.GetMusicPath | C:\Users\{ユーザー名}\Music | |
TPath.GetSharedMusicPath | C:\Users\Public\Music | |
TPath.GetMoviesPath | C:\Users\{ユーザー名}\Videos | |
TPath.GetSharedMoviesPath | C:\Users\Public\Videos | |
TPath.GetAlarmsPath | C:\Users\{ユーザー名}\Music | |
TPath.GetSharedAlarmsPath | C:\Users\Public\Music | |
TPath.GetRingTonesPath | C:\Users\{ユーザー名}\Music | |
TPath.GetSharedRingTonesPath | C:\Users\Public\Music | |
TPath.GetDownloadsPath | C:\Users\{ユーザー名}\AppData\Local | |
TPath.GetSharedDownloadsPath | C:\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
- ジェネリクスを使っている為、スレッド処理の戻り値を指定可能
- 終了処理は、通常の終了と、例外の2種類を記述可能
- 終了処理は、TThread.OnTerminateから呼び出されるので、Synchronizeがいらない。System.Classes.TThread.OnTerminate
- メイン中に記述すれば、受け渡しの変数を作成しなくても済む
とかかなー。便利そう。
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 受け渡しにロック操作不要
- TThreadList 追加・削除にロック操作不要、Listアクセス時ロック
TThreadedQueue
- メソッド PushItem,PopPItem
- キュー内の個数は不明
- PushItem,PopItemにタイムアウト設定可能(INFINITEも有り)
- DoShutdownでロック解放
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
- メソッド Add,Remove,Clear
- プロパティ Dupulicates※dupAcceptしないと遅い(そうだよね)
- アイテム個数は不明
- LockList,UnlockListでTListを取得してアイテムにアクセス
スレッド同期
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見たいに表示される。
コンパイラ指令
Delphi | C言語 |
{$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}
使いそうなの
MSWINDOWS | Windows、WIN32,WIN64よりこっち使う |
MACOS | Mac 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)
クロスプラットフォーム アプリケーションのデバッグ(新しいリモート デバッグ方法)
- paserverをインストール
- paserverを実行
- 接続プロファイルを作成
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をコピー
- BorlndMM.dll
- FastMM_FullDebugMode.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 によって生成されるファイルの拡張子
必須
*.pas | Pascalソース |
*.dfm | VCLフォーム |
*.fmx | FMXフォーム |
*.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.
2024-09-02 16:25:50 32400