RAD Studio/Delphi/C++Builder 10.2 Tokyoがリリースされています。
RAD Studio 10.2 Tokyo ヘルプ (en)
リリース ノート (en)
インストール ノート (en)
新機能 (en)
RAD Studio 10.2 Tokyo 新機能および不具合修正リスト (en)
30722 RAD Studio, Delphi, C++Builder 10.2 Tokyo Web Install
30724 RAD Studio, Delphi, C++Builder 10.2 Tokyo ISO
30765 Linux libmidas file for RAD Studio, Delphi, C++Builder 10.2
30780 RAD Studio 10.2 Tokyo FireMonkey Accessibility Pack
30752 BDE Installer for RAD Studio, Delphi, C++Builder 10.2 Tokyo
30728 FastReport VCL for RAD Studio, Delphi, C++Builder 10.2 Tokyo
30729 FastReport FMX for RAD Studio, Delphi, C++Builder 10.2 Tokyo
30730 IP*Works for RAD Studio, Delphi 10.2 Tokyo
30731 IP*Works for RAD Studio, C++Builder 10.2 Tokyo
30750 Codesite for RAD Studio, Delphi, C++Builder 10.2 Tokyo
30761 Jedi Code Library Snapshot Binary Installer for 10.2
30762 Jedi Visual Component Library Snapshot Bin-Installer 10.2
GitHub - lynatan/StarterFix: Restore Form Designer Options for Starter edition.
【Delphi / C++Builder 10.2 Tokyo 新機能・改善点】C++ コンパイラ 周りの改善など - Qiita
【Delphi / C++Builder 10.2 Tokyo 新機能・改善点】FireDAC、データベース接続周りの改善など - Qiita
【Delphi / C++Builder 10.2 Tokyo 新機能・改善点】RAD Server の改善など - Qiita
【Delphi / C++Builder 10.2 Tokyo 新機能・改善点】IDEまわりの改善など - Qiita
【Delphi / C++Builder 10.2 Tokyo 新機能・改善点】ライブラリ周りの機能など - Qiita
What's New in C++Builder 10.2: Part 1 - The Linker
What's New in C++Builder 10.2: Part 2 - Code Generation
What's New in C++Builder 10.2: Part 3 - Debugging
2017年3月23日
2017年3月22日
ジェネリックスのリストをアルゴリズムを指定してソートする
Delphi 2009で導入されたジェネリックスのコンテナの一つであるTList<T>にはSortメソッドがあり、比較関数としてデフォルト以外のComparer(コンペアラ)も渡すことができるのですが、ソートアルゴリズムそのものはクイックソートしかありません。一般的には平均して性能が出るとされるクイックソートですが、安定ではないこと、苦手な状況が存在することなど、決して万能というわけではありません。ところがTList<T>.Sortは内部で保持しているTArray.Sortにソートを丸投げしており、ソートアルゴリズムを変更することができません。そこでアルゴリズムを指定してTList<T>(およびTObjectList<T: class>)をソートする方法を考えてみました。ただしDelphi 2009のTList<T>にはExchangeメソッドもMoveメソッドも存在していないため、Delphi 2010以降の対応になります。
まずソートを行うためのレコード型と、ソートアルゴリズムを実装するクラスの継承元クラスの宣言です。
→ジェネリックスのリストをアルゴリズムを指定してソートする(Gist)
まずソートを行うためのレコード型と、ソートアルゴリズムを実装するクラスの継承元クラスの宣言です。
{$IF RTLVersion <= 20.00} {$MESSAGE ERROR 'Need Delphi 2010 or later'} {$IFEND} uses {$IF RTLVersion >= 23.00} System.Rtti, System.Generics.Defaults, System.Generics.Collections; {$ELSE} Rtti, Generics.Defaults, Generics.Collections; {$IFEND} type { Forward declarations } TSortAlgorithm<T> = class; { TGenericListSorter } TGenericListSorter = record private class function GetComparer<T>(List: TList<T>; const AComparer: IComparer<T>): IComparer<T>; static; public class procedure Sort<T: record>(List: TList<T>; Algorithm: TSortAlgorithm<T>; const AComparer: IComparer<T>{$IF CompilerVersion >= 24.00} = nil{$IFEND}); overload; static; {$IF CompilerVersion < 24.00} class procedure Sort<T: record>(List: TList<T>; Algorithm: TSortAlgorithm<T>); overload; static; {$IFEND} class procedure Sort<T: class>(List: TObjectList<T>; Algorithm: TSortAlgorithm<T>; const AComparer: IComparer<T>{$IF CompilerVersion >= 24.00} = nil{$IFEND}); overload; static; {$IF CompilerVersion < 24.00} class procedure Sort<T: class>(List: TObjectList<T>; Algorithm: TSortAlgorithm<T>); overload; static; {$IFEND} class procedure Sort(List: TList<String>; Algorithm: TSortAlgorithm<String>; const AComparer: IComparer<String>{$IF CompilerVersion >= 24.00} = nil{$IFEND}); overload; static; {$IF CompilerVersion < 24.00} class procedure Sort(List: TList<String>; Algorithm: TSortAlgorithm<String>); overload; static; {$IFEND} end; { TSortAlgorithm (abstract) } TSortAlgorithm<T> = class(TObject) public class function Instance: TSortAlgorithm<T>; virtual; abstract; class procedure Sort(List: TList<T>; const AComparer: IComparer<T>); virtual; abstract; end;TGenericListSorterはソートを行うためのレコード型で、overloadされたpublicな3つ(XE2およびそれ以前は6つ、後述)のSortメソッドと、比較を行うコンペアラを決定するためのprivateなGetComparerメソッドを持ちます。Sortメソッドの1つめは値型用(レコード制約)、2つめはクラス型用(クラス制約)、3つめはこのどちらにも含まれない文字列型用です。IComparer<T>にデフォルトパラメータを指定できるのはDelphi XE3以降のため、それ以前のバージョンではコンペアラを指定しないオーバロードをさらに3つ用意しました。またTSortAlgorithm<T>はソートアルゴリズムを実装するためのクラスの継承元になります。実際にソートを行うSortメソッドと、シングルトンなインスタンスを取得するためのInstanceメソッドを持ちます。TGenericListSorterの実装は次のようになります。
class procedure TGenericListSorter.Sort<T>(List: TList<T>; Algorithm: TSortAlgorithm<T>; const AComparer: IComparer<T>); var Comparer: IComparer<T>; begin if (List = nil) or (List.Count <= 1) then begin Exit; end; Comparer := GetComparer<T>(List,AComparer); Algorithm.Sort(List,Comparer); end; {$IF CompilerVersion < 24.00} class procedure TGenericListSorter.Sort<T>(List: TList<T>; Algorithm: TSortAlgorithm<T>); var Comparer: IComparer<T>; begin if (List = nil) or (List.Count <= 1) then begin Exit; end; Comparer := GetComparer<T>(List,nil); Algorithm.Sort(List,Comparer); end; {$IFEND} class procedure TGenericListSorter.Sort<T>(List: TObjectList<T>; Algorithm: TSortAlgorithm<T>; const AComparer: IComparer<T>); var Comparer: IComparer<T>; OwnsObjects: Boolean; begin if (List = nil) or (List.Count <= 1) then begin Exit; end; Comparer := GetComparer<T>(List,AComparer); OwnsObjects := List.OwnsObjects; try List.OwnsObjects := False; Algorithm.Sort(List,Comparer); finally List.OwnsObjects := OwnsObjects; end; end; {$IF CompilerVersion < 24.00} class procedure TGenericListSorter.Sort<T>(List: TObjectList<T>; Algorithm: TSortAlgorithm<T>); var Comparer: IComparer<T>; OwnsObjects: Boolean; begin if (List = nil) or (List.Count <= 1) then begin Exit; end; Comparer := GetComparer<T>(List,nil); OwnsObjects := List.OwnsObjects; try List.OwnsObjects := False; Algorithm.Sort(List,Comparer); finally List.OwnsObjects := OwnsObjects; end; end; {$IFEND} class procedure TGenericListSorter.Sort(List: TList<String>; Algorithm: TSortAlgorithm<String>; const AComparer: IComparer<String>); var Comparer: IComparer<String>; begin if (List = nil) or (List.Count <= 1) then begin Exit; end; Comparer := GetComparer<String>(List,AComparer); Algorithm.Sort(List,Comparer); end; {$IF CompilerVersion < 24.00} class procedure TGenericListSorter.Sort(List: TList<String>; Algorithm: TSortAlgorithm<String>); var Comparer: IComparer<String>; begin if (List = nil) or (List.Count <= 1) then begin Exit; end; Comparer := GetComparer<String>(List,nil); Algorithm.Sort(List,Comparer); end; {$IFEND} class function TGenericListSorter.GetComparer<T>(List: TList<T>; const AComparer: IComparer<T>): IComparer<T>; var ctx: TRttiContext; begin Result := AComparer; if Result = nil then begin Result := ctx.GetType(List.ClassType).GetField('FComparer').GetValue(List).AsType<IComparer<T>>; end; end;Sortメソッドはいずれもコンペアラを確定し、指定されたソートアルゴリズムのインスタンスのSortメソッドを呼び出しています。ただしTObjectList<T>用のオーバロードはソートを行っている間、一時的にOwnsObjectsをFalseに変更しています。これはOwnsObjectsがTrueだと(以下の例のマージソートのように)Items[]に代入を行ったときに、もともと格納されているTのインスタンスを解放してしまうためで、ソートアルゴリズムのクラスで直接ソートを行うのではなく、レコード型TGenericListSorterの3つのオーバロードに処理を分けて、そこから間接的に呼び出すようになっているのはこれが理由です。またGetComparerメソッドはコンペアラが指定されていない(=nil)ときに、TList<T>の持つデフォルトのコンペアラを(RTTIを使って)取得します。 次に実際のソートアルゴリズムを実装したクラスですが、まずコムソートを実装してみます。
type TCombSort<T> = class(TSortAlgorithm<T>) protected class var FInstance: TSortAlgorithm<T>; public class destructor Destroy; class function Instance: TSortAlgorithm<T>; override; class procedure Sort(List: TList<T>; const AComparer: IComparer<T>); override; end; class destructor TCombSort<T>.Destroy; begin FInstance.Free; end; class function TCombSort<T>.Instance: TSortAlgorithm<T>; begin if FInstance = nil then begin FInstance := Self.Create; end; Result := FInstance; end; class procedure TCombSort<T>.Sort(List: TList<T>; const AComparer: IComparer<T>); const SHRINK_FACTOR = 1.247330950103979; var Index: Integer; Gap: Integer; Swapped: Boolean; begin Gap := List.Count; Swapped := True; while (Gap > 1) or (Swapped = True) do begin if Gap > 1 then begin Gap := Trunc(Gap / SHRINK_FACTOR); end; if Gap < 1 then begin Gap := 1; end; Swapped := False; Index := 0; while (Gap + Index) < List.Count do begin if AComparer.Compare(List.Items[Index],List.Items[Index + Gap]) > 0 then begin List.Exchange(Index,Index + Gap); Swapped := True; end; Index := Index + 1; end; end; end;前述の通り(TGenericListSorterとは異なり)1種類の<T>に対してのみSortを実装すればよいようになっています。またSortメソッド以外にシングルトンなインスタンスを取得するためのInstanceメソッドと、そのインスタンスを解放するためのクラスデストラクタを用意します。これで例えばInteger型のリストに対しては
var I: Integer; Value: Integer; List: TList<Integer>; begin List := TList<Integer>.Create; try for I := 0 to 999 do begin List.Add(Random(100000)); end; TGenericListSorter.Sort<Integer>(List,TCombSort<Integer>.Instance,TComparer<Integer>.Construct( function(const Left, Right: Integer): Integer begin Result := Left - Right; end)); for Value in List do begin Memo1.Lines.Add(IntToStr(Value)); end; finally List.Free; end end;このような形でソートを呼び出すことができます。 同じようにその他のソートアルゴリズムを実装していきます。ノームソートです。
type TGnomeSort<T> = class(TSortAlgorithm<T>) protected class var FInstance: TSortAlgorithm<T>; public class destructor Destroy; class function Instance: TSortAlgorithm<T>; override; class procedure Sort(List: TList<T>; const AComparer: IComparer<T>); override; end; class destructor TGnomeSort<T>.Destroy; begin FInstance.Free; end; class function TGnomeSort<T>.Instance: TSortAlgorithm<T>; begin if FInstance = nil then begin FInstance := Self.Create; end; Result := FInstance; end; class procedure TGnomeSort<T>.Sort(List: TList<T>; const AComparer: IComparer<T>); var Index: Integer; begin Index := 0; while Index < List.Count do begin if (Index = 0) or (AComparer.Compare(List.Items[Index],List.Items[Index - 1]) >= 0) then begin Index := Index + 1; end else begin List.Exchange(Index,Index - 1); Index := Index - 1; end; end; end;選択ソートです。
type TSelectionSort<T> = class(TSortAlgorithm<T>) protected class var FInstance: TSortAlgorithm<T>; public class destructor Destroy; class function Instance: TSortAlgorithm<T>; override; class procedure Sort(List: TList<T>; const AComparer: IComparer<T>); override; end; class destructor TSelectionSort<T>.Destroy; begin FInstance.Free; end; class function TSelectionSort<T>.Instance: TSortAlgorithm<T>; begin if FInstance = nil then begin FInstance := Self.Create; end; Result := FInstance; end; class procedure TSelectionSort<T>.Sort(List: TList<T>; const AComparer: IComparer<T>); var Index1: Integer; Index2: Integer; MinIndex: Integer; Temp: T; begin for Index1 := 0 to List.Count - 2 do begin MinIndex := Index1; Temp := List.Items[MinIndex]; for Index2 := Index1 + 1 to List.Count - 1 do begin if AComparer.Compare(List.Items[Index2],Temp) < 0 then begin MinIndex := Index2; Temp := List.Items[MinIndex]; end; end; if MinIndex <> Index1 then begin List.Move(MinIndex,Index1); end; end; end;挿入ソートです。
type TInsertionSort<T> = class(TSortAlgorithm<T>) protected class var FInstance: TSortAlgorithm<T>; public class destructor Destroy; class function Instance: TSortAlgorithm<T>; override; class procedure Sort(List: TList<T>; const AComparer: IComparer<T>); override; end; class destructor TInsertionSort<T>.Destroy; begin FInstance.Free; end; class function TInsertionSort<T>.Instance: TSortAlgorithm<T>; begin if FInstance = nil then begin FInstance := Self.Create; end; Result := FInstance; end; class procedure TInsertionSort<T>.Sort(List: TList<T>; const AComparer: IComparer<T>); var Comparer: IComparer<T>; Index1: Integer; Index2: Integer; Temp: T; begin for Index1 := 1 to List.Count - 1 do begin Temp := List.Items[Index1]; Index2 := Index1 - 1; while (Index2 >= 0) and (AComparer.Compare(List.Items[Index2],Temp) > 0) do begin Index2 := Index2 - 1; end; List.Move(Index1,Index2 + 1); end; end;クイックソートです。
type TQuickSort<T> = class(TSortAlgorithm<T>) protected class var FInstance: TSortAlgorithm<T>; class procedure InternalSort(List: TList<T>; Left: Integer; Right: Integer; const AComparer: IComparer<T>); class function Partition(List: TList<T>; Left: Integer; Right: Integer; const AComparer: IComparer<T>): Integer; public class destructor Destroy; class function Instance: TSortAlgorithm<T>; override; class procedure Sort(List: TList<T>; const AComparer: IComparer<T>); override; end; class destructor TQuickSort<T>.Destroy; begin FInstance.Free; end; class function TQuickSort<T>.Instance: TSortAlgorithm<T>; begin if FInstance = nil then begin FInstance := Self.Create; end; Result := FInstance; end; class procedure TQuickSort<T>.Sort(List: TList<T>; const AComparer: IComparer<T>); var Comparer: IComparer<T>; begin InternalSort(List,0,List.Count - 1,AComparer); end; class procedure TQuickSort<T>.InternalSort(List: TList<T>; Left: Integer; Right: Integer; const AComparer: IComparer<T>); var Pivot: Integer; begin if Left < Right then begin Pivot := Partition(List,Left,Right,AComparer); InternalSort(List,Left, Pivot,AComparer); InternalSort(List,Pivot + 1,Right,AComparer); end; end; class function TQuickSort<T>.Partition(List: TList<T>; Left: Integer; Right: Integer; const AComparer: IComparer<T>): Integer; var Index1: Integer; Index2: Integer; Pivot: T; begin Pivot := List.Items[(Left + Right) div 2]; Index1 := Left - 1; Index2 := Right + 1; while True do begin repeat Index1 := Index1 + 1; until (AComparer.Compare(List.Items[Index1],Pivot) >= 0); repeat Index2 := Index2 - 1; until (AComparer.Compare(List.Items[Index2],Pivot) <= 0); if Index1 >= Index2 then begin Result := Index2; Exit; end; List.Exchange(Index1,Index2); end; end;ヒープソートです。
type THeapSort<T> = class(TSortAlgorithm<T>) protected class var FInstance: TSortAlgorithm<T>; class procedure BuildHeap(List: TList<T>; const AComparer: IComparer<T>); class procedure Heapify(List: TList<T>; Index: Integer; Max: Integer; const AComparer: IComparer<T>); public class destructor Destroy; class function Instance: TSortAlgorithm<T>; override; class procedure Sort(List: TList<T>; const AComparer: IComparer<T>); override; end; class destructor THeapSort<T>.Destroy; begin FInstance.Free; end; class function THeapSort<T>.Instance: TSortAlgorithm<T>; begin if FInstance = nil then begin FInstance := Self.Create; end; Result := FInstance; end; class procedure THeapSort<T>.Sort(List: TList<T>; const AComparer: IComparer<T>); var Index: Integer; begin BuildHeap(List,AComparer); for Index := List.Count - 1 downto 1 do begin List.Exchange(0,Index); Heapify(List,0,Index,AComparer); end; end; class procedure THeapSort<T>.BuildHeap(List: TList<T>; const AComparer: IComparer<T>); var Index: Integer; begin for Index := (List.Count div 2) - 1 downto 0 do begin Heapify(List,Index,List.Count,AComparer); end; end; class procedure THeapSort<T>.Heapify(List: TList<T>; Index: Integer; Max: Integer; const AComparer: IComparer<T>); var Left: Integer; Right: Integer; Largest: Integer; begin Left := Index * 2 + 1; Right := Index * 2 + 2; if (Left < Max) and (AComparer.Compare(List.Items[Left],List.Items[Index]) > 0) then begin Largest := Left; end else begin Largest := Index; end; if (Right < Max) and (AComparer.Compare(List.Items[Right],List.Items[Largest]) > 0) then begin Largest := Right; end; if Largest <> Index then begin List.Exchange(Index,Largest); Heapify(List,Largest,Max,AComparer); end; end;マージソートです。
type TMergeSort<T> = class(TSortAlgorithm<T>) protected class var FInstance: TSortAlgorithm<T>; class procedure InternalSort(List: TList<T>; var Work: array of T; Left: Integer; Right: Integer; const AComparer: IComparer<T>); public class destructor Destroy; class function Instance: TSortAlgorithm<T>; override; class procedure Sort(List: TList<T>; const AComparer: IComparer<T>); override; end; class destructor TMergeSort<T>.Destroy; begin FInstance.Free; end; class function TMergeSort<T>.Instance: TSortAlgorithm<T>; begin if FInstance = nil then begin FInstance := Self.Create; end; Result := FInstance; end; class procedure TMergeSort<T>.Sort(List: TList<T>; const AComparer: IComparer<T>); var WorkArea: array of T; begin SetLength(WorkArea,List.Count); try InternalSort(List,WorkArea,0,List.Count - 1,AComparer); finally SetLength(WorkArea,0); end; end; class procedure TMergeSort<T>.InternalSort(List: TList<T>; var Work: array of T; Left: Integer; Right: Integer; const AComparer: IComparer<T>); var Index1: Integer; Index2: Integer; Index3: Integer; Mid: Integer; begin if Left >= Right then begin Exit; end; Mid := (Left + Right) div 2; InternalSort(List,Work,Left, Mid, AComparer); InternalSort(List,Work,Mid + 1,Right,AComparer); for Index1 := Left to Mid do begin Work[Index1] := List.Items[Index1]; end; Index2 := Right; for Index1 := Mid + 1 to Right do begin Work[Index1] := List.Items[Index2]; Index2 := Index2 - 1; end; Index1 := Left; Index2 := Right; for Index3 := Left to Right do begin if AComparer.Compare(Work[Index1],Work[Index2]) > 0 then begin List.Items[Index3] := Work[Index2]; Index2 := Index2 - 1; end else begin List.Items[Index3] := Work[Index1]; Index1 := Index1 + 1; end; end; end;シェルソートです。
type TShellSort<T> = class(TSortAlgorithm<T>) protected class var FInstance: TSortAlgorithm<T>; class procedure InternalSort(List: TList<T>; Gap: Integer; const AComparer: IComparer<T>); public class destructor Destroy; class function Instance: TSortAlgorithm<T>; override; class procedure Sort(List: TList<T>; const AComparer: IComparer<T>); override; end; class destructor TShellSort<T>.Destroy; begin FInstance.Free; end; class function TShellSort<T>.Instance: TSortAlgorithm<T>; begin if FInstance = nil then begin FInstance := Self.Create; end; Result := FInstance; end; class procedure TShellSort<T>.Sort(List: TList<T>; const AComparer: IComparer<T>); var Gap: Integer; begin Gap := List.Count div 2; while Gap > 0 do begin InternalSort(List,Gap,AComparer); Gap := Gap div 2; end; end; class procedure TShellSort<T>.InternalSort(List: TList<T>; Gap: Integer; const AComparer: IComparer<T>); var Index1: Integer; Index2: Integer; begin for Index1 := Gap to List.Count - 1 do begin Index2 := Index1 - Gap; while Index2 >= 0 do begin if AComparer.Compare(List.Items[Index2 + Gap],List.Items[Index2]) > 0 then begin Break; end; List.Exchange(Index2,Index2 + Gap); Index2 := Index2 - Gap; end; end; end;Instanceメソッドとクラスデストラクタを毎回書かなければならないことを除けば、ソートのコードを1つ書くだけで値型に対するTList<T>(TList<String>を含む)、クラスに対するTObjectList<T>のどちらであってもソートを行うことができます。
→ジェネリックスのリストをアルゴリズムを指定してソートする(Gist)
2017年3月15日
DELPHI / C++BUILDER 0315 IN TOKYO
本日13:30から次期RAD Studio/Delphi/C++Builder(10.2 Tokyo)のお披露目となるDELPHI / C++BUILDER 0315 IN TOKYOが東京ミッドタウン・カンファレンスで行われます。UStreamによる中継も行われます。
レセプションも含め終了しました。参加者、関係者の皆さん、お疲れさまでした。
今日のアバウトなまとめ
レセプションも含め終了しました。参加者、関係者の皆さん、お疲れさまでした。
今日のアバウトなまとめ
- RAD Studio/Delphi/C++Builderの次期バージョンは"10.2 Tokyo"で、2017/03/28販売開始予定
- 主な新機能はLinux(Intel x64)サポート(C++Builderは次のアップデートにあたる"10.2.1"でサポート)
- LinuxサポートはEnt SKU以上が必要(Add-on Packなどは今のところなし)
- 勉強会やりましょう
2017年3月9日
InterBase 2017リリース
InterBase 2017(13.0.0.129)がリリースされています。現時点(2017/03/09)でDeveloper EditionとServer Edition(トライアル版)のみダウンロード可能です。ODSは17になっています。
30748 InterBase 2017 Server Ed., Windows/Linux (13.0.0.129, Japanese)
30749 InterBase 2017 Server Ed., Windows/Linux (13.0.0.129, English)
30736 InterBase 2017 (13.0.0.129) Developer Edition, Japanese
30738 InterBase 2017 (13.0.0.129) Developer Edition, English
30756 InterBase 2017 ToGo Ed., Windows/Linux/macOS/iOS/Android
Readme - InterBase (en)
InterBase 2017 の新機能 - InterBase(en)
InterBase 2017 - Now available • DelphiABall
2017/03/10追記: Server Editionもダウンロードできるようになっています。
2017/03/24追記: ToGo Editionもダウンロードできるようになっています。
30748 InterBase 2017 Server Ed., Windows/Linux (13.0.0.129, Japanese)
30749 InterBase 2017 Server Ed., Windows/Linux (13.0.0.129, English)
30736 InterBase 2017 (13.0.0.129) Developer Edition, Japanese
30738 InterBase 2017 (13.0.0.129) Developer Edition, English
30756 InterBase 2017 ToGo Ed., Windows/Linux/macOS/iOS/Android
Readme - InterBase (en)
InterBase 2017 の新機能 - InterBase(en)
InterBase 2017 - Now available • DelphiABall
2017/03/10追記: Server Editionもダウンロードできるようになっています。
2017/03/24追記: ToGo Editionもダウンロードできるようになっています。
2017年3月7日
Adobe Reader(X以降)で指定したファイルの指定したページを開く
以前Adobe Acrobat/Readerで指定したPDFファイルの指定したページを開く方法について書きましたが、コメントでおかぽんさんからAdobe Reader X以降ではこの方法が使えなくなっているとの指摘をいただきました。
調べてみると、Actobat/ReaderのVersion 10(X)以降で、DDEのサービス名が Acroview + [A|R] + <MajorVersion> に変更されたことが原因とのことです。というわけでこれに対応してみました。
おかぽんさん、情報ありがとうございました。
→Adobe Reader(X以降)で指定したファイルの指定したページを開く(Gist)
...DDEを使った方法ですが、Acrobat X から、DDEのサービス名が変更されています。...
uses {$IF RTLVersion >= 23.00} Winapi.Windows, System.SysUtils, System.Win.Registry, System.AnsiStrings, Vcl.DdeMan; {$ELSE} Windows, SysUtils, Registry, {$IFDEF UNICODE}AnsiStrings, {$ENDIF}DdeMan; {$IFEND} function GetAcrobatPathname: String; begin with TRegistry.Create do begin try RootKey := HKEY_CLASSES_ROOT; OpenKeyReadOnly('Software\Adobe\Acrobat\Exe'); try Result := AnsiDequotedStr(ReadString(''),'"'); finally CloseKey; end; finally Free; end; end; end; procedure OpenPDF(const Filename: String; Page: Integer); const CDdeCommand: AnsiString = '[DocOpen("%s")][DocGoTo(NULL,%d)]'; var Macro: AnsiString; Pathname: String; ServiceName: String; MajorVersion: Integer; AcrobatType: String; begin Macro := {$IFDEF UNICODE}{$IF RTLVersion >= 23.00}System.{$IFEND}AnsiStrings.{$ENDIF} Format(CDdeCommand,[Filename,Page - 1]); Pathname := GetAcrobatPathname; ServiceName := 'Acroview'; MajorVersion := GetFileVersion(Pathname) shr 16; if MajorVersion >= 10 then begin if CompareText(ExtractFileName(Pathname),'AcroRd32.exe') = 0 then begin AcrobatType := 'R'; end else begin AcrobatType := 'A'; end; ServiceName := ServiceName + Format('%s%d',[AcrobatType,MajorVersion]); end; with TDdeClientConv.Create(nil) do begin try ConnectMode := ddeManual; ServiceApplication := ChangeFileExt(Pathname,''); SetLink(ServiceName,'Control'); if (OpenLink or ((MajorVersion >= 15) and OpenLink)) = True then begin ExecuteMacro(PAnsiChar(Macro),False); CloseLink; end; finally Free; end; end; end;Acrobat/Readerの実行ファイルの場所をレジストリの"HKEY_CLASSES_ROOT\Software\Adobe\Acrobat\Exe"から取得し、SysUtils.GetFileVersionで問い合わせたバージョン情報の上位16ビット(メジャーバージョン)とファイル名からサービス名を組み立て、DDEをTDdeClientConv.OpenLinkで呼び出してTDdeClientConv.ExecuteMacroでマクロ実行することでファイルを開きページを移動する、という手順になります。またAcrobat/Reader DCの場合TDdeClientConv.OpenLinkの内部でWinExec(en)を使用して起動した直後にDdeConnect(en)を呼び出すと失敗するようなので、リトライするようにしています。
おかぽんさん、情報ありがとうございました。
→Adobe Reader(X以降)で指定したファイルの指定したページを開く(Gist)
2017年3月5日
[書籍][ebook]Dependency Injection In Delphi
Leanpubで
Dependency Injection In Delphi (amazon US)/Nick Hodges著/Leanpub/ISBN 978-1941266229(printed), ISBN 978-1-941266-19-9(ebook)/29.99USD(printed), 29.99USD(ebook)
を購入(LearnpubのebookはPDF/EPUB/MOBI形式をダウンロード可能)。
2017/05/31追記: Amazon (US)で書籍版が購入可能になっています。ということでDelphi in Depth: FireDACと一緒に注文したDependency Injection In Delphiの書籍版が配送されてきました。29.99USD=3,433JPY(1USD=114.479JOY、配送料は含まず)ということになりました。
Dependency Injection In Delphi (amazon US)/Nick Hodges著/Leanpub/ISBN 978-1941266229(printed), ISBN 978-1-941266-19-9(ebook)/29.99USD(printed), 29.99USD(ebook)
を購入(LearnpubのebookはPDF/EPUB/MOBI形式をダウンロード可能)。
2017/05/31追記: Amazon (US)で書籍版が購入可能になっています。ということでDelphi in Depth: FireDACと一緒に注文したDependency Injection In Delphiの書籍版が配送されてきました。29.99USD=3,433JPY(1USD=114.479JOY、配送料は含まず)ということになりました。
2017年3月1日
2017/03開催のセミナー
- Web Seminar
- 2017/03/06 17:00-17:50(JST) Delphi / C++Builder Starter チュートリアルシリーズ シーズン2『プログラミング言語をやさしく覚えよう』第7回 Delphi「オブジェクト指向」/C++「オブジェクト指向」
- 2017/03/13 17:00-17:50(JST) Delphi / C++Builder Starter チュートリアルシリーズ シーズン2『プログラミング言語をやさしく覚えよう』第8回 Delphi「作ってみよう Delphiの部」
- 2017/03/27 17:00-17:50(JST) Delphi / C++Builder Starter チュートリアルシリーズ シーズン2『プログラミング言語をやさしく覚えよう』第9回 C++「作ってみよう C++の部」
- Live Seminar
- 2017/03/15 13:30-20:00(JST) DELPHI / C++BUILDER 0315 IN TOKYO
- 2017/03/28 17:00-19:00(JST) 「RAD Studioで始めるマルチデバイス・クロスプラットフォーム開発ワークショップ」
登録:
投稿 (Atom)