2

私が達成しようとしているものの簡略化されたバージョン:

  • バックグラウンドで非表示 (Visible = false) で実行される WinForms アプリがあります。
  • フォームは 1 つしかなく、デフォルト名の Form1 のままにしました。
  • この WinForms アプリは、WCF サービスをホストします。ここでは、これを Listener サービスと呼びます。
  • このリスナー サービスには、サービス関数として公開されている「DisplayAlert()」という関数があります。
  • 別のマシンにあるアプリは、標準の WCF サービス呼び出しを介してリスナー サービスにメッセージを送信します。

上記のすべてがうまく機能しています。コードをステップ実行して、DisplayAlert() 関数が呼び出されるときのメッセージの流れを確認できます。

私が理解できないこと、そしてこの単純なことを行う方法を見つけるのがとても難しいとは信じられません:

- ホストされたサービスの DisplayAlert() 関数が、それをホストしている WinForm と直接対話して、フォームを表示できるようにしたいと考えています。

やりたいことは、Visibility を true に設定し、WinForm で別の関数を呼び出すことだけです。

これは、フォームへの参照を追加するか、フォームにパブリック関数を作成してサービスクラスから呼び出すのと同じくらい簡単であるように思えますが、サービス内から Form1 を参照する方法さえわかりませんクラス。

明らかな何かが欠けていますか?サービスをホストしている Form1 のインスタンスを参照するにはどうすればよいでしょうか?

……という道をたどりました。

  • ListenerService (AlertReceived、仮想 void OnAlertReceived) でイベントを作成し、フォームにイベント ハンドラーを追加できると考えました。
    • サイコロはありません。ListenerService クラスを直接インスタンス化するのではなく、ServiceHost で実行しています。
  • Application.Form1 として参照できると考えて、クラス内から Application オブジェクトを参照しようとしていますが、違います。サービス クラス内から Application オブジェクトを確認することさえできません。
    • ここで明らかな何かが欠けている可能性がありますが、よくわかりません。

他の提案はありますか?

役立つ場合は、コードを追加できます。

4

3 に答える 3

11

この方法を使用すると、完全にスレッドセーフなアプリケーションが作成され、制限はありません。

サービス契約の定義

[ServiceContract]
public interface IService
{
    [OperationContract]
    void DisplayAlert();
}

サービスの実装

public class Service:IService
{
    public void DisplayAlert()
    {
        var form = Form1.CurrentInstance;
        form.MySynchronizationContext.Send(_ => form.Show(), null);
    }
}

Program.cs

[STAThread]
static void Main()
{        
    var host = new ServiceHost(typeof (Service));
    host.Open();

    Application.SetCompatibleTextRenderingDefault(false);
    Application.EnableVisualStyles();
    Application.Run(new Form1());
 }

フォームの実装

public partial class Form1 : Form
{
    public static Form1 CurrentInstance;
    public SynchronizationContext MySynchronizationContext;
    private bool showForm = false;

    public Form1()
    {
        InitializeComponent();
        MySynchronizationContext = SynchronizationContext.Current;
        CurrentInstance = this;
    }

    // use this method for hiding and showing if you want this form invisible on start-up
    protected override void SetVisibleCore(bool value)
    {
        base.SetVisibleCore(showForm ? value : showForm);
    }

    public void Show()
    {
        showForm = true;
        Visible = true;   
    }

    public void Hide()
    {
        showForm = true;
        Visible = true;
    }
}

クライアント

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Press Enter to show form");
        Console.ReadLine();

        var client = new ServiceClient();
        client.DisplayAlert();
    }
}
于 2012-06-29T19:40:32.083 に答える
1

私の意見では、友人が言うように、答えは「simpol」です。まず第一に、Web サービスはそれと通信するために必要なすべての手段を提供するため、あなたが説明した道をたどる気にさえなりません。Form1 (サービスをホストする) とホストされたサービスの間にクライアント (クライアント コードが同じ Form1 によってホストされている) を追加し、クライアントが二重チャネルを使用してサービスと通信できるようにします。このようにして、クライアントは、長時間実行されるリクエストを開始し、コールバックを通じて通知を受けることによって、メッセージがサービスに送信されたかどうかを認識します。デュプレックス チャネルに関連するすばらしい記事へのリンクは次のとおりです

PS: これは、開始するための大まかな提案であり、確実に改善できます。

于 2012-06-29T19:00:43.300 に答える
0

サービスにシングルトンを使用できますか? もしそうなら、次のように実装できます:

[ServiceContract]
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class MyClass : IMyClass
{
    Form1 _f;
    public MyClass(Form1 f)
    {
        _f = f;
    }

    [OperationContract]
    public void Alert(string mess)
    {
        _f.Text = mess;
    }
}

次に、サービス ホストをセットアップするときに、それをインスタンス化し、次の形式で渡します。

MyClass c = new MyClass(this);
string baseAddress = "http://localhost:12345/Serv";
var host = new ServiceHost(c, new Uri(baseAddress));
于 2012-06-29T19:04:45.057 に答える