DcadCapture ver.2015.5.3 for DraftSight 2015 / ARES Commander 2015

※ARES は廉価版でも COM API (ActiveX) が使えます。そちらを使ったほうがスマートです。

Bricscad 用のキャプチャーソフトを DraftSight / ARES 用に改造してみました。
キャプチャーした図面イメージをダブルクリックすると、CADの図面が切り替わります。
フォームの大きさは、自由に変更できます。

・コマンドラインに文字列を流し込む方法なので、タイミングが合わない場合があります。
・クリップボードを使用してコマンド履歴を取得しているため、操作後は、クリップボードに文字列が残ります。
・図面の切り替えは、メニューを操作しています。
 取得した時の図面数と、「メニュー」 - 「ウィンドウ」の図面数が合わない場合は、「閉じる」、「すべて閉じる」が選択される可能性があります。
 ※ARES では、「Classic Default」 表示にしておいて下さい。
・高解像度のPCでは、キャプチャー位置が合わないときがあります。DcadCapture.exe のプロパティーから、「高解像度DPI...」にチェックを付けて下さい。

・各所にタイミングをとるためのタイマーを入れているため、取得にかなり時間がかかります。
 ( 2015/5/3 :若干改善しました。23図面 130sec -> 45sec)

■履歴
2015/04/22
 ・初版作成
2015/04/28
 ・シート番号、またはページ番号を取得するを追加
 ・マウス右クリックのポップアップメニューに「再キャプチャ」を追加
2015/04/28(2回目)
 ・システム変数の取得を、属性と同様にDXFファイルから取得に変更。若干高速にした。
2015/05/03
 ・コマンド送信で、[Enter]、[ESC] の送信を追加。
 ・不要なタイミングタイマーを極力省いた。

■Draftsight 2015

■ARES Commander 2015

■ダウンロード
 DcadCapture.zip (2015/05/03 EXE本体のみ)

■ソースコード


unit DcadCaptureUnit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,{BricscadApp_TLB, BricscadDb_TLB,}
  Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.Grids, ComObj, IniFiles, Vcl.Buttons,
  Vcl.Menus, ClipBrd ;

type
  TDwgDoc = record
    Index : integer;
    Name : string;
    Comment : string;
  end;
type
  TForm2 = class(TForm)
    DrawGrid1: TDrawGrid;
    Panel1: TPanel;
    ComboBox1: TComboBox;
    SpeedButton1: TSpeedButton;
    PopupMenu1: TPopupMenu;
    N3: TMenuItem;
    N1: TMenuItem;
    N2: TMenuItem;
    SpeedButton2: TSpeedButton;
    SpeedButton3: TSpeedButton;
    N4: TMenuItem;
    N5: TMenuItem;
    Edit1: TEdit;
    SpeedButton4: TSpeedButton;
    procedure FormDestroy(Sender: TObject);
    procedure DrawGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure FormResize(Sender: TObject);
    procedure DrawGrid1DblClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ComboBox1Change(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
    procedure N3Click(Sender: TObject);
    procedure N2Click(Sender: TObject);
    procedure SpeedButton2Click(Sender: TObject);
    procedure SpeedButton3Click(Sender: TObject);
    procedure DrawGrid1Click(Sender: TObject);
    procedure DrawGrid1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    procedure N5Click(Sender: TObject);
    procedure SpeedButton4Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }

    // 取得するブロック名と属性名
    BlkName, BlkName1, BlkName2, AttName, AttName1, AttName2 : string;
    AttFlag1, AttFlag2 : boolean;

    // 連続実行するLISP
    LspFileName : TFileName;
    LspCmdName  : string;
    LspCommand  : string;

    MoveX, MoveY : integer;
  end;

var
  Form2: TForm2;

  // キャプチャしたビットマップを保持
  BmpAry : array of TBitMap;
  // 図面名とその位置を保持
  DwgDocAry : array of TDwgDoc;
  // ビットマップの大きさ
  BmpW, BmpH : integer;
  // タイトル表示の高さ
  TitleH1, TitleH2 : integer;
  // シート名、ページ番号を表示する
  CmtDispFlag : boolean;
  // キャプチャ時、属性を取得
  CmtGetFlag  : boolean;
  BcadActiveFlag : boolean;

  // メインウィンドウ
  DcadMainWinHandle : HWND;
  DcadMDIClientWinHandle : HWND;
  DcadCommandWinHandle : HWND;
  DcadInputWinHandle : HWND;
  DcadOutputWinHandle : HWND;
  DcadMDIActiveWinHandle : HWND;
  DcadMDIChildWinHandle : HWND;
  DCadProcessID : DWORD;     //プロセスID

implementation


{$R *.dfm}

uses DcadCaptureCfgUnit;

//****************************************
// 最後の指定文字列より後を得る
//****************************************
function LastSubstringAfter(const ststr, s: string):String;
var
  st, stlen : integer;
  temp : string;
