[GIMP][VB.NET] Classic Windows アプリ用のアイコン(.ico)を GIMP で作る

2015年10月24日


 Visual Studio でアイコンファイル(ico) の作り方が分からず、GIMP で作ったときのメモ。

  1. アイコンに必要な画像の確認
     まず、アイコン画像は 16px / 24px / 32px / 48px / 128px / 256px の画像があるらしいのですが、Windows 7 の Explorer で確認したところ 24px / 128px の画像は表示されませんでした。
  2. 画像の作成
     それぞれのレイヤーに各サイズのアイコン画像を作成します。
     すべてのサイズの画像を作成する必要はありません。表示したいサイズの画像がない場合は、ほかのサイズの画像を拡大縮小するして表示するようなので、48px のみか、48px + 32px を作成すれば、多くの場合十分ではないかと思います。
  3. ファイル名の指定
     拡張子を .ico にします。(「ファイルタイプを選択」で選ぼうとしても ico は出てこないので)
  4. 保存
     内容を確認して保存します。
  5. その他、画像を編集する際に使ったコマンドなど
    ・レイヤーウインドウの表示
    ウインドウ → 最近閉じたドック → レイヤー
    ・テキストを入力
    ツールボックス → テキスト
    ・範囲変更
    メニュー → 画像 → キャンバスを選択範囲にあわせる
    メニュー → 画像 → キャンバスサイズの変更
    メニュー → 画像 → 画像の拡大・縮小
    メニュー → レイヤー → レイヤーをキャンバスに合わせる
    ・透明化
    レイヤーウインドウ → レイヤー右クリック → アルファチャネルを追加
    ツールボックス → ファジー選択(透明にしたい部分を)
    メニュー → 色 → 色を透明度に
    ・レイヤーの統合
    レイヤーウインドウ → レイヤー右クリック → 可視レイヤーの統合
    ・指定形式でのファイルの保存
    メニュー → ファイル → 名前を付けて保存 → (拡張子はファイル名で指定すればそのファイル名で保存する)
    ・アイコンファイルの保存
    メニュー → ファイル → 名前を付けて保存→(拡張子を.icoに)
    ico の場合、レイヤーを別にするとサイズ別アイコンファイルとして組成される。
    ・塗り
    ツールボックス → 塗りつぶし
    ・二値化
    メニュー → 色 → しきい値
    ・レイヤー操作
    ・追加
    レイヤーウインドウ→新規レイヤー
    ・移動
    ツールボックス→移動

[VHDL] BASYS2 と開発環境のインストール

