8

上司の要求に応じて、特定のデバイスとプロセスのステータスを定期的に監視するために使用されるスクリプトの小さなセットを作成しました。この情報は、その後、すべての情報を収集し、数式を適用し、範囲を設定し、グラフを生成するなど、比較的複雑なVBAモジュールを使用して処理されます。

ただし、2つの問題があります。私はアマチュアプログラマーなので、VBAルーチンは非常に非効率的です。それは私にとっては大きな問題ではありませんが(実行中に起きてコーヒーを飲むだけです)、他のユーザーにとっては、なぜそんなに時間がかかるのかわからないので、これは面倒なことになる可能性があります。進捗状況をグラフで表現したい。アプリケーションの構成は、テキストファイルを介して行われます。構成をセットアップするための適切なGUIを提供したいと思います。

これを実現するために、C#WinFormsアプリケーションをVBAアプリケーションと通信させる方法を探しています。C#アプリからVBAルーチンを実行する方法は知っていますが、リアルタイムで通信させる方法がわかりません。

これが私が特に達成したい2つのことです:

  • VBAルーチンの最後に保存されたログファイルがすでにあります。ただし、その代わりに、ログ/デバッグメッセージをC#アプリケーションにリアルタイムで(アプリケーションの最後だけでなく)送信して、メッセージがVBAアプリによって生成されたときにGUIアプリに表示できるようにします。 。
  • また、VBAアプリがリアルタイムの進行状況に関する情報をGUIアプリに送信して、GUIアプリでグラフィカルな進行状況バーを作成できるようにします

標準出力を介した通信についてはすでに考えました。C#/。Netを使用して標準出力から読み取る方法は知っていますが、VBAを使用してStdOutストリームに書き込む方法がわかりません。

私が達成しようとしていることは愚かで古風な(または完全に不要な)ことだと多くの人が指摘するでしょうが、アマチュアプログラマーとしては、私にとって本当にやりがいのある楽しいプロジェクトのように思えました。または2つ。

4

4 に答える 4

10

C# COM 可視クラスの作成は非常に簡単で、(コメントで述べたように) 楽しいものです。ここに小さなサンプルがあります。

新しい C# ライブラリ プロジェクトで、以下を追加します。

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace CSharpCom
{
    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    //The 3 GUIDs in this file need to be unique for your COM object.
    //Generate new ones using Tools->Create GUID in VS2010
    [Guid("18C66A75-5CA4-4555-991D-7115DB857F7A")] 
    public interface ICSharpCom
    {
        string Format(string FormatString, [Optional]object arg0, [Optional]object arg1, [Optional]object arg2, [Optional]object arg3);
        void ShowMessageBox(string SomeText);
    }

    //TODO: Change me!
    [Guid("5D338F6F-A028-41CA-9054-18752D14B1BB")] //Change this 
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    interface ICSharpComEvents
    {
        //Add event definitions here. Add [DispId(1..n)] attributes
        //before each event declaration.
    }

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(ICSharpComEvents))]
    //TODO: Change me!
    [Guid("C17C5EAD-AA14-464E-AD32-E9521AC17134")]
    public sealed class CSharpCom : ICSharpCom
    {
        public string Format(string FormatString, [Optional]object arg0, [Optional]object arg1, [Optional]object arg2, [Optional]object arg3)
        {
            return string.Format(FormatString, arg0, arg1, arg2, arg3);   
        }

        public void ShowMessageBox(string SomeText)
        {
            MessageBox.Show(SomeText);
        }
    }
}

プロジェクトのプロパティの [署名] タブに移動し、ボックスをオンにしてアセンブリに署名し、新しい「厳密な名前のキー ファイル」を作成します。これにより、登録済み DLL のバージョン管理の問題を防ぐことができます。

regasmそれをコンパイルし、Visual Studio コマンド プロンプトで使用して DLL を登録します。使用している Office のバージョンに応じて、32 ビットまたは 64 ビットの regasm を使用します...C:\windows\Microsoft.NET\FrameworkまたはC:\windows\Microsoft.NET\Framework64(それぞれ 32 ビットと 64 ビット) にRegAsm があります。

