Powershellスクリプト at Windows 10

テーマ:
以前作ってWindows7では動いていたWPFのスクリプトが、Windows10では動かなかったので調査開始。

エラーメッセージをぱっと見るとWindowの表示方法が変更されたのかな?と思ったけど、
.Netフレームワークのはずなので、こんな極端に互換性殺さないよね?って思い直した。

よくよくエラーを見るとどうやらテキストボックスのプロパティ、IsReadOnlyの設定であることを確認。とりあえず『s/IsReadOnly=.*"//』こんな感じで全滅させたら動いた。

個人用のプログラムなのでまぁこの程度はどうでも構わないということで、これ以上の調査はしないつもりだけど、もともと間違いをスルーされてたのかってことくらいは調べようかな。

Windows PowerShell実践システム管理ガイド 第2版/日経BP社

¥価格不明
Amazon.co.jp

VB.Netでenumports

テーマ:
Webで探すのにすごく苦労したので成果を残す。

苦労したのはDeclareの書き方とVBで構造体のポインタはどう扱うのか。
C++で書いたときは簡単だったんだけどねぇ。

ちょっといじってるけど、おそらくまるごとコピーで動くはず。
なにか足りなかったら適当に増やしてね。

Imports System.Runtime.InteropServices

Structure PORT_INFO_2
Dim pPortName As String
Dim pMonitorName As String
Dim pDescription As String
Dim fPortType As Integer
Dim Reserved As Integer
End Structure

Declare Function EnumPorts Lib "winspool.drv" Alias "EnumPortsA" (ByVal pName As String, ByVal Level As Integer, ByVal lpbPorts As Integer, ByVal cbBuf As Integer, ByRef pcbNeeded As Integer, ByRef pcReturned As Integer) As Integer

Declare Function HeapAlloc Lib "kernel32" (ByVal hHeap As Long, ByVal dwFlags As Long, ByVal dwBytes As Long) As Long
Declare Function GetProcessHeap Lib "kernel32" () As Long
Declare Function HeapFree Lib "kernel32" (ByVal hHeap As Long, ByVal dwFlags As Long, ByVal lpMem As Object) As Long
Declare Sub CopyMem Lib "kernel32" Alias "RtlMoveMemory" (ByVal pTo As Object, ByVal uFrom As Object, ByVal lSize As Long)

Dim bufSize As Integer
Dim numPorts As Integer
Dim tmp As IntPtr = IntPtr.Zero '暫定の入れ物

PrinterUtil.EnumPorts(vbNullString, 2, CInt(tmp), 0, bufSize, numPorts)
tmp = Marshal.AllocHGlobal(bufSize - 1)

If PrinterUtil.EnumPorts(vbNullString, 2, CInt(tmp), bufSize, bufSize, numPorts) = 0 Then
' Error処理
End If

Dim portInfo(numPorts) As PORT_INFO_2
Dim curPtr As IntPtr = tmp

For i = 1 To numPorts
portInfo(i) = CType(Marshal.PtrToStructure(curPtr, GetType(PORT_INFO_2)), PORT_INFO_2)
curPtr = IntPtr.op_Explicit(curPtr.ToInt32 + Marshal.SizeOf(GetType(PORT_INFO_2)))
Next
Marshal.FreeHGlobal(tmp)

これであとはportInfoの配列でいじり放題。

ポインタ扱うの面倒だね。


VB.NET基礎学習Bible―270例題で学ぶプログラミングの散歩道/技術評論社

¥3,110
Amazon.co.jp

スクリプトで得られるcsvに日付が入っていて、ソコから曜日を求めたかったんだけど、マクロ作ると色々面倒になるから、csvに関数直書きしてどうにかならないかな~っていうのが動機でちょっと作ってみた。

先ずは曜日を求める関数。これは「WEEKDAY(日付,種別)」。
なお、日付はシリアル値。種別は曜日と得られる数字の対応を変更するもの。詳細はここ

csvにsedで「…,"=weekday(シリアル値,3)",…」ってな感じで突っ込むと数字が出るので関数自体は入れられることが発覚。あとはシリアル値の部分を日付があるセルを参照するようにするだけ。

でもセルを直に指定すると行数をカウントしないといけなくなるので、単に左のセルを参照するようにしたい。
セルの行と列はそれぞれ「ROW()」と「COLUMN()」で求められるので、1つ左はCOLUMNを-1するとOK。
この値をセルのアドレスとして認識させるには、「ADDRESS(行,列,参照方法,シート名)」。
ようするに「ADDRESS(ROW(),COLUMN()-1,1)」。シート名は不要だし参照方法も不要かもしれない。

これを組み合わせて「"=weekday(ADDRESS(ROW(),COLUMN()-1,1),3)"」ってやってもまだダメ。
だってADDRESSはアドレスをくれるだけ、WEEKDAYが欲しいのはシリアル値。
よってADDRESSの結果得られるセルの値を取ってこないとダメ。
これは「INDIRECT(セル,形式)」で可能。形式はTRUE=A1形式、FALSE=R1C1方式。
これも組み合わせて「WEEKDAY(INDIRECT(ADDRESS(ROW(),COLUMN()-1,1),TRUE),3)」ってやると、月曜=0~日曜=6の数値として出てくる。

