c# async、awaitを使わずにコードを書く方法

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,755

bybalsender

score 31

初心者的なことで申し訳ありません。
bybalsenderともうします。

先日async、awaitを使用したコードをもらいましたが、環境がvs2005のため動作しません、そこでasync、awaitを利用しない方法を教えてください。

取得したいのは翻訳結果です。
どのような方法でも構いません。

以下のコードでasync、awaitを削除したいです。

const string ur = "http://honyaku.yahoo.co.jp/transtext/";
List<string> _ant = new List<string>();             
private void Form1_Shown(object sender, EventArgs e)
{
webBrowser1.ScriptErrorsSuppressed = true;
webBrowser1.Navigate(ur);
_ant.Add("日本語に翻訳");
_ant.Add("英語に翻訳");
_ant.Add("中国語に翻訳");
_ant.Add("ドイツ語に翻訳");
}
async private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (ur == e.Url.ToString())
{
var tasks = new List<Task>();
foreach (string htm in _ant)
{
webBrowser1.Document.GetElementById("textText").InnerText = htm;
webBrowser1.Document.GetElementById("btn").InvokeMember("Click");
await Task.Run(() =>
{
Thread.Sleep(1000);
}).ContinueWith(_ => btn_click(),
TaskScheduler.FromCurrentSynchronizationContext());
}
Task.WaitAll(tasks.ToArray());
}
}
private void btn_click()
{
MessageBox.Show(webBrowser1.Document.GetElementById("trn_textText").InnerText);
}

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • SurferOnWww

    2016/09/25 13:06

    「先日async、awaitを使用したコードをもらいましたが」というのはどこでもらったのか、他のスレッドとかでしたらその URL を示せませんか?

    キャンセル

  • bybalsender

    2016/09/25 13:08

    申し訳ありません。
    コチラです。
    https://teratail.com/questions/49124

    キャンセル

回答 2

checkベストアンサー

0

何故先のスレッドの回答者の方が 1 秒待って[翻訳]ボタンをクリックするのを非同期で実装したのか真意が分かっていませんが、そんなことをする必要はなさそうな気がします。

前のスレッドの質問者さんのコードで 1 回目の「てすと1」がうまく行ったのは、1 回目だけは WebBrowser.DocumentCompleted イベントの発生(応答が返ってきてドキュメントの読み込みが完了)を待って InvokeMember("Click") メソッドで[翻訳]ボタンをクリックしたから、2 回目以降は 1 回目の応答が返ってこないのに foreach ループで[翻訳]ボタンを高速クリックしたからではないかと思います。

先のスレッドの回答者の方が 1 秒待つという案を出していますが、それは応答が返ってきてドキュメントの読み込みが完了するまで時間がかかるからそれを待つという話だと思います。

でも、それを非同期にやる必要はないと思います。また、1 秒で応答が返ってこない(混んでると 10 秒かかるかもしれない)ということもあるわけで、「1 秒」が適切かは疑問です。

たぶん、WebBrowser.DocumentCompleted ハンドラの中で InvokeMember("Click") メソッドで[翻訳]ボタンをクリックした場合、WebBrowser は再度同じ URL にナビゲートされると共に、id="textText" の原文を送信すると思われます。

その応答が返ってくると再度 WebBrowser.DocumentCompleted イベントが発生するはずです。

なので、WebBrowser.DocumentCompleted イベントハンドラの中で foreach で回すのはやめて、[翻訳]ボタンクリックで「てすと1」を送信 ⇒ その応答が返ってきて DocumentCompleted イベントが発生した時「テスト2」を送信 ⇒ 同様に「test三」「test四」を繰り返す・・・とすればよさそうです。

お試しください。

【サンプルコード追記】

最初のスレッドにあった以下の質問者さんの DocumentCompleted イベントハンドラコードを、

private void webBrowser1_DocumentCompleted(object sender, 
                                      WebBrowserDocumentCompletedEventArgs e)
{
    if (ur == e.Url.ToString())
    { 
        foreach (string htm in _ant)
        {
            webBrowser1.Document.GetElementById("textText").InnerText = htm;
            webBrowser1.Document.GetElementById("btn").InvokeMember("Click");
            btn_click();
        }
        test();
    }
}

を以下のようにしてみたらいかがでしょう? 頭の中で考えて書いただけで検証はしてないので、質問者さんの方でデバッガ等を使って期待通りの動きになるか十分検証してください。

int n = 0;

private void webBrowser1_DocumentCompleted(object sender, 
                                      WebBrowserDocumentCompletedEventArgs e)
{
    if (ur == e.Url.ToString())
    { 
        if (n < _ant.Count)
        {
            string htm = _ant[n];
            webBrowser1.Document.GetElementById("textText").InnerText = htm;
            webBrowser1.Document.GetElementById("btn").InvokeMember("Click");
            btn_click();
            n++;
        }
        test();
    }
}

 

