BcadCapture.exe for Bricscad V13

■概要
編集中のすべての図面を切り替え、画面をキャプチャーします。※
キャプチャーした画像は、シート番号(またはページ番号)順にソートされ、画像をダブルクリックすると、編集中の図面が切り替わります。
また、キャプチャーとは関係なく、連続実行する LISP コマンドを1個登録できます。
※シート番号(またはページ番号)を取得すると、結構時間がかかります。

プロパティーバーの下に置いた時



プロパティーバーを隠すくらいの大きさにした時


すべてを表示させた時


■設定画面


■ご注意
・Bricscadのバージョン、OS環境(32bit/64bit)が違うと動かないと思います。
・キャプチャ時点の情報しか保持していませんので、Bricscadの図面構成が変わると、正しく動きません。
・キャプチャ位置が合わないときは、「高DPI設定では、画面のスケーリングを無効にする」をチェックしてみて下さい。



■ダウンロード
  BcadCapture.zip


■ソースコード

unit BcadCaptureUnit;
 
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 ;

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;
    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);
  private
    { Private 宣言 }
  public
    { Public 宣言 }

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

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

var
  Form2: TForm2;

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

implementation


{$R *.dfm}

uses BcadCaptureCfgUnit;

//****************************************
// 画面の指定位置を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;
//****************************************
// 属性の値を取得
//****************************************
function GetAttString(doc : AcadDocument; const BlkName : string; const TagName : string): string;
var
  mspc   : AcadModelSpace;
  ent    : AcadEntity;
  blkref : AcadBlockReference;
  attr   : OleVariant;
  att    : AcadAttributeReference;
  idisp  : IDispatch;
  j , n, m, k : integer;
begin
  Result := '';
  mspc := doc.ModelSpace;

  if mspc.Count > 0 then begin
    for j := 0 to mspc.Count - 1 do begin
      ent := mspc.Item(j);
      if 'AcDbBlockReference' = ent.EntityName then begin
        blkref := ent as AcadBlockReference;
        if blkref.HasAttributes then begin
          if blkname = blkref.Name then begin
            attr := blkref.GetAttributes;
            n := VarArrayLowBound(attr, 1);
            m := VarArrayHighBound(attr, 1);
            for k := n to m do begin
              // 個々の属性を取得
              idisp :=   attr[k];
              att := idisp as AcadAttributeReference;
              if TagName = att.TagString then begin
                Result := att.TextString;
                Break;
              end;
            end;
            break;
          end;
        end;
      end;
    end;
  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;

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 : integer;
  app : IAcadApplication;
  docs : IAcadDocuments;
begin
  with DrawGrid1 do begin
    i := Row * ColCount + col;
    if (i >= 0) and (i < Length(DwgDocAry)) then begin
      try
        app := GetActiveOleObject('BricscadApp.AcadApplication') as IAcadApplication;
        docs := app.Documents;
        id := DwgDocAry[i].Index;
        ComboBox1.ItemIndex := i;
        if id < docs.Count then begin
          docs.Item(id).Activate;
          if BcadActiveFlag then
            // Bricscad をアクティブに
            SetForegroundWindow(app.HWND);
        end;
      except
        ShowMessage('有効な BricsCAD が見つかりません.');
      end;
    end;
  end;
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.FormCreate(Sender: TObject);
var
  ini : TIniFile;
