« 2007年12月 | トップページ | 2008年2月 »

■マルコフ連鎖

「マルコフ連鎖」なんて難しいことは私にはよくわかりません。
ただ、酔っ払いがデスクトップにヒョコヒョコって出てきて、何やら言葉が表示されるのを見てると 楽しいです。そう、この言葉がマルコフ連鎖によって作られているとの事です。
(公開されているコードでは、日本語は使えないですけどね)


Zarko's Delphi Programming Blog
Barstool Philosopher - Fancy Delphi Application Contest Entry #5

Wikipedia
マルコフ連鎖

|

☆金額をひたすら入力する時・・・。

仕事で数十万円から数千万円の金額をひたすら入力しなければならなかったので、 下記のような処理で、テンキーの[ . ](コンマ)を[000](ゼロ3つ)に使えるようにしました。
[000]キーがあるUSBテンキーが手元になかったので、とても便利でした。
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
  if Key = '.' then
  begin
    Edit1.Text := Edit1.Text + '000';
    Key := #0;
    Edit1.SelStart := Length(Edit1.Text);
  end;
end;

|

■Delphi 2005 プログラミングテクニック Vol8 インターネット編 (Ⅱ)

この本のサンプルはWindowsフォームアプリケーションだったので、Delphi2006で 試してみました。
スパムメールフィルターの実装やIndyコンポーネントのリビルド方法等も説明されていて、とても勉強になりました。ただ、CD-ROMもダウンロードサービスもないため、サンプルを自分で入力しなくてはならず、大変でしたけどね。(結局、質問メールへの返事もなかったですし・・・)

|

■TMS Advanced ToolBars & Menusアップデート

TMS Advanced ToolBars & Menus が v3.5.2.0 から v3.5.3.0 にアップデートされました。

メーカーホームページからの引用です。

in v3.5.3.0
- New : TStickyMenuItem.Visible property added
- Improved : tasCheck style TAdvToolBarButton can have dropdown menu
- Improved : toolbar & toolbar control persistence

tmssoftware.com

|

☆Docuworksで「ファイル名に日付を付加する」プラグイン

Docuworksは、設定すればスキャン時に日時をファイル名に自動的に付加してくれますが、 印刷した場合等には自分で付ける必要があります。(と思います。)
そこで、作成日時か最終更新日時の内、古い方の日時をファイル名に付加する機能を 持つプラグインを作ってみます。(実は仕事で必要だったりします。)

前回のプラグインのコードと同様なので、PLG_ExecuteCommand関数のみ書きますね。
// ExecuteCommand Entry
function PLG_ExecuteCommand(pPlgStruct: PLUGGEDIN_STRUCT): LongInt; stdcall;

  function GetFileDateTime(const FileName: String; var CreateYMD, LastWriteYMD,
    LastAccessYMD: TDateTime): Boolean;
  var
    Handle: Integer;
    Time1, Time2, Time3: TFILETIME;
    SysTime: TSYSTEMTIME;
    SECURITYATTRIBUTES: TSECURITYATTRIBUTES;
  begin
    Result := False;
    with SECURITYATTRIBUTES do
    begin
      nLength := SizeOf(TSECURITYATTRIBUTES);
      lpSecurityDescriptor := nil;
      bInheritHandle := False;
    end;

    Handle := CreateFile(PChar(FileName),GENERIC_READ,0,@SECURITYATTRIBUTES,
                          OPEN_EXISTING,FILE_ATTRIBUTE_READONLY,0);

    if Handle = 0 then Exit;
    try
      GetFileTime(Handle, @Time1, @Time2, @Time3);

      FileTimeToLocalFileTime(Time1,Time1);
      FileTimeToSystemTime(Time1, SysTime);
      CreateYMD := SystemTimeToDateTime(SysTime);

      FileTimeToLocalFileTime(Time2,Time2);
      FileTimeToSystemTime(Time2, SysTime);
      LastAccessYMD := SystemTimeToDateTime(SysTime);

      FileTimeToLocalFileTime(Time3,Time3);
      FileTimeToSystemTime(Time3, SysTime);
      LastWriteYMD := SystemTimeToDateTime(SysTime);

      Result := True;
    finally
      CloseHandle(Handle);
    end;
  end;

var
  I: Integer;
  S: String;
  InputFile: String;
  CreateYMD, LastWriteYMD, LastAccessYMD: TDateTime;
begin
  S := '';
  for I := 0 to pPlgStruct^.ps_nFiles-1 do
  begin
    InputFile := pPlgStruct^.ps_pszFiles^[I];
    if GetFileDateTime(InputFile, CreateYMD, LastWriteYMD, LastAccessYMD) then
    begin
      if CreateYMD <= LastWriteYMD then
        S := FormatDateTime('yyyymmdd', CreateYMD)
      else
        S := FormatDateTime('yyyymmdd', LastWriteYMD);
      S := ExtractFilePath(InputFile) + S + ExtractFileName(InputFile);
      RenameFile(InputFile, S);
    end;
  end;
  Result := 1;
end;

上記のコードで、きちんとファイル名を修正してくれるのですが、Docuworks Deskでのファイル位置が 一番最後に移動してしまいます。DocuworksのAPIには、ファイル名の変更をするものがなさそうなので、 解決できずにいます。メッセージでメニューとクリップボードを使って処理する方法もありますが、 スマートじゃないですよね。

|

☆Docuworksプラグイン「自動正立」を作る。

最近の記事の処理を組み合わせて、実際に使えるプラグイン「自動正立」を作ってみます。このプラグインは、Docuworks Deskで選択したファイルの全ページを文字の読める方向に自動回転させます。

library Project1;

uses
  Windows,
  SysUtils;

{$R *.res}

// xdwapiを使うための宣言--ここから
const
  XDW_OPEN_UPDATE	= 1;

