カテゴリ: 旧C#IE11のスクレイピング用コード

何で今時こんなコードを書いたかって言うと・・・
「NET4以降のThreadがまともに動かない」
「WCF以降はコードでUIを生成するタイプのプログラマにとって苦痛でしかない」
ので、
  • FormにWebBrowserを全面貼り付け
  • Labelで矩形確保して中身はプログラム描画のUI
  • 唯一まともに動くEventLoopをぶん回す
という単純な解に至っただけ。

 ちなみにスクレイピングは、

static なWebBrowserDocumentCompleteイベントハンドラdelegateから
  • 表示ページを判定して
  • switch caseで処理を決めて
  • Tickキューに実作業を押し付けた
つまり、WebBrowserが終わったって言うと、
  • ハンドラが書類の山(Tickキュー)に突っ込むか一番上に置いてく
  • おっさん(Tickループ)はひたすらこなして
  • 隙を見ては一服(Application.DoEvent())
  • 待ちになっては一服を繰り返しし
  • ひたすら週末まで耐える。
  • 業務が一段落する時間に一日一回自動リブート
という、どっかのオフィスみたいなプログラムだったんだけど、MSが馬鹿で、システムクロックがUTCに飛ぶバグを仕込んで、回らなくなっちゃった。

で、改装をあきらめてギブアップしたのが現状。ATI(AMD)のドライバも安定して動かなくなったし、INTELSocなら平気かと思うと、やっぱりおかしいし、動作しなくなるドライバパッチを勝手に当てるのは・・・(買い替えてくれという気持ちは判るが、最終顧客との利益相反が限界を超えたってことだよね。PL2303HXが動かなくなるのが良い例。コンピュータなんてのは繰り返し動き続けるだけが取り柄なんだから、それが無くなったら、メンテをあきらめてゴミにするしかない。)
で、あきらめたわけ。Windowsはゴミだ。エンタメ製品は消費産業だから、それを指向した時点で、自動化を目的とした制御回路としてのコンピュータとしては使えなくなった。

This article's copyright goes Akitsugu Tomisawa but riabilities stays with me, Yoshiyuki Yamaguchi.  As I owe some cash fto him.

