VBAとC#で対話的にプログラムを実行する

VBA、つらいですね。

VBAC#で標準入出力を介し、対話的にプログラムを実行する方法です。

やりたいこと

f:id:usagisagi:20190817132210p:plain

docxというところが闇が深いです。

可搬性を考慮してRegAsmは使用禁止です。最初はC#のdllをラップするC++のdllを作ろうとしましたが、C++はよくわからないので断念しました。

C#

文字列を返すだけの簡単なアプリケーションです。

using System;

class Program {
    static void Main(string[] args) {
        var s = "";
        while (s != "EOF") {
            s = Console.ReadLine();
            Console.WriteLine(s);
        }
    }
}

出力の種類をWindows アプリケーションとすることでコンソールウィンドウを表示せずにすみます。

f:id:usagisagi:20190817132312j:plain

VBA

標準入出力ですがWindows Script Object ModelWshShell.exec()から生まれるWshExecオブジェクトを介して操作できます。

Option Explicit

' 参照設定 -> Windows Script Object Model
Public Sub TestConsole()
    Dim path As String
    path = ThisDocument.path & "\sandbox.exe"  ' フルパス
    
    If Dir(path) = "" Then
        MsgBox path & "が見つかりません"
        Exit Sub
    End If
    
    path = """" + path + """"
    
    Dim wsh As IWshRuntimeLibrary.WshShell
    Set wsh = New IWshRuntimeLibrary.WshShell
       
    On Error GoTo finally
    
    Dim exec As IWshRuntimeLibrary.WshExec
    Set exec = wsh.exec(path)
    
    ' 標準入出力の処理
    exec.StdIn.WriteLine "test1"
    Debug.Print exec.StdOut.ReadLine 

    exec.StdIn.WriteLine "test1"
    Debug.Print exec.StdOut.ReadLine
    
    exec.StdIn.WriteLine "test1"
    Debug.Print exec.StdOut.ReadLine
    
    exec.StdIn.WriteLine "EOF"  
    
finally:
    Set exec = Nothing
    Set wsh = Nothing

End Sub

標準入出力とかよくわからない

AtCoderをやりましょう

ストレージ換えた時のGNU GRUBの設定

TL;DR

  1. 追加ドライブのUUIDをsudo blkidで調べる
  2. /etc/fstabで旧ドライブのUUIDを新ドライブのUUIDへ変更
  3. sudo update-grubで更新

背景

  • C: 中華SSD : win10Home + GNU GRUB
  • D: HDD : win10用データHDD
  • E: 日本SSD : Ubuntu18.04
  • F: HDD : バックアップ用

今積んでいる中華SSD(C: )が不安になったので、WDのSSDに換装する。

症状

Cドライブを新SSDにディスククローンし、Cドライブ外して新SSDGNU GRUBを起動したところ、Win10は起動するがUbuntuが起動しない。でもCドライブつけたままだと新SSDGNU GRUBUbuntuが起動する。

SSD -> Cドライブ -> Ubuntu付きEドライブの経路で起動しているのだろうか。でもこれだとCドライブを取り外すことができない。

やったこと

Cドライブ外してUbuntu起動すると、90sくらいa start job is running for dev-disk-by-...で止まる。ググったところ、どうやらGNU GRUBが新SSDのUUIDを認識していないのが原因らしい。

Ubuntuでa start job is running for dev-desk-by ...とかでて起動が遅い - くれなゐの雑記

Cドライブを取り付けてUbuntuに入った後、sudo blkidで新ドライブのSSIDを確認し/etc/fstabを新ドライブのUUIDへ編集する。

変更後sudo update-grubGNU GRUBの情報を更新しておしまい。

東日本大震災のときに仙台にいたとある大学院生の記録