begin
  Result := '';
  if Pos(ststr, s) > 0 then begin
    temp := s;

    stlen := Length(ststr);
    while Pos(ststr, temp) > 0 do begin
      st := Pos(ststr, temp) + stlen;
      temp := Copy(temp, st, Length(temp) - st + 1);
      if temp = '' then break;
    end;
    Result := Trim(temp);
  end;
end;

//****************************************
//最初に現れたststrからedstrまでを返す
//****************************************
function StrAfterStrBefore(const ststr, edstr, s:string):String;
var
  st, ed:integer;
  temp : string;
begin
  Result:='';
  st:=Pos(ststr,s);
  if st > 0 then begin
    temp := s;
    Delete(temp,1,st + Length(ststr)-1);
    ed := Pos(edstr, temp)-1;
    if ed >= 0 then
      Result := Trim(Copy(temp, 1, ed));
  end;
end;

//****************************************
// 最後の指定文字列より後を得る
//****************************************
function LastStrAfter(const ststr, s: string):String;
var
  st, stlen : integer;
  temp : string;
begin
  Result := '';
  if Pos(ststr, s) > 0 then begin
    temp := s;

    stlen := Length(ststr);
    while Pos(ststr, temp) > 0 do begin
      st := Pos(ststr, temp) + stlen;
      temp := Copy(temp, st, Length(temp) - st + 1);
      if temp = '' then break;
    end;
    Result := Trim(temp);
  end;
end;
//******************************************
// ウィンドウのタイトル(キャプション)を得る
//******************************************
function GetWindowCaption(h : HWND) : string;
var
  Title : array [0..255] of char;
begin
  result := '';
  if GetWindowText(h, Title, 255) <> 0 then
    result := Title;
end;
//****************************************
// メインウィンドウ用コールバック関数
//****************************************
function EnumWindowProcMainWin(h: HWND; lp: LParam): BOOL; stdcall;
var
  Title : array [0..255] of char;
  ClassName : array [0..255] of char;
begin
  Result := true;
  GetClassName(h, ClassName, 255);
  if 'Qt5QWindowIcon' = ClassName then begin
    // タイトルを得る
    if GetWindowText(h, Title, 255) <> 0 then begin
      if (Pos('DraftSight - [', Title) = 1) or
        (Pos('ARES Commander 2015 - [', Title) = 1) then begin
        DCadMainWinHandle := h;
        GetWindowThreadProcessId(h, @DCadProcessID);
        Result := false;
      end;
    end;
  end;
end;

//****************************************
// メインウィンドウのハンドルを得る
//****************************************
function GetDCadMainWinHandle : HWND;
begin

  EnumWindows(@EnumWindowProcMainWin, 0);
  Result := DCadMainWinHandle;
end;

// ************************************
// MDIClientウィンドウ取得用コールバック関数
// ************************************
function EnumCWinProc_MDIClient(h:HWND;lparam:Integer):Bool;stdcall;
var
  Title : array [0..255] of char;
begin
  Result:=True;
  if GetWindowText(h, Title, 255) <> 0 then begin
    if 'CFxWorkspaceImplClassWindow' = Title then begin
      DcadMDIClientWinHandle := GetWindow(h, GW_CHILD);
      DcadMDIChildWinHandle := GetWindow(DcadMDIClientWinHandle, GW_CHILD);
      Result := False;
    end;
  end;
end;

// ************************************
// MDIClientウィンドウ取得
// 実際には、WindowsのMDIClientでは無い
// ************************************
function GetDcadMDIClientHandle:HWND;
begin
  EnumChildWindows(DcadMainWinHandle,@EnumCwinProc_MDIClient,0);
  result:=DcadMDIClientWinHandle;
end;

// ************************************
// コマンドウィンドウ取得用コールバック関数
// ************************************
function EnumCWinProc_CommandWinHandle(h: HWND; lparam: Integer):Bool;stdcall;
var
  Title : array [0..255] of char;
begin
  Result:=True;
  if GetWindowText(h, Title, 255) <> 0 then begin
    if 'CFxCommandWindowWindow' = Title then begin
      DcadCommandWinHandle := h;
      Result:=False;
    end;
  end;
end;

// ************************************
// コマンドラインウィンドウを取得
// ************************************
function GetDcadCommandWinHandle:HWND;
begin
  EnumChildWindows(DcadMainWinHandle, @EnumCwinProc_CommandWinHandle, 0);
  result := DcadCommandWinHandle;
  if result <> 0 then begin
    DcadOutputWinHandle := GetWindow(DcadCommandWinHandle, GW_CHILD);
    DcadInputWinHandle := GetWindow(DcadOutputWinHandle, GW_HWNDNEXT);
  end;
end;

// ************************************
// 対象ウィンドウに文字列を送信
// ************************************
function SendHwndCmdLine(hCmdLine: HWND; const cmd : string):boolean;
var
  i : integer;
