月別アーカイブ: 2023年8月

サインイン 4 アプリからオンラインのOneDriveを表示

これまで「サインイン」と題して、オンラインのOneDriveへ、いかに楽してサインインするか・・・という内容の記事を3つ書いた。

それはアプリのOneDriveから、簡単にオンラインのOneDriveを表示する方法がわからなかったから。で、ことここに至ってようやく、その方法を発見。

結局、これまでの全ては、オンラインのOneDriveへ「いかに苦労してサインインするか」に変わった気が・・・。

【目次】

1.アプリからオンラインのOneDriveを表示
2.アカウントの切り替えも簡単
3.まとめ
4.お願いとお断り

1.アプリからオンラインのOneDriveを表示

なんで今まで気がつかなかったんだろう・・・。アプリのOneDriveの「フォルダーを開く」の右隣にオンラインのOneDriveを表示する「オンラインで表示」があった!

こんなコマンドがあったなんて・・・ちっとも気がつきませんでした。

さらに、アプリのOneDriveの「フォルダーを開く」で表示されるエクスプローラーの右上の「同期しています」をクリックすると表示されるサブメニューの右下にも「オンラインで表示」が存在!(ここのキャプションは、その時々の状況を伝えるほかの文字列「例:エラー」等になることもあるようだ)

ここにも「オンラインで表示」があった!

いずれもクリックすると、Webブラウザが起動してオンラインのOneDriveが表示される。

データ交換用のUSBメモリのようにオンラインのOneDriveを使用したい時は、このWebブラウザに表示されたオンラインのOneDriveへ、必要なデータをアップロードして、別のPCで同様にオンラインのOneDriveにサインインして、必要なデータをダウンロードすればいい。

オンラインのOneDriveへデータをアップロード

追記(20230829)

回線速度とは別に、使用するWebブラウザによりダウンロード速度に違いが生じることがあるのだろうか? 昨日、150MB程度のZipファイルをOneDriveからダウンロードしたのだが、Myプログラムから実行したそれは「あまりにも」遅く、耐え難かったので、ChromeからOneDriveに接続してダウンロードしてみたら「ものすごく」速かった・・・です。

遅かったのはTWebBroeserを使ったプログラムだったので、TEdgeBrowserに変更した新しいプログラムで試してみたら、ChromeでOneDriveに接続した場合と変わらないダウンロード速度で快適に作業できました!

2.アカウントの切り替えも簡単

個人のアカウントから、組織のアカウントへ(もちろん、その逆も)の切り替えも簡単でした。オンラインで表示したOneDrive画面右上のアカウントマネージャーをクリックして切り替えたいアカウントを選択するだけです。

アカウントの切り替え画面

3.まとめ

(1)アプリのOneDriveからオンライン表示への切り替えは走召簡単(泣)
(2)複数のアカウントがある場合、アカウントマネージャーで切り替え可能
(3)間違った思い込みは無駄な苦労の元。アプリの使い方をよく勉強しよう!

4.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

サインイン 3

追記(20230827 OneDriveアプリからオンライン表示へ切り替え)

無駄にプログラムなんか書く必要はありませんでした!

以下、『いかに苦労してOneDriveにサインインするか』という、上記サインイン 4に辿り着くまでの、長いながいまわり道の記録です。なので、お読みいただく価値がないことを最初に申し添えます。m(__)m

この記事は、アプリとして実行(タスクトレイに常駐)するOneDriveではなく、Web上のOneDriveへ直接データをアップロードし、別のPCでそのデータをダウンロードする、言わばデータ交換用USBメモリのようにOneDriveを使用する方法の一例です。PC内のOneDriveフォルダにあるデータと、クラウド上のOneDriveにあるデータの同期などは、まったく考慮しておりませんので、その点にはどうかご注意願います。

プロローグ

2023年8月のある日を境に、OneDriveの挙動が変わったことに気づいた。組織アカウントと個人アカウントの両方で同一ID(メールアドレス)を使用している場合、個人アカウントとしてサインインしようとすると、個人アカウント用のサインイン画面が新たに表示され、その都度、パスワードの入力が必須になったようだ・・・。

1.あれは夢だったのか・・・?
2.IDの入力を2回求められるようになった・・・
3.イロイロ調べてみた!
4.パスワードも自動入力!
5.画面の表示設定
6.まとめ
7.お願いとお断り

1.あれは夢だったのか・・・?

これまでに、過去の記事として「サインイン」、「サインイン 2」と題し、クラウド上のOneDriveへ、いかに「楽して入るか」ということについて自分なりに工夫した点のまとめの記事を(プログラミングの一つの区切りでもあったし)掲載した。

実行形式ファイルを配布していないから、動作の確認のしようがないじゃないか・・・という声が聞こえてくるような気もするけど、exeの配布が僕の目的ではなく、夢中になったことのプログラミング記録を残すことがこのBlogを書く目的なので、そこは悪しからずご了解ください(バグに満ちた?実行形式ファイルを配って、多くの方にご迷惑をおかけしたくないという思いも当然あります)。

「サインイン 2」で思った通りのプログラムが完成して、「使うぞー!」という段階に入った8月中旬、これまでと異なる挙動をWebブラウザが示すことに僕は気づいた。

サインイン2で作成した接続専用プログラムを起動し、IDを自動入力して「次へ」をクリックして「個人用アカウント」をクリックしてもOneDriveに素直に入れないのだ・・・。例外なく新しい画面でIDの入力を再度求められ、さらに毎回パスワードも入力しなければならない。

今まではこんなことなかったのに・・・
IDを入力するだけで入れた、あのOneDriveは夢だったのか・・・?

2.IDの入力を2回求められるようになった・・・

具体的にはどうなるのか、画面をつけて説明すると以下の通り。
FormCreate手続きの最後で、次のようにOneDriveのサインイン画面を呼び出して・・・IDとして利用しているメールアドレスを自動入力。

  //Navigate
  EdgeBrowser1.Navigate('https://onedrive.live.com/about/ja-jp/signin/');
IDとして利用するメールアドレスを入力して「次へ」をクリック(もちろん入力は自動化)

すると、次の画面が表示されて、これまでなら「個人用アカウント」をクリックするだけでOneDriveにサインインできた(過去にパスワードを入力してサインインに成功していればそのCookieが残っているから?)。

過去(90日間以内?)にサインインに成功していれば、パスワード入力は必要なかった・・・はず。
この画面が今回の問題の根本的な原因がここにあることを示唆している(気がする)。

ところが、2023年8月中旬(頃?)からは、「個人用アカウント」をクリックすると、なんと・・・(おそらく、ここでサインインするアカウントが「個人用アカウント」であることがユーザーによって確定されたということで、Microsoftさん的には、今度は安心して・・・再度、個人アカウント専用のOneDriveへのサインイン画面を表示して、サインインしてください・・・という意味なのだと思いますが)

サインインの最初の画面に戻ってしまう・・・ 感覚的には、ウソだろ、なんで? って感じ。

「戻ってしまう・・・」と書いたが、正確にはカーソルのキャレットの点滅位置(スクリーン座標)が異なっていることと、その下に「サインイン オプション」なる最初の画面にはない表示があることから気づいたのだが、(最初に表示されたのとは)「別」の(=個人用アカウントの?)サインイン画面が表示されるのだ(サインイン画面のURLも確認したが、当然最初のサインイン画面とは異なっている)。

IDとして利用するメールアドレスは、この段階では疑似クリック&貼り付け失敗に備えてクリップボード上に送ってあるから、キャレットの点滅を確認し、Ctrl+Vして入力欄に貼り付けて「次へ」をクリックすると、さらに、これまでは出てこなかったパスワードの入力画面が表示される・・・。

Cookieの存在など忘れたかのようだ

これまで利用していたはずのCookieは何処へ消えたのか・・・不思議に思いながら、パスワードを入力してサインインボタンをクリックすると、やっとOneDriveに入れる・・・。

しかも、最後に表示される画面で、「今後このメッセージを表示しない」をチェックして「はい」を選択(クリック)しても、このメッセージは毎回必ず表示される・・・つまり、個人用アカウントでサインインした場合は、「サインインの状態は維持されない」。

My 環境では、「はい」をクリックしても設定は維持されない

拝啓 マイクロソフト様

オレみたいな輩がいるから、こんな仕様になったんですか?

Webブラウザが見えないところでやってることなんて、何にもわかりません。わからないけど・・・

「ボクのお父さんは、桃太郎というやつに殺されました。」

あの手紙を読んだときと同じくらいショックでした。

悪いことをするつもりはまったくありません。

いつも使ってるID(=メールアドレス)で、

OneDriveに楽して入りたいだけなんです。

「それが大きな間違いだ・・・」と言われたら、素直に「はい」と言うしかありませんが・・・

3.イロイロ調べてみた!

(期待したことなど一度もないが)これまで通りの七転八倒の結末に、今回も大いに落ち込む。が、唯一の救いは「OneDriveにサインインできなくなったわけではない」という部分だ。設定が変わって、セキュリティがより厳しくなった・・・というか、組織アカウントと個人アカウントの区分がより厳密になった・・・と言えばいいのかな? この問題の全体像は、多分、僕には把握できないだろうけれど、とりあえず、僕がわかるところまでOneDriveへサインインする仕組みについて調べてみることにした。

その結果、いちばんわかりやすかったのが、こちらの記事。

『サインインの状態を維持しますか ?』のオン/オフをユーザーごとに制御する

https://itbeginner.tech/2020/07/25/keep-me-signed-in/

