Bricacad VB.NET 画面キャプチャサンプル BCapt.dll

■概要
 Bricscad .NET 本来の機能で、密かにスナップショットを作成しようと、いろいろやってみたのですが、よく分かりませんでした。
 結局、他の外部アプリと同じ手法「スクリーンキャプチャ」を使っています。
 表示させている画像は1個だけすが、マウススクロールで切り替わります。
 (実際には、コンボボックスをアクティブにして、それが切り替わっています。)
 フォームは、モードレス表示です。
 どこかで、Bitmapの破棄が必要なのだと思います。

 シート番号(図枠ブロックの属性)順にソートされて表示されます。
 画像をダブルクリックすると、アクティブな図面が切り替わります。



' system 
Imports System.Text
Imports System.Runtime.InteropServices
Imports System.IO

' ODA
Imports Teigha.Runtime
Imports Teigha.DatabaseServices
Imports Teigha.Geometry
Imports Teigha.GraphicsSystem

' Bricsys
Imports Bricscad.ApplicationServices
Imports Bricscad.Runtime
Imports Bricscad.EditorInput

' alias
Imports _AcRx = Teigha.Runtime
Imports _AcAp = Bricscad.ApplicationServices
Imports _AcDb = Teigha.DatabaseServices
Imports _AcGe = Teigha.Geometry

Imports _AcEd = Bricscad.EditorInput
Imports _AcGi = Teigha.GraphicsInterface
Imports _AcClr = Teigha.Colors
Imports _AcWnd = Bricscad.Windows
Imports Bricscad.PlottingServices

Public Class Commands
    <CommandMethod("BCapt", CommandFlags.Modal)> _
    Public Shared Sub CmdBCapt()
        '' ウィンドウのが存在を確認
        Dim hWnd As New IntPtr(0)
        hWnd = Form1.FindWindow(Nothing, "BCapt")
        '' ウィンドウが存在しない
        If hWnd.Equals(IntPtr.Zero) Then
            Dim frm As Form1 = New Form1
            '' モードレス表示
            Application.ShowModelessDialog(frm)
            '' ここでは破棄しない
            ''frm.Dispose()
        End If
    End Sub
End Class

' system 
Imports System.Text
Imports System.Runtime.InteropServices
Imports System.IO
Imports System.Drawing
Imports System.Windows.Forms

' ODA
Imports Teigha.Runtime
Imports Teigha.DatabaseServices
Imports Teigha.Geometry
Imports Teigha.GraphicsSystem

' Bricsys
Imports Bricscad.ApplicationServices
Imports Bricscad.Runtime
Imports Bricscad.EditorInput

' com
Imports BricscadDb
Imports BricscadApp

' alias
Imports _AcRx = Teigha.Runtime
Imports _AcAp = Bricscad.ApplicationServices
Imports _AcDb = Teigha.DatabaseServices
Imports _AcGe = Teigha.Geometry

Imports _AcEd = Bricscad.EditorInput
Imports _AcGi = Teigha.GraphicsInterface
Imports _AcClr = Teigha.Colors
Imports _AcWnd = Bricscad.Windows
Imports Bricscad.PlottingServices

