VAIO Z (VPCZ21 2011) + Windows10 Pro

残りあと100日も切ってしまった、Windows10への無料アップグレード。
メインPCのWindows7をWindows10にアップグレードするか、未だ、踏ん切りが付きません(ーー;;; マイクロソフトは無料アップグレード期間の延長は絶対無い、と言い切ってるし・・・。

ってなわけで、去年Surface3買ってから使わなくなって放置してたVAIO Z (Windows7 Pro版)を、試しにWindows10にアップグレードして、手順をおさらいしておこう、と。VAIOもSONYから放り出されてWindows10のサポートも無くなったので、この際どうなってもいいや、ってな感じ。

まずは、Windows7を立ち上げて、去年の10月頃からの膨大な量の Windows Update を適用しておく。ちなみにこれ、一晩かかりました(^^;;; 夜、仕掛けてから寝て、朝起きてもまだ終わっていない・・・orz。Windows Updateは溜めておくと地獄ですな・・・。

連休の終わりかけの今日(5/5)、タスクバーの通知エリアのWindows10アイコンをクリックしていざアップグレード開始。アップグレードの最終確認後、30分~1時間程放置してたら終わってました。

upgrade-to-windows10

気付いた点を今後のメインPCのWindows10アップグレード時のため、以下に列挙しておく。

  • Windows7の時に使っていたアプリやツール類はすべて元通りに使用できた。
  • 関連付け、アイコンなどは、一部を除いて設定がそのまま残った
  • システムの設定は、ほぼすべて引き継がれた。
  • Windows7で使用してたセキュリティ対策ソフト、Security Essentials が自動的に削除され、Windows Defender に引き継がれていた。
  • 英語キーボードをAXドライバで使用する設定(右ALTにIMEトグルを割り当てるため)などはちゃんと引き継がれた。
  • Windows7ではIMEをATOKにしてたんですが、Windows標準のIMEに初期化されていた。
  • xyzzyのキーバインド変更ツールは、ちゃんと使えた。ただし設定ダイアログに表示上の不具合発生。
  • タスクバーに登録していたアプリもそのまま残っていた。
  • Windows7のスタートメニューとWindows10のスタートメニューは根本的に違うので、スタートメニューはまっさら状態。よく使うアプリのスタートメニューへの登録をやりなおす必要がある。
  • 使用メモリが増えてる気が・・・最初だけ??? Windows7も使い始めはメモリ使用量多かったしな・・・
  • windows.old は速攻で削除。どうせもうWindows7には戻れないし・・・。
  • デスクトップPCでコルタナはOFFにするべし。(パソコンに音声アシストは必要か???)
  • Windows10のデフォルトのフォント、游ゴシックはやっぱりブサイク! なんでメイリオやめたん???
  • デフォルトでIISが起動して80番ポートを塞いでいるので、開発環境にapacheを使っているWebアプリ開発者は、IISのサービスを無効にするかアンインストールする必要がある。
    (追記:Visual Studio がインストールしてあるからなのかな~?という気がしてきた。)
  • デフォルトブラウザ Microsoft Edgeはデフォルトでループバックアドレス(localhost)にアクセスできないので、CheckNetIsolation.exeコマンドで別途許可する必要がある。
  • デスクトップPCでWindows10を使う場合、ローカルアカウントで運用した方がいい。
  • ストアアプリをPCで使う場合はマイクロソフトアカウントで運用しなければならない。が必要。(普通に考えてパソコンで制限のあるストアアプリなんて使わないと思うが・・・)
  • VAIOホームネットワークプレーヤー(レコーダーやNasneの録画番組を見るVAIO専用のアプリ)はライセンスの認証をやりなおし。
  • VAIO独自のハードウェアキーはほぼ全部使えた。
  • スピーカーのON/OFFに割り当てられた Fn + F2は効かなくなった(残念)

ほんとかどうかは定かでは無いけど、Windows10は、「入力データをマイクロソフトに送信しない」設定にしてても実際にはそういった入力情報は密かに?送られる!!! ようなことをネットで見かけます。まぁ、現実問題、一般人には確かめようがないっすよねぇ。

Visual Studioとか主要なアプリケーションの対応OSから Windows7 が除外された時点でアップグレードしようなかな~。うーん・・・。

twitterの「いいね」画像をダウンロード C#版

2017年7月15日
こっちに移動 → https://ptsv.jp/2016/04/20/tweet-image-download-agent/

2016年4月29日 Windows フォームを利用したGUI版追加
検索ワード対応とスレッドプールにダウンロード処理を投げるように、ソース本体も改編した版

2015年11月27日・29日 コード修正・他
コード修正 画像ファイルの正規表現の間違い修正及びcommandを追加。
コード修正 URL を favorites => likes に変更。文章をちょろっと変更。

2015年11月23日 コード修正
2回目以降のtimeline URLの取得方法が間違ってました。ごめんなさい。(m_m)


perl で組んだものを C# に書き直したコードです。perlで組んだもので僕がやりたかったことは全部できた訳ですが・・・ま、今仕事暇なんで(^^;

エラーチェックとか全然してません。このコードは参考テスト程度のものなので、あしからず。
間違ったtargetとか入力してサーバーが404エラー返すと例外吐いて死にます(^_^;; エラー処理をコードに盛り込むとコードの要点が見えずらくなるので・・・いないとは思いますが、コードを使う時は、エラー処理(例外捕捉とか)を入れてね。

また、Twitter API使った方法ではないので、twitter側がブラウザ表示の方法を変更してしまうと、たぶんエラーで落ちると思う。まぁ、いつまでこの方法で使えんのかな、という感じです。

コンソールプログラムで良ければ、コンパイルしたものを置いときます。[ getTweetImage.exe ]
※使用方法は下記コードのコメントを参照してください。
(EXEファイルをダウンロードすると、たぶんセキュリティーチェックで引っかかると思いますが、信用できない方は、下記コードをご自身でコンパイルしてください。 )

たぶん Windows以外のOSでも、monoが入っていたら 上記 exe ファイルがそのまま動くんじゃないかなー。
[追記]
CentOS6 + mono で実行してみましたが、https通信でセキュリティ?かなんかの例外が発生してしまいました。
デフォルトでインストールしたまんまのmonoの場合はルート証明書をインポートする等しないといけないみたいですね。mozroots.exeをインストールして解決しました。下記ページを参考にさせていただきましたm(__)m。
~Monoで、Secure Socket Layer (SSL) / Transport Layer Security (TLS)通信を行うには~
http://qiita.com/takanemu/items/129acc13d8b7ce088b92

[追記ここまで]

これにて終了。

/*******************************************************************************

  saving image file on twitter.(getTweetImage.cs)

   build: Visual Studio 2015 Community.( also Express Edition) or Windows SDK
     IDE使うほどでもないので、コマンドラインツールでcsc.exeでバイトコンパイルしてください。
     >> csc.exe getTweetImage.cs

  howto: Console command
     >> getTweetImage.exe command target [username password]
     
     command  : ターゲットが"いいね"した画像 => 'favo' 
                ターゲットがアップした画像   => 'profile'
                ターゲットのTLの画像        => 'timeline'
     target   : ターゲットとなる取得したい画像のアカウント名 
     username : 自分のログインアカウント
     password : 自分のパスワード

     usernameとpasswordは、「いいね」画像の取得及び、
     ターゲットとなるアカウントが非公開アカの場合に必要かと思います。

*******************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Net;
using System.Linq;

namespace ptsv.jp
{
  public class ConsoleApplication1
    {
      private static Dictionary<string,string> TIMELINEURL = new Dictionary<string,string>
        {
          { "profile","https://twitter.com/i/profiles/show/{0}/media_timeline"},
          { "favo","https://twitter.com/{0}/likes/timeline"},
          { "timeline","https://twitter.com/i/profiles/show/{0}/timeline"}
        };

      /// <summary>
      /// スタートアップ
      /// </summary>
      /// <param name="args">コマンドライン引数</param>
      public static void Main(string [] args)
        {
          if(args.Length < 2)
            {
              Console.WriteLine("USAGE: getTweetImage.exe command target [username password]");
              Console.WriteLine("USAGE: command is 'favo' or 'profile'");
              Console.WriteLine("require username and password\n when command is 'favo',or target is private account.");
              return;
            }
          string command  = args[0];
          string target = args[1];
          string username_or_email = null;
          string password = null;

          if(args.Length == 4)
            {
               username_or_email = args[2];
               password = args[3];
            }

          if(command == "favo" && (string.IsNullOrEmpty(username_or_email) || string.IsNullOrEmpty(password)))
            {
               Console.WriteLine("when command is 'favo', username and password is required.");
               Console.WriteLine("USAGE: getTweetImage.exe command target [username password]");
               return;
            }

          string timeline_url = TIMELINEURL.ContainsKey(command) ? string.Format(TIMELINEURL[command],target) : null;

          var twitter = new TwitterDownload((type,v) =>
                                            {
                                              switch(type)
                                                {
                                                case "get-timeline":
                                                  Console.WriteLine("---- getting and parsing\n{0} ...\n----",v);
                                                  break;
                                                case "getting-image":
                                                  Console.WriteLine("fetching: {0}",v);
                                                  break;
                                                case "saved-image":
                                                  Console.WriteLine("saved {0}",v);
                                                  break;
                                                case "file-exists":
                                                  Console.WriteLine("{0} is already exists.",v);
                                                  break;
                                                case "fail-login":
                                                  Console.WriteLine("Auth code not found.","");
                                                  break;
                                                case "fail-auth":
                                                  Console.WriteLine("username or password were refused.","");
                                                  break;
                                                case "try-login":
                                                  Console.WriteLine("Trying to login by '{0}' for twitter.com", v);
                                                  break;
                                                }
                                            });

          if(!string.IsNullOrEmpty(timeline_url))
            {
              twitter.Agent(timeline_url,username_or_email,password);
            }
          else
            {
              Console.WriteLine("[INVALID COMMAND] command should be 'favo' or 'profile'");
            }
        }
    }

  /// <summary>
  /// 画像ダウンロード本体クラス
  /// </summary>
  public class TwitterDownload
    {
      /*------------------------------------------------------------------------------

        Const

      ------------------------------------------------------------------------------*/
      public const string URL_LOGIN    = "https://twitter.com/login";
      public const string URL_SESSIONS = "https://twitter.com/sessions";
      public const string USER_AGENT   = "Mozilla/5.0 (compatible; MSIE 11.0; Windows NT 6.0; Trident/5.0)"; //適当
      public const string REFERER      = "https://twitter.com/";
      private const string RE_MEDIA_URL = @"https://pbs\.twimg\.com/media/([\w\-]+\.\w{3,4})(:large)?";
      private const string RE_TWEET_ID  = @"data-tweet-id=\\""(\d+)\\""";
      private const string RE_AUTH_CODE = @"<input type=""hidden"" value=""([\d\w]+?)"" name=""authenticity_token""(?:\s*/)?>";

      /*------------------------------------------------------------------------------

        Instance

      ------------------------------------------------------------------------------*/
      private string OutputDirectory = Path.Combine(".", "tmp");
      private TwitterWebClient webClient;
      private Action<string,string> Callback;

      public TwitterDownload(Action<string,string> callback, string cookieFile)
        {
         this.Callback = callback;
         this.webClient =  new TwitterWebClient(cookieFile);
        }

      public TwitterDownload(Action<string,string> callback)
        {
          this.Callback = callback;
          this.webClient = new TwitterWebClient();
         }

      public string directory
        {
          set
            {
              this.OutputDirectory = value;
            }
          get
            {
              return this.OutputDirectory;
            }
        }

      public void Agent(string timelineURL,string username,string password)
        {
          if(String.IsNullOrEmpty(timelineURL))
            return;

          if(!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
            {
                if (!GetLogin(username, password))
                 {
                    Callback("fail-auth", "");
                    return;
                 }
            }

          if (!Directory.Exists(OutputDirectory))
            Directory.CreateDirectory(OutputDirectory);

          string param = "?include_available_features=1&include_entities=1";
          string url;
          do
            {
              url = timelineURL + param;
              Callback("get-timeline",url);

              string result = webClient.DownloadString(url);
              param = GetImage(result);

            } while(!string.IsNullOrEmpty(param));
        }

      private bool GetLogin(string username,string password)
        {
          Callback("try-login", username);
          webClient.Headers[HttpRequestHeader.UserAgent] = USER_AGENT;
          webClient.Headers[HttpRequestHeader.Referer] = REFERER;

          string loginResult = webClient.DownloadString(URL_LOGIN);
          Match authMatch = Regex.Match(loginResult,RE_AUTH_CODE);
          if(!authMatch.Success)
            return false;

          var param = new NameValueCollection();
          param.Add("session[username_or_email]",username);
          param.Add("session[password]",password);
          param.Add("authenticity_token",authMatch.Groups[1].Value);
          param.Add("remember_me","1");
          param.Add("redirect_after_login","/");

          return Encoding.UTF8.GetString(webClient.UploadValues(URL_SESSIONS,param)).IndexOf("error") < 0;
        }

      private string GetImage(string json)
        {
          json = json.Replace(@"\/", "/");

          foreach(Match match in Regex.Matches(json,RE_MEDIA_URL))
            {
              string url = match.Value;
              string basename = match.Groups[1].Value;
              string filename = Path.Combine(OutputDirectory,basename);

              url = string.IsNullOrEmpty(match.Groups[2].Value) ? url + ":orig" : url.Replace(":large",":orig");

              if(File.Exists(filename))
                {
                  Callback("file-exists",filename);
                }
              else
                {
                  Callback("getting-image",url);
                  webClient.DownloadFile(url,filename);
                  Callback("saved-image",filename);
                }
            }

          var ids = new List<string>();
          foreach(Match match in Regex.Matches(json,RE_TWEET_ID))
            {
              ids.Add(match.Groups[1].Value);
            }

          return ids.Count > 0 ? string.Format("?max_position={0}&include_available_features=1&include_entities=1",ids.Last()) : null;
        }
    }

  /// <summary>
  /// クッキーの読書に対応するためだけのWebClientクラス。
  /// </summary>
  internal class TwitterWebClient : WebClient
    {
      private CookieContainer cookieContainer = new CookieContainer();
      private bool isAutoRedirectEnabled = false;

      public TwitterWebClient() : this("") {}

      /// <summary>
      /// もしクッキーファイルが存在したらクッキーファイルを読み込んでCookieContainerに追加する。
      /// </summary>
      /// <param name="cookieFile">ブラウザからエクスポートしたクッキーファイル</param>
      public TwitterWebClient(string cookieFile)
        {
          if(File.Exists(cookieFile))
            {
              using(var sr = new StreamReader(cookieFile))
              {
                string line;
                var reCRLF = new Regex(@"[\r\n]$");
                var reEmpty = new Regex("^$");
                var reComment = new Regex("^#");
                var reSpace = new Regex(@"\s+");

                while ((line = sr.ReadLine()) != null) 
                  {
                    line = reCRLF.Replace(line,"");
                    if(reEmpty.Match(line).Success || reComment.Match(line).Success)
                      continue;
                    string[] ar =  reSpace.Split(line);

                    cookieContainer.Add(new Cookie(ar[5],ar[6],ar[2],ar[0]));
                  }
              }
            }
        }

      /// <summary>
      /// WebClient.GetWebRequestメソッドをオーバーライド
      /// </summary>
      protected override WebRequest GetWebRequest(Uri address)
        {
          WebRequest request = base.GetWebRequest(address);

          if (request is HttpWebRequest)
            {
              var httpWebRequest = request as HttpWebRequest;
              if(httpWebRequest != null)
                {
                  httpWebRequest.CookieContainer = this.cookieContainer;
                  httpWebRequest.AllowAutoRedirect = this.isAutoRedirectEnabled;
                }
            }

          return request;
        }
      /// <summary>
      /// 自動リダイレクトするか?
      /// </summary>
      public bool AutoRedirect
         {
            set { isAutoRedirectEnabled = value; }
            get { return isAutoRedirectEnabled; }
         }
    }

}

MDBファイルへのアクセス

備忘録です。

ちょっと前に Accessで作成しているシステムをWebシステムに置き換える案件の仕事をしている時に、MDBファイルを覗く(データ抽出)する必要があった。そのときに使っていたPCには、ランタイム版ではない、本物の? 32bit版のMicrosoft Accessがインストールされていたので問題は無かったのですが、今使っているPCはMicrosoft Officeは入っていない。

いや、たしか、OLEDB経由でMDBファイルは読めるはず・・・と思ってコントロールパネルからODBCドライバが入っているかどうか見てみたけど・・・残念・・・入ってなかった・・・。

ググったら、「Microsoft Access データベース エンジン 2010 再頒布可能コンポーネント(https://www.microsoft.com/ja-jp/download/details.aspx?id=13255)」をインストールすれば読める、ということで、64bit版のドライバを入れて、Visual Studio 2015 community のサーバーエクスプローラで確認。

で、一応32bit版も入れとくか・・・と思って、32bit版もダウンロードしてインストーラーを立ち上げたら、先に64bit版を削除しろ! と怒られた(^^;

ええい、/passive オプションをつけてバージョン・チェックなしでもう一度インストーラーを起動して、問答無用で入れてやりましたよ!32bit版はバージョン違いのものが既にインストールされてた。この辺よく分からん。

odbc64  odbc32

左が %WINDIR%\system32\odbcad32.exe右が%WINDIR%\syswow64\odbcad32.exe
32bit/64bitアプリケーションでちゃんと動くのかな?今度確認してみよ。

でも、これ、下記ような制限があるんだけど、意味が分からない。MDBファイルを読むために使っちゃダメなの? いいの? 分からん・・・

  1. Jet の全般的な代替としての使用。Jet の全般的な代替が必要な場合は、SQL Server Express Edition (英語版) が必要です。
  2. サーバー側アプリケーション内での Jet OLEDB プロバイダーとしての使用。
  3. 一般的なワード プロセッサ、スプレッドシート、またはデータベース管理システムとしての使用。 つまり、ファイル作成の手段としての使用。Microsoft Office または Office オートメーションを使うと、Microsoft Office でサポートされるファイルを作成することができます。
  4. システム サービスまたはサーバー側プログラム (コードがシステム アカウントの下で実行されるもの、複数のユーザー ID を同時に処理するもの、高度に再入可能で動作が不安定になるもの) による使用。これには、ユーザーがログインしていないときにタスク スケジューラーから実行されるプログラムや、ASP.NET などのサーバー側 Web アプリケーションから呼びだされるプログラム、COM+ サービスの元で実行される分散コンポーネントなどがあります。

そして、OneNoteは起動しなくなった・・・

標準でついてる、ストア版のアプリの方の話です。

つい、1週間前までは、Surface3 + Windows10 は機嫌良く動作していたのですが、この間からOneDrive上にある特定のノートと同期が全くされないようになりまして・・・。デスクトップ版のOneNote 2013は正常に使える。

なんだかんだ、しているうちに、タブレットモードでOneNoteをタップすると、延々とサインインに問題が発生しました、とかなんとかのメッセージが出て何もできなくなった(゚Д゚)
どうにもこうにもならなかったので、グーグル先生に問い合わせると、Powershellから一度OneNoteをアンインストールし、アプリストアから再度インストールすれば良い、という情報を得て、試してみました。

上手くいきました。。。のも、束の間、今度はOneNoteのアイコンをタップしても、全く反応なし。今度はメッセージもでません。再度アンインストールしてインストールしても一緒。

解決方法を探すと、一度のそのアカウントを削除して新たにアカウントを作れと・・・こんなん解決方法じゃねーよ。

仕方ないので、もうバンドルされているストア版のOneNoteを使うのを諦め、デスクトップ版のOneNote2013を使うことにした。

あーーーーー、もうなんかイライラする! Windows10アプリは完全に使えない。

さらにイライラするのが、バンドルされている動画プレーヤー。一時停止して、しばらくして再開させると100%落ちてしまう。なんじゃこりゃ。

Windows10アプリに、少しでも期待した、おいらがバカだった(゚Д゚)

タブレット的な使い方をしたい人はSurface3 + Windows10 の組み合わせは不幸になりますので、絶対止めましょう。素直にiPadで幸せになりましょう(笑)Office Mobileがタダで使えますしね。

amazonの商品ページから画像一覧を取得するブックマークレット

2017年1月18日 追記
アマゾンの画像を置いているサーバーのドメインが変わったみたい?
ドメイン名のところを修正しています(m´・ω・`)m ゴメン…


ひさしぶりにブックマークレット・ネタ。

会社でたまに、商品紹介の版下データをIllustratorで作成することがあるのですが・・・商品画像を集める作業は一苦労してました。あるときは、紙のカタログからスキャンしたり、PDFのページをリーダーで表示させた状態をスクリーンショット撮ったり(笑)、まぁ結構なんぎしてました。
でも最近は違います! アマゾンのサイトに行くと、ほぼ全ての画像が!!!(^^;;; ま、当たり前だけど、amazonに載ってある画像を勝手に版下データに流用するのはチョシャッケンを侵害!!! ってのは理解しているんですが、商品メーカーの販売会社の営業さんとかに問い合わせもらっても、いちいち対応がメンドクサイのか、アマゾンの画像を使えと(笑) 末端の下っ端なんてそんなもんですよ。

ってなわけで、ブラウザでいちいち右クリック・画像保存・・・とかいうルーチンは、パソコンが得意。

商品ページのHTMLコードをザラッと解析して(< オーバーだよ・・・)、ブックマークレットを作った。
だいたい、↓ こんな感じ。

/* 
amazonの任意の商品ページでF12キーでDevツールを出し、
コンソールにて以下をコピペして実行して試せます。
2017/1/18 画像サーバーのドメイン名をマッチするための正規表現を修正してます。
*/
(function($,undefined)
{
  var multi = {};
  var m = $.each($('body').html().match(/https?:\/\/images\-\w+\.ssl\-images\-amazon\.com\/images\/I\/(?:[\w%\-]+)(?:\._S[A-Z]\d+_)?\.jpg/g),
                function(i,v)
                {
                  var url = v.replace(/\._S[A-Z]\d+_/,'');
                  if(url in multi)
                    return;
                  multi[url] = null;
                });

  var urls = [];
  $.each(multi,
         function(url,v)
         {
           urls.push(url);
         });

  var len = urls.length;
  var doc = window.open("","image").document;
  doc.open("text/html");
  doc.writeln("<html><head><title>Imaging</title><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"></head><body><p><a href=\"javascript:window.close();\">ウィンドウを閉じる。</a></p><p>");
  for(var i=0;i<len;i++)
    {
      doc.writeln("<a target=\"_blank\" href=\"",urls[i],"\"><img width=\"100\" border=\"0\" src=\"",urls[i],"\"></a>");
    }
  doc.writeln("</p></body></html>");
  doc.close();

})(jQuery);

ただ、これだと、window.openメソッドを使っているので、ブラウザの設定によってはポップアップブロックされるかも。amazon.co.jpだけ例外設定してやればヨシ。

上のコードをコピペしてブラウザのデベロッパーツール>console にて確認してみてください。
ポップアップされたウィンドウまたはタブに画像がデデデと列挙されるはず・・・されなかったらごめんなさい。
画像は幅100ピクセルに縮小して表示されてますが、画像をクリックしてやると大きくなるはず・・・されなかったらごめんなさい。

wordpress.comでは javascript: なリンクは貼れないので、ミニファイしたコードを↓に書いときますので、適当なブックマークを作って、編集して、URLに、↓のコードをコピペして保存してください。

javascript:(function($,undefined){var multi={};var m=$.each($('body').html().match(/https?:\/\/images\-\w+\.ssl\-images\-amazon\.com\/images\/I\/(?:[\w%\-]+)(?:\._S[A-Z]\d+_)?\.jpg/g),function(i,v){var url=v.replace(/\._S[A-Z]\d+_/,'');if(url in multi)return;multi[url]=null});var urls=[];$.each(multi,function(url,v){urls.push(url)});var len=urls.length;var doc=window.open("","image").document;doc.open("text/html");doc.writeln("<html><head><title>Imaging</title><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"></head><body><p><a href=\"javascript:window.close();\">ウィンドウを閉じる。</a></p><p>");for(var i=0;i<len;i++){doc.writeln("<a target=\"_blank\" href=\"",urls[i],"\"><img width=\"100\" border=\"0\" src=\"",urls[i],"\"></a>")}doc.writeln("</p></body></html>");doc.close()})(jQuery);

使い方は・・・分かりますよね? amazonって、頻繁に表示方法を変えているので、たぶんそのうち使えなくなるかもですね。
あ、エッチな商品の画像を取っちゃだめですよ(笑) あくまでお仕事の一助に(^^;;;

ブックマークレットって何? どうやって登録するの? どうやって使うの? おいしいの? とかいうメンドクサイ人は・・・適当にググってください(_ _)