C:\windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /codebase /tlb CSharpCom.dll

これにより、DLL が Windows に登録されるため、VBA エディターで [ツール] > [参照] に移動し、COM の名前空間 "CSharpCom" を見つけることができるはずです。そのボックスをオンにすると、VBA で COM オブジェクトを作成できるようになります。

Sub TestCom()
    Dim c As CSharpCom.CSharpCom
    Set c = New CSharpCom.CSharpCom
    c.ShowMessageBox c.Format("{0} {1}!", "Hello", "World")
End Sub

フォームを COM オブジェクトに追加し、VBA が特定のメソッドを呼び出したときにフォームを開くことができます。これを使用して、進行状況バーのあるフォームを作成できるはずです。ただし、VBA はシングル スレッドであることを考慮する必要があります。これは、コードの実行中に他のすべてがフリーズすることを意味するため、少し複雑になる可能性があります。

私の頭の上から、COM 可視クラスに 3 つのメソッドを作成できます。1 つはフォームを開き、もう 1 つは進行状況を更新します (VBA が COM オブジェクトの update() メソッドを呼び出した直後に VBA の DoEvents() メソッドを呼び出します)。 、Office が画面の更新を処理できるようにするため)、および 1 つを閉じます。フォームで Dispose() を呼び出す必要があります。これは「close」メソッドで実行できますが、VBA がクラッシュし、close メソッドが呼び出されない場合、メモリ リークや問題が発生する可能性があると思います。

于 2012-11-19T02:23:45.460 に答える
5

VB6 プロジェクトで .NET DLL を使用して、アセンブリで定義した任意のオブジェクトのインスタンスを作成できます。これは、タイプ ライブラリ ファイル (.tlb) を作成して COM 相互運用 (VB6 で使用) 用に .NET アセンブリ (.dll) を登録することによって行われます。.tlb ファイルには、VB6 プロジェクトで .dll を使用できるようにするための追加情報が含まれています。VB6 プロジェクトは .dll ではなく、対応する .tlb ファイルを参照することに注意してください。Regasm.exe ユーティリティを使用して、タイプ ライブラリを生成および登録し、マネージ アセンブリの場所を登録できます。次に、それが WinForm であろうと他のクールなクラスであろうと、.NET オブジェクトのインスタンスを作成するだけです。=)。

Dim MyDotNetObject As MyDotNetClass

Set MyDotNetObject = New MyDotNetClass
MyDotNetObject.SomeMethod(value1, value2)

''end of execution
Set MyDotNetObject = Nothing

参照: http://support.microsoft.com/default.aspx?scid=kb;en-us;817248

于 2012-11-18T00:30:13.797 に答える
4

このリンクのコードは、大まかにあなたが望むことをすると思います:

  • C#コードはCOMオブジェクトを作成します(例ではIEですが、説明から、VBAルーチンのインスタンスを作成できました)

  • .Net イベント ハンドラー (+= を使用) を COM オブジェクト (タイトルが変更されたとき) によって発生したイベントにアタッチします。

  • サンプルのイベント処理コードを定義します

http://msdn.microsoft.com/en-us/library/66ahbe6y.aspx

私はこのコードを理解していると公言していません。私の目的は、このソリューションが複雑になることを示すことです! これを 1 つのソリューションで構築する必要があります。完全な VBA ソリューションは、開発がより迅速になります (簡単な VBA フォームを学習するだけで済みます) が、古いテクノロジです。完全な C# ソリューションの開発は遅くなりますが、C# を学ぶことができます。

パフォーマンスは実際には問題です。現在のパフォーマンスが非効率的である場合、処理するレコード数が 5 倍になったらどうなるでしょうか? 多くのレコードを取得する前に、パフォーマンスの問題を解決する必要があります。

于 2012-11-18T01:35:37.070 に答える