2014年12月12日金曜日

Windows 7 の Android Emulator で HAXM が動かない

こんにちは。yu1rowです。

リリースされたばっかりのAndroid Studio 1.0を使ってみたんですが、その過程でWindows 7(x86)上のエミュレータでHAXM(Intel Hardware Accelerated Execution Manager)が動いてくれませんでした。
なんとか動かせるようになったので記録を残しておきます。

一般的な手順

Android SDK Managerで"Intel x86 Emulator Accelerator (HAXM installer)をインストールします。
この後、SDKのインストールされたフォルダにダウンロードされたインストーラを実行します。
{ANDROID_SDK_HOME}/extras/intel/Hardware_Accelerated_Execution_Manager/intelhaxm-android.exe

HAXMのバージョンが古い?

インストールが完了してエミュレータを実行してもウンともスンとも言わず、コマンドプロンプトから叩いてみると以下のようなエラーが発生していました。
emulator: ERROR: x86 emulation currently requires hardware acceleration!
Please ensure Intel HAXM is properly installed and usable.
CPU acceleration status: HAX kernel module is not installed!
困ったぞと思って調べていたところ、Intelのサイトから最新版をDLしてきてインストールすれば良いぞという情報が。
[HAXM最新版]
一旦元のHAXMをアンインストールして、ダウンロードしたほうをインストールします。
で、これでOK!とはならなかったです。
※うまくいったって報告例もありました

Failed to configure driver

Intelのサイトから最新版(調査時点で"haxm-windows_r05")をインストールしていたら、以下のようなメッセージが表示されました。
Failed to configure driver: unknown error. Failed to open driver
インストール完了後に"intelhaxm"というサービスが起動しているはずらしいんですが、以下のコマンドを叩いて確認してみます。
> sc query intelhaxm
しかし結果は"指定されたサービスはインストールされたサービスとして存在しません。"とのこと。
またまた困ったぞと思って調べていたところ、Intelの開発者フォーラムに[Workaround Patch for HAXM Installation Error "Failed to configure driver: unknown error. Failed to open driver"]などというドンピシャっぽいエントリを発見。
[パッチのページ]
ページ内に書いてある手順に従います。
  1. ページ内のリンクからzipを落としてくる
  2. 適当なフォルダに解凍する
  3. 解凍されたファイルの"hax_extract.cmd"を、HAXMインストーラのあるフォルダにコピーする
  4. コピーした"hax_extract.cmd"を右クリックして「管理者として実行」する
  5. 権限についてのダイアログがでたら「はい」を選んで実行
これでうまくいくそうです。実際うまくいきました。
コマンドプロンプトで"sc query intelhaxm"を叩いてみて"STATE:RUNNING"って出てたら多分成功。

まぁ実は上記手順で最初はうまくいきませんでした。
コマンドプロンプトを管理者として実行して再度"hax_extract.cmd"を叩いてみると、実行結果に「まずは再起動してくれ」って書いてました。
再起動後に上記手順を行うと、今度は無事成功しました。めでたし。

2014年9月10日水曜日

iOS 8 と Wi-Fi

こんにちは。yu1row です。

iPhone 6 が発表されて、iOS 8 も提供されます。
iOS 8 の情報が頻出する中で、「MAC アドレスがランダム生成される」ってキーワードが出てきて「えっ?」って思ったんで調べてみました。

どうやら Wi-Fi のスキャニングに行う「Probe request」及び「Probe response」に含まれる MAC アドレスがランダム化するようです。
街中の 野良 AP が勝手に個人情報を集めるみたいな動作を忌避しての対策なんでしょうか。

この変更による影響として、Wi-Fi の親機で MAC アドレスフィルタリングを行っているような場合、問題になるのではないかと思いました。
ただ、実際の接続要求に行う「Association Request」でランダムな MAC アドレスを使うとは書かれていませんでした。
親機が「Probe request」ではなく、「Association Request」でフィルタリングする仕様なら問題ないのかも?
問題が出ても、ファームアップデートされるかもですが。

Wi-Fi ネットワーク接続後の DHCP サーバへのブロードキャスト時には固定(universal)アドレスが用いられるので、MAC アドレス毎に IP 固定をしている環境は問題にはならないでしょう。

結論。
「iOS 8 にすると、Wi-Fi で MAC アドレス制限をしているネットワークに繋げられなくなる問題」が起こる(かも)。

2014年6月6日金曜日

OpenSSLの脆弱性が再度発見されたそうです

こんにちは。yu1row です。

少し前にOpenSSLの脆弱性が発見されちょっとした騒ぎになっていましたが、昨日もまた新たな脆弱性が発見されました。
ニュースによると、どうやら日本人が発見したとのことですが、それはどうでもよろしいと思っています。
それよりも引き起こされる問題と解決方法について知りたいのですが、不明な点が多いです。
以前の"HeartBleed"と、今回の"CCS Injection"、併せて記録しておきます。

  • CVE-2014-0160:HeartBleed:2014/04/07
    読み取りオーバーランにより、不適切なコードを実行&SSLの秘密鍵が取得できてしまうなどの諸問題が発生する。
    OpenSSLを使用している各種ディストリビューションではパッチが発行され、一応の事態収束に向かっている。
    個人的には DebianやFedora、RedHatなどのサーバにパッチを当てて問題がなくなることを確認した。
     
  • CVE-2014-0224:CCS Injection:2014/06/05
    脆弱性の存在するOpenSSLを使用したサーバ、脆弱性の存在するOpenSSLを使用したクライアント間で通信を行う際に、長さ0の情報をChangeCipherSpecメッセージに不適切なタイミングで紛れ込ませることによりセッションを奪い取ったり、機密情報の入手が可能になる。とのこと。
    サーバ、クライアントのどちらかが対策されれば問題は発生しなくなる。
    ただしパッチが発行されたかどうかは現時点(2014/6/6)で定かではない。
    またハートブリードに比べてどの程度深刻なのかは不明。
    ※拾い集めた情報を要約したので説明に不備がある可能性があります
    追加/訂正情報をお持ちの方は是非教えてください。

※上記脆弱性情報データベースの各種スコア値は下記IPAの情報を参照してください
http://www.ipa.go.jp/security/vuln/CVSS.html

2014年5月30日金曜日

Jenkins で VSS プラグイン を使う

こんにちは。yu1row です。

ソースコード管理に VSS を使用しているプロジェクトを Jenkins で CI しようとすると、「Visual SourceSafe Plugin」ってのがありまして、これを使用することにしたんです。
紆余曲折ありまして、なんとか使用できるようになったんで備忘録として書き留めておきます。
そうそう、当該の「VSS を使用しているプロジェクト」の VSS のバージョン、6.0d なんです。
2005 でもないんです。こんなプロジェクト、他の会社とかでもまだまだあるんですかね?

前提条件

  • Jenkins サーバマシン (Windows) には VSS のインストールが必要
  • たぶん Linux では動かない(※com4j ってので COM に動的バインドしてるみたい)
  • VSS のデータベース初期化ファイル (srcsafe.ini) は Windows 共有フォルダにアクセスしますので、このファイルにアクセス権がないと Jenkins のビルド時にソースを落としてくることができません
    • Jenkins のプロセスを Windows のサービスとして起動している場合は、起動するユーザを共有フォルダにアクセスできるものに変更しておきます
      (たぶん初期状態では起動するユーザは「Local System」になっていると思います)
    • Jenkins をサービスとして起動していない場合は、プロセス起動時のユーザが共有フォルダにアクセスできるようにしておきます

      ※ユーザID/パスワードが共有フォルダへのアクセスに使用するものとまったく同じものを作っておくか、「Windows資格情報コンテナー」をいじってアクセスするID/パスワードを適切なものにしておきます


VSSへのチェックインをしていないのに「変更あり」とされ続けてしまう問題

「SCM をポーリング」にチェックを入れて設定していたら、ソースを変更していないのにポーリングの時間毎に毎回以下のような「変更あり」の状態になってしまい、ビルドがいっぱい走っていました。
Started on 201X/XX/XX 10:00:00
Polling SCM changes on master
[poll] No previous build, so forcing an initial build.
Done. Took 0 ms
Changes found

なんでやろー?って思ってたら、VSS プラグインのバグのようです。
issues に挙がってました。


しかし VSS プラグインはしばらく更新されていませんし、自分でパッチ当ててビルドするしかなさそうです。

自前でパッチ当て&ビルドする


プラグインを開発するための手順ページとして以下を参考にしました。
[Jenkins プラグインを開発する] http://qiita.com/kazuqqfp/items/ded99eb8d7bd967b9d2a

  1. 上記ページの1の手順、maven をインストールします
    ※maven のインストールは以下の URL でアーカイブをダウンロードして「Windows 2000/XP」の所よく読んでインストールして下さい。
    http://maven.apache.org/download.cgi
  2. 2~4の手順の代わりに、上記の VSS プラグインのソースを落としてきて、さらにパッチを当てます
    ※1行だけ、if 文の「==」を「!=」に変更するだけなんで、手で修正してもいいですけどね
  3. 5の手順どおり、ソースのルートフォルダで「mvn install」します
  4. hpi ファイルが完成したら Jenkins に手動でインストールします
何度も何度もビルドされちゃう問題はこれで回避できました。
これで幸せになれる人がいれば嬉しいです。

2014年5月29日木曜日

Visual Studio、MSBuild での Clean で特定フォルダを削除する

こんにちは。yu1row です。

タイトルの通りです。備忘録として残します。
ビルドの際 Clean で特定のフォルダを削除する方法です。

*.csproj や *.vbproj を直接開いて、<Target> タグに <RemoveDir> を追加して、削除するフォルダを記述しておきます。

以下記述例
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
  <Target Name="Clean" Condition=" '$(Configuration)' == 'Release' ">
    <RemoveDir Directories="$(TargetDir)" />
  </Target>
...
</Project>

  • 上記の例では Release の時だけ出力先フォルダが削除されるが、"Condition" を書かなければ Release のときも Debug の時も削除される
  • Directories の削除対象フォルダは";"で区切れば複数指定可能。
  • .vshost.exe や .pdb が邪魔して削除できない場合があるが、その場合は以下を参照
    .vshost.exeファイルと.pdbファイルが生成されないようにするには?[VS 2008、VS 2005]
    http://www.atmarkit.co.jp/fdotnet/dotnettips/831stoppdbfile/stoppdbfile.html
以上であります。

2014年5月28日水曜日

GetWindowText のデッドロック

こんにちは。yu1rowです。

GetWindowText(SendMessage で WM_GETTEXT を使用した場合も同じ) でハマったので、その情報を備忘録として残します。
※回避コードだけ見るなら、一番下までスクロール。

マルチスレッドで EnumWindows で列挙した全 Window の文字列を GetWindowText やSendMessage を使って調べる処理を DLL に記述。
そしてこの DLL の関数を Excel VBA から呼び出すというケースで、スレッドがブロックしてしまっていつまでも終了しないことがある。

SendMessage を MSDN で確認すると、以下のように記述されていた。

複数のスレッド間で送信されたメッセージが処理されるのは、受信側スレッドがメッセージ取得コードを実行したときだけです。送信側スレッドは、受信側スレッドがメッセージの処理を終えるまで、ブロックされます(待機状態になります)。

しかし、日本語版に無い情報を英語版の MSDN で発見。
To prevent this, use SendMessageTimeout with SMTO_BLOCK set.For more information on nonqueued messages, see Nonqueued Messages.
MSDN に限ったことではないけれど、API リファレンスの英語版には日本語訳では見つけられない重要な文章がサラッと書かれている場合が見受けられる。英語版もたまには読んでみたほうがいい。

全ウィンドウの文字列を GetWindowText(SendMessage) で取得する処理を別スレッドで起動・終了待ちをする処理をExcel VBA で行うときに問題が発生した。
その別スレッドから Excel のウインドウ自体にも SendMessage することになるんだけど、そうするとずっと返ってこない。ブロックしちゃうってことらしい。

SendMessage でブロックされていてスレッドが終了しない場合、SendMessage の代わりにSendMessageTimeout を使用して、適切なタイムアウト時間を指定すれば良いとのこと。
以下コード例。
SendMessageTimeout(hWnd, WM_GETTEXT, 1024, (LPARAM)text, SMTO_BLOCK, 100, &dwRet);
  • 上記 100 となっている値は適切なタイムアウト時間を設定すること
  • SMTO_NORMAL、SMTO_BLOCK|、MTO_ABORTIFHUNG を必要に応じて組み合わせて使用すると良いと思う(オプションの詳細は MSDN 見てね)

ただし上記を使用すると、スレッド終了後の待機(WaitForSingleObject とか)でなんかちょっとだけ待機時間が発生して遅くなる。
上記の例であれば、「100msec×スレッド数」ぐらい?

これが気になる場合、「呼出元プロセスのウインドウに対して SendMessage するから止まる」 というデッドロックが元々の原因であるとすれば、上記の SendMessageTimeout を使用する前に、GetWindowThreadProcessId と GetCurrentProcessId を使用して呼出元プロセスのウインドウを回避するチェックを組み合わせるともっと良いかもしれない。
こうすると、ぅぉっなんか遅いってならない。ピュッピュッって終了してくれる。

以下上記を実装した例。呼出元プロセスのウィンドウは回避、ブロックしても 100ms で終わるようにしている。
DWORD pID;
GetWindowThreadProcessId(hWnd, &pID);
if (GetCurrentProcessId() == pID)
{
    return 0;
}

DWORD dwRet;
if (SendMessageTimeout(hWnd, WM_GETTEXT, 1024, (LPARAM)text, SMTO_BLOCK, 100, &dwRet) == 0)
{
    return 0;
}

ハマった。こんときゃ辛かった。

2014年5月27日火曜日

C# の配列変換

こんにちは。yu1row です。

配列変換の方法を備忘録として書いておきます。
int[] arr みたいな配列があったとして、これから string[] を得る場合、どしたらいいんでしょうか?

当然、これはダメです。
int[] arr = new int[] { 1, 2, 3 };
string[] dest = (string[])arr; // Can't compile

以下のように単純にループで回せば、まぁいけますよね。
int[] arr = new int[] { 1, 2, 3 };
string[] dest = new string[arr.Length];
for (int i = 0; i < arr.Length; i++)
{
    dest[i] = arr[i].ToString();
} 

Array.CovertAll() を使えばなんかすっきりした感じになる気がします。
int[] arr = new int[] { 1, 2, 3 };
string[] dest = Array.ConvertAll(arr, delegate(int i) { return i.ToString(); });

ラムダ式が使える(C# 3.0以降)のであれば、もっとすっきりした感じになる気がします。
int[] arr = new int[] { 1, 2, 3 };
string[] dest = Array.ConvertAll(arr, i => i.ToString());

これだけです。 もっとカッコイイくて短くてジェネリックで、特殊条件にも対応できるコードにできる方法があれば、誰か教えてください。

2014年5月26日月曜日

なんてステキなString.Join()

こんにちは。yu1rowです。

区切り文字を使った文字列連結に便利な String.Join() のご紹介です。
C# に限らず、区切り文字を使った文字列の連結はちょっと面倒に感じることがあります。
たとえば、以下の例のような{"A","B","C"}のような配列を"A - B - C"みたいに繋げたいとき、どうしますか?

入力配列出力値
{"A","B","C"}"A - B - C"
{"A"}"A"
{}""

.NET では、String に String.Join(String, String[]) というメソッドがありまして、これで上記を実現してくれます。

コード例
string[] arr = new string[]{ "A", "B", "C" };
Console.WriteLine(String.Join(" - ", arr)); // Output: "A - B - C"

便利。使わない理由はないと思う。

ただ、これ入力が String の配列なんで、int 型とか、他の型の配列はいちいち変換しなきゃいけない。
ただし、.NET Framework 4 以降は String.Join(String, Object[]) なんてオーバーロードが追加されまして、これが便利なんですわ。

一応コード例
int[] arr = new int[] { 1, 2, 3 };

// For C# 2.0
Console.WriteLine(String.Join(" - ", Array.ConvertAll(arr, delegate(int n) { return n.ToString(); })));

// For C# 3.0 or later
Console.WriteLine(String.Join(" - ", Array.ConvertAll(arr, n => n.ToString())));

// For .NET Framework 4.0 or later
Console.WriteLine(String.Join(" - ", arr));


使わんテはありまへんな! 今日のネタはこれだけ。それでは!

2014年4月30日水曜日

IME と Windows8

こんにちは。yu1rowです。

Windows 8 や Windows 8.1 が登場してしばらくが経ちましたが、アプリケーションによってはIMEの制御に問題が出ることがあり、調査を行いましたのでメモを残します。

デスクトップなアプリケーションでは、テキストボックスにカーソルが移動した場合、日本語入力を自動的に切り替えたりするなんてことをする場合があります。
Windows 8 より以前では、他のウィンドウに切り替えて入力方法を切り替えても、元のウィンドウには何の影響もありませんでした。
Windows 8 以降では、インストールした直後の設定では、日本語入力モードはユーザ(セッション)毎に共通で、一つのウィンドウでの変更が他のウィンドウに影響してしまうという現象が起こる可能性があります。
※ちなみに Windows Server 2012 でも Windows 8 と同じことが起こります。

Windows 7 までの動作と同じにしたい場合、コントロールパネルから切り替えることが出来ます。
  • (jp) - [言語] - [詳細設定] - [アプリ ウィンドウごとに異なる入力方式を設定する]
  • (en) - [language] - [advanced settings] - [Let me set a different input method for each app window]

この操作をわざわざ顧客にさせるようなベンダーはあまり居ないと思いますけどね。

アプリケーションの開発者が現在の設定を確認するには SystemParametersInfo という API を使用します。
IDE 立ち上げるのも面倒だったので Excel VBA の標準モジュールで関数を書いてみました。
Option Explicit
#If VBA7 Then
Declare PtrSafe Function SystemParametersInfo Lib "user32" Alias "SystemParametersInfoA" (ByVal uAction As Long, ByVal uparam As Long, ByRef lpvParam As Any, ByVal fuWinIni As Long) As Long
#Else
Declare Function SystemParametersInfo Lib "user32" Alias "SystemParametersInfoA" (ByVal uAction As Long, ByVal uparam As Long, ByRef lpvParam As Any, ByVal fuWinIni As Long) As Long
#End If
Const SPI_GETTHREADLOCALINPUTSETTINGS As Long = &H104E

' ------------------------------------------------------------
' [Description]
'  IMEの入力モードがセッション毎/スレッド毎どちらで
'  保持されるかの設定状況を返します。
' ------------------------------------------------------------
' [Returns]
'  0 : セッション毎(Windows 8以降のみ)
'  1 : スレッド毎
' ------------------------------------------------------------
' [Author]
'  yu1row
' ------------------------------------------------------------
Public Function GetThreadLocalInputSettings() As Integer
    Dim ret As Long
    Dim data As Long
    ret = SystemParametersInfo(SPI_GETTHREADLOCALINPUTSETTINGS, 0, data, 0)
    If ret <> 0 Then
        GetThreadLocalInputSettings = IIf(data = 0, 0, 1)
    Else
        GetThreadLocalInputSettings = 1
    End If
End Function

SPI_GETTHREADLOCALINPUTSETTINGS(0x104E) は Windows 8 から追加されています。また、SPI_SETTHREADLOCALINPUTSETTINGS(0x104F) も追加されており、これでアプリケーションから設定の変更も可能です。

さて、「コントロールパネルの設定を変えずに、他のウィンドウに影響させずに入力モードを切り替えたい」というワガママソリューションは存在するのでしょうか?
未検証ですが、以下の手順でうまくいくかもしれません。保証できませんけど。

  1. Windows 8 で、かつ入力モードがアプリ毎保持ではない場合、アプリ毎保持に変更する(SPI_GETTHREADLOCALINPUTSETTINGS / SPI_SETTHREADLOCALINPUTSETTINGS)
  2. IME の制御を行う (ImmSetConversionStatus とか)
  3. 1 でアプリ毎保持に変更を行っていた場合、設定を元に戻す(SPI_SETTHREADLOCALINPUTSETTINGS)
どうなんでしょうかね。やってみた人、結果教えてくれると嬉しいですw

それでは。