type
  XDW_DOCUMENT_HANDLE = record
    dummy: LongInt;
  end;

  XDW_OPEN_MODE = record
    nSize: Integer;
    nOption: Integer;
  end;

  XDW_DOCUMENT_INFO = record
    nSize: Integer;
    nPages: Integer;
    nVersion: Integer;
    nOriginalData: Integer;
    nDocType: Integer;
    nPermission: Integer;
    nShowAnnotations: Integer;
    nDocuments: Integer;
    nBinderColor: Integer;
    nBinderSize: Integer;
  end;

  function XDW_OpenDocumentHandle(const lpszFilePath: PAnsiChar;
    var Handle: XDW_DOCUMENT_HANDLE; var OpenMode: XDW_OPEN_MODE): Integer;
    stdcall; external 'xdwapi.dll';

  function XDW_GetDocumentInformation(Handle: XDW_DOCUMENT_HANDLE;
    var DocumentInfo: XDW_DOCUMENT_INFO): Integer;
    stdcall; external 'xdwapi.dll';

  function XDW_RotatePageAuto(Handle: XDW_DOCUMENT_HANDLE; nPage: Integer;
    reserved: Pointer=nil): Integer; stdcall; external 'xdwapi.dll';

  function XDW_SaveDocument(Handle: XDW_DOCUMENT_HANDLE;
    reserved: Pointer= nil): Integer; stdcall; external 'xdwapi.dll';

  function XDW_CloseDocumentHandle(Handle: XDW_DOCUMENT_HANDLE;
    reserved: Pointer=nil): Integer; stdcall; external 'xdwapi.dll';

  function XDW_Finalize(reserved: Pointer = nil): Integer;
    stdcall; external 'xdwapi.dll';

// xdwapiを使うための宣言--ここまで

// 以下、PLUGINSPI用の処理
const
  PLUGGEDIN_REGKEY = 'Software\\FujiXerox\\MPM3\\MPWS\\PLUGGEDIN';
  MAX_PLUGGEDIN_NUMBER = 30;
  PLUGGEDIN_VERSION = 2;
  //
  PLUGGEDIN_NAME_INITIALIZE = '_PLG_Initialize@4';
  PLUGGEDIN_NAME_FINALIZE ='_PLG_Finalize@4';
  PLUGGEDIN_NAME_CANFINALIZE = '_PLG_CanFinalize@4';
  PLUGGEDIN_NAME_REQUIREFILES =  '_PLG_RequireFiles@4';
  PLUGGEDIN_NAME_EXECUTECOMMAND = '_PLG_ExecuteCommand@4';
  PLUGGEDIN_NAME_ISPARALLEL = '_PLG_IsParallel@4';
  PLUGGEDIN_NAME_ISCLONINGCOMMAND = '_PLG_IsCloningCommand@4';
  PLUGGEDIN_NAME_GETNEWCLONE =  '_PLG_GetNewClone@8';
  PLUGGEDIN_NAME_RELEASECLONE = '_PLG_ReleaseClone@4';
  PLUGGEDIN_NAME_EXECUTABLE =  '_PLG_Executable@8';
  PLUGGEDIN_NAME_CANSETPROFILE = '_PLG_CanSetProfile@4';
  PLUGGEDIN_NAME_SETPROFILE = '_PLG_SetProfile@4';
  PLUGGEDIN_NAME_GETCOMMANDICON = '_PLG_GetCommandIcon@8';
  PLUGGEDIN_NAME_ENUMERATECOMMANDS = '_PLG_EnumerateCommands@12';

type
  Tps_pszFiles = ^_Tps_pszFiles;
  _Tps_pszFiles = array[0..32767] of PChar;

  Tps_pnPageNumbers = ^_ps_pnPageNumbers;
  _ps_pnPageNumbers = array[0..32767] of LongInt;

  PLUGGEDIN_STRUCT = ^TPluggedin_struct;
  TPluggedin_struct = packed record
    ps_nPlugVersion: LongInt;
    ps_pszFunction: PChar;
    ps_execParallel: LongInt;
    ps_nFiles: LongInt;
    ps_pszFiles: Tps_pszFiles;
    ps_pnPageNumbers: Tps_pnPageNumbers;
    ps_pszExecFolder: PChar;
    ps_hwndDWDesk: LongInt;
  end;

