C6/C7ステートは無効にせよ

家にあるPCは、部品寄集めの所謂、自作機。Haswel Core i7 と Gigabyte製のM/B。

(4年前の画像が残ってた)

Windows7の時は特にこれといって不満はなかったんですが・・・Windows10にした頃から、なんだかフォルダウィンドウが開くの妙に遅く感じてた。開くだけでなく、ウィンドウの切り替えが全体的にもっさり。タスクバーに登録したフォルダのショートカットをクリックしてウィンドウが現れるのに最悪2秒くらいかかる。

会社で使っているPCは 第二世代のCore i5 なんですが、それよりも遅い・・・今までいろいろググってみたけど、分からず・・・。
でも、つい最近なんですが、動画エンコード中にフォルダウィンドウを開くと、普通に速く開くのにやっと気付く(遅ッ)・・・あれ? もしかしてCPUの省電力が無駄に働いてんのかな・・・。
早速ググると、どうやら デスクトップPCではM/Bの設定でC6/C7ステートは無効にする、っていうのは FAQ らしい・・・。今更・・・気付いても・・・もうじきリプレースしようしているのに・・・(笑)

再起動しDELキーでBIOS画面に入り、無効化したら、今までの不満はなんだったのか?というほど嘘みたいに普通になる・・・。ウィンドウ間の切り替えもフツーの速さに・・・。

ああ、自作機においてデフォルト設定はダメなのね・・・。やっぱり僕みたいなのは、大人しくメーカー製のPCの方がいいかもしれませんね。。。

この夏もグチで終わり。。。

WebClient派生クラスでのクッキー読み書きについて