上記Webサイト様の記事によれば、「有効期限が切れるシナリオ以外に、サインイン画面が表示される代表的な例」は次の5つがあるとのこと。

  • ユーザーのパスワードが変更されている
  • サインインの際に prompt=login パラメーターが付与されている
  • 多要素認証 (MFA) を実施する必要がある
  • inPrivate モードのブラウザでサインインしている
  • ブラウザが Cookie を保存できない、送信できない … など。

https://itbeginner.tech/2020/07/25/keep-me-signed-in/より引用

パスワードは変更してないから、それ以外の4つのうち、個人用アカウントでサインインする場合には、どの理由が該当するのか(自分的には、組織アカウントと個人のアカウントの両方に登録されているIDが使用された場合に、どちらのアカウントでのサインインであるかを確定することがサインイン画面が2回表示される最大の目的だと思うのだけれど)、いずれにしても原因がはっきりわかっても、そこから先が独力では解決できそうにありません。

おそらく、組織アカウントと個人アカウントを明確に切り分けない限り、現段階でこの問題の解決策はないように思えてきました。

また、上記Webサイト様の記事では『Fiddler』(フィドラー?)というHTTPS通信を解析するソフトが紹介されており、記事を読んで(僕には絶対に結果を上手く扱えない・・・)と直感的に思ったけれど、取り敢えずLink先へ飛んでプログラムをダウンロードしてMy PCにインストール。動かしてみた結果が次の通り。

HTTPS通信の内容(My IDやPW)が表示されてる・・・ すごいー!!

『Fiddler』のインストールと使い方は、次の記事を参照して行いました!

HTTPS パケット キャプチャ ツール Fiddler のインストールから使用開始まで。

https://qiita.com/Shinya-Yamaguchi/items/37347ec532824c2dccad

で、せっかくインストールして動かしてみた『Fiddler』ですが、この『Fiddler』が表示してくれているHTTPS通信の内容を、Delphi の Object Pascal で書く OneDrive への接続プログラムで活用する方法がわかりません・・・。残念ながら僕には、現時点でそれだけのプログラミングスキルが・・・悔しいけれどありません。それをイチから学ぶには、とんでもない時間がかかりそうです・・・

もはやこれまで・・・
あきらめるしかないかぁ・・・

っと、思ったところで気がつきました!

何をあきらめるというのだろう?
Cookieを利用した形でのパスワード入力を回避できないなら、
パスワードも自前で暗号化して定義ファイルに保存しといて、
自動入力すればイイだけのことじゃないか・・・

サインイン画面が再び表示されたらCtrl+Vで、クリップボードにあるIDのデータを貼ればいいだけだし、さらに続けてパスワード入力が要求される場面があっても、僕のプログラム側で対応して、ID入力同様にパスワード入力を半自動化してしまえばいい。

負け惜しみじゃなく、すべてを手入力するよりか、はるかにラクだ!
貼り付けのショートカットだって、Ctrl+Vだけなら覚えて貰えるはず・・・
目の前に見えてるボタンのクリックなら、なんの問題もない。

要は、困った時のサポートと、「慣れ」だ。

OneDriveにサインインする「敷居」さえ、もっと低くできれば・・・
みんなに やさしい プログラムになる。

風邪などのウイルス性疾患全般に効く特効薬はないみたいだけれど、たとえウイルスは退治できなくでも、ウイルスの引き起こす様々な症状への対症療法ならたくさんある。

それと同じように、Windows Hello や Cookie を利用してパスワード入力そのものを回避するというような根本的な問題解決は(今の僕には)出来ないけれど、サインインのID入力画面が表示されたら、IDを自動入力、クリックで進めるところは素直にクリックして次へ進み、もしパスワード入力画面が表示されたら、そこでまたパスワードを半自動入力するという、いわば対症療法的な方法で少しでも「楽に」サインインする方法が実現できるよう頑張ればいい。それだって、IDやパスワードを毎回全部手入力するよりは、ずっとラクなはずだ・・・。

繰り返せば、やがて「慣れ」という免疫ができる・・・。

それに、実験してて気がついたんだけど、自動的にCookieが適用?されて、パスワード入力を求められない(パスワード入力の画面が表示されない)IDもあるようだ。

詳しくは書かないけど、サインイン後の画面そのものがIDによって・・・違う。

なんでIDにより、Webブラウザの挙動が異なるのか?

おそらく、僕がOneDriveへのサインイン時に、IDとして使用しているメールアドレスは、Office 365の申し込み時にもその登録に使用したから当然Azure ADアカウント(=職場や学校のアカウント:組織アカウント)になっていて、さらに、同じメールアドレスが昔のLive IDつながりのMicrosoftアカウント(=個人のアカウント)としても登録されているから、このアカウントの二重登録状態をなんとか解消したい(させたい)Microsoft社の意向があって、こういうことになっているんだと思うんだけど・・・。

そうか、組織アカウントなら・・・。

ただ、僕のように、個人のアカウントのメールアドレスをどうしても変更したくない場合は、どうしたらよいのだろう?

そのへんの違いと仕組みは、これからの成長課題としておいて・・・、今は、今の僕に出来るいちばんイイことをしよう!

4.パスワードも自動入力!

さっそく、次のようにGUIを修正。

ID=メールアドレス入力用のGUIはそのまま利用(前回、作成したもの)

上のGUIの「待ち時間:1500(ミリ秒)」ComboBoxの右隣りに下のGUIを追加。

パスワード入力用のGUIを追加

上のように、パスワードをマスクするには、次のように設定。

パスワードを入力させるとき、入力した文字が他人に見られないように*などを表示(現在は黒丸●が標準?)するには、PasswordCharプロパティに * を設定するだけでOK!

//Password入力用文字列に'*'を設定
Edit1.PasswordChar:='*';

//Password入力用文字列設定を解除(''で#0を囲まないこと!)
Edit1.PasswordChar := #0;

【注意】
Editコントロールのプロパティで直接指定する場合は、アスタリスクをシングルクオートで囲んで ‘*’ としないこと! 「プロパティ値が違います」と即エラーになる。

シングルクオート囲みなし、単に #0 or * を入力すればOK!

マスク解除のプロパティでの指定例

パスワードのマスクを、CheckBoxのチェックと連動させるのであれば・・・

「確認」チェックボックスのチェックに連動してマスク状態が変化する
procedure TForm1.chkPWClick(Sender: TObject);
begin
  if chkPW.Checked then
  begin
    EditPW.PasswordChar := #0;
  end else begin
    EditPW.PasswordChar := '*';
  end;
end;

【再掲:マウスカーソルの現在位置座標の取得方法】

座標チェックに☑すると、マウスカーソルの現在位置のスクリーン座標がリアルタイムで表示される。その方法は前回も示しましたが、次の通り。

マウスのカーソルが現在置かれている位置のスクリーン座標を取得してLabelに表示。

procedure TForm1.chkZahyoClick(Sender: TObject);
begin
  if chkZahyo.Checked then
  begin
    //Enabled
    Timer1.Enabled:=True;
  end else begin
    //Enabled
    Timer1.Enabled:=False;
    LabelXY.Caption:='[X座標, Y座標]';
  end;
end;

Timer1のOnTimerプロパティをダブルクリックして作成されたTimer1Timer手続きに次のコードを記述。これでほぼリアルタイムにカーソルの位置座標を取得して表示できる。

procedure TForm1.Timer1Timer(Sender: TObject);
var
  lh_Handle:  HWND;
  lpt_Pos:    TPoint;
  lrc_Rect:   TRect;
  lrg_Region: HRGN;
  li_Ret:     Integer;
begin
  if chkZahyo.Checked then
  begin
    //マウスカーソル位置をスクリーン座標で取得
    GetCursorPos(lpt_Pos);
    //自身のウィンドウリージョンを調べる
    lh_Handle := Self.Handle;

    //ウィンドウリージョン取得のため空のリージョンを作っておく
    lrg_Region := CreateRectRgn(0,0,0,0);
    try
      //ウィンドウリージョン取得
      li_Ret := GetWindowRgn(lh_Handle, lrg_Region);
      if (li_Ret <> ERROR) then begin
        //ウィンドウのRectを取得
        GetWindowRect(lh_Handle, lrc_Rect);
        //スクリーン座標からウィンドウの左上を原点とした座標に変換
        lpt_Pos.X := lpt_Pos.X - lrc_Rect.Left;
        lpt_Pos.Y := lpt_Pos.Y - lrc_Rect.Top;
        //ウィンドウリージョン内にマウスカーソルがあるかテスト
        if (PtInRegion(lrg_Region, lpt_Pos.X, lpt_Pos.Y)) then begin
          LabelXY.Caption:=Format('OK %d (%d-%d)', [li_Ret, lpt_Pos.X, lpt_Pos.Y]);
        end else begin
          LabelXY.Caption:=Format('NG %d (%d-%d)', [li_Ret, lpt_Pos.X, lpt_Pos.Y]);
        end;
      end else begin
        LabelXY.Caption:=Format('[X:%d, Y:%d]', [lpt_Pos.X, lpt_Pos.Y]);
      end;
    finally
      DeleteObject(lrg_Region);
    end;
  end;
end;

これでパスワード入力欄のスクリーン座標を取得・保存しておいて、実際はパスワード入力画面が表示されたら、「入力」ボタンをクリック(パスワード入力画面が表示されなければ、GUIそのものを表示する必要もないので、接続環境に合わせてGUIそのものの表示もON/OFFできるようにした)。もちろん、GUIの表示状態そのものを保存可能。

パスワードはクリップボードに送信せず、Editコントロールにマスクをかけて表示しておき、入力が必要であればボタンクリックで実行できるように設定。

入力ボタンをクリックすると指定座標位置へパスワードを送信

パスワードの半自動入力は、次のコードで実行(前回のID入力用コードを修正)。