begin
  Result := False;
  if hCmdLine <> 0 then begin
    // 文字列を送信
    for i := 1 to Length(cmd) do begin
      if cmd[i] = #13 then begin
        SendMessage(hCmdLine, WM_KEYDOWN,VK_RETURN, 0);
        SendMessage(hCmdLine, WM_KEYUP,  VK_RETURN, 0);
        Sleep(1);
      end
      else if (cmd[i] = #27) or (cmd[i] = #3) then begin
        SendMessage(hCmdLine, WM_KEYDOWN, VK_ESCAPE, 0);
        SendMessage(hCmdLine, WM_KEYUP,   VK_ESCAPE, 0);
        Sleep(1);
      end
      else begin
        SendMessage(hCmdLine, WM_CHAR, Word(cmd[i]), 0);
        if cmd[i] = #20 then Sleep(1);
      end;
    end;
    result := true;
  end;
end;
// ************************************
// コマンドラインに文字列を送信
// ************************************
function SendDcadCommand(const cmd: string): boolean;
begin
  GetDcadCommandWinHandle;
  Result := SendHwndCmdLine(DcadInputWinHandle, cmd);
end;

function GetDcadVariable(const SysVar: string): string;
var
  s : string;
begin
  Result := '';

  GetDCadMainWinHandle;

  SetForegroundWindow(DcadMainWinHandle);
  SendDcadCommand(SysVar + #13#3);

  Sleep(10);

  SendDcadCommand('COPYHISTORY'#13);
  Sleep(200);
  s := '';
  try
    s := ClipBoard.AsText;
    s := LastSubstringAfter(': ' + SysVar, s);

    Result := '';
    if (Pos('=', s) > 0) and (Pos('(', s) > 1) then
      Result := Trim(StrAfterStrBefore('=', '(', s));
    if (Result = '') and (Pos('(', s) > 0) and (Pos(')', s) > 1) then
      Result := Trim(StrAfterStrBefore('(', ')', s));
    if (Result = '') and (Pos('デフォルト:', s)>0) then begin
      Result := Trim(StrAfterStrBefore('デフォルト:', #13, s));
    end;
  except
    ;
  end;
end;

// ************************************
// CAD をアクティブに
// ************************************
function SetDcadActive:boolean;
begin
  Result := False;
  GetDCadMainWinHandle;
  if IsWindow(DcadMainWinHandle) then begin
    SetForegroundWindow(DcadMainWinHandle);
    Result := True;
  end;
end;

//****************************************
// 画面の指定位置をBitmapに変換
//****************************************
procedure CaptureToBmp(Lf, Tp, W, H: Integer; bmp: TBitmap);
const
  CAPTUREBLT = $40000000;
var
  hdcScreen : HDC;
begin
  bmp.Width := W;
  bmp.Height := H;
  hdcScreen := CreateDC('DISPLAY', nil, nil, nil);
  try
    BitBlt( bmp.Canvas.Handle, 0, 0, W, H, hdcScreen, Lf, Tp, SRCCOPY or CAPTUREBLT);
  finally
    DeleteDC(hdcScreen);
  end;
end;

// *******************************
// DwgDocAryをソート
// *******************************
procedure SortDwgDocAry;
var
  len, n, m : integer;
  i, j: integer;
  DwgDocTmp : TDwgDoc;
  s, s1 : string;
begin
  n := Length(DwgDocAry);
  if n > 1 then begin

    // コメント文字列の最大文字数
    len := 0;
    for i := 0 to n -1 do begin
      m := DwgDocAry[i].Comment.Length;
      if len < m then len := m;
    end;
    // 文字列比較のため同じ長さにする
    for i := 0 to n -1 do begin
      with DwgDocAry[i] do begin
        m := Comment.Length;
        if m < len then begin
          for j := 1 to len - m do begin
            Comment:= ' ' + Comment;
          end;
        end;
      end;
    end;
    // ソート
    for i := 0 to n - 2 do begin
      s := DwgDocAry[i].Comment + ':' + DwgDocAry[i].Name;
      for j := i + 1 to n - 1 do begin
        s1 := DwgDocAry[j].Comment + ':' + DwgDocAry[j].Name;
        if s1 < s then begin
          DwgDocTmp := DwgDocAry[i];
          DwgDocAry[i] := DwgDocAry[j];
          DwgDocAry[j] := DwgDocTmp;
          s := s1;
        end;
      end;
    end;
  end;
end;

// ************************************
// DrawGrid 同期
// ************************************
procedure TForm2.ComboBox1Change(Sender: TObject);
var
  idx : integer;
  i : integer;
begin
  with ComboBox1 do begin
    idx := ItemIndex;
    if idx >= 0 then
      i := Integer(Items.Objects[idx])
    else
      i := -1;
  end;
  if (i >= 0) and (i < Length(DwgDocAry)) then begin
    with DrawGrid1 do begin
      Col := i mod ColCount;
      Row := i div ColCount;
    end;
  end;
end;

// ************************************
// コンボボックスを同期
// ************************************
procedure TForm2.DrawGrid1Click(Sender: TObject);
var
  i : integer;
begin
  with DrawGrid1 do
    i := Row * ColCount + col;
  ComboBox1.ItemIndex := i;
end;

// ************************************
// 図面切り替え
// ************************************
procedure TForm2.DrawGrid1DblClick(Sender: TObject);
var
  i, id, j : integer;
begin

  with DrawGrid1 do begin
    i := Row * ColCount + col;
    if (i >= 0) and (i < Length(DwgDocAry)) then begin
      id := DwgDocAry[i].Index;

      if GetDCadMainWinHandle = 0 then Exit;

      SetForegroundWindow(DcadMainWinHandle);

      // メニュー操作
      keybd_event(VK_MENU, 0, 0, 0);
      keybd_event(Ord('W'), 0, 0, 0);
      keybd_event(Ord('W'), 0, KEYEVENTF_KEYUP, 0);
      keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);

      for j := 0 to id + 4 do begin
        keybd_event(VK_DOWN, 0 ,0 ,0);
        keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0);
      end;

      keybd_event(VK_RETURN, 0, 0, 0);
      keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);

      ComboBox1.ItemIndex := i;
      //if BcadActiveFlag then
      //  SetForegroundWindow(DcadMainWinHandle);
    end;
  end;
  //if not BcadActiveFlag then
  //  SetForegroundWindow(Handle);
end;

// **************************
// キャプチャ画像を描画
// **************************
procedure TForm2.DrawGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  i, w, h, dx, dy : integer;
  idx : integer;
begin
  with DrawGrid1 do begin
    // 現在の表示位置
    i := ARow * ColCount + Acol;

    idx := -1;
    if (i >= 0) and (i < Length(DwgDocAry)) then
      idx := DwgDocAry[i].Index;

    if (idx >= 0) and (idx < Length(BmpAry)) then begin
      w := BmpAry[idx].Width;
      h := BmpAry[idx].Height;

      dx := (DefaultColWidth - w) div 2;

      if not CmtDispFlag then
        dy := (DefaultRowHeight - h - TitleH1) div 2
      else
        dy := (DefaultRowHeight - h - TitleH2) div 2;

      with Canvas do begin
        if not CmtDispFlag then
          Draw(Rect.Left + dx, Rect.Top + dy + TitleH1, BmpAry[idx])
        else
          Draw(Rect.Left+dx,Rect.Top+dy + TitleH2, BmpAry[idx]);

        // 背景を塗りつぶし
        if (ARow = Row) and (ACol = Col) then
          Brush.Color := clBlue
        else
          Brush.Color := clGray;

        if not CmtDispFlag then
          Rectangle(Rect.Left + 1, Rect.Top + 1, Rect.Right - 1, Rect.Top + TitleH1 + 1)
        else
          Rectangle(Rect.Left + 1, Rect.Top + 1, Rect.Right - 1, Rect.Top + TitleH2 + 1);

        Font.Color := clWHITE;

        // タイトルを描画
        TextOut(Rect.Left + 3, Rect.Top + 3, ExtractFileName(DwgDocAry[i].Name));

        // コメントを描画
        if CmtDispFlag then
          TextOut(Rect.Left + 3, Rect.Top + 3 + TitleH1 + 1, DwgDocAry[i].Comment);

        // 標準の設定に戻す
        Pen.Style := psSolid;
        Brush.Style := bsClear;
        Pen.Color := clGray;
        Pen.Width := 1;
        Rectangle(Rect);
      end;
    end
    else begin
      with Canvas do begin
        Brush.Color := clWhite;
        FillRect(Rect);
      end;
    end;
  end;
end;

// ************************************
// マウス座標を保持
// ************************************
procedure TForm2.DrawGrid1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  MoveX := X;
  MoveY := Y;
end;

// ************************************
// フォーム生成
// ************************************
procedure TForm2.FormCreate(Sender: TObject);
var
  ini : TIniFile;
begin
  SetLength(BmpAry, 0);
  SetLength(DwgDocAry, 0);

  BmpH := 168;
  BmpW := 240;
  TitleH1 := 20;
  TitleH2 := 42;

  with DrawGrid1 do begin
    DefaultColWidth := BmpW;
    DefaultRowHeight := BmpH + 20;
    Options := Options + [goThumbTracking];
  end;

  BlkName := 'TITLE';
  AttName := 'ZSHEET';
  BlkName1 := BlkName;
  AttName1 := AttName;
  BlkName2 := BlkName;
  AttName2 := AttName;
  AttFlag1 := True;
  AttFlag2 := False;

  // ダブルクリックで Bricscad をアクティブに
  BcadActiveFlag := False;

  // コメント表示
  CmtDispFlag := False;

  // キャプチャ時、コメントを取得
  CmtGetFlag := True;

  LspFileName := '';
  LspCmdName := '';
  LspCommand := '';

  ini := TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini'));
  try
    with Ini do begin
      Top := ReadInteger('Form', 'Top', Top);
      Left := ReadInteger('Form', 'Left', Left);
      Width:= ReadInteger('Form', 'Width', Width);
      Height := ReadInteger('Form', 'Height', Height);
      if Left >=  Screen.Width then Left := (Screen.Width + Width) div 2;
      if Top >= Screen.Height then Top := (Screen.Height + Height) div 2;

      BlkName1 := ReadString('Dcad', 'BlkName1', BlkName1);
      BlkName2 := ReadString('Dcad', 'BlkName2', BlkName2);
      AttName1 := ReadString('Dcad', 'AttName1', AttName1);
      AttName2 := ReadString('Dcad', 'AttName2', AttName2);
      AttFlag1 := ReadBool('Dcad', 'AttFlag1', AttFlag1);
      AttFlag2 := ReadBool('Dcad', 'AttFlag2', AttFlag2);

      BcadActiveFlag := ReadBool('Dcad', 'ActiveFlag', BcadActiveFlag);

      CmtDispFlag := ReadBool('DcadCapt', 'CmtDispFlag', CmtDispFlag);
      CmtGetFlag := ReadBool('DcadCapt', 'CmtGetFlag', CmtGetFlag);

      LspFileName := ReadString('Dcad', 'LispFileName', LspFileName);
      LspCmdName := ReadString('Dcad', 'LispCmdName', LspCmdName);
      LspCommand := ReadString('Dcad', 'LispCommand', LspCommand);

      // メニュー
      N2.Caption := LspCmdName;
      SpeedButton2.Caption := LspCmdName;
    end;
  finally
    ini.Free;
  end;
end;

// ************************************
// フォーム破棄
// ************************************
procedure TForm2.FormDestroy(Sender: TObject);
var
  i : integer;
  ini : TIniFile;
begin
  if Length(BmpAry) > 0 then begin
    for i := 0 to Length(BmpAry) -1 do
      BmpAry[i].Free;
  end;
  ini := TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini'));
  try
    with Ini do begin
      WriteInteger('Form', 'Top', Top);
      WriteInteger('Form', 'Left', Left);
      WriteInteger('Form', 'Width', Width);
      WriteInteger('Form', 'Height', Height);

      WriteString('Dcad', 'BlkName1', BlkName1);
      WriteString('Dcad', 'BlkName2', BlkName2);
      WriteString('Dcad', 'AttName1', AttName1);
      WriteString('Dcad', 'AttName2', AttName2);
      WriteBool('Dcad', 'AttFlag1', AttFlag1);
      WriteBool('Dcad', 'AttFlag2', AttFlag2);

      WriteBool('Dcad', 'ActiveFlag', BcadActiveFlag);

      WriteBool('DcadCapt', 'CmtDispFlag', CmtDispFlag);
      WriteBool('DcadCapt', 'CmtGetFlag', CmtGetFlag);

      WriteString('Dcad', 'LispFileName', LspFileName);
      WriteString('Dcad', 'LispCmdName', LspCmdName);
      WriteString('Dcad', 'LispCommand', LspCommand);
    end;
  finally
    ini.Free;
  end;
end;

// ***************************
// フォームリサイズ
// ***************************
procedure TForm2.FormResize(Sender: TObject);
var
  n, xcnt, ycnt:Integer;
begin
  n := Length(BmpAry);
  if n > 0 then begin
    with DrawGrid1 do begin
      xcnt := (Width - 23) div DefaultColWidth;
      if xcnt=0 then xcnt:=1;
      ycnt := n div xcnt;
      if n mod xcnt > 0 then Inc(ycnt);
      ColCount := xcnt;
      RowCount := ycnt;
    end;
  end;
end;

procedure TForm2.N2Click(Sender: TObject);
begin
end;

// ************************************
// 設定フォーム
// ************************************
procedure TForm2.N3Click(Sender: TObject);
begin
  Form3 := TForm3.Create(Form2);
  with Form3 do begin
    try
      Edit1.Text := BlkName1;
      Edit2.Text := AttName1;
      Edit3.Text := BlkName2;
      Edit4.Text := AttName2;
      Edit5.Text := BmpW.ToString;
      Edit6.Text := BmpH.ToString;
      Edit7.Text := TitleH1.ToString;
      Edit8.Text := TitleH2.ToString;

      CheckBox1.Checked := AttFlag1;
      CheckBox2.Checked := AttFlag2;
      CheckBox4.Checked := BcadActiveFlag;

      CheckBox3.Checked := CmtGetFlag;
      CheckBox5.Checked := CmtDispFlag;

      Edit9.Text := LspFileName;
      Edit10.Text := LspCommand;
      Edit11.Text := LspCmdName;


      if ShowModal = mrOk then begin
        BlkName1 := Edit1.Text;
        AttName1 := Edit2.Text;
        BlkName2 := Edit3.Text;
        AttName2 := Edit4.Text;

        BmpW := StrToIntDef(Edit5.Text, BmpW);
        BmpH := StrToIntDef(Edit6.Text, BmpH);
        TitleH1 := StrToIntDef(Edit7.Text, TitleH1);
        TitleH2 := StrToIntDef(Edit8.Text, TitleH2);

        AttFlag1 := CheckBox1.Checked;
        if AttFlag1 and (BlkName1 <> '') and (AttName1 <> '') then begin
          BlkName := BlkName1;
          AttName := AttName1;
        end;
        AttFlag2 := CheckBox2.Checked;
        if AttFlag2 and (BlkName2 <> '') and (AttName2 <> '') then begin
          BlkName := BlkName2;
          AttName := AttName2;
        end;

        BcadActiveFlag := CheckBox4.Checked;

        CmtGetFlag := CheckBox3.Checked;
        CmtDispFlag := CheckBox5.Checked;

        LspFileName := Edit9.Text;
        LspCommand := Edit10.Text;
        LspCmdName := Edit11.Text;

        // メニュー
        N2.Caption := LspCmdName;
        SpeedButton2.Caption := LspCmdName;

      end;
    finally
      Free;
    end;
  end;
end;

// ************************************
// DXFファイルからシステム変数(2D座標)を取得する
// ************************************
function ReadDxfVariablePoint2D(dxfSl: TStrings; const sysName: string):string;
var
  idx, i : integer;
  code : integer;
  s : string;
  dxfSys : string;
begin
  Result := '';

  dxfSys := '$' + Uppercase(sysName);

  idx := dxfSL.IndexOf('ENTITIES');
  for i := 0 to idx div 2 do begin
    code := StrToInt(dxfSl[i *2]);
    s := dxfSl[i * 2 + 1];
    if (code = 9) and (s = dxfSys) then begin
      result := dxfSl[(i + 1) * 2 + 1]  + ',' +  dxfSl[(i + 2) * 2 + 1];
      break;
    end;
  end;
end;

// ************************************
// DXFファイルから属性を取得する
// ************************************
function ReadDxfAttRib(dxfSl: TStrings; const InsertName: string; const AttRibName: string): string;
var
  i : integer;
  insFlag, attFlag, hasAtt : boolean;
  s : string;
  AblkName , AattName, AattString : string;
  code, idx : integer;
begin
  Result := '';
  insFlag := False;
  hasAtt  := False;
  attFlag := False;

  // ENTITIESセクションを探す
  idx := dxfsl.IndexOf('ENTITIES');

  for i := idx div 2 + 1 to dxfsl.Count div 2 - 1 do begin
    // DXFコード
    code := StrToInt(dxfsl[i * 2]);
    // その値
    s := dxfsl[i * 2 + 1];

    if insFlag then begin

      // 属性取得終了
      if hasAtt and (code = 0) and (s = 'SEQEND') then begin
        insFlag := False;
        hasAtt  := False;
        attFlag := false;
      end;

      if hasAtt and (AblkName = '') and (code = 2) then
        AblkName := s;

      if attFlag then begin
        // 属性の値を保持
        if code = 1 then
          AattString := s;
        // 属性名
        if code = 2 then begin
          AattName := s;
          // ブロック名、属性名が同じ
          if (InsertName = ABlkName) and (AttRibName = AattName) then begin
            Result := AattString;
            Break;
          end;
        end;
      end;
      if hasAtt and (code = 0) and (s = 'ATTRIB') then
        attFlag := True;
    end;

    if (code = 0) and (s = 'INSERT') then begin
      insFlag := True;
      hasAtt  := False;
      attFlag := False;
      AblkName := '';
    end;
    // 属性有
    if (code = 66) and (Trim(s) = '1') then
      hasAtt := True;
    // ENTITIES セクション終わり
    if (code = 0) and (s = 'ENDSEC') then
      Break;
  end;

end;

// ************************************
// DXFファイルを作成する
// ************************************
function SaveDxfSl(dxfsl : TStringList):boolean;
var
  fname : TFileName;
  cnt : integer;
begin
  Result := False;
  fname := ChangeFileExt(ParamStr(0), '.dxf');

  DeleteFile(fname);
  cnt := 0;
  while True do begin
    Sleep(10);
    if not FileExists(fname) then begin
      Break;
    end;
    Inc(cnt);
    if cnt > 5 then Break;
  end;

  if SetDcadActive then begin
    SendDcadCommand(#3'FILEDIA'#13'0'#13);
    Sleep(10);
    SendDcadCommand('DXFOUT'#13 + fname + #13'Vesion'#13'R18'#13'16'#13);
    SendDcadCommand('''FILEDIA'#13'1'#13);
    cnt := 0;

    while True do begin
      Sleep(10);
      if FileExists(fname) then begin
        Sleep(10);
        Break;
      end;
      Inc(cnt);
      if cnt > 50 then Break;
    end;
    if FileExists(fname) then begin
      dxfsl.LoadFromFile(fname);
      Result := dxfsl.Count > 0;
    end;
  end;
end;

// ************************************
// 再キャプチャ
// ************************************
procedure TForm2.N5Click(Sender: TObject);
var
  i, idx, j : integer;
  ACol, ARow :integer;
  s, s1 : string;
  n : integer;
  scrW, scrH : Integer;
  ARect : TREct;
  ALeft, ATop : integer;
  scale : Double;
  APoint : TPoint;
begin
  with DrawGrid1 do begin

    // マウス座標をCol,Rowに
    MouseToCell(MoveX, MoveY, ACol, ARow);
    Row := ARow;
    Col := ACol;
    i := Row * ColCount + Col;
    if (i >= 0) and (i < Length(DwgDocAry)) then begin
      idx := DwgDocAry[i].Index;

      if GetDCadMainWinHandle = 0 then Exit;

      // MDI のウィンドウを探す
      GetDcadMDIClientHandle;

      SetForegroundWindow(DcadMainWinHandle);

      // メニュー操作
      keybd_event(VK_MENU, 0, 0, 0);
      keybd_event(Ord('W'), 0, 0, 0);
      keybd_event(Ord('W'), 0, KEYEVENTF_KEYUP, 0);
      keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);

      for j := 0 to idx + 4 do begin
        keybd_event(VK_DOWN, 0 ,0 ,0);
        keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0);
      end;

      keybd_event(VK_RETURN, 0, 0, 0);
      keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);

      ComboBox1.ItemIndex := i;

      Sleep(500);

      // ウィンドウの位置と大きさを取得
      GetWindowRect(DcadMDIClientWinHandle, ARect);

      // グラフィックウィンドウの左上座標
      ALeft := ARect.Left;
      ATop  := ARect.Top;

      // cad からグラフィック画面のサイズを取得
      s := GetDcadVariable('SCREENSIZE');
      n := Pos(',', s);
      scrW := Trunc(StrToFloatDef(Copy(s, 1, n -1), 0));
      scrH := Trunc(StrToFloatDef(Copy(s, n + 1  ), 0));

      // システム変数取得失敗であれば、終了
      if (scrW = 0) or (scrH = 0) then Exit;

      // 高解像度DPI対策
      ALeft := Trunc(ALeft * scrW / (ARect.Right  - ARect.Left));
      ATop  := Trunc(ATop  * scrH / (ARect.Bottom - ARect.Top));

      // キャプチャするビットマップの大きさに縮小する尺度
      if BmpH / BmpW < scrH / scrW then
        scale := BmpW / scrW
      else
        scale := BmpH / scrH;
      //
      SetCursorPos(ALeft + 1, ATop + 1);

      // 図面範囲でズーム
      s1 := GetDcadVariable('LIMMAX');
      s  := GetDcadVariable('LIMMIN');

      if (s <> '') and (s1 <> '') then begin
        SendDcadCommand('ZOOM'#13 + s + #13 + s1 + #13);
      end
      else begin
        SendDcadCommand('ZOOM'#13'Fit'#13);
      end;
      Sleep(200);

      SendDcadCommand('ZOOM'#13 + Format('%.3f', [scale]) + 'X'#13);

      // 描画待ち
      Sleep(500);

      if Length(BmpAry) > idx then begin
        // キャプチャ
        CaptureToBmp(
          ALeft + (scrW - BmpW) div 2,
          ATop  + (scrH - BmpH) div 2,
          BmpAry[idx].Width, BmpAry[idx].Height, BmpAry[idx]);
      end;

      // 直前の画面表示に戻す
      SendDcadCommand('ZOOM'#13'P'#13);

      // マウスカーソルを戻す
      APoint.X := MoveX;
      APoint.Y := MoveY;
      APoint := ClientToScreen(APoint);
      SetCursorPos(APoint.X, APoint.Y);

      Repaint;
    end;
  end;
end;

// ***********************************
// 画面キャプチャ
// ***********************************
procedure TForm2.SpeedButton1Click(Sender: TObject);
var
  limmax, limmin : OleVariant;
  scrH, scrW : integer;
  ARect : TRect;
  ALeft, ATop, AWidth, AHeight : integer;
  i : integer;
  scale : double;
  h, hdwg : HWND;
  dwgTitle, ext : string;
  cnt : integer;
  s, s1 : string;
  n, j :integer;
  Tics : Cardinal;
  sl : TStringList;
  Title : string;
  count : integer;
begin
  Tics := GetTickCount;
  // メインウィンドウのハンドルを取得
  if GetDCadMainWinHandle = 0 then Exit;

  // 最小化されていれば戻す
  if isIconic(DcadMainWinHandle) then begin
    OpenIcon(DcadMainWinHandle);
    Sleep(100);
  end;

  // MDI のウィンドウを探す
  GetDcadMDIClientHandle;
  h := DcadMDIChildWinHandle;
  cnt := 0;

  // ここでは、ドキュメントの数を数えるだけ
  while h <> 0 do begin
    hdwg := GetWindow(h, GW_CHILD);
    dwgTitle := GetWindowCaption(hdwg);
    // ファイル名を取得
    ext := Uppercase(ExtRactFileExt(dwgTitle));

    // 念のため、拡張子を比較
    if (ext = '.DWG') or (ext = '.DWG*') then
      Inc(cnt);

    // 次のウィンドウを探す
    h := GetWindow(h, GW_HWNDNEXT);
  end;
  if cnt = 0 then Exit;

  // ウィンドウの位置と大きさを取得
  GetWindowRect(DcadMDIClientWinHandle, ARect);

  // グラフィックウィンドウの左上座標
  ALeft := ARect.Left;
  ATop := ARect.Top;

  SetCursorPos(ALeft + 1, ATop + 1);

  AWidth := ARect.Right - ARect.Left;
  AHeight := ARect.Bottom - ARect.Top;

  // Draftsight からグラフィック画面のサイズを取得
  s := GetDcadVariable('SCREENSIZE');
  n := Pos(',', s);
  scrW := Trunc(StrToFloatDef(Copy(s, 1, n -1), 0));
  scrH := Trunc(StrToFloatDef(Copy(s, n + 1), 0));

  // 高解像度DPI対策
  ALeft := Trunc(ALeft * scrW / AWidth);
  ATop := Trunc(ATop * scrH / AHeight);

  // キャプチャするビットマップの大きさに縮小
  if BmpH / BmpW < scrH / scrW then
    scale := BmpW / scrW
  else
    scale := BmpH / scrH;

  // 自フォームを最小に
  //WindowState := wsMinimized;
  //Sleep(100);

  // すでにBitmap が作成されているときは、破棄
  if Length(BmpAry) > 0 then begin
    for i := 0 to Length(BmpAry) -1 do
      BmpAry[i].Free;
  end;


  SetLength(BmpAry, cnt);
  SetLength(DwgDocAry, cnt);

  with DrawGrid1 do begin
    // 適当な大きさを確保
    RowCount := cnt div ColCount;
    if cnt mod ColCount > 0 then RowCount := RowCount + 1;
  end;

  SetForegroundWindow(DcadMainWinHandle);
  sl := TStringList.Create;
  try
    Title := '';
    // メニューを操作
    for i := 0 to cnt - 1 do begin

      keybd_event(VK_MENU, 0, 0, 0);
      keybd_event(Ord('W'), 0, 0, 0);
      keybd_event(Ord('W'), 0, KEYEVENTF_KEYUP, 0);
      keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);

      for j := 0 to i + 4 do begin
        keybd_event(VK_DOWN, 0 ,0 ,0);
        keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0);
      end;

      keybd_event(VK_RETURN, 0, 0, 0);
      keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);

      count := 0;
      while True do begin
        Sleep(10);
        if Title <> GetWindowCaption(DcadMainWinHandle) then begin
          Break;
        end;
        Inc(count);
        if Count > 60 then Break;
      end;

      // 図面切り替わり待ち
      Sleep((count+2) * 50);

      // 編集の図面名を取得
      s := GetWindowCaption(DcadMainWinHandle);
      Title := s;
      // 配列に格納
      with DwgDocAry[i] do begin
        Index := i;
        Name := StrAfterStrBefore('[', ']', s);
      end;

      if SaveDxfSL(sl) then begin
        // シート番号、またはページ番号を取得
        s := ReadDxfAttRib(sl, BlkName, AttName);
        DwgDocAry[i].Comment := s;

        s  := ReadDxfVariablePoint2D(sl, 'LIMMAX');
        s1 := ReadDxfVariablePoint2D(sl, 'LIMMIN');

        SendDcadCommand('ZOOM'#13 + s + #13 + s1 + #13);
      end
      else begin
        SendDcadCommand('ZOOM'#13'F'#13);
        DwgDocAry[i].Comment := '';
      end;

      SendDcadCommand('ZOOM'#13 + Format('%.3f', [scale]) + 'X'#13);
      Sleep(200);

      // キャプチャ用ビットマップを作成
      BmpAry[i] := TBitmap.Create;
      BmpAry[i].PixelFormat := pf32bit;

      // ビットマップの大きさ
      BmpAry[i].Width := BmpW;
      BmpAry[i].Height := BmpH;

      // キャプチャ
      CaptureToBmp(
        ALeft + (scrW - BmpW) div 2,
        ATop  + (scrH - BmpH) div 2,
        BmpAry[i].Width, BmpAry[i].Height, BmpAry[i]);

      SendDcadCommand('ZOOM'#13'P'#13);
      Sleep(200);


    end;
  finally
    sl.Free;
  end;


  // シート番号、ページ番号順にソート
  SortDwgDocAry;

  with ComboBox1 do begin
    Items.Clear;
    Sorted := False;
    for i := 0 to Length(DwgDocAry) - 1 do
      Items.AddObject(DwgDocAry[i].Comment +':'+ DwgDocAry[i].Name , TObject(i));
  end;

  FormResize(self);
  ComboBox1Change(self);

  // 自フォームを戻す
  //WindowState := wsNormal;

  // 自フォームをアクティブに
  SetForegroundWindow(Handle);
  ShowMessage('終了しました。'+#13#10 +
    Format('%.1f',[(GetTickCount - Tics) / 1000]) + '秒');

end;

procedure TForm2.SpeedButton2Click(Sender: TObject);
begin
  // コマンド実行
  N2Click(self);
end;

procedure TForm2.SpeedButton3Click(Sender: TObject);
begin
  // 設定
  N3Click(self);
end;

procedure TForm2.SpeedButton4Click(Sender: TObject);
var
  s:string;
begin
  s := GetDcadVariable(Edit1.Text);
  ShowMessage(s);

end;

end.