begin
  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 := True;
  // キャプチャ時、コメントを取得
  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('Bcad', 'BlkName1', BlkName1);
      BlkName2 := ReadString('Bcad', 'BlkName2', BlkName2);
      AttName1 := ReadString('Bcad', 'AttName1', AttName1);
      AttName2 := ReadString('Bcad', 'AttName2', AttName2);
      AttFlag1 := ReadBool('Bcad', 'AttFlag1', AttFlag1);
      AttFlag2 := ReadBool('Bcad', 'AttFlag2', AttFlag2);

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

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

      LspFileName := ReadString('Bcad', 'LispFileName', LspFileName);
      LspCmdName := ReadString('Bcad', 'LispCmdName', LspCmdName);
      LspCommand := ReadString('Bcad', '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('Bcad', 'BlkName1', BlkName1);
      WriteString('Bcad', 'BlkName2', BlkName2);
      WriteString('Bcad', 'AttName1', AttName1);
      WriteString('Bcad', 'AttName2', AttName2);
      WriteBool('Bcad', 'AttFlag1', AttFlag1);
      WriteBool('Bcad', 'AttFlag2', AttFlag2);

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

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

      WriteString('Bcad', 'LispFileName', LspFileName);
      WriteString('Bcad', 'LispCmdName', LspCmdName);
      WriteString('Bcad', '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);
var
  app : IAcadApplication;
  docs : IAcadDocuments;
  adoc, doc : IAcadDocument;
  i : integer;
begin
  if (LspCommand <> '') then begin
    try
      app := GetActiveOleObject('BricscadApp.AcadApplication') as IAcadApplication;
      adoc := app.ActiveDocument;
      docs := app.Documents;
      for i := 0 to docs.Count - 1 do begin
        doc := docs.Item(i);
        doc.Activate;
        if LspFileName <> '' then
          doc.SendCommand(#27 + '(load "' + LspFileName + '")' + #13#10);
        doc.SendCommand(LspCommand + #13#10);
      end;
      adoc.Activate;
    except
      ShowMessage('有効な BricsCAD が見つかりません.');
    end;
  end;
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;

// ***********************************
// 画面キャプチャ
// ***********************************
procedure TForm2.SpeedButton1Click(Sender: TObject);
var
  app : IAcadApplication;
  docs : IAcadDocuments;
  doc, docOrg : IAcadDocument;
  limmax, limmin : OleVariant;
  scrsize : OleVariant;
  scrH, scrW : integer;
  hnd : THandle;
  ARect : TRect;
  ALeft, ATop : integer;
  gridMode : Integer;
  ucsIcon : integer;
  dwgName : TFileName;
  i : integer;
  idxOrg : integer;
  scale : double;

begin
  // Bricscad を見つける
  try
    if not Supports(GetActiveOleObject('BricscadApp.AcadApplication'), AcadApplication, app) then begin
      ShowMessage('サポートされていません.');
      exit;
    end;
  except
    ShowMessage('有効な BricsCAD が見つかりません.');
    Exit;
  end;
  docs := app.Documents;

  // 自フォームを最小に
  WindowState := wsMinimized;
  Sleep(50);
  try
    if Length(BmpAry) > 0 then begin
      for i := 0 to Length(BmpAry) -1 do
        BmpAry[i].Free;
    end;

    SetLength(BmpAry, docs.Count);
    SetLength(DwgDocAry, docs.Count);

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

    // 最小化されていれば、最大にする
    if app.WindowState = acMin then begin
      app.WindowState := acMax;
    end;


    // アクティブドキュメントを取得
    docOrg := app.ActiveDocument;
    idxOrg := 0;

    for i := 0 to docs.Count - 1 do begin
      doc := docs.Item(i);
      // 現在のドキュメントの位置
      if doc = docOrg then idxOrg := i;

      // 配列に格納
      with DwgDocAry[i] do begin
        Index := i;
        Name := doc.Name;
        // シート番号またはページ番号
        if CmtGetFlag then
          Comment := GetAttString(doc, BlkName, AttName)
        else
          Comment := '';
      end;
      doc.Activate;

      // ファイル名を表示
      dwgName := doc.Name;

      // Bricscad をアクティブに
      SetForegroundWindow(doc.HWND);

      // 図面範囲を取得
      limmax := doc.GetVariable('LIMMAX');
      limmin := doc.GetVariable('LIMMIN');

      // 図面範囲をズーム
      app.ZoomWindow(limmin, limmax);

      // グラフィック画面のサイズを取得
      scrsize := doc.GetVariable('SCREENSIZE');
      scrW := scrsize[0];
      scrH := scrsize[1];

      // グリッドの表示モードを取得
      gridMode := doc.GetVariable('GRIDMODE');

      // UCSアイコンの表示モードを取得
      ucsICon := doc.GetVariable('UCSICON');

      // グリッドを非表示に
      if gridMode > 0 then
        doc.SetVariable('GRIDMODE', 0);

      // UCSアイコンを非表示に
      if ucsIcon > 0 then
        doc.SetVariable('UCSICON', 0);

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


      app.ZoomScaled(scale, 0);

      // グラフィックウィンドウのハンドルを取得
      hnd := FindWindowEx(app.ActiveDocument.HWND, 0, 'AfxFrameOrView100u', nil);
      // ウィンドウの位置と大きさを取得
      GetWindowRect(hnd, ARect);
      // グラフィックウィンドウの左上座標
      ALeft := ARect.Left;
      ATop := ARect.Top;

      // キャプチャ用ビットマップを作成
      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]);

      // ズームを元に戻す
      app.ZoomWindow(limmin, limmax);
      // グリッドの表示を戻す
      if gridMode > 0 then
        doc.SetVariable('GRIDMODE', gridMode);
      // UCSアイコンの表示を戻す
      if ucsIcon > 0 then
        doc.SetVariable('UCSICON', ucsIcon);
    end;
    // 元の図面をアクティブに
    docOrg.Activate;

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

    with ComboBox1 do begin
      Items.Clear;
      Sorted := False;
      for i := 0 to Length(DwgDocAry) - 1 do begin
        Items.AddObject(DwgDocAry[i].Comment +':'+ DwgDocAry[i].Name , TObject(i));
        if DwgDocAry[i].Index = IdxOrg then
          ItemIndex := i;
      end;
    end;
    FormResize(self);
    ComboBox1Change(self);
  except
    ;
  end;
  // 自フォームを戻す
  WindowState := wsNormal;
  // 自フォームをアクティブに
  SetForegroundWindow(Handle);

end;

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

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

end.