« 2017年4月 | トップページ | 2017年6月 »

2017.05.28

Delphi 10.1 Starterでの多言語化

Delphi XEではプロジェクトメニューの言語言語→追加で多言語化が出来ますが、Starter版にはこの機能はありません。そこでネット上を探したところtokibitoさんの記事を見つけました。
で早速試してみたのですが、Delphi 10.1 Starter版ではそのままでは実現できなかったため、備忘録として残しておきます。

まずはhttps://sourceforge.net/projects/dxgettext/からGNU Gettext for Delphi, C++ and Kylixをダウンロードしてgnugettextをインストールする。
このセットアップでインストールされるgnugettext.pasはDelphi XE以降ではコンパイルエラーとなるため、https://sourceforge.net/p/dxgettext/patches/26/からgnugettext.pasをダウンロードして置き換える。次に追加する言語を編集するためにhttps://ja.osdn.net/projects/sfnet_poedit/releases/からpoedit-1.5.7-setup.exeをダウンロードしてインストールする。
これで準備完了です。

ますはプロジェクトフォルダにgnugettext.pasをコピーして、プロジェクトソースのusesにgnugettextを追加します。

program Project1;

uses
Vcl.Forms,
gnugettext in 'gnugettext.pas', //この一行を追加
Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.

続いてプログラムソース本体のusesにもgnugettextを追加します。
gnugettextは自動でフォームの文字列を抽出してくれますが、そのままではソースコード内の文字列は抽出してくれません。ソースコード中の多言語化したい文字列を_( )で括ります。

unit Unit1;
 
interface
 
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Menus,
  gnugettext; // gnugettextユニットを追加
 
type
  TForm1 = class(TForm)
    MainMenu1: TMainMenu;
    F1: TMenuItem;
    E1: TMenuItem;
    O1: TMenuItem;
    X1: TMenuItem;
    C1: TMenuItem;
    V1: TMenuItem;
    P1: TMenuItem;
    N1: TMenuItem;
    Language1: TMenuItem;
    J1: TMenuItem;
    English1: TMenuItem;
    Label1: TLabel;
    Button1: TButton;
    Label2: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure J1Click(Sender: TObject);
    procedure English1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 
procedure TForm1.Button1Click(Sender: TObject);
begin
  Label2.Caption := _('これはプログラムコード中の文字列を切り替える確認です.'); // 多言語化したい文字列を_()で括る
end;
 
procedure TForm1.English1Click(Sender: TObject);
begin
  English1.Checked := True;
  UseLanguage('en_US');          // 英語(US)に切り替える
  ReTranslateComponent(Self); // 表示し直す
end;
 
procedure TForm1.J1Click(Sender: TObject);
begin
  J1.Checked := True;
  UseLanguage('ja');         // 日本語に切り替える
  ReTranslateComponent(Self);  // 表示し直す
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  TranslateComponent(Self); // これを追加
end;
 
end.

ソースコードの修正が終わったら、プロジェクトフォルダーでコマンドプロンプトを開き(エクスプローラでフォルダを選択して、SHIFTキーを押しながら右クリックするとコマンドウィンドウをここで開くメニューが出ます)、コマンドラインから
dxgettext --delphi --nonascii
を実行します。
すると言語ファイルであるdefault.poが作成されていますので、このファイルをpoeditで編集します。

Poedit
ここで日本語の対訳を入れていきます。尚、編集ファイルを保存することで自動的に実行に必要なファイルが一種に作成されます(拡張子が.moのファイルです)。
また、この時カタログメニューの設定で基本情報を入れておきます。
Posetting

編集で出来上がったmoファイルをプロジェクト実行ファイルがあるフォルダにサブフォルダlocale\言語名\LC_MESSAGESフォルダを作成して、そこに保存します(ファイル名はdefault.moのままで構いません。またdefault.poは不要です)。

で動作確認結果は以下の通りです。

通常
Testjp

English化
Testeng


「Project1.zip」をダウンロード

のダウンロード


| | コメント (0) | トラックバック (0)

2017.05.20

MiGrep2 ver1.0フルパッケージ版の公開

MiGrep2に当初考えていた機能を実装出来ましたので公開します。

Migrep2

ファイルやフォルダー、ZIP/LZH/CAB書庫ファイルを指定して、その中のファイルをGrep(検索ワードがあるか検索)します。ZIP/LZH/CAB書庫ファイル内の検索はWindowsシェルの標準機能を使用していますので、外部ライブラリファイルは不要です。また、テキストファイルの文字コードは、標準でShift-JIS, UTF-8, Unicode BE, Unicode LEに対応、オプション設定でUTF-8N, JIS, EUCを自動判別します。

