5

IMessageFilterExcelアドインで有効にしたいのですが、Excelに書き込む必要があります。私はここから例を取りました:

メッセージフィルターはスレッドごとであるため、このスレッドをメッセージフィルターとして登録します(アドインが作成されるメインスレッドではありません-Excelのメインスレッドであるため)

私の問題は、タイマーが経過するとシステムがExcelに書き込み、その結果、Excelがの一部にIMessageFilterアクセスできないために壊れたThreadPoolスレッドから書き込みメソッドが呼び出されることです。これは、Excelがによって生成された実行スレッドではなく呼び出し元スレッドに存在するためです。タイマー。RetryRejectedCallIMessageFilter

だから、私の質問は:タイマーを初期化したのと同じスレッドでタイマーのElapsedイベントを強制的に実行する方法はありますか?

編集:

IMessageFilter私の質問は、拒否/ビジーをスローしたときにキャッチエクセルエラーを作成するにはどうすればよいですか?

どうも

4

2 に答える 2

8

プロパティを使用Timer.SynchronizingObjectして、間隔が経過したときに発行されるイベント ハンドラー呼び出しをマーシャリングできます。

これはMSDNからのものです:

Elapsed イベントがボタンなどの視覚的な Windows フォーム コンポーネントによって処理される場合、システム スレッド プールを介してコンポーネントにアクセスすると、例外が発生するか、機能しない可能性があります。SynchronizingObject を Windows フォーム コンポーネントに設定することで、この影響を回避します。これにより、Elapsed イベントを処理するメソッドが、コンポーネントが作成されたのと同じスレッドで呼び出されます。

WinFrom を使用していて、メイン フォーム内からタイマー インスタンスを作成しているとします。

System.Timers.Timer t = new System.Timers.Timer();
t.SynchronizingObject = this;
t.Elapsed += t_Elapsed;
t.Start();
于 2012-12-06T07:48:48.920 に答える
3

完全な答えは次のとおりです。

問題: データを Excel に書き込むクラスが、Excel からの「busy/reject」応答メッセージを処理できません。

解決策:ここでIMessageFilter説明されているようにインターフェースを実装します

IMessageFilter定義(リンクから):

namespace ExcelAddinMessageFilter
{
        [StructLayout(LayoutKind.Sequential, Pack = 4)]
        public struct INTERFACEINFO
        {
            [MarshalAs(UnmanagedType.IUnknown)]
            public object punk;
            public Guid iid;
            public ushort wMethod;
        }

        [ComImport, ComConversionLoss, InterfaceType((short)1),
        Guid("00000016-0000-0000-C000-000000000046")]
        public interface IMessageFilter
        {
            [PreserveSig, MethodImpl(MethodImplOptions.InternalCall,
                MethodCodeType = MethodCodeType.Runtime)]
            int HandleInComingCall([In] uint dwCallType, [In] IntPtr htaskCaller,
                [In] uint dwTickCount,
                [In, MarshalAs(UnmanagedType.LPArray)] INTERFACEINFO[]
                lpInterfaceInfo);

            [PreserveSig, MethodImpl(MethodImplOptions.InternalCall,
                MethodCodeType = MethodCodeType.Runtime)]
            int RetryRejectedCall([In] IntPtr htaskCallee, [In] uint dwTickCount,
                [In] uint dwRejectType);

            [PreserveSig, MethodImpl(MethodImplOptions.InternalCall,
                MethodCodeType = MethodCodeType.Runtime)]
            int MessagePending([In] IntPtr htaskCallee, [In] uint dwTickCount,
                [In] uint dwPendingType);
        }
    }

IMessageFilter私のクラスの実装部分(リンクを参照):

#region IMessageFilter Members

        int ExcelAddinMessageFilter.IMessageFilter.
            HandleInComingCall(uint dwCallType, IntPtr htaskCaller, uint dwTickCount, ExcelAddinMessageFilter.INTERFACEINFO[] lpInterfaceInfo)
        {
            // We're the client, so we won't get HandleInComingCall calls.
            return 1;
        }

        int ExcelAddinMessageFilter.IMessageFilter.
        RetryRejectedCall(IntPtr htaskCallee, uint dwTickCount, uint dwRejectType)
        {
            // The client will get RetryRejectedCall calls when the main Excel
            // thread is blocked. We can handle this by attempting to retry
            // the operation. This will continue to fail so long as Excel is 
            // blocked.
            // As an alternative to simply retrying, we could put up
            // a dialog telling the user to close the other dialog (and the
            // new one) in order to continue - or to tell us if they want to
            // abandon this call
            // Expected return values:
            // -1: The call should be canceled. COM then returns RPC_E_CALL_REJECTED from the original method call.
            // Value >= 0 and <100: The call is to be retried immediately.
            // Value >= 100: COM will wait for this many milliseconds and then retry the call.
            return 1;
        }

        int ExcelAddinMessageFilter.IMessageFilter.
            MessagePending(IntPtr htaskCallee, uint dwTickCount, uint dwPendingType)
        {
            return 1;
        }

        #endregion

IMessageFilterインターフェイスを定義して実装したら、次のように STA と をセットアップしますThreadTimers.Timer

スレッド:

thread = new Thread(WriteToExcel);
thread.SetApartmentState(ApartmentState.STA);

タイマー:

timer = new System.Timers.Timer();
timer.Interval = 2000;
timer.Elapsed += new ElapsedEventHandler(StartSTAThread_Handler);

は次のようにStartSTAThread_Handler定義されます。

void StartSTAThread_Handler(object source, ElapsedEventArgs e)
{
     thread.Start();
     thread.Join();
     thread = null;
}

このスレッドは、Excel への書き込みに使用するメソッドを呼び出し、IMessageFilter上記のインターフェイスで拒否されたメッセージを処理します。私がしなければならなかった最後のことは、Excel OM 参照を完全に修飾することでした。それ以外の:

Excel.Range rng = app.ActiveSheet.Range["range_name"];
rng.Copy(); // ERRROR: message filter's RetryRejectedCall is NOT called

完全修飾参照を使用する必要がありました。

app.ActiveSheet.Range["range_name"].Copy // OK: calls RetryRejectedCall when excel dialog etc is showing

これは私のニーズには合っているようですが、ここの別のポスターで説明されている「2ドットルール」と矛盾しているようです...

于 2012-12-06T08:45:04.023 に答える