プログラミングの最近のブログ記事

GetProcAddressの罠

| コメント(0) | トラックバック(0)
GetProcAddressはGetProcAddress自身のアドレスを正しく取得できないことがあるようです。どうもUACで高ILにしたデバッガー下だと違うアドレスが返っています。

どうしてCreateRemoteThreadがロジックボムになるのかと思ったら、なんという罠でしょうか。

環境はVista x86、Visual Studio 2008 SP1。

関係あるのかないのか、ntdll.dllのUndocumentedなLdrGetProcedureAddressだと正しく取得できるという情報があり、試してみるとその通りでした。

単独で実行させる限り大丈夫なら、UndocumentedなAPIに手を出すこともありませんが、常時正しく動いてほしいのが本音です。

C文字列ではなくANSI_STRING構造体でAPI名を渡すので、RtlInitAnsiString/RtlFreeAnsiStringも使う必要があります。モジュールハンドルはLdrLoadDLLではなくLoadLibraryのもので大丈夫のようです。

ダミーウィンドウを作らせるだけですから、GetModuleHandleでuser32.dllの存在を確認した上で、DllMainから作ってしまうのが楽ではあります。

しかし「のどか」にて、これは共用メモリの話ですが、Adobe Readerという全くのGUIアプリでエラーが発生したことを考えると、ロードされていてもDllMainからuser32.dllのAPIを呼ぶのは危険かもしれません。
OpenProcessしてEnumProcessModulesしてDLLのハンドルを取得し、CreateRemoteThread経由でFreeLibraryに渡すという手がありました。

が、次にウィンドウが生成された時に、同じDLLがWH_CALLWNDPROCRETフックに指定されていると(つまりフックを使用するそのアプリがもう一度起動されていると)、アクセス違反で落ちました。

呼び出し履歴によるとCreateWindowから最終的にDispatchHookWで落ちており、名前からすると何事もなくフックを呼ぼうとしているのでしょう。DLLは開放できてもフックの状態は解除できなかったと推測されます。

となると何とかしてウィンドウプロシージャを呼ばせる、つまりCreateRemoteThreadからCreateWindowするしかありません。どんどんイケナイ道に踏み出している気がします。

LoadLibraryさせたDLLで、隠しダミーウィンドウを一瞬生成(WM_CREATEでDestroyWindow)すると開放できました。最初は論理試作としてDllMainでやったりしましたが、もちろん危険なのでエクスポート関数に実装しなければなりません。

コード注入だけで行なうのは無理そうです。CREATESTRUCTのlpCreateParams経由で受け取ったアドレスを、保存しておく場所がないためです。静的な変数が一切使えず、それがなくてはDefWindowProcすら呼べないのでは、ウィンドウプロシージャの実装は無理でしょう。

ウィンドウを生成するためにはRegisterClassする必要がありますが、クラス登録は相手プロセスの中ですから、同名クラスが既にあれば重複エラーとなります。時刻等を組み合わせてリトライする方向になるでしょうか。

メッセージフックとDLL開放

| コメント(0) | トラックバック(0)
DeltaEndでも使っているメッセージフック(WH_CALLWNDPROCRET、ウィンドウプロシージャ戻りフック)はDLLをプロセスに注入します。

開放されるタイミングは、UnhookWindowsHookExした後にウィンドウプロシージャが呼ばれた時だという話なのですが、開放されないプロセスがあるので調べてみました。

ehmsas.exeとjusched.exeの2つで、Spy++で見ると共にウィンドウがありません。前者はWindows Media Centerのモジュールで、ehtray.exeからCOMとして呼ばれています。後者はJavaのアップデート。

ログオン時タスクからでのみ発生し、起動後はタスクの手動起動でもDeltaEnd.exeの直接起動でも再現しません。

ログオン時タスクの起動はとても早いので、ひょっとして最初はウィンドウがあるんじゃなかろうか(少なくともjusched.exeはCreateWindowExAを使っている)、というわけで検証用のコンソールアプリを作ってみました。

WindowWithConsoleApp.zip
"w"入力でウィンドウを作り、"q"で終了します。ウィンドウを閉じるまでキー入力が利きません。

ProcessExplorerで監視してみると、最初は何をしても変化ありませんが、ウィンドウを作ると同時にDeltaEndDll.dllが入り、閉じてもそのままと見事に再現できました。

ウィンドウがなくなっても開放されるわけではなく、その場合はあくまでプロセス終了時の開放処理に任されているようです。ウィンドウ破棄後も動作するアプリは想定外なのでしょう。

DllMainでプロセス名を判別してFALSEを返し失敗させる、という方法は考えられます。しかし汎用的な方法はちょっと思いつきません。