コアコードその二
Timer 派生クラスTick

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace ak5
{
    public class Tick : Timer
    {
        public static Tick sTick;
        public static int kInterval = 1000 / 8, nictMninute = 40, nictWait = 5;
        public static void sfStart() { sTick.Start(); }
        public static void sfPause() { sTick.Stop(); }
        public static void sfQuitt()
        {
            DateTime[] last = new DateTime[3];
            for (int i = 0; i < 3; i++)
            { last[i] = datetimes[i]; }
            sfDateTimeUpdate();
            int index = 9; // quit
            for (int i = 0; i < sTodos[index].Count; i++)
            { sTodos[index][i].callback(datetimes, last); }
            Win.sfQuit();
        }

        public delegate void dfCBTick(DateTime[] newDTs, DateTime[] lastDTs);

        private class Todo
        {
            public Todo(mode iMode, DateTime iTime, dfCBTick iCB)
            {
                time = iTime;
                callback = iCB;
            }
            public DateTime time;
            public dfCBTick callback;
        }

        private static List<Todo>[] sTodos;

        #region // labels

        private void sfLabelsUpdate()
        {
            sfLabelWeekUpdate();
            sfLabelDayUpdate();
            sfLabelDateUpdate();
            sfLabelTimeUpdate();
        }

        private void sfLabelWeekUpdate() { }
        private void sfLabelDayUpdate() { }
        private void sfLabelDateUpdate() { }
        private void sfLabelTimeUpdate() { }

        #endregion

        public Tick()
            : base()
        {
            if (sTick != null) { throw (null); }
            sTick = this;
            if (sTodos != null) { throw (null); }
            sTodos = new List<Todo>[modes.Length];
            for (int i = 0; i < modes.Length; i++)
            { sTodos[i] = new List<Todo>(); }
            datetimes = new DateTime[times.Length];
            DateTime end = DateTime.Now;
        LOOPNICT:
            end = end.AddSeconds(nictWait);
            if (sfNICT()) { while (DateTime.Now < end) { Application.DoEvents(); } goto LOOPNICT; }
            sfDateTimeUpdate();
            sfLabelsUpdate();
            sTick.Interval = kInterval;
            sTick.Tick += sfTick;
        }

        public static void sfTick(Object o, EventArgs e)
        {
            // better fast than clean
            DateTime[] last = new DateTime[3];
            for (int i = 0; i < 3; i++)
            { last[i] = datetimes[i]; }
            sfDateTimeUpdate();
            int index = 0; // urgent
            for (int i = 0; i < sTodos[index].Count; i++)
            {
                Todo curTodo = sTodos[index][0];
                sTodos[index].Remove(curTodo);
                curTodo.callback(datetimes, last);
                curTodo = null;
            }
            Application.DoEvents();
            sfDateTimeUpdate();
            index = 1; // tick
            for (int i = 0; i < sTodos[index].Count; i++)
            {
                Todo curTodo = sTodos[index][0];
                sTodos[index].Remove(curTodo);
                curTodo.callback(datetimes, last);
                curTodo = null;
            }
            sfDateTimeUpdate();
            DateTime newDt = datetimes[1], oldDt = last[1];
            if (newDt.Second == oldDt.Second) { return; }
            index = 2; // second
            for (int i = 0; i < sTodos[index].Count; i++)
            { sTodos[index][i].callback(datetimes, last); }
            index = 3; // once
            for (int i = 0; i < sTodos[index].Count; i++)
            {
                Todo curTodo = sTodos[index][0];
                if (curTodo.time > newDt) { goto DONEONCE; }
                sTodos[index].Remove(curTodo);
                curTodo.callback(datetimes, last);
                curTodo = null;
            }
        DONEONCE:
            if (newDt.Minute == oldDt.Minute) { return; }
            index = 4; // minute
            for (int i = 0; i < sTodos[index].Count; i++)
            { sTodos[index][i].callback(datetimes, last); }
            if (oldDt.Minute < 40 && newDt.Minute >= 40)
            {
                DateTime end = DateTime.Now;
            LOOPNICT:
                end = end.AddSeconds(nictWait);
                if (sfNICT()) { while (DateTime.Now < end) { Application.DoEvents(); } goto LOOPNICT; }
                if (newDt.Hour == 7)
                {
                    index = 8; // daystart
                    for (int i = 0; i < sTodos[index].Count; i++)
                    { sTodos[index][i].callback(datetimes, last); }
                }
            }
            if (oldDt.Minute < 20 && newDt.Minute >= 20)
            {
                if (newDt.Hour == 7)
                {
                    index = 7; // dayend
                    for (int i = 0; i < sTodos[index].Count; i++)
                    { sTodos[index][i].callback(datetimes, last); }
                }
            }
            if (newDt.Hour == oldDt.Hour) { return; }
            index = 5; // hour
            for (int i = 0; i < sTodos[index].Count; i++)
            { sTodos[index][i].callback(datetimes, last); }
            if (newDt.Day == oldDt.Day) { return; }
            index = 6; // date
            for (int i = 0; i < sTodos[index].Count; i++)
            { sTodos[index][i].callback(datetimes, last); }
        }


        #region // NICT

        public static bool sfNICT()
        {
            try
            {
                DateTime tNew = new DateTime(1900, 1, 1), tBefore;
                tNew.AddSeconds(2208988800);

                System.Net.Sockets.UdpClient sock;
                System.Net.IPEndPoint ipAny =
            new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);
                sock = new System.Net.Sockets.UdpClient(ipAny);

                Byte[] pSend = new Byte[48];
                pSend[0] = 0xB;
                tBefore = DateTime.Now;
                sock.Send(pSend, pSend.GetLength(0), "ntp.nict.jp", 123);

                Byte[] pRecv = sock.Receive(ref ipAny);

                long seconds = (long)(
                          pRecv[40] * Math.Pow(2, (8 * 3)) +
                          pRecv[41] * Math.Pow(2, (8 * 2)) +
                          pRecv[42] * Math.Pow(2, (8 * 1)) +
                          pRecv[43]);

                tNew = tNew.AddSeconds(seconds + 9 * 60 * 60 + 1);
                sfSysTime(tNew + (DateTime.Now - tBefore));

                return false;
            }
            catch (Exception) { return true; }
        }

        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        private struct SystemTime
        {
            public ushort wYear;
            public ushort wMonth;
            public ushort wDayOfWeek;
            public ushort wDay;
            public ushort wHour;
            public ushort wMinute;
            public ushort wSecond;
            public ushort wMiliseconds;
        }

        [System.Runtime.InteropServices.DllImport("kernel32.dll")]
        private static extern bool SetLocalTime(ref SystemTime sysTime);

        private static void sfSysTime(DateTime newTime)
        {
            SystemTime sTime = new SystemTime();
            sTime.wYear = (ushort)newTime.Year;
            sTime.wMonth = (ushort)newTime.Month;
            sTime.wDay = (ushort)newTime.Day;
            sTime.wHour = (ushort)newTime.Hour;
            sTime.wMinute = (ushort)newTime.Minute;
            sTime.wSecond = (ushort)newTime.Second;
            sTime.wMiliseconds = (ushort)newTime.Millisecond;
            SetLocalTime(ref sTime);
        }

        #endregion

        #region // table for time and mode

        private static DateTime[] datetimes;
        private static void sfDateTimeUpdate()
        {
            for (int i = 0; i < times.Length; i++)
            { datetimes[i] = DateTime.Now.AddSeconds(timeOffsets[i]); }
        }

        public DateTime sfDt() { return datetimes[1]; }
        public DateTime sfDt(time iTime)
        {
            switch (iTime)
            {// better fast than clean
                case time.cpu: return datetimes[0];
                case time.man: return datetimes[1];
                case time.dat: return datetimes[2];
                default: throw (null);
            }
        }

        public enum time
        { cpu, man, dat };

        private static readonly time[] times = new time[] { time.cpu, time.man, time.dat };

        private static readonly int[] timeOffsets = new int[] { 3, 13, -60 * 3 };

        private int sfTime2Index(time iTime)
        {
            for (int i = 0; i < times.Length; i++)
            { if (iTime == times[i]) { return i; } }
            throw (null);
        }

        private time sfIndex2Time(int iIndex)
        {
            if (iIndex < 0) { throw (null); } if (iIndex >= times.Length) { throw (null); }
            return times[iIndex];
        }

        #endregion

        #region // mode

        public enum mode
        { urgent, tick, second, once, minute, hour, date, dayend, daystart, quit };

        private static readonly mode[] modes = new mode[] { mode.urgent, mode.tick, mode.second, mode.once, mode.minute, mode.hour, mode.date, mode.dayend, mode.daystart, mode.quit };

        private int sfMode2Index(mode iMode)
        {
            for (int i = 0; i < modes.Length; i++)
            { if (iMode == modes[i]) { return i; } }
            throw (null);
        }

        private mode sfIndex2Mode(int iIndex)
        {
            if (iIndex < 0) { throw (null); } if (iIndex >= modes.Length) { throw (null); }
            return modes[iIndex];
        }

        #endregion
    }
}