さらに数値だとわかりづらいのでこの数値を元に文字を生成。
これは「MID(文字列,始点,長さ)」でできる。
※書式設定でaaaとかにするとMID不要だけど、CSV読んでから一手間が面倒…

最終形は「…,"=MID(""MonTueWedThuFriSatSun"",WEEKDAY(INDIRECT(ADDRESS(ROW(),COLUMN()-1,1),TRUE),3)*3+1,3)",…」

もっと簡単にできるかもしれないけど、素直に考えたらこうなった。
ダブルクォート連続の意味はこちら
全体をダブルクォートで括るのはこちら

これでもかなり面倒だったね(;´∀`)



ーーー追記ーーー
text使えばmidとweekdayいらないじゃん。
=TEXT(INDIRECT(ADDRESS(ROW(),COLUMN()-1,1),TRUE),"ddd")
これだけでよかったorz



Excel VBA 逆引き辞典パーフェクト 2010/2007/2003対応/翔泳社

¥2,786
Amazon.co.jp

kshでltsv

テーマ:
AIXでやってみた。

まず連想配列が使えないと面倒なのでどうやるのか検索。
どうやら/usr/bin/ksh93なら使えるらしい。

ってわけでログを吐く方はこんな感じ


#!/usr/bin/ksh93

. ./ログ吐き本体

typeset -A hash
hash[time]=`date +"%H:%M:%S"`
hash[date]=`date +"%Y/%m/%d"`
hash[question]="nanu?"
hash[shock]="ga---n"
hash[birds]="ptarmigan"

writeltsv hash >> ログファイル名

hash[time]=`date +"%H:%M:%S"`
hash[date]=`date +"%Y/%m/%d"`
hash[question]="nanzo?"
hash[shock]="oh my godness"
hash[birds]="blakiston's fish owl"

writeltsv hash >> ログファイル名


あとはログ吐き本体にwriteltsvを作って出力させるだけ。

でもこれが意外に難儀した。hashを関数で受け取る方法がわからない。
一体にしてると簡単にできるけど、再利用が面倒になる。
いろいろ調べたところ参照で受ければあとは普通に使えると発覚。

完成品がこんな感じ。


#!/usr/bin/ksh93

function writeltsv {
typeset -n data="$1"
typeset -i top=0
for key in ${!data[*]} ; do
if((${top} != 0));then
print "\t\c"
fi
echo "${key}:${data[${key}]}\c"
top=1
done
echo
}

これで行末の余計なtabも消えて出てくる。

これの問題点は使う機会がなさそうというところorz
なんかのスクリプトを作る機会に混ぜてみるかな。


入門 Kornシェル (UNIX programming)/オライリー・ジャパン

¥3,456
Amazon.co.jp

Perlで少しハマった

テーマ:
ファイルの読み込みでハマった。
foreachとwhileの違いを初めて知った。

ソース


#!/usr/bin/perl

open($fp,"<","./hoge.txt");
$i=1;
$len;

foreach(<$fp>){
$len=length($_);
last if($i++==3);
print;
}
print"-Rewind-\n";
seek $fp,-$len,1;
foreach(<$fp>){
print;
}
close($fp);

print "======================\n";

open($fp,"<","hoge.txt");
$i=1;

while(<$fp>){
$len=length($_);
last if($i++==3);
print;
}
print"-Rewind-\n";
seek $fp,-$len,1;
while(<$fp>){
print;
}
close($fp);



読み込むファイルはこれ


1:Baird
2:TY
3:North Island
4:Shigakogen
5:Minami-Shinshu
6:Oraho
7:Umenishiki
8:Fujizakura
9:Yo-ho



ソースは前半と後半でほぼ同じ。foreachとwhileの違いだけ。
これを実行すると


1:Baird
2:TY
-Rewind-
zakura
9:Yo-ho
======================
1:Baird
2:TY
-Rewind-
3:North Island
4:Shigakogen
5:Minami-Shinshu
6:Oraho
7:Umenishiki
8:Fujizakura
9:Yo-ho


こうなった。

理由
foreachは全部読んでからループの中身を実行する。いつループを抜けても最後まで読んだことになってる。
whileは1行読む度にループの中身を実行する。よってループを途中で抜けると、ちゃんとと途中で止まっている。
この差が結果にでる。

3行目を読んで文字数数えてループを抜けるまでは同じ。
seekの時点で、foreachは現在地がファイル末尾。whileは3行目末尾。
だからforeachは3行目の文字数分を末尾から戻るから8行目の途中から続きを読んでいる。
whileは3行目の末尾まで読んだ状態で3行目の文字数分戻るから、3行目を再読み込みして継続している。

こんなこと知らなかった。新版Perl言語プログラミングレッスン入門編/ソフトバンククリエイティブ

¥2,916
Amazon.co.jp

初めてのPerl 第6版/オライリージャパン

¥3,888
Amazon.co.jp