Public Class Form1
    '' データ保持用構造体
    Structure DwgDoc
        Dim DwgName As String
        Dim DwgPrefix As String
        Dim DocIndex As Integer
        Dim DimScale As Double
        Dim SheetNo As String
        Dim PageNo As String
        Dim Bmp As Drawing.Bitmap
    End Structure
    '' 動的配列
    Public Shared DwgDocs() As DwgDoc

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Left = Screen.PrimaryScreen.WorkingArea.Width - Me.Width
        Me.Top = Screen.PrimaryScreen.WorkingArea.Height - Me.Height
        'If MessageBox.Show("キャプチャを開始しますか?", _
        '                                                 "BCapt", _
        '                                                 MessageBoxButtons.YesNo, _
        '                                                 MessageBoxIcon.Information, _
        '                                                 MessageBoxDefaultButton.Button1) = DialogResult.Yes Then
        '    System.Threading.Thread.Sleep(100)

        '    Button1.PerformClick()
        'End If
    End Sub

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure RECT
        Public left As Integer
        Public top As Integer
        Public right As Integer
        Public bottom As Integer
    End Structure

    <DllImport("user32.dll")> _
    Private Shared Function GetWindowRect(ByVal hwnd As IntPtr, ByRef lpRect As RECT) As Integer
    End Function

    Enum GetWindow_Cmd : uint
        GW_HWNDFIRST = 0
        GW_HWNDLAST = 1
        GW_HWNDNEXT = 2
        GW_HWNDPREV = 3
        GW_OWNER = 4
        GW_CHILD = 5
        GW_ENABLEDPOPUP = 6
    End Enum

    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
    Public Shared Function GetWindow(ByVal hWnd As IntPtr, ByVal uCmd As UInt32) As IntPtr
    End Function

    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
    Public Shared Function FindWindow( _
        ByVal lpClassName As String, _
        ByVal lpWindowName As String) As IntPtr
    End Function

    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
    Public Shared Function FindWindowEx(ByVal parentHandle As IntPtr, _
        ByVal childAfter As IntPtr, _
        ByVal lclassName As String, _
        ByVal windowTitle As String) As IntPtr
    End Function

    <DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
    Public Shared Function GetWindowText(ByVal hwnd As IntPtr, ByVal lpString As StringBuilder, ByVal cch As Integer) As Integer
    End Function

    <DllImport("user32.dll", CharSet:=CharSet.Auto)> _
    Public Shared Function GetClassName(ByVal hWnd As System.IntPtr, _
        ByVal lpClassName As System.Text.StringBuilder, ByVal nMaxCount As Integer) As Integer
    End Function

    Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
        '' ここで破棄
        Me.Dispose()
    End Sub

    '' 図枠ブロックの属性を取得
    Public Sub GetAttString(ByRef doc As AcadDocument, ByVal BlkName As String, _
                                 ByVal TagName1 As String, ByVal TagName2 As String, _
                                 ByRef AttString1 As String, ByRef AttString2 As String)

        Dim Flag1 As Boolean = False
        Dim Flag2 As Boolean = False
        Dim mspc As AcadModelSpace = doc.ModelSpace
        If mspc.Count > 0 Then
            For j As Integer = 0 To mspc.Count - 1
                Dim ent As AcadEntity = mspc.Item(j)
                If "AcDbBlockReference" = ent.EntityName Then
                    Dim blkref As AcadBlockReference = ent
                    If blkref.HasAttributes Then
                        If BlkName = blkref.Name Then
                            Dim attr As Object = blkref.GetAttributes
                            For k As Integer = LBound(attr, 1) To UBound(attr, 1)
                                Dim att As AcadAttributeReference = attr(k)
                                If TagName1 = att.TagString Then
                                    AttString1 = att.TextString
                                    Flag1 = True
                                End If
                                If TagName2 = att.TagString Then
                                    AttString2 = att.TextString
                                    Flag2 = True
                                End If
                                If Flag1 And Flag2 Then Exit For
                            Next
                        End If
                    End If

                End If
                If Flag1 And Flag2 Then Exit For
            Next
        End If
    End Sub

    '' 取得した情報をシート番号順にソート
    Public Shared Sub SortDwgDocsSheetNo(pageSort As Boolean)
        Dim temp As DwgDoc
        Dim s As String
        Dim s1 As String

        Dim n As Integer = 0
        For i As Integer = 0 To UBound(DwgDocs)
            If pageSort Then
                If n < DwgDocs(i).PageNo.Length Then n = DwgDocs(i).PageNo.Length
            End If
        Next
        If UBound(DwgDocs) > 0 Then
            For i As Integer = 0 To UBound(DwgDocs) - 1
                If pageSort Then
                    s = Microsoft.VisualBasic.Right("0000" + DwgDocs(i).PageNo, n)
                Else
                    s = DwgDocs(i).SheetNo
                End If
                For j As Integer = i + 1 To UBound(DwgDocs)
                    If pageSort Then
                        s1 = Microsoft.VisualBasic.Right("0000" + DwgDocs(j).PageNo, n)
                    Else
                        s1 = DwgDocs(j).SheetNo
                    End If
                    If s > s1 Then
                        temp = DwgDocs(i) : DwgDocs(i) = DwgDocs(j) : DwgDocs(j) = temp
                        s = s1
                    End If
                Next
            Next
        End If
    End Sub

    '' キャプチャボタン
    Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
        Const bmpW As Integer = 240
        Const bmpH As Integer = 170

        ComboBox1.Items.Clear()

        Dim app As IAcadApplication = _AcAp.Application.AcadApplication
        Dim appDocs As IAcadDocuments = app.Documents
        Dim appDoc As IAcadDocument

        ReDim DwgDocs(appDocs.Count - 1)

        Dim stIndex As Integer = -1
        For i As Integer = 0 To appDocs.Count - 1
            If appDocs.Item(i).Active Then
                stIndex = i
                Exit For
            End If
        Next
        For i As Integer = 0 To appDocs.Count - 1
            appDoc = appDocs.Item(i)
            appDoc.Activate()

            With DwgDocs(i)
                GetAttString(appDoc, "TITLE", "ZSHEET", "ZITEM9", .SheetNo, .PageNo)
                .DocIndex = i
                .DwgPrefix = appDoc.GetVariable("DWGPREFIX")
                .DwgName = appDoc.GetVariable("DWGNAME")
            End With

            Dim lmax(1) As Double
            lmax = appDoc.GetVariable("LIMMAX")
            Dim lmin(1) As Double
            lmin = appDoc.GetVariable("LIMMIN")
            Dim h As Integer = FindWindowEx(app.ActiveDocument.HWND, IntPtr.Zero, "AfxFrameOrView100u", "")
            If h <> 0 Then
                Dim rec As RECT
                GetWindowRect(h, rec)
                app.ZoomWindow(lmin, lmax)

                Dim recWidth As Integer = rec.right - rec.left
                Dim recHeight As Integer = rec.bottom - rec.top
                Dim scale As Double
                If bmpH / bmpW < recHeight / recWidth Then
                    scale = bmpW / recWidth
                Else
                    scale = bmpH / recHeight
                End If

                Dim gridMode As Integer = appDoc.GetVariable("GRIDMODE")
                Dim ucsICon As Integer = appDoc.GetVariable("UCSICON")
                If gridMode > 0 Then appDoc.SetVariable("GRIDMODE", 0)
                If ucsICon > 0 Then appDoc.SetVariable("UCSICON", 0)

                app.ZoomScaled(scale, AcZoomScaleType.acZoomScaledRelative)

                ''Bitmapの生成
                Dim bmp As New Bitmap(bmpW, bmpH)
                ''Graphicsの作成
                Dim gr As Graphics = Graphics.FromImage(bmp)
                '' グラフィック画面の中心キャプチャする
                gr.CopyFromScreen(
                    New Point(rec.left + Int((recWidth - bmpW) / 2), rec.top + Int((recHeight - bmpH) / 2)),
                    New Point(0, 0), bmp.Size)
                '' 破棄
                gr.Dispose()

                DwgDocs(i).Bmp = New Bitmap(bmp)
                app.ZoomPrevious()
                If gridMode > 0 Then appDoc.SetVariable("GRIDMODE", gridMode)
                If ucsICon > 0 Then appDoc.SetVariable("UCSICON", ucsICon)
            End If
        Next
        appDoc = appDocs.Item(stIndex)
        appDoc.Activate()

        appDoc = Nothing
        appDocs = Nothing
        app = Nothing

        '' ソート
        SortDwgDocsSheetNo(False)

        '' コンボボックスにファイル名を追加
        For i As Integer = 0 To UBound(DwgDocs)
            ComboBox1.Items.Add(DwgDocs(i).SheetNo + ":" + DwgDocs(i).DwgName)
        Next
        '' コンボボックスのインデックスを最初の図面にする
        For i As Integer = 0 To UBound(DwgDocs)
            If DwgDocs(i).DocIndex = stIndex Then
                ComboBox1.SelectedIndex = i
                Exit For
            End If
        Next

        'System.Threading.Thread.Sleep(100)

        'Me.Focus()
        '' フォーカスをコンボボックスに
        ComboBox1.Focus()

    End Sub

    '' ビットマップとファイル名を更新
    Private Sub ComboBox1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBox1.SelectedIndexChanged
        Dim idx As Integer = ComboBox1.SelectedIndex
        If idx >= 0 Then
            PictureBox1.Image = DwgDocs(idx).Bmp
            Label1.Text = DwgDocs(idx).DwgName
        End If
    End Sub

    Private Sub PictureBox1_Click(sender As Object, e As EventArgs) Handles PictureBox1.Click

    End Sub

    '' アクティブなドキュメントを切り替え
    Private Sub PictureBox1_MouseDoubleClick(sender As Object, e As MouseEventArgs) Handles PictureBox1.MouseDoubleClick
        Dim ret As Boolean = False
        Dim idx As Integer = ComboBox1.SelectedIndex
        If idx >= 0 Then
            Dim app As IAcadApplication = _AcAp.Application.AcadApplication
            Dim appDocs As IAcadDocuments = app.Documents
            Dim appDoc As IAcadDocument
            For i As Integer = 0 To appDocs.Count - 1
                appDoc = appDocs.Item(i)
                If (DwgDocs(idx).DwgPrefix = appDoc.GetVariable("DWGPREFIX")) And _
                        (DwgDocs(idx).DwgName = appDoc.GetVariable("DWGNAME")) Then
                    appDoc.Activate()
                    ret = True
                    Exit For
                End If
            Next
            '' 取得時と同じファイル名が無い
            If Not ret Then
                MsgBox("DwgPrefix : " + DwgDocs(idx).DwgPrefix + vbCrLf + _
                       "DwgNme : " + DwgDocs(idx).DwgName + vbCrLf + "が、見つかりません.", MsgBoxStyle.OkOnly, "BCapt")
            End If
        End If
    End Sub
End Class