procedure TForm1.btnCopyClick(Sender: TObject);
var
  dwFlags : DWORD;
  X,Y : Integer;
  LKeyByte : Byte;
begin

  boolInput:=False;

  //Information
  if chkInfo.Checked then
  begin
    if MessageDlg('パスワード入力画面が見えていて、入力欄は空欄ですか?', mtInformation, [mbYes, mbNo], 0) = mrYes then
    begin

      try

        //クリップボードを初期化
        Clipboard.Clear;
        //文字列をクリップボードへ
        Clipboard.AsText:=EditPW.Text;

        dwFlags:=MOUSEEVENTF_MOVE or MOUSEEVENTF_ABSOLUTE;
        X:=Trunc(StrToInt(EditPWX.Text)/Screen.Width*65537);
        Y:=Trunc(StrToInt(EditPWY.Text)/Screen.Height*65535);

        //移動
        Mouse_Event(dwFlags,X,Y,0,0);
        //クリック
        Mouse_Event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
        //Mouse_Event(MOUSEEVENTF_LEFTUP,0,0,0,0);
        Application.ProcessMessages;

        WaitTime(StrToInt(cmbWaitTime.Text));

        Mouse_Event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
        Mouse_Event(MOUSEEVENTF_LEFTUP,0,0,0,0);
        Application.ProcessMessages;

        // [Ctrl] + [V] のキー操作
        LKeyByte := Ord('V');
        keybd_event(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0), 0, 0);
        keybd_event(LKeyByte, MapVirtualKey(LKeyByte, 0), 0, 0);
        keybd_event(LKeyByte, MapVirtualKey(LKeyByte, 0), KEYEVENTF_KEYUP, 0);
        keybd_event(VK_CONTROL,MapVirtualKey(VK_CONTROL,0),KEYEVENTF_KEYUP, 0);

        boolInput:=True;


      except

        boolInput:=False;

      end;

      end else begin

        MessageDlg('パスワード入力画面を表示し、入力欄が空欄の状態で、再度実行してください。', mtInformation, [mbOk] , 0);

    end;

  end else begin

    try

      //クリップボードを初期化
      Clipboard.Clear;
      //文字列をクリップボードへ
      Clipboard.AsText:=EditPW.Text;

      dwFlags:=MOUSEEVENTF_MOVE or MOUSEEVENTF_ABSOLUTE;
      X:=Trunc(StrToInt(EditPWX.Text)/Screen.Width*65537);
      Y:=Trunc(StrToInt(EditPWY.Text)/Screen.Height*65535);

      //移動
      Mouse_Event(dwFlags,X,Y,0,0);
      //クリック
      Mouse_Event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
      //Mouse_Event(MOUSEEVENTF_LEFTUP,0,0,0,0);
      Application.ProcessMessages;

      WaitTime(StrToInt(cmbWaitTime.Text));

      Mouse_Event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
      Mouse_Event(MOUSEEVENTF_LEFTUP,0,0,0,0);
      Application.ProcessMessages;

      // [Ctrl] + [V] のキー操作
      LKeyByte := Ord('V');
      keybd_event(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0), 0, 0);
      keybd_event(LKeyByte, MapVirtualKey(LKeyByte, 0), 0, 0);
      keybd_event(LKeyByte, MapVirtualKey(LKeyByte, 0), KEYEVENTF_KEYUP,0);
      keybd_event(VK_CONTROL,MapVirtualKey(VK_CONTROL,0),KEYEVENTF_KEYUP,0);

      boolInput:=True;

    except

      boolInput:=False;

    end;
  end;
end;

5.画面の表示設定

IDのみの入力でOneDriveにサインイン可能な場合は、不要なGUIは表示せずに運用。

これが理想的画面

ID&パスワードの入力が必要な場合は、画面左上の設定ボタンをクリックしてGUIを表示。初回のみ、ID&パスワード入力欄のスクリーン座標を計測&保存して、次回以降は半自動入力でサインイン。

対症療法的&非理想的画面(GUIを活用してサインイン)

現実世界に追従するカタチでのプログラミングは、夢を追いかけて・・・ではなくて、正直、必要に追われて・・・って感じで、書いていて楽しくはないけど。

でも、もし、これが誰かの役に立つなら・・・

OneDriveが使えなくて、すごく困っている人の役に立つなら・・・

僕のしたことに、ほんの少しだけ

意味や価値を見出せる気がします。

そうだ・・・。今、思い出せた・・・。

プロが書いた、見た目も美しい、あらゆる要求に対応した高価なプログラムではなく、
こんな僕の書いた、みすぼらしい、しかも機能限定のプログラムがいいと・・・

二者択一の場面で、僕のプログラムを選んでくださる人がいることを。

うん。そうだ。
きみも言ってくれたね。

「生きていれば必ず前進できます。
 もっとよくなれるんです。

 ・・・

 お互い 夢の実現に向けて、自分らしく歩きましょう。」

今、信じなくて、いつ、信じるんだ。
1ミリでもかまわない。
前へ行くんだ。

僕に今できる、唯一、確かなことをするんだ・・・

6.まとめ

(1)OneDriveへのサインインはIDによりその挙動が異なる。
(2)対症療法的にプログラミングすれば半自動ログインはできる。
(3)全自動ログインには、Cookieの利用を含めたさらなる学習が必要。

7.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

Lubricate the clutch wire

クラッチワイヤーのメンテナンス

バイクのクラッチレバーの付け根部分のグリスの汚れがひどくなってきた。
今日は時間もあることだし、クラッチレバー周辺をきれいに掃除しようか・・・と思った時、もう長いことクラッチワイヤーに注油してなかったことを思い出した。

清掃と注油。どうせならいっぺんにやってしまった方がいい。

そう思ってネットで検索すると専用の工具(ワイヤーインジェクター)を使わずに、でもムダなくスマートに注油する方法を発見。さっそく、やってみた。

買ってきたのはコレ!

で、専用工具(ワイヤーインジェクター)の代わりに用意したモノは、コレ!
(こっちは仕事でよく使うので、家に買い置きがたくさんあった)

チャック付きで、密封できることがポイントだそうです。(やってみて思いました。確かに!)
袋の大きさは幅50mm、高さ70mm、厚み0.08mmで強度が高いタイプ。
材質はポリプロピレン(PP)。

で、ケーブルのタイコ部分がギリ通過するよう、ポリ袋の右下をカットします(赤い点線部分)。失敗したくなかったので、最初は小さめに切って、取り外したケーブルのタイコ部分の大きさに合うよう、後から少しずつ穴を大きくしました。

赤の点線部分を小さめにカット

以下、ゼファー400での作業例です。
(他のバイクの場合、クラッチワイヤーをレバーから取り外す手順が異なります)

クラッチワイヤーの先端部分のタイコをクラッチレバーから外すため、クラッチレバーを前側に押して、アジャスタを「5」にセットします(5◀の状態)。こうすると、クラッチレバーが後ろに下がって、ケーブルのタイコ部分が外せる状態になるようです。

アジャスタを「5」にセット。

アジャスタの現在の状態を記録しておきます。ケーブルの交換ではないので、後からこの数値にセットすれば外す前の(=現在の)状態に戻るはずです。半クラッチの感覚(=レバーの握り代)は、アタマというより左手が覚えていますから、この数値は重要です。

現在の状態は「6mm」

ロックナットを弛めて、アジャスタを右へねじ込んで(写真のような状態)、クラッチレバーとロックナットとアジャスタの切り欠き(溝)とが一直線になるようにして、ケーブルを(写真の状態で向かって左へ)引っ張ると、アジャスタからアウターワイヤーのキャップ部分がうまいこと抜けてくれました(これで抜けない場合は、エンジン右側上のロックナットを弛めるんだそうです)。

アジャスタの回転が渋いのは、汚れたグリスに混じった砂?を噛んでいるためでした。
パーツクリーナーとナイロンブラシで清掃したらクルクルよく回るようになりました。

ワイヤーを外し、清掃した段階で、ケーブルの状態をチェックしました。サビやケバ立ち等はなく、30年前に買った時のままの純正ケーブルですが・・・ まだまだ大丈夫のようです。

ポリ袋にあけた穴にワイヤーケーブルのタイコ部分を通して、アウターワイヤーのキャップ部分がちょっと袋に入ったところで、穴の周囲にガムテープを巻き、ポリ袋とアウターワイヤーのキャップ部分を一体化させます。後でポリ袋に入れる潤滑油がすべてケーブル内へ流れ込むように、アウターワイヤーのキャップ部分をポリ袋内へ深く挿入しすぎないことがポイントです。

キャップ部分は浅く(少しだけ)ポリ袋に入れます。

ガムテープは、しっかりきつめに、2重巻きしました。

ここから潤滑油が漏れないことを祈ります・・・

流れ出た潤滑油を受ける、使い古した布をエンジンの上に用意します。

ポリ袋に潤滑油を注入します(見た感じで2~3ccくらい?)。注入後はポリ袋のチャックをしっかり閉じます。購入してきた潤滑油は発泡性で、ポリ袋がどんどん膨らみ、このガスの圧力で注油がスムーズに行われました。

潤滑油がすべてケーブル内に入るよう、ポリ袋の傾きを調整しながら作業しました。

途中、ガスの圧力が高まり、ポリ袋がパンパンになってチャックが開いてしまうかと思われたので、片手でチャック部分を押さえ、密閉状態を保ちました。

この後、ポリ袋はさらに膨らんだのでチャック部分全体を押さえました。

およそ1分後、注入した潤滑油が反対側から出てきました。

潤滑油は、初めから茶色だったので、これはアウターケーブル内の汚れではありません。

