1

Windows CE 5.0 で実行している .NET CF 2.0 アプリケーションで奇妙な動作が発生しました。

コントロールを定期的に更新するタイマーがあります。そのコントロールは、(マウスダウンハンドラーで) ユーザーからタップアンドホールドジェスチャを受け取ることもできます。私が見つけたのは、TAH が開始すると (終了する前に) タイマー イベントが処理を開始し、実行の途中でマウス ダウン ハンドラーをプリエンプトする可能性があることです。

私の調査によると、これは通常の動作ではなく、単にタイマー/イベントを誤解しているだけですか? SHRecognizeGesture が Application.DoEvents と同等のものを呼び出しているだけでしょうか?

いずれにせよ、アプリがTAHをチェックしているときにタイマーデリゲートが「カチカチ」しないように、この例を修正する「良い」方法を誰かが持っていますか?

この問題を説明するサンプル プログラムについては、以下を参照してください (リスト ボックスの下の空きスペースをタップ アンド ホールドすると、ログ メッセージが生成されます)。

前もって感謝します。

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace DeviceApplication1
{
    public class BugExample : Control
    {
        [Flags]
        internal enum SHRGFLags
        {
            SHRG_RETURNCMD = 0x00000001,
            SHRG_NOTIFYPARENT = 0x00000002,
            SHRG_LONGDELAY = 0x00000008,
            SHRG_NOANIMATION = 0x00000010,
        }

        [DllImport("aygshell.dll")]
        private extern static int SHRecognizeGesture(ref SHRGINFO shrg);

        private struct SHRGINFO
        {
            public int cbSize;
            public IntPtr hwndClient;
            public int ptDownx;
            public int ptDowny;
            public int dwFlags;
        }

        public bool TapAndHold(int x, int y)
        {
            SHRGINFO shrgi;

            shrgi.cbSize = 20;
            shrgi.hwndClient = this.Handle;
            shrgi.dwFlags = (int)(SHRGFLags.SHRG_RETURNCMD );
            shrgi.ptDownx = x;
            shrgi.ptDowny = y;

            return (SHRecognizeGesture(ref shrgi) > 0);

        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            BugExampleForm parent = (BugExampleForm)this.Parent;

            //The problem is that the parent tick event will fire whilst TapAndHold is running
            //Does TapAndHold perform an equivelant to Application.DoEvents?
            parent.AddLog("Tap Hold - Enter");
            parent.AddLog(String.Format("Tap Hold - Exit - {0}", TapAndHold(e.X, e.Y)));

        }
    }

    public class BugExampleForm : Form
    {
        Timer _timer;
        BugExample _example;
        ListBox _logBox;

        public BugExampleForm()
        {
            _example = new BugExample();
            _example.Dock = DockStyle.Fill;

            _logBox = new ListBox();
            _logBox.Dock = DockStyle.Top;

            _timer = new Timer();
            _timer.Interval = 1000;
            _timer.Enabled = true;
            _timer.Tick += new EventHandler(_timer_Tick);

            this.SuspendLayout();
            this.Text = "Example";
            this.Size = new System.Drawing.Size(200, 300);

            this.Controls.Add(_example);
            this.Controls.Add(_logBox);
            this.ResumeLayout();
        }

        void _timer_Tick(object sender, EventArgs e)
        {
            AddLog("Tick");
        }

        public void AddLog(string s)
        {
            _logBox.Items.Add(s);
            _logBox.SelectedIndex = _logBox.Items.Count - 1;
        }
    }
}

画像をインラインでリンクできないため、動作を示すスクリーンショットへのリンクを次に示します。

編集:私の実際のアプリケーションでは、タイマーの目盛りがコントロールを更新しています。そのため、私は 1 つのスレッド内での作業に制限されています。(イベントハンドラーでも必要なことを実際に達成することはできません)。

4

4 に答える 4

0

おそらくこの動作を妨げるもう1つの答え(なぜそれが表示されるのかについての説明については、私の他の答えを参照してください)は、フォームタイマーの使用からスレッドタイマーに変更して、スレッドprocが別のスレッドになるようにすることです。タイマー プロシージャが UI に影響を与えていない場合、これはうまく機能するはずです。それでも競合が発生する場合 (タイマー proc が実際に何をしているのかわかりません)、タイマー proc の開始時にスレッドの優先度を BelowNormal のようなものに変更すると、UI から CPU のクォンタムを占有するのを防ぐことができます。

于 2009-10-29T13:09:40.380 に答える
0

Timer は WinForms タイマーであるため、UI およびすべての UI ハンドラー (マウス ダウン ハンドラーを含む) と同じスレッド コンテキストで実行されます。つまり、一度に実行できるのは 1 つだけです。表示されている問題は、マウス ハンドラーが実行をスワップしている他のタスクを取得して実行しているためです。

これは、クリティカル セクション (モニター) を使用して回避できます。マウスダウン ハンドラー全体 (または開始時に Monitor を入力し、最後にモニターを終了)をロックしてから、同じ object をロックする timer proc にロックを配置できます。これは、タイマー proc が実行される前にマウスダウン ハンドラー全体を実行する必要があり、タイマーの「ティック」が実際にキューに入れられることを意味します (特にそれを回避するために Monitor.TryEnter を使用しない限り)。

考えられる欠点は、その逆も当てはまることです。マウスダウン ハンドラーは、保留中のタイマー プロシージャが完全に完了するまで実行できません。これが問題であるかどうかは、ユースケースに基づいており、早期に終了するイベントまたはフラグを探すことで、タイマー proc でいつでも軽減できます。

于 2009-10-29T13:07:02.837 に答える
0

この問題の回避策は、パブリックの静的クラス変数 (おそらくシングルトン スタイル) にブール値フラグを設定することです。たとえば、IgnoreTick と呼びます。

マウス ダウン ハンドラーで IgnoreTick を true に設定します。ティック ハンドラーで、IgnoreTick の値を確認します。それが本当なら戻って、そうでなければあなたがすることをしてください。もちろん、IgnoreTick を false に戻すマウスアップ ハンドラーを追加する必要があります。

于 2009-10-29T02:38:01.643 に答える
0

ハンドラーの開始時に Timer の Enabled プロパティを false に設定し、最後に ture に戻さないのはなぜですか?

于 2009-10-29T03:15:16.880 に答える