This article's copyright goes Akitsugu Tomisawa but riabilities stays with me, Yoshiyuki Yamaguchi.  As I owe some cash fto him.

コアコードその一
WebBrowser 埋め込み済みの Form 派生クラスWin

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace ak5
{
    public abstract class Win : Form
    {
        //public abstract uint vfMemSize() { return 0; }

        public Win()
        { sfInitializeCommon(this, !isRelease, null, null, false, null, null); }

        //public Win(dfProcessCommandLine idfProcessCommandLine, String mutexName)
        //{ sfInitializeCommon(this, !isRelease, idfProcessCommandLine, mutexName, false, null, null); }

        //public Win(String mutexName, String homeUrl, String InitailUri)
        //{ sfInitializeCommon(this, !isRelease, null, mutexName, true, homeUrl, InitailUri); }

        //public Win(String homeUrl, String InitailUri)
        //{ sfInitializeCommon(this, !isRelease, null , null, true, homeUrl, InitailUri); }

        public Win(bool isDebug, dfProcessCommandLine idfProcessCommandLine, String mutexName, bool haveWeb, String homeUrl, String InitailUri)
        { sfInitializeCommon(this, isDebug, idfProcessCommandLine, mutexName, haveWeb, homeUrl, InitailUri); }

        public delegate bool dfProcessCommandLine(String[] args, ref String mutexName, ref bool haveWeb, ref String homeUrl, ref String InitailUri);
       
        protected static void sfInitializeCommon(Win f, bool isDebug, dfProcessCommandLine idfProcessCommandLine, String mutexName, bool haveWeb, String homeUrl, String InitailUri)
        {
           
            isRelease = !isDebug;
            if (sWins == null)
            {
                bool isDone = false;
                if (idfProcessCommandLine != null)
                { isDone = idfProcessCommandLine(System.Environment.GetCommandLineArgs(), ref mutexName, ref haveWeb, ref homeUrl, ref InitailUri); }
                if (!String.IsNullOrEmpty(mutexName))
                {
                    sMutexWin = new System.Threading.Mutex(false, mutexName);
                    if (sMutexWin.WaitOne(0, false) == false)
                    { isDone = true; }
                }
                if (isDone)
                {
                    System.Diagnostics.Process.GetCurrentProcess().Close();
                    System.Diagnostics.Process.GetCurrentProcess().Kill();
                }
                sWins = new List<Win>();
                Screen[] all = Screen.AllScreens;
                monCount = all.Length;
                mons = new Rectangle[monCount + 1];
                mons[monCount] = Screen.PrimaryScreen.WorkingArea;
                monUse = 0;
                for (int i = 0; i < monCount; i++)
                {
                    mons[i] = all[i].Bounds;
                    if (all[i].Primary) { monPri = i; }
                    if (mons[i].Left > mons[monUse].Left) { monUse = i; }
                    if (mons[i].Left == mons[monUse].Left) { if (mons[i].Top < mons[monUse].Top) { monUse = i; } }
                }
            }
            sWins.Add(f);
            if (isRelease)
            {
                f.Opacity = 0;
                f.ShowInTaskbar = false;
            }
            f.FormBorderStyle = FormBorderStyle.None;
            f.BackColor = Color.FromArgb(255, 255, 254);
            //if (isRelease) { f.TransparencyKey = f.BackColor; }
            f.StartPosition = FormStartPosition.Manual;
            f.Bounds = mons[monUse];
            //f.Icon = Properties.Resources.ico_quick;
            int div = 3;
            f.Width = 1920 / div;
            for (int i = 0; i < sWins.Count; i++)
            { if (f == sWins[i]) { f.Left += (f.Width * (i % div)); } }
            f.urlHome = homeUrl;
            f.urlInitial = InitailUri;
            if (haveWeb)
            {
                WebBrowser c = new WebBrowser();
                f.mWeb = c;
                f.Controls.Add(c);
                c.Dock = DockStyle.Fill;
                c.BringToFront();
                f.mWeb.Navigating += mWeb_Navigating;
                f.mWeb.DocumentCompleted += mWeb_DocumentCompleted;
                if (!String.IsNullOrEmpty(f.urlInitial)) { c.Navigate(f.urlInitial); }
            }
            f.BringToFront();
        }

        protected static void mWeb_Navigating(Object o, WebBrowserNavigatingEventArgs e)
        { (((WebBrowser)o).FindForm()).Text = e.Url.ToString(); }

        protected static void mWeb_DocumentCompleted(Object o, WebBrowserDocumentCompletedEventArgs e)
        {
            WebBrowser c = (WebBrowser)o;
            String title = String.Empty;
            if (c.Document != null)
            { title = c.Document.Title; }
            if (String.IsNullOrEmpty(title)) { (c.FindForm()).Text = e.Url.ToString(); }
            else { (c.FindForm()).Text = title; }
        }

        protected override void OnClosed(EventArgs e)
        {
            if (this == sWins[0])
            {
                for (int i = (sWins.Count - 1); i == 1; i--)
                { sWins[i].Close(); }
                if (sMutexWin != null) { sMutexWin.Close(); sMutexWin = null; }
            }
            sWins.Remove(this);
            if (sWins.Count == 0) { sWins = null; }
            base.OnClosed(e);
        }

        public WebBrowser mWeb;
        protected String urlHome, urlInitial;
        public static bool isRelease = false;
        protected static List<Win> sWins;
        public static void sfQuit() { sWins[0].Close(); }
        protected static int monCount, monPri, monUse;
        protected static Rectangle[] mons;
        private static System.Threading.Mutex sMutexWin;
    }
}

This article's copyright goes Akitsugu Tomisawa but riabilities stays with me, Yoshiyuki Yamaguchi.  As I owe some cash fto him.

↑このページのトップヘ