Delphi XE5 で Canon ケーブルIDプリンター MK2500 を使う

一応プリンターなので、Delphi からでも使えます。が、あまり使い勝手が良いとは言えません。
本体の編集文字や設定とはまったく別に動くことが、何かのメリットになるのかも。(^^;
(製品に同梱されているツールの使い勝手も、お世辞にも良いとは言えません)


MK2500 本体の編集窓部分に、文字列を転送するのではなく、プリンターとしてラベル、チューブ... に描画する使い方になります。
フォントは、MK2500 のフォントではなく、Windows のフォントになります。
※プリンタ情報の取得、設定については、Mr.Xray さんのサイトを参考にさせていただきました。m(_"_)m


エクセルからのコピーペーストを想定して、一行の文字列中にタブがあれば、2行で印字するようにしています。
今のところ、連番には未対応 ... です。

カット長(用紙長)の設定がサポートされていないようなので、プロパティウィンドウを表示させて、その部分を別スレッドから無理やり書き換えています。
なので、一瞬、プロパティウィンドウが表示されます。





unit mk2500Unit2;

interface

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

type
  TForm2 = class(TForm)
    Memo1: TMemo;
    ComboBox2: TComboBox;
    Label1: TLabel;
    Edit1: TEdit;
    Button3: TButton;
    Edit2: TEdit;
    Label2: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

uses mk2500Unit3;
procedure TabTextToSL(const atext: string; sl: TStrings);
var
  n : integer;
  s : string;
begin
  sl.Clear;
  s := atext;
  n := Pos(#09, s);
  while n > 0 do begin
	  sl.Add(Copy(s, 1, n - 1));
	  Delete(s, 1, n);
	  n := Pos(#09, s);
  end;
  if Trim(s) <> '' then sl.Add(s);
end;


procedure TForm2.Button3Click(Sender: TObject);
var
  ADevice : array [0..255] of Char;
  ADriver : array [0..255] of Char;
  APort : array [0..255] of Char;
  pDevMode : PDeviceModeW;

  PaperLength : SmallInt;
  PaperSize : SmallInt;
  ADeviceMode: THandle;
  hPrinter : THandle;
  Mode : Cardinal;

  ret : integer;
  i : integer;
  ytop, xleft, txtH : integer;
  sl : TStringList;
begin
  ret := 0;
  PaperLength := Trunc(StrToFloatDef(Edit1.Text, 0) * 10);
  with ComboBox2 do begin
    if ItemIndex >= 0 then
      PaperSize := SmallInt(Items.Objects[ItemIndex])
    else
      PaperSize := 0;
  end;

  if (PaperLength > 0) and (PaperSize > 0) then begin
    // プリンターを MK2500 に
    Printer.PrinterIndex := Printer.Printers.IndexOf('MK2500');
    hPrinter := 0;

    // 現在のプリンタに関する情報を取り出す
    Printer.GetPrinter(ADevice, ADriver, APort, ADeviceMode);
    // 初期化
    Printer.SetPrinter(ADevice, ADriver, APort, 0);
    // 対象のプリンタ情報
    Printer.GetPrinter(ADevice, ADriver, APort, ADeviceMode);

    if (ADeviceMode > 0) then begin
      // プリンタ情報のアドレスを取得
      pDevMode := GlobalLock(ADeviceMode);
      try
       // 対象のプリンタのハンドルを取得
        if OpenPrinter(ADevice, hPrinter, nil) then begin
          try
            // カット長の設定のためにスレッド生成
            Tmk2500edit.Create(Edit1.Text);

            if (pDevMode^.dmFields and DM_PAPERSIZE) <> 0 then
              pDevMode^.dmPaperSize := PaperSize;

            // ※PAPERLENGTH はサポートされていない
            if (pDevMode^.dmFields and DM_PAPERLENGTH) <> 0 then
              pDevMode^.dmPaperLength := PaperLength;

            if (pDevMode^.dmFields and DM_COPIES) <> 0 then
              pDevMode^.dmCopies := 1;

            Mode := DM_IN_PROMPT or DM_OUT_BUFFER or DM_IN_BUFFER;

            // MK2500 のプロパティダイアログを表示
            // 別スレッドから自動で閉じられる
            ret := DocumentProperties(Handle, hPrinter, ADevice, pDevMode^, pDevMode^, Mode);

          finally
            // プリンターを閉じる
            ClosePrinter(hPrinter);
          end;
        end;
      finally
        // ロック解除
        GlobalUnLock(ADeviceMode);
      end;
      // 設定を反映
      Printer.SetPrinter(ADevice, ADriver, APort, ADeviceMode);
    end;
    if ret = 1 then begin
      with Printer do begin
        // Canvas.Font.Name := 'MS ゴシック';
        Canvas.Font.Size := 10;
        txtH := Canvas.TextHeight('A');
        sl := TStringList.Create;
        try
          // 転送開始
          BeginDoc;
          for i := 0 to Memo1.Lines.Count - 1 do begin
            if i > 0 then NewPage;

            TabTextToSL(Memo1.Lines[i], sl);
            if sl.Count = 1 then begin
              // センタリングのための開始 Y 座標
              ytop := (PageHeight - txtH) div 2;
              // センタリングのための開始 X 座標
              xleft := (PageWidth - Canvas.TextWidth(sl[0])) div 2;
              if xleft < 0 then xleft := 0;
              // 文字列描画
              Canvas.TextOut(xleft, ytop, sl[0]);
            end
            else if sl.Count >= 2 then begin
              // 2行印字
              ytop := PageHeight div 2 - txtH + 2;
              xleft := (PageWidth - Canvas.TextWidth(sl[0])) div 2;
              if xleft < 0 then xleft := 0;
              Canvas.TextOut(xleft, ytop, sl[0]);

              ytop := PageHeight div 2 - 2;
              xleft := (PageWidth - Canvas.TextWidth(sl[1])) div 2;
              if xleft < 0 then xleft := 0;
              Canvas.TextOut(xleft, ytop, sl[1]);
            end;
          end;
          EndDoc;
        finally
          sl.Free;
        end;
      end;
    end;
  end;
end;

procedure TForm2.FormCreate(Sender: TObject);
var
  ADevice : array [0..255] of Char;
  ADriver : array [0..255] of Char;
  APort : array [0..255] of Char;
  ADeviceMode : THandle;
  Count : Integer;

  // 動的配列
  PaperNames : array of array [0..63] of Char;
  PaperNumbers : array of WORD;

  i : integer;
  s : string;
begin
  // 現在のプリンタを MK2500 にする
  Printer.PrinterIndex := Printer.Printers.IndexOf('MK2500');

  // 現在のプリンタに関する情報を取り出す
  Printer.GetPrinter(ADevice, ADriver, APort, ADeviceMode);

  // そのプリンタ 用紙名の数を取得
  Count := DeviceCapabilities(ADevice, APort, DC_PAPERNAMES, nil, nil);

  // その分だけ用紙名配列の長さと用紙番号の配列の長さを確保
  SetLength(PaperNames, Count);
  SetLength(PaperNumbers, Count);

  // その配列に用紙名と用紙番号を取得
  DeviceCapabilities(ADevice, APort, DC_PAPERNAMES, PChar(PaperNames), nil);
  DeviceCapabilities(ADevice, APort, DC_PAPERS, Pointer(PaperNumbers), nil);

  // 用紙名をコンボボックスに追加
  if Count > 0 then begin
    for i := 0 to Count - 1 do begin
      s := String(PaperNames[i]);
      if s <> '' then
        //用紙名と用紙番号を格納
        ComboBox2.Items.AddObject(s, TObject(PaperNumbers[i]));
    end;
    ComboBox2.ItemIndex := 1;
  end;
end;

end.

// スレッド部分
unit mk2500Unit3;

interface

uses
  System.Classes,Winapi.Windows, Winapi.Messages, System.SysUtils;

type
  Tmk2500edit = class(TThread)
  private
    { Private 宣言 }
    sLen : string;
  protected
    procedure Execute; override;
  public
    constructor Create(PaperLength : string);
  end;

implementation

uses mk2500Unit2;

var
  // mk2500 長さ設定のエディットウィンドウ
  mkedHwnd: Hwnd;


constructor Tmk2500edit.Create(PaperLength : string);
begin
  // カット長
  sLen := PaperLength; 
  // 生成と同時にスレッドを実行
  inherited Create(False);
  FreeOnTerminate := True;
end;

{ Tmk2500edit }

// プロパティウィンドウの長さ設定の Edit ウィンドウを探す
// 最初に見つかったクラス名'Edit'で終了
function EnumWindowProcEditWin(h: HWND; lp: LPARAM):Bool; stdcall;
var
  ClassName : array [0..255] of Char;
begin
  Result := True;
  //クラス名を取得
  GetClassName(h, ClassName, 255);
  if Classname = 'Edit' then begin
    mkedHwnd := h;
    Result := False;
  end;
end;

procedure Tmk2500edit.Execute;
var
  hmk : HWND;
  s : string;
  i : integer;
begin
  while not Terminated do begin
    Sleep(100);
    // プロパティ設定ダイアログを探す
    hmk := FindWindow(nil, 'MK2500のプロパティ');
    if hmk <> 0 then begin
      // オーナーが Form2 であれば
      if GetWindow(hmk, GW_OWNER) = Form2.Handle then begin

        // ウィンドウが完成するまで待つ (必須)
        Sleep(100);
        // 子ウィンドウを探す
        EnumChildWindows(hmk, @EnumWindowProcEditWin, 0);
        if IsWindow(mkedHwnd) then begin
          s := 'A' + sLen;
          //文字の送信
          for i := 1 to Length(s) do
            //1文字送る
            SendMessage(mkedHwnd, WM_CHAR, Word(s[i]), 0);

          // アクティブに
          SetForegroundWindow(hmk);
          // ちらっと見せる
          Sleep(100);

          //[Enter]キーを離す
          keybd_event(VK_RETURN, 0, 0, 0);
          //[Enter]キーを離す
          keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);

          // keybd_event(VK_ESCAPE, 0, 0, 0);
          // keybd_event(VK_ESCAPE, 0, KEYEVENTF_KEYUP, 0);
        end;
        // スレッド終了
        break;
      end;
    end;
  end;
end;

end.