InvokeMember("Click") のあと、制御がまたこのハンドラに戻ってくるかがキモなのですが、うまく動かなかったらデバッガでそのあたりがどうなっているか調べて連絡ください。

【検証に使ったコードを追記】 

検証に使ったコードを以下にアップしておきます。既存のコードを流用したので、手動で http://honyaku.yahoo.co.jp/transtext/ へのナビゲーションを開始するところがちょっと違いますが、基本は質問者さんがコードで示したやりたいことの通りのはずです。

実行結果は以下の画像の通りです。

http://surferonwww.info/BlogEngine/image.axd?picture=2016%2f9%2ftranstext.jpg

一応このサンプルでは期待通りの結果になりましたが、原文に入力している時に原文が何語かの判定を行うためサーバーとのやり取りをしているようで、プログラムで原文をセットして即送信では、場合によってはそのあたりがうまくいかないかもしれません。

対症療法的なことのカタマリのようなもので、Yahoo! は予告無しに html 要素の id、呼び出し先 url、その他いろいろ変えるでしょうから、明日は動かないかもしれません。

というわけで、最初のスレッドで htsign さんが書かれたように、自分もお勧めはできません。脅かすわけではないですが、クローラを作って図書館のサイトにアクセスしたら業務妨害で逮捕されたという例もあるようですし。

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

namespace WebBrowser
{
    public partial class Form1 : Form
    {
        // 2016/9/26 teratail の検証用
        // https://teratail.com/questions/49159
        private string url = "http://honyaku.yahoo.co.jp/transtext/";
        List<string> _ant = new List<string>() { "日本語に翻訳",
                                                 "英語に翻訳", 
                                                 "中国語に翻訳", 
                                                 "ドイツ語に翻訳" };
        List<string> translated = new List<string>();
        int count = 0;

        public Form1()
        {
            InitializeComponent();
            textBox1.Text = url;

            webBrowser1.ScriptErrorsSuppressed = true;
        }

        private void navigateButton_Click(object sender, EventArgs e)
        {
            webBrowser1.Navigate(textBox1.Text);
        }

        private void webBrowser1_DocumentCompleted(object sender, 
                                              WebBrowserDocumentCompletedEventArgs e)
        {
            // 初期画面
            if (e.Url.ToString() == url)
            {
                if (count < _ant.Count)
                {
                    // 最初の原文をセットして送信                    
                    webBrowser1.Document.GetElementById("textText").InnerText = _ant[count];
                    webBrowser1.Document.GetElementById("btn").InvokeMember("Click");
                }
            }

            // 翻訳が返ってきたときの処理
            if (e.Url.ToString().Contains("http://honyaku.yahoo.co.jp/darla/php/fc.php"))
            {
                count++;
                translated.Add(webBrowser1.Document.GetElementById("trn_textText").InnerText);

                // 次の原文をセットして送信
                if (count < _ant.Count)
                {
                    webBrowser1.Document.GetElementById("textText").InnerText = _ant[count];
                    webBrowser1.Document.GetElementById("btn").InvokeMember("Click");
                }

                if (count == _ant.Count)
                {
                    string result = "";

                    foreach (string s in translated)
                    {
                        result += s + "\n";
                    }
                    MessageBox.Show(result);
                }
            }
        }
    }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/09/25 22:33

    横からすみません。
    先の質問に回答させていただいた者です。
    私の意図としては極力コードを改変しない方が理解が早いかな、と思ってのことでした。
    またご指摘の通り、1秒という待ち時間は多分にサーバーとの通信の安定性に依存したもので、よくないことは承知していました。

    WebBrowserクラスのMSHTMLレンダラがUIスレッドとは別に動作するようであればもう少しやりようもあったのかもしれませんが、今一つ上手く動かなかったため非同期に「逃げ」てしまいました。
    また、XMLHttpRequestによる通信のC#側からのハンドリングに自信がなかったのもあります。
    そのせいでbybalsenderさんにご迷惑をかけたようで、この場を借りてお詫びします。

    キャンセル

  • 2016/09/26 12:38

    ご参考に、上のコメントを書いた時に検証に使ったコードを回答欄に追加しておきます

    キャンセル

  • 2016/09/27 21:51



    SurferOnWwwさんへ
    お返事有難う御座いました。
    お見事に表示成功です。
    自分としては、これで解決にしたいと思います。



    htsignさんへ
    お気ずかい有難う御座いました。
    同期、非同期の勉強になりました。

    みなさん有難う御座いました。
    これで実装可能状態です。



    キャンセル

0

根本的な疑問なのですが、vs2005のような古いバージョンを使い続ける理由はなんでしょうか?
今はvs2015 community(基本無料 企業でも条件付きで使用可能)もありますし、
それを検討してみるのはいかがでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/10/08 08:06

    返信が遅れてすいません。

    >vs2005のような古いバージョンを使い続ける理由はなんでしょうか?
    3D-CADに付属しているVisual Studio が2005なものですから、VS2005で対応できればと思っていました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる