ソフトウェアエンジニアの人たちって、筋トレとかジョギングとかロードバイクとかやってますよね。なんか体を鍛えるとエンジニア力も上がるみたいな定説ありますよね。
やる気出すだめにスポーツウェアを買いました。
スポーツウェアがやる気スイッチになって、今の所三日坊主は回避できました。
ジョギングの効果が出るのは3ヶ月後らしいので、3ヶ月後にまた何か書きます。
書かなかった場合は察してください。
ソフトウェアエンジニアの人たちって、筋トレとかジョギングとかロードバイクとかやってますよね。なんか体を鍛えるとエンジニア力も上がるみたいな定説ありますよね。
やる気出すだめにスポーツウェアを買いました。
スポーツウェアがやる気スイッチになって、今の所三日坊主は回避できました。
ジョギングの効果が出るのは3ヶ月後らしいので、3ヶ月後にまた何か書きます。
書かなかった場合は察してください。
以下のようなコードがあったわけです。
#include <stdio.h> char data[16] = "abcdefg"; int main(void){ // Your code here! char* pData = NULL; pData = data; printf("%s\n", pData); // abcdefg }
以下のように関数にしたかったわけです。
#include <stdio.h> char data[16] = "abcdefg"; void func(char* pData){ pData = data; } int main(void){ // Your code here! char* pData = NULL; func(pData); printf("%s\n", pData); // Runtime error(Exit status:139(Invalid memory reference)) }
エラーが出るわけです。
それぞれのアドレスを表示してみます。
#include <stdio.h> char data[16] = "abcdefg"; void func(char *pData){ pData = data; printf("func-in: %d\n", pData); // func-in: 6295616 } int main(void){ // Your code here! char* pData = NULL; func(pData); printf("func-out: %d\n", pData); // func-out: 0 pData = data; printf("%d\n", pData); // 6295616 }
これなんでだろ~ってずっと悩んでいたんですが、小一時間悩んで、このfunc()に渡しているpDataはいわゆる「ポインタの値渡し」というやつでは?と思い至る。
その場合、ポインタのポインタを渡せば解決できるかもと思い修正。func2()を作成します。
#include <stdio.h> char data[16] = "abcdefg"; void func(char *pData){ pData = data; printf("func-in: %d\n", pData); // func-in: 6295616 } void func2(char **pData){ *pData = data; printf("func2-in: %d\n", *pData); // func2-in: 6295616 } int main(void){ // Your code here! char* pData = NULL; func(pData); printf("func-out: %d\n", pData); // func-out: 0 func2(&pData); printf("func2-out: %d\n", pData); // func2-out: 6295616 printf("%s\n", pData); // abcdefg pData = data; printf("%d\n", pData); // 6295616 }
オッ、意図したとおりに動作しました。
「C言語に参照なし」とは言いますが、こうして見ると、「&」が参照渡しの記号に見えてしょうがない。
ブログ名を「ベスパライフ」から「ベスパリブ」に変更しました。
ベスパはまだ持ってません。バイクは250ccです。
WinAPIの話です。
仮想キーVK_DELETEをSendMessageしたら、なぜか"." (ドット) が出力されてしまう問題に悩んでいました。
原因は単純で、SendMessage(hWnd, WM_IME_CHAR, VK_DELETE, 0)としていたせいでした。
WM_IME_CHARとは何か。
http://www.geocities.jp/katayama_hirofumi_mz/imehackerz/ja/WM_IME_CHAR.html
によると、第三引数のwParamはUnicode文字の値と書いてあります。
VK_DELETEは0x2eです。
WikipediaでUnicodeを調べます。
https://ja.wikipedia.org/wiki/Unicode%E4%B8%80%E8%A6%A7_0000-0FFF
なるほど。ドットです。
WM_IME_CHARの仕様は、
IMEが変換結果の文字を取得するときに、アプリに送られます。ウィンドウは、そのウィンドウプロシージャーを通じてこのメッセージを受け取ります。
らしいですが、ちょっとよくわかりません。
原文のMSDNも読みましたが、やはりちょっとよくわかりません。
よくわかっていないなりの理解なのですが、Unicodeを送信したいときはWM_IME_CHARを使えということでしょう。実際、そういう動作をします。
Call SendMessage(hWnd, WM_IME_CHAR, &H21, 0) ' ! Call SendMessage(hWnd, WM_IME_CHAR, &H2E, 0) ' . Call SendMessage(hWnd, WM_IME_CHAR, &H7E, 0) ' ~
やりたいことは「DELETEキーを押したい」と同じです。
ここにきて、"仮想キー"の考えが腑に落ちた気がします。仮想キーは文字通り「仮想的なキーボード」なので、「キーを押して」、「キーを離す」というメッセージを送信するということになります。
Call SendMessage(hWnd, WM_IME_KEYDOWN, VK_DELETE , 0) Call SendMessage(hWnd, WM_IME_KEYUP, VK_DELETE, 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("a"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("b"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("c"), 0) Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_BACK, 0) Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_BACK, 0)
とすると、"b"と"c"が消えました( VK_BACK はBackSpace)。
WM_IME_KEYDOWN と WM_IME_KEYUPでワンセットかと思いきや、 WM_IME_KEYDOWN と WM_IME_KEYUPで二回VK_BACKを押したことになっているようです(私の環境だけ?)。
ちなみに、「押しっぱなし」がどうなるのかも確かめてみました。
Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("a"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("b"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("c"), 0) Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_BACK, 0)
"c"が消えただけで、"a"と"b"は残りました。どうやら「押しっぱなし」のようなことにはならないようです。
さらに、VK_LEFT(左キー)の挙動を調べました。
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("a"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("b"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("c"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("d"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("e"), 0) Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_LEFT, 0) Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_LEFT, 0) Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_LEFT, 0) Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_LEFT, 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("X"), 0) ' "abcXde"
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("a"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("b"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("c"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("d"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("e"), 0) Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_LEFT, 0) Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_LEFT, 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("X"), 0) ' "abcXde"
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("a"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("b"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("c"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("d"), 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("e"), 0) Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_LEFT, 0) Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_LEFT, 0) Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("X"), 0) ' "abcdeX"
この辺、挙動を確認しながらプログラム組まないと、よくわからないバグになりそうです。環境による気もしてきました……。 どこかに良い書籍や情報はないでしょうか。
キーボードでできることは仮想キーにやらせよう(オライリー・ジャパン風)
逆に、 「ʨ」とかの入力はUnicodeでしかできないと思います(入力できるのか知らないけど。私の環境だと?に変換された)
WinAPIの話です。
Editコントロールのハンドルを取得できても、Editコントロールに入力可能というわけではないようです。
このせいで、ハンドル取得した直後に文字列をSendMessageしても、テキストボックスに文字が入っていない場合があります。
しょうがないので、Editコントロールが入力可能になるまで待つ関数を作ります。
方法としては、
1. Editコントロールに適当な文字をSendMessageし、文字数を取得する。
2. 文字数が1文字以上なら、入力可能なので、文字を消して終了
3. 文字数が0ならば、入力できていないので、1.に戻る
です。単純ですね。
Public Declare Function SendMessage4GetText Lib "user32" Alias "SendMessageW" (ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal iParam As System.Text.StringBuilder) As Integer Public Const VK_BACK As Integer = &H8 ' BackSpace Public Sub Wait4Edit(ByVal hWnd As IntPtr) Dim buf As New System.Text.StringBuilder() While 1 buf.Length = 2048 System.Windows.Forms.Application.DoEvents() Call SendMessage(hWnd, WM_IME_CHAR, Asc("a"), 0) ' 適当な文字を送信する If SendMessage4GetText (hWnd, WM_GETTEXT, buf.Length, buf) >= 1 Then ' 入力できていたら、文字数が1文字以上になる Exit While End If System.Threading.Thread.Sleep(100) End While ' 適当な文字を送っているので、コントロールに入力されている文字を全部消す While 1 buf.Length = 2048 System.Windows.Forms.Application.DoEvents() Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_BACK, 0) Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_BACK, 0) If SendMessage4GetText (hWnd, WM_GETTEXT, buf.Length, buf) = 0 Then Return End If System.Threading.Thread.Sleep(100) End While End Sub
引数hWndにはEditコントロールのハンドルを与えます。
与えるハンドル間違えたりすると無限ループから返ってこないことに注意。
なぜWhile文を2つに分けているかというと、最初一つにまとめていたのですが、なぜか入力ボックスに文字が残っていたりした("a"が2回送られる? or VK_BACKが反応しない?)ので、「文字を消す」処理を確実に行わせたかったため二つに分けています。この辺の仕様をまだよくわかっていません。でもまあ、動くしいいか(適当)
IsWindowVisible()とIsWindowEnabled()という便利な関数を見つけました。これを使えば良さそうです。
Public Declare Function IsWindowEnabled Lib "user32" Alias "IsWindowEnabled" (ByVal hWnd As IntPtr) As Boolean Public Declare Function IsWindowVisible Lib "user32" Alias "IsWindowVisible" (ByVal hWnd As IntPtr) As Boolean Public Sub Wait4Edit(ByVal hWnd As IntPtr) ' エディットボックスが入力可能になるまで待つ While True If (IsWindowEnabled(hWnd) = True) And (IsWindowVisible(hWnd) = True) Then Exit While End If System.Threading.Thread.Sleep(10) End While End Sub
うるさい!!!
WinAPIの話です。
FindWIndowでウィンドウハンドルを取得する際、PCが重かったりするとウィンドウの表示が遅れたりして取得に失敗したりするので、FindWindow()前にSleepするという方法がありますが、これもどれだけSleepすればよいかという問題を抱えており、PCが激重だった場合、Sleepして待っても結局ウィンドウハンドルが取得できないという問題も抱えています。
このような問題はFindWIndowに「タイムアウトするまで探し続ける機能」があればそれで解決する話なので、そのようなラッパー関数を作成しました。できれば標準で欲しかった。
Declare Unicode Function FindWindow Lib "user32.dll" Alias "FindWindowW" (ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr Public Function FindWindow2(ByVal cnm As String, ByVal cap As String, Optional ByVal timeout As Integer = 10000) As IntPtr ' ウィンドウが見つからない場合、timeoutミリ秒経過するまで無限ループで取得し続ける ' タイムアウトしたら0を返す ' 関数名はもっと考えよう Dim hWnd As IntPtr = 0 Dim sw As New System.Diagnostics.Stopwatch() sw.Start() While 1 System.Windows.Forms.Application.DoEvents() hWnd = FindWindow(cnm, cap) If hWnd <> 0 Then Return hWnd End If If sw.ElapsedMilliseconds >= timeout Then ' タイムアウトした Return 0 End If 'System.Threading.Thread.Sleep(500) End While Return hWnd End Function
CPU使用率が気になる場合は、ループ内で適当な時間Sleepさせれば良いと思います。
2月の終わりですが、2018年の目標を決めました。
1. 英語
2. 競プロ
3. 機械学習系のプロダクトを1つ作成
英語を一番頑張りたいと思います。
他2つはジャストアイデアですが、興味がある分野なのでやっていきたいと思います(やらなそう)。
・英検1級
・TOEIC900台
を目標とします。
たしか3年前くらいのTOEICが600台だったような気がします。
現状はReadingはまあまあできます。
Listening、Writing、Speakingがボロボロです。
なので結構強気の目標です。
なんで英検なのかっていうのは、WritingとSpeakingがあるからです(Writingは申し訳程度ですが)。
TOEFLにしろよと突っ込まれそうですが、TOEFLには怖いイメージしかないので英検にしました。
最近競技プログラミングをはじめました。
全然問題解けないけど面白いので、趣味としてやろうと思います。
目標スコアは……よくわかりません。とりあえず一年やってみた結果どの程度になるのか見ようと思います。