検索できるファイルは標準ではテキストファイルだけですが、hishidaさんが公開しているxdoc2txtを使用することで、MS OfficeファイルやPDFファイル等も検索できるようになります。また、各社が公開しているiFilterも使用することが出来ます。
※詳細はヘルプファイル(手抜きですが)を参照して下さい。

「mig2setup.zip」をダウンロード

ダウンロードしたファイルを解凍して、mig2setup.exeを実行して下さい。

尚、セットアップファイルの作成にはInno Setupを使用しました。

| | コメント (0) | トラックバック (0)

2017.05.19

MiBarcode ver7.1アップデート

先日公開のVer7.1にいくつかの不具合があったのでアップデート版を公開します。
不具合;ヘルプメニューの目次でヘルプファイルが表示されない、タスクバーのアイコンが二重になる、の2点を修正しています。

Ver7.1をインストール済みの場合は上書きでインストールして下さい。

「Mibarcd71.zip」をダウンロード

| | コメント (2) | トラックバック (0)

2017.05.16

MiBarcode ver7.1フルパッケージ版

Excelでオートメーションサーバ機能を使用する際にクリップボード関連のエラーが発生する対策としていくつかのプロパティとメソッドを追加してクリップボードを介さずに作成したバーコードを貼り付けられるようにしました。
また、合わせてプロパティ、メッソドの一部と、MiBarcode本体の「名前をつけて保存」機能の不具合も修正しています。
主な変更点は、
1) ExcelのVBAでクリップボードを使用せずにバーコード貼り付けを行うために、オートメーションサーバ機能にプロパティAutoCopy,ClipBWatchとメッソドSaveTempPictを追加。
2)名前を付けて保存(SaveFileメソッド含む)でメタファイルを保存できなかった不具合を修正。
です。

クリップボードを使用しない方法については、サンプルファイルQR_sample4.xlsmのVBAマクロを参照して下さい(以下は抜粋です)。

MiBar.CopyType = 0      '作成する画像の種類(0=Bitmap)
MiBar.AutoCopy = 0      '自動コピーをOFF
MiBar.ClipBWatch = 0    'クリップボード監視OFF
 
'コードをセット
MiBar.Code = Code
'バーコードを作成
MiBar.Execute
pfname = MiBar.SaveTempPict 'バーコードを一時ファイルに保存して、そのフルパス名を返す
         
'カーソルをセットする処理
Cells(i, 1).Activate
'一時ファイルを選択位置に直接読み込む
Set myShape = ActiveSheet.Shapes.AddPicture( _
    Filename:=pfname, _
    LinkToFile:=True, _
    SaveWithDocument:=False, _
    Left:=Selection.Left, _
    Top:=Selection.Top, _
    Width:=0, _
    Height:=0)
         
'--(2) 挿入した画像に対して元画像と同じ高さ・幅にする
With myShape
   .ScaleHeight 1, msoTrue
   .ScaleWidth 1, msoTrue
End With

ver71.をインストールする前に、MiBarcodeをインストールしたフォルダにあるAuninst.batを管理者権限で実行して、その後現在のMiBarcodeをコントールパネルのアンインストールからアンインストールして下さい。

「Mibarcd71.zip」をダウンロード

ダンロードしたMibarcd71.zipを解凍して、Mibarcd71.exeを実行するとインストールできます。


| | コメント (2) | トラックバック (0)

2017.05.13

Delphiでのメモリリーク

MiGrep2でふとメモリリークを起こしていたりしないよね?ということで念のため確認してみました。
メモリリークが置きているかどうかを確認するのは簡単で、プロジェクトソースに一行追加するだけです(Delphi2006以降)。

program MemLeakTestProg;

uses
Vcl.Forms,
MemLeakTest in 'MemLeakTest.pas' {TestForm};

{$R *.res}

begin
ReportMemoryLeaksOnShutdown := True; //これを追加
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TTestForm, TestForm);
Application.Run;
end.