他、Firefoxのplugin-container.exeでもたまに起きています。こちらもウィンドウがない場合ですが、このプロセスを除外するとIETabやFlash内に干渉できなくなるので、一度Firefoxを閉じて対応するしかななさそうです。

眺望は桜の散る頃に

| コメント(0) | トラックバック(0)
Windows Vistaの個人向け製品は4月にサポートが終了します。BusinessとEnterpriseはもう5年ありますが。

業務分担の都合でUltimateが必須のため、今の仕事が一段落したらWindows 7にしなければなりません。

64ビット版を検討しているところですが、開発環境はどうしたものかと思ったので、VMWare Player上で試してみました。

といっても、Visual Studioではなくマイコンの話です。V850はCubeSuite+が既に対応済みですが、H8SXのHEWは実際のところどうなのか。コンパイラのライセンスが古いのがネックです。

結論から言うと使えるようでした。E10A-USB他をWin7対応版にして、H8系コンパイラだけ6.02 Release 02の状態で、動作を確認できました。

念のためインストールはProgram Files下ではなくC:\Renesas\HEWディレクトリにしましたが、UACもAeroも有効なVistaで動いている組み合わせですから、デバッガさえ接続できれば、そう変わることもないのでしょう。

しかしMacBook MB062J/Bを非力に感じ始めているので、こちらも更新できたら嬉しいのですが。
antidevice.png
まずAntiDeviceは、パスの先頭一致で排除と許可が指定できるようになりました。特定メーカー(VID_****)などに限定することができます。
また64ビット版のファイル名をAntiDevice64.exeとし、64ビット版WindowsでAntiDevice.exeが起動された場合にAntiDevice64.exeを自動で起動するようにしました。

deltaend04.png次にDeltaEndは、ATOK 2011対応と、カナキーの指定ができるようになったことなどです。

いずれもホームページ側のWin32コーナーにて。

Shell_NotifyIcon

| コメント(0) | トラックバック(0)
通知領域に入れるようなアプリケーションは常駐型が多いでしょう。

となるとスタートアップか、Vista以降だと管理者権限のログオン時タスクで起動をすることが多いと思います。

Windowsの起動中は色々面倒くさいのですが、Shell_NotifyIconも例外ではありません。

登録に失敗する場合、GetLastError()がERROR_TIMEOUTを返すものだと思っていましたが、タスクからの起動だとタイミングがもっと早いため、ERROR_FILE_NOT_FOUNDが返っていました。

タスクバーの通知領域(実体はツールウィンドウ)が見つからない場合に起こるそうな。ただしその後でTaskbarCreatedのメッセージが来るので、リトライせずともそちらでカバーできます。

何か原因不明のエラーで無限ループになっても困るので、リトライするならこの2つの場合だけにするか、回数制限をするのが良いのでしょう。

DeltaEnd Ver.0.45公開

| コメント(0) | トラックバック(0)
deltaend04.pngBoot CampのCtrl + 1アイコン再登録、それにATOK 2008のタブ同時確定をメインとしたDeltaEndの刷新版を公開しました。
DeltaEnd 0.45

最近試した色々が盛り込まれ、32ビット版と64ビット版が同期して動作するものになっています。

ATOKのタブ同時確定は今のところ2008専用ですが、新しいのを購入することがあったら更新するでしょう。

実はThinkPad X61tが壊れてMacBook Pro(15.4)を導入しました。手持ちの2008というのがMac+Windows版でして、Lionも出たので新しくしたいのですがいつになるやら。

32/64共同作業

| コメント(0) | トラックバック(0)
64ビット版Windowsにおける一部ユーティリティは、32/64両方のバイナリを動作させなければならない場合があります。

この場合、32ビット版を主体とし、64ビット依存の部分だけ子プロセスを呼んだり、一歩進んでアーキテクチャ依存部と非依存部に分離させ、依存部を子プロセスにしたりするでしょうか。

個人の趣味レベルであれば、同一ソースコードから2つのバイナリを作り、両方とも動作させても構わないでしょう。ただ設定ぐらいは連動させ、可能であればマスター/スレーブとしてそれぞれ振る舞わせ、スレーブ側はUIを表示しないようにしたいところ。

というわけで、平たく言えばATOK2008タブ同時確定Bootcampアイコン再登録をDeltaEndに組み込むため、ファイルマッピングを利用して協調動作させるクラスとそのサンプルアプリを作ってみました。

SyncRun.zip