被災~電気復活までの記録(原文ママ


14:46 三陸沖(牡鹿半島の東南東約130km付近)の深さ約24km,マグニチュード (Mw) 9.0.

自分のいた仙台市青葉区震度6強(?)直後 実験室で20mM リン酸バッファーを作っている途中に発生.以前地震4程度の地震が起きていたため暫くは甘く見ていた.様子が違うので実験室を飛び出したが逃げ道が防火扉で塞がれていた.防火扉を蹴破って院生室の扉をこじ開けて,机の下に飛び込む.みたところ本棚等が倒れ残念なことになってた.地震後 余震警戒のため外に飛び出す.もう笑うしかない.理学部棟の壁が剝がれおちてた.

15:35 全員の生存を確認.皆笑っていた.もう笑うしかない.手早く身の回りのもの,貴重品,英単語帳などを持ち再び脱出.エバポ一台がお亡くなりになったことを知る.残念.

16:25 帰宅許可が出る.外観上は皆無事.

16:45 帰宅.家はかなり滅茶苦茶.炊飯器死亡確認.NotePCは無事だがバッテリーが少なかった.

~日没 とりあえず掃除.寝られる場所を確保.水周りは食器が散乱していたので放置.水道はまだ出ていた.

18:00 残り少ないバッテリーを使い果たしとりあえず抜く.おかずはあずにゃん

19:00 日没したのでとりあえず大学へ行く.臨時避難所が体育館に開設される.飲み物,食べ物は少ないらしいので辞めておいた.というのは建前で,リア充たちがグループで石油ストーブを囲んでおり居場所が無かったので辞めておいた.

2,3日は絶食覚悟で,炊き出し出たら食べるつもりでいた. アースクエイクダイエット☆ミ

19:30 ローソンが暗闇の中営業していたが,長蛇の列を成しており何か取られたりパニックになったら不味いのでスルー.

20:00 大学の公衆電話から家族へ安否を伝える.

20:30 少し遠いところにいく.外観上は無事だが,基礎部分をみるとところどころ"もげて"ている.壁がはがれているところも.

22:30 大学病院に行く.ここだけ明りはついていて安心.

0:00 就寝

~5:00 起床.ここで携帯のバッテリーが切れる.

7:00頃? 大学近くのコンビニが開店したのでじゃがりこ買う.あとついでに梅酒2 Lを買う.これはふざけているわけじゃなくて真面目に考えて買った結果.甘いからカロリーあるし...酒飲みたいし...

8:00 おそうじかいし.

12:00 あれ・・・?なんか地震が起きる前より綺麗になったような・・・?

13:00 コンビニに放置してあった新聞を読んで初めて事態の重大さに気付く.こわい.

13:30 駅前近くまで行く.ドンキホーテの長蛇の列はまるでコミケの様.ならぶ気力はなかった.

16:00頃 スーパーが営業していたのでならんでスナック菓子を買う.

18:00 日没.炊き出しやってたので避難所に行ってみるが変な態度で断られてので別の避難所に.そっちは水も電気もなかったので家の方がいいと思い帰宅.

19:00 眠れない.ラジオから緊急地震速報が鳴りまくって寝られない.

0:00 アルコールと睡眠薬のカクテルで寝る.よいこは絶対にマネしないでね(テへぺロ☆ミ

5:00 起床.駅前にいくことに.

5:30 相当近くまで電気が復帰していることを確認.

6:00 アーケード街.電気はついておりみた目は繁華街だが誰もいない.まるでゴーストタウン.自販機は大量に飲料がストックされているようだが,売り切れておらず逆に不安に.アーケードの上部が割れていたり,ビルの壁がはがれていたりしているところもあった.

11:00 大学の生協でスナック菓子とソーラーチャージャー,電池を買う.飲料品,食品一人1000円まで.本は買い放題.

13:00 避難所に行くが,大学生の自主的(?)なボランティアの態度が妙にでかい.食料,飲料水の販売情報を執拗に聞かれたのでとりあえず答えておいた.新聞だけ読んで離脱.食料はあったけど食べる気がしなかった.

13:30 ソーラーチャージャー電池切れ.おいいいい.出力が弱過ぎてどうしようもない.ラジオ聞きながら不貞寝.

17:00 携帯の電池を入れに駅前に行ったが充電池忘れてしまいうぎぎ.服屋がなぜかやっていた.クレジットカードが使えます><.

18:30 日没したし睡眠薬と酒飲んで寝るかーと思ったら電気復活

Selenium WebDriverでWebページを印刷する方法

C#で書きますが、適宜自分の言語で読み替えてください。

やりたいこと

Webページ操作のエビデンス、つまりスクショがいる。だるいのでSeleniumで自動化したいが、pngではなくxpsが必要なので、Selenium スクショ機能が使えない。

テスト準備

C#ならNuGetでSelenium.WebDriverSelenium.Supportを入れ、Downloads - ChromeDriver - WebDriver for Chromeからchromedriver.exeをダウンロード、プロジェクトに追加し、プロパティでビルドするときに出力フォルダにコピーするようにする。

以下、テストするまでのコード。PrintPage()で印刷をかけることとする。

[TestClass]
public class PrintTest {
    [TestMethod]
    public void TestSearch() {
        var chromeOptions = new ChromeOptions();
        var chromeDriverService = ChromeDriverService.CreateDefaultService();
        IWebDriver driver
            = new ChromeDriver(chromeDriverService, chromeOptions);

        driver.Navigate().GoToUrl("http://www.google.com");

        IWebElement element = driver.FindElement(By.Name("q"));
        element.SendKeys("Hello selenium WebDriver");

        element.Submit();

        PrintPage(driver);
    }
}

SendKeys

ctrl + Pを押して……ってやる方法。あまりやりたくない。印刷ドライバ選択も面倒そう。

private static void PrintPage(IWebDriver driver) {
    var keys = new List<string> {
    "^(p)",  "{Tab}", "{Tab}", "{Enter}"};  // 実際はドライバ選択操作も入れる。

    keys.ForEach(s => {
        SendKeys.SendWait(s);
        Thread.Sleep(500); // これ入れないと操作がPCの処理を追い越してしまう。
    });
}

XPath(できない)

XPathを使う方法。実はchromeの印刷画面はDriverのWindowHandlesの末尾に入るらしい。このHandleを取得して操作する。XPathは要素を右クリックし、検証、ソースの選択行を右クリック、Copy, Copy XPathクリップボードにとれる。

private void PrintPage(IWebDriver driver, int loopSpan = 500) {
    var handles = driver.WindowHandles;

   // stringでwindowの識別子みたいなのが入る
    var oldLastTab = driver.WindowHandles.Last();

    var executor = (IJavaScriptExecutor)driver;
    // 印刷windowを開く
    executor.ExecuteScript("setTimeout(function(){window.print();}, 0);");

    // 新しいhandleを取得するだけ
    while(true) {
        Thread.Sleep(loopSpan);
        handles = driver.WindowHandles;
        var lastTab = driver.WindowHandles.Last();
        if (lastTab != oldLastTab) {
            driver.SwitchTo().Window(lastTab);
            break;
        }
    }
    
    // 実際はここでエラーが吐かれる
    driver.FindElement(By.XPath("//*[@id=\"button-strip\"]/paper-button[1]"));
    
    driver.SwitchTo().Window(oldLastTab);
}

上記で実行すると、「XPathがみつからない」というエラーが出て実行されない。なぜかというと、chromeの印刷画面ではShadow DOMなるものが使われており、配下の要素はXPathで取得できないらしい。

shadow DOM の使い方 - Web Components | MDN

実際ソースコードを見ると、確かにそれっぽいところが見つかる。

f:id:usagisagi:20190217110015p:plain

JS Pathをつかう

ではどうするかというと、JSPathなるものを使ってJava Script上で要素を選択しJava Script上でクリックする。chrome上ではCopy XPathのかわりにCopy JS Pathを選択することで、Java Scriptでの要素がコピーできる。

shadow DOM の使い方 - Web Components | MDN

// driver.FindElement(By.XPath("//*[@id=\"button-strip\"]/paper-button[1]"));

var jsPath = "document.querySelector('body > print-preview-app')" + 
  ".shadowRoot.querySelector('#sidebar > print-preview-header')" +
  ".shadowRoot.querySelector('#button-strip > paper-button.action-button').click();"
executor.ExecuteScript(jsPath);

これならドライバ選択もJava Scriptでできるので安定しそう。めでたしめでたし。

2/8

  • 五輪のPC回収がもうすぐ終わるらしい。回収されるPCの条件は割とゆるい(HDDいらない)らしいので、いらないディスプレイなりキーボードなりを出してやろうと思う。古い空気清浄機やディスプレイを廃棄したいモノである。扇風機は入らなそうだが、空気清浄機は入りそうだ。とりあえず明日は近所のヤマトにダンボールを買いに行こう。いつもダンボールは捨てるものなのに今回は買うとかこれいかに。

  • とりあえず回収の依頼をして退路を絶った。

  • そろそろセキュスペに本気出さないとやばい。ほとんど何もしてないぞい。でも、C#GUIのアプリケーションを作りたいと企んでいる。ただ、どうしたらよいか、フレームワークがあるのかすらわからない。自分の頭で作ろうとすればなにかしら作れるだろうが、無駄な努力、俗にいう車輪の再発明になりそうで怖い。WPFってのが良いのか?

ここが参考になりそうだ。 stackoverflow.com

  • crankyのRave-Slaveの2017や2018聞いてるけど買って本当に良かった。テンションが上がる。

あしたやること

  • ダンボールを買いに行って家電製品を詰める。
  • 洗濯をする
  • セキュスペの勉強をする。
  • 時間があれば温泉行きたい

2/8

  • 五輪のPC回収がもうすぐ終わるらしい。回収されるPCの条件は割とゆるい(HDDいらない)らしいので、いらないディスプレイなりキーボードなりを出してやろうと思う。古い空気清浄機やディスプレイを廃棄したいモノである。扇風機は入らなそうだが、空気清浄機は入りそうだ。とりあえず明日は近所のヤマトにダンボールを買いに行こう。いつもダンボールは捨てるものなのに今回は買うとかこれいかに。

  • そろそろセキュスペに本気出さないとやばい。ほとんど何もしてないぞい。でも、C#GUIのアプリケーションを作りたいと企んでいる。ただ、どうしたらよいか、フレームワークがあるのかすらわからない。自分の頭で作ろうとすればなにかしら作れるだろうが、無駄な努力、俗にいう車輪の再発明になりそうで怖い。WPFってのが良いのか?

  • crankyのRave-Slaveの2017や2018聞いてるけど買って本当に良かった。テンションが上がる。

2/6 ボツにした

・久しぶりにAccessをやったら頭がこんがらがった。俺はデータベーススペシャリスト保持者なのにテーブル設計できないのか。と思っていたけど、これはデータベーススペシャリスト云々の話じゃなくて、多分自分が計画性がなく、いきなりテーブル作成とかしているせい。

社内ツールでも少し複雑なものは、手癖で打たないでちゃんと要件を定義して、機能と画面を設計して、それからモノを作り始めなければならないな、と思いました。こういう計画性のなさが今までの経歴に如実に現れている。いきあたりばったり。(56分、1回書いたけどまとまらないので全部消した。)