Tweet image download agent で、致命的なエラーを放置してた件。
もともとファボったツイートの画像だけを自動ダウンロードするために書いたコード。最近はもともとの動機となった機能はほとんど使わず、ツイッター内で検索した画像を自動ダウンロードするために使っていたので、いつの間にかログインできない状態になっていて、それにも気づかず・・・。コメントで報告してもらって初めて気づいたという、お粗末さ(^^;;;

それはともかく、原因は、ログイン後のクッキーの取り扱い。それが雑だったという、二重のお粗末さ・・・。C#使いとしては失格ですえ。

何が原因なのか、VisualStudio2015のIDEでとりあえず、該当箇所をステップ実行してデバッグしてたら、HttpWebResponse.Cookies に セッションクッキーしかストアされていないことに、まず気付いた。要するに、サーバーから返されたレスポンスヘッダ Set-Cookie の Expires が設定されていないものだけが HttpWebResponse.Cookiesにストアされている・・・。

どゆこと?

いくら実行しても、セッションクッキーしか保存されない・・・。これじゃログインが成功してたとしても、だめだわ・・・。
ChromeのDevToolsでTwitterサイトへのログインのレスポンスヘッダーを眺めてたら、あれ? もしかして、Expires に記述されている日付書式のパースに失敗してんのかな???と、グーグル先生に聞いてみると、.NETのSet-Cookieヘッダのパーサーはバカだよ(超意訳)、みたいな投稿が StackOverflowに出てた。

ってなわけで、WebClient.GetWebResponseをオーバーライドして、

WebResponse.Headers[“Set-Cookie”] から自前でクッキーをパースして、CookieContainer.Add しちゃいなよ!

っていうアドバイスに従い、テキトーにパースして Add しちゃう、しちゃう。

でもなー、前はちゃんと動いてたのに・・・。やっぱり、Twitter が吐く Set-Cookieヘッダが変わったぐらいしか、原因が分からないすッ。

探せばもっとマトモなコードがあると思われるので後で探そう・・・とりあえず↓でヨシとする。(要点のみ)
※ すべてのコードは、https://osdn.jp/users/earlgreyx/pf/TwitterImageDownloadAgent/wiki/FrontPage

/********************************************************
修正:2017/08/20 WebResponse.Headers.GetValuesメソッドから取得するように変更。
修正:2017/08/22 やっぱり forループよりLinq使った方がいいか・・・。
*********************************************************/
protected override WebResponse GetWebResponse(WebRequest request)
{
  WebResponse response = base.GetWebResponse(request);
  if(response is HttpWebResponse)
    {
      var httpWebResponse = response as HttpWebResponse;
      fixCookies(httpWebResponse);
 
      cookieContainer.Add(httpWebResponse.Cookies);
    }
  return response;
}
 
private void fixCookies(HttpWebResponse response) 
{
  var cookies = Enumerable.Range(1,response.Headers.Count)
                          .Where( i => response.Headers.GetKey(i - 1).ToLower() == "set-cookie" )
                          .SelectMany(i => response.Headers.GetValues(i - 1).Select(val => val));

  foreach(var singleCookie in cookies)
    {
      string domain=null,path=null,expires=null,n=null,v=null;
      bool httponly = false,secure = false;
 
      foreach(string el in Regex.Split(singleCookie, @"\s*;\s*"))
        {
          string[] kv = el.Split(new char[] {'='},2);
          string key=null,val=null;
          if(kv.Length == 2)
            {
              key = kv[0];
              val = kv[1];
            }
          else if(kv.Length == 1)
            {
              key = kv[0];
            }
 
          switch(key.ToLower())
            {
            case "domain":
              domain = val;
              break;
 
            case "expires":
              expires = val.Replace(" UTC","Z");
              break;

            case "path":
              path = val;
              break;

            case "httponly":
              httponly = true;
              break;

            case "secure":
              secure = true;
              break;

            default:
              n = key;
              v = val;
              break;
            }
        }
      var cookie = new Cookie(n,v);
      if(!string.IsNullOrEmpty(path))
        cookie.Path = path;
 
      /* Domainがない場合は、.twitter.com にしちゃう!
         Domainプロパティを設定しないと、Addが失敗しちゃう!! */
      cookie.Domain = string.IsNullOrEmpty(domain) ? ".twitter.com" : domain;

      if(!string.IsNullOrEmpty(expires))
        {
          //パースが失敗しちゃう場合は、Expiresは無かったことにしちゃう!
          DateTime dt;
          if(DateTime.TryParse(expires,out dt))
            cookie.Expires = dt;
        }
 
      cookie.HttpOnly = httponly;
      cookie.Secure = secure;
 
      response.Cookies.Add(cookie);
    }
}

シェルスクリプト右往左往

Bash on Ubuntu on Windows(以下BoWと略す)の環境下で作成したシェルスクリプト(ほとんどがImageMagickとffmpeg関連)がある程度貯まってきたので、CentOS側にコピーして動かしてみたら、エラーが出まくって動かなくなった・・・トホホ。

原因のほとんどが、basenameでのエラー。BoWは Ubuntuで、coreutilsのバージョンは 8.25。CentOS 6に標準で載ってる coreutilsのバージョンは、8.4・・・7年前にリリースされたもの・・・。

ってなわけで、 `basename -s <suffix> <filepath>` となっているところを片っ端から `basename <filepath> <suffix>` に変換してようやく動く。

今までシェルスクリプトは、「なんか覚えるのメンドーだなー」とか、「Perlで組めば大抵代用できるしなー」とか思ってて、あえて避けてきたんだけど、やっぱり画像の一括リサイズとか、画像への文字合成処理とかの単純作業は、Photoshopのアクション&バッチ処理をするより、圧倒的に手間が少なく、ラクできる。 特定ディレクトリ下に無秩序に掘られたサブディレクトリに格納された画像1000枚以上にシリアル番号入れて一括リサイズなんてPhotoshopでやってらんねーーーーし!

とりあえず、if-elif-else、case、while、for、などの基本制御式さえ押さえれば、あとは manページ参照しながらやってると、よく使う find コマンドとか、imagemagickなどのオプションなんて自然とソラで書けるようになってくるのがアラ不思議(^^

google検索のおかげで、CLIはグッとハードルが下がりましたよねぇ。。。