またとないチャンスですから、クラッチレバーと、その周辺もキレイに清掃しました。パーツクリーナーを吹いて、古いグリスを洗浄液で洗い流し、クラッチワイヤーの通り道をきれいにしました。結果、バイクは確かにキレイになりましたが、古いグリス混じりの洗浄液の跳ね返りが大量に飛び散って、服のあちこちに黒いシミが・・・。

早く脱いで、洗剤に漬けて、なんとか処置しないと・・・
彼女に叱られる・・・

写真に撮るとあちこち塗装が剥げていて、あらためて古いバイクだなーと思います。
このバイクを買ったとき、僕はまだ22歳だった・・・。

新しいグリスを塗って、クラッチワイヤーを元通りにセットします。

30分程度で作業は完了しました。

作業後、クラッチは思ったほど軽くはならず、むしろ、操作感の違い・・・今までのなんとなく「モッサリ」した感じから、元気溌剌とした感じに・・・ の方が印象に残りました。

服は汚れたけど、おもしろかったー☆

まとめ

(1)クラッチワイヤーへの注油は専用工具がなくても行える。
(2)ポリ袋は厚めを使用、発泡性の潤滑油を注入すると効率よく作業できる。
(3)パーツクリーナーの洗浄液の跳ね返りを浴びてもよい服装で作業する。

お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

My z33 with 20 inch wheels

My Z33 Version ST

ガレージで眠ってた深リムの20インチホイールを、この夏、久しぶりに履いた。
丸2年履いてた純正の18インチ(後期型)に噛ましてたワイトレ・・・
言いたくない理由で壊しちゃったから、なんだけど・・・。

走れない道や、入れない店があっても、20インチは別格だ。
フロント235/30、リア255/30だから、スペーサーなしでツライチに決まる。
車高短なんて、もう死語なんだろうけれど、笑いたいヤツは笑え。
やっぱり、コレがいい。
もし、今、苦手なものがあるとすれば、それは、路面のキャッツアイだけだ。

ずっと気になっていたフロント側を、今までより1cm下げた。

車輪止めを噛まし、覚悟をきめてジャッキアップ。
タイヤを外し、ホイールハウスに潜り込んで(今、地震 来るなよって祈りながら)、
車高調のネジ部分をナイロンブラシでキレイに掃除して・・・
固く締まっているロアブラケットのロックシートを舐めないように弛めて、
車高を下げたい分だけ、そうミリ単位で、このロックシートを上げておく。
次に、その上側のスプリングシートを時計回りに回転させて、
一緒にまわるショック本体を、ロアブラケット内に落とし込んでいく・・・。

この作業をする度に、必ず、思う。

オレはバカなんじゃないか・・・

自己満足のための、わずか1cm。

センターコンソールには、スクリーンが2つ、縦に並ぶ。
ここから見える風景が、たまらなく好きだ。

ここから先は・・・ もし、よかったら、
リンク先の音源、聴きながら読んでもらえないかな?

うん。今は、もう、古いクルマさ・・・
欧米なら、NISSAN 350Z って呼ぶのかな・・・。
日本では、FAIRLADY.
そう、フェアレディ Z33.

カタログで初めて見た・・・、あの日から
恋焦がれた、コックピット。

バケットシートに身を沈めて・・・
ステアリングを握る時は、いつも瞳を閉じてしまう。

・・・

FAIRLADY・・・
君が見える気がするんだ・・・。

バイクを眺めてるシーンを別にすれば・・・
こんなに、ドキドキする、空間はここしかない。

そうさ・・・ 僕のいちばん、好きな場所。

出会った、あの日から、変わらない
バケットシートの香り・・・

My FAIRLADY

z33。

きみが機械だなんて、僕には思えない・・・

RZ34は見たこともないし、z34には興味がない。

z33が好きだ。

死ぬまで、大好きだ。

燃費も最高さ☆
3.5Lのエンジン、気持ちよく回して、これだけ走れば上等でしょう?

6MTだけど・・・
気持ちよく走れるのは、3速まで。

一般道で、VQ35DE エンジンのトルクを楽しむには、3速が限界じゃないかな・・・。

でも・・・、こんなに凄いエンジンが市販されてる、この国が・・・好きだ。
この国に生まれて、ほんとうに、よかった・・・。

グラマラスなリアビュー。マフラーは柿本改。

トランク部分の造形は、『美しい』の一言に尽きて。
このかたちに決まるまで、どれほどの葛藤があったことだろう・・・。

最高のデザインじゃないか。

きみの名は、フェアレディ

美しい お嬢さん

なんて、いい、響きなんだろう。

僕の・・・最後の1台。

My FAIRLADY

僕の z33。

約束だよ・・・

お互いの命、ある限り・・・

きみと、いつも。

そう、 いつまでも・・・

走ろう!

Flight Simulator

出会い

かつてIBM製のPCを使っていたことがあった。ThinkPad220ってマシンだ。DOS/Vなんて今はもう聞かなくなった言葉が雑誌の表紙を飾っていた頃、僕は一冊の本に出会った。

ちょっと破れたけど、その本は帯付きでまだ僕の本棚に・・・

インターネット黎明期で、高速な光回線なんて存在しなかったし、それどころかADSLだっておとぎの国の夢物語のように感じてた僕だ。もちろん、Amazonもまだなかった。

だから、たぶん・・・八重洲あたりで、この本に出会ったんじゃないかな?
プログラミング関連の書籍(主なターゲットは、はじめC言語とVB・後にDelphi)は、地方の書店には、ほとんどなかったから、新幹線を利用する機会がある度に、この3月末になくなった・・・あの巨大な「ブックセンター」のPC関連の書籍売り場へ、僕は必ず足を運んでたんだ。

この本「徹底活用ブック」と銘打つだけあって、ThinkPad220に関するありとあらゆる情報が掲載されている感があり、どこから読んでも面白い本だった。新幹線の車内で電源を確保する方法など「こんなことやって、ほんとに大丈夫なのか?」と思っちゃったりもしたけど、そのゲーム機としての利用案内で知ったのが「Microsoft Flight Simulator Version 4」

(こんなん、あるんだー!)

それが「Flight Simulator」なるモノと、僕の出会いだった。

FS2020

FS98、FS2004どちらも楽しく遊べた。FS2004はWindowsXP時代のソフトで、インストールディスクなしで動かすには fs9.exe そのものを入れ替えるという裏技も必要だったりしたけど、ヤフオクで「Microsoft Force FeedBack2」なるジョイスティックも入手。現実世界では絶対に実現できない「火酒」を片手に操縦桿を握るという楽しみも、僕はこのFS2004で覚えた・・・。

FS2004で十分満足したためか、2006年に発表(日本語版の発売は2007年)されたFSXを、僕は購入しなかった。

そして今年。以前の職場で使っていたが、さまざまな理由から今の勤務先ではちょっと使えない自作PC(デスクトップ機)をどう使うか、思い悩んで。ふと思い立ったのがゲーム専用機として使えないか・・・ということ。FS2020がリリースされたのは知っており、そのあまりにもリアルな画面に心を奪われたことも本当なんだけど(太陽光が当たって出来る計器の影まで再現されたリリース当時のCM映像は衝撃そのものだった)、快適な動作環境として要求されるハードウェアのスペックが高すぎて、ゲームをするためだけに高価な機材を購入するのは到底無理と、あきらめていたのだった(当時、このデスクトップ機は職場で仕事に使用しており、自宅で使えるグラボ搭載可能なデスクトップ機を僕は持っていなかった)。

職場が変わって・・・、デスクトップ機は不要になり、今、それが自宅にある!

