C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[3]: コンソールとWindowsアプリのコールバックの違い


(過去ログ 11 を表示中)

[トピック内 4 記事 (1 - 4 表示)]  << 0 >>

■2471 / inTopicNo.1)  コンソールとWindowsアプリのコールバックの違い
  
□投稿者/ アーラン (5回)-(2007/04/09(Mon) 17:42:46)

分類:[C# (Windows)] 

● 開発環境・使用言語

Windows XP,Visual Studio 2005 Standard Edition
Visual C# 2005

● 要点

以下の非同期のpingのサンプル・プログラムをWindowsアプリケーションとして実行するように書き換えました。
http://msdn2.microsoft.com/ja-jp/library/system.net.networkinformation.ping(VS.80).aspx

サンプルをそのまま実行すると期待どおりに機能しますが,Windowsアプリケーションとして実行させると途中でハングアップします。

調べると waiter.WaitOne() でイベント待ちに入ったあと,PingCompletedCallbackが発生していません。

何が悪いのでしょうか。よろしくお願いいたします。

● 詳細

Visual Studio 2005 C# で新規にWindowsアプリケーションを作成し,button1を1個だけ配置しました。

サンプルのソース・コードを全部コピー・ペースとしてMainをMain2に書き替えました。
Main以外の2つの関数はそのままコピーペーストしました。

必要なusingを追加しました。
button1のイベントハンドラにMain2を呼び出すコードを追加しました。

修正部分は以下のとおりです。

===============================================================
サンプルのコード(コンソールアプリケーション)
===============================================================
public static void Main(string[] args) {
    if (args.Length == 0)
        throw new ArgumentException("Ping needs a host or IP Address.");

    string who = args[0];
    AutoResetEvent waiter = new AutoResetEvent(false);

    Ping pingSender = new Ping();
===============================================================

===============================================================
Windowsアプリケーションのコード(修正部分だけ)
===============================================================
private void button1_Click(object sender, EventArgs e) {
    string[] p = new string[1];
    p[0] = "192.168.2.1";
    Main2(p);
}

public static void Main2(string[] args) {
    if (args.Length == 0)
        throw new ArgumentException("Ping needs a host or IP Address.");

    string who = args[0];
    AutoResetEvent waiter = new AutoResetEvent(false);

    Ping pingSender = new Ping();
===============================================================

Windowsアプリケーションを実行させ,button1をクリックすると waiter.WaitOne (); でイベント待ちになったあと,PingCompletedCallbackが呼び出されません。
コンソールアプリケーションだとPingCompletedCallbackが呼び出されます。

Conosle.WriteLineの出力は[表示]→[出力]で確認しています。

どうしてこのように挙動が違うのでしょうか。どうやってこの問題を回避すればいいのでしょうか。

引用返信 編集キー/
■2487 / inTopicNo.2)  Re[1]: コンソールとWindowsアプリのコールバックの違い
□投稿者/ アーラン (7回)-(2007/04/10(Tue) 15:15:45)
● ここまでの結論

Main2を別スレッドで動作させることで waiter.WaitOne(); を待っているときのイベントは発生するようになりました。

これがベストかどうかはわかりません。

● 考察

Windowsアプリケーションではbutton1をクリックしてMain2を実行したときに PingCompletedCallback は発生するのですが,Main2が終了するまでそのイベント・ハンドラは実行できないようです。このためMain2を別スレッドで実行する必要があるようです。

また,ThreadStartではパラメタを渡すことができないそうです。

動作確認したコードを示します。


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Net;
using System.Net.NetworkInformation;
using System.Threading;

namespace win_ping_sample03 {
    public partial class Form1 : Form {

        private Thread thMain;

        public Form1() {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e) {
            Thread thMain = new Thread(new ThreadStart(this.Main2));
            thMain.IsBackground = true;
            thMain.Start();
        }

        //public void Main2(string[] args) {
        public void Main2() {
            //if (args.Length == 0)
            //    throw new ArgumentException("Ping needs a host or IP Address.");

            //string who = args[0];
            string who = "192.168.2.1";
            AutoResetEvent waiter = new AutoResetEvent(false);

            Ping pingSender = new Ping();

            // When the PingCompleted event is raised,
            // the PingCompletedCallback method is called.
            pingSender.PingCompleted += new PingCompletedEventHandler(PingCompletedCallback);

            // Create a buffer of 32 bytes of data to be transmitted.
            string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
            byte[] buffer = Encoding.ASCII.GetBytes(data);

            // Wait 12 seconds for a reply.
            int timeout = 12000;

            // Set options for transmission:
            // The data can go through 64 gateways or routers
            // before it is destroyed, and the data packet
            // cannot be fragmented.
            PingOptions options = new PingOptions(64, true);

            Console.WriteLine("Time to live: {0}", options.Ttl);
            Console.WriteLine("Don't fragment: {0}", options.DontFragment);

            // Send the ping asynchronously.
            // Use the waiter as the user token.
            // When the callback completes, it can wake up this thread.
            pingSender.SendAsync(who, timeout, buffer, options, waiter);

            // Prevent this example application from ending.
            // A real application should do something useful
            // when possible.
            waiter.WaitOne();
            Console.WriteLine("Ping example completed.");
        }

        public static void PingCompletedCallback(object sender, PingCompletedEventArgs e) {
            // If the operation was canceled, display a message to the user.
            if (e.Cancelled) {
                Console.WriteLine("Ping canceled.");

                // Let the main thread resume. 
                // UserToken is the AutoResetEvent object that the main thread 
                // is waiting for.
                ((AutoResetEvent)e.UserState).Set();
            }

            // If an error occurred, display the exception to the user.
            if (e.Error != null) {
                Console.WriteLine("Ping failed:");
                Console.WriteLine(e.Error.ToString());

                // Let the main thread resume. 
                ((AutoResetEvent)e.UserState).Set();
            }

            PingReply reply = e.Reply;

            DisplayReply(reply);

            // Let the main thread resume.
            ((AutoResetEvent)e.UserState).Set();
        }

        public static void DisplayReply(PingReply reply) {
            if (reply == null)
                return;

            Console.WriteLine("ping status: {0}", reply.Status);
            if (reply.Status == IPStatus.Success) {
                Console.WriteLine("Address: {0}", reply.Address.ToString());
                Console.WriteLine("RoundTrip time: {0}", reply.RoundtripTime);
                Console.WriteLine("Time to live: {0}", reply.Options.Ttl);
                Console.WriteLine("Don't fragment: {0}", reply.Options.DontFragment);
                Console.WriteLine("Buffer size: {0}", reply.Buffer.Length);
            }
        }
    }
}

解決済み
引用返信 編集キー/
■2489 / inTopicNo.3)  Re[2]: コンソールとWindowsアプリのコールバックの違い
□投稿者/ よねKEN (30回)-(2007/04/10(Tue) 16:57:58)
よねKEN さんの Web サイト
.NET Framework2.0とのことですので、

> また,ThreadStartではパラメタを渡すことができないそうです。

ParameterizedThreadStartデリゲートが使えるかもしれません。

解決済み
引用返信 編集キー/
■2490 / inTopicNo.4)  Re[3]: コンソールとWindowsアプリのコールバックの違い
□投稿者/ アーラン (8回)-(2007/04/10(Tue) 17:11:24)
No2489 (よねKEN さん) に返信

> ParameterizedThreadStartデリゲートが使えるかもしれません。

情報ありがとうございます。

ここの解説によるとParameterizedThreadStartで引き渡すことにできるパラメタは
object型を1つだけとのことです。
http://www.atmarkit.co.jp/fdotnet/dotnettips/434paramthread/paramthread.html

今回はサンプル・プログラムをWindowsアプリケーションに書き換えて
動作確認をするのが目的でしたので安易な方向に走ってしまいました(汗)。

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -