実装どうしよう会
tmyt opened this issue · 53 comments
実装方針で悩んだのでリポジトリ作りました
ご招待されました
お悩み一覧
- 64bitプロセスから32bitプロセスをHookできない
- 両方作った
- 32bitプロセスにSendMessageをすると頑張っても64bitまでしか送れない
- GUIDが128bitなので困ったのでよそのプロセスでメモリ確保するヘルパ関数作った
- 64bitプロセスから32bitプロセス上にメモリは確保できるみたい
- アプリでどうやってこれら呼ぶのがいいでしょう
現時点で作った流れが、
- グローバル キーフック検知
- 入力されたキー + Modifiers が設定されているアクションとマッチするかチェック
- マッチしたら、GetForegroundWindow() でアクティブなウィンドウのハンドルをゲッツ
- MoveWindowToDesktop で 3 のハンドルをぶっこんで終了
フックの時点から作り直しぽよ…
よくわかってないんだけど、アプリから VDMHelper (32bit & 64bit) を DllImport するの?
どうなんだろう.NET のWH_KEYBOARD_LL なら大丈夫なのかもしれない
で考えたのがこれ
- アプリがロードするのは環境に最適なDLLと仮定
- C++/CLIでC#向けにAPIを提供する
- 64bitからでも32bitプロセスにメモリ確保はできるのでここはOK
- 64bit環境では32bitプロセス用ヘルパプロセスを起動する
- ぐらばくさんがC#でこんなコードを実行
var is64bit = Marshal.SizeOf(typeof(IntPtr));
var asm = Assembly.Load(is64bit ? "VDMHelperCLR64.dll" : "VDMHelperCLR32.dll");
var type = asm.GetType("VDM.VdmHelper");
var obj = (IVdmHelper)Activator.CreateInstance(type);
if(is64bit){
// 64bit環境なら32bitプロセス用ヘルパプロセスを起動
// obj.Init() 相当を面倒みてくれるプロセス
}
obj.Init(); /// <- ここでSetWindowsHookEx してる
---
obj.MoveWindowToDesktop(hwnd, guid); // こんな感じで呼ぶと内部でSendMessageに置換する
どうでしょう
DLLImportするとstatic link されちゃうのでAnyCPUにしてるとはまる
ほーなるほど いい感じ
その場合、キーフックで検知した入力キーをどこから取るのかなと
たぶんだけど、キーフックについては.NETで実装した場合64bit/32bitいい感じにしてくれているのではないだろうか、と予想。ちょっとためしてみるね
結論:C#で書いたWH_KEYBOARD_LLはBit数の境界越えれる。強い。
アプリ側で32bit用ヘルパプロセス起動しなくて良くなったからこんな感じでよくなった。
IVdmHelper
は AnyCPUで作ってあるC#のライブラリ、VDMHelperCLR.Common に入ってます。
var is64bit = Marshal.SizeOf(typeof(IntPtr));
var asm = Assembly.Load(is64bit ? "VDMHelperCLR64.dll" : "VDMHelperCLR32.dll");
var type = asm.GetType("VDMHelperCLR.VdmHelper");
var obj = (IVdmHelper)Activator.CreateInstance(type);
obj.Init(); // <- ここでSetWindowsHookEx してる
---
obj.MoveWindowToDesktop(hwnd, guid); // こんな感じで呼ぶと内部でSendMessageに置換する
---
obj.Dispose(); // IVdmHelperがIDisposableなので適当に
DeInit と Dispose って使い分けるのん
Disposeだけでいいよん
おっけー
ちょっと組み込んでみるから明日にでもレビューしてちょ
おっけー
is 何
var is64Bit = Marshal.SizeOf(typeof(IntPtr)) == 8;
var asm = Assembly.Load(is64Bit ? @"VDMHelperCLR64.dll" : @"VDMHelperCLR32.dll");
var type = asm.GetType("VDMHelperCLR.VdmHelper");
this.helper = (IVdmHelper)Activator.CreateInstance(type);
this.helper.Init();
の asm.GetType で吐かれるかんじ
Load じゃなくて LoadFile だった…
VDMHelper のビルドでなにかミスってるのかな…
おやぁ、なんだこれ、帰ったらためしてみる
実行ファイルと同じ所にDLL一式32/64全部つっこんだらちゃんと動いてるっぽい
VDMHelper32.dll
VDMHelper64.dll
VDMHelperCLR.Common.dll
VDMHelperCLR32.dll
VDMHelperCLR64.dll
でたりてる?
たりてる、このコードで動いたのは確認しました
var dir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\";
var is64Bit = Marshal.SizeOf(typeof(IntPtr)) == 8;
var asm = Assembly.LoadFile(dir + (is64Bit ? @"VDMHelperCLR64.dll" : @"VDMHelperCLR32.dll"));
var type = asm.GetType("VDMHelperCLR.VdmHelper");
this.helper = (IVdmHelper)Activator.CreateInstance(type);
this.helper.Init();
ってことは dll のビルドで何かミスってる予感
しょうたいしてしまえ
いくつかばぐあったからなおしてるところいま
バグ治ったけど他人のウィンドウがうまく動かないので調査中
あ、dllのビルドの時、ターゲットプロセッサをx64にしてますか、もしかしてanyとかになってませんか
うおおおお うまくうごきました すげぇ
あ、dllのビルドの時、ターゲットプロセッサをx64にしてますか、もしかしてanyとかになってませんか
これかもしれない!
つまりは凡ミスだった
結合ためしてみるね
とりあえず DLL 読むの成功するところまでプッシュした
最新のコミットで、起動してから 左Ctrl + 左Alt + 左Win + ←→ でアクティブウィンドウを飛ばす
ようにしてるんだけど、動いてないのでたぶんどこかがわるい
なんかちょっと直したけどうまくうごいたよん
どこ直したのん…
diffみる、あとウィンドウ閉じてもApplication.OnExit 呼ばれない謎で困ってる、フック外せない
あー、ウィンドウは表示しない前提だから ShutdownMode.OnExplicitShutdown にしてる
自分で Shutdown() しないと落ちないぽよ…
ところで
var is64Bit = Marshal.SizeOf(typeof(IntPtr)) == 8;
と
var is64Bit = Environment.Is64BitProcess
っておんなじなのかね。
referencesource 見ると #if で作られててわからん
どうなんだろう、いっしょなのかもしれない?
とりあえずInjectDll32.exeもassembliesに足してあげてほしい
ぷっしゅした
32bitプロセスで CoCreateInstance(CLSID_ImmersiveShell, nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&pServiceProvider));
するとE_ACCESSDENIED 出るのでウィンドウが移動できない現象を確認しました
こっちが 64bit で移動しようとしてるウィンドウが 32bit プロセスのぱたーんか
そーそーそれそれ、なんかWoW64な環境で32bitプロセスからCoCreateInstanceするとだめらしい 謎
explorer だと、2 回叩いて 2 回目で別仮想デスクトップに飛んでいく、ということに気付いたなど
(1 回目は反応しない)
謎だ
あぷりのリポジトリ、ブランチ切ってpushしてPRとかしていいんだろうか
ぜんぜんおっけー
とりあえず実装はいい感じになってきたしここはClose。なんかあったら別のissueにしよう。