起動するとプロセスが2つ立ち上がります。マスター側のエディットボックスに入れた数字をスレーブ側に伝えることができ、マスター終了でスレーブも終了しますが、スレーブは単独で終了・再立ち上げ可能です。32ビット版、64ビット版の混在可能、どちらもマスター・スレーブ双方になれます。

このサンプルではアーキテクチャを判別していないので、32ビット版同士/64ビット版同士での動作もできますが、DeltaEndに組み込んだ際にはIsWow64Process APIの戻り値を参照したフラグを入れています。

シングル/マスター/スレーブのモード判別ができ、それに応じてUIを切り替えることができます(もちろんアプリの実装として)。

スレーブが消え失せた場合の処理は少し入れてありますが、マスターが消え失せた場合のスレーブ終了処理はまだ入っていません。タイマーを使って1分毎ぐらいでマスターにWM_NULLをPostMessageするといったあたりでしょうか。

Boot CampとTaskbarCreated

| コメント(0) | トラックバック(0)
4.0は知りませんが、3.xまでのBoot Campはタスクバー再生成の際にアイコンが登録されません。

外部ツールがついでに登録できないかと32ビット版3.2と64ビット版3.3で調べたところ、NOTIFYICONDATAのuIDに0x86を設定することで、他は普通に可能でした。

メッセージを飛ばす相手のウィンドウは、クラス名"Afx:00400000:0"(3.2/32)又は"Afx:000000013FF20000:0"(3.3/64)ですが、2つあるので大きさで判別する必要があります。目的のブツは10x10のものです。

クラス名でなくプロセス名がBootcamp.exe(2.xまではKbdMgr.exe)であるのを確認しても良いでしょう。こちらが参考になります。Bootcamp.exeの在処は、決め打ちでなければプロセス名のフルパスから取るのが正しいですから。

なおアイコンのリソースIDは128、コールバックのメッセージはWM_USER+10です。

exeをLoadLibraryしてLoadIcon(LoadImage)して、Shell_NotifyIconしてFreeLibraryすれば完了です。TerminateProcessして再起動するより行儀の良い再登録が行なえます。

Paint.NETとTWAIN

| コメント(0) | トラックバック(0)
先月末、COMサーバーについて書きました。これが64ビットWindowsにおけるTWAINの話だったのですが、形になりました。

TWAINLocalServer.zip

Paint.NETでTWAINを使うものとして、効果プラグインのTwainableというのが既にありますが、64ビット環境では動作しませんでした。またクリップボードに入れるところまでしか行なわないらしいので、もう一手間かかりました。

こちらはファイルタイプのプラグインとして作ってあり、拡張子pdtwainを開くとTWAINから読み込みます。またアウトプロセスCOMサーバー経由なので、64ビット32ビット問わず動作します。スキャナの選択は通知領域から行ないます。

pdtwain.dllとTWAINLocalServerLib.dllをPaint.NETのFileTypesフォルダにコピーしてください。その上で、TWAINLocalServer.exeを適当な場所にコピーし、
TWAINLocalServer.exe -RegServer
として登録してください(管理者権限が必要)。登録解除は
TWAINLocalServer.exe -UnregServer
です(同じく)。

ベースとして、らんゆうじろう氏のCTWAINManagerを使っています。スキャンのウィンドウとスキャナ選択とを自動で切り替えるため、処理が完了してから行なえるようPostMessageをいくつか追加しました。

...というのが、そもそもTWAINが64ビット非対応というところから始まったものでした。公式TWAINは対応を果たしているのですが、Windows同梱の古い仕様のものは未対応なのです。

AnyCPUマネージドアプリであるPaint.NETから呼ばれるTwainableは、64ビット環境ではアンマネージドDLLとして64ビット版を要求するのですが、TWAINのDLL本体はWindowsフォルダの32ビット版しか存在せず、読もうとして落ちていたわけです。

そこでCOMサーバー。しかし肝心のデータの受け渡しをどうしたものかと試行錯誤をし、結局SAFEARRAYにまで手を付けました。ファイルマッピングでも良いのですが、不慣れなマネージド側の処理が楽になるので、マネージド配列として扱えるSAFEARRAYは好都合だったのです。

ちなみにタスクメモリは32/64の垣根を越えることができませんでした。ハンドルではなく直接アドレスを扱うAPIだからでしょうか。タスクメモリを使わなければデュアルインターフェース/オートメーションが使えるので、これはこれで悪い話ではありませんが。

まだエラー対策が不充分なのと、TWAINのウィンドウが背後に隠れる場合があるので、もう少し改良してからホームページ側で公開します。
<<前のページへ 12345678

アーカイブ

ウェブページ

Powered by Movable Type 5.2.13

ホームページ