// Initialize Entry
function PLG_Initialize(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 1;
end;

// Finalize Entry
function PLG_Finalize(const cmdName: PChar): LongInt; stdcall;
begin
  XDW_Finalize(nil);
  Result := 1;
end;

// CanFinalize Entry
function PLG_CanFinalize(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 1;
end;

// RequireFiles Entry
function PLG_RequireFiles(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 1;
end;

// ExecuteCommand Entry
function PLG_ExecuteCommand(pPlgStruct: PLUGGEDIN_STRUCT): LongInt; stdcall;
const
  M1 = '自動正立';
  M2 = '時間がかかるけどいいの?';
  M3 = '終了しました。';
var
  I,J,K,L,Last_Page: Integer;
  S: String;
  h: XDW_DOCUMENT_HANDLE;
  mode: XDW_OPEN_MODE;
  info: XDW_DOCUMENT_INFO;
  InputFile: String;

begin
  Result := 1;
  if MessageBox(0, PChar(M2), PChar(M1), MB_OKCANCEL) <> 1 Then Exit;

  // 文書ハンドルを開く
  mode.nSize := sizeof(XDW_OPEN_MODE);
  mode.nOption := XDW_OPEN_UPDATE;

  S := '';
  for I := 0 to  pPlgStruct^.ps_nFiles-1 do
  begin
    //pPlgStruct^.ps_pnPageNumbers[I]; 表示ページ番号
    InputFile := pPlgStruct^.ps_pszFiles^[I];
    J := XDW_OpenDocumentHandle(PAnsiChar(InputFile), h, mode);
    try
      if J > -1 then
      begin
        info.nSize := SizeOf(XDW_DOCUMENT_INFO);

        XDW_GetDocumentInformation(h, info);
        Last_Page := info.nPages;

        K := 0;
        for L := 1 to Last_Page do
        begin
          J := XDW_RotatePageAuto(h, L);
          K := K + J;
        end;

        // エラーがない場合に変更内容を保存する
        if K = 0 then
          XDW_SaveDocument(h);
      end;
    finally
      // 文書ハンドルを閉じる
      XDW_CloseDocumentHandle(h);
    end;
  end;

  MessageBox(0, PChar(M3),PChar(M1), MB_OK);

  Result := 1;
end;

// IsParallel Entry
function PLG_IsParallel(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 0;
end;

// IsCloning Entry
function PLG_IsCloningCommand(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 0;
end;

// GetNewClone Entry
function PLG_GetNewClone(cmdName: PChar; nBufSize: LongInt): LongInt; stdcall;
begin
  Result := 0;
end;

// ReleaseClone Entry
function PLG_ReleaseClone(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 0;
end;

// Executable Entry
function PLG_Executable(const cmdName: PChar; nArgFiles: LongInt):
  LongInt; stdcall;
begin
  Result := nArgFiles; // ファイルが選択されていない場合には処理させない。
end;

// CanSetProfile Entry
function PLG_CanSetProfile(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 0;
end;

// SetProfile Entry
function PLG_SetProfile(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 0;
end;

// GetAppIcon Entry
function PLG_GetCommandIcon(const cmdName: PChar; bNormalSize: LongInt):
  HICON; stdcall;
begin
  // 本当はbNormalSize (0,1) により大きなアイコンと小さなものを設定する。
  Result:= LoadIcon(hInstance, MAKEINTRESOURCE('MAINICON'));
end;

// EnumerateCommands Entry
function PLG_EnumerateCommands(var cmdNameList: PChar; nBufSize: LongInt;
  var nBufSizeRequired: LongInt): LongInt; stdcall;
const
  S = '自動正立 by hiderin';
begin
  nBufSizeRequired := Length(S)+1;
  if (nBufSize < nBufSizeRequired) then
  begin
    Result := 0;
    Exit;
  end;
  StrCopy(@cmdNameList, S);
  Result := 1;
end;

exports
  PLG_INITIALIZE name PLUGGEDIN_NAME_INITIALIZE,
  PLG_FINALIZE name PLUGGEDIN_NAME_FINALIZE,
  PLG_CANFINALIZE name PLUGGEDIN_NAME_CANFINALIZE,
  PLG_REQUIREFILES name PLUGGEDIN_NAME_REQUIREFILES,
  PLG_EXECUTECOMMAND name PLUGGEDIN_NAME_EXECUTECOMMAND,
  PLG_ISPARALLEL name PLUGGEDIN_NAME_ISPARALLEL,
  PLG_ISCLONINGCOMMAND name PLUGGEDIN_NAME_ISCLONINGCOMMAND,
  PLG_GETNEWCLONE name PLUGGEDIN_NAME_GETNEWCLONE,
  PLG_RELEASECLONE name PLUGGEDIN_NAME_RELEASECLONE,
  PLG_EXECUTABLE name PLUGGEDIN_NAME_EXECUTABLE,
  PLG_CANSETPROFILE name PLUGGEDIN_NAME_CANSETPROFILE,
  PLG_SETPROFILE name PLUGGEDIN_NAME_SETPROFILE,
  PLG_GETCOMMANDICON name PLUGGEDIN_NAME_GETCOMMANDICON,
  PLG_ENUMERATECOMMANDS name PLUGGEDIN_NAME_ENUMERATECOMMANDS;

begin
end.

|

☆「char**」 を Delphiで書くと・・・。

☆Docuworksプラグインを作成してみる。で、Delphi向けに書き直したPLUGGEDIN_STRUCT 構造体(レコード型)ですが、ファイルリストの取得がうまくいかないのでチェックしてみました。
type
  PLUGGEDIN_STRUCT = ^TPluggedin_struct;
  TPluggedin_struct = packed record
 (略)
    ps_pszFiles: PChar; ←ここが問題!
  (略)
  end;

ヘッダーファイルの構造体をよく見てみると・・・ん?・・・ 「char**」 ・・・ おおっ、なんと*(アスタリスク)が2つも並んでいるではないですか。
struct _pluggedin_struct {
 (略)
  const char** ps_pszFiles;
  (略)
};

Cがよくわかっていない私には難解でしたが、昨日、今日といろいろと試した結果、 次のコードでうまく取得できました。でも合ってるのかな??
type
  Tps_pszFiles = ^_Tps_pszFiles;
  _Tps_pszFiles = array[0..32767] of PChar;

  PLUGGEDIN_STRUCT = ^TPluggedin_struct;
  TPluggedin_struct = packed record
    ps_nPlugVersion: LongInt;
    ps_pszFunction: PChar;
    ps_execParallel: LongInt;
    ps_nFiles: LongInt;
    ps_pszFiles: Tps_pszFiles;
    ps_pnPageNumbers: LongInt;
    ps_pszExecFolder: PChar;
    ps_hwndDWDesk: LongInt;
  end;      

ファイル名の取得部分です。ちょっとややこしいですね。
function PLG_ExecuteCommand(pPlgStruct: PLUGGEDIN_STRUCT): LongInt; stdcall;

  (略)
  // 選択されたファイル名を順番に取得します。
  for I := 0 to  pPlgStruct^.ps_nFiles-1 do
  begin
    InputFile := pPlgStruct^.ps_pszFiles^[I];
 (略)
end;

|

■TMS Advanced ToolBars & Menusアップデート

TMS Advanced ToolBars & Menus が v3.5.1.0 から v3.5.2.0 にアップデートされました。

メーカーホームページからの引用です。

in v3.5.2.0
- New : exposed the toolbar displayed menu as TAdvToolBar.ActiveMenu
- New : property IntlKeybShortCuts added for automatic internationalization of keyboard values
- Improved : changed from TImageList to TCustomImageList in TAdvStickyPopupMenu
- Fixed : update of checkbox from button click event solved in TAdvStickyPopupMenu

tmssoftware.com

|

☆Google SketchUp & Delphi

Google SketchUpはRubyスクリプトで、機能拡張ができるということで試したみました。下記のコードで四角形を描画できました。
#Rubyのコードです。

def myScript

#四角形を作成
depth = 30.mm
width = 60.mm
model = Sketchup.active_model
entities = model.active_entities
pts = []
pts[0] = [0, 0, 0]
pts[1] = [width, 0, 0]
pts[2] = [width, depth, 0]
pts[3] = [0, depth, 0]
face = entities.add_face pts
boundingbox = face.bounds

end
いろいろ調べてみると、Delphiで作成したDLLの手続き(関数)をRubyスクリプトから呼び出すサンプルがありました。 これを使うとDelphiで入力と計算を行い、その結果をRubyスクリプトで描画させるというスタイルが可能なようです。

Google Sketchup Delphi Integration
http://www.delphigl.com/forum/viewtopic.php?t=7161

Ruby Tutorial - Dialog Boxes and WIN32API (Advanced)
http://www.suwiki.org/suwiki/index.php?title=Ruby_Tutorial_-_Dialog_Boxes_and_WIN32API_(Advanced)

|

■Delphiの本

今日、ジュンク堂書店京都BAL店に行って、『Delphi 2005 プログラミングテクニック Vol7 ,Vol8 インターネット編(Ⅰ) (Ⅱ)』の2冊と建築の本を数冊買ってきました。この本にはサンプルを収録したCDがないんですね。ダウンロード先も書いていないようなので、先程、出版社に「サンプルのダウンロードってできますか」という問い合わせメールを送りました。サンプルコードが手に入らない場合には、スキャンしてOCRしようかな。 そうそうRay Lischner著のInside Delphiが1冊置いてありました。ずっとほしかった本ですが、さすがにDelphi1~3の内容のみとなると悩むところです。

|

☆Docuworksプラグインを作成してみる。

Fuji XeroxはDocuworksのプラグイン仕様についても丁寧な解説とヘッダーファイルを配布しておられるのですが、 C++向けとなっており、私にはよくわからないんですよね。いろいろ試した結果、下記のコードで一応動作しているようなので書いておきますね。

プラグインの実態は、DLLなので、ファイル新規作成その他DLLウィザードをクリックして新規にプロジェクトを作成します。
library Project1;

uses
  Windows,
  SysUtils;

{$R *.res}

const
  PLUGGEDIN_REGKEY = 'Software\\FujiXerox\\MPM3\\MPWS\\PLUGGEDIN';
  MAX_PLUGGEDIN_NUMBER = 30;
  PLUGGEDIN_VERSION = 2;
  //
  PLUGGEDIN_NAME_INITIALIZE = '_PLG_Initialize@4';
  PLUGGEDIN_NAME_FINALIZE ='_PLG_Finalize@4';
  PLUGGEDIN_NAME_CANFINALIZE = '_PLG_CanFinalize@4';
  PLUGGEDIN_NAME_REQUIREFILES =  '_PLG_RequireFiles@4';
  PLUGGEDIN_NAME_EXECUTECOMMAND = '_PLG_ExecuteCommand@4';
  PLUGGEDIN_NAME_ISPARALLEL = '_PLG_IsParallel@4';
  PLUGGEDIN_NAME_ISCLONINGCOMMAND = '_PLG_IsCloningCommand@4';
  PLUGGEDIN_NAME_GETNEWCLONE =  '_PLG_GetNewClone@8';
  PLUGGEDIN_NAME_RELEASECLONE = '_PLG_ReleaseClone@4';
  PLUGGEDIN_NAME_EXECUTABLE =  '_PLG_Executable@8';
  PLUGGEDIN_NAME_CANSETPROFILE = '_PLG_CanSetProfile@4';
  PLUGGEDIN_NAME_SETPROFILE = '_PLG_SetProfile@4';
  PLUGGEDIN_NAME_GETCOMMANDICON = '_PLG_GetCommandIcon@8';
  PLUGGEDIN_NAME_ENUMERATECOMMANDS = '_PLG_EnumerateCommands@12';

type
  PLUGGEDIN_STRUCT = ^TPluggedin_struct;
  TPluggedin_struct = packed record
    ps_nPlugVersion: LongInt;
    ps_pszFunction: PChar;
    ps_execParallel: LongInt;
    ps_nFiles: LongInt;
    ps_pszFiles: PChar;
    ps_pnPageNumbers: LongInt;
    ps_pszExecFolder: PChar;
    ps_hwndDWDesk: LongInt;
  end;

// Initialize Entry
function PLG_Initialize(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 1;
end;

// Finalize Entry
function PLG_Finalize(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 1;
end;

// CanFinalize Entry
function PLG_CanFinalize(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 1;
end;

// RequireFiles Entry
function PLG_RequireFiles(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 1;
end;

// ExecuteCommand Entry
function PLG_ExecuteCommand(pPlgStruct: PLUGGEDIN_STRUCT): LongInt; stdcall;
begin
  MessageBox(0, pPlgStruct.ps_pszFunction,
    'Welcome to Docuworks Plugins', MB_OK);
  Result := 1;
end;

// IsParallel Entry
function PLG_IsParallel(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 0;
end;

// IsCloning Entry
function PLG_IsCloningCommand(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 0;
end;

// GetNewClone Entry
function PLG_GetNewClone(cmdName: PChar; nBufSize: LongInt): LongInt; stdcall;
begin
  Result := 0;
end;

// ReleaseClone Entry
function PLG_ReleaseClone(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 0;
end;

// Executable Entry
function PLG_Executable(const cmdName: PChar; nArgFiles: LongInt):
  LongInt; stdcall;
begin
  Result := 1;
end;

// CanSetProfile Entry
function PLG_CanSetProfile(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 0;
end;

// SetProfile Entry
function PLG_SetProfile(const cmdName: PChar): LongInt; stdcall;
begin
  Result := 0;
end;

// GetAppIcon Entry
function PLG_GetCommandIcon(const cmdName: PChar; bNormalSize: LongInt):
  HICON; stdcall;
begin
  Result := LoadIcon(hInstance, MAKEINTRESOURCE('MAINICON'));
end;

// EnumerateCommands Entry
function PLG_EnumerateCommands(var cmdNameList: PChar; nBufSize: LongInt;
  var nBufSizeRequired: LongInt): LongInt; stdcall;
const
  S = 'SamplePlugin by hiderin';
begin
  nBufSizeRequired := Length(S)+1;
  if (nBufSize < nBufSizeRequired) then
  begin
    Result := 0;
    Exit;
  end;
  StrCopy(@cmdNameList, S);
  Result := 1;
end;

exports
  PLG_INITIALIZE name PLUGGEDIN_NAME_INITIALIZE,
  PLG_FINALIZE name PLUGGEDIN_NAME_FINALIZE,
  PLG_CANFINALIZE name PLUGGEDIN_NAME_CANFINALIZE,
  PLG_REQUIREFILES name PLUGGEDIN_NAME_REQUIREFILES,
  PLG_EXECUTECOMMAND name PLUGGEDIN_NAME_EXECUTECOMMAND,
  PLG_ISPARALLEL name PLUGGEDIN_NAME_ISPARALLEL,
  PLG_ISCLONINGCOMMAND name PLUGGEDIN_NAME_ISCLONINGCOMMAND,
  PLG_GETNEWCLONE name PLUGGEDIN_NAME_GETNEWCLONE,
  PLG_RELEASECLONE name PLUGGEDIN_NAME_RELEASECLONE,
  PLG_EXECUTABLE name PLUGGEDIN_NAME_EXECUTABLE,
  PLG_CANSETPROFILE name PLUGGEDIN_NAME_CANSETPROFILE,
  PLG_SETPROFILE name PLUGGEDIN_NAME_SETPROFILE,
  PLG_GETCOMMANDICON name PLUGGEDIN_NAME_GETCOMMANDICON,
  PLG_ENUMERATECOMMANDS name PLUGGEDIN_NAME_ENUMERATECOMMANDS;

begin
end.



コンパイルしてできたDLLを C:\Program Files\Fuji Xerox\DocuWorks\bin\pluginsにコピーします。
Docuworksを起動させて、プラグイン設定を選択すると次のようなダイアログが表示されます。

Plugin1



実行させてみると、きちんとダイアログが表示されました。

Plugin2





DocuWorks Development Tool Kit 6.2.3 日本語版
http://www.fujixerox.co.jp/soft/docuworks/download301.html



[20080121 追記]
レコード型PLUGGEDIN_STRUCTを下記の記事で書き換えています。
☆「char**」 を Delphiで書くと・・・。

|

☆特殊フォルダのパス名を取得する。

SHGetFolderPathを使って特殊フォルダのパス名を取得してみます。
この関数は、SHFolder名前空間に定義されていますが、なぜか第4パラメーターdwFlagsのフラグが定義されていません。 又、SHFolder.dllからの呼び出しになっていますが、MSDNによると現在は、shell32.dllで実装されているとのことです。 但し、下位互換のためSHFolder.dllは含まれているとのことで特に問題はなさそうです。

下記のコードでMy Documentsフォルダが取得できました。
uses SHFolder;

const
  // dwFlags用フラグ
  SHGFP_TYPE_CURRENT = 0;
  SHGFP_TYPE_DEFAULT = 1;

procedure TForm1.Button1Click(Sender: TObject);
var
  Path: String;
begin
  SetLength(Path, MAX_PATH);
  if Succeeded(SHGetFolderPath(0, CSIDL_PERSONAL, 0,
    SHGFP_TYPE_CURRENT,PChar(Path))) then
  begin
    SetLength(Path, Pred(Pos(#0, Path)));
    ShowMessage(Path);
  end;
end;



MSDN
SHGetFolderPath Function
http://msdn2.microsoft.com/en-us/library/bb762181.aspx

|

■TMS Advanced ToolBars & Menusアップデート

TMS Advanced ToolBars & Menus が v3.5.0.0 から v3.5.1.0 にアップデートされました。

メーカーホームページからの引用です。

in v3.5.1.0
- New : exposed the toolbar displayed menu as TAdvToolBar.ActiveMenu
- New : property IntlKeybShortCuts added for automatic internationalization of keyboard values
- Improved : changed from TImageList to TCustomImageList in TAdvStickyPopupMenu
- Fixed : update of checkbox from button click event solved in TAdvStickyPopupMenu

tmssoftware.com

|

■DocuWorks Development Tool Kitのアップデート

Docuworksのアップデートに伴い、SDKもDocuWorks Development Tool Kit 6.2.3 日本語版にアップデートされています。 ただ、リリースノートによりますとContent Filter I/F以外は更新されていないようです。ついでにリリースノートの日付も2007年10月から更新されていませんけど(^^;

FUJI XEROX
DocuWorks Development Tool Kit 6.2.3 日本語版
http://www.fujixerox.co.jp/soft/docuworks/download301.html

|

☆AquesTalkを使って音声合成させる。

LaLaVoice 2001の記事を書きながら、東芝ユーザー限定の話なので、あまり意味がないかなと思っていたら、FDelphiでAquesTalkを使った音声合成のサンプルが・・・Fermionさん、タイミングよすぎです(笑)

私はAquesTalkを知らなかったんですが、 こんな高度なライブラリをフリーで公開されておられるなんて信じられないです。 (しかも営利、非営利にかかわらず無償で使用できるそうです)
ただ、このライブラリでは、より自然な発音をさせるために音声記号列というのを使うため、LaLaVoiceのように読み上げに使うというよりは音声ガイド的な使い方になるのでしょうか。
いや、どちらにしても本当に素晴らしいです。


Delphi User's Forum
http://fdelphi.com/
音声案内を出すには?

株式会社 アクエスト
AquesTalk - テキスト音声合成ミドルウェア
http://www.a-quest.com/aquestalk/

|

☆LaLaVoice 2001を使って音声合成させる。

LaLaVoice 2001とは、東芝の音声認識/音声合成ソフトです。製品としてまだ販売されているかどうかわかりませんが、最新のDynabookでもプレインストールされているようですね。

LaLaVoice 2001には、LaLaVoice 2001 SDKが配布されていますが、私のDynabookでは、LaLaVoice 2001がプレインストールされているのにもかかわらず 「東芝音声システム v5.0以上もしくはLaLaVoice 2001がインストールされている必要があります」 というエラーが出てインストールできません。この件については以前、東芝に問い合わせしましたが、SDKについては一切サポートをしないという悲しいメールを頂いています。

前置きが長くなりましたが、OCXで提供されているので、音声合成のプログラムは簡単です。
まず、コンポーネントのインポートで、東芝音声合成コントロールというActiveXを取り込み、インストールします。

フォームにTTOSTTSX、TButton、TMemoを配置して次のような処理をさせます。
procedure TForm1.Button1Click(Sender: TObject);
begin
  TOSTTSX1.Speak(Memo1.Text);
end;
これだけで、Memo1の内容を読み上げることができます。


東芝
LaLaVoice 2001
http://www3.toshiba.co.jp/pc/lalavoice/index_j.htm

LaLaVoice 2001 SDK
http://www3.toshiba.co.jp/pc/lalavoice/sdk.htm

|

☆DocuWorksファイルの自動正立

DocuWorksでスキャンした書類を向きを揃えるのは面倒なので、何か方法がないのかなと思って調べてみました。 DocuWorks Deskのメニューには自動処理的なものが見当たらなかったのですが、APIにXDW_RotatePageAutoというのがあったので、試してみました。
→メニューにありました。この記事の追記を参照して下さい。

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

const
  XDW_OPEN_UPDATE	= 1;

type
  XDW_DOCUMENT_HANDLE = record
    dummy: LongInt;
  end;

  XDW_OPEN_MODE = record
    nSize: Integer;
    nOption: Integer;
  end;

  XDW_DOCUMENT_INFO = record
    nSize: Integer;
    nPages: Integer;
    nVersion: Integer;
    nOriginalData: Integer;
    nDocType: Integer;
    nPermission: Integer;
    nShowAnnotations: Integer;
    nDocuments: Integer;
    nBinderColor: Integer;
    nBinderSize: Integer;
  end;

  function XDW_OpenDocumentHandle(const lpszFilePath: PAnsiChar;
    var Handle: XDW_DOCUMENT_HANDLE; var OpenMode: XDW_OPEN_MODE): Integer;
    stdcall; external 'xdwapi.dll';

  function XDW_GetDocumentInformation(Handle: XDW_DOCUMENT_HANDLE;
    var DocumentInfo: XDW_DOCUMENT_INFO): Integer;
    stdcall; external 'xdwapi.dll';

  function XDW_RotatePageAuto(Handle: XDW_DOCUMENT_HANDLE; nPage: Integer;
    reserved: Pointer=nil): Integer; stdcall; external 'xdwapi.dll';

  function XDW_SaveDocument(Handle: XDW_DOCUMENT_HANDLE;
    reserved: Pointer= nil): Integer; stdcall; external 'xdwapi.dll';

  function XDW_CloseDocumentHandle(Handle: XDW_DOCUMENT_HANDLE;
    reserved: Pointer=nil): Integer; stdcall; external 'xdwapi.dll';

  function XDW_Finalize(reserved: Pointer = nil): Integer;
    stdcall; external 'xdwapi.dll';


procedure TForm1.Button1Click(Sender: TObject);
var
  h: XDW_DOCUMENT_HANDLE;
  mode: XDW_OPEN_MODE;
  info: XDW_DOCUMENT_INFO;
  InputFile: String;
  I, J, K, Last_Page: Integer;
begin
 // 文書ハンドルを開く
  mode.nSize := sizeof(XDW_OPEN_MODE);
  mode.nOption := XDW_OPEN_UPDATE;

  InputFile := 'c:\delphi.xdw';

  I := XDW_OpenDocumentHandle(PAnsiChar(InputFile), h, mode);
  try
    if I > -1 then
    begin
      info.nSize := SizeOf(XDW_DOCUMENT_INFO);
      info.nPages := 0;
      info.nVersion := 0;
      info.nOriginalData := 0;
      info.nDocType := 0;
      info.nPermission := 0;
      info.nShowAnnotations := 0;
      info.nDocuments := 0;
      info.nBinderColor := 0;
      info.nBinderSize := 0;

      XDW_GetDocumentInformation(h, info);
      Last_Page := info.nPages;

      K := 0;
      for I := 1 to Last_Page do
      begin
        J := XDW_RotatePageAuto(h, I);
        K := K + J;
      end;

      // エラーがない場合に変更内容を保存する
      if K = 0 then
        XDW_SaveDocument(h);
    end;
  finally
    // 文書ハンドルを閉じる
    XDW_CloseDocumentHandle(h);
  end;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  XDW_Finalize(nil);  // XDW_RotatePageAutoを呼び出したらこの処理が必ず必要
end;

end.


実行すると次のような横向きの画像が・・・

Yoko

このようにきちんと縦向きに回転されました。

Tate



上の画像以外にもページ数が多いものや少ないもの等いろいろな書類を処理してみましたが、一つだけ次のようなエラーが表示されるものがありました。

Error

状況としては10ページある書類データで最終ページの処理でエラーになります。他のデータと同じくスキャンして取り込んだものなのでパーミッションの問題ではないと思うのですが、エラーの原因はわかりませんでした。


DocuWorks Development Tool Kit 6.2 日本語版
http://www.fujixerox.co.jp/soft/docuworks/download301.html



[20080121 追記]
Docuworks Deskの設定スキャン取り込みタブにスキャン文書の後処理読める方向に全ページを自動回転するという設定がありました。

|

☆TStringGridのDrag&Drop

行単位でDrag&Dropにより、データを入れ替えることを前提としたサンプルです。 行番号部分をクリックすると列選択となり、列の選択には、CtrlキーやShiftキーと左クリックによる選択やマウスのスライドによる範囲選択ができます。実際に使えるものにするには、まだまだ作りこまないといけませんが、まあ、そのあたりはサンプルということで(^^;

Stringgrid_2


unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    StringGrid1: TStringGrid;
    procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure StringGrid1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure StringGrid1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure StringGrid1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure StringGrid1DragOver(Sender, Source: TObject; X, Y: Integer;
      State: TDragState; var Accept: Boolean);
    procedure StringGrid1DragDrop(Sender, Source: TObject; X, Y: Integer);
    procedure FormCreate(Sender: TObject);
  private
  public
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

const
  Flag = 1;

var
  CurrRow, FocusRow: Integer;
  Sliding: Boolean;

procedure TForm1.FormCreate(Sender: TObject);
var
  I,J: Integer;
begin
  StringGrid1.DefaultDrawing := False;
  StringGrid1.Align := alClient;
  StringGrid1.RowCount := 50;

  // とりあえずサンプルデータ
  for I := 1 to StringGrid1.RowCount -1 do
    for J := 1 to StringGrid1.ColCount -1 do
      StringGrid1.Cells[J,I] := 'ITEM'+IntToStr(I*J) ;
end;

procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  DRect: TRect;
  Mode: Integer;
begin
  DRect := Rect;
  InflateRect(DRect, -2, -2);   

  { 描画位置の設定・・・適当に }
  if ACol = 1 then        //右寄せ
    Mode := DT_RIGHT
  else if ACol = 2 then   //左寄せ
    Mode := DT_LEFT
  else
    Mode := DT_CENTER;    //中央

  { 固定行が選択されている場合の表示 }
  if Integer(StringGrid1.Objects[ACol,ARow]) = Flag then
  begin
    StringGrid1.Canvas.Brush.Color := clGray;
    StringGrid1.Canvas.FillRect(Rect);
    DrawEdge(StringGrid1.Canvas.Handle, Rect, BDR_SUNKENINNER, BF_BOTTOMRIGHT);
    DrawEdge(StringGrid1.Canvas.Handle, Rect, BDR_SUNKENINNER, BF_TOPLEFT);
    DrawText(StringGrid1.Canvas.Handle, PChar(IntToStr(ARow)),
      Length(IntToStr(ARow)), DRect, Mode);
    Exit;
  end;

  { 固定行の標準描画 }
  if (ACol = 0) and (ARow > 0) then
  begin
    StringGrid1.Canvas.Brush.Color := clBtnFace;
    StringGrid1.Canvas.FillRect(Rect);
    DrawEdge(StringGrid1.Canvas.Handle, Rect,BDR_RAISEDINNER, BF_BOTTOMRIGHT);
    DrawEdge(StringGrid1.Canvas.Handle, Rect,BDR_RAISEDINNER, BF_TOPLEFT);
    DrawText(StringGrid1.Canvas.Handle, PChar(IntToStr(ARow)),
      Length(IntToStr(ARow)), DRect, DT_CENTER);
    Exit;
  end;

  { 固定列の標準描画 }
  if (ARow = 0) then
  begin
    StringGrid1.Canvas.Brush.Color := clBtnFace;
    StringGrid1.Canvas.FillRect(Rect);
    DrawEdge(StringGrid1.Canvas.Handle, Rect,BDR_RAISEDINNER, BF_BOTTOMRIGHT);
    DrawEdge(StringGrid1.Canvas.Handle, Rect,BDR_RAISEDINNER, BF_TOPLEFT);

    if (ACol > 0) then
      DrawText(StringGrid1.Canvas.Handle, PChar(IntToStr(ACol)),
        Length(IntToStr(ACol)), DRect, DT_CENTER);
    Exit;
  end;

  { 行選択、セル選択の背景色の設定 }
  if (goRowSelect in StringGrid1.Options) and
     (Integer(StringGrid1.Objects[0,ARow]) = Flag) then
    StringGrid1.Canvas.Brush.Color := clAqua
  else
    StringGrid1.Canvas.Brush.Color := clWindow;

  StringGrid1.Canvas.FillRect(Rect);

  DrawText(StringGrid1.Canvas.Handle, PChar(StringGrid1.Cells[ACol,ARow]),
    Length(StringGrid1.Cells[ACol,ARow]), DRect, Mode);

  if (not (goRowSelect in StringGrid1.Options)) and (gdFocused in State) then
    StringGrid1.Canvas.DrawFocusRect(Rect);
end;

procedure TForm1.StringGrid1MouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

  procedure FlagClear;
  var
    I: Integer;
  begin
    for I := 0 to StringGrid1.Cols[0].Count - 1 do
      StringGrid1.Cols[0].Objects[I] := nil;
  end;

var
  I, ACol, ARow: Integer;
begin
  if (Button = mbRight) then Exit;

  { マウスの座標から現在のセル位置を取得します。 }
  StringGrid1.MouseToCell(X, Y, ACol, ARow);

  if (ACol <= 0) and (ARow <= 0) then Exit;

  { 0列目をクリックされた場合の処理 }
  if (ACol = 0) then
  begin
    {0列で1行目以上の場合にはDragを許可します。}
    if StringGrid1.Objects[0,ARow] = TObject(Flag) then
    begin
      StringGrid1.Row := ARow;
      StringGrid1.BeginDrag(True);
      Exit;
    end;

    { 行選択の処理 }
    CurrRow := Arow;
    Sliding := True;
    StringGrid1.Options := StringGrid1.Options + [goRowSelect];

    // CtrlキーかShiftキーが押されていない場合には、Flagを消去します。
    if (not (ssCtrl in Shift)) and (not (ssShift in Shift)) then
      FlagClear;

    if (ssCtrl in Shift) then
      StringGrid1.Objects[0, ARow] := TObject(Flag)
    else if (ssShift in Shift) then
    begin
      FlagClear;
      // 選択範囲にFlagを設定します。
      if (FocusRow < ARow) then
      begin
        for I := FocusRow to ARow do
          StringGrid1.Objects[0, I] := TObject(Flag);
      end
      else
      begin
        for I := FocusRow downto ARow do
         StringGrid1.Objects[0, I] := TObject(Flag);
      end
    end
    else
      StringGrid1.Objects[0,ARow] := TObject(Flag);

    { カーソルを現在の列に移動させます。 }
    StringGrid1.Row := ARow;
    FocusRow := ARow;
    StringGrid1.Invalidate;
  end
  else
  begin
    { 0列目以外の処理 }
    FlagClear;
    if ARow <> 0 then
      StringGrid1.Objects[0, ARow] := TObject(Flag);
    StringGrid1.Options := StringGrid1.Options - [goRowSelect];
    FocusRow := ARow;
  end;
end;

procedure TForm1.StringGrid1MouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: Integer);
var
  ACol, ARow: Integer;
begin
  {マウスの座標から現在のセル位置を取得します。}
  StringGrid1.MouseToCell(X, Y, ACol, ARow);
  if (ACol <= 0) and (ARow <= 0) then Exit;

  if Sliding and (ARow <> CurrRow)then
  begin
   if Integer(StringGrid1.Objects[0,ARow]) = Flag then
     StringGrid1.Objects[0,ARow] := nil
   else
     StringGrid1.Objects[0,ARow] := TObject(Flag);

   StringGrid1.Invalidate;
   CurrRow := Arow;
  end;    
end;

procedure TForm1.StringGrid1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  Sliding := False;
end;

procedure TForm1.StringGrid1DragOver(Sender, Source: TObject; X,
  Y: Integer; State: TDragState; var Accept: Boolean);
var
  ACol, ARow: Integer;
begin
  if (Source is TStringGrid) then
    (Sender as TStringGrid).MouseToCell(X, Y, ACol, ARow);
    
  Accept := ((Sender is TStringGrid) and (Source is TStringGrid)) and
            ((Sender as TStringGrid) = (Source as TStringGrid)) and
            (ARow > 0);
end;

procedure TForm1.StringGrid1DragDrop(Sender, Source: TObject; X,
  Y: Integer);
var
   I, DestRow, ACol: Integer;
   SL: TStringList;
   Temp: TList;
begin
  Temp := TList.Create;
  try
    {挿入位置をマウス座標から取得します。}
    StringGrid1.MouseToCell(X, Y, ACol, DestRow);

    {DestRowまで選択行以外のものを保存します。}
    for I := 0 to DestRow -1 do
    begin
      if StringGrid1.Objects[0,I] <> TObject(Flag) then
      begin
        SL := TStringList.Create;
        SL.Assign(StringGrid1.Rows[I]);
        Temp.Add(SL);
      end;
    end;

    {選択行を保存します。}
    for I := 0 to StringGrid1.RowCount -1 do
    begin
      if StringGrid1.Objects[0,I] = TObject(Flag) then
      begin
        SL := TStringList.Create;
        SL.Assign(StringGrid1.Rows[I]);
        Temp.Add(SL);
      end;
    end;

    {DestRow以上で選択行以外を保存します。}
    for I := DestRow to StringGrid1.RowCount -1 do
    begin
      if StringGrid1.Objects[0,I] <> TObject(Flag) then
      begin
        SL := TStringList.Create;
        SL.Assign(StringGrid1.Rows[I]);
        Temp.Add(SL);
      end;
    end;

    {StringGrid1に設定します。}
    for I := 0 to Temp.Count -1 do
      StringGrid1.Rows[I].Assign(TStringList(Temp[I]));
  finally
    for I := 0 to Temp.Count -1 do
      TStringList(Temp[I]).Free;
    Temp.Free;
  end;
end;

end.

|

☆DocuWorksViewerを作る。

下記のようなDocuWorksViewerを作ってみます。
(DocuWorksとはFuji Xeroxのドキュメント有効活用ソフトウエアです)
※DocuWorksがインストールされている必要があります。

Dw7


DocuWorksのAPIではなく、ActiveXを使うだけなので簡単です。
まず、ActiveXの取り込みを下記の手順で行います。

Dw1


メニューから コンポーネント → コンポーネントのインストールを選択します。

Dw2


Dw3


Dw4_3


Dw5


Dw6


インストールしたTDeskCtrlコンポーネントを使ってDocuWorksのファイルを表示させます。
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, OleCtrls, DESKCTRLLib_TLB;

type
  TForm1 = class(TForm)
    DeskCtrl1: TDeskCtrl;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  WS: WideString;
begin
  WS := 'C:\Delphi_QA_ja.xdw';
  DeskCtrl1.LoadFile(WS);
end;

end.


FUJI XEROX
DocuWorks
http://www.fujixerox.co.jp/soft/docuworks/

|

« 2007年12月 | トップページ | 2008年2月 »