で結果は・・・思いっきりメモリリークを起こしていました(^^;
原因を色々と調べたところ、TListview.item.dataポインタに格納している検索ファイルの付属情報を格納している構造体ポインタ内のstringが開放されていないことがわかりました。
ファイル情報を登録する際に、

var
finfo: PFoundInfo; // 検索情報構造体ポインタ
begin
New(finfo);
finfo^.Fullpathname := ほにゃらら
fino^.xxxx
Listview.item[x].Data := finfo;

として、開放する際に何も考えずに
procedure TMainForm.ListView1Deletion(Sender: TObject; Item: TListItem);
begin
if Assigned(Item.Data) then
Dispose(Item.Data);

としてしまっていました。
これだと、Item.Dataに格納されたポインタ構造体は解放されるものの、構造体メンバは開放されないのでした。
ここはItem.dataポインタを格納されている構造体でキャストしてやることで正しく下放されるのでした。
Dispose(PFoundInfo(Item.Data));

以下はテスト用プログラムです。

unit MemLeakTest;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;

type
// テスト用構造体1(メンバにStringを含む)
PRec1 = ^TRec1;
TRec1 = record
TestString1,
TestString2: string;
end;
// テスト用構造体1(メンバもポインタ)
PRec2 = ^TRec2;
TRec2 = record
TestString1,
TestString2: PString;
end;
TTestForm = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;

var
TestForm: TTestForm;

implementation

{$R *.dfm}

// 直接操作
procedure TTestForm.Button1Click(Sender: TObject);
var
r1: PRec1;
i: integer;
begin
for i := 1 to 100 do
begin
New(r1);
r1^.TestString1 := 'TEST1';
r1^.TestString2 := 'TEST2';
Dispose(r1); // メモリリークは起こらない
end;
end;

// 代入ポインタ操作1
procedure TTestForm.Button3Click(Sender: TObject);
var
r1: PRec1;
i: integer;
p: Pointer;
begin
for i := 1 to 100 do
begin
New(r1);
p := r1; // ポインタpにメモリを確保したr1を代入
PRec1(p)^.TestString1 := 'TEST1';
PRec1(p)^.TestString2 := 'TEST2';
Dispose(p); // pを開放→TestString1/TestString2が開放されずメモリリーク
end;
end;

// 代入ポインタ操作2
procedure TTestForm.Button2Click(Sender: TObject);
var
r1: PRec1;
i: integer;
p: Pointer;
begin
for i := 1 to 100 do
begin
New(r1);
p := r1; // ポインタpにメモリを確保したr1を代入
PRec1(p)^.TestString1 := 'TEST1';
PRec1(p)^.TestString2 := 'TEST2';
Dispose(PRec1(p)); // pをPRec1でキャストして開放→メモリリークは起こらない
end;
end;

// 構造体メンバもポインタ
procedure TTestForm.Button4Click(Sender: TObject);
var
r2: PRec2;
i: integer;
p: Pointer;
begin
for i := 1 to 100 do
begin
New(r2);
p := r2;
New(PRec2(p)^.TestString1);
New(PRec2(p)^.TestString2);
PRec2(p)^.TestString1^ := 'TEST1';
PRec2(p)^.TestString2^ := 'TEST2';
Dispose(PRec2(p)^.TestString1); // TestStrng1, TextString2を開放
Dispose(PRec2(p)^.TestString2);
Dispose(p); // ポインタpを開放→メモリリークは起こらない
end;
end;

end.

で、今プログラムをもしやと思ってLazarus/FPCでも実行してみましたが結果は同じでした・・・まぁそうですよね。

最終的にMiGrep2ではメモリ効率も考えて、上の四番目の方法(メンバもポインタ)に変更しました。ちょっと面倒ですが、全部自前で問答を見たほうが後々問題が起きにくいかなと思った次第です。

| | コメント (0) | トラックバック (0)

DelphiでiFilterを使用する

Migrep2ではxdoc2txtを使用してOfficeファイルやPDFファイルのテキスト検索を行っていますが、MicrosoftやAbobe等が公開しているiFilterを使ってみようと思い立ちました。で、ネット上を検索して見つけたRussell Libby氏が公開されているユニットを見つけ、これをもとにコンポーネント化して見ました。

使い方はとてもシンプルで、

procedure TForm1.Button1Click(Sender: TObject);
var
txtbuff: string;
begin
if OpenDialog1.Execute then
begin
Memo1.Lines.Clear;
iFilterText1.ReadText(OpenDialog1.FileName, txtbuff);
Memo1.Lines.Text :=txtbuff;
end;
end;

これだけでTmemoに指定したファイルのテキストを表示できます(そのファイル形式のiFilterがお使いのWindows環境にインストールされていればですが)。

例えば、以下のPDFファイルを読み込むと、
Pdf

このようにテキストが読み込まれます。
Ifilter

尚、Windowsが64bit環境の場合はiFilterも64bit用のインストールが必要で、Delphiの作成するターゲットプラットホームを64ビットWindowsにして、64bit版の実行ファイルにする必要があります。
#Delphi XE2 Pro版で実行ファイルを作成して確認

で、一旦はMiGrep2にiFIlterを使用する機能を追加したのですが、テキスト変換速度がxdoc2txtよりも遅いことや32bit/64bit環境の対応を考えると、やっぱりやめようかなと思ったりもしています。64bit Windows用のiFilterラッパーを準備する手もあるかとは思いますが、そこまでして実装するだけのメリットがあるのかどうか悩ましいところです。


「iFilterコンポーネントとサンプルプロジェクト」をダウンロード


| | コメント (0) | トラックバック (0)

« 2017年4月 | トップページ | 2017年6月 »