あらためて、FS2020を快適に動作させるために要求される理想スペックを調べてみた。
(知りたかったのは「推奨」ではなく、その上を行く「理想」スペック

OS:Windows10 64bit 2019年11月のアップデート適用済み
ストレージ:SSD 150GB
CPU:Intel Core i7-9800X
メモリ:32GB
グラボ:GeForce RTX2080

5年前に自作したPCだから、Windows11を入れるのはちょっと厳しい。でも、FS2020はWindows10でも動く。CPUはCore i7-7700だから理想スペックには、ちょっと足りないけど・・・搭載メモリを32GBにして、それなりの性能のグラフィックボードを載せれば、ある程度快適に飛べるんじゃないか?と思えてきた。幸いにして、最近はほとんど高額な買い物をしなかったので、貯金も復活。グラボの購入資金はある・・・。

早速、近所のPCデポに行ってメモリを購入。16GBのDDR4 SDRAM2枚セットで価格は¥17,500。お店の外に出て空を見上げたら、ちょうどジェット機が月をかすめて飛んでた。いい風景だった。

ホンモノが飛んでる・・・

グラボはAmazonで「ASUS NVIDIA GeForce RTX 2080 搭載 トリプルファンモデル 8GB ROG-STRIX-RTX2080-O8G-GAMING」の中古品を¥42,600で入手。

予想以上に箱が巨大でびっくり。梱包も豪華。値段だけのことはあります・・・。

GEFORCE RTX 2080載せてみました!

がらーんとしてた筐体の中が急に狭くなった感じ。グラボの存在感ってすごいんですね!
※ 電源ユニット交換後の写真

初めてこのグラボを持った時は、そのデカさと重さに驚き、果たしてMy デスクトップ機の筐体に収まるものか、不安になったけど。案ずるよりナントカで、そぉーっと挿入してネジ止めしたら、ずっと前から「わたし、ここにいました☆」みたいなイイ感じに。

CPUの冷却ファンに埃がたまっていたので、いい機会だと思い、外して清掃。ついでに5年間ご無沙汰状態のCPUも拝ませていただきましたが、5年前はペタペタしてた熱伝導グリスが乾き切って「粉」になっているのを発見。あわててAmazonで「ARCTIC MX-4( スパチュラ付き 4グラム) – サーマルコンパウンドペースト」を購入。税込み¥900なり。

商品の説明には「高い耐久性: 金属とシリコンの熱化合物とは対照的に、MX-4は時間の経過とともに損傷しません。 一度付けると、再度付着させる必要はありません(少なくとも8年間は持ちます)。」と書いてあった。

マザーボードはH270 Pro4で、256GBのM.2 SSDを載せてある。使わないソフトを全部削って、入ってるのはほとんどOSだけにしたら、空き容量は170GBになった。FS2020を入れるには150GB必要とのこと(アップデートするとこの半分程度になるという情報もWebにあった)。なら、これでギリ足りるはずだ。追加でダウンロードしなければならないデータが100GB程度あるようだけど、ほとんど使ってない容量4TBのHDDを載せてるから、FS2020起動時にちょっと時間がかかることさえ我慢できれば、なんとかなるはずだ。

そうそう、いちばん大切な主役を忘れていました。

Microsoft Flight Simulator : プレミアムデラックス 日本語版 ¥18,073

日本語版が入手できるなら、それに越したことはないか・・・と。

これで必要なモノは、全部揃ったはず。M.2 SSDの容量空けて、グラボも載せた。いよいよFS2020のインストールだ。で、電源をON。すると見たこともないメッセージが表示された・・・。なんじゃコレは・・・

「グラフィックカード用のPCle電源ケーブルを接続してください。」ってコト?

おかしいなー。ちゃんと電源ケーブル、グラボに差し込んだケド・・・。

不思議に思いながらGoogle先生にお伺いをたてると、なんと「グラボに供給される電力が足りてないから起動しない」ことが原因だと判明。

PLEASE POWER DOWN AND CONNECT THE PCle POWER CABLE(S) FOR GRAPHICS CARD 対処方法

https://favorite-fashion.com/blog110/amp/

そう言えば、購入したグラボには電源ケーブルの差し込みコネクタが2つあった。僕の手持ちの550W電源から供給できるグラボ用の電源ケーブルのコネクタは見た所1つしかない。グラボ側のコネクタのどっちに接続すれば、いいのか? ちょっと悩んで、あの時は、説明書にあった通り、左側のコネクタを選んだんだ。だから、右側のコネクタは空いてる・・・。

資金にまだ余裕はある・・・。慌ててバイクのキーを握り、ガレージに飛び込んで、エンジンに火を入れて、そのままPCデポへ直行。750Wの電源ユニットを購入。¥15,000

付けたばかりのグラボをいったん外して・・・

どこに、どんなカタチのコネクタが刺さっていたか、スマホで撮影しながら、電源ユニットを交換。マザーボードその他への電源供給コネクタを全て繋ぎ変えたことを慎重に確認してから、グラボを再度装着。グラボ行きの電源供給コネクタを探すと思った通り2つある。これで、グラボの電源ケーブル差し込みコネクタも無事全部埋まった。

650Wでもイケそうだったけど、敢えて750Wをチョイス。電気料金のことは遠い未来で考えることにする。

で、電源ON。今度は無事起動した。さっそくFS2020をインストール。覚悟していたけど、実際に半日かかった。これでも早い方らしい。ちなみにMy インターネット環境での回線速度は・・・

そんなに遅くはないと思うんだけど・・・

実は、ここまでは前置きで、ここからが本題

なんで今回僕がコレを書いたかというと、これから書くことをネットの片隅に記録しておきたかったから。

僕が調べた範囲では、この情報はどこにも書かれていなかった気がする。

それは何かというと、FS2020が自動認識&自動設定してくれない古いジョイスティックの接続設定方法。これを間違えたために、ホントに操縦苦労しました☆

ってか、キーボードならスイスイ飛ばせるのに、ジョイスティックで飛ぼうとすると滑走路から飛び立った瞬間に機首がグングン上を向いて、あっという間に失速して墜落。

これが現実なら、ほんとに命がいくつあっても足りません。
☆ちなみに僕は数十回、お亡くなりになりました☆

それでも慣れとは恐ろしいもので、FS2004では「あり得ない」くらい、ものすごく慎重に操縦桿を引いて、かろうじて離陸できるところまでウデを上げ・・・。ただ、離陸には成功しても、その後がさらにいけません。どう頑張っても水平飛行ができない。急上昇したり、急下降したり、常に機首を上下に振りながら飛んでる感じで、もう、どうにも、こうにも、ならなくて・・・。泣。

古いジョイスティックだからかなー☆

そう思って、使用を中止し、USBのプラグを引っこ抜いて、それからしばらくはキーボードで操縦していたのですが・・・、FS2020の解説本の記事から不具合の真の原因は、コントロール オプションで行う軸の割り当て設定の誤りであることが判明。

ジョイスティックの接続設定はコントロール オプションから行える。
ほんとにFS2020の画面はリアル・・・ってか、ホンモノは見たことないけど。

ここで、本来ならジョイスティックL軸Xを「エルロン軸」、ジョイスティックL軸Yを「エレベーター軸」に割り当てなければならないのに、僕は間違えて、ジョイスティックL軸Yを「エレベーターを下げる(ピッチを下げる)」と「エレベーターを上げる(ピッチを上げる)」に割り当ててしまっていたのだ。

これが正しい設定
赤枠部分を拡大

これでジョイスティックでスイスイ飛べるようになりました。
これまでさんざん苦労したのは、いったい何だったのか・・・

接続設定の誤りを教えてくれたのが、こちらの本。

著者である田中さん、ほんとうにありがとうです。

この本の18ページに、ジョイスティックの軸の割り当てについての解説があり、そこを読んでいて初めて割り当ての誤りに気づきました。この本に出会えて本当によかった!

ホントに飛んでるみたい・・・

来年は、要求スペックは現行のものと同じで、起動時間をさらに短縮したFS2024がリリースされるとのこと。

「Microsoft Flight Simulator 2024」の詳細を公開。四季の移り変わりや,嵐,オーロラの自然現象に加えて,路上の交通状況までも再現

https://www.4gamer.net/games/714/G071481/20230627020/

すごく、楽しみです!

お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。

サインイン 2

追記(20230827 OneDriveアプリからオンライン表示へ切り替え)

無駄にプログラムなんか書く必要はありませんでした!

以下、『いかに苦労してOneDriveにサインインするか』という、上記サインイン 4に辿り着くまでの、長いながいまわり道の記録です。なので、お読みいただく価値がないことを最初に申し添えます。m(__)m

この記事は、アプリとして実行(タスクトレイに常駐)するOneDriveではなく、Web上のOneDriveへ直接データをアップロードし、別のPCでそのデータをダウンロードする、言わばデータ交換用USBメモリのようにOneDriveを使用する方法の一例です。PC内のOneDriveフォルダにあるデータと、クラウド上のOneDriveにあるデータの同期などは、まったく考慮しておりませんので、その点にはどうかご注意願います。

また、サインイン画面でのID(Microsoftアカウントに登録したメールアドレス)入力後のOneDriveの応答について、いろいろ調べたのですが、起こり得る個々の問題一つ一つについては、僕のプログラミングスキルでは到底対応できないと感じました。そこで、ID入力後にパスワード入力が必要になった場合の処理について、後日、自分なりのスーパー・ローレベル対応方法として「サインイン3」というタイトルで書きたいと思います。

コンピューターと同期する OneDrive フォルダーを選択する

クラウド上のOneDriveとPCのデータの同期については上のリンク先記事をご参照ください。

OneDriveのサインイン画面に、IDとして利用するメールアドレスを自動入力するプログラムを前回、作成した。目的通りのプログラムが出来たことは出来たが、WebView4Delphiコンポーネント(MITライセンス)のdemoフォルダにあったSampleをそのまま使わせてもらったため、「ID(メールアドレス)を自動入力する」以外にも様々な機能が実装(Sampleなんだから当然と言えば当然)されており、ただプログラムを起動するだけで、exeを置いたフォルダ内に容量がおよそ200MBくらいある「CustomCache」という名前のフォルダが出来てしまう。無視すればイイと言ってしまえば・・・それまでかもしれないけど・・・。

WebView4Delphi

https://github.com/salvadordf/WebView4Delphi

とりあえず、自分的には使わない機能をカットしようと思い、プログラムソースを読んでみたんだけれど・・・、コードどうしの関連がよくわからない。ヘタにいじって不具合とバグの山を築くより、「IDの自動入力」という初心に帰って、プログラムをイチから作り直した方がいい気がしてきた。

で、作ったのがコレ。

OneDriveのサインイン画面にID(メールアドレス)を自動入力する機能だけを搭載

【作成の手順】

1.TEdgeBrowserを使う
2.GUIの設計とプログラムコード
(1)GUIの設計
(2)VCLコントロールの表示/非表示を切り替え
(3)入力値の保存/読み込みと暗号化
(4)カーソル位置の座標を取得
(5)プログラムコードから指定位置をクリック
(6)ダウンロードフォルダを開く
(7)リソースにDLLを埋め込む
(8)操作方法の案内
3.まとめ
4.お願いとお断り

1.TEdgeBrowserを使う

Delphiで、Web コンテンツやローカルに置いたhtmlファイルの読み込みと表示を行うためのビジュアル コンポーネントには、TEdgeBrowser や TWebBrowser があるけれど、表示したいWebページがJavaScript のダイアログ ボックス、パネル、その他要素を使用しているとTWebBrowser では Web ページを正しく表示できないことがあるようだ。

正直に言うと、Edge には印刷その他の不具合でかつて悩まされた記憶があり、個人的にあまり良いイメージを持っていなかったので、新しい TEdgeBrowser コンポーネントではなく、古い TWebBrowser コンポーネントの方を使いたかった。だから、最初は、次のリンク先にあるような情報を参考にして、TWebBrowser コンポーネントで OneDrive のサインイン画面を表示するプログラムを書いてみたのだが・・・

Delphi / C++Builder Starter Edition の VCL で WebBrowser コンポーネントを使う

https://qiita.com/ht_deko/items/c69902d644ea03f61deb

上のリンク先記事のおしまいの部分でも述べられている通り、TWebBrowser コンポーネントを使って OneDrive のサインイン画面を表示するコードを書くと、結構盛大にスクリプトエラー発生のメッセージが表示される。

なんとかならないか・・・と思い、Google先生にお伺いをたてると、FMX版の TWebBrowser の記事ではあるが、本家本元embarcaderoさん提供のスクリプトエラー発生回避策を発見。

FMX.WebBrowser.TWebBrowser

https://docwiki.embarcadero.com/Libraries/Sydney/ja/FMX.WebBrowser.TWebBrowser

それによれば「この問題を回避するには、アプリケーションは、Internet Explorer の FEATURE_BROWSER_EMULATION 機能を使用して、Web ページを IE11 エッジ モードで表示しなければなりません。」と説明があり、具体的な回避策として「FormCreate イベント ハンドラで、TForm1.SetPermissions メソッドを呼び出す」方法がソースコード付きで掲載されていた。

早速、スクリプトエラー回避策なるそのコードをコピペして実行してみたが、ナニがよくないのか、スクリプトエラーは発生状況に変化は見られなかった。

上のリンク先ページでは、スクリプトエラー回避策コードの下に「メモ: レジストリに対するこれらの変更は、アプリケーションが開始する前に適宜行わなければなりません。最初にアプリケーションを開始した際には、それを一度閉じ、再度開始します。」という説明があるので、プログラム起動時にレジストリに対する変更を行って、いったんプログラムを終了し、再度実行すればOKなのか? とも思ったが、原因の究明に時間を割くより、新しいTEdgeBrowserコンポーネントでOneDriveのサインイン画面を表示する方法を試した方が賢い気がして、ここで方針を変更。素直にTEdgeBrowserコンポーネントを使うことにする。その際、参考にさせていただいた記事がこちら

TEdgeBrowserでWebView2を使う ~Delphiソースコード集

https://mam-mam.net/delphi/tedgebrowser.html

OSがWindows11であれば、動作に必要なMicrosoft WebView2 ランタイムは、既に入っているので、インストール不要とのこと。作成するプログラムを動かす予定のPCのOSはすべてWindows11なので、その点は心配ないが、いちおうReadme.txtファイルを用意して、OSがWindows10の場合にはMicrosoft WebView2 ランタイムのインストールが必要であることを案内した方がよさそうだ。

2.GUIの設計とプログラムコード

(1)GUIの設計

Delphiを起動し、「ファイル」→「新規作成」→「Windows VCLアプリケーション」と辿って、表示されたFormにPanelを3つ図のように配置する。

Panel1が階層構造的にはいちばん下にあり、AlignプロパティはalTopを指定。その上にPanel2及び 3 を乗せて、Panel2のAlignプロパティはalLeft、Panel3のAlignプロパティはalClientをそれぞれ指定する。このようにプロパティを設定しておけば、Formの大きさが変化しても、Panel2の大きさ(幅と高さ)は変わらず、Panel3の高さはそのままで幅がFormの大きさ(幅)に合わせて自動的にサイズが変化し、各VCLコントロールの位置はFormの左上を原点とした設計時の位置に固定されて表示される。

VCLコントロールの階層構造

この後、Panel2の上には「ダウンロードフォルダを開く」ボタン、Panel3の上には暗号化してiniファイルに保存する予定の「ID」入力用のEditその他のVCLコントロールを次のように配置する。

必要と思われる最小限度のVCLコントロールを使い勝手を考えながら配置する

(2)VCLコントロールの表示/非表示を切り替え

ID(メールアドレス)や、自動的にクリックする座標値はいったん設定・保存してしまえば、通常の使用の際には必要ないので、普段は非表示に設定。つまり、□設定チェックボックス以外のVisibleプロパティはすべてFalseを指定する。で、設定を☑したときだけ、表示されるように設定。

procedure TForm1.chkSettingClick(Sender: TObject);
begin
  if chkSetting.Checked then
  begin
    LabelID.Visible:=True;
    btnCopy.Visible:=True;
    btnCopy.Enabled:=True;
    Edit1.Visible:=True;
    LabelX.Visible:=True;
    EditX.Visible:=True;
    LabelY.Visible:=True;
    EditY.Visible:=True;
    btnSave.Visible:=True;
    chkZahyo.Visible:=True;
    LabelXY.Visible:=True;
    LabelWaitTime.Visible:=True;
    cmbWaitTime.Visible:=True;
  end else begin
    LabelID.Visible:=False;
    btnCopy.Visible:=False;
    Edit1.Visible:=False;
    LabelX.Visible:=False;
    EditX.Visible:=False;
    LabelY.Visible:=False;
    EditY.Visible:=False;
    btnSave.Visible:=False;
    chkZahyo.Visible:=False;
    LabelXY.Visible:=False;
    LabelWaitTime.Visible:=False;
    cmbWaitTime.Visible:=False;
  end;
end;

(3)入力値の保存/読み込みと暗号化

各VCLコントロールに入力された値は、必要な個所は暗号化してiniファイルに保存する。

uses
  System.IniFiles;

procedure TForm1.btnSaveClick(Sender: TObject);
var
  strID:string;
  Ini:TIniFile;
begin

  //入力の有無をCheck
  if Edit1.Text='' then
  begin
    MessageDlg('IDとして利用するメールアドレスを入力してください', mtInformation, [mbOk] , 0);
    Edit1.SetFocus;
    Exit;
  end;

  if (EditX.Text='') or (EditY.Text='') then
  begin
    if EditX.Text='' then
    begin
      MessageDlg('自動クリックするX座標を入力してください', mtInformation, [mbOk] , 0);
      EditX.SetFocus;
    end;
    if EditY.Text='' then
    begin
      MessageDlg('自動クリックするY座標を入力してください', mtInformation, [mbOk] , 0);
      EditY.SetFocus;
    end;
    Exit;
  end;

  if cmbWaitTime.Text='' then
  begin
    MessageDlg('カーソル移動の待機時間をミリ秒単位で入力してください', mtInformation, [mbOk] , 0);
    cmbWaitTime.SetFocus;
    Exit;
  end;

  //暗号化
  strID:=EDText(Edit1.Text, IntToStr(HashOf('XXXXXXXX')), True);

  //iniファイルに保存
  Ini := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
  try
    //保存
    Ini.WriteString('Section', 'ID', strID);
    Ini.WriteString('Section', 'IchiX', EditX.Text);
    Ini.WriteString('Section', 'IchiY', EditY.Text);
    Ini.WriteString('Section', 'WaitTime', cmbWaitTime.Text);
    //Userに通知
    MessageDlg('現在の設定を保存しました!', mtInformation, [mbOk] , 0);

    if not btnCopy.Enabled then btnCopy.Enabled:=True;

  finally
    Ini.Free;
  end;

end;

コードの中で使用しているEDText関数はテキスト暗号化の関数。

  private
    { Private 宣言 }
    //HashNameMBCS(Create hashed values from a Unicode string)
    //MBCS:Multibyte Character Set=マルチバイト文字セット
    function HashOf(const key: string): cardinal;

    //テキスト暗号化/復号化
    Function EDText(KeyStr,PassW:string; EncOrDec:Boolean):string;
    //KeyStr:平文 or 暗号化文のいずれかを指定
    //PassW:パスワード
    //EncOrDec:True -> Encode / False -> Decode

  public
    { Public 宣言 }
  end;

function TForm1.HashOf(const key: string): cardinal;
var
  I: integer;
begin
  Result := 0;
  for I := 1 to length(key) do
  begin
    Result := (Result shl 5) or (Result shr 27);
    Result := Result xor Cardinal(key[I]);
  end;
end;

function TForm1.EDText(KeyStr, PassW: string; EncOrDec: Boolean): string;
var
  {暗号化用変数}
  Source, Dest, Password:TStringBuilder;
  lpSource, lpPass:Integer;
  PassValue, SourceValue, EDValue:Word;
  {共用変数}
  //乱数の種
  Seed1,Seed2,Seed3:integer;
  //実数の一様乱数
  RandNum:Double;
  //秘密鍵Seed
  Seed:string;
  {復号化用変数}
  DecSource:string;
begin
  //1.シード値を準備
  // (1)Passwordを整数へ変換→シード値1へ代入
  Password := TStringBuilder.Create;
  //Seed1を初期化
  //Seed1:=0;
  try
    Password.Append(PassW);
    PassValue := 0;
    for lpPass := 0 to Password.Length - 1 do
    begin
      //パスワード→整数
      PassValue := PassValue + Word(Password.Chars[lpPass]);
    end;
    Seed1:=PassValue;
  finally
    Password.Free;
  end;

  // (2)パスワード文字列の長さを取得→シード値2へ代入
  Seed2:= ElementToCharLen(PassW,Length(PassW));

  // (3)シード値1とシード値2の排他的論理和を計算して、シード値3へ代入
  Seed3 := Seed1 xor Seed2;

  //2.実数の一様乱数を計算
  //---------------------------------------------------------------------------
  // 0より大きく1より小さい実数の一様乱数を発生する関数
  // B.A.Wichmann and I.D.Hill, Applied Statistics, 31, 1982, p.188 に基づく
  // Seed1-3に入れる初期値(整数)は16bit長(maxint=32767)で十分
  // Seed1-3には1から30000までの任意の整数値を準備する(0ではいけない)
  //---------------------------------------------------------------------------

  //Seed1:=171*Seed1 mod 30269 と同値
  Seed1:=(Seed1 mod 177)*171-(Seed1 div 177)* 2;
  if Seed1<0 then Seed1:=Seed1+30269;
  //Seed2:=172*Seed1 mod 30307 と同値
  Seed2:=(Seed2 mod 176)*172-(Seed2 div 176)* 35;
  if Seed2<0 then Seed2:=Seed2+30307;
  //Seed1:=170*Seed1 mod 30323 と同値
  Seed3:=(Seed3 mod 178)*170-(Seed3 div 178)* 63;
  if Seed3<0 then Seed3:=Seed3+30323;
  //See1-3それぞれの乱数を0<RandNum<1となるように
  //計算結果が0より大きく、1未満の実数に直し、和の小数部分をとる
  RandNum:=(Seed1/30269.0) + (Seed2/30307.0) + (Seed3/30323.0);
  while RandNum>=1 do RandNum:=RandNum-1;

  //3.秘密鍵を生成

  //整数の一様乱数の上限値を決めて、整数の一様乱数を生成し、
  //これに上で計算した実数の一様乱数を加えて秘密鍵を生成する
  //Seedが秘密鍵(文字列として利用)となる
  Seed:= FloatToStr(RandNum + trunc((Seed1+Seed2+Seed3)*RandNum));

  //4.暗号化 / 復号化
  if (EncOrDec) then
  begin
    //暗号化(Encode)
    Source := TStringBuilder.Create;
    Dest := TStringBuilder.Create;
    Password := TStringBuilder.Create;
    try
      Source.Append(KeyStr);
      //秘密鍵をセット
      Password.Append(Seed);
      lpPass := 0;
      //テキストのエンコード
      for lpSource := 0 to Source.Length - 1 do
      begin
        //パスワード→整数
        if Password.Length = 0 then
          PassValue := 0
        else begin
          PassValue := Word(Password.Chars[lpPass]);
          Inc(lpPass);
          if lpPass >= Password.Length then lpPass := 0;
        end;
        //テキスト→整数
        SourceValue := Word(Source.Chars[lpSource]);
        //XOR演算
        EDValue := PassValue xor SourceValue;
        //16進数文字列に変換
        Dest.Append(IntToHex(EDValue, 4));
        //処理結果を返り値にセット
        Result:=Dest.ToString;
      end;
    finally
      Password.Free;
      Dest.Free;
      Source.Free;
    end;
  end else begin
    //復号化(Decode)
    DecSource:=keyStr;
    Dest := TStringBuilder.Create;
    Password := TStringBuilder.Create;
    try
      //暗号化テキストのデコード
      Dest.Clear;
      Password.Clear;
      //秘密鍵をセット
      Password.Append(Seed);
      lpPass := 0;
      for lpSource := 1 to Length(DecSource) div 4 do
      begin
        SourceValue := StrToInt('$' + Copy(DecSource, (lpSource - 1) * 4 + 1, 4));
        if Password.Length = 0 then
          PassValue := 0
        else
        begin
          PassValue := Word(Password.Chars[lpPass]);
          Inc(lpPass);
          if lpPass >= Password.Length then lpPass := 0;
        end;
        EDValue := SourceValue xor PassValue;
        Dest.Append(Char(EDValue));
      end;
      //処理結果を返り値にセット
      Result:=Dest.ToString;
    finally
      Password.Free;
      Dest.Free;
    end;
  end;
end;

サインイン時にIDとして入力するメールアドレスは暗号化されてiniファイルに保存され、FormCreate時にこれを復号して、Editコントロールに表示する。

procedure TForm1.FormCreate(Sender: TObject);
var
  Ini: TIniFile;
  strID, strX, strY, strWaitTime: String;
  i:integer;
begin

  //Formを最大化して表示
  Form1.WindowState:=wsMaximized;

  //待ち時間の選択肢(100~3000ミリ秒を100ミリ秒単位で用意)
  for i := 1 to 30 do
  begin
    cmbWaitTime.Items.Add(IntToStr(i*100));
  end;

  //iniファイルの存在を確認
  if FileExists(ChangeFileExt(Application.ExeName, '.ini')) then
  begin
    //iniファイルからデータを読込み
    Ini := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
    try
      strID:=Ini.ReadString('Section', 'ID', '');
      strX:=Ini.ReadString('Section', 'IchiX', '580');
      strY:=Ini.ReadString('Section', 'IchiY', '420');
      strWaitTime:=Ini.ReadString('Section', 'WaitTime', '500');
    finally
      Ini.Free;
    end;
    //復号して表示
    Edit1.Text:=EDText(strID, IntToStr(HashOf('XXXXXXXX')), False);
    EditX.Text:=strX;
    EditY.Text:=strY;
    cmbWaitTime.Text:=strWaitTime;
  end;

  //Navigate
  EdgeBrowser1.Navigate('https://onedrive.live.com/about/ja-jp/signin/');

end;

(4)カーソル位置の座標を取得

マウスのカーソルが現在置かれている位置のスクリーン座標を取得してLabelに表示。

procedure TForm1.chkZahyoClick(Sender: TObject);
begin
  if chkZahyo.Checked then
  begin
    //Enabled
    Timer1.Enabled:=True;
  end else begin
    //Enabled
    Timer1.Enabled:=False;
    LabelXY.Caption:='[X座標, Y座標]';
  end;
end;

Timer1のOnTimerプロパティをダブルクリックして作成されたTimer1Timer手続きに次のコードを記述。これでほぼリアルタイムにカーソルの位置座標を取得して表示できる。

procedure TForm1.Timer1Timer(Sender: TObject);
var
  lh_Handle:  HWND;
  lpt_Pos:    TPoint;
  lrc_Rect:   TRect;
  lrg_Region: HRGN;
  li_Ret:     Integer;
begin
  if chkZahyo.Checked then
  begin
    //マウスカーソル位置をスクリーン座標で取得
    GetCursorPos(lpt_Pos);
    //自身のウィンドウリージョンを調べる
    lh_Handle := Self.Handle;

    //ウィンドウリージョン取得のため空のリージョンを作っておく
    lrg_Region := CreateRectRgn(0,0,0,0);
    try
      //ウィンドウリージョン取得
      li_Ret := GetWindowRgn(lh_Handle, lrg_Region);
      if (li_Ret <> ERROR) then begin
        //ウィンドウのRectを取得
        GetWindowRect(lh_Handle, lrc_Rect);
        //スクリーン座標からウィンドウの左上を原点とした座標に変換
        lpt_Pos.X := lpt_Pos.X - lrc_Rect.Left;
        lpt_Pos.Y := lpt_Pos.Y - lrc_Rect.Top;
        //ウィンドウリージョン内にマウスカーソルがあるかテスト
        if (PtInRegion(lrg_Region, lpt_Pos.X, lpt_Pos.Y)) then begin
          LabelXY.Caption:=Format('OK %d (%d-%d)', [li_Ret, lpt_Pos.X, lpt_Pos.Y]);
        end else begin
          LabelXY.Caption:=Format('NG %d (%d-%d)', [li_Ret, lpt_Pos.X, lpt_Pos.Y]);
        end;
      end else begin
        LabelXY.Caption:=Format('[X:%d, Y:%d]', [lpt_Pos.X, lpt_Pos.Y]);
      end;
    finally
      DeleteObject(lrg_Region);
    end;
  end;
end;

(5)プログラムコードから指定位置をクリック

前回作成したプログラムでいちばん、悩んだのがここ。最初はサインイン画面のウィンドウハンドルを取得して文字列を送信しようと思ったんだけれど・・・これがうまくいかない。その詳細は前回の記事を参照してください。

さんざん悩んで、ようやく思いついた方法がプログラムコードで画面上の任意の位置をクリックする方法。Formが完全に描画された段階で、指定位置のクリックと、その位置への文字列の入力を実行している。そのコードを再掲。

  private
    { Private 宣言 }

    //アドレス貼り付け実行の成否
    boolInput:boolean;

    fgWaitBreak : boolean;  //変数は「functionより先に定義」する

    //待ち関数  指定カウントが経過すれば True, 中断されたならば False
    function WaitTime(const t: integer): Boolean;

    //Formの表示終了イベントを取得
    procedure CMShowingChanged(var Msg:TMessage); message CM_SHOWINGCHANGED;


//待機関数
function TForm1.WaitTime(const t: integer): Boolean;
var
  Timeout: TDateTime;
begin
  //待ち関数  指定カウントが経過すれば True, 中断されたならば False
  fgWaitBreak := False;
  Timeout := Now + t/24/3600/1000;
  while (Now < Timeout)and not fgWaitBreak do begin
    Application.ProcessMessages;
    Sleep(1);
  end;
  Result := not fgWaitBreak;
end;


procedure TForm1.CMShowingChanged(var Msg: TMessage);
var
  dwFlags : DWORD;
  X,Y : Integer;
  LKeyByte : Byte;
begin
  inherited; {通常の CMShowingChagenedをまず実行}
  if Visible then
  begin

    Update; {完全に描画}

    if Edit1.Text='' then
    begin
      Edit1.SetFocus;
      Exit;
    end;

    if (EditX.Text='') or (EditY.Text='') then
    begin
      if EditX.Text='' then EditX.SetFocus;
      if EditY.Text='' then EditY.SetFocus;
      Exit;
    end;

    fgWaitBreak:=False;

    //さらに念のためちょっと待機
    WaitTime(StrToInt(cmbWaitTime.Text));

    dwFlags:=MOUSEEVENTF_MOVE or MOUSEEVENTF_ABSOLUTE;

    //クリック位置を取得
    X:=Trunc(StrToInt(EditX.Text)/Screen.Width*65537);
    Y:=Trunc(StrToInt(EditY.Text)/Screen.Height*65535);

    //移動
    Mouse_Event(dwFlags,X,Y,0,0);
    Application.ProcessMessages;

    //クリック
    Mouse_Event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
    Application.ProcessMessages;

    WaitTime(StrToInt(cmbWaitTime.Text));

    Mouse_Event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
    Application.ProcessMessages;
    Mouse_Event(MOUSEEVENTF_LEFTUP,0,0,0,0);

    //文字列を送信
    boolInput:=False;
    try

      //クリップボードを初期化
      Clipboard.Clear;

      //文字列をクリップボードへ
      Clipboard.AsText:=Edit1.Text;
      
      //[Ctrl] + [V] のキー操作
      LKeyByte := Ord('V');
      keybd_event(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0), 0, 0);
      keybd_event(LKeyByte,   MapVirtualKey(LKeyByte, 0),   0, 0);
      keybd_event(LKeyByte,   MapVirtualKey(LKeyByte, 0),   KEYEVENTF_KEYUP, 0);
      keybd_event(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0), KEYEVENTF_KEYUP, 0);
      //操作に成功
      boolInput:=True;
    except
      //操作に失敗
      boolInput:=False;
    end;

    //貼り付け操作に成功した場合は入力ボタンを操作不可に設定
    if boolInput then btnCopy.Enabled:=False;

  end;