2015年10月24日

 FPGA (というか VHDL) で遊ぶために BASYS2 という教育ボードを手に入れたので、そのセットアップと、主要ツールの使い方についてメモ。

  • インストール手順
    1. 環境の準備

       Windows を使う場合であれば、Windows 7 の 32bit 版を用意したほうが無難なようです。(以後のバージョンや 64bit 版だと色々と問題があるとの噂…)

    2. Xilinx ISE WebPack のインストール

       以下のサイトから ISE をダウンロードしセットアップします。
       http://japan.xilinx.com/support/download/index.html/content/xilinx/ja/downloadNav/design-tools.html

       ISE Design Suite をダウンロードし、ライセンス選択時に WebPack を選択します。Licence ファイルは別途入手します。(ユーザー登録すると貰えます)
       インストール自体はウィザードに従えばひっかかりはないと思いますが、インストール時間はかなりかかります。また、インストールに必要なディスク容量が 15GB 程度必要なので注意します。

    3. Degilent ADEPT のインストール

       以下のサイトから、Adept 2.16.1 System をダウンロードしてインストールします。
       http://www.digilentinc.com/Products/Detail.cfm?NavPath=2,66,828&Prod=ADEPT2

       また、ついでに Adept 2.2.1 Utilities もダウンロードしてインストールします。(使わないかもしれませんが念のため)

  • プロジェクトの作成とダウンロード

     主に http://junkbox.wicurio.com/index.php?VHDL%E3%81%8B%E3%82%89%E4%BD%9C%E6%88%90 を参考にしました。
     以下で不足する情報は、上記リンクを確認してみてください。

    1. まず ISE を起動して、File -> New Project でプロジェクトを作ります。Name は適当に。Top Level Source Type は "HDL" で。Location はプロジェクトの保存場所なので任意のフォルダを設定します。
    2. Family を "Spartan 3E", Device を "XC3S250E", Package を "CP132" に設定。あと、VHDL Source Analysus Standard を "VHDL-200X" とします。
    3. "Finish" で。
    4. "xc3s250e-5cp 132" を右クリックして "New Source" を選択。
    5. VHDL モジュールを選択。ファイル名は適当に。
    6. 入出力ポートを設定します。今回は A~D を in, E~F をout に設定。(A~F は変数名なので任意でよい)
    7. "Finish" で。
    8. すると、ソースコードエディタに展開します。上記で設定したポート設定がコードに反映されています。
    9. hello world っぽいコードを書きます。(begin と end の間)
    10. "User Constraints" の "I/O pin planning (PlanAhead) - Post-Synthesis" を右クリックし、"Run" を選択します。
    11. "Yes" で。(hoge.vhd を保存します)
    12. Plan Ahead の起動が開始されます。
    13. "YES" で。(.ucf ファイルを作ります)
    14. Plan Ahead が起動していないように感じたら、タスクマネージャーで確認します。多分起動しているので我慢強く待ちます(汗
    15. Plan Ahead 起動中です。
    16. ポートの割り付けをします。site に指定する名称はボードのシルク印刷に記されています。
    17. ところが私の BASYS2 はなぜか M4 の印刷が複数あり、タクトスイッチのほうはシルク印刷通り M4 ですが、スライドスイッチのほうは M4 ではなく P11 なので注意します。

      本来のピンアサイン [リファレンスマニュアル] [リンク元のページ]

    18. Plan Ahead を閉じます。以下のダイアログが表示されたら "Save" で。
    19. ISE に戻り、"Genarate Program File" を右クリックし、"Process Properties" を選択します。
    20. "FPGA Start-Up Clock" を "JTAG Clock" に変更し、"OK" を押します。
    21. "Genarate Program File" を右クリックし、"Run" を選択します。
    22. エラーがなければ、プロジェクトのフォルダに .bit ファイルが作成されています。
    23. Digilint Adept を起動。この .bit ファイルを 選択しダウンロードします。

[PS1] Powershell でシリアル通信

2015年10月13日

 Powershell を使ったシリアル通信についてのメモ。
 Hyper Terminal 亡き今、ノンインストールでシリアル通信をする手段として。

PS > $com = New-Object System.IO.Ports.SerialPort "COM1",
>> 9600,
>> ([System.IO.Ports.Parity]::None),
>> 8,
>> ([System.IO.Ports.StopBits]::One)
>>
PS > $com.NewLine = [Char]0x0D + [Char]0x0A    # 終端はCR-LF
PS > $com.Open()
PS > $com.ReadLine()               # 受信
PS > $com.WriteLine("send text")   # 送信
PS > $com.Close()
PS > $com.Dispose()

 もっとも、上のコードでは受信はバックグラウンドでしないから、使い勝手はハイパーターミナルに劣りますが…
 逆にマクロっぽいものは作りやすいかもしれません。

[工業] ポジドライブと、プラスドライバーと、ネジの合いの確認方法

2015年10月6日

 最近(でもないらしいですが)、プラスねじに見せかけて実は規格が違う「ポジドライブ」というものが出回っているらしいです。主に海外で使われているらしく、輸入家具(イケアとか)のネジがこれとのこと。

 見た目ではほとんどわからないです。が、よく見るとプラスの溝とは別に、45°の部分に切り欠きのようなものを確認することができます。

 見た目プラスのネジなので、普通にプラスドライバーでも差し込めてしまいます。そして回せもするのですが、遊びが大きく、失敗するとネジ穴をナメてしまうので要注意です。というか、普通のプラスドライバーで回してはいけません(汗

 どのくらい遊びがあるかというと、ネジをドライバーに挿して、横方向に力を入れて傾けると、以下の写真くらい傾きます。これはドライバーとネジ穴が合っていない証拠です。

 ポジドライブ用のドライバーを差し込むと、軸と座面はほぼ直角になり、ここから力を入れてもこれ以上傾きません。

 ・・・

 ところで、プラスネジはよくネジをナメます。理由はほぼすべてで「合っていないドライバーで回した」です。

 よくありがちなのは、+2 のネジを +1 で回した、というケースです。
 小さいネジをみると、つい小さいドライバーで回しがちで、実際小さいドライバーでも回ったりするのですが、そういうことをしていると次第にネジ穴がナメっていきます。

 たとえば、以下のネジとドライバーは合っている例ですが (ともに +2)、ネジに対してドライバーが大きすぎるように見えたりします。

 ですが、プラスドライバーというのは使っているのは先端の数ミリの部分だけで、残りはフェイクです(汗
 この点が、ネジに対して小さいドライバーを選択する、原因となっている気がします。

 たとえば以下の2つのドライバーは、見た目は上側のドライバーが小さいネジ用に見えますが、同じネジ穴 (+2) 用のドライバーです。
 要するに、マイナスドライバーと違い、プラスドライバーはドライバーの軸の太さとネジ穴は関係がないので、注意する必要があります。

 ・・・

 ところで、普通のプラスドライバーは海外では「フィリップス」という規格らしいのですが、このフィリップス、国内モノと海外モノとで若干形状が異なります(汗

 以下の写真のように、海外製のものはネジ穴が深い(ドライバーの先端が鋭い)です。国内用はネジ穴が浅いです。

 このため、同じ +2 規格のドライバーでも海外製のドライバーで国内のネジを回そうとすると、先端の鋭い部分がネジ穴の底に先に当たってしまい、本来当たらなくてはならない部分に隙間ができてしまうため、ネシ穴をナメてしまいます。
 今はどうか知りませんが、ホームセンターで安売りのドライバーとかはこの手のものが混入しているかもしれないので注意が必要です。

 ・・・

 いずれにしても、ネジを回す前には、ドライバーが合っているかどうかをちゃんと確認するようにしましょう。
 確認の仕方は、上記のどのケースでも同じで以下です。

  1. ドライバーをネジに挿します。
  2. 挿した状態のまま、ドライバーを左右上下に軽い力で傾けます。
  3. ドライバーとネジが合っている場合、ほとんど傾きません。座面とドライバーは垂直の状況を保ちます。
  4. ドライバーとネジが合っていない場合は傾きます。この場合は違うドライバーで傾かないものを探します。

 個人的な経験では、ナメるネジは +2 のプラスネジが大多数です。なので、ベッセルあたりの信用できる国内品の +2 ドライバーを用意しておくと、ナメる確率が少なくなる気がします。

[機材] RS-232C のピンの名称と信号の方向

2015年9月11日

 よく忘れるのでメモ。

Direction   -------------------- 9Pin   ---------------------- 25pin    ------意味
   OUT←    SD  Send Data           3   TxD  Transmitted Data      2    送信
 →IN       RD  Received Data       2   RxD  Received Data         3    受信
   OUT←    ER  Equipment Ready     4   DTR  Data Terminal Ready  20    自局接続
 →IN       DR  Data set Ready      6   DSR  Data Set Ready        6    相手局接続
   OUT←    RS  Request to Send     7   RTS  Request To Send       4    送信要求
 →IN       CS  Clear to Send       8   CTS  Clear To Send         5    送信可能
 →IN       CD  Carrier Detect      1   DCD  Data Carrier Detect   8    搬送波検出
            SG  Signal Ground       5   GND  common GrouND         7    信号接地
 →IN       CI  Calling Indication  9   RI   Ring Indicator       22    着呼

[Oracle] 接続先を確認する方法 (Windowsの場合)

2015年9月11日

 データベースサーバーに接続してメンテナンスする場合を考える。
 正しく接続すれば目的のサーバーに接続される。しかし正しく接続されなかった場合は・・・?

 本来、ホスト名やデータベースサーバーの識別子などはユニークであるはずである。したがって、接続先を正しく記述していれば間違いなく目的のサーバーに接続できるはず。

 しかしそうでない場合がある(汗

 たとえば、hosts を使用していて hosts に登録している IP アドレスをテストのために別のサーバーのものにしていたとか。あるいは、とあるフォルダにある設定ファイルを読むはずが、Path の優先順などで別の設定ファイルを読んでしまっていたとか。
 こういう状況が発生すると、表示上は正しく接続しているように見えるのに、実際に接続しているのは別のサーバーだった・・・ということが発生する。

 正しく環境を設定していれば問題ないはずですが、本当に正しいのか?、と疑問に思う場合もあるので、そういう時どう確認したらいいのか、ちょっと考えてみました。

  1. 指定のポートに対する通信が、どこの IP アドレスと通信しているかを確認する

     指定のポートに対する通信を netstat を使って確認します。
     Oracle の場合、デフォルトではポート 1521 で通信しているので、以下のように記述します。

    C> netstat -on | find ":1521"
      TCP         10.7.28.1:1591         10.7.28.21:1521        ESTABLISHED     3264
    
  2. 通信しているプロセスを確認する。

     上記では、PID 3264 がポート 1521 の通信をしているという結果なので、そのプロセスが何であるかを確認します。
     (自分のマシンのプロセスです。接続相手のプロセスではありません)

    C> wmic process where "ProcessID=3264" get ExecutablePath, Name
    ExecutablePath                                       Name
    C:appclientPcproduct12.1.0client_1sqlplus.exe  sqlplus.exe
    
  3. sqlplus でサーバー側のホスト名/データベースサービス名(SID)が知りたい場合。

     SQLPlus を使用して、sys_context('USERENV','INSTANCE_NAME'), sys_context('USERENV','SERVER_HOST') を表示します。

    SQL> select sys_context('USERENV','INSTANCE_NAME') from dual;
    
    SYS_CONTEXT('USERENV','INSTANCE_NAME')
    --------------------------------------------------------------------
    orcl
    
    SQL> select sys_context('USERENV','SERVER_HOST') from dual;
    
    SYS_CONTEXT('USERENV','SERVER_HOST')
    --------------------------------------------------------------------
    oracleserver
    
  4. sqlplus でサーバー側の IP アドレスを知りたい場合。

     SQLPlus を使用して、UTL_INADDR.GET_HOST_NAME, UTL_INADDR.GET_HOST_ADDRESS を表示します。
     しかし、権限がないと失敗するので、権限がない場合はあきらめます(汗

    SQL> set serveroutput on
    SQL> begin
      2    dbms_output.put_line('Host: ' || UTL_INADDR.GET_HOST_NAME); 
      3    dbms_output.put_line('IP  : ' || UTL_INADDR.GET_HOST_ADDRESS);
      4  end;
      5  /
    Host: ORACLESERVER
    IP  : 192.168.0.1
    
  5. 上記で IPv6 アドレスが表示されてしまう。IPv4 アドレスで得たい。

     無理です。諦めましょう (汗

     IPv6 が表示されるのは IPv6 が IPv4 より優先されるように設定されているからです。
     優先順は以下のコマンドで確認できます。(サーバー側で実施)

    C> netsh interface ipv6 show prefixpolicies
    優先順位   ラベル  プレフィックス
    ----------  -----  --------------------------------
            50      0  ::1/128                ← IPv6 ループバック
            40      1  ::/0                   ← IPv6
            35      4  ::ffff:0:0/96          ← IPv4
            (以下略)
    

     IPv4 を最優先に設定すれば、IPv4 アドレスが表示されるようになります。
     上記の例だと、以下のコマンドを実行して、IPv4 が一番上に来るようにします。

    C> netsh interface ipv6 set prefixpolicy ::ffff:0:0/96 51 4
    OK
    
    C> netsh interface ipv6 show prefixpolicies
    優先順位   ラベル  プレフィックス
    ----------  -----  --------------------------------
            51      4  ::ffff:0:0/96          ← IPv4 を一番上にする
            50      0  ::1/128
            40      1  ::/0
            (以下略)
    

     これを事前にサーバー側で実施していれば、IPv4 アドレスで表示されます。

     また、デフォルトに戻したい場合は、以下を実行します。(要再起動)

    C> netsh interface ipv6 reset
    

[PS1] Powershell の配列

2015年9月10日

 Powershell の配列でハマったのでメモ。

  1. 配列を作る。

     こんな感じで , (カンマ)演算子を使うことで配列が生成できます。

    ps> $a = 1, 2, 3
    
    ps> Write-Host "Len:" $a.Length "   Type:" $a.GetType().Name
    Len: 3    Type: Object[]
    
  2. 要素 1 の配列を作る。

     上記だと、要素 2 以上の配列は作成できますが、要素 1 の配列が作成できません。
     要素 1 の配列を作るには、@(~) を使用します

    ps> $a = @(1)
    
    ps> Write-Host "Len:" $a.Length "   Type:" $a.GetType().Name    # 確認
    Len: 1    Type: Object[]
    

     また、@() とすれば要素 0 の配列を作れます。

    ps> $a = @()
    
    ps> Write-Host "Len:" $a.Length "   Type:" $a.GetType().Name    # 確認
    Len: 0    Type: Object[]
    
  3. 配列の配列(ジャグ配列)を作る。

     括弧で括ってやれば、そこを一つの配列として構成してくれます。

    ps> $a = (1, 2), 3, (4, 5, 6)
    
    ps> Write-Host "Len:" $a.Length "   Type:" $a.GetType().Name    # 確認
    Len: 3    Type: Object[]
    
    ps> $a | % {Write-Host "Len:" $_.Length "   Type:" $_.GetType().Name}    # 確認
    Len: 2    Type: Object[]
    Len: 1    Type: Int32
    Len: 3    Type: Object[]
    
  4. 要素 1 の配列に、配列を入れたい。

     今回のメインテーマ。以下はダメです。

    # @() を重ねる
    ps> $a = @(@(1, 2))
    ps> Write-Host "Len:" $a.Length "   Type:" $a.GetType().Name    # 確認
    Len: 2    Type: Object[]
    
    
    # 配列を作ってから、@() で括る
    ps> $b = 1, 2;  $a = @($b)
    
    ps> Write-Host "Len:" $a.Length "   Type:" $a.GetType().Name    # 確認
    Len: 2    Type: Object[]
    
    
    # 空配列を作ってから、配列を足す
    ps> $a = @();  $a += 1,2
    
    ps> Write-Host "Len:" $a.Length "   Type:" $a.GetType().Name    # 確認
    Len: 2    Type: Object[]
    

     しかし、これはできる!

    # 一旦、要素 1 の配列を作ってから、その中身を入れ替える
    ps> $a = @(1); $a[0] = 1, 2
    
    ps> Write-Host "Len:" $a.Length "   Type:" $a.GetType().Name    # 確認
    Len: 1    Type: Object[]
    
    ps> $a | % {Write-Host "Len:" $_.Length "   Type:" $_.GetType().Name}    # 確認
    Len: 2    Type: Object[]
    

     でも、かっこ悪い(汗 ・・・ので Powershell v3 以降は , (カンマ)単項演算子で対処できる模様です。

    ps> $a = ,(1, 2)
    
    ps> Write-Host "Len:" $a.Length "   Type:" $a.GetType().Name    # 確認
    Len: 1    Type: Object[]
    
    ps> $a | % {Write-Host "Len:" $_.Length "   Type:" $_.GetType().Name}    # 確認
    Len: 2    Type: Object[]
    

     括弧を省略すると、意図しないところが配列になる可能性があるので気をつけるようにします。(冷静に考えたら当然そうなる…)

    ps> $a = ,1, 2
    
    ps> Write-Host "Len:" $a.Length "   Type:" $a.GetType().Name    # 確認
    Len: 2    Type: Object[]
    
    ps> $a | % {Write-Host "Len:" $_.Length "   Type:" $_.GetType().Name}    # 確認
    Len: 1    Type: Object[]
    Len: 1    Type: Int32
    
  5. 要素 1 に配列が入れられると、何の役に立つか?

     これが何の役に立つかと言うと、配列の引数を一つだけとるコンストラクタを呼びたいときなどに使えます。
     たとえば、string(char[]) を呼びたいとき、配列を素直に渡すと失敗します。

    ps> $a = [Char]0x41, [Char]0x42
    ps> New-Object string $a
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    

     これは、string(char, int) が呼ばれた結果で、Powershell は配列を展開してから引数に渡してくれるのでした(汗 #余計なお世話

     このため、配列の引数を一つだけ取るコンストラクタを呼びたい場合は、この展開を考慮して「配列の配列」で渡してやる必要があるようです。

    ps> $a = ,([Char]0x41, [Char]0x42)
    ps> New-Object string $a
    AB
    

     また、これをワンライナーで書こうとした場合、以下のように書くと失敗します。

    ps> New-Object string ,([Char]0x41, [Char]0x42)
    New-Object : パラメーター 'ComObject' で必要とされる型 'System.String' に 'System.Object[]' を変換できません。指定されたメソッドはサポートされていません。
    発生場所 行:1 文字:12
    + New-Object string ,([Char]0x41, [Char]0x42)
    +            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidArgument: (:) [New-Object]、ParameterBindingException
        + FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.NewObjectCommand
    

     エラーメッセージの言っていることがよく分かりませんが(ぇ)、前後に括弧を入れてやることで対策できます。

    ps> New-Object string (,([Char]0x41, [Char]0x42))
    AB
    
    ps> $a = [Char]0x41, [Char]0x42
    ps> New-Object string (,$a)
    AB
    

[PS1] String と Char 配列の相互変換など

2015年8月29日

 Powershell で String と Char 配列の相互変換、Char と文字コードの相互変換に苦労したのでメモ。

  • String から Char 配列にする

     ToCharArray メソッドを使う。

    PS> $a = "abcde"
    PS> $b = $a.ToCharArray()
    PS> $b.GetType().Name
    Char[]
    PS> $b
    a
    b
    c
    d
    e
    
  • Char 配列から String にする
    1. -Join を使う

       -join の左辺には何も書かないのがポイント。

      PS> $a = [Char[]](0x41, 0x42, 0x43, 0x44, 0x45)
      PS> $b = -join $a
      PS> $b
      ABCDE
      
    2. New-Object string を使う

       string コンストラクタの第一引数に何も指定しないかのように書くのがポイント。(*1)

      PS> $a = [Char[]](0x41, 0x42, 0x43, 0x44, 0x45)
      PS> $c = New-Object string (,$a)
      PS> $c
      ABCDE
      

      (*1)
       正確には () はコンストラクタの括弧ではないし、 , (カンマ)も演算子でコンストラクタの第一引数を省略しているわけではないです。詳しくは [PS1] Powershell の配列 の 4 項, 5 項 を参照。

  • 文字コードを Char にする

     Char でキャストする

    PS> $a = [Char]0x41
    PS> $a
    A
    
  • Char から文字コードを得る

     整数(Int16 など)でキャストする

    PS> $a = [Char]"A"
    PS> $b = [Int16]$a
    PS> $b
    65
    

[Excel][文字] 制御文字の可視化

2015年8月29日

 全国の PC88 ファンの皆様、こんにちは! (こんにちは!) #違う

 通信テキストとかを触っていると、制御文字とかを扱うケースがあります。
 ところが制御文字は「目には見えない文字」なので、Poweshell や メモ帳 で見ようとしても、見えないかよくわからない文字で表示されるかどちらかです。たとえば Powershell の場合、以下のように表示されたりします。

 その点、PC88 系であれば制御文字を表示できて、かつ、その表示がどの制御コードを指しているのかが分かりやすかったわけです…(ぇ

 しかし Unicode な世界な今、制御コード文字は U+2400 あたりに登録されています。
 たとえば IME パッドを使用して確認する場合、左側ペインの「制御機能用記号」を選択すると、U+2400 に飛んでくれます。しかし表示フォントが MS ゴシックになっていると文字が表示されません。これは MS ゴシックに制御機能用記号のグリフが割り当てられていないためです。このため、制御機能用記号の文字を可視化したい場合は Arial Unicode MS など、グリフのあるフォントを選択します。

 制御文字をこの「制御機能用記号」に置き換えてやれば、Excel でも制御文字を表示できるようになります。
 ちなみに、以下は "Migu 1M" フォントで表示した結果です。(等幅で表示したかったので)

 置換用の VBA 関数は以下。 =VisualizeControlCharacter("文字列") といった感じで使用します。

Function VisualizeControlCharacter(s As String)

    For i = 0 To &H20
        s = Replace(s, Chr(i), ChrW(&H2400 + i))
    Next

    s = Replace(s, Chr(&H7F), ChrW(&H2421))

    VisualizeControlCharacter = s

End Function

 ついでに Powershell の場合の関数も作ってみました。
 (上記 Excel VBA のコードとはアプローチを変えて作っています。また、空白文字を "SP" ではなく "␣" にしています。)

function VisualizeControlCharacter($s) {
    -join (
        $s.ToCharArray() |
        % {
            switch -regex ($_){
                '[u0000-u001F]' { [Char]([Int16]$_ + 0x2400) };
                '[u0020]'        { [Char](0x2423) };
                '[u007F]'        { [Char](0x2421) };
                default           { $_ };
            }
        }
    )
}

 ただし、Powershell の Console だと非 CP932 の文字は化けてしまうので、ISE の Console ペインで実行します。

[EXCEL] 条件付き書式で設定したセルの色を固定化して貼り付ける

2015年8月16日
  • 命題

     条件付き書式で設定したセル色を固定化して貼り付けたい。

  • 問題1: 標準の操作は用意されていない

     標準の操作で、条件付き書式で設定したセル色を固定化して貼り付けることはできません。
     数式を値で貼り付けたり書式だけを貼り付けるには「形式を選択して貼り付け」で対処できます。しかし条件付き書式は数式を値として貼り付けるようなことが「形式を選択して貼り付け」では行えませんし、他の方法もないようです。
     それでは VBA を使って対処できるかというと、一筋縄とはいかないようです。

  • 問題2: VBA での対処が難しい理由

     ググってみると結構昔からある問題のようでした。
     条件付き書式でない普通の書式のセルの色は Range("~").Interiorで取得できます。また、条件付き書式はのセルの色は Range("~").FormatConditions(n).Interior (nは条件番号)で取得できますが、この値は条件付き書式の定義の値であり、現在適用されているセルの色ではありません。そして現在適用されているセルの書式を表すプロパティなどは用意されていないようです。
     このため、現在適用されているセルの色を取得しようとすると、FormatConditions から条件設定を抽出し、セルの値が条件に合致しているかを検証する必要があります。つまり、ミニ言語を解析するような作業を必要とします。

  • 対策1: Word を使った回避策

     上記の通り、現在のセル色を直接 VBA で取得することが困難です。そこで、Word を用います。
     一旦 Word に貼り付けて、そしてさらに Excel に貼り付けなおすと、条件付き書式は削除されコピーした時点のセルの色が固定化できます。
     手順は以下の通りです。

    1. 固定化したい領域をコピーします。
    2. Word に貼り付けます。
    3. 貼り付けた表を選択し、コピーします。
    4. Excel に貼り付けます。
    5. セルのデータを消すと、元のセルは条件付き書式に従ってセルの色が変わりますが、Word から貼り付けたほうはセルの色は固定化しています。
  • 対策2: ExcelVBA でなんとかならないか?

     以下の VBA を実行することで、上記と同じ動作を実現できます。

    Sub PasteDisplayInterior()
    
        Set wsh = CreateObject("WScript.Shell")
        
        cmd = "Powershell -sta -command " _
            & """" _
              & " Add-Type -an System.Windows.Forms; " _
              & " $a = [System.Windows.Forms.Clipboard]::GetData('HTML Format');  " _
              & " [System.Windows.Forms.Clipboard]::Clear(); " _
              & " [System.Windows.Forms.Clipboard]::SetData('HTML Format', $a); " _
            & """"
    
        wsh.Run cmd, 0, True
    
        ActiveSheet.Paste
    
    End Sub
    

     やっていることは、Powershell を使ってコピーした内容から html 要素だけを抜き出して戻し、それを Excel に貼り付けています。
     クリップボードの操作には .net の Windows.Forms.Clipboard を使用しています。

  • その他注意事項

     とはいえ、一般の書式に置き換わらない(=HTMLの要素にならない) データバーやアイコンセット、スパークラインなどは固定化できません。

[VB.NET] System.Text.Encoding.Default は使わないほうがよい

2015年8月13日

 System.Text.Encoding.Default は使わないほうがよいです。
 正確には「真にその挙動が得たいときには Encoding.Default を使えばよいのですが、多くの場合はその挙動を期待しているわけではないと思われる」です。

  1. 最初に結論

     System.Text.Encoding.Default を使いたい局面とは、主に ShiftJIS のバイト列を得たいときが大半だと思います。(正確には コードページ932 ですが、以下 ShiftJIS で統一します)
     しかしその場合、System.Text.Encoding.GetEncoding(932) または System.Text.Encoding.GetEncoding("Shift-JIS") と書くほうが適切です。また、そう書くことでソースコード上でも得たい文字コードを明示でき可読性も良くなります。

     また、多言語対応を考えた場合で何らかの理由でローカルの文字コードが欲しい場合は、System.Text.Encoding.Default ではなく System.Globalization.CultureInfo クラスの TextInfo.ANSICodePage を参照したほうが良い気がします。
     具体的には以下のような記述にします。(もっとよい記述があるかもしれませんが)

    System.Text.Encoding.GetEncoding(
        System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ANSICodePage
    ) 
    

     こうすることで、カルチャの変更にコードページを連動させることが可能です。

  2. 使わないほうがよい理由

     System.Text.Encoding.Default は Win32API の GetACP 関数で戻ってくるコードページで評価されるようです。
     GetACP 関数がどこの設定値を参照するのかわかりませんが、以下のリファレンスを見ると「current Windows ANSI code page identifier for the operating system」であるのと、システムで一意の設定と思われます。

     https://msdn.microsoft.com/ja-jp/library/windows/desktop/dd318070.aspx

    GetACP function
    Retrieves the current Windows ANSI code page identifier for the operating system.
    Caution The ANSI API functions, for example, the ANSI version of TextOut, implicitly use GetACP to translate text to or from Unicode. For the Multilingual User Interface (MUI) edition of Windows, the system ACP might not cover all code points in the user's selected logon language identifier. For compatibility with this edition, your application should avoid calls that depend on GetACP either implicitly or explicitly, as this function can cause some locales to display text as question marks. Instead, the application should use the Unicode API functions directly, for example, the Unicode version of TextOut.

     したがって、確認はしていませんが GetACP 関数が取得する値は、おそらく「コントロール パネル」→「時計、言語、および地域」→「地域」にある「Unicode 対応ではないプログラムの現在の言語」で設定された値(以下「システムロケール」と記述)ではないかと思います。

     このため、たとえば 日本語 OS の場合は期待通り ShiftJIS のコードが得られますが、日本語 OS でない場合は ShiftJIS のコードは得られません。日本語 OS 以外で Encoding.Default で ShiftJIS を得るためには、上記のシステムロケールを変更する必要があります。
     また、Encoding.Default はシステムロケールに従うので、カルチャ(System.Threading.Thread.CurrentThread.CurrentCulture など) のコードページと不一致が発生する場合があります。
     したがって、多言語対応などで「カルチャの設定ではなくシステムロケールの文字コードを得たい」という強い意志がある場合を除いて Encoding.Default は使用しないほうが無難です。

  3. Encoding.Default と GetACP 関数の関係

     Encoding.Default が GetACP 関数に依存している事は、以下から確認することができます。
     mscorlib/system/text/encoding.cs, Default プロパティ

     引用は以下。(但し個人的に見やすいように順序の入れ替え・省略等をしているので、正確な内容は上記リンクを参照してください)

    public static Encoding Default {
        [System.Security.SecuritySafeCritical]  // auto-generated
        get {
            if (defaultEncoding == null) {
                defaultEncoding = CreateDefaultEncoding();
            }
            return defaultEncoding;
        }
    }
    
    private static Encoding CreateDefaultEncoding()
    {
        Encoding enc;
    
    #if FEATURE_CODEPAGES_FILE            
        int codePage = Win32Native.GetACP();
        if (codePage == 1252)
            enc = new SBCSCodePageEncoding(codePage);
        else
            enc = GetEncoding(codePage);
    ~略~
        return (enc);
    }
    
  4. サードパーティライブラリの文字化けに対応する方法(ただし邪道)

     ソースコードが修正できない DLL(サードパーティー製など)が Encoding.Default を使っていると思われる案件で、非日本語 OS 上で文字化けしてまい、かつシステムロケールの変更や Microsoft AppLocale Utility の使用による解決が難しい場合、以下のようなコードで無理やり Encoding.Default の エンコードを変更することで文字化けを抑止できるかもしれません。

    ' リフレクションを使って Private フィールドにアクセスし、強引に Default Encoding を ShiftJIS にする。
    ' 要 Imports System.Text / Imports System.Reflection
    Dim a = GetType(Encoding)
    Dim b = a.GetField("defaultEncoding",
                       BindingFlags.NonPublic Or BindingFlags.Static)
    b.SetValue(a, Encoding.GetEncoding("Shift-JIS"))
    

     対策できる範囲が狭く、かつ副作用が大きいので上記のようなことはすべきではありませんが、それらを理解したうえで他に手がないようであれば、上記で解決できるかを試してみるのも良いかもしれません。

[XAML][WPF] DataGrid のフォーマットの調整

2015年8月5日

 WPF の DataGrid 関連のメモ。
 主に、セルの書式設定に関する設定について。

  1. セルの色を指定する(表示)

     セルの色を指定する場合、ElementStyle に Style を設定します。
     DataGridTextColumn の場合 ElementStyle に TextBlock の Style を与えます。
     今回は、Background を "Red" と固定で設定しているので当該列すべてが赤表示になります。各行ごとに色を変えたい場合は Binding してやればよいです。

    <DataGrid>
      <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Data001}">
          <DataGridTextColumn.ElementStyle>
            <Style TargetType="{x:Type TextBlock}">
              <!--赤で表示 -->
              <Setter Property="Background" Value="Red"/>
            </Style>
          </DataGridTextColumn.ElementStyle>
        </DataGridTextColumn>
    
  2. 2.セルの色を指定する(編集中)

     1.の方法だと、編集モードになると色がデフォルトに戻ってしまいます。
     編集中も同じ色、あるいはデフォルトとは違う色にしたい場合は EditingElementStyle を操作します。
     編集中はターゲットが TextBlock から TextBox に変わっている点も注意です。

    <DataGrid>
      <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Data001}">
          <DataGridTextColumn.EditingElementStyle>
            <Style TargetType="{x:Type TextBox}">
              <!--編集中は黄色で表示 -->
              <Setter Property="Background" Value="Yellow"/>
            </Style>
          </DataGridTextColumn.EditingElementStyle>
        </DataGridTextColumn>
    
  3. セルのデータを右寄せにする

     右寄せにするには CellStyle を操作します。
     上記1,2で設定した ElementStyle, EditingElementStyle でも可能ですが、それぞれに設定する必要があるので面倒です。

    <DataGrid>
      <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Data001}">
          <DataGridTextColumn.CellStyle>
            <Style TargetType="{x:Type DataGridCell}">
              <Setter Property="HorizontalAlignment" Value="Stretch" />
              <Setter Property="TextBlock.TextAlignment" Value="Right"/>
            </Style>
          </DataGridTextColumn.CellStyle>
        </DataGridTextColumn>
    
  4. セルの書式指定

     数値のフォーマット等を指定したい場合は、Binding で StringFormat を指定します。

    <DataGridTextColumn Binding="{Binding Data001,StringFormat=0.00}"/>
    
  5. 列幅変更禁止/ソート禁止/読み取り専用

     表(DataGrid)全体で制限する場合は DataGrid の属性を変更します。列幅変更禁止は CanUserResizeColumns、ソート禁止は CanUserSortColumns を操作します。読み取り専用にする場合は、IsReadOnly を操作します。
     列個別で設定する場合は DataGridTextColumn の属性を変更します。列幅変更禁止は CanUserResize、ソート禁止は CanUserSort を操作します。読み取り専用にする場合は、IsReadOnly を操作します。

     表(DataGrid)全体

    <DataGrid CanUserResizeColumns="False" CanUserSortColumns="False" IsReadOnly="True">
    

     列個別で設定

    <DataGridTextColumn Binding="{Binding Data001}"
                        CanUserResize="False" CanUserSort="False" IsReadOnly="True"/>
    
  6. 列自動追加禁止

     バインディングしているオブジェクトのメンバーが、勝手に列に新規追加されてしまうのを抑止します。

    <DataGrid AutoGenerateColumns="False">
    

[Excel] セルに自身のワークシート名等を表示させる

2015年7月28日

 Excel の関数でありそうでないのが、自分のブック名やシート名を表示する関数です。なぜないのか(汗 ヘッダーやフッターには表示できるのに・・・
 ということで、ワークシート名等を表示させる関数を以下にメモ。いずれも標準モジュールに作成します。

  1. 自ワークブック名を取得する

     自身のワークブック名を取得します。ポイントは Application.Volatile (*1) で、これでブック名を変更しても変更後のブック名に追従させることが可能です。
     使い方は "=ThisWorkbookName()"。 最後の "()" を省略すると、"#NAME?" になってしまうので忘れずに付けます。

    Function ThisWorkbookName() As String
        Application.Volatile
        ThisWorkbookName = Application.ThisWorkbook.Name
    End Function
    

    (*1) https://msdn.microsoft.com/ja-jp/library/office/Ff195441.aspx

  2. 自ワークシート名を取得する

     自身のワークシート名を取得します。ポイントは Application.Caller.Worksheet で、これで関数を呼び出したワークシートを特定します。
     使い方は "=ThisWorksheetName()"

    Function ThisWorksheetName() As String
        Application.Volatile
        ThisWorksheetName = Application.Caller.Worksheet.Name
    End Function
    
  3. 指定シートのワークシート名を取得する

     おまけ。指定のワークシート名を取得します。
     使い方は、"=WorksheetName(1)" など引数にシート番号を指定します。

    Function WorksheetName(index As Integer) As String
        Application.Volatile
        WorksheetName = Application.Sheets(index).Name
    End Function
    

[PS1] Powershell で html の特殊文字をエスケープする

2015年7月24日

 Wordpress とかでコードを引用したい場合などの時に、Powershell で html の特殊文字を手軽にエスケープする方法は以下。
 System.Web.HttpUtility の HtmlEncode を使用します。

  1. 最初に System.Web を読み込みます
    Add-Type -AssemblyName System.Web
    
  2. ファイルの内容を変換したい場合は以下のように。(-raw がポイントです)
    [System.Web.HttpUtility]::HtmlEncode((gc -raw ファイル名.xml))
    
  3. テキストの内容を直接変換したい場合は以下のように。(逐語的文字列を使うところがポイントです)
    $a = @"
    <?xml version="1.0"
          encoding="UTF-8" ?>
    "@
    
    [System.Web.HttpUtility]::HtmlEncode($a)
    

[Excel] リボンにカスタムボタンを追加する

2015年7月24日

 Excel 2007 以降で、リボンにカスタムボタンを追加し、そのボタンにマクロを割り当てる方法を以下の記す。

  1. マクロ付きのブックを作ります。ファイル名を仮に Book1.xlsm とします。
  2. Book1.xlsm を Book1.xlsm.zip にファイル名を変更し zip ファイルとしてアクセスできるようにします。
  3. Book1.xlsm.zip を開き、その中の Book1.xlsm.zip_rels.rels ファイルをローカルにコピー、そして以下のように、マーカーの部分を追記します。
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
      <Relationship Id="customUIRelID" Type="http://schemas.microsoft.com/office/2006/relationships/ui/extensibility" Target="customUI/customUI.xml"/>
      <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
      <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
      <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
    </Relationships>
    

    実際のファイルは「<Relationships …> ~ </Relationships>」まで一行で記述されていますが、適宜字下げ、改行を入れても大丈夫です。
    また、追記する「<Relationship Id="customUIRelID" ~」も Relationships 内にあれば順番は問いません。

  4. customUI フォルダをローカルに作成し、その中に customUI.xml を作成します。内容は以下の通りにします。エンコードは UTF-8 で保存します。
    <?xml version="1.0" encoding="UTF-8" ?>
    
    <customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui">
      <ribbon>
        <tabs>
          <tab id="Tab1"
               label="タブ1">
            <group id="Group1"
                   label="グループ1">
              <button id="Button1"
                      label="ボタン1"
                      size="large"
                      imageMso="ImportSharePointList"
                      onAction="Module1.Button1" />
              <button id="Button2"
                      label="ボタン2"
                      size="large"
                      imageMso="ExportSharePointList"
                      onAction="Module1.Button2" />
            </group>
          </tab>
        </tabs>
      </ribbon>
    </customUI>
    
  5. この customUI.xml がある customUI フォルダ を Book1.xlsm.zip にコピーします。Book1.xlsm.zipcustomUIcustomUI.xml ができていれば OK です。
  6. ファイル名の拡張子を .zip から .xlsm に戻します。
  7. Book1.xlsm を開き、ボタンに対応するマクロを記述します。
    Sub Button1(rc As IRibbonControl)
        MsgBox "Button1"
    End Sub
    
    Sub Button2(rc As IRibbonControl)
        MsgBox "Button2"
    End Sub
    

    customUI.xml の onAction に指定したマクロを記述します。今回は Module1 に Button1,Button2 の 2つのマクロを記述しています。
    注意点としては、リボンのボタンから呼ばれるマクロは、引数に IRibbonControl 型の変数が1つ付いて来るので、それを受ける必要があります。(引数なしのマクロを書くとエラーになります。)

  8. リボンのボタンを押して、指定のマクロが実行できれば OK です。
  9. その他の注意点は以下。

[時事] 探査機が冥王星到達

2015年7月14日

 地球を発つこと9年余、惑星降格から9年弱・・・
 降格していなければ、これですべての惑星に探査機が飛んだことになります。

 ニューホライズンズによる画像はこんな。

 そして、これまで最高画質だったハッブル宇宙望遠鏡の画像とかは淘汰されて見られなくなっていきそうな予感・・・

 ところで、このサイトのファビコンは冥王星とその衛星カロンをかたどったものですね。どうでもいいことですが(汗

[時事] 太陽誘電、光記録メディア事業から撤退

2015年6月13日

 「太陽誘電、光記録メディア事業から撤退」との由…
 http://pc.watch.impress.co.jp/docs/news/20150611_706536.html

 太陽誘電製は好んで使っていただけに、残念な感じはします。

 そういえば、B's Recorder の BHA がなくなったのもずいぶん前だな… 6年前か…
 http://www.itmedia.co.jp/news/articles/0908/21/news083.html

[環境設定] ASUS VivoTab RT (TF600T) のゴーストタッチ対策

2015年6月2日

 いつだったか、Windows RT 端末である ASUS VivoTab RT (TF600T) にゴーストタッチが発生するようになり、使い物にならなくなっていました…

 しかし、もしかすると以下の設定を行うことによって、回復するかもしれません。
 手順は以下の通り

  1. コマンドプロンプトを起動します
  2. powercfg -L を実行し設定したい電源設定の GUID を確認します

     今回はデフォルトのバランス設定を変えるので、381b4222-f694-41f0-9685-ff5bb260df2e を記録します。
     直接、現在の設定を変更するのが怖い場合は、事前にカスタムプランを作成し、それの GUID を記録するようにしてください。

    C:> powercfg -L
    
    既存の電源設定 (* アクティブ)
    -----------------------------------
    電源設定の GUID: 381b4222-f694-41f0-9685-ff5bb260df2e  (バランス) *
    
  3. 以下の2つの設定を変えます

     指定する GUID は powercfg -L を実行して記録したものを指定します。

    システムの冷却ポリシー
    C:> powercfg -setDCValueindex 381b4222-f694-41f0-9685-ff5bb260df2e SUB_PROCESSOR SYSCOOLPOL 1
    
    スリープ解除タイマーの許可
    C:> powercfg -setDCValueindex 381b4222-f694-41f0-9685-ff5bb260df2e SUB_SLEEP RTCWAKE 1
    

 Surface 3 が非 RT で発表され、既存 RT 端末もまた 10 Mobile に update されるわけでもなさそうなので、これで細々と余生を過ごさせてやれればと思っています…

 以下、参考までにトラブルシューティングの過程をメモ。

  • どんな状況状況だったのか

     私の端末の場合、バッテリー駆動時のスリープから復旧する時にゴーストタッチが出るようになりました。
     電源オンや再起動時、短時間での(一分程度の)スリープからの復旧、AC駆動時にはゴーストタッチは発生しませんでした。

  • 変えた設定の意味

     わかりません(汗
     スリープから復旧するときにゴーストタッチが起こること、電源に接続している時には再現がないこと、の2点から、電源関係の設定ではないか? ということで、電源オプションの設定で「バッテリ駆動」の設定を「電源に接続」の設定にあわせてみましたが駄目でした。
     そこで powercfg -Q で「バッテリ駆動」の設定と「電源に接続」の設定を比較し、トライアンドエラーの結果、以下の 2 設定が影響していると推定しました。

    1. SUB_PROCESSOR SYSCOOLPOL (システムの冷却ポリシー)

      0 : 無効 (「バッテリ駆動」時のデフォルト値)
      1 : 有効 (「電源に接続」時のデフォルト値)

    2. SUB_SLEEP RTCWAKE (スリープ解除タイマーの許可)

      0 : パッシブ (「バッテリ駆動」時のデフォルト値)
      1 : アクティブ (「電源に接続」時のデフォルト値)

     しかし、この設定がゴーストタッチにどのように関与しているのかは不明です(汗

  • ハードウェアの故障ではないのか?

     ぐぐると「メーカー修理で治った」という話もあるようなので、ハードウェア起因かもしれません。
     しかし、以前に他人の TF600T でゴーストタッチが出たらしき案件で「再セットアップしたらなおった」ということを聞いた覚えがあるので、原因は必ずしもハードウェアではないかもしれません。

  • 現在の設定

     現在、ゴーストタッチが起こらなくなった、私のマシンの設定は以下です。
     もし、上記設定を変えてもだめな場合は参考にしてみてください。

    C:> powercfg -Q 381b4222-f694-41f0-9685-ff5bb260df2e
    電源設定の GUID: 381b4222-f694-41f0-9685-ff5bb260df2e  (バランス)
      GUID エイリアス: SCHEME_BALANCED
      サブグループの GUID: fea3413e-7e05-4911-9a71-700331f1c294  (どのサブグループにも属さない設定)
        GUID エイリアス: SUB_NONE
        電源設定の GUID: 0e796bdb-100d-47d6-a2d5-f7d2daa51f51  (復帰時のパスワードを必要とする)
          GUID エイリアス: CONSOLELOCK
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: いいえ
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: はい
        現在の AC 電源設定のインデックス: 0x00000001
        現在の DC 電源設定のインデックス: 0x00000001
    
      サブグループの GUID: 0012ee47-9041-4b5d-9b77-535fba8b1442  (ハード ディスク)
        GUID エイリアス: SUB_DISK
        電源設定の GUID: 6738e2c4-e8a5-4a42-b16a-e040e769756e  (次の時間が経過後ハード ディスクの電源を切る)
          GUID エイリアス: DISKIDLE
          利用可能な設定の最小値: 0x00000000
          利用可能な設定の最大値: 0xffffffff
          利用可能な設定の増分: 0x00000001
          利用可能な設定の単位: 秒
        現在の AC 電源設定のインデックス: 0x0000001e
        現在の DC 電源設定のインデックス: 0x0000001e
    
      サブグループの GUID: 02f815b5-a5cf-4c84-bf20-649d1f75d3d8  (Internet Explorer)
        電源設定の GUID: 4c793e7d-a264-42e1-87d3-7a0d2f523ccd  (JavaScript タイマーの間隔)
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: 最大限の省電力
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: 最大パフォーマンス
        現在の AC 電源設定のインデックス: 0x00000001
        現在の DC 電源設定のインデックス: 0x00000000
    
      サブグループの GUID: 0d7dbae2-4294-402a-ba8e-26777e8488cd  (デスクトップの背景の設定)
        電源設定の GUID: 309dce9b-bef4-4119-9921-a851fb12f0f4  (スライド ショー)
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: 有効
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: 一時停止
        現在の AC 電源設定のインデックス: 0x00000000
        現在の DC 電源設定のインデックス: 0x00000001
    
      サブグループの GUID: 19cbb8fa-5279-450e-9fac-8a3d5fedd0c1  (ワイヤレス アダプターの設定)
        電源設定の GUID: 12bbebe6-58d6-4636-95bb-3217ef867c1a  (省電力モード)
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: 最大パフォーマンス
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: 省電力 (低)
          利用可能な設定のインデックス: 002
          利用可能な設定のフレンドリ名: 省電力 (中)
          利用可能な設定のインデックス: 003
          利用可能な設定のフレンドリ名: 省電力 (高)
        現在の AC 電源設定のインデックス: 0x00000000
        現在の DC 電源設定のインデックス: 0x00000002
    
      サブグループの GUID: 238c9fa8-0aad-41ed-83f4-97be242c8f20  (スリープ)
        GUID エイリアス: SUB_SLEEP
        電源設定の GUID: 29f6c1db-86da-48c5-9fdb-f2b67b1f44da  (次の時間が経過後スリープする)
          GUID エイリアス: STANDBYIDLE
          利用可能な設定の最小値: 0x00000000
          利用可能な設定の最大値: 0xffffffff
          利用可能な設定の増分: 0x00000001
          利用可能な設定の単位: 秒
        現在の AC 電源設定のインデックス: 0x00000708
        現在の DC 電源設定のインデックス: 0x00000384
    
        電源設定の GUID: 94ac6d29-73ce-41a6-809f-6363ba21b47e  (ハイブリッド スリープを許可する)
          GUID エイリアス: HYBRIDSLEEP
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: オフ
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: オン
        現在の AC 電源設定のインデックス: 0x00000001
        現在の DC 電源設定のインデックス: 0x00000001
    
        電源設定の GUID: 9d7815a6-7ee4-497e-8888-515a05f02364  (次の時間が経過後休止状態にする)
          GUID エイリアス: HIBERNATEIDLE
          利用可能な設定の最小値: 0x00000000
          利用可能な設定の最大値: 0xffffffff
          利用可能な設定の増分: 0x00000001
          利用可能な設定の単位: 秒
        現在の AC 電源設定のインデックス: 0x00000000
        現在の DC 電源設定のインデックス: 0x00000000
    
        電源設定の GUID: bd3b718a-0680-4d9d-8ab2-e1d2b4ac806d  (スリープ解除タイマーの許可)
          GUID エイリアス: RTCWAKE
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: 無効
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: 有効
        現在の AC 電源設定のインデックス: 0x00000001
        現在の DC 電源設定のインデックス: 0x00000001
    
      サブグループの GUID: 2a737441-1930-4402-8d77-b2bebba308a3  (USB 設定)
        電源設定の GUID: 48e6b7a6-50f5-4782-a5d4-53bb8f07e226  (USB のセレクティブ サスペンドの設定)
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: 無効
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: 有効
        現在の AC 電源設定のインデックス: 0x00000001
        現在の DC 電源設定のインデックス: 0x00000001
    
      サブグループの GUID: 2e601130-5351-4d9d-8e04-252966bad054  (アイドル回復性)
        GUID エイリアス: SUB_IR
      サブグループの GUID: 48672f38-7a9a-4bb2-8bf8-3d85be19de4e  (割り込みステアリング設定)
        GUID エイリアス: SUB_INTSTEER
      サブグループの GUID: 4f971e89-eebd-4455-a8de-9e59040e7347  (電源ボタンとカバー)
        GUID エイリアス: SUB_BUTTONS
        電源設定の GUID: 5ca83367-6e45-459f-a27b-476b1d01c936  (カバーを閉じたときの操作)
          GUID エイリアス: LIDACTION
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: 何もしない
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: スリープ
          利用可能な設定のインデックス: 002
          利用可能な設定のフレンドリ名: 休止状態
          利用可能な設定のインデックス: 003
          利用可能な設定のフレンドリ名: シャットダウン
        現在の AC 電源設定のインデックス: 0x00000001
        現在の DC 電源設定のインデックス: 0x00000001
    
        電源設定の GUID: 7648efa3-dd9c-4e3e-b566-50f929386280  (電源ボタンの操作)
          GUID エイリアス: PBUTTONACTION
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: 何もしない
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: スリープ
          利用可能な設定のインデックス: 002
          利用可能な設定のフレンドリ名: 休止状態
          利用可能な設定のインデックス: 003
          利用可能な設定のフレンドリ名: シャットダウン
        現在の AC 電源設定のインデックス: 0x00000001
        現在の DC 電源設定のインデックス: 0x00000001
    
        電源設定の GUID: 96996bc0-ad50-47ec-923b-6f41874dd9eb  (スリープ ボタンの操作)
          GUID エイリアス: SBUTTONACTION
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: 何もしない
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: スリープ
          利用可能な設定のインデックス: 002
          利用可能な設定のフレンドリ名: 休止状態
          利用可能な設定のインデックス: 003
          利用可能な設定のフレンドリ名: シャットダウン
        現在の AC 電源設定のインデックス: 0x00000001
        現在の DC 電源設定のインデックス: 0x00000001
    
        電源設定の GUID: a7066653-8d6c-40a8-910e-a1f54b84c7e5  ([スタート] メニューの電源ボタンの操作)
          GUID エイリアス: UIBUTTON_ACTION
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: スリープ
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: 休止状態
          利用可能な設定のインデックス: 002
          利用可能な設定のフレンドリ名: シャットダウン
        現在の AC 電源設定のインデックス: 0x00000000
        現在の DC 電源設定のインデックス: 0x00000000
    
      サブグループの GUID: 501a4d13-42af-4429-9fd1-a8218c268e20  (PCI Express)
        GUID エイリアス: SUB_PCIEXPRESS
        電源設定の GUID: ee12f906-d277-404b-b6da-e5fa1a576df5  (リンク状態の電源管理)
          GUID エイリアス: ASPM
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: オフ
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: 適切な省電力
          利用可能な設定のインデックス: 002
          利用可能な設定のフレンドリ名: 最大限の省電力
        現在の AC 電源設定のインデックス: 0x00000001
        現在の DC 電源設定のインデックス: 0x00000002
    
      サブグループの GUID: 54533251-82be-4824-96c1-47b60b740d00  (プロセッサの電源管理)
        GUID エイリアス: SUB_PROCESSOR
        電源設定の GUID: 893dee8e-2bef-41e0-89c6-b55d0929964c  (最小のプロセッサの状態)
          GUID エイリアス: PROCTHROTTLEMIN
          利用可能な設定の最小値: 0x00000000
          利用可能な設定の最大値: 0x00000064
          利用可能な設定の増分: 0x00000001
          利用可能な設定の単位: %
        現在の AC 電源設定のインデックス: 0x00000014
        現在の DC 電源設定のインデックス: 0x00000014
    
        電源設定の GUID: 94d3a615-a899-4ac5-ae2b-e4d8f634367f  (システムの冷却ポリシー)
          GUID エイリアス: SYSCOOLPOL
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: パッシブ
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: アクティブ
        現在の AC 電源設定のインデックス: 0x00000001
        現在の DC 電源設定のインデックス: 0x00000001
    
        電源設定の GUID: bc5038f7-23e0-4960-96da-33abaf5935ec  (最大のプロセッサの状態)
          GUID エイリアス: PROCTHROTTLEMAX
          利用可能な設定の最小値: 0x00000000
          利用可能な設定の最大値: 0x00000064
          利用可能な設定の増分: 0x00000001
          利用可能な設定の単位: %
        現在の AC 電源設定のインデックス: 0x00000064
        現在の DC 電源設定のインデックス: 0x00000064
    
      サブグループの GUID: 7516b95f-f776-4464-8c53-06167f40cc99  (ディスプレイ)
        GUID エイリアス: SUB_VIDEO
        電源設定の GUID: 3c0bc021-c8a8-4e07-a973-6b14cbcb2b7e  (次の時間が経過後ディスプレイの電源を切る)
          GUID エイリアス: VIDEOIDLE
          利用可能な設定の最小値: 0x00000000
          利用可能な設定の最大値: 0xffffffff
          利用可能な設定の増分: 0x00000001
          利用可能な設定の単位: 秒
        現在の AC 電源設定のインデックス: 0x00000000
        現在の DC 電源設定のインデックス: 0x00000e10
    
        電源設定の GUID: aded5e82-b909-4619-9949-f5d71dac0bcb  (ディスプレイの明るさ)
          利用可能な設定の最小値: 0x00000000
          利用可能な設定の最大値: 0x00000064
          利用可能な設定の増分: 0x00000001
          利用可能な設定の単位: %
        現在の AC 電源設定のインデックス: 0x00000064
        現在の DC 電源設定のインデックス: 0x00000000
    
        電源設定の GUID: f1fbfde2-a960-4165-9f88-50667911ce96  (ディスプレイ暗転時の明るさ)
          利用可能な設定の最小値: 0x00000000
          利用可能な設定の最大値: 0x00000064
          利用可能な設定の増分: 0x00000001
          利用可能な設定の単位: %
        現在の AC 電源設定のインデックス: 0x00000032
        現在の DC 電源設定のインデックス: 0x00000032
    
        電源設定の GUID: fbd9aa66-9553-4097-ba44-ed6e9d65eab8  (自動輝度調整を有効にする)
          GUID エイリアス: ADAPTBRIGHT
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: オフ
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: オン
        現在の AC 電源設定のインデックス: 0x00000001
        現在の DC 電源設定のインデックス: 0x00000001
    
      サブグループの GUID: 8619b916-e004-4dd8-9b66-dae86f806698  (プレゼンス認識電源動作)
        GUID エイリアス: SUB_PRESENCE
      サブグループの GUID: 9596fb26-9850-41fd-ac3e-f7c3c00afd4b  
        電源設定の GUID: 34c7b99f-9a6d-4b3c-8dc7-b6693b78cef4  (ビデオの再生時)
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: ビデオ品質の最適化
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: バランス
          利用可能な設定のインデックス: 002
          利用可能な設定のフレンドリ名: 省電力の最適化
        現在の AC 電源設定のインデックス: 0x00000000
        現在の DC 電源設定のインデックス: 0x00000001
    
      サブグループの GUID: e73a048d-bf27-4f12-9731-8b2076e8891f  (バッテリ)
        GUID エイリアス: SUB_BATTERY
        電源設定の GUID: 637ea02f-bbcb-4015-8e2c-a1c7b9c0b546  (バッテリ切れの動作)
          GUID エイリアス: BATACTIONCRIT
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: 何もしない
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: スリープ
          利用可能な設定のインデックス: 002
          利用可能な設定のフレンドリ名: 休止状態
          利用可能な設定のインデックス: 003
          利用可能な設定のフレンドリ名: シャットダウン
        現在の AC 電源設定のインデックス: 0x00000003
        現在の DC 電源設定のインデックス: 0x00000003
    
        電源設定の GUID: 8183ba9a-e910-48da-8769-14ae6dc1170a  (低残量バッテリのレベル)
          GUID エイリアス: BATLEVELLOW
          利用可能な設定の最小値: 0x00000000
          利用可能な設定の最大値: 0x00000064
          利用可能な設定の増分: 0x00000001
          利用可能な設定の単位: %
        現在の AC 電源設定のインデックス: 0x00000006
        現在の DC 電源設定のインデックス: 0x00000006
    
        電源設定の GUID: 9a66d8d7-4ff7-4ef9-b5a2-5a326ca2a469  (バッテリ切れのレベル)
          GUID エイリアス: BATLEVELCRIT
          利用可能な設定の最小値: 0x00000000
          利用可能な設定の最大値: 0x00000064
          利用可能な設定の増分: 0x00000001
          利用可能な設定の単位: %
        現在の AC 電源設定のインデックス: 0x00000002
        現在の DC 電源設定のインデックス: 0x00000002
    
        電源設定の GUID: bcded951-187b-4d05-bccc-f7e51960c258  (低残量バッテリの通知)
          GUID エイリアス: BATFLAGSLOW
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: オフ
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: オン
        現在の AC 電源設定のインデックス: 0x00000001
        現在の DC 電源設定のインデックス: 0x00000001
    
        電源設定の GUID: d8742dcb-3e6a-4b3c-b3fe-374623cdcf06  (低残量バッテリの動作)
          GUID エイリアス: BATACTIONLOW
          利用可能な設定のインデックス: 000
          利用可能な設定のフレンドリ名: 何もしない
          利用可能な設定のインデックス: 001
          利用可能な設定のフレンドリ名: スリープ
          利用可能な設定のインデックス: 002
          利用可能な設定のフレンドリ名: 休止状態
          利用可能な設定のインデックス: 003
          利用可能な設定のフレンドリ名: シャットダウン
        現在の AC 電源設定のインデックス: 0x00000000
        現在の DC 電源設定のインデックス: 0x00000000
    
        電源設定の GUID: f3c5027d-cd16-4930-aa6b-90db844a8f00  (省電源移行バッテリ レベル)
          利用可能な設定の最小値: 0x00000000
          利用可能な設定の最大値: 0x00000064
          利用可能な設定の増分: 0x00000001
          利用可能な設定の単位: %
        現在の AC 電源設定のインデックス: 0x00000004
        現在の DC 電源設定のインデックス: 0x00000004
    

[工業] ノルマルリューベーとは何か

2015年3月18日

 ノルマルリューベー とか、Nm3とか、某業界方面とかでまことしやかに使われている単位,工業量があるわけですが、これはいったい何なのか。

 一言でいうと質量の単位です。体積の単位じゃないです。

 ちなみに、ノルマルリューベー に使用されている「N」という記号は補助単位(SI補助単位)ではありません。そんな風に見えますが「N」なんて補助単位はありません。
 もちろん組立単位でもありません。この「N」はニュートンではないです。
 つまり、この「N」は「ノルマル」であることを示すために、慣例的に使用している記号です。
 なので記述位置も固定されておらず「m3N」のように書く場合もあります。

 あらためて。それでは「ノルマルリューベー」とはいったい何なのか。

 リューベーは「立米」の読みで、「立米」は「立方メートル」です。つまり「㎥」のことです。「米」はメートルのことです。
 「ノルマル」は、「ノーマル(Normal)」のことで、正常な、基準の、という意味です。
 なので「ノルマル」「立米」というと、「正常化(Normalize)された」「立方メートル」ってことですね。

 それではなにが "正常" であり、何が "正常ではない" のかというと。
 体積は温度や圧力によって値が変わります。ボイル・シャルルの法則というやつです。温度を高くすると体積は大きくなり、圧力を大きくすると体積は小さくなる、というアレです。なので、温度と圧力を無視して体積だけを計測しても正確な量が把握できません。
 そこで、測定した体積とその時の温度と圧力をもとに、ある基準の温度と圧力であった場合の体積量に換算します。これが「ノルマルリューベー」です。

 では「ある基準の温度と圧力」とは何か、ですが、多くは「常温常圧」と言われます。(*1)
 そうすると「常温常圧」って何?ってことになりますが、常圧は大気圧であることが多く、常温は15℃~20℃を採用することが多い気がします。(確証はありませんが…)
 大切なことは基準とする温度・圧力は、その工場とかそのプロセスで自由に決められているということです。
 したがって、ある工場(プロセス)と、別の工場(プロセス)で「ノルマルリューベー」を使っていたとしても、両者が同じ量であるとは限らないことです。

 かくて。
 どんな温度/圧力であったとしても、基準の温度/圧力(たとえば、1013hPa/15℃)での体積を示しているものが「ノルマルリューベー」です。
 温度や圧力に左右されない物質量ということなので、つまるところ質量ってことなのです。

 じゃあなんで kg にしなかったんですかね? kg なら他の工場との比較も何も考えずに可能なのに…という話はありますが、たぶん重量の単位で体積を表すのは「直観」に反するからなんでしょうね…(実際問題として、プロパンガスとか Kg で取引しているものもあります。)


(*1)
 あらためてぐぐってみると、基準温度は 0℃ が多勢を占める模様です。
 しかし「0℃とする」規格があるわけではないので、基準温度/圧力は確認してから指示を読むようにします。
 (もし何かしらの規格があるのであれば、教えてほしいです。)

[情報技術] 数値フォーマットの表現方法の違い

2015年1月4日

 小数点つきデータを文字列化する書式が、言語によって違い、いろいろな文化があります。
 代表的な書式文字列と、それを与えた場合どんな出力なるか、を確認してみた結果が以下。

  • 符号,小数点を「含む」全桁数 + 小数点以下の桁数 を指定するケース その1

    FORTRAN … F6.2

     例

      5.50      # write(*,'(F6.2)')    5.5
     55.55      # write(*,'(F6.2)')   55.55
    555.55      # write(*,'(F6.2)')  555.555
    ******      # write(*,'(F6.2)') 5555.55     ←桁オーバー
    -55.50      # write(*,'(F6.2)')  -55.5
    ******      # write(*,'(F6.2)') -555.5      ←桁オーバー
    
  • 符号,小数点を「含む」全桁数 + 小数点以下の桁数 を指定するケース その2

    C … %6.2f

     例

      5.50      # printf("%6.2fn",    5.5);
     55.55      # printf("%6.2fn",   55.55);
    555.55      # printf("%6.2fn",  555.555);
    5555.55     # printf("%6.2fn", 5555.55);   ←桁オーバー
    -55.50      # printf("%6.2fn",  -55.5);
    -555.50     # printf("%6.2fn", -555.5);    ←桁オーバー
    
  • 小数点を「含まない」全桁数 + 小数点以下の桁数 を指定するケース

    ORACLE … NUMBER(5,2)

     例

    SQL> create table NUMTEST
      2  (
      3      NUM number(5,2)
      4  );
    
    表が作成されました。
    
    SQL> insert into NUMTEST values (5.5);
    
    1行が作成されました。
    
    SQL> insert into NUMTEST values (55.55);
    
    1行が作成されました。
    
    SQL> insert into NUMTEST values (555.555);
    
    1行が作成されました。
    
    SQL> insert into NUMTEST values (5555.55);
    insert into NUMTEST values (5555.55)
                                *
    行1でエラーが発生しました。:
    ORA-01438: この列に許容される指定精度より大きな値です
    
    
    SQL> insert into NUMTEST values (-55.5);
    
    1行が作成されました。
    
    SQL> insert into NUMTEST values (-555.5);
    
    1行が作成されました。
    
    SQL> select * from NUMTEST;
    
           NUM
    ----------
           5.5
         55.55
        555.56
         -55.5
        -555.5
    
  • 整数桁数 + 少数桁数 を指定するケース

    COBOL … 9(3)V9(2)

     例

                # data division.
                #   working-storage section.
                #   01 hoge PIC 9(3)V9(2).
                #
                # procedure division.
                #   main.
    005.50      #     move    5.5   TO hoge.   DISPLAY hoge.
    055.55      #     move   55.55  TO hoge.   DISPLAY hoge.
    234.56      #     move  234.567 TO hoge.   DISPLAY hoge.
    234.56      #     move 1234.56  TO hoge.   DISPLAY hoge.    ←桁オーバー
    055.50      #     move  -55.5   TO hoge.   DISPLAY hoge.    ←符号なし
    555.50      #     move -555.5   TO hoge.   DISPLAY hoge.    ←符号なし
    
  • 数値で桁が指定できないケース その1

    EXCEL … ???.??

     例

    "  5.5 "    #    5.5
    " 55.55"    #   55.55
    "555.56"    #  555.555
    "5555.55"   # 5555.55    ←桁オーバー
    "- 55.5 "   #  -55.5     ←桁オーバー
    "-555.5 "   # -555.5     ←桁オーバー
    
  • 数値で桁が指定できないケース その2

    ORACLE の to_char() … 999.99

     例

       5.50     # select to_char(   5.5,   '999.99') from DUAL;
      55.55     # select to_char(  55.55,  '999.99') from DUAL;
     555.56     # select to_char( 555.555, '999.99') from DUAL;
    #######     # select to_char(5555.55,  '999.99') from DUAL;      ←桁オーバー
     -55.50     # select to_char( -55.5,   '999.99') from DUAL;
    -555.50     # select to_char(-555.5,   '999.99') from DUAL;    
    

     ※符号は+の場合は空白で確保さるため、文字列長は常に 7 桁となる。

 要するに、自分の扱っている言語の書式が世の中の標準ではない、というのを肝に銘じておかないと、足元を掬われるハメになる可能性が高いということで(汗

[FORTRAN] gfortran のインストール

2015年1月4日

 [COBOL] OpenCobol のインストール の続き。

 ここまで来たら、ついでに FORTRAN を…と思ったのでやってみた。

  1. 前提

     mingw-get (c:mingw*) と zlib1.dll (c:mingwbin) のコピーと、Path の設定は済ませているものとします。
     (詳細は「[COBOL] OpenCobol のインストール」の 2項, 3項を参照)

  2. gfortran のインストール

     以下のコマンドを実行します。

    mingw-get install gfortran
    
  3. hello world プログラムのコンパイルおよび実行の確認

    1. Hello.f を作成する
      !234567
            print *,'Hello' 
            end
      
    2. Hello.f をコンパイルする

       -o 指定を省略すると a.exe ができてしまうので、省略せずに指定します。

      gfortran hello.f -o hello
      

[COBOL] OpenCobol のインストール

2015年1月4日

 やっと Windows で OpenCobol をインストールしてコンパイルするところまで行けたので、それまでの手順をここに記す。

  1. はじめに

     ともかくまず、いろんなサイトや書籍のインストール手順を読む前に、readme.txt を眺めておいたおいたほうがよいです。(各種サイト,書籍の情報は古い可能性があるため)

     たどり方は以下。

     とはいえ、以下では readme.txt とは若干違う方法でインストールします。(mingw-get-setup.exe を使いたくなかったので)

  2. Mingw-get の取得とインストール

     Mingw-get の zip ファイルを http://sourceforge.jp/projects/mingw/releases/ から取得します。

     取得した zip ファイル (2015/1 現在は mingw-get-0.6.2-mingw32-beta-20131004-1-bin.zip が最新)を解凍し、適当なフォルダ(この手順では 'C:mingw' とする)にコピーします。

     環境変数 Path に C:mingwbin を追加します。(お好みの方法でどうぞ)

  3. zlib1.dll の取得とインストール

     zlib1.dll というものが必要らしいので、以下から取得します。
     http://www.zlib.net/
     取得したら、解凍し、include,lib フォルダを C:mingw にコピーし、zlib1.dll は C:mingwbin にコピーします。

  4. mingw-get による各種ソフトウェアの取得とインストール

     以下のコマンドを実行し、ソフトウェアの取得とインストールを行います。
     上の二つが、Readme.txt では Mingw インストール時にインストールするソフトウェア、下の三つが Readme.txt に書かれているソフトウェアです。msys-mintty は入れていません。

    mingw-get install gcc
    mingw-get install mingw-developer-toolkit
    mingw-get install mingw32-libpdcurses
    mingw-get install mingw32-gmp
    mingw-get install mingw32-pdcurses
    
  5. Open Cobol Binary の取得とインストール

     上記、Readme.txt のサイトから、以下の zip ファイルを取得します。

    • open-cobol-1.1-1.zip
    • vbisam-2.0-1.zip

     取得したら、解凍して C:Mingw にそのままにコピーします。(=上書き)

  6. COB_CONFIG_DIR を設定する

     COB_CONFIG_DIR を設定していない場合に以下のようなメッセージがあることがあります。

    /mingw/share/open-cobol/config/default.conf: No such file or directory
    Error: failed to load the initial config file
    

     この場合は、環境変数 COB_CONFIG_DIR に config フォルダの path を設定します。(環境変数の設定はお好みの方法でどうぞ)

    set COB_CONFIG_DIR=C:mingwshareopen-cobolconfig
    
  7. hello world プログラムのコンパイルおよび実行の確認

    ここまで来ると、コンパイルできるようになっているはずなので、hello world を実行してみます。

    • Hello.cob を作成する

       面倒なので、フリーフォーマットスタイルで書きます。

      identification division.
          program-id. hello.
      
      environment division.
      
      data division.
      
      procedure division.
      main.
          display "Hello!".
          stop run.
      
    • Hello.cob をコンパイルする

       以下を実行し、hello.exe が作成できたら完了です。

      cobc -x -free Hello.cob
      

[Raspi][VB.NET] RaspberryPi で VB.NET (GPIO出力)

2015年1月2日

 Raspberry PI の GPIO(General Purpose Input/Output) は Shell Script から リダイレクトで操作できるらしい。
 ということは、ストリーム入出力を使えば mono から操作できるのでは?ということでやってみました。

 VB のプログラムは以下。(VisualStudio でコンパイルして RaspiGPIOAccess.exe を生成する)

Module RaspiGPIOAccess
    Sub Main(args() As String)
        Dim path = args(0)

        If args.Length = 1 Then
            ' Read
            Using sr As New IO.StreamReader(path)
                Dim value = sr.ReadLine()
                Console.WriteLine(value)
            End Using

        Else
            ' Write
            Using sw = New IO.StreamWriter(path)
                Dim value = args(1)
                sw.WriteLine(value)
                sw.Flush()
            End Using

        End If
    End Sub
End Module

 使い方は以下。

  • "sudo mono RaspiGPIOAccess.exe パス名" とした場合は読み込み

     例: RaspiGPIOAccess.exe /sys/class/gpio/gpio4/value

  • "sudo mono RaspiGPIOAccess.exe パス名 値" とした場合は書き込み

     例: RaspiGPIOAccess.exe /sys/class/gpio/gpio4/value 1

 以下、実行結果。

# 最初に GPIO の状態を見る
~ $ ls /sys/class/gpio/
export  gpiochip0  unexport

# GPIO 4 の制御を開始
~ $ sudo mono RaspiGPIOAccess.exe /sys/class/gpio/export 4
~ $ ls /sys/class/gpio/
export  gpio4  gpiochip0  unexport

# GPIO 4 を書き込みモードに
~ $ sudo mono RaspiGPIOAccess.exe /sys/class/gpio/gpio4/direction out
~ $ sudo mono RaspiGPIOAccess.exe /sys/class/gpio/gpio4/direction
out

# GPIO 4 の値を読む
~ $ sudo mono RaspiGPIOAccess.exe /sys/class/gpio/gpio4/value
0

# GPIO 4 に 1 を書く (そののち読む)
~ $ sudo mono RaspiGPIOAccess.exe /sys/class/gpio/gpio4/value 1
~ $ sudo mono RaspiGPIOAccess.exe /sys/class/gpio/gpio4/value
1

# GPIO 4 に 0 を書く (そののち読む)
~ $ sudo mono RaspiGPIOAccess.exe /sys/class/gpio/gpio4/value 0
~ $ sudo mono RaspiGPIOAccess.exe /sys/class/gpio/gpio4/value
0

# GPIO 4 の制御を終了
~ $ sudo mono RaspiGPIOAccess.exe /sys/class/gpio/unexport 4
~ $ ls /sys/class/gpio/
export  gpiochip0  unexport

 素直にアクセスできました。
 ただ、ハード結線の設備とかまだ持っていないため、実際に入出力できているかの確認はしていません(汗

[Raspi][VB.NET] RaspberryPi で VB.NET

2015年1月2日

 Linux 系でも mono を使用すれば C# などのプログラムは動作するよね?ということですが、Raspberry Pi (Raspbian) でも動作するんですかね?ということで試してみました。

 まず導入。これはコマンド一発で行けるようです。(時間はかかりますが)

pi@raspberrypi ~ $ sudo apt-get install mono-complete

 次。Raspberry Pi で C# のプログラムをコンパイルしてみます。

public class HelloWorld
{
    public static void Main()
    {
        System.Console.WriteLine("hello!");
    }
}

 上記のソースコードをコンパイルする場合は、gmcs コマンドを使用します。コンパイルに成功すると exe ファイルができるので、それを実行するには mono コマンドを使用します。

pi@raspberrypi ~ $ gmcs hello.cs
pi@raspberrypi ~ $ mono hello.exe
hello!

 C# がコンパイルできることが分かったけど VB は?という話ですが調べてません(汗

 そもそも Raspberry Pi 上でコンパイルすると結構時間がかかりますし、ソースファイルが複数になった場合や参照設定云々…という話になると面倒なので、Raspberry Pi 上で開発するのはナシということで・・・

 gmcs が出力するファイルが exe なんだったら Windows で Visual Studio が吐いた exe でもいけるんじゃないの?ということで、以後開発には Visual Studio を使うことにします。 (Visual Studio 2013 を使用)

 とりあえず、動作することを確認したいので UDP で通信するプログラムを作ってみることにします。

Module Module1

    Sub Main()

        Dim udp = New System.Net.Sockets.UdpClient(60000)

        ' 受信
        Dim remoteEP As System.Net.IPEndPoint = Nothing
        Dim recvBytes = udp.Receive(remoteEP)

        ' 受信メッセージ取得
        Dim enc = System.Text.Encoding.UTF8
        Dim recvString = enc.GetString(recvBytes)
        Console.WriteLine("Recv Message : '{0}'", recvString)

        ' 送信メッセージ作成
        Console.Write("Input Return Message > ")
        Dim msg = Console.ReadLine

        Dim sendString = String.Format("{0} : Recv:'{1}' Send:'{2}'", Date.Now, recvString, msg)
        Dim sendBytes = enc.GetBytes(sendString)

        ' 送信元へ返信
        udp.Send(sendBytes, sendBytes.Length, remoteEP)

        udp.Close()

    End Sub

End Module

 このプログラムの機能は以下です。

  • UDP ポート 60000 で待ち受けて、
  • メッセージを受信すると返信メッセージの入力を促し、
  • Raspberry Pi 側の時刻, 受信メッセージ, 入力された返信メッセージを応答メッセージにして、
  • 受信元の IP/Port に UDP で送信する。
  • 一度送信したら、プログラムを終了する。

 このプログラムをコンパイルして、できた exe (UDPServer.exe とした) を teraterm pro などでファイル転送します。
 (teraterm pro の場合、メニューの file → SSH SCP を選択することでファイル転送ができます。)
 ファイル転送した後、mono UDPServer.exe、と入力しプログラムを起動させ、受信を待ち受けます。

 次に Raspberry Pi で待ち受けているプログラムにアクセスするコードを書きます。
 以下は、ホスト名 "raspberrypi" の UDP 60000 ポートに対して、自PC の 60001 ポートから "往信" を送信して、その応答を受信,表示する Powershell Script です。

$udp = New-Object System.Net.Sockets.UdpClient(60001)

$enc = [System.Text.Encoding]::UTF8

$sendString = $enc.GetBytes("往信")
$udp.Send($sendString, $sendString.Length, "raspberrypi", 60000)

$recvBytes = $udp.Receive([ref]$null)
Write-Host $enc.GetString($recvBytes)

$udp.Close()
$udp.Dispose()

 実行結果は以下。
 左端の番号は実行順序で、赤マーカー部はプログラムへの入力、緑マーカー部はプログラムの出力です。

  • Raspberry Pi 側
     1.    pi@raspberrypi ~ $ mono UDPServer.exe
    ---
    10.    Recv Message : '往信'
    11.    Input Return Message > 返信
    12.    pi@raspberrypi ~ $
    
  • Windows (Powershell) 側
     2.    PS > $udp = New-Object System.Net.Sockets.UdpClient(60001)
     3.    PS >
     4.    PS > $enc = [System.Text.Encoding]::UTF8
     5.    PS >
     6.    PS > $sendString = $enc.GetBytes("往信")
     7.    PS > $udp.Send($sendString, $sendString.Length, "raspberrypi", 60000)
     8.    PS >
     9.    PS > $recvBytes = $udp.Receive([ref]$null)
    ---
    13.    PS > Write-Host $enc.GetString($recvBytes)
    14.    2015/01/01 22:21:59 : Recv:'往信' Send:'返信'
    15.    PS >
    16.    PS > $udp.Close()
    17.    PS > $udp.Dispose()
    18.    PS >
    

 割とあっさり動作しました。
 ファイル入出力や COM(RS-232C) などへのアクセスはしていないのでわかりませんが、標準的なことをする分には普通に動くのではないかと思います。 #しらんけど

[VB.NET][与太] なぜ VB6 は偉大だったのか

2015年1月1日

 いまだに、VB6 (旧Visual Basic) は偉大だったな、と感じていて、今だそれを超える処理系は Windows 系では出てきていないと思っている。
 ということで、偉大だなと思うその理由についてまとめてみる。

  1. VC++ の場合 (Visual Studio 2013)

     このボタンも何もない、ウインドウがあるだけのアプリを作るとします。

     Visual Studio が自動生成するコードは以下。182 行のスケルトンコードが生成されます。

    // Win32Project2.cpp : アプリケーションのエントリ ポイントを定義します。
    //
    
    #include "stdafx.h"
    #include "Win32Project2.h"
    
    #define MAX_LOADSTRING 100
    
    // グローバル変数:
    HINSTANCE hInst;								// 現在のインターフェイス
    TCHAR szTitle[MAX_LOADSTRING];					// タイトル バーのテキスト
    TCHAR szWindowClass[MAX_LOADSTRING];			// メイン ウィンドウ クラス名
    
    // このコード モジュールに含まれる関数の宣言を転送します:
    ATOM				MyRegisterClass(HINSTANCE hInstance);
    BOOL				InitInstance(HINSTANCE, int);
    LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
    INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);
    
    int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                         _In_opt_ HINSTANCE hPrevInstance,
                         _In_ LPTSTR    lpCmdLine,
                         _In_ int       nCmdShow)
    {
    	UNREFERENCED_PARAMETER(hPrevInstance);
    	UNREFERENCED_PARAMETER(lpCmdLine);
    
     	// TODO: ここにコードを挿入してください。
    	MSG msg;
    	HACCEL hAccelTable;
    
    	// グローバル文字列を初期化しています。
    	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    	LoadString(hInstance, IDC_WIN32PROJECT2, szWindowClass, MAX_LOADSTRING);
    	MyRegisterClass(hInstance);
    
    	// アプリケーションの初期化を実行します:
    	if (!InitInstance (hInstance, nCmdShow))
    	{
    		return FALSE;
    	}
    
    	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32PROJECT2));
    
    	// メイン メッセージ ループ:
    	while (GetMessage(&msg, NULL, 0, 0))
    	{
    		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
    		{
    			TranslateMessage(&msg);
    			DispatchMessage(&msg);
    		}
    	}
    
    	return (int) msg.wParam;
    }
    
    
    
    //
    //  関数: MyRegisterClass()
    //
    //  目的: ウィンドウ クラスを登録します。
    //
    ATOM MyRegisterClass(HINSTANCE hInstance)
    {
    	WNDCLASSEX wcex;
    
    	wcex.cbSize = sizeof(WNDCLASSEX);
    
    	wcex.style			= CS_HREDRAW | CS_VREDRAW;
    	wcex.lpfnWndProc	= WndProc;
    	wcex.cbClsExtra		= 0;
    	wcex.cbWndExtra		= 0;
    	wcex.hInstance		= hInstance;
    	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT2));
    	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
    	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
    	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_WIN32PROJECT2);
    	wcex.lpszClassName	= szWindowClass;
    	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    
    	return RegisterClassEx(&wcex);
    }
    
    //
    //   関数: InitInstance(HINSTANCE, int)
    //
    //   目的: インスタンス ハンドルを保存して、メイン ウィンドウを作成します。
    //
    //   コメント:
    //
    //        この関数で、グローバル変数でインスタンス ハンドルを保存し、
    //        メイン プログラム ウィンドウを作成および表示します。
    //
    BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
    {
       HWND hWnd;
    
       hInst = hInstance; // グローバル変数にインスタンス処理を格納します。
    
       hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
          CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
    
       if (!hWnd)
       {
          return FALSE;
       }
    
       ShowWindow(hWnd, nCmdShow);
       UpdateWindow(hWnd);
    
       return TRUE;
    }
    
    //
    //  関数: WndProc(HWND, UINT, WPARAM, LPARAM)
    //
    //  目的:    メイン ウィンドウのメッセージを処理します。
    //
    //  WM_COMMAND	- アプリケーション メニューの処理
    //  WM_PAINT	- メイン ウィンドウの描画
    //  WM_DESTROY	- 中止メッセージを表示して戻る
    //
    //
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	int wmId, wmEvent;
    	PAINTSTRUCT ps;
    	HDC hdc;
    
    	switch (message)
    	{
    	case WM_COMMAND:
    		wmId    = LOWORD(wParam);
    		wmEvent = HIWORD(wParam);
    		// 選択されたメニューの解析:
    		switch (wmId)
    		{
    		case IDM_ABOUT:
    			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
    			break;
    		case IDM_EXIT:
    			DestroyWindow(hWnd);
    			break;
    		default:
    			return DefWindowProc(hWnd, message, wParam, lParam);
    		}
    		break;
    	case WM_PAINT:
    		hdc = BeginPaint(hWnd, &ps);
    		// TODO: 描画コードをここに追加してください...
    		EndPaint(hWnd, &ps);
    		break;
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		break;
    	default:
    		return DefWindowProc(hWnd, message, wParam, lParam);
    	}
    	return 0;
    }
    
    // バージョン情報ボックスのメッセージ ハンドラーです。
    INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	UNREFERENCED_PARAMETER(lParam);
    	switch (message)
    	{
    	case WM_INITDIALOG:
    		return (INT_PTR)TRUE;
    
    	case WM_COMMAND:
    		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
    		{
    			EndDialog(hDlg, LOWORD(wParam));
    			return (INT_PTR)TRUE;
    		}
    		break;
    	}
    	return (INT_PTR)FALSE;
    }
    

     また、上記のメインコードのほかに自動生成されるファイル群は以下。かなり多くのファイルが生成されます。

  2. VB2003 の場合

     VB2003 でボタンを一つ置いただけのアプリを作るとします。

     Visual Studio 2003 が自動生成するコードは以下。61 行のスケルトンコードが生成されます。
     うち、ボタン部分のコードは最後のほうの3行だけですから、ボタンに関係ないコードが 58 行生成される、ともいえます。

    Public Class Form1
        Inherits System.Windows.Forms.Form
    
    #Region " Windows フォーム デザイナで生成されたコード "
    
        Public Sub New()
            MyBase.New()
    
            ' この呼び出しは Windows フォーム デザイナで必要です。
            InitializeComponent()
    
            ' InitializeComponent() 呼び出しの後に初期化を追加します。
    
        End Sub
    
        ' Form は、コンポーネント一覧に後処理を実行するために dispose をオーバーライドします。
        Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
            If disposing Then
                If Not (components Is Nothing) Then
                    components.Dispose()
                End If
            End If
            MyBase.Dispose(disposing)
        End Sub
    
        ' Windows フォーム デザイナで必要です。
        Private components As System.ComponentModel.IContainer
    
        ' メモ : 以下のプロシージャは、Windows フォーム デザイナで必要です。
        'Windows フォーム デザイナを使って変更してください。  
        ' コード エディタを使って変更しないでください。
        Friend WithEvents Button1 As System.Windows.Forms.Button
         Private Sub InitializeComponent()
            Me.Button1 = New System.Windows.Forms.Button
            Me.SuspendLayout()
            '
            'Button1
            '
            Me.Button1.Location = New System.Drawing.Point(176, 16)
            Me.Button1.Name = "Button1"
            Me.Button1.Size = New System.Drawing.Size(80, 40)
            Me.Button1.TabIndex = 0
            Me.Button1.Text = "Button1"
            '
            'Form1
            '
            Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12)
            Me.ClientSize = New System.Drawing.Size(292, 273)
            Me.Controls.Add(Me.Button1)
            Me.Name = "Form1"
            Me.Text = "Form1"
            Me.ResumeLayout(False)
    
        End Sub
    
    #End Region
    
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    
        End Sub
    End Class
    

     それでも C++ の例に比べるとかなりコード量が減っており、また #Region ~ #End Region は普段折り畳んでいるから、目に見える部分はさらに少なくなります。
     とはいえ、折り畳みを解除すれば自動生成コードは容易に見えてしまいます。

  3. VB2005 以降の場合

     VB2003 以前では #Region で自動生成コードを隠すようにしていました。が、恐らくこれは不興で、VB.NET 採用を敬遠する理由の一つになっていた気がします。
     それを解決するため、VB2005 以降では Partial Class という機能 (=同一クラスを複数のファイルに記述できる仕組み) が実装されました。この機能の登場により VB2003 で #Region で囲われた部分が別のファイルに移されました。したがって、画面のコードとしては、ほぼボタン関係のコードのみが自動生成されたように見えます。

     かつて #Region で囲われた部分 は Form1.Designer.vb に移動しましたがそのファイルはデフォルトでは表示されないようになっています。

  4. VB6 の場合

     そもそも VB6 はどうだったかというと、こんなボタンが一つだけの画面の場合。

     コードはこうでした。

     VB2005 以降でも残っていた、Class ~ End Class の記述もなく、ボタンのコードしかありません。

     ボタンに関係ないコードの生成は 0 行です。また構成されるファイルも Form1 ただひとつしかないように見えます。じつにシンプルです。ファイルも "Form1" 用のファイルが一つだけあるかのように見えます。( 実際には form1.frm ファイルとは別にプロジェクトファイルがありますが。)

     VB6 の場合、プロジェクト作成直後の自動生成コードは 0 行です。「プロジェクトを作ったらたくさんコードが生成された」ということはありません。あくまで「このコンポーネントのイベントプロシージャを作りたい」という操作の後にコード生成されます。そうして自動生成されたコードもイベントプロシージャのブロック (Sub と End Sub) だけですから、VB6 で書かれているコードは、ほぼ "プログラマー自身が書いた" コードです。

  5. Windows 8 ストアアプリの場合

     ところが Windows 8 ストアアプリになると、C++ の WinForm と同じような状況を呈してきます。

     まず、プロジェクト作成時点で表示されるのは画面 (Mainpage.xaml) ではなくて、app.xaml.vb です。これが 56 行あり、これまでの from デザイナを見慣れていると、いきなりコードを突きつけられるため、結構な威圧感があります。


    ' 空のアプリケーション テンプレートについては、http://go.microsoft.com/fwlink/?LinkId=234227 を参照してください
    
    ''' <summary>
    ''' 既定の Application クラスを補完するアプリケーション固有の動作を提供します。
    ''' </summary>
    NotInheritable Class App
        Inherits Application
    
        ''' <summary>
        ''' アプリケーションがエンド ユーザーによって正常に起動されたときに呼び出されます。他のエントリ ポイントは、
        ''' アプリケーションが特定のファイルを開くために呼び出されたときに
        ''' 検索結果やその他の情報を表示するために使用されます。
        ''' </summary>
        ''' <param name="args">起動要求とプロセスの詳細を表示します。</param>
        Protected Overrides Sub OnLaunched(args As Windows.ApplicationModel.Activation.LaunchActivatedEventArgs)
            Dim rootFrame As Frame = Window.Current.Content
    
            ' ウィンドウに既にコンテンツが表示されている場合は、アプリケーションの初期化を繰り返さずに、
            ' ウィンドウがアクティブであることだけを確認してください
    
            If rootFrame Is Nothing Then
                ' ナビゲーション コンテキストとして動作するフレームを作成し、最初のページに移動します
                rootFrame = New Frame()
                If args.PreviousExecutionState = ApplicationExecutionState.Terminated Then
                    'TODO: 以前中断したアプリケーションから状態を読み込みます。
                End If
                ' フレームを現在のウィンドウに配置します
                Window.Current.Content = rootFrame
            End If
            If rootFrame.Content Is Nothing Then
                ' ナビゲーション スタックが復元されていない場合、最初のページに移動します。
                ' このとき、必要な情報をナビゲーション パラメーターとして渡して、新しいページを
                ' 構成します
                If Not rootFrame.Navigate(GetType(MainPage), args.Arguments) Then
                    Throw New Exception("Failed to create initial page")
                End If
            End If
    
            ' 現在のウィンドウがアクティブであることを確認します
            Window.Current.Activate()
        End Sub
    
        ''' <summary>
        ''' アプリケーションの実行が中断されたときに呼び出されます。アプリケーションの状態は、
        ''' アプリケーションが終了されるのか、メモリの内容がそのままで再開されるのか
        ''' わからない状態で保存されます。
        ''' </summary>
        ''' <param name="sender">中断要求の送信元。</param>
        ''' <param name="e">中断要求の詳細。</param>
        Private Sub OnSuspending(sender As Object, e As SuspendingEventArgs) Handles Me.Suspending
            Dim deferral As SuspendingDeferral = e.SuspendingOperation.GetDeferral()
            ' TODO: アプリケーションの状態を保存してバックグラウンドの動作があれば停止します
            deferral.Complete()
        End Sub
    
    End Class
    

     次に「空のアプリケーション 」テンプレートを選択した場合でも、複数のプロジェクトファイル生成されています。
     画面用のファイルがどれであるかを特定するだけでも、VB6 に比べれば困難です。

     ただ MainPage.xaml.vb 自体は、コメントや Inherits 他の自動生成コードがあるとはいえ、WinForm に比べて多量のコードが生成されるわけではありません。この点は VB2005 以降の WinForm とあまり変わりません。(Inherits Page が余計ですが)

    ' 空白ページのアイテム テンプレートについては、http://go.microsoft.com/fwlink/?LinkId=234238 を参照してください
    
    ''' <summary>
    ''' それ自体で使用できる空白ページまたはフレーム内に移動できる空白ページ。
    ''' </summary>
    Public NotInheritable Class MainPage
        Inherits Page
    
        Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
    
        End Sub
    End Class
    

     しかし、Win 8 ストアアプリの場合、Win Form と同じように適当にボタンを配置していって・・・というスタイルでアプリを作っていけるかというと必ずしもそうではありません。
     ストアアプリらしい(=Microsoftのデザインガイドに従った)アプリを作ろうとすると、上記の「空のアプリケーション」か作成するのは困難です。その場合、いくつかの「ハブアプリケーション」や「グリッドアプリケーション」のようなテンプレートが用意されており、それを選択することでストアアプリ用のフレームワークを使用することができます。

     しかし、そのテンプレートを選択した場合、やはりファイルやコードが大量に自動生成されるわけです。以下は「グリッドアプリケーション」テンプレートを選択したときの GroupedItemsPage.xaml.vb ですが、103 行のコードが自動生成されます。
     103 行のうち、ほとんどはコメントと空行で、それらを除けば正味 30 行ほどです。内容も プロパティ×2, コンストラクタ(New)×1, プロシージャ×5 であり、プロシージャの内容も 2 行程度ではあるのです。しかし、使用するためにはその意味を知る必要があり、その点で学習コストが高くなってしまっています。

    ' グループ化されたアイテム ページのアイテム テンプレートについては、http://go.microsoft.com/fwlink/?LinkId=234231 を参照してください
    
    ''' <summary>
    ''' グループ化されたアイテムのコレクションを表示するページです。
    ''' </summary>
    Public NotInheritable Class GroupedItemsPage
        Inherits Page
    
        ''' <summary>
        ''' NavigationHelper は、ナビゲーションおよびプロセス継続時間管理を
        ''' 支援するために、各ページで使用します。
        ''' </summary>
        Public ReadOnly Property NavigationHelper As Common.NavigationHelper
            Get
                Return Me._navigationHelper
            End Get
        End Property
        Private _navigationHelper As Common.NavigationHelper
    
        ''' <summary>
        ''' これは厳密に型指定されたビュー モデルに変更できます。
        ''' </summary>
        Public ReadOnly Property DefaultViewModel As Common.ObservableDictionary
            Get
                Return Me._defaultViewModel
            End Get
        End Property
        Private _defaultViewModel As New Common.ObservableDictionary()
    
    
        Public Sub New()
            InitializeComponent()
            Me._navigationHelper = New Common.NavigationHelper(Me)
            AddHandler Me._navigationHelper.LoadState,
                AddressOf NavigationHelper_LoadState
        End Sub
    
        ''' <summary>
        ''' このページには、移動中に渡されるコンテンツを設定します。前のセッションからページを
        ''' 再作成する場合は、保存状態も指定されます。
        ''' </summary>
        ''' <param name="sender">
        ''' イベントのソース (通常、<see cref="NavigationHelper"/>)
        ''' </param>
        ''' <param name="e">このページが最初に要求されたときに
        ''' <see cref="Frame.Navigate"/> に渡されたナビゲーション パラメーターと、
        ''' 前のセッションでこのページによって保存された状態の辞書を提供する
        ''' イベント データ。ページに初めてアクセスするとき、状態は null になります。</param>
        Private Async Sub NavigationHelper_LoadState(sender As Object, e As Common.LoadStateEventArgs)
            ' TODO: 問題のドメインでサンプル データを置き換えるのに適したデータ モデルを作成します
            Dim sampleDataGroups As IEnumerable(Of Data.SampleDataGroup) = Await Data.SampleDataSource.GetGroupsAsync()
            Me.DefaultViewModel("Groups") = sampleDataGroups
        End Sub
        ''' <summary>
        ''' グループ ヘッダーがクリックされたときに呼び出されます。
        ''' </summary>
        ''' <param name="sender">ボタンは、選択されたグループのグループ ヘッダーとして使用されます。</param>
        ''' <param name="e">クリックがどのように開始されたかを説明するイベント データ。</param>
        Private Sub Header_Click(sender As Object, e As RoutedEventArgs)
    
            ' ボタン インスタンスがどのグループを表すかを確認します
            Dim group As Object = DirectCast(sender, FrameworkElement).DataContext
    
            ' 適切な移動先のページに移動し、新しいページを構成します。
            ' このとき、必要な情報をナビゲーション パラメーターとして渡します
            Me.Frame.Navigate(GetType(GroupDetailPage), DirectCast(group, Data.SampleDataGroup).UniqueId)
        End Sub
    
        ''' <summary>
        ''' グループ内のアイテムがクリックされたときに呼び出されます。
        ''' </summary>
        ''' <param name="sender">クリックされたアイテムを表示する GridView (アプリケーションがスナップ
        ''' されている場合は ListView) です。</param>
        ''' <param name="e">クリックされたアイテムを説明するイベント データ。</param>
        Private Sub ItemView_ItemClick(sender As Object, e As ItemClickEventArgs)
    
            ' 適切な移動先のページに移動し、新しいページを構成します。
            ' このとき、必要な情報をナビゲーション パラメーターとして渡します
            Dim itemId As String = DirectCast(e.ClickedItem, Data.SampleDataItem).UniqueId
            Me.Frame.Navigate(GetType(ItemDetailPage), itemId)
        End Sub
    
    #Region "NavigationHelper の登録"
    
        ''' このセクションに示したメソッドは、NavigationHelper がページの
        ''' ナビゲーション メソッドに応答できるようにするためにのみ使用します。
        ''' 
        ''' ページ固有のロジックは、
        ''' <see cref="Common.NavigationHelper.LoadState"/>
        ''' および <see cref="Common.NavigationHelper.SaveState"/> のイベント ハンドラーに配置する必要があります。
        ''' LoadState メソッドでは、前のセッションで保存されたページの状態に加え、
        ''' ナビゲーション パラメーターを使用できます。
    
        Protected Overrides Sub OnNavigatedTo(e As NavigationEventArgs)
            _navigationHelper.OnNavigatedTo(e)
        End Sub
    
        Protected Overrides Sub OnNavigatedFrom(e As NavigationEventArgs)
            _navigationHelper.OnNavigatedFrom(e)
        End Sub
    #End Region
    End Class
    
  6. 結論

     そういったわけで、いまだに VB6 に変わるフレームワーク、というか開発環境が出てきていないな、といった感想。

     自動生成コードが大きい、というのは学習コストを高め、敬遠する要因を作っている気がします。
     また、ここでは触れいてませんが、WPF をはじめとした XAML 処理系は小さな機能を組み合わせることで一つの機能を作り出している、という点においては美しい設計だと思います。しかし反面、VB6 で難なくできていたことができなかったり、しようと思ったときに xaml の内部構造を掌握する必要が出てきたりなど、やはり学習コストで辛い面があります。

     Windows 8 ストアアプリを普及させるためには、VB6 程度にライトな(=内部実装を隠しまくった)開発環境の導入が必要なのではないかな?と個人的には思ったりはしています。さて今後どうなるのか・・・

[EXCEL] 祝日判定関数

2014年12月31日

 Excel でスケジュールなど日付を扱う場合に、祝日を意識しないといけない場合があります。しかし祝日は Excel の関数などから得ることができません。Windows の locale も祝日の情報までは持っていませんので、OS から得ることもできません。

 ネットから (たとえばGoogleカレンダーから) 取得するというのも一般的なようですが、かならずしもネットに接続できる環境であるとも限られないので、自前判定プログラムを書いてみました。

 下記のコードは 2014 年現在、2030 年までに対応したつもりのコードです。
 特徴は以下。

  • 春分の日と秋分の日は計算するのが面倒なので、Wikipedia から予想日をコピペしています。(汗
    前年に官報で発表される日付と違っていた場合は、適宜修正してください。
  • 春分の日と秋分の日は 2000 年から設定していますが、他の祝日は 2013 年以前の状況を反映しません。(例:みどりの日)
    あくまで今年 2014 年以降の表示用のプログラムです。過去の祝日を正確に反映したい場合は、修正が必要です。
  • 祝日法が改正されたら、適宜修正してください。(未来は予想できません)
  • おまけで「非公式な休日」を足しています。会社とかの休みを反映したい場合は場合はここへ記述します。
    以下のプログラムだとサンプルで盂蘭盆の日程とかが入っています。不要な場合は当該ルーチン、および呼び出しを削除してください。
    また「世間は祝日だがうちの会社は休みじゃない」など、祝日を打ち消すロジックは用意していません(汗
'------------------------------------------------------------------
' Excel VBA (標準モジュール)
' 日付を指定すると休みの属性に応じて、祝/休/振/日/土、を戻します。平時は空文字。
'------------------------------------------------------------------
 
'------------------------------------------------------------------
Public Function 日本の休み(vInDate As Date) As String
 
    Dim ret As String
    
    ' 祝日判定
    ret = 日本の祝日(vInDate)
    If ret <> "" Then
        日本の休み = "祝"
        Exit Function
    End If
        
    ' 国民の休日判定
    If is国民の休日(vInDate) = True Then
        日本の休み = "休"
        Exit Function
    End If
    
    ' 振替休日判定
    If is振替休日(vInDate) = True Then
        日本の休み = "振"
        Exit Function
    End If
 
    ' 非公式の休日 (盆とか県民の日とか会社の休日) / 必要に応じて
    ret = 非公式の休日(vInDate)
    If ret <> "" Then
        日本の休み = ret
        Exit Function
 
    End If
 
    ' 土日判定
    Select Case (Weekday(vInDate))
        Case 1: 日本の休み = "日"
        Case 7: 日本の休み = "土"
        Case Else: 日本の休み = ""
    End Select
 
End Function
 
 
'------------------------------------------------------------------
Private Function 日本の祝日(vInDate As Date) As String
 
    Dim strRet As String
    strRet = ""
    
    ' 2000年から2030年のまでの春分/秋分の一覧表
    ' (Wkipediaより http://ja.wikipedia.org/wiki/%E7%A7%8B%E5%88%86%E3%81%AE%E6%97%A5)
    Dim 春分 As Variant
    春分 = Array(20, 20, 21, 21, 20, 20, 21, 21, 20, 20, 21, 21, 20, 20, 21, 21, 20, 20, 21, 21, 20, 20, 21, 21, 20, 20, 20, 21, 20, 20, 20)
    
    Dim 秋分 As Variant
    秋分 = Array(23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 22, 23, 23, 23, 22, 23, 23, 23, 22, 23, 23, 23, 22, 23, 23, 23, 22, 23, 23)
    
    ' 要素分解
    Dim intyear As Integer:     intyear = Year(vInDate)
    Dim intday As Integer:      intday = Day(vInDate)
    Dim intMonth As Integer:    intMonth = Month(vInDate)
    Dim intWeekDay As Integer:  intWeekDay = Weekday(vInDate)
    Dim intWeek As Integer:     intWeek = (intday - 1)  7 + 1
    
    ' 月日固定
    If (intMonth = 1) And (intday = 1) Then strRet = "元日"
    If (intMonth = 2) And (intday = 11) Then strRet = "建国記念の日"
    If (intMonth = 4) And (intday = 29) Then strRet = "昭和の日"
    If (intMonth = 5) And (intday = 3) Then strRet = "憲法記念日"
    If (intMonth = 5) And (intday = 4) Then strRet = "みどりの日"
    If (intMonth = 5) And (intday = 5) Then strRet = "こどもの日"
    If (intMonth = 8) And (intday = 11) And (intyear >= 2016) Then strRet = "山の日"
    If (intMonth = 11) And (intday = 3) Then strRet = "文化の日"
    If (intMonth = 11) And (intday = 23) Then strRet = "勤労感謝の日"
    If (intMonth = 12) And (intday = 23) Then strRet = "天皇誕生日"
    
    ' 春分/秋分
    If (intMonth = 3 And intday = 春分(intyear - 2000)) Then strRet = "春分の日"
    If (intMonth = 9 And intday = 秋分(intyear - 2000)) Then strRet = "秋分の日"
 
    ' ハッピーマンデー(特定週の月曜日固定)
    If (intWeekDay = 2) Then    ' 月曜
        If (intMonth = 1) And (intWeek = 2) Then strRet = "成人の日"
        If (intMonth = 7) And (intWeek = 3) Then strRet = "海の日"
        If (intMonth = 9) And (intWeek = 3) Then strRet = "敬老の日"
        If (intMonth = 10) And (intWeek = 2) Then strRet = "体育の日"
        
    End If
 
    日本の祝日 = strRet
 
End Function
 
 
'------------------------------------------------------------------
Private Function is振替休日(vInDate As Date) As Boolean
 
    ' 自身が祝日であれば、振替ではない
    If 日本の祝日(vInDate) <> "" Then
        is振替休日 = False
        Exit Function
    End If
 
    ' 自身が祝日でなければ、祝日が日曜まで連続しているかを確認
    For n = 1 To 7
        Dim d As Date
        d = DateAdd("d", -n, vInDate)   ' n日前
        
        ' 日曜に至るまでに祝日が途切れれば振替でない
        If (日本の祝日(d) = "") Then
            is振替休日 = False
            Exit Function
            
        Else
            If (Weekday(d) = 1) Then
                ' 祝日であり日曜
                is振替休日 = True
                Exit Function
            
            Else
                ' LOOP(さらに前日を確認)
            
            End If
        End If
    Next
 
End Function
 
 
'------------------------------------------------------------------
Private Function is国民の休日(vInDate As Date) As Boolean
 
    ' 自身が祝日/日曜であれば、国民の休日ではない
    If (日本の祝日(vInDate) <> "") Or (Weekday(vInDate) = 1) Then
        is国民の休日 = False
        Exit Function
 
    End If
 
    If 日本の祝日(DateAdd("d", -1, vInDate)) <> "" And _
        日本の祝日(DateAdd("d", 1, vInDate)) <> "" Then
    
        is国民の休日 = True
        Exit Function
 
    End If
    
    is国民の休日 = False
 
End Function
 
 
'------------------------------------------------------------------
Private Function 非公式の休日(vInDate As Date) As String
    
    Dim strRet As String
    strRet = ""
 
    Dim intday As Integer:      intday = Day(vInDate)
    Dim intMonth As Integer:    intMonth = Month(vInDate)
 
    ' 盂蘭盆を 8/13~15 と仮定した場合
    If (intMonth = 8) And (13 <= intday And intday <= 15) Then strRet = "盆"
 
    ' 群馬県民の日
    If (intMonth = 10) And (intday = 28) Then strRet = "群"
    
    非公式の休日 = strRet
 
End Function

 使い方は以下。

  • 上記コードを、標準モジュールにコピペする。
  • Excel のセルに「=日本の休み(対象の日付)」といった感じで記述する。

[PS1] Powershell で正規表現 (複数のMatch)

2014年12月28日

 複数の箇所に match する場合、-match 演算子では最初の match しか $Matches に登録されません。

PS > "AB12-AB345" -match "AB[0-9]+"
True
PS > $Matches

Name                           Value
----                           -----
0                              AB12

 複数マッチの場合は regex を素直に使うようにします。

PS > $a = [RegEx]::Matches("AB12-AB345", "AB[0-9]+")
PS > $a | % {$_.Value}
AB12
AB345

 また、match 部分に名前を付けた場合は、.Groups プロパティ経由でアクセスします。

PS > $a = [RegEx]::Matches("AB12-AB345", "AB(?<name>[0-9]+)")
PS > $a | % {$_.Groups["name"].Value}
12
345

[環境] コントロールパネル系プログラムの一覧

2014年12月27日

 コントロールパネルのウインドウを「ファイル名を指定して実行」などで、一発起動したい場合の呼び出し方一覧。

  • コントロール パネルプログラムプログラムと機能

    appwiz.cpl

  • コントロール パネルデスクトップのカスタマイズディスプレイ画面の解像度

    desk.cpl

  • コントロール パネルデスクトップのカスタマイズ個人設定

    control DESKTOP

  • コントロール パネルデスクトップのカスタマイズ個人設定色とデザイン

    control COLOR

  • コントロール パネルシステムとセキュリティアクション センター

    wscui.cpl

  • コントロール パネルシステムとセキュリティWindows ファイアウォール

    Firewall.cpl

  • コントロール パネルシステムとセキュリティ管理ツール

    control ADMINTOOLS

  • コントロール パネルハードウェアとサウンド電源オプション

    powercfg.cpl

  • コントロール パネルハードウェアとサウンドデバイスとプリンター

    control PRINTERS

  • コントロール パネルネットワークとインターネットネットワーク接続

    ncpa.cpl
    control NETCONNECTIONS

  • コントロール パネルユーザー アカウントユーザー アカウント

    control USERPASSWORDS

  • ユーザーアカウント

    control USERPASSWORDS2

  • デバイスマネージャー

    hdwwiz.cpl
    devmgmt.msc

  • システムのプロパティ

    sysdm.cpl

  • インターネットのプロパティ

    inetcpl.cpl

  • 地域

    intl.cpl
    control INTERNATIONAL

  • ゲームコントローラー

    joy.cpl

  • マウスのプロパティ

    main.cpl
    control MOUSE

  • キーボードのプロパティ

    control KEYBOARD

  • サウンド

    mmsys.cpl

  • 所在地情報

    telephon.cpl
    control TELEPHONY

  • 日付と時刻

    timedate.cpl
    control DATE/TIME

  • ペンとタッチ?

    TabletPC.cpl

  • Bluetooth?

    bthprops.cpl

  • ワイヤレス リンク?

    irprops.cpl

  • フォルダーオプション

    control FOLDERS

  • スキャナーとカメラ

    control SCANNERCAMERA

  • インテル(R) グラフィック/メディア・コントロール・パネル

    igfxcpl.cpl

  • 承認マネージャー

    azman.msc

  • 証明書 - ローカルコンピューター

    certlm.msc

  • 証明書 - 現在のユーザー

    certmgr.msc

  • コンポーネントサービス

    comexp.msc

  • コンピューターの管理

    compmgmt.msc

  • ディスクの管理

    diskmgmt.msc

  • イベントビューアー

    eventvwr.msc

  • 共有フォルダー

    fsmgmt.msc

  • ローカルグループポリシーエディター

    gpedit.msc

  • ローカルユーザーとグループ

    lusrmgr.msc

  • NAP クライアントの構成

    NAPCLCFG.MSC

  • パフォーマンスモニター

    perfmon.msc

  • 印刷の管理

    printmanagement.msc

  • ポリシーの結果セット

    rsop.msc

  • ローカルセキュリティポリシー

    secpol.msc

  • サービス

    services.msc

  • タスクスケジューラー

    taskschd.msc

  • コンピュータのトラステッドプラットフォームモジュール(TPM)の管理

    tpm.msc

  • Hyper-V マネージャー

    virtmgmt.msc

  • セキュリティが強化された Window ファイアウォール

    WF.msc

  • Wmi マネージャー

    WmiMgmt.msc

[VB.NET] Date型でタイムゾーンを扱う場合の注意点

2014年12月26日

 Date型 (C# では Datetime 型) でタイムゾーンを意識する場合、注意が必要です。

 まず Date 型は 3つのタイムゾーン(モード)を持っています。

  1. ローカル時 (.Kind = DateTimeKind.Local)

     Dim d1 = Date.Now とした場合のモード。
     OS の時計の表示と同じ。

  2. 世界標準時 (.Kind = DateTimeKind.Utc)

     Dim d2 = Date.UtcNow とした場合のモード。
     世界標準時として記憶する。タイムゾーンが JST の場合、OSの表示 -9 時間になる。 

  3. 不定 (.Kind = DateTimeKind.Unspecified)

     Dim d3 = Date.Parse("2013/7/1 12:00:00") のように「タイムゾーン」を指定しなかった場合のモード。
     指定した時刻そのものを記憶する。

 これらのモードを意識せずに扱った場合、様々な問題を引き起こす可能性があります。
 たとえば以下のような問題が予想されます。

  • 問題1:上記d1 と 上記d2 を差し引くと、-9:00 が算出される

     コード

    Dim d1 = Date.Now 
    Dim d2 = Date.UtcNow
    Console.Writeline(d2 - d1)
    

     結果

    -9:00:00
    
  • 問題2:タイムゾーンが不定な時刻を、世界標準時,ローカル時に変換した場合

     コード

    Dim a = Date.Parse("2013/7/6")
    Console.WriteLine(a)
    Console.WriteLine(a.ToUniversalTime)
    Console.WriteLine(a.ToLocalTime)
    

     結果

    2013/07/06 0:00:00
    2013/07/05 15:00:00     ← 変換前時刻 -9:00 (変換前時刻が ローカル時 だとして計算)
    2013/07/06 9:00:00      ← 変換前時刻 +9:00 (変換前時刻が 世界標準時 だとして計算)
    
  • 問題3:タイムゾーンは比較の対象外

     コード

    Dim a = New Date(2013, 7, 6, 0, 0, 0, DateTimeKind.Local)
    Dim b = New Date(2013, 7, 6, 0, 0, 0, DateTimeKind.Utc)
    Dim c = New Date(2013, 7, 6, 0, 0, 0, DateTimeKind.Unspecified)
    Console.WriteLine(a = b)
    Console.WriteLine(a = c)
    

     結果

    True
    True
    
  • 問題4:タイムゾーンなし文字列だと不定モードで評価され、タイムゾーンつき文字列だとローカル時に変換される

     コード

    Dim a = Date.Parse("2013/7/6")
    Dim b = Date.Parse("2013/7/6 +1:00")
    
    Console.WriteLine("{0} {1}", a, a.Kind.ToString)
    Console.WriteLine("{0} {1}", b, b.Kind.ToString)
    

     結果

    2013/07/06 0:00:00 Unspecified   ← 不定モードで評価
    2013/07/06 8:00:00 Local         ← タイムゾーンを考慮の上、ローカル時に変換 (元のタイムゾーン情報は失われる)
    

[Raspi] Raspberry Pi でサイネージ端末

2014年12月26日

 Raspberry PI を使用してサイネージ端末を作る手順のメモ。
 仕様は以下の通り

・あるウェブサイト (192.168.0.1:60000/index.html) を全画面で表示する。
・電源ONで上記webサイトに自動接続。
・Web ブラウザに chromium を採用。(=AJAX が可能であること)
・自IP は 192.168.0.254。
・Windows からリモートデスクトップ接続(mstsc)にて操作可能とする。

 以下設定方法

  1. 前提

     noobs のセットアップが済んでいる。
     noobs による raspbian のセットアップは完了している。
     (なお、raspbian を再セットアップする場合は、Shiftキーを押しながら起動すると noobs が起動します。)

  2. ディスプレイの設定(SSH 接続)

     sudo nano /boot/config.txt

    hdmi_force_hotplug=1
    hdmi_group=2
    hdmi_mode=16
    

     手元の環境では、デフォルト設定だと映像出力が行われなかったので、出力モードを固定に変更しています。
     本設定をしなくても問題がない場合はこの設定は不要です。

  3. raspi-config

     sudo raspi-config

    • 3.Enable Boot to Desktop/Scratch

      "Desktop Log in as user 'pi' at the Graphical desktop"を選択

    • 4 Internationalisation Oprions
      • I1 Change Locale

         "ja_JP.UTF-8 UTF-8" を選択

      • I2 Change Timezone

         "アジア" → "東京" を選択

      • I3 Change Keyborad Layout

         "標準105キー" → "日本語(OADG 109A)" → "デフォルト" → "コンポーズキーなし" → "はい"

         本体に接続しているキーボードをスキャンするようなので、本体にキーボードが刺さっていないと設定ができない点に注意。(SSH接続時等注意)

          

  4. 各種ソフトウェアのインストール

     sudo apt-get install chromium
     sudo apt-get install xrdp
     sudo apt-get install vino

  5. IP アドレスの設定

     sudo nano /etc/network/interfaces

    iface eth0 inet static
    address 192.168.0.254
    netmask 255.255.255.0
    gateway 0.0.0.0
    

     iface eth0 inet dhcp を上記に変更。

  6. xrdp の設定 (Remote Desktop Protocol Server)

     sudo nano /etc/xrdp/xrdp.ini

    [xrdp0]
    name=Active Local Login
    lib=libvnc.so
    username=
    password=ask
    ip=127.0.0.1
    port=5900
    

     上記を適当な位置に追記。

  7. スタートアップの設定 (LXDE)

     mkdir ~/.config
     mkdir ~/.config/lxsession
     mkdir ~/.config/lxsession/LXDE
     nano ~/.config/lxsession/LXDE/autostart

    @chromium --kiosk http://192.168.0.1:60000/index.html
    /usr/lib/vino/vino-server
    

     chromium を kiosk (全画面) モードで起動しています。
     vino(vnc server) を起動しています。

  8. vino の設定 (VNC Server)

     vino-preferences

    vino-preferences

     パスワード等はなしで設定しています。

  9. 再起動

     sudo shutdown -r now

[PS1] Powershell で tail -f

2014年12月22日

 ログファイルを継続的に参照する場合に、追記のみを待ち受けて表示してほしい状況があります。

 unix系 や cygwin だと tail コマンドがあってたとえば以下のように書ける(らしい)。

tail test.txt -f -n 0

 Windows で行う場合、コマンドプロンプトでは tail コマンドに相当するコマンドが標準では用意されていませんが、Powershell では以下のように書けば同じことができるようです。

Get-Content .test.txt -Wait -Tail 0 

 また、-tail オプションは Powershell 2 系では使用できないようで、そうすると素の Windows 7 では使えません。この場合は、-tail オプションの代わりに select を通すことで似た結果が得られます。

Get-Content .test.txt -Wait | select -Skip (gc .test.txt).Count

 注意点としては、Get-Content の特性のせいか、保存された時点までの差分を pipe に渡します。このため、保存された箇所で改行しているかのように表示されてしまいます。

 たとえば、一行のテキスト "aa bb cc "を、

  1.  "aa " を入力して保存 (CTRL + S)
  2.  "bb " を入力して保存 (CTRL + S)
  3.  "cc " を入力して保存 (CTRL + S)

とすると、テキストファイル本体は "aa bb cc " で一行ですが、Powershell での表示は 3行で表示されます。

 ここを確実に扱いたい場合は、Cygwin の tail コマンドを持ち込まないとダメですかね…といった感じです(汗

[Oracle] [VB.NET] ODP.NET で Merge を行うと AccessViolationException が発生する

2014年12月22日

 ODP.NET を使用して MERGE 文を実行したところ、AccessViolationException が発生しました。
 原因は不明。また、どんな環境でも再現するかどうかは不明ですが、その環境では再現性があったので、念のためメモ。

 以下がそのコード。

'参照設定 Oracle.DataAccess, Version=4.112.3.0, Culture=neutral
Imports Oracle.DataAccess.Client

Module Module1

    Sub Main()
        Dim conn = New OracleConnection("User Id=scott; Password=tiger; Data Source=????")
        conn.Open()
        Dim tr = conn.BeginTransaction

        Dim cmd = New OracleCommand(
                    "merge into EMP " &
                    "     using DUAL " &
                    "        on (EMP.EMPNO = :empno) " &
                    "  when matched then " &
                    "      update set ENAME    = :ename " &
                    "                 JOB      = :job " &
                    "                 MGR      = :mgr " &
                    "                 HIREDATE = :hiredate " &
                    "                 SAL      = :sal " &
                    "                 COMM     = :comm " &
                    "                 DEPTNO   = :deptno " &
                    "  when not matched then " &
                    "      insert ( EMPNO,  ENAME,  JOB,  MGR,  HIREDATE,  SAL,  COMM,  DEPTNO) " &
                    "      values (:empno, :ename, :job, :mgr, :hiredate, :sal, :comm, :deptno) ",
                conn)

        cmd.Parameters.Add("empno", 9999)
        cmd.Parameters.Add("ename", "BBBB")
        cmd.Parameters.Add("job", "CLERK")
        cmd.Parameters.Add("mgr", "")
        cmd.Parameters.Add("hiredate", Now)
        cmd.Parameters.Add("sal", 100)
        cmd.Parameters.Add("comm", "CCCC")
        cmd.Parameters.Add("deptno", 10)

        Dim q = cmd.ExecuteNonQuery()

        tr.Commit()

        conn.Clone()
    End Sub

End Module

 結果

[与太] Windows 8.1 で Edlin

2014年12月22日

 時折「昔は Edlin というエディタがあってだな…」と語るおっさんな友人がおりまして。いや、Win7 や Win8 にはもうないよ、と答えていたのですが、ないのは x64 版だけで、私が x64 Windows を使っているからないと思っていただけで、実は x86 Windows にはいまだに存在することが分かったので、反省の意味を込めてここに記す(汗

 ちなみに edlin というのはテキストエディタの一種で「ラインエディタ」と呼ばれるものです。今使われる多くのテキストエディタはスクリーンエディタと呼ばれるものですが、その出現以前に使用されていたエディタです。行の単位で編集するエディタです。すくなくとも MS-DOS 2.11 あたりでは OS 標準のエディタでした。

 

 使ってみます。
 コマンドプロンプトで edlin と入力すると、初回、なにやらインストールしますよとダイアログが出ます。

 NTVDM というのは、仮想 DOS マシンのことらしいです。http://ja.wikipedia.org/wiki/仮想DOSマシン
 ここは素直にインストールします。

 インストールしてしまえば、edlin ファイル名、といった感じで普通に使えます。

 ただ、コードページが us になるという、よくわからない現象が生じます。(=フォントが変わる)
 このままだと日本語が使えなさそうですが、本気で使う予定はないので、深追いはしないことに。

 起動直後はコマンドモードです。ここで i と打てば挿入モードで編集モードに移行し、行が追加できます。
 入力を終了したら、CTRL + Z を押すと、コマンドモードに戻ります。
 w で書き込み、e で終了です。

 edlin を終了すると、コードページが jp に戻ります。(=変わったフォントが元に戻る)
 type コマンドで、test.txt が編集した通りになっていることを確認できます。

 もう一度 edlin で test.txt を開いてみます。
 内容を確認する場合は、l を入力します。「える」です。List の l です。
 修正したい場合は、修正したい行番号を入力します。
 上記の例では 3 行目を 「hello edlin !」から「Goodby edlin !」に変更しています。

 type コマンドで確認すると、編集の結果が反映されていることが確認できます。

 

 Win 8.1 でも edlin が健在なことを確認しました。
 しかしきっともう使わない。
 さようなら edlin。もう会うことはないと思う!

[VB.NET] Linq to XML メモ その2

2014年12月20日

 XML のデータを検索してみる。
 前エントリで書いたように、どう書けば何が選択されるかがわかると割と平易になってきます。

Sub Main()

    Dim xdoc = <?xml version="1.0" encoding="UTF-8"?>
               <Menu>
                   <Button>
                       <背景色 id="2">赤</背景色>
                       <背景色 id="4">緑</背景色>
                   </Button>
                   <Button>
                       <背景色 id="7">白</背景色>
                       <背景色 id="0">黒</背景色>
                   </Button>
               </Menu>

    Dim a = From x In xdoc.<Menu>.<Button>.<背景色>
            Where x.@id >= 4
            Select x.Value

    Console.WriteLine("------")
    For Each n In a
        Console.WriteLine(n)
    Next

    Dim b = From x In xdoc.<Menu>.<Button>(0).<背景色>
             Where x.@id >= 4
             Select x.Value

    Console.WriteLine("------")
    For Each n In b
        Console.WriteLine(n)
    Next

End Sub

 結果

------
緑
白
------
緑

[PS1] Powershell で「タスクバーを自動的に隠す」を設定する

2014年12月20日

 「タスクバーを自動的に隠す」を Script で設定したいのですが、調べたところ、グループポリシー操作でもレジストリ操作でもだめらしいです。
 私が調べた限りにおいては、「タスクバーを自動的に隠す」は GUI から操作する以外に方法はない模様。

 なんでだよ…(汗

 そこで、Powershell で UIAutomation という Extension を使うと、GUI の自動操作ができるという情報をいただいたので試してみました。

 実際に使ってみます

  1. UI Automation PowerShell Extensions のダウンロード

     まずここから、PowerShell の Extension をダウンロードします。
     UI Automation PowerShell Extensions

     使い方一般については以下が親切です。
     WindowsアプリのUI自動操作をUI Automation PowerShell Extensionで行う

  2. ダウンロードした zip を展開する

     ダウンロードした zip を展開します。

  3. Powershell を Version 2 で起動する

     Powershell を Version 2 で起動します。Win8 とかだと Version 4 とかで起動しますが、Vesion 4 だと PowerShell Extensions が使用できません。(12/20現在)

    C> powershell -version 2
    
  4. Import-Module を実行する

     解凍したフォルダに移動して、以下を実行します。

    Import-Module .UIAutomation
    
  5. 以下のスクリプトを実行する

     以下のスクリプトは、「タスクバーを自動的に隠し」「タスクバーの位置を上側に」しています。

    # 「タスク バーとナビゲーションのプロパティ」ウインドウの起動
    Start-Process -FilePath "rundll32.exe" `
                  -ArgumentList "shell32.dll,Options_RunDLL 1"
    
    # 「タスク バーとナビゲーションのプロパティ」ウインドウ
    $win = Get-UiaWindow -Name 'タスク バーとナビゲーションのプロパティ' 
    
    # 「タスクバー」タブ
    $tab = $win |
      Get-UiaTab -Class 'SysTabControl32' | 
      Get-UiaTabItem -Name 'タスク バー'
    
    # 「自動的に隠す」チェックボックス
    $box = $tab | Get-UiaCheckBox -Name 'タスク バーを自動的に隠す(U)'
    
    # 「自動的に隠す」チェックボックスがチェックされていなければ、チェックして OK ボタンを押す
    if (($box | Get-UiaButtonToggleState) -eq $false) { 
      $box | Invoke-UiaCheckBoxToggle
    }
    
    # 「画面上のタスク バーの位置」を変更
    $comboBase = $tab |
     Get-UiaComboBox -Name '画面上のタスク バーの位置(T):'
    
    $comboBase |
     Get-UiaListItem -Name '上' |
     Invoke-UiaListItemSelectItem -ItemName '上'
    
    # ウインドウを閉じる
    $win | Get-UiaButton -Name 'OK' | Invoke-UiaButtonClick
    

[VB.NET] Linq to XML メモ

2014年12月20日

 VB で Linq2XML で… というか XDocument をアクセスしてみる。
 ポイントは以下。

  • 要素名は不等号でくくる
  • 属性名は@を先頭につける
  • 要素名と要素名の間、要素名と属性名の間は . で区切る
  • VB であっても要素名や属性名は大文字小文字区別なので注意
Sub Main()

    ' ↓ 外部ファイル読み込み「Dim xdoc = XDocument.Load(".test.xml")」の代わり
    Dim xdoc = <?xml version="1.0" encoding="UTF-8"?>
               <Menu>
                   <Button>
                       <背景色 id="2">赤</背景色>
                       <背景色 id="4">緑</背景色>
                   </Button>
                   <Button>
                       <背景色 id="7">白</背景色>
                       <背景色 id="0">黒</背景色>
                   </Button>
               </Menu>

    ' 直接要素(InnerText)を参照する
    Dim a = xdoc.<Menu>.<Button>(0).<背景色>(1).Value
    Console.WriteLine("------")
    Console.WriteLine(a)

    ' 直接属性を参照する
    Dim b = xdoc.<Menu>.<Button>(0).<背景色>(1).@id
    Console.WriteLine("------")
    Console.WriteLine(b)

    ' 要素をコレクションで取得する
    Dim c = xdoc.<Menu>.<Button>(1).<背景色>
    Console.WriteLine("------")
    For Each d In c
        Console.WriteLine("{0}", d.Value)
    Next

End Sub

 結果

------
緑
------
4
------
白
黒

[PS1] デバイス名からNICの情報にアクセスする

2014年12月19日

 NIC の情報を GUI ではなくコマンドラインで取得/設定したい場合があります。

 ただし、ネットワーク名(下記画像だと "vEthernet …")ではなく、デバイス名(下記画像だと "Hyper-V 仮想イーサネット アダプター #2" )でアクセスしたい、という制約があるとします。

 Windows 8 以降の Powershell を使用する場合、NetTCPIP モジュールが追加されており、このモジュールを使うことで NIC の情報にアクセスできます。

$searchName = "Hyper-V 仮想イーサネット アダプター #2"
$na = Get-NetAdapter -InterfaceDescription $searchName
Get-NetIPAddress -ifIndex $na.ifIndex

 しかし、Windows 7 以前の場合、NetTCPIP モジュールが組み込めないため、この方法が使えません(Win8系の新機能)

 仕方がないので、WMI へのアクセスと netsh で実現します。

$searchName = "Hyper-V 仮想イーサネット アダプター #2"
$na = Get-WmiObject Win32_NetworkAdapter | ? {$_.Name -eq $searchName}
netsh interface ip show config $na.NetConnectionID

 wmi を参照しているのは、netsh ではデバイス名からネットワーク名を引くことができないからです。

[PS1] Powershell で TCPing もどき

2014年12月17日

 Ping だと遮断されていたり、遮断されていなくても当該ポートへ到達できるかどうかというのはわからなかったりします。

 そういうとき、TCPing というツールがあるようで、これを使うとTCPのポートが開いているかどうかを確認できます。
 http://www.elifulkerson.com/projects/tcping.php
 Microsoft 純正だと、PortQry というツールがあるらしいです。
 http://support.microsoft.com/kb/310099/ja

 ただ、外部ツールのインストールは難しい… という場合が多々あります。その場合には、Powershell を使うと似たようなことができます。たとえば以下のような感じ。

# ipv4,ipv6 共通処理
Function executeTCPing($tcp) {

    $sw = New-Object System.Diagnostics.Stopwatch

    $sw.Start()
    try {
        $tcp.Connect($target, $port)
        $sw.Stop()
        Write-Output ("{0} port={1} ({2}) への接続: 時間 ={3:0.00}ms" -f $target, $port, $tcp.Client.RemoteEndPoint, ($sw.Elapsed).TotalMilliseconds)

    } catch {
        $sw.Stop()
        Write-Output ("{0} port={1} への接続: 応答が確認できません" -f $target, $port, ($sw.Elapsed).TotalMilliseconds)

    } finally {
        $tcp.Close()
        $tcp.Dispose()   # エラーが出る場合は無くても良い

    }
}

# ipv4 用
Function TCPing($target, $port) {
    $tcp = New-Object System.Net.Sockets.TcpClient
    executeTCPing $tcp
}

# ipv6 用
Function TCPing6($target, $port) {
    $ipv6 = [System.Net.Sockets.AddressFamily]::InterNetworkV6 
    $tcp = New-Object System.Net.Sockets.TcpClient $ipv6
    executeTCPing $tcp
}

 使い方はこんな感じ

PS > TCPing localhost 445
localhost port=445 (127.0.0.1:445) への接続: 時間 =0.81ms

PS > TCPing localhost 80
localhost port=80 への接続: 応答が確認できません

PS > TCPing6 localhost 445
localhost port=445 ([::1]:445) への接続: 時間 =0.91ms

[PS1] Powershell で CSV ファイルを読み込む

2014年12月17日

 PowerShell Advent Calendar 2014 17日目の記事です。

 CSV ファイルを参照するとき、その PC に Excel が入っていればよいのですが、そうでない場合メモ帳で・・・、となります。しかし素の csv テキストを目で追うというのは辛いです(汗

 Windows 7 以降だと Powershell が標準でインストールされているので、それを使えば、Excel 程ではないですがある程度目に優しい形に整形することが可能です。

 たとえば、以下のような sample.csv を操作する場合を考えます。

都道府県コード,*,*,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47
都道府県,,,北海道,青森県,岩手県,宮城県,秋田県,山形県,福島県,茨城県,栃木県,群馬県,埼玉県,千葉県,東京都,神奈川県,新潟県,富山県,石川県,福井県,山梨県,長野県,岐阜県,静岡県,愛知県,三重県,滋賀県,京都府,大阪府,兵庫県,奈良県,和歌山県,鳥取県,島根県,岡山県,広島県,山口県,徳島県,香川県,愛媛県,高知県,福岡県,佐賀県,長崎県,熊本県,大分県,宮崎県,鹿児島県,沖縄県
県庁所在地,,,札幌市,青森市,盛岡市,仙台市,秋田市,山形市,福島市,水戸市,宇都宮市,前橋市,さいたま市,千葉市,新宿区,横浜市,新潟市,富山市,金沢市,福井市,甲府市,長野市,岐阜市,静岡市,名古屋市,津市,大津市,京都市,大阪市,神戸市,奈良市,和歌山市,鳥取市,松江市,岡山市,広島市,山口市,徳島市,高松市,松山市,高知市,福岡市,佐賀市,長崎市,熊本市,大分市,宮崎市,鹿児島市,那覇市
面積,km2,,8万3456.20,9607.04,1万5278.77,7285.73,1万1612.22,9323.44,1万3782.75,6095.69,6408.28,6363.16,3797.25,5156.58,2187.42,2415.84,1万2583.46,4247.4,4185.47,4189.27,4465.37,1万3562.23,1万0621.17,7780.09,5164.06,5776.87,4017.36,4613,1896.83,8395.47,3691.09,4726.12,3507.26,6707.57,7113,8478.52,6112.22,4145.69,1876.47,5677.38,7105.01,4976.17,2439.58,4095.22,7405.21,6339.33,7734.78,9187.8,2275.28
可住地面積,km2,,21899.07,3203.45,3709.81,3129.79,3154.52,2850.03,4218.02,3975.87,2946.03,2294.56,2565.6,3487.76,1395.86,1459.51,4481.28,1850.29,1382.73,1066.03,950.33,3333.82,2144.72,2730.98,2951.63,2021.76,1289.04,1155.06,1313.87,2755.57,850.53,1097.31,912.01,1255.99,2210.47,2254.82,1749.12,1021.5,991.25,1669.78,1168.16,2737.19,1339.8,1617.12,2745.58,1769.05,1835,3242.51,1159.05
可住地比率,%,,26.2,33.3,24.3,43,27.2,30.6,30.6,65.2,46,36.1,67.6,67.6,63.8,60.4,35.6,43.6,33,25.4,21.3,24.5,20.2,35.1,57.3,35,32.1,25,69.4,32.8,23,23.2,26,18.7,31.1,26.6,28.6,24.6,52.9,29.4,16.4,55.1,54.9,39.5,37.1,27.9,23.7,35.3,51
人口,国勢調査,2010,"5,506,419","1,373,339","1,330,147","2,348,165","1,085,997","1,168,924","2,029,064","2,969,770","2,007,683","2,008,068","7,194,556","6,216,289","13,159,388","9,048,331","2,374,450","1,093,247","1,169,788","806,314","863,075","2,152,449","2,080,773","3,765,007","7,410,719","1,854,724","1,410,777","2,636,092","8,865,245","5,588,133","1,400,728","1,002,198","588,667","717,397","1,945,276","2,860,750","1,451,338","785,491","995,842","1,431,493","764,456","5,071,968","849,788","1,426,779","1,817,426","1,196,529","1,135,233","1,706,242","1,392,818"
人口,国勢調査,2005,"5,627,737","1,436,657","1,385,041","2,360,218","1,145,501","1,216,181","2,091,319","2,975,167","2,016,631","2,024,135","7,054,243","6,056,462","12,576,601","8,791,597","2,431,459","1,111,729","1,174,026","821,592","884,515","2,196,114","2,107,226","3,792,377","7,254,704","1,866,963","1,380,361","2,647,660","8,817,166","5,590,601","1,421,310","1,035,969","607,012","742,223","1,957,264","2,876,642","1,492,606","809,950","1,012,400","1,467,815","796,292","5,049,908","866,369","1,478,632","1,842,233","1,209,571","1,153,042","1,753,179","1,361,594"
人口,国勢調査,1995,"5,692,321","1,481,663","1,419,505","2,328,739","1,213,667","1,256,958","2,133,592","2,955,530","1,984,390","2,003,540","6,759,311","5,797,782","11,773,605","8,245,900","2,488,364","1,123,125","1,180,068","826,996","881,996","2,193,984","2,100,315","3,737,689","6,868,336","1,841,358","1,287,005","2,629,592","8,797,268","5,401,877","1,430,862","1,080,435","614,929","771,441","1,950,750","2,881,748","1,555,543","832,427","1,027,006","1,506,700","816,704","4,933,393","884,316","1,544,934","1,859,793","1,231,306","1,175,819","1,794,224","1,273,440"
人口,国勢調査,1970,"5,184,287","1,427,520","1,371,383","1,819,223","1,241,376","1,225,618","1,946,077","2,143,551","1,580,021","1,658,909","3,866,472","3,366,624","11,408,071","5,472,247","2,360,982","1,029,695","1,002,420","744,230","762,029","1,956,917","1,758,954","3,089,895","5,386,163","1,543,083","889,768","2,250,087","7,620,480","4,667,928","930,160","1,042,736","568,777","773,575","1,707,026","2,436,135","1,511,448","791,111","907,897","1,418,124","786,882","4,027,416","838,468","1,570,245","1,700,229","1,155,566","1,051,105","1,729,150","945,111"
人口,国勢調査,1945,"3,518,389","1,083,250","1,227,789","1,462,254","1,211,871","1,326,350","1,957,356","1,944,344","1,546,355","1,546,081","2,047,261","1,966,862","3,488,284","1,865,667","2,389,653","953,834","887,510","724,856","839,057","2,121,050","1,518,649","2,220,358","2,857,851","1,394,286","860,911","1,603,796","2,800,958","2,821,892","779,685","936,006","563,220","860,275","1,564,626","1,885,471","1,356,491","835,763","863,700","1,361,484","775,578","2,746,855","830,431","1,318,589","1,556,490","1,124,513","913,687","1,538,466",-
人口,国勢調査,1920,"2,359,183","756,454","845,540","961,768","898,537","968,925","1,362,750","1,350,400","1,046,479","1,052,610","1,319,533","1,336,155","3,699,428","1,323,390","1,776,474","724,276","747,360","599,155","583,453","1,562,722","1,070,407","1,550,387","2,089,762","1,069,270","651,050","1,287,147","2,587,847","2,301,799","564,607","750,411","454,675","714,712","1,217,698","1,541,905","1,041,013","670,212","677,852","1,046,720","670,895","2,188,249","673,895","1,136,182","1,233,233","860,282","651,097","1,415,582","571,572"
  1. 前準備 (読み込み)
    1. ファイルの読み込む

       まず gc (Get-Content) で読み込みます。
       エンコード指定が必要であれば -Encoding を使用して指定します

      $txt = gc .sample.csv
      
    2. 読み込んだファイルをcsvファイルとして評価する

       読み込んだファイルを csv ファイルとして評価します。
       -Header 指定なしで読み込むと一行目を列名として評価してくれます。しかし一行目に重複する項目がありエラーになる場合や、ヘッダーなしCSVファイルの場合などは -header を指定します。
       上記 sample.csv の場合、一行目の 2 列目と 3 列目が同じ値があるために、列名の評価に失敗します。したがって、-header に @(1..50) を与えています。# まともに列名を付けても良いですが、面倒くさいので(汗

      $csv = $txt | ConvertFrom-Csv -Header @(1..50)
      
  2. 参照
    1. 全体を見る

       評価した結果が $csv に入っているので、それを ogv (Out-GridView) で参照します。

      $csv | ogv
      
    2. 特定の行を選択する

       たとえば、3行目だけ見たい場合。(配列は 0 オリジン)

      $csv[2] | ogv
      
    3. 特定の行(複数行)を選択する

       たとえば、4行目~6行目を見たい場合。

      $csv[3..5] | ogv
      
    4. 連続でない複数行を選択する

       たとえば、2行目と7行目から最後のあたりまで見たい場合は以下。99 は「最終行よりも大きい」程度の意味です。

      $csv[@(1) + @(6..99)] | ogv
      
    5. 16列と26列と30列を射影する

       ogv (Out-GridView) はなぜか 30列までしか表示してくれません。(誰か 30 列以上表示させる方法教えて!)
       なので、見たい行のみを指定します。
       指定の際、select 1,2,… ではなく select "1","2",… のように項目名をダブルクォーテーションで囲んでおく所がポイントです。

      $csv | select "1","2","16","26","30" | ogv
      
    6. 列の内容によって選択する

       たとえば、3列目が 1989 以上を選択したい場合。

      $csv[@(1)+@(6..99)] | ? {$_."3" -ge 1989} | ogv
      
    7. 複数の選択結果を合成する (Union)

       異なる選択結果を選択結果を足し合わせることができます。

      $a = @($csv[1])
      $b = $csv[6..99] | ? {$_."3" -ge 1989}
      $a + $b | ogv
      
    8. 新しい列を作る

       新しい計算列を作る場合、Name に列名、Expression に列を生成するコード(無名関数)を設定します。

      $csv | ? {$_."1" -eq "人口"} |
        select "3",
               @{Name="東京"; Expression={"{0:0}万人" -f ($_."16" / 10000) }} |
        ogv
      

[与太] ゆるい広島の電停事情

2014年12月17日

 ゆるい広島 Advent Calendar 2014 17日目の記事です。
 昨日の@csc_kamera25 さんは「広島のIT勉強会に広報・宣伝・勉強会しに来ませんか?」という内容でした。

 google map とかで広島の地図を見ているとですね。「~駅」って表示がそこかしこにあるのです。たとえばこんな。

 それは駅じゃねーよ。電停だろ!(汗

 ・・・

 現物はこんな感じです。

 屋根はあるけど、駅舎があるわけでも改札があるわけでもないです。ほんのちょっと路面よりも高いプラットフォームがあるだけです。

 wikipedia (http://ja.wikipedia.org/wiki/土橋停留場) でも「土橋停留場(どばしていりゅうじょう、通称:土橋電停 )」と書いてあり、"電停" であり "駅" ではないのです。
 

 なので、広島市民に「土橋駅」と言っても通用しません。「電停」と呼称しましょう。

 google map がなんと言おうとも、です。

 広島県外の方、ご注意を・・・

 ・・・

 ところで、上記「土橋」の次の電停が「小網町」なのですが、ここがすごい。

 google map だと、やはり「小網町 "駅"」なのですが。

 現物はこう。

 プラットフォームすらありません。なにこのペイント(汗

 そして駅名標はこんなところに…

 対岸の電柱っぽい何かにくくってありますか?てな趣き。

 しかも駅名標のその下に時刻表があるのですが、見たいときは横断歩道のない道路を横断する必要が・・・ #危険注意

 これを見て、なお、"駅" と呼びたくば呼べ! (ぉ

 以上、名称も現物も、なにかとゆるい広島の電停事情についてお話ししました・・・(汗

 明日のゆるい広島 Advent Calendar 2014@kanrame さん です。明日もお楽しみに!

[VB.NET][PS1] VB から Powershell Script を呼び出す

2014年12月12日

 VB から Powershell Script を呼び出す方法についてのメモ。

  1. まずは参照設定を行います。 (Windows 8 x64 の場合)
    C:Program Files (x86)Reference AssembliesMicrosoftWindowsPowerShell3.0System.Management.Automation.dll
    
  2. Imports しておきます。
    Imports System.Management.Automation
    Imports System.Management.Automation.Runspaces
    
  3. 標準入力を与えて、msg.exe を起動した場合のサンプル。
    Using rs = RunspaceFactory.CreateRunspace()
        rs.Open()
    
        Using ps = PowerShell.Create
            ps.Runspace = rs
    
            ' script に与える標準入力
            Dim stdin = {"メッセージ1", "メッセージ2"}
    
            +' powershell script
            Dim script = "$OutputEncoding=[System.Text.Encoding]::GetEncoding('Shift_Jis'); " _
                       & "$input | msg *; "
    
            ' 実行
            ps.AddScript(script)
            ps.Invoke(stdin)
        End Using
    End Using
    
    • 註1

       文字が UTF8 で渡され文字化けが起きるので、$OutputEncoding に ShiftJIS を設定します。

    • 註2

       OS が x64 かつ、アプリのターゲットCPU が "x86" 、または "AnyCPU で x86 優先" の場合は、「msg *」の部分は「c:windowssysnativemsg *」とします。

  4. 結果を取得する場合のサンプル。
    Using rs = RunspaceFactory.CreateRunspace()
        rs.Open()
    
        Using ps = PowerShell.Create
            ps.Runspace = rs
    
            ' script に与える標準入力
            Dim stdin = {1, 2, 3, 4}
    
            ' powershell script
            Dim script = "$input | % {$_ * 2}"
    
            ' 実行と結果の取得
            ps.AddScript(script)
            Dim results = ps.Invoke(stdin)
    
            ' 結果の表示
            For Each x In results
                Console.WriteLine("{0}", x)
            Next
        End Using
    End Using
    

     結果

    2
    4
    6
    8
    

[環境設定] Windows 8.1 の左エッジスワイプの動作設定

2014年12月11日

 Windows 8 で、左エッジスワイプをした場合、アプリ一覧が表示される場合と、アプリが切り替わる(引き出せる)場合とがあります。この動作は設定で変えられます。

  • アプリ一覧が出てくる場合

  • アプリが切り変わる場合

  • 設定方法

     チャームバー → 設定 → PC設定の変更 → PCとデバイス → 画面の操作 → 左端から… を ON にします。

[環境] System32 のねじれ

2014年12月11日

 C:WindowsSystem32 フォルダは、OS やアプリのターゲットCPU (x86 or x64) によって、見えているものが違います。
 具体的には、x64 Windows の 32bit モードで動作するアプリで C:WindowsSystem32 を参照すると、C:WindowsSysWOW64 が見えます。

 多くの場合、問題は出ませんが、たとえば 32bit モードで動作しているアプリから msg.exe を呼ぶと失敗します。
 これは、C:WindowsSysWOW64 に msg.exe がないからです。

 この事態を回避するためには、C:WindowsSysnative というフォルダにアクセスします。このフォルダは C:WindowsSystem32 を見ているため、msg.exe を見つけることができます。

 しかし、この C:WindowsSysnative は x64 Windows の 32bit モードでしか使用できないので、x86 Windows や x64 Windows の 64bit モードでこのフォルダをアクセスしようとすると失敗します…

 …なんと難儀な(汗

  • cmd.exe (32bitモード, c:WindowsSysWOW64cmd.exe) で msg.* を検索した視た結果

     C:WindowsSysnative でのみ msg.exe が見つかる。

  • cmd.exe (64bit モード) で msg.* を検索した視た結果

     C:WindowsSystem32 で msg.exe が見つかるが、C:WindowsSysnative にはアクセスできない。

[Oracle] SQLPlus の環境設定

2014年12月8日

 sqlplus を使っていて、毎回ログインするたびに set linesize 300 とか実行するのが面倒な場合、以下のようにします。

  1. login.sql というファイルを作り、その中に設定を書きます。

    設定ファイル例

    set linesize 300
    set pagesize 30
    set tab off
    alter session set NLS_DATE_FORMAT='RR-MM-DD HH24:MI'; 
    
  2. %ORACLE_HOME%dbs に login.sql を配置します。

    %ORACLE_HOME% は当該 Oracle をインストールしたディレクトリ。
    たとえば 11g の Server(home1) にインストールした場合は、以下のエントリーの場所に login.sql を配置します。

    # Powershell Script
    (Get-ItemProperty -Path HKLM:SOFTWAREORACLEKEY_OraDB11g_home1).SQLPATH
    

[VB.NET] フォームの閉じるボタンを押せなくする

2014年12月8日

 WinForms で 最大化ボタン、最小化ボタンを個別に消したり、コントロールボックス全体(最大化/最小化/閉じる、タイトルバー左端のアイコン)を消すことはできます。
 しかし「最大化ボタンは有効にしつつ」「閉じるボタンだけ消す」といったことが、フォームデザイナから設定できません。
 なので「閉じるボタンだけを無効にしたい」場合は、以下のコードを埋め込むことで対応します。

    Protected Overrides ReadOnly Property CreateParams As CreateParams
        Get
            Dim c = MyBase.CreateParams
            c.ClassStyle = c.ClassStyle Or &H200
            Return c
        End Get
    End Property

[Linux] NFSの設定と、Windows からの共有の方法

2014年12月8日

 NFS ボリュームの設定の仕方と、Windows からの共有の方法についてのメモ。

  • CentOS 側設定
    1. su - で root に入る。(または最初から root ログイン)
    2. vi /etc/exports で 以下を設定
      ----ここから
      /share 192.168.0.0/24(rw,no_root_squash,sync)
      ----ここまで
      

      192.168.0.0 は公開先。末尾が 0 なのは /24 だから。

    3. 共有フォルダを作成
      mkdir /share
           ↑root 重要(絶対Path)
      

       実行後、ls / で / に share が作成されていることを確認

    4. パーミッションを切る
      chmod 777 /share
      
    5. nfsサービスを起動する
      service nfs start
      
    6. 公開フォルダがどうなっているかを確認
      showmount -e 
      

       ただし、設定フォルダが間違っていても、設定どおりが表示されるので確認としてはいまいちです。

    7. 再起動時に立ち上げたい場合は以下を設定
      chgconfig nfs on
      
    8. 現在の設定を知るには以下
      chgconfig --list nfs
      
  • Windows 側設定
    1. NFS用サービスを追加 (Windowsの機能の有効化または無効化)

       ただしNFS用サービスは Windows 7 の場合は Ultimate または Enterprise、Windows 8 系の場合は Enterprise にしかありません。Home はおろか Pro であっても使用できない点に注意です。

    2. マウント
      mount \192.168.0.1share *
      mount \192.168.0.1share z: ←ドライブ名明示
      
    3. アンマウント
      umount -f z:
      

[Oracle] Oracle で 一行分の Upsert

2014年12月8日

 一行分の upsert だと merge 使ってもそんなにコード量は削減されないし、ODP.NET を経由した場合に、なにやら謎の例外 (AccessViolationException) が発生するケースがあったので、無名ブロックを使った対処方法をメモしておく。

declare
 v_count number;

begin
 select EMPNO into v_count from EMP where EMPNO = :empno;

 if (v_count = 1) then
  update EMP set ENAME = :ename where EMPNO = :empno;

 else
  insert into EMP (EMPNO, ENAME) values (:empno, :ename);

 end if;
end;
/

 ちなみに merge 文を使用する場合はこんな感じ。

merge into EMP using DUAL
   on (EMP.EMPNO = :empno)
when matched then
  update set ENAME = :ename
when not matched then
  insert (EMPNO, ENAME) values (:empno, :ename)
;

[Visio] 自動サイズ設定を無効にする

2014年12月7日

 Visio 2010 以降、「自動サイズ設定」という機能が追加となり、大きな図面を作成する場合は便利なのですが、Visio 2003 以前のように使いたい場合はかえって邪魔な場合があります。
 とはいえ、この設定はページごとのため、ページが多い文書だと一つ一つ手動で変更するのは面倒です。よって一気に変更するマクロを以下に示す。

Sub Macro1()

    Dim x As Page

    For Each x In Application.ActiveDocument.Pages

        x.AutoSize = False

    Next

End Sub

[Visio] フローチャートのループ端シェイプを作る

2014年12月7日

 かつて Visio 2003 では、フローチャート図形に「ループ端」があったはずなのですが、Visio 2010 (Visio 2007 は未確認) 以降なくなってしまっているようです。
 それは地味に困るので、フローチャート図形のカスタム図形4(準備)を変形させてループ端(上)を作る VBA を作成したのでメモがてら記す。

 以下のコードを VBE に貼って、VBE 上で実行すれば、アクティブページ上にループ端が作成されるはずです。
 下端も欲しい場合は、作成した上端図形をコピーして上下反転させてください。

Sub ループ端上図形作成()
    ' 図面左下に表示されます (座標 x=0,y=0)
    ' ループ端下はループ端上図形を上下反転させてください。

    ' カスタム図形=4 (準備) を図面に新規ドロップする
    Dim selectionShape As Shape
    Set selectionShape = Application.ActiveWindow.Page.Drop( _
                            Application.Documents.Item("BASFLO_M.VSSX").Masters.ItemU("Custom 4"), _
                            0, 0 _
                         )

    ' ループ端上に変形
    With selectionShape
        ' ---- A ----
        .CellsSRC(visSectionScratch, 0, visScratchX).FormulaU = "2.5mm"

        ' ---- Geometry1 ----
        .CellsSRC(visSectionFirstComponent, 1, 0).FormulaU = "0mm"
        .CellsSRC(visSectionFirstComponent, 2, 0).FormulaU = "Width"
        .CellsSRC(visSectionFirstComponent, 4, 0).FormulaU = "Width-Scratch.X1"
        .CellsSRC(visSectionFirstComponent, 5, 0).FormulaU = "Scratch.X1"
        .CellsSRC(visSectionFirstComponent, 7, 0).FormulaU = "0mm"
        .CellsSRC(visSectionFirstComponent, 3, 1).FormulaU = "Height-Scratch.Y1"
        .CellsSRC(visSectionFirstComponent, 6, 1).FormulaU = "Height-Scratch.Y1"
        .CellsSRC(visSectionFirstComponent, 7, 0).FormulaU = "Geometry1.X1"
    End With

End Sub

[R][VB.NET] 変数 c にデータを入力しようとするとメモリアクセス違反が発生する

2014年12月7日

 R.NET を使用して R の変数にアクセスする際、変数名が "c" の場合にメモリアクセス違反が発生するので、ここにメモる。

 多分ベクトル作成時に使用する c とかぶっているのではないかと予想。
 ただ実際問題、サンプルコードでもなければ "c" なんて変数使わないので気を付ければいっか、ということで(汗

SetSymbol

Error: cannot change value of locked binding for 'c'

ハンドルされていない例外: System.AccessViolationException: 保護されているメモリに読み取りまたは書き込み操作を行おうとしました。他のメモリが壊れていることが考えられます。
   場所 RDotNet.REnvironment.SetSymbol(String name, SymbolicExpression expression)
   場所 RDotNet.REngine.SetSymbol(String name, SymbolicExpression expression)
   場所 ConsoleApplication1.Module1.Main() 場所 C:UsersazalingDocumentsVisual Studio 2013ProjectsConsoleApplication1ConsoleApplication1Module1.vb:行 51

[R][VB.NET] VB.NET から R を使用して回帰分析を計算してみる

2014年12月7日

 VB.NET は行列計算と同様、標準の回帰分析ライブラリを持っていないので、その対策として R を使用する例。

 重回帰でも「c3 <- lm(y ~ x + x2 + x3);」といった感じで変数を並べればよいだけなので自作に比べるとかなりお手軽です。  また今回の場合、多項式の係数だけが欲しい場合を想定したので R のコードの最後に「c1$coefficients;」を書き、係数が戻り値として戻るようにしています。

 以下コード例。

Imports RDotNet.SymbolicExpressionExtension

Module Module1

    Sub Main()

        ' Rのインストールフォルダに PATH を通す (通っていればなくてもよい)
        Environment.SetEnvironmentVariable("PATH", Environment.GetEnvironmentVariable("PATH") & ";C:Program FilesRR-3.0.2bini386")

        ' ---------------
        ' 初期化
        ' ---------------
        Dim r = RDotNet.REngine.CreateInstance("RDotNet")
        r.Initialize()

        ' ---------------
        ' データの入力
        ' ---------------
        Dim yData = New RDotNet.NumericVector(r, {5, 8, 7, 8, 9, 11, 9, 11, 13, 12})
        r.SetSymbol("y", yData)

        Dim xData = New RDotNet.NumericVector(r, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
        r.SetSymbol("x", xData)

        ' ---------------
        ' 計算
        ' ---------------
        ' 単回帰( y = a・x + b)
        Dim c1 = r.Evaluate("c1 <- lm(y ~ x); " &
                            "c1$coefficients;")

        ' 重回帰( y = a・x^3 + b・x^2 + c・x + d )
        Dim c3 = r.Evaluate("x2 = x ^ 2; " &
                            "x3 = x ^ 3; " &
                            "c3 <- lm(y ~ x + x2 + x3); " &
                            "c3$coefficients;")

        ' ---------------
        ' 結果の取得
        ' ---------------
        Dim c1Data = c1.AsNumeric
        Dim c3Data = c3.AsNumeric

        ' ---------------
        ' 結果の表示
        ' ---------------
        Console.WriteLine("y = {0:0.00}x {1:+ 0.00;- 0.00;+0}",
                             c1Data(1), c1Data(0))
        Console.WriteLine("y = {0:0.00}x^3 {1:+ 0.00;- 0.00;0}x^2 {2:+ 0.00;- 0.00;0}x {3:+ 0.00;- 0.00;0}",
                             c3Data(3), c3Data(2), c3Data(1), c3Data(0))

        ' ---------------
        ' 終了
        ' ---------------
        r.Close()
        r.Dispose()
    End Sub

End Module

 結果。

y = 0.75x + 5.20
y = 0.01x^3 - 0.11x^2 + 1.38x + 4.30
続行するには何かキーを押してください . . .

[R][VB.NET] VB.NET から R を使用して行列を計算してみる

2014年12月7日

 VB.NET は、標準の行列計算ライブラリを持っていないので、その対策として R を使用する例。

 まずは前準備として、nuget で R.NET を入手します。

 以下コード例。
 注意点としては、結果を得る際に .AsNumericMatrix でデータを引き出しますが、これは拡張メソッドなので「Imports RDotNet.SymbolicExpressionExtension」が必須です。

' Imports 必須
Imports RDotNet.SymbolicExpressionExtension

Module Module1

    Sub Main()

        ' Rのインストールフォルダに PATH を通す (通っていればなくてもよい)
        Environment.SetEnvironmentVariable("PATH",
            Environment.GetEnvironmentVariable("PATH") & 
            ";C:Program FilesRR-3.0.2bini386")

        ' ---------------
        ' 初期化
        ' ---------------
        Dim r = RDotNet.REngine.CreateInstance("RDotNet")
        r.Initialize()

        ' ---------------
        ' データの入力
        ' ---------------
        Dim xData = New RDotNet.NumericMatrix(r, {{1, 2, 3}, {4, 5, 6}})
        r.SetSymbol("x", xData)

        Dim yData = New RDotNet.NumericVector(r, {2})
        r.SetSymbol("y", yData)

        Dim zData = New RDotNet.NumericMatrix(r, {{7, 8}, {9, 10}, {11, 12}})
        r.SetSymbol("z", zData)

        ' ---------------
        ' 計算
        ' ---------------
        ' 行列積 (f = x・y・z) を計算
        Dim f = r.Evaluate("f = (x * y) %*% z")

        ' その逆行列 (g = 1/f) を計算
        Dim g = r.Evaluate("g = solve(f)")

        ' 行列積と逆行列の積 (h = f・g) を計算
        Dim h = r.Evaluate("h = f %*% g")

        ' ---------------
        ' 結果の取得
        ' ---------------
        Dim fData = f.AsNumericMatrix
        Dim gData = g.AsNumericMatrix
        Dim hData = h.AsNumericMatrix

        ' ---------------
        ' 結果の表示
        ' ---------------
        Console.WriteLine("f = {0,8:0}, {1,8:0}", fData(0, 0), fData(0, 1))
        Console.WriteLine("    {0,8:0}, {1,8:0}", fData(1, 0), fData(1, 1))
        Console.WriteLine()

        Console.WriteLine("g = {0,8:0.00}, {1,8:0.00}", gData(0, 0), gData(0, 1))
        Console.WriteLine("    {0,8:0.00}, {1,8:0.00}", gData(1, 0), gData(1, 1))
        Console.WriteLine()

        Console.WriteLine("h = {0,8:0.00}, {1,8:0.00}", hData(0, 0), hData(0, 1))
        Console.WriteLine("    {0,8:0.00}, {1,8:0.00}", hData(1, 0), hData(1, 1))
        Console.WriteLine()

        ' ---------------
        ' 終了
        ' ---------------
        r.Close()
        r.Dispose()
    End Sub

End Module

 結果

f =      116,      128
         278,      308

g =     2.14,    -0.89
       -1.93,     0.81

h =     1.00,     0.00
        0.00,     1.00

続行するには何かキーを押してください . . .

[R] R で回帰分析

2014年12月6日
  1. 元となる y 軸と x 軸のデータを作成
    > y <- c(5,10,12,13,4,-2)
    > y
    [1]  5 10 12 13  4 -2
    
    > x <- c(1,2,3,4,5,6)
    > x
    [1] 1 2 3 4 5 6
    
    
  2. 重回帰用の変数を計算

    x の二乗を計算します。

    > x2 <- x ** 2
    > x2
    [1]  1  4  9 16 25 36
    
    
  3. 回帰分析の実行

     y = a・x^2 + b・x + c の各係数を求めます。

    > lm(y ~ x + x2)
    
    Call:
    lm(formula = y ~ x + x2)
    
    Coefficients:
    (Intercept)            x           x2  
         -4.300       10.889       -1.768
    
    
  4.  よってこの場合、y = -1.768x^2 + 10.889x - 4.3、が近似であるという結果になります。

[R] R で行列計算

2014年12月6日

 行列計算をする際の簡単なメモ

  • 行列の作成

     行列を作るには、matrix() にベクトルデータ(一次元配列データ)を渡すことで行います。

    > a <- matrix( c(1,2,3,4,5,6),nrow = 2,ncol = 3)
    > a
         [,1] [,2] [,3]
    [1,]    1    3    5
    [2,]    2    4    6
    

     横走査で配置したい場合は、byrow = TRUE、を追加で指定します。

    > matrix(c(101:106),nrow = 2, ncol = 3, byrow = TRUE)
         [,1] [,2] [,3]
    [1,]  101  102  103
    [2,]  104  105  106
    
  • 行列の和
    > a + a
         [,1] [,2] [,3]
    [1,]    2    6   10
    [2,]    4    8   12
    
  • 行列の項目ごとの積
    > a * a
         [,1] [,2] [,3]
    [1,]    1    9   25
    [2,]    4   16   36
    
  • 行列積
    > a %*% a   # 求められない
    Error in a %*% a : non-conformable arguments
    
    > c <- matrix( c(3,4,3,4,3,4), nrow = 3, ncol = 2)
    > c
         [,1] [,2]
    [1,]    3    4
    [2,]    4    3
    [3,]    3    4
    
    > d = a %*% c
    > d
         [,1] [,2]
    [1,]   30   33
    [2,]   40   44
    
  • スカラー積を求める
    > d * 2
         [,1] [,2]
    [1,]   60   66
    [2,]   80   88
    
  • 逆行列を求める
    > e <- matrix( c(1,2,3,4),nrow = 2,ncol = 2)
    > e
         [,1] [,2]
    [1,]    1    3
    [2,]    2    4
    
    > f = solve(e)
    > f
         [,1] [,2]
    [1,]   -2  1.5
    [2,]    1 -0.5
    

     元の行列と単位行列の行列積をとると、単位行列になります。(計算誤差があるので正確に 1 と 0 ではない場合がある点に注意)

    > e %*% f
         [,1] [,2]
    [1,]    1    0
    [2,]    0    1
    

[R] データフレーム

2014年12月6日

 データフレームの作り方と操作の簡単なメモ

  1. 2列3行のデータを列名付きで作成
    > df <- data.frame(aaa=c(1,2,3), bbb=c(11,12,13))
    > df
      aaa bbb
    1   1  11
    2   2  12
    3   3  13
    
  2. 列のデータを列挙
    > df$aaa
    [1] 1 2 3
    
    > df[,"aaa"]
    [1] 1 2 3
    
    > df[,1]
    [1] 1 2 3
    
    > df[1]
      aaa
    1   1
    2   2
    3   3
    
  3. 行のデータを列挙
    > df[1,]
      aaa bbb
    1   1  11
    
  4. 特定のデータを取得
    > df[1,2]
    [1] 11
    > df[2,"bbb"]
    [1] 12
    
  5. 列のデータを追加
    > df["ccc"] <- c(21,22,23)
    
    > df
      aaa bbb ccc
    1   1  11  21
    2   2  12  22
    3   3  13  23
    
  6. 行のデータを追加

     挿入する行番号を指定して追加します。

    > df[4,] <- c(4,14,24)
    > df
      aaa bbb ccc
    1   1  11  21
    2   2  12  22
    3   3  13  23
    4   4  14  24
    

     行番号をあけて追加すると、その間の行のデータが NA で追加されます。

    > df[7,] <- c(7,17,27)
    > df
      aaa bbb ccc
    1   1  11  21
    2   2  12  22
    3   3  13  23
    4   4  14  24
    5  NA  NA  NA
    6  NA  NA  NA
    7   7  17  27
    

     次の行へ挿入する場合。 (ほかにいい方法がみつからなかった…)

    > df[nrow(df) +1,] <- c(8,18,28)
    > df
      aaa bbb ccc
    1   1  11  21
    2   2  12  22
    3   3  13  23
    4   4  14  24
    5  NA  NA  NA
    6  NA  NA  NA
    7   7  17  27
    8   8  18  28
    

[R] Rの基本事項

2014年12月6日

 Rについての基本操作のメモ

  1. 代入('付値'と呼ぶらしい)
    > x <- 10
    > 10 -> x
    > x = 10   # 非推奨らしい
    
  2. コメント
    > # 行コメント
    

     変数にコメントが付くらしい。

    > comment(x) <- "変数のコメント"
    > comment(x)
    [1] "変数のコメント"
    
  3. 複素数
    > x = 10 + 4i
    
  4. 平方根を求めてみる。

     実数のマイナスの平方根はエラー。マイナスの平方根が欲しい場合は複素数で指定します。

    > sqrt(-1)
    [1] NaN
    Warning message:
    In sqrt(-1) : NaNs produced
    
    > sqrt(-1 + 0i)
    [1] 0+1i
    
  5. 定数
    TRUE
    FALSE
    NULL    # null。たとえば a = c(); a;
    NA      # 欠損。たとえば a = c(1); a[3] = 3; a[2];
    NaN     # 非数。たとえば a = 0/0; a;
    Inf     # +∞
    -Inf    # -∞
    
  6. NULL 等の比較
    > is.null(NULL)
    [1] TRUE
    > is.na(NA)
    [1] TRUE
    > is.nan(NaN)
    [1] TRUE
    
  7. 等差数列の作成と、表示時の左端の大かっこ内の数値
     n:m と書くと、n から m に向けて +1 または -1 の間隔の等差数列を作ります。
     また、表示をした時の左側の [n] は、値の表示の一番左側の配列番号です。

    > c(1:100)
      [1]   1   2   3   4   5   6   7   8   9  10  11  12  13  14
     [15]  15  16  17  18  19  20  21  22  23  24  25  26  27  28
     [29]  29  30  31  32  33  34  35  36  37  38  39  40  41  42
     [43]  43  44  45  46  47  48  49  50  51  52  53  54  55  56
     [57]  57  58  59  60  61  62  63  64  65  66  67  68  69  70
     [71]  71  72  73  74  75  76  77  78  79  80  81  82  83  84
     [85]  85  86  87  88  89  90  91  92  93  94  95  96  97  98
     [99]  99 100
    

[PS1] Powershell の基本 (6:定数)

2014年12月5日
  • 真理値と null
    $true
    $false
    $null
    
  • 文字リテラル

      ではなく ` (アクサングラーブ) 記号を使います。

    't
    'n
    
  • 正規表現における文字リテラル

     正規表現内では、 が使用できます。よって以下のような関係性が成り立ちます

    PS> "`n" -match "`n"
    True
    PS> "`n" -match "n"
    True
    PS> "n" -match "n"
    False
    PS> "n" -match "`n"
    False
    PS> "n" -match "\n"
    True
    
  • 無限大とか非数とか
     無限大とか非数とかの定数値は用意されているかどうかわからなかったので、必要であれば Double の StaticProperty を使用します。
     また、非数(NaN) は -eq で比較できないことに注意。非数であることを知りたい場合は、IsNaN() を使用します。

    PS> (1.0/0.0) -eq [double]::PositiveInfinity
    True
    PS> (-1.0/0.0) -eq [double]::NegativeInfinity
    True
    PS> (0.0/0.0) -eq [double]::NaN
    False
    PS> [double]::NaN -eq [double]::NaN
    False
    PS> [double]::IsNaN(0.0/0.0)
    True
    
  • 環境変数
    $env:環境変数名 でアクセスできます。

    PS> $env:windir
    C:Windows
    
  • バージョン情報
     バージョン情報を拾うには $PSVersionTable を使います。

    PS> $PSVersionTable
    
    Name                           Value
    ----                           -----
    PSVersion                      4.0
    WSManStackVersion              3.0
    SerializationVersion           1.1.0.1
    CLRVersion                     4.0.30319.34014
    BuildVersion                   6.3.9600.17090
    PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0}
    PSRemotingProtocolVersion      2.2
    

[PS1] Powershell の基本 (5:便利なコマンドレット他)

2014年12月5日
  • ファイル一覧

     Get-ChildItem を使用。dir とか ls と打っても良いけど、オプション引数とかが絶対的に違うので、gci と打つのを推奨。

    $a = gci c:
    

  • ファイルの内容の取得

     Get-Content を使用。type とか cat と打っても良いけど、Get-ChildItem と同様の理由で gc と打つのを推奨。

    $a = gc .test.txt
    

  • Csvファイルのインポート

      Import-Csv とかもありますが、ファイルからの入力になるので、パイプラインから処理できる ConvertFrom-Csv が便利。

    $a = gc .test.txt
    $b = $a | ConvertFrom-Csv
    

     ヘッダー行がない csv で、取り急ぎヘッダーを付けたい場合は以下のように書くとお手軽。

    $b = $a | ConvertFrom-Csv -Header @(1..100)
    
  • Csvファイルへのエキスポート
     ConvertTo-Csv が便利。-NoTypeInformation はおまじないで。
     ファイルに書きたい場合は Out-File を使います。-Encoding default はおまじないで。
     ただし、意図した形式の CSV ファイルを出力するのは難しいと思います。(目的に合致しないなら手作りで)

    $a = gci
    $b = $a | ConvertTo-Csv -NoTypeInformation
    $b | Out-File -FilePath .test.csv -Encoding default
    
  • 集計
     簡易に集計処理した場合は、measure-object というコマンドレットがあります。

    gci | measure Length -Sum
    

     結果

    Count    : 9
    Average  :
    Sum      : 191453
    Maximum  :
    Minimum  :
    Property : Length

[PS1] Powershell の基本 (4:関数)

2014年12月5日
  1. 呼び出し

     一般の関数と同じように思ってしまうが、いろいろと異なります。
     宣言(定義)は以下のような感じで一般言語とあまり変わりません。

    PS> function A_add_B_plus1($a, $b) {
     >>   return $a + $b + 1
     >> }
     >>
    

     ただし、呼び出す際に括弧とカンマとかは必要ありません。

    PS>A_add_B_plus1 10 20
    31
    

     不用意に括弧とかカンマとかつけると、謎の結果が戻ってきます。(*1)

    PS> A_add_B_plus1(10,20)
    10
    20
    1
    PS>
    

     他方、パラメーターを指定して呼び出すことが可能です。

    PS> function sub($paraA, $paraB) {
     >>   $paraA - $paraB
     >> }
     >>
    PS> sub -paraA 20 -paraB 10
    10
    PS> sub -paraB 20 -paraA 10
    -10
    

  2. 戻り値

     戻り値を戻すのに return は不要です。

    PS> function add($a, $b) {
     >>   $a + $b
     >> }
     >>
    PS> add 10 20
    30
    PS>
    

     パイプラインに複数の値を渡すと、複数の戻り値が戻ります。

    PS> function add1($a, $b) {
     >>   $a + 1
     >>   $b + 1
     >> }
     >>
    PS> $a = add1 10 20
    PS> $a.Count
    2
    PS> $a
    11
    21
    PS>
    

  3. パイプラインから入力

     パイプラインからの入力も $_ で拾えます。また、パイプラインの入力に先立って初期化処理を行いたい場合は、に初期化部分を begin ブロックに、パイプラインのイテレーター部分を process ブロックに記述することで実現できます。

    function func1($a, $b) {
      begin {
        $lineNo = 1
      }
    
      process {
        $judge = $lineNo % 2
    
        if ($judge -eq 1) {
          $_ * $a        # 奇数行は $a 倍
    
        } else {
          $_ * $b        # 偶数行は $b 倍 
    
        }
    
        $lineNo += 1     # 行番号を加算
      }
    }
    
    PS> @(1,2,3,4,5) | func1 10 100
    10
    200
    30
    400
    50
    

  4. 等価コード

     以下の 2つは等価です。process ブロックを使用しない場合、$input がパイプラインの内容なので、それを扱うことで同じことができます。

    function func1($a) {
      begin {
        "begin"
      }
      process {
        $_ + $a
      }
      end {
        "end"
      }
    }
    
    function func2($a) {
      "begin"
    
      $input | % { $_ + $a }
    
      "end"
    }
    
    PS> @(1,2,3) | func1 100
    begin
    101
    102
    103
    end
    
    PS> @(1,2,3) | func2 100
    begin
    101
    102
    103
    end
    

  5. 無名関数

     プレイスで囲ったものが、無名関数になります。実行する場合は、先頭に & を付加します。

    PS> $a = { $b + $_ }
    PS> @(1,2,3) | % { &$a 10 }
    11
    12
    13
    
    PS> $a = { process { $b + $_ } }
    PS> @(1,2,3) | &$a 10
    11
    12
    13
    

  • (*1)

     10,20 という指定は「$a に 10, $b に 20」ではなく、「$a に配列 @(10,20)」 が渡ることによって起こる。
     つまり、「@(10,20) + $null + 1」という式になり、結果「@(10,20,$null,1)」が生成される。($null は見た目表示されないが、要素しては格納されている)

[PS1] Powershell の基本 (3:パイプライン処理)

2014年10月16日
  1. パイプラインの基本
    • パイプライン変数

       $_ が要素一つ一つ。

      PS> @(1,2,3) | % { $_; } 
      1
      2
      3
      

    • 列挙

       % {} で表現。{} 内に列挙時の処理を書く。
       % は ForEach-Object の Alias。Foreach とも書ける。

      PS > @(1..5) | % { $_ * 100; }
      100
      200
      300
      400
      500
      

    • 列挙 その2

       パイプラインの始端,または終端で、初期処理,あるいは終了処理がしたい場合は、% (ForEach) ではなく & (スクリプトブロックの実行)を用いる。
       begin {} の中に、初期処理を、
       process {} の中に、ForEach などで書いていた、パイプラインのコレクションごとの処理を、
       end {} の中に、終了処理を書く。

      PS > @(1..3) | &{
      >>  begin   { "初期処理"; }
      >>  process { $_ * 200; }
      >>  end     { "終了処理"; }
      >> }
      初期処理
      200
      400
      600
      終了処理
      

    • フィルタリング

       ? {} で表現。{} 内にフィルター条件の処理を書く。
       ? は Where-Object の Alias。Where とも書ける。
       処理の結果、0 (= $False),$null,結果がない、などの場合は、入力されたオブジェクトは次のパイプラインに渡さない。

      PS > @(1..5) | ? { $_ % 2; }
      1
      3
      5
      
  2. パイプラインへの出力

     パイプラインへ出力は、直接その値を記述することで行う。

    1. リテラルをパイプラインに出力
      PS> "AAA" | % { $_; } 
      AAA
      
    2. 出力先の指定がない場合の出力先

       最終出力先(変数代入やファイルへの出力)の指定が場合、パイプライン出力はコンソールに渡される。

      PS> @(1,2,3) | % { $_; }  # この文の実行でデータが表示される
      1
      2
      3
      
    3. 出力先を変数にした場合

       変数への代入するとコンソールに出力されずに、その内容が変数に格納される。

      PS> $a = @(1,2,3) | % { $_; } # この文の実行ではデータは表示されない
      PS> $a.Count #件数
      3
      PS> $a #内容
      1
      2
      3
      
    4. 複数の値を出力した場合

       それぞれがパイプラインへ出力される。

      PS> $a = @(1,2,3) | % { "****"; $_; $_; } 
      PS> $a.Count
      9
      PS> $a
      ****
      1
      1
      ****
      2
      2
      ****
      3
      3
      
    5. 次のパイプラインに出力を渡さない

       パイプラインへは出力されない場合。

      PS> $a = @(1,2,3) | % { } 
      PS> $a.Count
      0
      

       Write-Host の出力は パイプラインに出力されない。

      PS> $a = @(1,2,3) | % { Write-Host $_; }
      1
      2
      3
      PS> $a.Count
      0
      
  3. パイプラインを経由した処理の実行順

     パイプラインでつないだ場合、処理の実行順が一般的な言語とは異なる点に注意。
     パイプラインに出力するたびに、パイプラインの先の処理に制御が移る。
     パイプラインに渡す前までの処理をすべてやりきって、その結果をまとめて次のパイプラインに渡すわけではない。
     一般的の言語の場合だと、コルーチン(yeild) を使った動きに似ている。(*1)

    PS > @("A","B","C") |
    >> % { Write-Host "1st :$_";  $_ + "*"; } |
    >> % { Write-Host "2nd :$_";  $_ + "#"; } |
    >> % { Write-Host "3rd :$_"; }
    >>
    1st :A
    2nd :A*
    3rd :A*#
    1st :B
    2nd :B*
    3rd :B*#
    1st :C
    2nd :C*
    3rd :C*#
    

     括弧で囲むと、そこまでの処理が確定される。

    PS > (
    >>   @("A","B","C") |
    >>   % { Write-Host "1st :$_";  $_ + "*"; } |
    >>   % { Write-Host "2nd :$_";  $_ + "#"; }
    >> ) |
    >> % { Write-Host "3rd :$_"; }
    >>
    1st :A
    2nd :A*
    1st :B
    2nd :B*
    1st :C
    2nd :C*
    3rd :A*#
    3rd :B*#
    3rd :C*#
    


(*1) VB で同様の動きをさせようとすると、以下のようなコードで実現できる。

Module Module1
    Sub Main()
        Third(
            Second(
                First(
                    {"A", "B", "C"}
                    )
                )
            ).
        ToArray()
    End Sub

    Iterator Function First(input As IEnumerable(Of String)) As IEnumerable(Of String)
        For Each s In input
            Console.WriteLine("1st :" & s)
            Yield s & "*"
        Next
    End Function

    Iterator Function Second(input As IEnumerable(Of String)) As IEnumerable(Of String)
        For Each s In input
            Console.WriteLine("2nd :" & s)
            Yield s & "#"
        Next
    End Function

    Iterator Function Third(input As IEnumerable(Of String)) As IEnumerable(Of String)
        For Each s In input
            Console.WriteLine("3rd :" & s)
            Yield s
        Next
    End Function
End Module

 結果

1st :A
2nd :A*
3rd :A*#
1st :B
2nd :B*
3rd :B*#
1st :C
2nd :C*
3rd :C*#

[PS1] Powershell の基本 (2:文字列処理)

2014年10月16日
  1. 文字列比較

     大文字/小文字区別のなしの場合は -eq -ne -lt -le -gt -ge

    PS> "A" -eq "a"
    True
    PS> "A" -ne "a"
    False
    

     大文字/小文字区別の場合は -ceq -cne -clt -cle -cgt -cge と c をつける。

    PS> "A" -ceq "a"
    False
    PS> "A" -cne "a"
    True
    

     ただ、これらの演算子には罠があり、なぜか以下の場合は True になるので注意。

    PS> "ー" -ceq "々"
    True
    

     どうしても上記を回避したい場合は、string クラスの Equals メソッドを使います。

    PS> "ー".Equals("々")
    False
    PS> [string]::Equals("ー","々")
    False
    
  2. 正規表現一致

     大文字/小文字区別のなしの場合は -match -replace

    PS> "ABCDEabcde" -match "b.*d"
    True
    PS> $matches[0]
    BCDEabcd
    PS> "ABCDEabcde" -replace "b.*d","★"
    A★e
    

     大文字/小文字区別の場合は -cmatch -creplace

    PS> "ABcdE" -match "b.*d"
    True
    PS> $matches[0]
    bcd
    PS> "ABcdE" -replace "b.*d","★"
    ABCDEa★e
    

     一致文字列を取得する場合は、$Matches を参照します。

    PS> $a = "AAAAAA_20141014_BBBBB"
    PS> $a -match "_(.*)_"
    True
    PS> $Matches
    Name Value
    ---- -----
    1    20141014
    0    _20141014_
    PS> $Matches[1]
    20141014
    

     一致箇所に名前を付けたい場合は、正規表現中に (?<変数名>正規表現)と記述することで名前付けをします。

    PS> $a = "AAAAAA_20141014_BBBBB"
    PS> $a -match "_(?<name>.*)_"
    True
    PS> $Matches
    Name Value
    ---- -----
    name 20141014
    0    _20141014_
    PS> $Matches["name"]
    20141014
    
  3. 書式整形

     -f を使用します。

    PS> "現在{0: d}日の {0:H:mm} です。" -f (Get-Date)
    現在 4日の 22:21 です。
    PS> "{0:0.000} {1:0.###}" -f 12.3,45.6
    12.300 45.6
    

[PS1] Powershell の基本 (1:変数と数値)

2014年10月15日
  1. コメント

    # で始まる。

    PS> # コメント
    PS> 
    
  2. 変数

     $ で始まる。

    • 単純変数
      PS> $a = 10  
      PS> $a
      10
      PS> 
      
    • 配列
      PS> $b = @(10,20,30)
      PS> $b[1]
      20
      PS> 
      
    • 連想配列

       囲みは波括弧、要素の区切りはセミコロンであることに注意。

      PS> $c = @{a = 100;
                 b = 200;
                 c = "A"}  
      PS> 
      PS> $c["a"]
      100
      PS> $c.b
      200
      PS> 
      

       要素=0 の連想配列を作ってから足していくこともできる。

      PS> $d = @{} 
      PS> 
      PS> $d["a"] = 300
      PS> $d.b = "AAA"
      PS> 
      PS> $d["a"]
      300
      PS> $d.b
      AAA
      
  3. コマンドレットの戻り値にアクセスする

     コマンドレットの戻り値にアクセスする場合は、変数に代入するか、() でコマンドレットを囲む必要がある。

    PS> $a = dir
    PS> $a.Count
    PS> (dir).Count  # dir.Count はできないことに注意
    PS> 
    PS> $b = dir c:
    PS> $b.Count
    PS> (dir c:).Count
    
  4. 算術関係演算子

     -eq -ne -ge -le -gt -lt。数学記号 =、<、> は使えないことに注意。

    PS> if ($a -eq 10) { …
    
  5. 算術演算子

     四則演算 … + - * /
     商 … なし (先に剰余を引いた後に / で計算するか、[Math]::DivRem を無理やり使う)
     剰余 … %
     冪乗 … なし ([Math]::Pow を使用)
     平方根 … なし ([Math]::Sqrt か [Math]::Pow を使用)

    PS> [Math]::DivRem(59,12,[ref]$null) # 商
    4
    PS> 59 % 12   # 剰余
    11
    PS> [Math]::Pow(10,2)   # 冪乗
    100
    PS> [Math]::Sqrt(3)   # 平方根
    1.73205080756888
    PS> [Math]::Pow(3,1/2)
    1.73205080756888
    

     ただし、[Math]::DivRem(~ は引数を int でキャストしてしまうために、除数/被除数が整数でない場合は意図した結果が出ない場合があります。

    PS> $quot = 0
    PS> [Math]::DivRem(5.7, 2.9, [ref]$quot)
    2      ← 5.7 が 6, 2.9 が 3 と評価されるために 2 が戻る ( 5.7/2.9 = 1.96 なので 1 を期待 )
    PS> $quot
    0      ← 剰余も同様に 6 % 3 相当と評価されるために 0 が戻る ( 同様に 2.8 を期待 )
    PS>
    PS>  5.7 % 2.9
    2.8    ← % 演算子の場合は、整数に丸めにられないので、小数点以下も戻る
    
  6. 論理演算子

    -and -or -xor -not

    PS> 1 -and 2
    True
    
  7. 論理演算子(ビット単位)

    -band -bor -bxor -bnot

    PS> 1 -bor 2
    3
    

[VB.NET] 操業日付の求め方

2014年8月29日

 たとえば、当日8:00~翌日8:00 までを、操業日として管理することがあります。現在時刻から操業日付を求めたい場合、現在時刻から操業日付の区切りの時刻を差し引くことで求めることができます。

 以下は、操業日付の区切りが 8:00 の場合の計算式です。

Dim bias = New TimeSpan(8, 0, 0) 
Dim 操業日付 = (現在時刻 - bias).Date

 これを計算した場合、現在時刻と操業日付は以下の対応になります。

============================================ 日付変更
現在時刻= 08/29 00:00  操業日付= 08/28 00:00
現在時刻= 08/29 01:00  操業日付= 08/28 00:00
現在時刻= 08/29 02:00  操業日付= 08/28 00:00
現在時刻= 08/29 03:00  操業日付= 08/28 00:00
現在時刻= 08/29 04:00  操業日付= 08/28 00:00
現在時刻= 08/29 05:00  操業日付= 08/28 00:00
現在時刻= 08/29 06:00  操業日付= 08/28 00:00
現在時刻= 08/29 07:00  操業日付= 08/28 00:00
-------------------------------------------- ここで操業日が変わる
現在時刻= 08/29 08:00  操業日付= 08/29 00:00
現在時刻= 08/29 09:00  操業日付= 08/29 00:00
現在時刻= 08/29 10:00  操業日付= 08/29 00:00
現在時刻= 08/29 11:00  操業日付= 08/29 00:00
現在時刻= 08/29 12:00  操業日付= 08/29 00:00
現在時刻= 08/29 13:00  操業日付= 08/29 00:00
現在時刻= 08/29 14:00  操業日付= 08/29 00:00
現在時刻= 08/29 15:00  操業日付= 08/29 00:00
現在時刻= 08/29 16:00  操業日付= 08/29 00:00
現在時刻= 08/29 17:00  操業日付= 08/29 00:00
現在時刻= 08/29 18:00  操業日付= 08/29 00:00
現在時刻= 08/29 19:00  操業日付= 08/29 00:00
現在時刻= 08/29 20:00  操業日付= 08/29 00:00
現在時刻= 08/29 21:00  操業日付= 08/29 00:00
現在時刻= 08/29 22:00  操業日付= 08/29 00:00
現在時刻= 08/29 23:00  操業日付= 08/29 00:00
============================================ 日付変更
現在時刻= 08/30 00:00  操業日付= 08/29 00:00
現在時刻= 08/30 01:00  操業日付= 08/29 00:00
現在時刻= 08/30 02:00  操業日付= 08/29 00:00
現在時刻= 08/30 03:00  操業日付= 08/29 00:00
現在時刻= 08/30 04:00  操業日付= 08/29 00:00
現在時刻= 08/30 05:00  操業日付= 08/29 00:00
現在時刻= 08/30 06:00  操業日付= 08/29 00:00
現在時刻= 08/30 07:00  操業日付= 08/29 00:00
-------------------------------------------- ここで操業日が変わる
現在時刻= 08/30 08:00  操業日付= 08/30 00:00
現在時刻= 08/30 09:00  操業日付= 08/30 00:00
現在時刻= 08/30 10:00  操業日付= 08/30 00:00
現在時刻= 08/30 11:00  操業日付= 08/30 00:00
現在時刻= 08/30 12:00  操業日付= 08/30 00:00

 

 また、前日22:00~当日22:00 までを、操業日としたい場合は、以下のようになります。

Dim bias = New TimeSpan(-2, 0, 0) 
Dim 操業日付 = (現在時刻 - bias).Date

 これを計算した場合、現在時刻と操業日付は以下の対応になります。

============================================ 日付変更
現在時刻= 08/29 00:00  操業日付= 08/29 00:00
現在時刻= 08/29 01:00  操業日付= 08/29 00:00
現在時刻= 08/29 02:00  操業日付= 08/29 00:00
現在時刻= 08/29 03:00  操業日付= 08/29 00:00
現在時刻= 08/29 04:00  操業日付= 08/29 00:00
現在時刻= 08/29 05:00  操業日付= 08/29 00:00
現在時刻= 08/29 06:00  操業日付= 08/29 00:00
現在時刻= 08/29 07:00  操業日付= 08/29 00:00
現在時刻= 08/29 08:00  操業日付= 08/29 00:00
現在時刻= 08/29 09:00  操業日付= 08/29 00:00
現在時刻= 08/29 10:00  操業日付= 08/29 00:00
現在時刻= 08/29 11:00  操業日付= 08/29 00:00
現在時刻= 08/29 12:00  操業日付= 08/29 00:00
現在時刻= 08/29 13:00  操業日付= 08/29 00:00
現在時刻= 08/29 14:00  操業日付= 08/29 00:00
現在時刻= 08/29 15:00  操業日付= 08/29 00:00
現在時刻= 08/29 16:00  操業日付= 08/29 00:00
現在時刻= 08/29 17:00  操業日付= 08/29 00:00
現在時刻= 08/29 18:00  操業日付= 08/29 00:00
現在時刻= 08/29 19:00  操業日付= 08/29 00:00
現在時刻= 08/29 20:00  操業日付= 08/29 00:00
現在時刻= 08/29 21:00  操業日付= 08/29 00:00
-------------------------------------------- ここで操業日が変わる
現在時刻= 08/29 22:00  操業日付= 08/30 00:00
現在時刻= 08/29 23:00  操業日付= 08/30 00:00
============================================ 日付変更
現在時刻= 08/30 00:00  操業日付= 08/30 00:00
現在時刻= 08/30 01:00  操業日付= 08/30 00:00
現在時刻= 08/30 02:00  操業日付= 08/30 00:00
現在時刻= 08/30 03:00  操業日付= 08/30 00:00
現在時刻= 08/30 04:00  操業日付= 08/30 00:00
現在時刻= 08/30 05:00  操業日付= 08/30 00:00
現在時刻= 08/30 06:00  操業日付= 08/30 00:00
現在時刻= 08/30 07:00  操業日付= 08/30 00:00
現在時刻= 08/30 08:00  操業日付= 08/30 00:00
現在時刻= 08/30 09:00  操業日付= 08/30 00:00
現在時刻= 08/30 10:00  操業日付= 08/30 00:00
現在時刻= 08/30 11:00  操業日付= 08/30 00:00
現在時刻= 08/30 12:00  操業日付= 08/30 00:00

[VB.NET] 文字列が数値であるかを評価する

2014年8月29日

 与えられた文字列が数値であるかどうかを判定するのに、型.TryParse() を使う方法がありますが、おなじ数値判定でも型によって結果が異なります。

' 数値判定
For Each x In {"100", "+0", "&HA", "-10.5", "5.", "10e+5", "+∞"}
    Dim value As Double
    Console.WriteLine("---- {0}", x)
    Console.WriteLine("Integer:{0}", Integer.TryParse(x, value))
    Console.WriteLine("Decimal:{0}", Decimal.TryParse(x, value))
    Console.WriteLine("Double :{0}", Double.TryParse(x, value))
Next

 結果

---- 100
Integer:True
Decimal:True
Double :True
---- +0
Integer:True
Decimal:True
Double :True
---- &HA
Integer:False
Decimal:False
Double :False
---- -10.5
Integer:False
Decimal:True
Double :True
---- 5.
Integer:False
Decimal:True
Double :True
---- 10e+5
Integer:False
Decimal:False
Double :True
---- +∞
Integer:False
Decimal:False
Double :True
  • 整数はどれも成功ます。
  • 符号つきの 0 も成功します。
  • 16進数表現は失敗します。
  • 小数点が入ると、整数型は Parse に失敗します。"5."のようにケースも不可です。
  • 指数表現と無限大は浮動小数点型のみ成功します。

 なので、文字列を数値として評価する場合、上記を考慮してどの型の Parse を使うかを検討するとよいかもしれません。(小数点を許容するが、指数表現は認めたくないなら、Decimal を使用するなど)

[VB.NET] 円記号(¥)を含む文字列に関する注意

2014年8月27日

 以下のコードを実行すると True が表示されます。

Module Module1

    Sub Main()
        Dim isExist = IO.File.Exists("C:WindowsSystem32driversetchosts")

        Console.WriteLine("{0}", isExist)
    End Sub

End Module

 結果

True
続行するには何かキーを押してください . . .

 以下のコードを実行すると False が表示されます。

Module Module1

    Sub Main()
        Dim isExist = IO.File.Exists("C:¥Windows¥System32¥drivers¥etc¥hosts")

        Console.WriteLine("{0}", isExist)
    End Sub

End Module

 結果

False
続行するには何かキーを押してください . . .

 見た目は同じコードのように見えますが結果が異なります。
 これは ¥ 記号が 前者のコードでは U+005C であるのに対し、後者のコードでは U+00A5 であるためです。フォルダのセパレーターは U+005C である必要があります。

 多くの場合、普通に入力していると、上側の正常なコードが書けますが、以下の手順を踏むと ¥ 記号の文字コードが U+005C から U+00A5 に変更されてしまう場合があります。以下その手順。

  1. Word で Path 文字列 (例: C:WindowsSystem32driversetchosts) を入力する。
  2. その文書を PDF で保存する。
  3. 作成した PDF から Path 文字列 (例: C:¥Windows¥System32¥drivers¥etc¥hosts) をコピーする。
  4. ソースコードにペーストする。

 なので、PDF ファイルから Path 文字列をコピーペーストする場合、¥ 記号が U+005C であるか U+00A5 であるかを確認しましょう。
 確認方法としては、当該 Path 文字列を Word 等にコピーし、フォントを Arial Unicode MS に変更すると、U+005C は で表示されるためお手軽です。

[VB.NET] Linq to XML による XML の検索

2014年8月26日

 たとえば以下の xml ファイル (test.xml) があり、/root/node/leaf の内容を列挙したいと考えたとします。

<Root>
    <Node>
        <Leaf></Leaf>
        <Leaf></Leaf>
    </Node>
    <Node>
        <Leaf></Leaf>
        <Leaf></Leaf>
        <dummy>く</dummy>
        <Leaf></Leaf>
        <Leaf>
            <Leaf></Leaf>
            <Leaf></Leaf>
            <dummy></dummy>
            <Leaf></Leaf>
        </Leaf>
    </Node>
    <Node>
        <Leaf></Leaf>
    </Node>
    <Leaf></Leaf>
    <Leaf></Leaf>
</Root>

 Linq to XML を使用する場合、以下のように書けます。

Sub Main()
    Dim xdoc = XDocument.Load(".test.xml")

    Dim q = From x In xdoc.<Root>.<Node>
             Select x.<Leaf>

    For Each n In q
        Console.WriteLine("----- {0} 要素発見", n.Count)

        For Each m In n
            Console.WriteLine(m.Value)
        Next
    Next

End Sub

 結果は以下。/root/node/leaf のデータが拾えています。

----- 2 要素発見


----- 4 要素発見



さしすせ
----- 1 要素発見

続行するには何かキーを押してください . . .

[VB.NET] 通知領域のバルーンヒントを表示する

2014年8月25日

 通知領域にバルーンヒントを表示させたい場合は、Win Form にツールボックスから NotifyIcon を貼り付け、以下のコードを実行かすれば表示できます。

Private Sub Button1_Click(...

    With Me.NotifyIcon1
        .Icon = SystemIcons.Application
        .Visible = True
        .BalloonTipTitle = "タイトル"
        .BalloonTipText = "メッセージ"
        .BalloonTipIcon = ToolTipIcon.Error
        .ShowBalloonTip(5000)

    End With

End Sub

 通知領域へのバルーンヒントは Win Form の機能(System.Windows.Forms 名前空間)なので、そのままでは呼び出すことができません。
 WPF や コマンドラインから使用したい場合は、System.Windows.Forms を参照設定した後、以下のように呼び出します。以下の例では、システムアイコンを参照するために System.Drawing も参照設定しています。
 (要するに、参照設定と Imports を明示して行う以外は変わりはありません)

Sub Main()

    Dim NotifyIcon1 As New System.Windows.Forms.NotifyIcon

    With NotifyIcon1
        .Icon = System.Drawing.SystemIcons.Application
        .Visible = True
        .BalloonTipTitle = "タイトル"
        .BalloonTipText = "メッセージ"
        .BalloonTipIcon = Windows.Forms.ToolTipIcon.Error
        .ShowBalloonTip(5000)

    End With

End Sub

[VB.NET] 標準入力を使ったプロセス呼び出しで、黒い画面を表示しない

2014年8月25日

 標準入力を使用しない呼び出しの場合、ProcessStartInfo の .WindowStyle を ProcessWindowStyle.Hidden とすれば、黒い画面(cmd.exe)が表示されなくなるのですが、標準入力にデータを与えようとすると、.UseShellExecute を False にしろと言われ、これを False にすると、黒い画面が復活してしまいます(汗

 対策としては、.WindowStyle は忘れて、.CreateNoWindow を True に設定します。(NoWindow なので True で表示なしない。ややこしい…)
 また、この場合、データを .write しても標準入力のストリームを開きっぱなしにしていると、.write したデータは呼び出しプロセスにわたらないようなので、忘れずにストリームを .close にします。
 (以下のコード例だと、using を抜けるときに 結果的に .close はされるので、なくても動きますが…)

Dim psInfo = New System.Diagnostics.ProcessStartInfo
With psInfo
    .FileName = "msg"
    .Arguments = "*"
    .RedirectStandardInput = True
    .UseShellExecute = False
    .CreateNoWindow = True

End With

Using ps = New System.Diagnostics.Process()
    ps.StartInfo = psInfo
    ps.Start()

    Using sw = ps.StandardInput
        sw.Write("ああああ" & vbCrLf & "いいいいい")

        .sw.Close()
    End Using

    ps.Close()
End Using

 ただ、msg コマンドで試した結果なので、ほかのコマンドだと挙動が違うかもしれません…

[VB.NET][Oracle] Dapper を使用して Oracle にアクセスしてみた

2014年8月24日

 VB.NET で Dapper を使って Oracle に接続してみました。
 Dapper というのは .net 用の MicroORM のことです。概要はこちらを。

  1. SELECT の場合(動的オブジェクトで戻してもらう場合)
    Dim connstr = "User Id=scott; Password=*****; Data Source=*****"
    
    Using cn As System.Data.IDbConnection = New Oracle.DataAccess.Client.OracleConnection(connstr)
        cn.Open()
    
        Dim result = cn.Query( _
                             "select * from EMP where EMPNO <= :empno", _
                             New With {.empno = "7800"})
    
        ' 一つずつ拾う
        For Each x In result
            Debug.Print(x.ENAME)
        Next
    
        cn.Close()
    End Using
    

     特筆すべきは、バインド変数の値を動的オブジェクトを使って与えられるところでしょうか。Parameter.Add が必要がありません。
     ただ、この場合は動的オブジェクトで戻るのでインテリセンスは効きません(汗

  2. SELECT の場合(事前にクラスを用意し、それに MAP したい場合)
    Dim connstr = "User Id=scott; Password=*****; Data Source=*****"
    
    Using cn As System.Data.IDbConnection = New Oracle.DataAccess.Client.OracleConnection(connstr)
        cn.Open()
    
        Dim result = cn.Query(Of EMP)( _
                             "select * from EMP where EMPNO <= :empno", _
                             New With {.empno = "7800"})
    
        ' 一つずつ拾う
        For Each x In result
            Debug.Print(x.ENAME)
        Next
    
        cn.Close()
    End Using
    

     Query メソッドに MAP したいクラスの型名を与えてやるだけです。
     クラスの定義はごく普通に。クラス内にある Private クラスでよいし、メンバーも Property ではなく Field で OK のようです。
     ポイントは、SELECT 側の列型名とクラス側のメンバー変数名を同じにしておくこと。

    Private Class EMP
        Public EMPNO As Integer
        Public ENAME As String
        Public JOB As String
        Public MGR As Integer
        Public HIREDATE As Date
        Public SAL As Decimal
        Public COMM As Decimal
        Public DEPTNO As Integer
    End Class
    
  3. Insert の場合
    Dim connstr = "User Id=scott;Password=*****;Data Source=*****"
    
    Using cn As System.Data.IDbConnection = New Oracle.DataAccess.Client.OracleConnection(connstr)
        cn.Open()
    
        Using tr = cn.BeginTransaction
            cn.Execute("insert into EMP (EMPNO) values (:EMPNO)", {New With {.EMPNO = 30},
                                                                   New With {.EMPNO = 40}})
    
            tr.Commit()
        End Using
    
    
        cn.Close()
    End Using
    

     なんと 単純変数だけでなく Collection も渡すことができます。上記は直接配列を作成していますが、ここに List(Of Object) な変数を充てても動作します。

[C#] StringBuilder は ThreadSafe か?

2014年8月10日

 StringBuilder は ThreadSafe かどうか試してみた。

 結果

37330 回目に失敗
結果の文字列長   : 10016
結果の先頭50文字 : OOOOOOOOOOOOOOO...................................
続行するには何かキーを押してください . . .

 壊れることはあるみたいですね…
 (この例では、先頭17文字目からオーバーライトしてしまっている感じ)

 上記の評価コードは以下

// 連結元の文字列
private static StringBuilder _a;
private static StringBuilder _b;
// 連結後の文字列
private static StringBuilder _concat;

// スレッドA側のAppend
static void append_a()
{
    _concat.Append(_a);
}

// スレッドB側のAppend
static void append_b()
{
    _concat.Append(_b);
}

static void Main(string[] args)
{
    const int L = 10000;

    // 準備:Append 元の文字列を作る
    _a = new StringBuilder();
    _b = new StringBuilder();

    for (var i = 0; i < L; i+=1)
    {
        _a.Append("O");
        _b.Append(".");
    }


    // ここからテスト
    var count = 0;

    while (true)
    {
        _concat = new StringBuilder();

        // スレッドを作って起動
        var thread_a = new System.Threading.Thread(append_a);
        var thread_b = new System.Threading.Thread(append_b);

        thread_a.Start();
        thread_b.Start();

        // スレッドの終了を待つ
        thread_a.Join();
        thread_b.Join();

        // 評価
        count += 1;
        if (_concat.Length != L * 2) // 文字数 L 同士の結合なので、期待する文字数は L*2
        {
            Console.WriteLine("{0} 回目に失敗",count.ToString());
            Console.WriteLine("結果の文字列長   : {0}", _concat.Length);
            Console.WriteLine("結果の先頭50文字 : {0}", _concat.ToString().Substring(1, 50));
            return;
        }
        
    }
}

[VB.NET] VB で Either 型(その2)

2014年6月11日

 http://ooltcloud.sakura.ne.jp/blog/201406/article_11235313.html を改良してみた。修正点は以下。

  • Either を Interface から Super Class に変えた。
  • Either に Match メソッドを追加した。
Class Either(Of A, B)
    Protected _right As A
    Protected _left As B

    Sub Match(Right As Action(Of A), Left As Action(Of B))
        Select Case Me.GetType
            Case GetType(Right(Of A, B))
                Right(_right)

            Case GetType(Left(Of A, B))
                Left(_left)

        End Select
    End Sub
End Class

Class Right(Of A, B)
    Inherits Either(Of A, B)

    Sub New(obj As A)
        _right = obj
    End Sub
End Class

Class Left(Of A, B)
    Inherits Either(Of A, B)

    Sub New(obj As B)
        _left = obj
    End Sub
End Class

 こうすることで使用側のコードは多少すっきりした…と思う(汗

Enum ErrorCode
    SyntaxError
End Enum
Module Module1

    Sub f(obj As Either(Of Tuple(Of Integer, Integer), ErrorCode))
        Dim isSucess = False
        Dim y As Double = 0

        obj.Match(Right:=Sub(x)
                             isSucess = True
                             y = x.Item1 + x.Item2
                         End Sub,
                   Left:=Sub(x)
                             isSucess = False
                             Console.WriteLine(x.ToString)
                         End Sub)

        If isSucess = False Then Exit Sub

        Console.WriteLine("{0}", y)
    End Sub

    Sub Main()
        Dim a = New Right(Of Tuple(Of Integer, Integer), ErrorCode)(Tuple.Create(100, 200))
        f(a)

        Dim b = New Left(Of Tuple(Of Integer, Integer), ErrorCode)(ErrorCode.SyntaxError)
        f(b)
    End Sub

End Module

 結果

300
SyntaxError
続行するには何かキーを押してください . . .

 まあこれだと使えないことはないか…な?(汗

[VB.NET] VB で Either 型

2014年6月11日

 Either 型っぽいものが VB で実装できないか、ちょっと試してみた。

' Either型
Interface Either(Of A, B)
End Interface

' Either.Right型
Class Right(Of A, B)
    Implements Either(Of A, B)

    Private _obj As A

    Sub New(obj As A)
        _obj = obj
    End Sub

    Public Function GetValue() As A
        Return _obj
    End Function
End Class

' Either.Left型
Class Left(Of A, B)
    Implements Either(Of A, B)

    Private _obj As B

    Sub New(obj As B)
        _obj = obj
    End Sub

    Public Function GetValue() As B
        Return _obj
    End Function
End Class
' エラーコード用 Enum
Enum ErrorCode
    NoError
    SyntaxError
End Enum
Module Module1

    ' Either型の評価(実処理)
    Sub f(a As Either(Of Tuple(Of Integer, Integer), ErrorCode))

        Select Case a.GetType

            Case GetType(Right(Of Tuple(Of Integer, Integer), ErrorCode))
                Dim b = CType(a, Right(Of Tuple(Of Integer, Integer), ErrorCode))
                Dim c = b.GetValue
                Console.WriteLine(c.Item1 + c.Item2)

            Case GetType(Left(Of Tuple(Of Integer, Integer), ErrorCode))
                Dim b = CType(a, Left(Of Tuple(Of Integer, Integer), ErrorCode))
                Console.WriteLine(b.GetValue.ToString)

        End Select

    End Sub

    ' Main
    Sub Main()
        Dim a = New Right(Of Tuple(Of Integer, Integer), ErrorCode)(Tuple.Create(100, 200))
        f(a)

        Dim b = New Left(Of Tuple(Of Integer, Integer), ErrorCode)(ErrorCode.SyntaxError)
        f(b)

    End Sub

End Module

 結果

300
SyntaxError
続行するには何かキーを押してください . . .

 なんとなく似たようなものはできたけど、型名の記述ばかりで処理が見えない…。煩雑で使い物にならないですね(汗

 これなら Tuple の第一引数にエラーコードを埋める形のほうが、まだスマートな感じです(汗

Module Module1

    ' 評価(実処理)
    Sub f(a As Tuple(Of ErrorCode, Integer, Integer))

        Select Case a.Item1

            Case ErrorCode.NoError
                Console.WriteLine(a.Item2 + a.Item3)

            Case Else
                Console.WriteLine(a.Item1.ToString)

        End Select

    End Sub

    ' Main
    Sub Main()
        Dim a = Tuple.Create(ErrorCode.NoError, 100, 200)
        f(a)

        Dim b = Tuple.Create(ErrorCode.SyntaxError, 0, 0)
        f(b)

    End Sub

End Module

[F#] Either 型による return

2014年6月11日

 「値がないのを知らせるのが Option 型として、では何故値がなかったかを知らせるにはどうすればいい?」という問いに対して、「Haskell だと Either ってのがある」教えてもらったので、F# で真似てみました。

 まず Either 型を定義する。

type Either<'a,'b> = Right of 'a | Left of 'b;;

 Either 型を渡すと、以下の処理をする関数を作る。

  • 成功(=Right) の時は、Right で渡された値の和を計算し(= a + b)、文字列で戻す。
  • 失敗(=Left) の時は Left で渡された値に "Error " を付加して戻す。
let f x = match x with
          | Right (a:int,b:int) -> string(a+b)
          | Left (e:int) -> sprintf "Error %d" e
;;

 実行。

> f (Either.Right (100,200));;
val it : string = "300"

> f (Either.Left 101);;
val it : string = "Error 101"

 この方法だと 成功(Right)か失敗(Left)かの評価を、Either 型を受け取る側に強いることができますね。

[Excel] マクロで xlsx 形式のシートを xls 形式の book にコピーしてしまうのを防止する方法

2014年4月25日

 以下のような Excel VBA なマクロプログラムを書く。やりたいことは「新しいブックを作って、そのブックにマクロがあるブックにあるシートを一枚コピー」したい。

Sub AddWorkbookWithCopyWorkSheet()

    Dim newBook As Workbook
    Set newBook = Workbooks.Add ' 新しい Book の生成と参照

    ' 自分のシートを新しいBookにコピー
    ThisWorkbook.Sheets(1).Copy newBook.Sheets(1)

End Sub

 ところがこのマクロ、特定の条件が成立するとエラーとなり、以下のダイアログが表示されてしまいます。

 その条件は以下。

  • マクロがある側(コピー元)が xlsx,xlsm のような office2007 形式で保存されている。
  • デフォルトの「ファイルの保存形式」が「Excel 97-2003 ブック (*.xls)」になっている

 この条件が両方成立するとき、Workbooks.Add をしたとき、xls形式(65535行/256列) で新規 Workbook が生成されます。その後、xls 形式の book に xlsm (1048576行/16384列) のシートをコピーしようとしたため、当該エラーが発生します。

 この問題が面倒くさいのは、従来 xls のマクロを xlsm に改名して保存するとエラーになってしまうこと。ユーザーは保存形式を変えただけでマクロが動かなくなってしまいためパニックに陥ります。
 もう一点面倒くさいのは Workbooks.Add がデフォルトの保存形式の設定によって、生成されるワークブックが異なる点。せめて Workbooks.Add の時に xls 形式か xlsx 形式か指定できればいいのだけれど、その指定はできないことです。

---------

 デフォルトの保存形式 xls で、マクロがある book が xlsm の場合に、新規で作成するワークブックを xls ベースでなく xlsx ベースにするための対策案は以下の2つ。

  1. デフォルトの保存形式を xlsx にしてから Workbook.Add を実行する。
    Application.DefaultSaveFormat = xlOpenXMLWorkbook
    
    Dim newBook As Workbook
    Set newBook = Workbooks.Add
    

     わりと単純な実装です。
     ですが、Excel のアプリケーション設定(=デフォルトの保存形式)を触ることになります。このため、使用後は忘れずに設定を元に戻す必要があります。

  2. コピーするシートを持つ Workbook(xlsm) でまずシートを追加し、そのシートを新規 Workbook として移動させる方法
    Dim newSheet As Worksheet
    Set newSheet = Sheets.Add  ' 自分に新しい Sheet を生成
    newSheet.Move              ' 新しい Book を生成 (Sheet を移動)
    
    Dim newBook As Workbook
    Set newBook = Workbooks.Item(Workbooks.Count) ' 新しい Book への参照
    

     この方法の場合、アプリケーション設定は触らないので、DefaultSaveFormat を操作するよりは安全です。加えて、生成される新規のワークブックは、マクロ側のワークブックの形式と同じになるので Excel 2003 でも動くはず。
     欠点としては、コードを一目でみただけでは何を目的としたコードなのかわからないこと。なのでコメントなどで注釈を入れておくなどの配慮が必要です。

---------

 ただ、今回の命題 (マクロのある Workbook にあるシートを、新しい Workbook にコピーしたい) の場合は、上記の様に Workbooks.Add 的なことをするまでもなく、以下のコード一発で解決します(汗

ThisWorkbook.Sheets("コピー元シート").Copy ' 新しい Book を生成 (Sheet を Copy)

Dim newBook As Workbook
Set newBook = Workbooks.Item(Workbooks.Count) ' 新しい Book への参照

[VB.NET] 半角カナのみ全角カナに変換する

2014年4月18日

 半角カナのみ全角カナに変換するサンプル。
 単純に StrConv の VbStrConv.VbStrConv.Wide を指定するだけだと、英数字も全角になってしまうのでその対策。

''' <summary>
''' 半角カナを全角カナに変換
''' </summary>
Public Shared Function ToZenkakuKana(ByVal in_strData As String) As String

    ' 半角カナのコード範囲
    Const HANKAKU_KANA_PTTERN As String = "[uFF61-uFF9F]+"

    Return Regex.Replace(in_strData, HANKAKU_KANA_PTTERN,
                         Function(in_match As Match) As String
                             ' 切り出した半角カナ部分を全角カナに変換
                             Return StrConv(in_match.ToString, VbStrConv.Wide)
                         End Function)

End Function

 ついでにその逆。ひらがなも含めて半角カナに。

''' <summary>
''' かなを半角カナに変換
''' </summary>
Public Shared Function ToHankakuKana(ByVal in_strData As String) As String

    Return StrConv(in_strData, VbStrConv.Katakana Or VbStrConv.Narrow)

End Function

 C# の人は、Microsoft.VisualBasic を参照設定したうえで Microsoft.VisualBasic.Strings.StrConv() を使用します。

[VB.NET] IPv4 アドレス or host 名の文字列から、IPAddress インスタンスを取得する

2014年4月17日

 TcpClient などで通信する際、相手先の IP アドレス(Net.IPAddress のインスタンス)を得る必要があります。
 ところが、Net.IPAddress を直接 new しようとすると、コンストラクタ引数が使い物にならないものばかりです(汗

 New Net.IPAddress を使用する場合、IP アドレスを指定する時は以下のように書けばよいです。

New Net.IPAddress({192, 168, 0, 1})

 しかしhost 名を指定することはできませんし、IP アドレスを指定する時も 4 つの数値ではなく、ping コマンドとかで指定するような感じで "192.168.0.1" と指定したかったりします。

 この要求に最も近いのが、Net.Dns.GetHostAddresses を使って解決する方法です。host 名指定でも IP アドレス文字列による 指定でも parse してくれます。

 しかし、結果が配列で戻ってきます。しかもプロトコル混在で…(汗
 仕組的にアドレスが複数戻ってくるのは当然とはいえ、通信相手のアドレスは一つあればいいです。なので、IP アドレスを一つだけ返すラッパー関数を作ってみました。以下は IPv4 のアドレスのうち、最初に回答のあったものを一つ戻す例です。

Public Shared Function GetIPv4FirstAddress(address As String) As Net.IPAddress

    Dim entries = Net.Dns.GetHostAddresses(address)

    For Each ip In entries
        If ip.AddressFamily = Net.Sockets.AddressFamily.InterNetwork Then
            Return ip
        End If
    Next

    Return Nothing
End Function

[VB.NET] 文字列から色コードを取得する

2014年4月15日

 色を app.config に保存している場合などで、色コードの文字列を Parse して色コードを取得する方法について。

 Windows Form の場合。ColorTranslator.FromHtml メソッドを使用します。

Dim c = ColorTranslator.FromHtml("#006400")
Dim c = ColorTranslator.FromHtml("DarkGreen")
Me.BackColor = c

 WPF の場合。ColorConverter.ConvertFromString メソッドを使用します。

Dim c = CType(ColorConverter.ConvertFromString("#006400"), Color) 
Dim c = CType(ColorConverter.ConvertFromString("DarkGreen"), Color)  
Me.Background = New SolidColorBrush(c)

 また、逆変換の場合は、Windows Form の場合は ColorTranslator.ToHtml メソッドで、WPF の場合は color 型インスタンスの ToString メソッドで行えるようです。

[PostgreSQL] PostgreSQL で配列要素を SQL で選択する

2014年4月15日

 先日、Oracle の VARRAY の Select をしましたが、それの PostgreSQL 版を試す。

 まずは create table から。Oracle のように create type は必要なし。
 型名(以下の例だと VARCHAR)に、[]、をつけてやれば、それで(上限なしの)配列として定義されるようです。

postgres=# CREATE TABLE tab1(
postgres(#     no NUMERIC(5),
postgres(#     list VARCHAR[]
postgres(# )
postgres-# ;
CREATE TABLE

 insert は「array[データの列挙]」か、「'{データの列挙}'」で書ける模様。
 ただ「'{データの列挙}'」の場合、文字はシングルクォートで括らずダブルクォートで括るようです。

postgres=# INSERT INTO tab1 VALUES (10, '{"AA", "BB", "CC", "DD", "EE"}');
INSERT 0 1
postgres=# INSERT INTO tab1 VALUES (11, ARRAY['A1', 'B1', 'C1', 'D1', 'E1']);
INSERT 0 1
postgres=# INSERT INTO tab1 VALUES (12, ARRAY['A2', 'B2', 'C2']);
INSERT 0 1
postgres=# INSERT INTO tab1 VALUES (13, ARRAY['Z3', 'B2', 'C2']);
INSERT 0 1

 psql.exe 上で select してみるとこんな感じ。
 Oracle の場合と大差ない感じですね。

postgres=# SELECT * FROM tab1;
 no |       list
----+------------------
 10 | {AA,BB,CC,DD,EE}
 11 | {A1,B1,C1,D1,E1}
 12 | {A2,B2,C2}
 13 | {Z3,B2,C2}
(4 行)

 そして、全行の配列要素(=index)が 2 のものだけ抽出…、というのは、Oracle と違い、あっさり、かつ直感的にかけてしまいます(汗
 このあたりは Oracle はぜひ見習ってほしい…

postgres=# SELECT no, list[2] FROM tab1;
 no | list
----+------
 10 | BB
 11 | B1
 12 | B2
 13 | B2
(4 行)

[C言語] C言語で可変引数の関数を作る

2014年4月14日

 C 言語では可変引数の関数を作ることができるし、使ってもいる(printf とか)けど、自分で書いたことはなかったので書いてみました。

#include 
 
void func(int argc, ...) {
    va_list argv;

    int     a;
    char*   b;
    double  c;

    va_start(argv, argc);
    
    if (argc >= 1) {
        a = va_arg(argv, int);
        printf("%dn", a);
    }

    if (argc >= 2) {
        b = va_arg(argv, char*);
        printf("%sn", b);
    }

    if (argc >= 3) {
        c = va_arg(argv, double);
        printf("%fn", c);
    }

    va_end(argv);

    printf("------------------------n");
}

void main() {

    func(3, 100, "aaa", 10.5);
    func(1, 200);

}

 ポイントは4つ

  1. 可変引数部分の仮引数宣言は...で表現する。
  2. 可変引数の取り出しには、va_start/va_arg/va_end 等のマクロを使用する。
  3. 可変引数がいくつ渡されたのかを直接知る方法はないので、いくつ渡したかは何らかの方法で伝える必要がある。
  4. 必ず1つは普通の引数(上記だと argc)が必要。

 --------------------

 あと、va_list/va_start/va_arg は大体何やっているか想像がついたけど、va_end がなにをやっているかどうしても想像できない。
 しかし、ぐぐっても「"va_end" は必ず指定しましょう」的な記述しかなく、その効用が書かれているものがない…
 そこで、K&R 本(プログラミング言語C 第2版)を見ても、最後は va_end 実行するように書かれていたが、何をしているのかは書かれていなかった(汗

 仕方ないので、VC++ の Header を覗いてみた。すると以下。

#define va_start _crt_va_start
#define va_arg   _crt_va_arg
#define va_end   _crt_va_end

 GCC 系の Header も見つかったので、そちらを見るとこんな感じ。

#define va_start(v,l)   __builtin_va_start(v,l)
#define va_end(v)       __builtin_va_end(v)
#define va_arg(v,l)     __builtin_va_arg(v,l)

 ともに参考にならない…(汗
 そこで LSI-C 試食版をダウンロードして確認すると以下。

typedef void    *va_list;
#define va_start(ap, pn)        (ap = (va_list)(&pn + 1))
#define va_arg(ap, t)           (*(*(t **)&ap)++)
#define va_end(ap)

 ああやっぱり、va_end って消えるのね…なんとなく納得。
 てことは、va_end は「お作法」だった、ってことですかね…

 ただ、今の実装を確認すると va_list は null になるようです。(VS2013 の VC++ で確認)
 もしかすると、これに加えてセキュリティ対策などで何かしらの終了処理があったりするのかもしれない。この辺までは確認していません。

[C#] C# から C 言語の可変引数を持つ関数を呼ぶ

2014年4月12日

 C 言語の可変引数の関数を呼び出さなくてはならない場合、選択肢の一つとして、__arglist キーワードを使用する方法があるようです。

 参考) 可変引数をパラメータに持つC++の関数を、C#から利用する方法

using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class Program
    {
        [DllImport("msvcrt", CallingConvention = CallingConvention.Cdecl)]
        static extern int printf(string format, __arglist);

        static void Main(string[] args)
        {
            printf("%dn", __arglist(123));
            printf("%05d, %fn", __arglist(123, 456.78));
            printf("%2.3f, %sn", __arglist((double)12.345f, "abc"));
        }

    }
}

 とりあえず int や double(倍精度)、char*(文字列) は普通に通る模様。上記例では float(単精度) 直渡しはダメっぽかったので double で cast しています。

 ただ、この __arglist、非公式機能らしい。

 参考) .NETと安全なポインタ

 管理下ポインタによく似たものに、「型付けされた参照」がある。ボックス化を伴わずに、任意の型の値型を参照渡しできる、といえばその特徴が伝わるだろうか。型付けされた参照を利用すれば、正当性検証可能なsscanf関数(=C言語の文字列書式化関数。関数引数として複数の型を受け取れる)を実装することができる。

 とはいえ、型付けされた参照はCLS(共通言語仕様)準拠ではなく、今後C#で正式にサポートされる可能性も低い。大多数の人にとって、いままでもこれからも、型付けされた参照を目にする機会はないだろう。ただし、Visual C#には非公式キーワードとして__makeref、__refvalue、__arglistがあって、これらを使用すれば対応するILコードを出力することはできる。

 この説明によると、__arglist とは「ボックス化を伴わずに任意の型の値型を参照渡し」するもののようです。

 しかしこの機能、非公式機能ということからも多用は禁物ですね。どうしても C 言語側ライブラリのインターフェースが変えられない場合に限って使用するようにしたほうが無難な気がします。

[Oracle] VARRAY の要素を SQL で選択する

2014年4月6日

 Oracle で配列型(ARRAY)を試してみました。

 作り方は以下。まず create type で型を作ってから、create table でその型を列の型として指定します。

create type VARCHAR2_ARRAY as varray(5) of varchar2(10)

create table TAB1 (
    NO number(5),
    LIST VARCHAR2_ARRAY
)
;

 insert は "型名(データの列挙)" って感じで書けるようです。

insert into TAB1 values (10, VARCHAR2_ARRAY('AA', 'BB', 'CC', 'DD', 'EE'));
insert into TAB1 values (11, VARCHAR2_ARRAY('A1', 'B1', 'C1', 'D1', 'E1'));
insert into TAB1 values (12, VARCHAR2_ARRAY('A2', 'B2', 'C2'));
insert into TAB1 values (13, VARCHAR2_ARRAY('Z3', 'B2', 'C2'));

 sql*plus 上で select してみるとこんな感じ。
 データは見えるけど命令文みたいでちょっと面白くない感じですね。

SQL> select * from TAB1;

        NO LIST
---------- --------------------------------------------------
        10 VARCHAR2_ARRAY('AA', 'BB', 'CC', 'DD', 'EE')
        11 VARCHAR2_ARRAY('A1', 'B1', 'C1', 'D1', 'E1')
        12 VARCHAR2_ARRAY('A2', 'B2', 'C2')
        13 VARCHAR2_ARRAY('Z3', 'B2', 'C2')

 普通の列みたいに見えないか?と思って調べたところ、TABLE 演算子というのがあり、これを使うと配列を表の行データに分解してくれるようです。(コレクション・ネスト解除、というらしい)

SQL> select
  2     ROWNUM,
  3     COLUMN_VALUE
  4  from
  5     TABLE (
  6             select LIST
  7             from TAB1
  8             where NO=11
  9     )
 10  ;

    ROWNUM COLUMN_VALUE
---------- --------------------
         1 A1
         2 B1
         3 C1
         4 D1
         5 E1

 ただこの TABLE 演算子、複数行の入力ができず、クエリの結果は必ず単一行/単一インスタンスでないとならないので、上記コードの 8 行目の where を外すとエラーになってしまいます。

 なのでたとえば、全行の配列要素(=index)が 2 のものだけ抽出…というと、以下のコードを書く必要があります。

SQL> select *
  2  from
  3      (
  4          select
  5              NO,
  6              row_number() over(partition by NO order by N) as ARRAY_INDEX,  -- [3]
  7              ARRAY_VALUE
  8          from
  9              (
 10                  select  -- [2]
 11                      A.NO,
 12                      ROWNUM as N,
 13                      C.COLUMN_VALUE as ARRAY_VALUE
 14                  from  -- [1]
 15                      TAB1 A,
 16                      TABLE(
 17                          select
 18                              LIST
 19                          from
 20                              TAB1 B
 21                          where
 22                              B.NO = A.NO
 23                      ) C
 24             )
 25     )
 26  where
 27     ARRAY_INDEX = 2  -- [4]
 28  ;

        NO ARRAY_INDEX ARRAY_VALUE
---------- ----------- --------------------
        10           2 BB
        11           2 B1
        12           2 B2
        13           2 B2

 上記のコードがやっていることは以下のようなことです。

  1. コレクション・ネスト解除した配列の表と、元の表を結合します。
  2. とりあえず、配列の格納順を保存するために rownum を含めて射影します。

    # この rownum は順序を保存しているだけなので配列の要素番号とは異なります。
    # 今回データの場合だと16件ヒットするので、1~16が振られています。

  3. NO で分割(Partition)します。

    # partition ではソート列を指定する必要があるので、ここで先ほどの rownum 列を指定します。
    # 求める値は分割後の行番号(row_number)にします。これが配列の要素番号になります。

  4. 配列の要素番号を指定し、絞り込みます。

 OLAP 関数使ってまで…って感じでちょっといまいちです。# 誰かほかにいい方法を知っていたら教えてください(汗

 まあ配列型を SQL で扱おうとすること自体がダウト、なのかもしれません。
 PL/SQL で配列の要素を参照する場合は以下のような感じ。

SQL> set serveroutput on
SQL> declare
  2    ar VARCHAR2_ARRAY;
  3  begin
  4    select LIST into ar from TAB1 where NO=11;
  5    dbms_output.put_line(ar(2));
  6  end;
  7  /
B1

PL/SQLプロシージャが正常に完了しました。

 全行の配列要素(=index)が 2 のものだけを表示したい場合は以下。
 ずいぶんとすっきりします。

SQL> set serveroutput on
SQL> declare
  2  begin
  3      for row in (select LIST from TAB1) loop
  4          dbms_output.put_line(row.LIST(2));
  5      end loop;
  6  end;
  7  /
BB
B1
B2
B2

PL/SQLプロシージャが正常に完了しました。

[勉強会] 「すごい広島 #46」に参加しました & GitHubの使用方法2

2014年4月3日

 great-h.github.io に issue を書くと、github に登録したメールアドレス宛に大量のメールが届くようになります。(週間200通程度)
 いやいや。それ、困るんですけど…という場合は、以下の設定を見直すとよいです。

 まず、great-h.github.io の右上部あたりに Unwatch というボタンがあるのを確認します。

 Unwatch をプルダウンすると、以下の3つのメニューが表示されます。

 意味は以下のような感じ。

  • Not Watching

    自分が書いた書き込みに関する変更、または、自分宛の書き込みのみ通知(メール)されます。
    他は通知されません。

  • Watching

    すべての変更が通知(メール)されます。

  • Ignoring

    すべての変更が通知(メール)されません。

 なので、自分に関係するところだけの通知がほしければ Not Watching を。一切の通知が必要なければ Ignoring を選択すればよいです。

 あと、注意してほしいのは、Githubからのメールを迷惑メールとして報告しないようにお願いします。
 …ってここまで書いてこのページ(GitHub からの通知が迷惑メールになった - 見ないリポジトリは Unwatch しよう)の存在を思い出した #というか検索したら引っかかった _| ̄|○
 内容はこちらのほうが正確かつ詳しいので、こちらを参考にしてください。

[JS] クロージャー怖い

2014年3月20日

 先日(といってもすでに半年前…)、HiroshimaRB にて「思ったようにコードが動かない!」という題目で LT をしたのですが、JavaScript のクロージャーのところで質問がありました。私にもっと理解力があって、冷静に対応できればよかったのですが、ぐでぐでな対応になってしまったので、反省を込めてここに記す(汗

  1. 問題のコード

     サンプルコードはこのようなものでした。

    <html><body>
    
    <span id="value" style="font-size: 48pt;" ></span>
    
    <script type="text/javascript">
    
      for (i=0; i<5; i++) {
        f = function() {
          // i の値を追記する
          document.getElementById("value").innerHTML += i + "<br>";
        }
    
        // i 秒後に i を追加表示する(予定)
        setTimeout("f()", i*1000);
      }
    
    </script>
    
    </body></html>
    

     期待する結果は、1 秒ごとに 0 → 1 → 2 → 3 → 4 と表示される予定です。

    0
    1
    2
    3
    4

     しかし実行結果は、 0 → 1 → 2 → 3 → 4、とはならず、すべて 5 で表示されます。

    5
    5
    5
    5
    5

  2. 指摘の内容

     この時、for のところに var を入れればよいのでは、という指摘がありました。
     指摘の理由は、上記のコードの変数 i スコープがグローバルです。だから値を共有してしまったのではないか、ということでした。
     つまり、上記のコードの以下の部分を、

    for (i=0; i<5; i++) {
    

    以下のように「var」を追加し、変数 i のスコープをグローバルからローカルに変えれぱ解決するのではないか?という指摘です。

    for (var i=0; i<5; i++) {
    

     しかし、これでは結果は変わらず、期待した動作にはなりませんでした。

  3. グローバル変数とローカル変数

     ところで、JavaScript では、なにも宣言せずに使用を始めた変数というのはグローバル変数相当(Window(=this) のメンバー) になります。(*1)
     試してみます。

    <html>
      <body>
        <form>
          <script>
            function click1()
            {
                x = "aaa";
                alert(x);
            }
    
            function click2()
            {
                alert(x);
                alert(this.x);
                alert(window.x);
            }
          </script>
    
          <input type="button" value="ボタン1" onClick="click1()">
          <input type="button" value="ボタン2" onClick="click2()">
    
        </form>
      </body>
    </html>
    

     ボタン1 を押すと、"aaa" で alert が表示されます。
     その後、ボタン2 を押すと、click2() では x は宣言や代入をしていないはずなのに "aaa" で alert が表示されます。
     つまり click1() で代入した x のスコープはローカル変数ではなく、グローバル変数に相当することがわかります。

     x をローカル変数にするためには、代入文の前に var をつけます。(*2)
     こうすると、click1() を実行した後に click2() を実行しても、click2() の x は定義されていないため、alert は表示されません。

    <html>
      <body>
        <form>
          <script>
            function click1()
            {
                var x = "aaa";
                alert(x);
            }
    
            function click2()
            {
                alert(x);
                alert(this.x);
                alert(window.x);
            }
          </script>
    
          <input type="button" value="ボタン1" onClick="click1()">
          <input type="button" value="ボタン2" onClick="click2()">
    
        </form>
      </body>
    </html>
    
  4. タイマー要素を排して、もう一度眺めてみる

     指摘の内容は、

    • グローバル変数なので、
    • 各 function がグローバル変数を参照(共有)してしまい、

    同じ値(x の値が for を抜けた時の値)になってしまうのではないか?という話でした。この指摘は、半分あたりだとおもいます。

     しかし単純にローカル変数にしてもうまくいかないことを試してみます。
     上記コードではタイマー(SetTimeout)に渡すようになっていますが、事を単純にするため、タイマーは使わないコードに変更します。

    <html>
      <body>
        <form>
          <script>
            function click1()
            {
                var f = new Array(5);
    
                // 仕込
                for (var i=0; i<5; i++) {
                    f[i] = function() {
                        alert(i);
                    }
                }
    
                // 実行
                f[0]();
                f[1]();
                f[2]();
                f[3]();
                f[4]();
            }
          </script>
    
          <input type="button" value="ボタン1" onClick="click1()">
    
        </form>
      </body>
    </html>
    

     結果は、5 回とも "5" で alert が表示されます。

  5. 原因

     上記コードの「仕込み」の部分の「alert(i)」の部分に注目します。
     この alert(i) は for 文のループ中では実行されません。function の宣言(定義)をしているだけなんですね。
     実行はその下の「実行」のところの f[0]()~f[4]() で行っています。

     alert(i) の i の値がどこで確定(*3)するかというと、function f[i]() の宣言時ではなく、f[0](); の実行時です。
     実行は for を抜けた後です。forを 抜けた後ということは、当然 i = 5 ですから、alert もすべて 5 で表示されます。

  6. 対策

     f[i] の中の i は、値ではなく変数 i を参照しています。これが原因で、f[i] は実行したときの変数 i の値を表示してしてしまいます。
     ということは、変数 i を参照するのではなく、for を実行したときの変数 i の"値"を何かしらの方法で保存してやればよいことになります。

     手順は以下の通り。
      1.f[i]() を作る処理を g() で囲みます。
      2.g() の中で、ループ変数 i を g() のローカル変数 x に代入します。
      3.for ループの中で、g() を実行します。

     こうすると、function を使わず直截変数 i を参照しているケースと違って、以下のように動作します。

    1. g() は for ループ内で実行するので、var x = i により、変数 x に変数 i の値が代入されます。
    2. g() は終了するとき、x は i の値で固定されます。
    3. for の 2 ループ目に g(); を実行するときは、変数 x のインスタンスは 1 ループ目のものとは違うものが与えられます。
      #変数 x は g() のスコープにいるので、前回ループで実行した g() での値は引き継がない。
    4. したがって、f[i] で参照している x は ループ中の i と同じ値になります。(*4)

     つまり、変数 i のスコープは click2() ですが、変数 x のスコープは g() です。したがって変数 x は f[i] では共有されず、それぞれ別々の値(=インスタンス)を保持することになります。

    <html>
      <body>
        <form>
          <script>
            function click2()
            {
                var f = new Array(5);
    
                for (var i=0; i<5; i++)
                {
                    g = function()
                        {
                            /* click2()のスコープである i を
                               g() の スコープである x に代入する */
                            var x = i;
    
                            f[i] = function() {
                                alert(x);
                            }
                        }
    
                    g();  // ここで g() の中の x が確定する。 
                }
    
                f[0]();
                f[1]();
                f[2]();
                f[3]();
                f[4]();
            }
          </script>
    
          <input type="button" value="ボタン1" onClick="click2()">
    
        </form>
      </body>
    </html>
    

    (*1)
     Perl もそうみたいです。(ただ、perl は var ではなく my と書くようです)
     VB(VBS や VBA も)は、ルーチン内で代入したものはデフォルトではローカルスコープ。但し、あとから同名のグローバル変数を作るとグローバル化してしまう。
     JavaScript のスコープは結構変態です。(と思う)

    (*2)
     実は var は後に記述してもいいらしいです。
     宣言を後に回した記述例。こう書いても x はローカル変数として認識します。
     この点も変態ですね。

    function click1()
    {
        x = "aaa";
        alert(x);
    
        var x
    }
    

    (*3)
     正確には i の値を「確定」しているわけではなく、ただ単純に i の値が参照されているだけですが…

    (*4)
     function が return すればローカル変数も解放されるはずですが、この場合はされません。
     ローカル変数のキャプチャという機能によって、そのローカル変数を参照している function がいなくなるまで保持されています。

[工業] 配管の「呼び径」と呼ばれるものは一体何か

2014年3月20日

 配管のサイズ(=管径)は「呼び径」とか「呼称」と呼ばれるもので表します。たとえば、消防法施行規則には「主配管のうち、立上り管は、管の呼びで32ミリメートル以上のものとすること」という記述があります。また図面では「管の呼びで32ミリメートル」は「32A」と表記します。

 同様に「配管の摩擦損失計算の基準」でも「大きさの呼び A」という記述があります。
 配管の摩擦損失計算の基準
 配管の摩擦損失計算の基準(google 検索)

 この「呼び」とはなにか?「32A」と書くと一般には「32アンペア」で "A" は電流の単位ですが、この「32A」の "A" はどういう単位なのか?というのが以前から気になっていたので調べてみました。

 ・・・

 まず前述の「立上り管は、管の呼びで32ミリメートル以上のものとすること」の文脈から、菅の太さ(=径)を指しているのはわかります。それではどちらの径を指すのか。外径を指すのか、内径を指すのか。

 たとえば 32A の配管の外径は約 42.7mm であり 32mm ではありません。では内径か?というと、実はそうでもありません。
 32A の 圧力配管用炭素鋼鋼管(JIS G 3454) の内径は以下です。
  ・スケジュール 40 のとき 35.5mm (= 42.7mm - 3.6mm × 2)
  ・スケジュール 50 のとき 33.7mm (= 42.7mm - 4.5mm × 2)
  ・スケジュール 60 のとき 32.9mm (= 42.7mm - 4.9mm × 2)
  参考) JIS G 3454圧力配管用炭素鋼鋼管 で検索してみてください。
 スケジュール 60 のときに、もっとも 32mm に近づきますが、それでも完全には一致しません。(ちなみにスケジュールは配管の肉厚です。スケジュール番号が大きくなるほど、肉厚が厚くなります)
 また 32A は 1¼B でもありますが 1.25 インチ = 31.75mm なのでやはり一致しません。

 ・・・

 結論としては、呼び径は、管の"外径"寸法を表しています。
  http://ja.wikipedia.org/wiki/金属管#管の太さのあらわし方(円管)
 「呼び径は内径」と述べている WEB ページもいくつか検索にひっかかりますが、規格上はあくまで「外径」です。そして 350A(14B) 以上のインチ呼称では外径そのものです。(350A = 14B = 14インチ = 355.6mm = 外径)
 しかし、350A 未満の配管では「数値=外径」ではありません。

 なぜそんなことになっているかというと、鉄パイプの規格が元々はインチ管の内径をもとに決められていたことに由来します。
 かなり古い英国規格(BS 78)にその名残りを見ることができます。
  http://en.wikipedia.org/wiki/Cast_iron_pipe#Standardization
 上述の表だと、10 以下の呼び径(Nominal Size)は、クラス C の内径(Internal Diameter)と同じ数値です。呼び径が 12 以上になるとクラス D の内径と同じ数値になっています。つまり、この規格ができた頃は内径を基準にしていたと思われます。

 流体は管の中を流れるので、内径が基準となるのは自然です。しかし内径を固定すると肉厚(=耐えるべき管内圧によって変化する)によって外径が変わってしまいます。外径が変わると支持金具等の寸法が変わってしまい施工的には不便です。呼び径が同じ値=外径が同じ、としたほうが施工的には便利です。(この方針は、前述の BS 78 ですでに表れています)このため、呼び径と内径が一致しなくなります。
 その後時代が下り、同じ外径で同じ菅内圧に耐えられるパイプが、より薄い肉厚でが作られるようになっていったことで、呼び径と内径の乖離がさらに大きくなっていきます。

 ・・・

 加えて、もともとは管の径はインチで呼称していましたが、それまでインチ呼称だった管をミリメートル呼称でも呼ぶようになります。このため、インチの呼びには「B」を、ミリメートルでの呼びには「A」を付けて区別するようになります。
 とはいえ A 呼称と B 呼称は、ミリメートル - インチ変換の単位変換では求められない点に注意です。たとえば、1インチ=25.4mm ですが、1B = 25A / 2B = 50A となり xB = x*25.4A ではありません。また 3B = 80A なので xB = x*25A ですらありません。

 なぜインチ基準の配管をわざわざミリメートル表示にしているかというと、メートル法への批准が理由みたいです。(計量法による規制)
 しかし、継手については 「呼び径(A)を継手の大きさを呼ぶのに用いないほうがよい」(JIS B 2301)とあり、必ずしもミリメートル呼称でなくてはならないということでもないようで、本当に計量法の関係で A 呼称が生まれたのかどうかについては確認できませんでした。
(継手がインチ表示のままなのは「継手を作るための鋳型にインチ表示が含まれており、それを変更するのは現実的でないから」という説があるようです。しかし、こちらの真偽も確認できていません)

 ・・・

 ということで、まとめると以下。

  • 呼び径は外径を指します。(=呼び径が一緒なら外径が一緒) (*1)(*2)
  • 歴史的な経緯から "細い配管では" 外径より内径に近い数値で表示されています。しかし、近いだけで実内径とは一致しませんし、厳密には内径との関連はありません。
  • また「32A」と書かれても、それがサイズの数値(=「32」)と単位(=「A」)を表すものではなく、「32A」あるいは「呼びAで32」とかで一つの規格を指しています。したがって、たとえば「33A」というサイズの配管は存在しません。

 (*1) JIS配管(JIS B 2001)と ANSI配管(ASME/ANSI B36.19)とでは、おなじ呼び径でも外径が異なるものが多い。
 (*2) ½B は 13A でもあり 15A でもあるらしく、½B(よんぶ=4/8の意)と呼ぶと文脈によって、サイズが違ったりするらしい。

[WEB] ブロック要素を畳む

2014年1月18日

 blog でソースコードなどを提示するとき、コード行数が多いものを全文表示すると本文がコード例に埋まってしまうので、折りたたんで表示したい。その方法について調査してみました。(*1)

 コード例は以下。

<button 
    onclick="document.getElementById('target').style.height='Auto';"
    style="margin:0px 10px;">ひらく
</button><button 
    onclick="document.getElementById('target').style.height='6em';"
    style="margin:0px 10px;">とじる
</button>
<blockquote id="target" style="height:6em; overflow: scroll; padding:10px;">
ここにコンテンツを書く
</blockquote>

 上記のコード例がやっていることは以下。

  1. ブロック要素 (上記例では blockquote) を配置し、そのブロック要素に ID (上記例では 'target') を付与
  2. 当該 ID のブロック要素の height を javascript で操作
    1. 開くボタンは、高さに"自動"を設定 (上記例では height='Auto')
    2. 閉じるボタンは、高さに固定値を設定 (上記例では height='6em')
  3. 当該 ID のブロックは、最初は閉じた状態と同じにしておく (上記例では style="height:6em;)
  4. コンテンツ本体は、当該 ID のブロック要素の中に記述

 上記のコード例を実装すると以下。(*2)

朕深く世界の大勢と帝国の現状とに鑑み非常の措置を以て時局を収拾せむと欲し茲に忠良なる爾臣民に告く

朕は帝国政府をして米英支ソ四国に対し其の共同宣言を受諾する旨通告せしめたり

抑々帝国臣民の康寧を計り万邦共栄の楽を偕にするは皇祖皇宗の遺範にして朕の拳々措かさる所先に米英二国に宣戦せる所以も亦実に帝国の自存と東亜の安定とを庶幾するに出て他国の主権を排し領土を侵すか如きは固より朕か志にあらす然るに交戦既に四歳を閲し朕か陸海将兵の勇戦朕か百僚有司の励精朕か一億衆庶の奉公各々最善を尽せるに拘らす戦局必すしも好転せす世界の大勢亦我に利あらす加之敵は新に残虐なる爆弾を使用して頻に無辜を殺傷し惨害の及ふ所真に測るへからさるに至る而も尚交戦を継続せむか終に我か民族の滅亡を招来するのみならす延て人類の文明をも破却すへし斯の如くむは朕何を以てか億兆の赤子を保し皇祖皇宗の神霊に謝せむや是れ朕か帝国政府をして共同宣言に応せしむるに至れる所以なり

朕は帝国と共に終始東亜の解放に協力せる諸盟邦に対し遺憾の意を表せさるを得す帝国臣民にして戦陣に死し職域に殉し非命に斃れたる者及其の遺族に想を致せは五内為に裂く且戦傷を負ひ災禍を蒙り家業を失ひたる者の厚生に至りては朕の深く軫念する所なり惟ふに今後帝国の受くへき苦難は固より尋常にあらす爾臣民の衷情も朕善く之を知る然れとも朕は時運の趨く所堪へ難きを堪へ忍ひ難きを忍ひ以て万世の為に太平を開かむと欲す

朕は茲に国体を護持し得て忠良なる爾臣民の赤誠に信倚し常に爾臣民と共に在り若し夫れ情の激する所濫に事端を滋くし或は同胞排擠互に時局を乱り為に大道を誤り信義を世界に失ふか如きは朕最も之を戒む宜しく挙国一家子孫相伝へ確く神州の不滅を信し任重くして道遠きを念ひ総力を将来の建設に傾け道義を篤くし志操を固くし誓て国体の精華を発揚し世界の進運に後れさらむことを期すへし爾臣民其れ克く朕か意を体せよ

(*1)
 勉強も兼ねて jQuery で…と思ったが、すごい広島#34 に参考書等々を持ち込み忘れたので断念し、素の javascript で css を操作する方法としました。
(*2)
 コンテンツ部分はいい加減です。読みにくいので、カタカナ・旧字体を、ひらがな・新字体にしてみましたが、正しく変換できているかどうかは保証の限りではありません。

[VB.NET] グループに指定のユーザーが参加しているかを確認する方法

2014年1月6日

 指定のグループに指定のユーザーが参加しているをプログラムから知る方法。
 (指定ユーザーが所属しているグループの一覧を得るわけではない点に注意)

  1. 事前準備
    • .NET の参照設定で、System.DirectoryServices を追加する。
    • COM の参照設定で、"Active DS Type Library" を追加する。
  2. ドメインのユーザーが、ドメインのグループに参加しているかを確認する場合は、以下で確認できます。
    ' Domain User - Domain Group 
    Dim groupName = "WinNT://domain/group"
    Dim userName = "WinNT://domain/user"
    
    Dim dsEntry = New System.DirectoryServices.DirectoryEntry(groupName)
    Dim domainGroup = CType(dsEntry.NativeObject, ActiveDs.IADsGroup)
    
    MsgBox(domainGroup.IsMember(userName))
    
  3. ドメインのユーザーが、ローカルのグループに参加しているかを確認する場合は、グループ名とユーザーの指定の部分を以下のように変更します。

    Dim groupName = "WinNT://domain/" & System.Environment.MachineName & "/group"
    Dim userName = "WinNT://domain/user"
    
  4. ローカルのユーザーが、ローカルのグループに参加しているかを確認する場合は、グループ名とユーザーの指定の部分を以下のように変更します。
    Dim groupName = "WinNT://domain/" & System.Environment.MachineName & "/group"
    Dim userName = "WinNT://domain/" & System.Environment.MachineName & "/user"
    

     上記の domain にはドメイン名またはワークグループ名を指定します。
     user には ユーザー名、group にはグループ名を指定します。

     注意点は、ローカルユーザーやローカルグループであっても、そのマシンがドメインに参加していれば domain 部分には参加しているドメイン名を指定する必要があります。そのマシンがドメインに参加していない場合に、domain 部分にワークグループ名を指定します。


参考)
 http://handcraft.blogsite.org/Memo/Article/Archives/150

p.s.
 ADSI がリークするという話があり、上記とリンクするのかどうかは確認していません。メモとして記録しておきます。(http://blogs.technet.com/b/jpilmblg/archive/2010/05/14/adsi-winnt-adsopenobject.aspx)

[VB.NET] 2進リテラル

2014年1月4日

 VB では 16進数リテラルとして &H、8進数リテラルとして &O があるのですが、2進数リテラルがありません。MSX-BASIC だと &B というのがあったのですが… (ぐぐると LotusScript にも &B があるらしい)

 リテラルはないですが、2進数表現で入力したいとき、2進数表現で表示したい時などは、以下の方法で行うことができます。

Sub Main()
    ' 二進表現 入力
    Dim a = Convert.ToInt32("00100101", 2)

    ' 二進表現 出力
    Dim b = Convert.ToString(a, 2)

    ' 確認
    Console.WriteLine("{0} {1}", a, b)
End Sub

 結果

37 100101
続行するには何かキーを押してください . . .

[VB.NET] リテラル型文字

2014年1月4日

 VB.NET には「リテラル型文字」というものがあって、リテラルに変数型を指定することができます。
 http://msdn.microsoft.com/ja-jp/library/s9cz43ek.aspx

 多くの場合、暗黙変換とかで事なきを得ますが、気にしておかないといけないのは 16進数との比較とか。たとえば以下の If 文は False なので、Then ブロックは実行されません。
 16進数表示指定で 8000 を表示し、その数値を &H8000 で比較しているのにも関わらずです。

Sub Main()
    Dim a As Short = -32768

    Console.WriteLine("{0:x}", a)

    If a = &H8000 Then
        ' 通らない
    Else
        Console.WriteLine("False")
    End If
End Sub

 結果

8000
False
続行するには何かキーを押してください . . .

 Then ブロックを実行するためには、If 文を以下のように書く必要があります。

    If a = &H8000S Then

 つまり &H8000 だと Int32(=Integer) 型で認識され、Int32 型の (8000)16 は 32768 と評価されるため、If の結果が False となります。
 &H8000S だと Int16(=Short) 型として認識され、Int16 型の (8000)16 は -32768 と評価されるため、If の結果が True になります。

 Select Case を使用すると「表現できない」と警告が出る場合がありますが、If の場合は警告が出ないので要注意です。

 また、あまり使うことはないかもしれませんが、SByte にはリテラル型文字がないので、負の領域と比較する場合は SByte を Short にキャストしてから比較するなどの手段を検討する必要があります。

Dim a As SByte = -128
If CShort(a) = &HFF80S Then

 いや、その頭の FF が気に入らない、とかだと Byte 型に変換した後に比較するとか。(Byte 型にも リテラル型文字はないので、以下は Int32 に暗黙変換されて比較されます)

Dim a As SByte = -128
If BitConverter.GetBytes(a)(0) = &H80 Then

 VB のリテラル型文字一覧

------- 数値型
S       Short       例) a = 10S
I       Integer
L       Long
D       Decimal
F       Single
R       Double
US      UShort
UI      UInteger
UL      ULong

------- 文字型
c       Char        例) a = "A"c

[SVG] 手書きSVG実験

2014年1月3日

 手書きで SVG を書いてみる実験。以下を参考にしました。
  SVG 1.1 (第2版)8 パス
  SVG 1.1 (第2版)9 基本図形

 コード

<svg width="100px"
     height="100px">

  <rect x="0"
        y="0"
        width="100"
        height="100"
        fill="none"
        stroke="Green"
        stroke-width="2" />

  <circle cx="50"
          cy="50"
          r="45"
          fill="yellow"
          stroke="blue"
          stroke-width="2"  />

  <path d="M 50,10 L 84,70 L 16,70 z"
        fill="red"
        stroke="Black"
        stroke-width=3" />

</svg>

 結果

  

    

    

    

  

 手描きは座標を頭で把握しつつ図形を描画…ということになるので、さすがに現実的な作業ではないですが、ちょっとした図形であれば描けそうです。昔の BASIC の LINE 文や CIRCLE 文のノリに近いですね。

 Path のメタ言語は xaml の Path.Data に設定するメタ言語と同じ(*1)であるようなので、Blend for Visual Studio で描いて、そのデータを持ち込んでもよいかもしれません。(*2)

 (*1) すべて同じかどうかは調べていません。
 (*2) ほかに良いエディタはないものか…

 あと、Wordpress に持ち込む場合、改行位置に <p> タグや <br> タグが自動付与されて svg として評価されなくなってしまいます。これを回避するために、この記事では svg のデータを <pre> タグで囲んでいます。(もっと良い方法はないのか…?)

[環境設定] ワイヤレス ネットワーク プロファイルの管理

2014年1月3日

 Windows XP とかだと GUI で参照できていたワイレスネットワークのプロファイル情報が、Windows 8 では参照できなくなっています。(多分)
 過去接続したワイヤレスネットワークのプロファイルの参照/削除などがしたい場合、netsh コマンドで実施する必要があるようです。

  • PC 上のすべてのワイヤレス プロファイルを表示する

     netsh wlan show profiles

  • セキュリティ キーを表示する

     netsh wlan show profile name="ProfileName" key=clear

  • プロファイルを削除する

     netsh wlan delete profile name="ProfileName"

 詳細
  http://windows.microsoft.com/ja-jp/windows-8/manage-wireless-network-profiles

[VB.NET] 期待通りに丸められない場合がある

2014年1月2日

 浮動小数点型を使用した丸め処理を行う場合、意図しない結果になる場合があります。
 たとえば以下。5.015 を小数点以下2桁としたいので、期待する結果は 5.02 ですが、結果は 5.01 になります。

Dim r As Double

r = 5.015R
r = Math.Round(r, 2)
Console.WriteLine("{0}", r)

 結果(期待値は 5.02)

5.01
続行するには何かキーを押してください . . .

 似たようなコードはいくらでも作れて、以下のようなコードでも期待する結果と異なります。

Dim r As Double

r = 1.001
r = r * 1000
r = Math.Floor(r)
r = r / 1000
Console.WriteLine("{0:0.000}", r)

 結果(期待値は 1.001)

1.000
続行するには何かキーを押してください . . .

 要するに、浮動小数点型は 10 進数的には近似表現になっている場合があり、それを意識せずに処理すると意図しない結果になりますよ、ということなのですが、Math.Round のような標準関数でも起こりうるという点には注意が必要です。

 そもそも浮動小数点型の値に、この手の厳密さが必要か?という話もありますが、万一ここを厳密に…という場合は、一度 decimal に代入してやると解決します。

Dim r As Double
Dim d As Decimal

r = 5.015R
d = r
r = Math.Round(d, 2)
Console.WriteLine("{0}", r)

r = 1.001
d = r
d = d * 1000
d = Math.Floor(d)
r = d / 1000
Console.WriteLine("{0:0.000}", r)

 結果

5.02
1.001
続行するには何かキーを押してください . . .

 速度的には ? なところがありますが、とりあえず 10進数的な精度を出したい、という場合はこれで対処できます。(多分 -_-;)

 しかし、浮動小数点型を使っている時点で、上記でいえば 5.015 や 1.001 を得る以前の段階でいろいろな誤差を生じている可能性があり、ここだけをお手当てしても全体的な精度という意味では上がりません。命令単体ではなく、演算全体で精度をどうするか、演算全体で、浮動小数点型が有利なのか、decimal が有利なのか、混在させたほうがよいのかを判断する必要があります。


 今回参考にしたサイト) http://d.hatena.ne.jp/hnw/20131229

[VB.NET] 切り捨て/切り上げと床関数/天井関数,四捨五入と偶数丸め

2014年1月1日

 プログラム中で切り上げ/切り捨てをしたい場合があります。

 切り捨てをする場合は Math.Floor、切り上げをする場合は Math.Ceiling、という関数を使用すると良い、という話を聞くわけですが、これらの関数、丸めの桁の指定ができないんですね。四捨五入などをする Math.Round では第二引数で桁を指定することができるのに…

 なぜかというと、この Ceiling とか Floor。切り捨てや切り上げのための関数ではなくて、床関数と天井関数というれっきとした数学関数らしいです。曰く「任意の実数に対し整数を対応付ける関数」だそうで。"対応する整数"を求めるわけだから、丸めの桁の指定などあるわけが…という話のようです(汗
 http://ja.wikipedia.org/wiki/床関数と天井関数

 しかし実務では、床関数とかは手段でしかなく「指定桁数で丸めたい」という要求のほうが大きいです。
 結局、結論としては、有効桁小数部2位なら、従来通り以下の手続きを行う必要があります。

  1. まず100倍する。
  2. これらの関数を利用して小数部を切り落とし。
  3. 再び1/100に。

 あれだけ膨大なクラスライブラリがあるんだからこの程度やってくれても…と思うわけですが、いまだに標準ライブラリではサポートされてはいない模様です…(汗

 ・・・

 ところで一口に"丸め"といっても、代表的なもので以下の3種6パターンがあります。

  1. 切り捨て
    1. 負の無限大への丸め
      … Math.Floor
    2. 0 への丸め
      … Math.Truncate
  2. 切り上げ
    1. 正の無限大への丸め
      … Math.Ceiling
    2. 無限大への丸め
      … 対応なし
  3. 最接近丸め
    1. 四捨五入
      … Math.Round(raw, digit, MidpointRounding.AwayFromZero)
    2. 偶数丸め(JIS 丸め)
      …Math.Round(raw, digit)

 切り上げの a と b、切り捨ての a と b は、正の数の領域では結果が同じです。ですが、負の数の領域で結果が異なります。
 a 側は、元の数を切り捨ての場合は上回らない、切り上げの場合は下回らないように丸めます。たとえば -3.5 という数値があった場合、切り捨てでは -4 が、切り上げでは -3 が得られます。
 b 側は、絶対値で切り捨て/切り上げをします。-3.5 という数値の場合は、切り捨てでは -3 が、切り上げでは -4 が得られます。直感的にはこちらのほうが理解しやすいですが、"切り上げた値<切り下げた値" と大小関係が逆になる点には注意です。あと 0 付近の区間の幅が他の区間の幅と異なる点にも注意です。C 言語の int でキャストした場合などで得られるのはこちらの切り捨てです。(なので int をキャストして 0 となる場合、元の値は -1 < x < 1 の区間で他の数値の場合の 2 倍の幅があります)

 最接近丸めはいわゆる四捨五入です。ですが VB や C# などでは単純な四捨五入ではなく、偶数丸めと呼ばれるものがデフォルトになっています。(CInt とか Convert.ToInt32 とか。ただし文字列に変換する場合(String.Format)とかは、普通に四捨五入です)
 偶数丸めとは四捨六入で、五の場合は結果が偶数になるように切り捨てたり切り上げたりする方法です。
 このため、普通の四捨五入をする場合には、MidpointRounding.AwayFromZero オプションの指定を必要とする点に注意です。

 あと、手入力などの値を指定桁数に制限したい場合は、正規表現(たとえば "^[+|-]?[0-9]+(.[0-9]{1,2})?$" みたいな)などで入力文字列が正しいかを確認し、丸め処理でごまかさないように注意します。

 以下コード的なまとめ。

''' <summary>
''' 丸め
''' </summary>
''' <remarks>
''' 参考)
'''  http://ja.wikipedia.org/wiki/%E7%AB%AF%E6%95%B0%E5%87%A6%E7%90%86#.E4.B8.B8.E3.82.81.E3.81.AE.E7.A8.AE.E9.A1.9E
'''  http://rurema.clear-code.com/2.1.0/method/BigDecimal/s/mode.html
''' </remarks>
Public Class Rounding
    
    ''' <summary>
    ''' 切り上げ(指定値を下回らない最小値 / RM: Rounding toward Minus infinity)
    ''' </summary>
    Public Shared Function Ceiling(value As Double, digits As Integer)
        Dim shift = 10 ^ digits
        Return Math.Ceiling(value * shift) / shift
    End Function

    ''' <summary>
    ''' 切り捨て(指定値を上回らない最大値 / RP: Rounding toward Plus infinity)
    ''' </summary>
    Public Shared Function Floor(value As Double, digits As Integer)
        Dim shift = 10 ^ digits
        Return Math.Floor(value * shift) / shift
    End Function

    ''' <summary>
    ''' 切り捨て(0 に近い側 / RZ: Rounding toward Zero)
    ''' </summary>
    Public Shared Function Down(value As Double, digits As Integer)
        Dim shift = 10 ^ digits
        Return Math.Truncate(value * shift) / shift
    End Function

    ''' <summary>
    ''' 切り上げ(0 に遠い側 / RI: Rounding toward Infinity)
    ''' </summary>
    Public Shared Function Up(value As Double, digits As Integer)
        Dim shift = 10 ^ digits
        Dim v = value * shift
        Return If(v < 0, Math.Floor(v), Math.Ceiling(v)) / shift
    End Function

    ''' <summary>
    ''' 四捨五入(R: Round)
    ''' </summary>
    Public Shared Function HalfUp(value As Double, digits As Integer)
        Return Math.Round(value, digits, MidpointRounding.AwayFromZero)
    End Function

    ''' <summary>
    ''' 偶数丸め(RN: Round to the Nearest even)
    ''' </summary>
    Public Shared Function HalfEven(value As Double, digits As Integer)
        Return Math.Round(value, digits)
    End Function

End Class

 参考) http://ja.wikipedia.org/wiki/端数処理

[環境設定] Win8.1 のセットアップで、Microsoft アカウントを使用せずにローカルアカウントを作成する

2014年1月1日

 Windows8.1 のセットアップで最初のアカウントを作成するとき、Microsoft アカウントでサインインするよう促されますが、それを回避して従来通りローカルアカウントを作成したい場合は以下のようにします。

  1. 「Microsoftアカウントへのサインイン」画面
    → "新しいアカウントを作る" を選択

  2. 「Microsoftアカウントの作成」画面
    → "Microsoftアカウントを使わずにサインインする" を選択

  3. 「お使いのアカウント」画面
    →あとは今まで通り、ローカルアカウントを作成します。

 Microsoft アカウントを作る気はないけど、まずは「新しいアカウントを作る」を選択するところがポイントでしょうか…(汗

[環境設定] NTFS ボリュームを縮小する

2013年12月31日

 ボリュームを縮小したい場合。GUI からの操作がよくわからない場合はコマンドラインから操作するとよいかもしれません。
 以下は投入コマンド例。(931GB 割当中を 700GB 縮小して 231GB の割当にする例)

Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.

C:UsersAdministrator>diskpart

Microsoft DiskPart バージョン 6.3.9600

Copyright (C) 1999-2013 Microsoft Corporation.
コンピューター: ****************

DISKPART> list vol

  Volume ###  Ltr Label        Fs    Type        Size     Status     Info
  ----------  --- -----------  ----  ----------  -------  ---------  --------
  Volume 0     D                UDF    DVD-ROM     4135 MB  正常

  Volume 1         システムで予約済み    NTFS   Partition    350 MB  正常
  システム
  Volume 2     C                NTFS   Partition    931 GB  正常         ブート


DISKPART> select vol c:

ボリューム 2 が選択されました。

DISKPART> shrink DESIRED=716800

ボリュームは、次の方法で正常に縮小されました:  700 GB

DISKPART> list vol

  Volume ###  Ltr Label        Fs    Type        Size     Status     Info
  ----------  --- -----------  ----  ----------  -------  ---------  --------
  Volume 0     D                UDF    DVD-ROM     4135 MB  正常

  Volume 1         システムで予約済み    NTFS   Partition    350 MB  正常
  システム
  Volume 2     C                NTFS   Partition    231 GB  正常         ブート


DISKPART> exit

DiskPart を終了しています...

C:UsersAdministrator>

[VB.NET] 丸めの方法の違い

2013年12月31日

 VB の CInt で実数を整数にする場合、四捨五入されるのではなく、偶数丸めと呼ばれる方法で丸められます。また、Convert.ToInt32 を使用しても同様の動作をします。
 String.Format などでフォーマットされた場合の値と若干異なるので、要注意です。

 コード

Module Module1
    Sub Main()
        For i = 0.5 To 8.5
            Console.WriteLine("{0:0.0}  {1:0}  {2}", i, i, CInt(i))
        Next
    End Sub
End Module

 結果 (1列目:元の値 / 2列目:String.Format / 3列目:CInt )

0.5  1  0
1.5  2  2
2.5  3  2
3.5  4  4
4.5  5  4
5.5  6  6
6.5  7  6
7.5  8  8
8.5  9  8
続行するには何かキーを押してください . . .

[VB.NET] 子クラスから親クラスに定義したイベントを発行する

2013年12月31日

 親クラスに定義したイベントを、直接子クラスから呼び出すことはできません。(子クラスから親クラスのイベントを直接 RaiseEvent することはできません。)
 そのようなことをしたい場合は、親クラスに Protected な中継ぎメソッドを用意しておいて、子クラスからはそれを呼び出します。

' 親クラス
Public Class Class1
    Public Event Event1(msg As String)

    Protected Sub OnEvent1(msg As String)
        RaiseEvent Event1(msg)
    End Sub
End Class

' 子クラス
Public Class Class2
    Inherits Class1

End Class

' 孫クラス
Public Class Class3
    Inherits Class2

    Sub test()
        OnEvent1("Hello!")
    End Sub
End Class

 呼び出し側は以下な感じ。
 呼び出し側は、Class1 は意識せず、Class3 のイベントとして受けることができます。

Module Module1
    Private WithEvents _class3 As New Class3

    Sub Main()
        _class3.test()
    End Sub

    Private Sub _class3_Event1(msg As String) Handles _class3.Event1
        Console.WriteLine(msg)
    End Sub
End Module

[VB.NET][WPF] タスクバーのアイコンが消えたり消えなかったり

2013年12月27日

 Window.Show の後に Window.Owner を設定すると、タスクバーアイコンがなぜか表示されません。
 Window.Owner を設定したのちに Window.Show を実行すると、タスクバーアイコンはちゃんと表示されます。

 たとえば、以下のような WPF アプリケーションを作成します。

 ■ Main Window (アプリケーション起動時に表示される Window)

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow">

    <Window.TaskbarItemInfo>
        <TaskbarItemInfo ProgressState="Normal" ProgressValue="0.5" />
    </Window.TaskbarItemInfo>

    <StackPanel>
        <Button Content="消えない" Click="Button_Click1" Height="100" />
        <Button Content="消える!" Click="Button_Click2" Height="100" />
    </StackPanel>
</Window>
Class MainWindow 
    Private Sub Button_Click1(sender As Object, e As RoutedEventArgs)
        ' 消えない
        Dim w = New Window1
        w.Owner = Me    ' A
        w.Show()        ' B
        Me.Hide()       ' C
    End Sub

    Private Sub Button_Click2(sender As Object, e As RoutedEventArgs)
        ' 消える
        Dim w = New Window1
        w.Show()        ' B
        w.Owner = Me    ' A
        Me.Hide()       ' C
    End Sub
End Class

 ■ Window1 (MainWindow から呼び出す Window)

<Window x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1">

    <Window.TaskbarItemInfo>
        <TaskbarItemInfo ProgressState="Error" ProgressValue="0.8" />
    </Window.TaskbarItemInfo>

</Window>

 このアプリケーションを起動します。
 すると、タスクバーに MainWindow のアイコンが表示されます。(プログレスを50%、緑色にしています)

 次に、「消えない」と表示されている方のボタンを押します。
 Window1 が表示され、タスクバーに Window1 のアイコンが表示されます。(プログレスを80%、赤色にしています)

 一度アプリケーションを終了させ、今度は「消える!」と表示されている方のボタンを押します。
 すると、Window1 が表示されますが、タスクバーに Window1 のアイコンが表示されません。

 このように、Window.Show と Window.Owner の設定順序で、タスクバーのアイコンが表示されたり、非表示となってしまったりします。

 しかし、以下の手順を踏むと消えたタスクバーアイコンが再表示されます。

  1. 別のウインドウを選択して、このウィンドウのフォーカスを一旦外す
  2. 再度このウインドウを選択(タスクバーアイコンは表示されていないので、ウインドウを直接選択)して、フォーカスを取り戻す。

 「なぜこのような仕様なのか」と考えましたが、もしかすると単純にバグ、あるいはしてはいけない手続きなのかもしれません。
 とりあえず、Window.Owner を設定してから Window.Show を実行するようにしたいと思います。

[VB.NET] Application.Exit の使用は期待を裏切る

2013年12月19日

 「End の使用は信頼を裏切る」(http://ooltcloud.sakura.ne.jp/blog/201312/article_11223915.html)の続きエントリーです。

 アプリケーションをコードから終了させたいとき、End(=Environment.Exit) を使わないとすると、どうすればいいのか?という問いに対して、WinForm の場合だと、Application.Exit を使えばよいといわれることがあります。
 ということなので、Application.Exit の動きを確認してみます。

 まず、こんなコードを用意してみます。

Public Class Form1

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        Dim ret1 = MsgBox("終了しますか?", MsgBoxStyle.OkCancel)

        If ret1 = MsgBoxResult.Ok Then
            Debug.Print("終了しました")
            Application.Exit()
            ' これ以下は実行しないはず?
        End If


        Dim ret2 = MsgBox("処理Aを実行します。よろしいですか?", MsgBoxStyle.OkCancel)

        If ret2 = MsgBoxResult.Ok Then
            Debug.Print("処理Aを実行")
            Dim f = New Form2
            f.ShowDialog()
        Else
            Debug.Print("処理Aを中止")
        End If

    End Sub

End Class

 途中に出てくる form2 ではこんな処理を書いておきます。

Public Class Form2

    Private Sub Form2_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        Debug.Print("1 Form2_Load")
    End Sub

    Private Sub Form2_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
        Debug.Print("2 Form2_FormClosing")
    End Sub

    Private Sub Form2_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
        Debug.Print("3 Form2_FormClosed")
    End Sub

End Class

 最初の「終了しますか?」の MsgBox で「OK」を選択します。
 すると Application.Exit が実行されるので、そこで終了…となるかというとそうではありません。実行結果は以下のようになります。

終了しました
処理Aを実行
1 Form2_Load

 一行目の「終了しました」は期待通りの動作です。そして、2つ目の MsgBox は表示されません。これも期待通りです。
 しかし実行結果の二行目には「処理Aを実行」が出力されています・・・

 まとめると、Application.Exit を実行した場合、以下のことが起こります。

  • 今表示中のウインドウはすべて閉じる (閉じるボタンが押されるのと等価)
  • これから表示しようとするウインドウは表示しない。

 これで最終的にはすべてのウインドウが閉じられるので、アプリケーションも終了・・・となるわけですが、その途中の処理は一切キャンセルされません。この点には注意が必要です。

 そのほかにも以下の点に注意が必要です。

  • 表示しなかった MsgBox の戻り値は MsgBoxResult.Ok が戻ります。Cancel ではなく Ok が戻る点に注意が必要です。
  • 表示しようとした Form2 は、Load イベントは処理されますが、Close系 のイベントは発生しません。(実行結果の3行目)
  • FormClosing イベントの e.Cancel を True で戻すと Application の終了を抑止できてしまいます。(例示のコードにはこの処理は書いていません)

 つまり、Application.Exit は都合のいいところで中断できる便利な命令ではありません。Application.Exit を実行するとその場で中断してもらえると思っていると、期待に反する動きをしてしまうことがあります。End と同様、使いどころには十分な注意を払う必要がある命令です。

[VB.NET][C#] 変数名ローンダリング実験

2013年12月18日

 参照型は同じ実体に対して、異なる変数名を持つことが可能になるので、油断すると容易く一筋縄では追えないコードを生成することができてしまいます。ということの実験。

 以下は、Class5.Init() の a.A で設定した値を、Main() の E.B で参照しているコード例。

Class Class1
    Public A As Integer()
End Class

Interface Interface2
    Property B As Integer()
End Interface

Class Class3
    Public Shared C As New Class1
End Class

Class Class4
    Implements Interface2

    Private D As Class1 = Class3.C

    Public Property B As Integer() Implements Interface2.B
        Get
            Return D.A
        End Get
        Set(value As Integer())
            D.A = value
        End Set
    End Property
End Class

Class class5
    Sub Init()
        Dim a = Class3.C
        a.A = {1, 2, 3}
    End Sub
End Class

Module Module1
    Private E As Interface2

    Sub Main()
        E = New Class4
        Dim f = New class5

        f.Init()

        Console.WriteLine("{0}", E.B(0))
    End Sub
End Module

 実行結果

1
続行するには何かキーを押してください . . .

 ついでに C# 版。結果は同じ。

class Class1
{
    public int[] A;
}

interface Interface2
{
    int[] B { get; set; }
}

class Class3
{
   public static Class1 C = new Class1();
}

class Class4:Interface2
{
    private Class1 D = Class3.C;

    public int[] B
    {
        get
        {
            return D.A;
        }
        set
        {
            D.A = value;
        }
    }
}

class Class5
{
    public void Init() {
        var a = Class3.C;
        a.A = new int[] { 1, 2, 3 };
    }
}


class Program
{
    private static Interface2 E;

    static void Main(string[] args)
    {
        E = new Class4();
        var f = new Class5();

        f.Init();

        Console.WriteLine("{0}", E.B[0]);
    }
}

[XAML] ListView で ダブルクリックでイベント処理したいとき

2013年12月17日

 ListView とかの子要素があるコントロールで、子要素(メニューの一項目とか)をダブルクリックで操作したい場合があります。
 これを実現するとき、単純に ListView のイベント(ListView.MouseDoubleClick) に処理を記述すると、子要素のない領域や、スクロールバーへのダブルクリックでも反応してしまい、面倒です。
 これに対処するには、子要素(ListViewItem) のイベント (ListViewItem.MouseDoubleClick) に処理を書くようにします。
 ただ、直接は指定できないので、Style 設定を経由して指定します。 (直接書ける方法があるかもしれないけど知らない--;)

<ListView Name="A"
          ItemsSource="{Binding Items}"
          Margin="0,0,0,275">
    <ListView.Resources>
        <Style TargetType="ListViewItem">
            <EventSetter Event="MouseDoubleClick"
                         Handler="ListViewItem_MouseDoubleClick" />
        </Style>
    </ListView.Resources>

    <ListView.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding}" />
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

 コードビハインドの例はこんな感じ

Class MainWindow 
    Public Property Items As New ObjectModel.ObservableCollection(Of String)

    Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
        Me.DataContext = Me

        Me.Items.Add("あああ")
        Me.Items.Add("いいい")
        Me.Items.Add("ううう")
        Me.Items.Add("えええ")
    End Sub

    Private Sub ListViewItem_MouseDoubleClick(sender As Object, e As MouseButtonEventArgs)
        Dim item = CType(sender, ListViewItem)
        MsgBox(item.Content)

    End Sub
End Class
 
Powered by Wordpress and MySQL. Theme by Shlomi Noach, openark.org