読者です 読者をやめる 読者になる 読者になる

C#ATIA

↑タイトル詐欺 主にCATIA V5 の VBA

マクロを高速に実行させる(非公式)

何とか高速に動かないものか?と考えていたら思い出しました。
以前、"Unofficial CATIA User Forum"の常連さんの所で記載し、一人で
ゴチャゴチャ書いた方法です。 もう十分時効なので記載します。


個人的な実感として、同じソースコードのマクロであれば、
ツールバーの登録し、呼び出した形が一番速度が速いです。


○独自のツールバー作成
ツール」-「カスタマイズ」-「ツールバー」タブ-「新規作成」
f:id:kandennti:20150424121807j:plain

○コマンドの登録
ツール」-「カスタマイズ」-「コマンド」タブ-「マクロ」カテゴリ

f:id:kandennti:20150424121808j:plain


これが出来ない場合もあるかと思います。例えば、ExcelVBAを実行し
CATIAに対し、点や線を作成するような場合です。
偶然発見し根拠も良くわからないのですが、

実行時にシステムメニューを表示させた状態にすると、
マクロが高速で実行されます。

"システムメニューを表示させる"と言うのは、CATIAのWindowのタイトル
部分で右クリックしメニューを表示させた状態です。たったこれだけです。
f:id:kandennti:20150424121810j:plain



手動で確認するのも良いですが、折角なのでマクロで再現させます。
VBAでは、こちらのコードを元に
レイヤーの扱いを考える5 - C#ATIA
SubCreateDatumPointメソッドは、そのままで上部の部分のみを
修正します。

'vba
Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" _
    (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
  
Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" _
    (ByVal Hwnd As Long, ByVal msg As Long, _
     ByVal wParam As Long, ByVal lParam As Long) As Long


'Windows APIの構造体の定義
Private Type POINT
  X As Long
  Y As Long
End Type


'マウスポインタ関係のAPI
Private Declare Function GetCursorPos Lib "user32" _
                          (lpPoint As POINT) As Long
Private Declare Function SetCursorPos Lib "user32" _
                          (ByVal X As Long, ByVal Y As Long) As Long
                          
'マウスボタン関係のAPI
Declare Sub mouse_event Lib "user32" (ByVal dwFlags As Long, _
                                      ByVal dx As Long, ByVal dy As Long, _
                                      ByVal cButtons As Long, ByVal dwExtraInfo As Long)


Private Const MOUSEEVENTF_LEFTUP As Integer = &H4      '左ボタンUP
Private Const MOUSEEVENTF_LEFTDOWN As Integer = &H2    '左ボタンDown
Private Const MOUSEEVENTF_RIGHTUP As Integer = &H10    '右ボタンUP
Private Const MOUSEEVENTF_RIGHTDOWN As Integer = &H8   '右ボタンDown


Public Sub MouseRIGHT(ByVal posLeft As Long, ByVal posTop As Long)
    '右クリック
    Call SetCursorPos(posLeft, posTop)
    Call mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0)
    Call mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0)
End Sub


Public Sub MouseLEFT(ByVal posLeft As Long, ByVal posTop As Long)
    '左クリック
    Call SetCursorPos(posLeft, posTop)
    Call mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
    Call mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
End Sub


Sub CATMain()
    Dim T As Long
    Dim Left As Long, Top As Long
    Dim StartPos As POINT
    
    Left = CATIA.Left + 10
    Top = CATIA.Top + 10
    Call GetCursorPos(StartPos)
    
    T = timeGetTime
    
    Call MouseRIGHT(Left, Top)
    Call LayerStart
    Call MouseLEFT(StartPos.X, StartPos.Y)


    CATIA.RefreshDisplay = True
    MsgBox CDbl(timeGetTime - T) / 1000 & "秒"
End Sub

右クリックを再現するには、C#でもAPIを使用するしかなさそうです。
C#では、こちらのコードを元に
レイヤーの扱いを考える6 - C#ATIA
"LayerTest_CSクラス"と"CatiaContainerクラス"と"CatExtensionクラス"は
そのままで、他の部分を以下の様に修正します。

//cs
namespace catLeyTest {
    using System.Linq;
    using System.Diagnostics;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;//COMオフ゛シ゛ェクトを操作するために必要
    using INFITF;//Catiaのライブラリ
    using MECMOD;//Catiaのライブラリ
    using HybridShapeTypeLib;//Catiaのライブラリ


    using System;
    using System.Drawing;
    using System.Threading;


    static class Program {
        static void Main() {
            var catia = (INFITF.Application)Marshal
                        .GetActiveObject("CATIA.Application");


            var test = new LayerTest_CS(catia);
            SystemMenu.State = CatExtension.State;
            Thread.Sleep(100);
            SystemMenu.SystemMenuShow();
            Stopwatch sw = new Stopwatch();
            sw.Start();
            test.StartTest();
            sw.Stop();
            SystemMenu.SystemMenuHide();
            
            MessageBox.Show(string.Format("{0}秒", sw.Elapsed));
        }
    }


    static class SystemMenu {
        [DllImport("user32.dll")]
        static extern void mouse_event(
            uint dwFlags,uint dx, uint dy,
            uint dwData,int dwExtraInfo);


        const uint MOUSEEVENTF_LEFTDOWN = 0x0002;
        const uint MOUSEEVENTF_LEFTUP = 0x0004;
        const uint MOUSEEVENTF_RIGHTDOWN = 0x0008;
        const uint MOUSEEVENTF_RIGHTUP = 0x0010;


        public static CatiaContainer State { get; set; }
        private static System.Drawing.Point StartPos { get; set; }


        public static void SystemMenuShow() {
            StartPos = Cursor.Position;
            Cursor.Position = new System.Drawing.Point(
                                (int)State.Catia.Left + 10,
                                (int)State.Catia.Top + 10);
            MouseRIGHT();
            Cursor.Position = StartPos;
        }


        public static void SystemMenuHide() {
            Cursor.Position = StartPos;
            MouseLEFT();
        }


        //右クリック
        private static void MouseRIGHT() {
            mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0);
            Thread.Sleep(100);
            mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
        }
        //左クリック
        private static void MouseLEFT() {
            mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
            Thread.Sleep(100);
            mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
        }
    }
}

システムメニューを直接表示する方法も試しましたが、効果ありませんでした。
あくまで右クリックで表示です。
又、実行中はシステムメニューが、表示されたままにしなければなりません。
他のソフトを使用してシステムメニューが消えてしまうとダメです。
なので基本、放置です。


「レイヤーの扱いを考える6」で使用したものと同一データでの
程のものでの比較です。

4MB弱 C#ノーマル C#システムメニュー表示
1回目 10.24秒 7.94秒
2回目 10.21秒 6.49秒
3回目 10.23秒 6.37秒
22MB C#ノーマル C#システムメニュー表示
1回目 187.24秒 82.43秒
2回目 185.91秒 82.32秒


どうでしょう、たったこれだけで処理時間が半分以下になる場合も
有ります。(もちろん手動でも)
全てのマクロで効果が出るものと思いますが、恐らく、画面上での描写が
多いものほど効果があるのではないかと思います。


但し、最初のツールバーへの登録ほど高速にはなりませんし、ツールバーへ登録
するマクロではやらない方がよいです。(最後に変な動きをするかもです)

・アウトプロセスなマクロ
・開発中にテストする際のエディタからの実行
ぐらいしか活用できませんが、手動でも機能します。(が、あくまで放置です)