end;

{入力ボタンのClick手続きは、確認メッセージの表示以外は上のコードとほとんど同じ}

また、予期せぬ事故を防止するため、プログラムの終了時にはクリップボードを空に(初期化)する。

procedure TForm1.FormDestroy(Sender: TObject);
begin
  //クリップボードを初期化
  Clipboard.Clear;
end;

(6)ダウンロードフォルダを開く

OneDriveからデータのダウンロードが無事終了すれば、次のようにダウンロードフォルダを開くリンク付きのWindowが表示されるから、特殊なフォルダである「ダウンロードフォルダを開く」ボタンは、別になくてもかまわない気もするけど。

このWindowは移動できない?

もしかしたら任意のタイミングで、それを開きたい時があるかもしれない。エクスプローラーを開けばいいじゃないかという意見は、ここでは聞かなかったことに。

ダウンロードフォルダを開くプログラムコード。

uses
  Vcl.Clipbrd, System.IniFiles, System.UITypes,
  Winapi.ShlObj, Winapi.KnownFolders, Winapi.ShellAPI;

procedure TForm1.btnOpenDLFolderClick(Sender: TObject);
var
  FolderID:TGUID;
  FolderPath:PChar;
  D_FolderPath, ExeFileName:string;
  LhInst:Cardinal;
begin
  FolderID:=StringToGUID('{374DE290-123F-4565-9164-39C4925E467B}');
  if SHGetKnownFolderPath(FolderID,0,0,FolderPath)= S_OK then
  begin
    D_FolderPath := FolderPath;
    //確認
    //ShowMessage(D_FolderPath);
    //ダウンロードフォルダを開く
    ExeFileName:= 'explorer.exe';
    LhInst:=ShellExecute(Handle, 'open', PChar(ExeFileName), PChar(D_FolderPath), nil, SW_SHOW);
    if LhInst <= 32 then
    begin
      MessageBox(Handle, '起動に失敗しました.', '情報', MB_ICONINFORMATION);
    end;
  end;
end;

(7)リソースにDLLを埋め込む

このプログラムの動作には「WebView2Loader.dll」が必須(WebView2Loader.dll は、アプリがデバイス上で WebView2 ランタイム (Microsoft Edge プレビュー チャネル) を見つけるのに役立つコンポーネントであるとのこと)。

WebView2 アプリを 1 つの実行可能ファイルとして配布する

https://learn.microsoft.com/ja-jp/microsoft-edge/webview2/how-to/static

このDLLがないと困るので、添付忘れを防止するため、リソースに埋め込んでおいて、プログラムの実行時にexeのある場所にその有無を確認し、なければリソースから生成するように設定。

メニューの「プロジェクト」→「リソースと画像」で埋め込むDLLを指定

で、FormCreate時に有無を確認、なければexeのある場所に生成。

procedure TForm1.FormCreate(Sender: TObject);
var
  Ini: TIniFile;
  strID, strX, strY, strWaitTime: String;
  i:integer;
  dllFileName:string;
begin

  //リソースからDLLを(なければ)生成
  //rijnファイルの位置を指定
  dllFileName:=ExtractFilePath(Application.ExeName)+'WebView2Loader.dll';
  //rijnファイルの存在を確認
  if not FileExists(dllFilename) then
  begin
    //リソースを再生
    with TResourceStream.Create(hInstance, 'Resource_1', RT_RCDATA) do
    begin
      try
        SaveToFile(dllFileName);
      finally
        Free;
      end;
    end;
  end;

  ・・・

end;

(8)操作方法の案内

この他に、画面最下部に設置したStatusBarに次のような案内を表示できるようにした。

操作方法の案内をStatusBarに表示(OKをクリックすると消える)
案内を表示する/しないはユーザーが選択して、その設定状態の保存も可能に

操作方法の案内の表示/非表示の切り替え。

procedure TForm1.chkInfoClick(Sender: TObject);
var
  strInfo:string;
  strWidth:integer;
begin
  if chkInfo.Checked then
  begin
    //表示する文字列
    strInfo:='ID(メールアドレス)が自動入力されないときは、Ctrl+V で入力できます!';
    strWidth:=StatusBar1.Canvas.TextWidth(strInfo);
    btnOK.Visible:=True;
    with btnOK do
    begin
      Parent:=StatusBar1;
      Left:=strWidth-20;
      Top:=1;
    end;
    //StatusBar1の設定(重要:このプロパティがFalseだとStatusBarにテキストが表示されない)
    StatusBar1.SimplePanel:=True;
    //Info
    StatusBar1.SimpleText:=strInfo;
  end else begin
    StatusBar1.SimpleText:='';
    btnOK.Visible:=False;
  end;
end;

案内を「表示する」が選ばれていた場合はFormCreate時に案内表示を出すよう設定。

procedure TForm1.FormCreate(Sender: TObject);
var
  Ini: TIniFile;
  strID, strX, strY, strWaitTime: String;
  i:integer;
  dllFileName:string;
  strWidth:Integer;
  strInfo:string;
  boolInfo:boolean;
begin

  if chkInfo.Checked then
  begin
    //表示する文字列
    strInfo:='ID(メールアドレス)が自動入力されないときは、Ctrl+V で入力できます!';
    strWidth:=StatusBar1.Canvas.TextWidth(strInfo);
    with btnOK do
    begin
      Parent:=StatusBar1;
      Left:=strWidth-20;
      Top:=1;
    end;
    //StatusBar1の設定(重要:このプロパティがFalseだとStatusBarにテキストが表示されない)
    StatusBar1.SimplePanel:=True;
    //Info
    StatusBar1.SimpleText:=strInfo;
  end;

  ・・・

  //iniファイルの存在を確認
  if FileExists(ChangeFileExt(Application.ExeName, '.ini')) then
  begin
    //iniファイルからデータを読込み
    Ini := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
    try
      strID:=Ini.ReadString('Section', 'ID', '');
      strX:=Ini.ReadString('Section', 'IchiX', '580');
      strY:=Ini.ReadString('Section', 'IchiY', '420');
      strWaitTime:=Ini.ReadString('Section', 'WaitTime', '500');
      boolInfo:=Ini.ReadBool('Section','Info',True);
    finally
      Ini.Free;
    end;
    //復号して表示
    Edit1.Text:=EDText(strID, IntToStr(HashOf('adminy')), False);
    EditX.Text:=strX;
    EditY.Text:=strY;
    cmbWaitTime.Text:=strWaitTime;
    chkInfo.Checked:=boolInfo;
  end;

  ・・・

end;

案内そのものを表示したくない場合は、ユーザーの自由意思でその設定も可能に。

procedure TForm1.btnSaveClick(Sender: TObject);
var
  strID:string;
  Ini:TIniFile;
begin

  //入力の有無をCheck
  ・・・

  //暗号化
  strID:=EDText(Edit1.Text, IntToStr(HashOf('adminy')), True);

  //iniファイルに保存
  Ini := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
  try
    //保存
    Ini.WriteString('Section', 'ID', strID);
    Ini.WriteString('Section', 'IchiX', EditX.Text);
    Ini.WriteString('Section', 'IchiY', EditY.Text);
    Ini.WriteString('Section', 'WaitTime', cmbWaitTime.Text);
    Ini.WriteBool('Section','Info',chkInfo.Checked);
    //Userに通知
    MessageDlg('現在の設定を保存しました!', mtInformation, [mbOk] , 0);

    if not btnCopy.Enabled then btnCopy.Enabled:=True;

  finally
    Ini.Free;
  end;

end;

3.まとめ

(1)TEdgeBrowserを使えばOneDriveのサインイン画面をエラーなしで表示できる。
(2)サインイン画面へのIDの入力はプログラムコードで実行可能。
(3)IDはクリップボードに送信しておき、Ctrl+Vでも貼り付け可能に設定。

4.お願いとお断り

このサイトの内容を利用される場合は、自己責任でお願いします。ここに記載した内容を利用した結果、利用者および第三者に損害が発生したとしても、このサイトの管理者は一切責任を負えません。予め、ご了承ください。