Win32API質問箱 Build126
■ このスレッドは過去ログ倉庫に格納されています
Win32APIについての質問はこちらへどうぞ。 ■注意 ・質問する前にMSDNライブラリやPlatformSDK、Google等で検索しましょう。 ・日本語版MSDN Online Libraryは不完全です。 英語版( http://msdn.microsoft.com/en-us/library/ )の利用推奨。 ・APIフックなど高度な事をしたい場合はできるだけAdvenced Windowsを読みましょう。 ・言語特有の問題やIDE、MFCやVCLなどの質問はそれぞれの言語や開発環境スレで ■過去スレ Win32API質問箱 Build125 https://mevius.5ch.net/test/read.cgi/tech/1551247748/ Win32API質問箱 Build124 https://mevius.5ch.net/test/read.cgi/tech/1510395780/ ■関連スレ Visual Studio 2019 Part4 https://mevius.5ch.net/test/read.cgi/tech/1585715794/ Visual Studio 2017 Part7 https://mevius.5ch.net/test/read.cgi/tech/1558179898/ 【C++】 DirectX初心者質問スレ Part41 【C】 https://mevius.5ch.net/test/read.cgi/tech/1521786252/ _beginthreadexだけは許さんよ もっとも醜悪な名前だわ http://eternalwindows.jp にはWin32の核心部分が載ってる。参考になる記事だ。 GetOpenFileName で初期ディレクトリとして OPENFILENAME の lpstrInitialDir に 任意のディレクトリを指定してファイルを開くダイアログを開いていて、おおよそ 意図した通りに動いているのですが、"C:\Users\Public\Documents"を指定したときだけ 初期ディレクトリが現在使用中ユーザーのドキュメントディレクトリになってしまいます アクセス権のない他のユーザーのドキュメントディレクトリを指定したのなら このような動作は理解できるのですが、パブリックディレクトリでこのように なってしまうのは仕様でしょうか? パブリックディレクトリの取得方法を聞いているわけではないのですが "C:\Users\Public\Documents" は存在する有効なパスです 再現した C:\Users\Public ならちゃんと public が開くが C:\Users\Public\Documents にすると自分のアカウントに行ってしまう あと存在しないディレクトリを与えると C:\Users\自分のアカウント\Documents が開いた lpstrInitialDir The initial directory. The algorithm for selecting the initial directory varies on different platforms. Windows 7: If lpstrInitialDir has the same value as was passed the first time the application used an Open or Save As dialog box, the path most recently selected by the user is used as the initial directory. Otherwise, if lpstrFile contains a path, that path is the initial directory. Otherwise, if lpstrInitialDir is not NULL, it specifies the initial directory. If lpstrInitialDir is NULL and the current directory contains any files of the specified filter types, the initial directory is the current directory. Otherwise, the initial directory is the personal files directory of the current user. Otherwise, the initial directory is the Desktop folder. Windows 2000/XP/Vista: If lpstrFile contains a path, that path is the initial directory. Otherwise, lpstrInitialDir specifies the initial directory. Otherwise, if the application has used an Open or Save As dialog box in the past, the path most recently used is selected as the initial directory. However, if an application is not run for a long time, its saved selected path is discarded. If lpstrInitialDir is NULL and the current directory contains any files of the specified filter types, the initial directory is the current directory. Otherwise, the initial directory is the personal files directory of the current user. Otherwise, the initial directory is the Desktop folder. comdlg32.dllのコモンダイアログ関係は >>367 のような想定外の動きをしたり 右クリックのコンテキストメニューで存在しない32bitフォルダを探しに行ったりと色々不具合を抱えてる 明らかにメンテされてない ダイアログ内のListViewで開発者の想定外の事ができたりするから許されるなら使わない方が良い コモンコントロール使ってるアプリに使うなと言ってしまうと、開発プラットフォームから見直さないとダメ案件では >>372 > Otherwise, the initial directory is the personal files directory of the current user. この分岐に填まってるようですが、lpstrInitialDir が NULL でない以上おかしいですよね とりあえず仕様ということで対応します ありがとうございました >>373-374 確かに他にも怪しい挙動があるので差し替えたい所ですが、そんな単純にコモンコントロール部分だけ 差し替えが可能なものがあればいいのですが・・ XP 以前と 7 で違うんだから 10 ならさらに変更されたとかありそう >コモンコントロール部分だけ差し替えが可能なもの 秀丸ファイラーのdllって無かったかな こんにちは、お世話になります。DLLファイルの削除について質問です。 プログラム終了時にロードしたDLLファイルを削除したいのですが、 場合により削除できたりできなかったりします。 実際にはもう少し複雑ですが、大まかな処理の手順を示します。 1: inst=LoadLibrary("tmp.dll") 2: hook=SetWindowsHookEx(...) 3: ...あれやこれやの処理... 4: ret1=UnhookWindowsHookEx(hook) 5: ret2=FreeLibrary(inst) 6: ret3=DeleteFile("tmp.dll") 処理部[3:]は、所望の通り動作していて、inst,hook共に正常値のよ うです。アプリケーション終了時に[4:]以降の処理を行います。ret1, ret2は成功値を返すのですが、ret3は失敗し、GetLastErrorの値は5で ERROR_ACCESS_DENIED「アクセス拒否」を返してきます。アプリ ケーション終了後はプロンプトからのDELコマンドに成功します。 処理部[3:]をスキップすると、ret3は成功値を返しファイルもただ しく削除されています。 FreeLibraryだけでは、DLLを開放してくれないのかと思っていますが、 少し手つまりになっています。当方、日曜プログラマレベルの知識で す。質問などに不備等ございましたらご指摘ください。 (環境)Win10-64bit,Vs2019Comm,VC++ 追記 2: hook=SetWindowsHookEx(WH_KEYBOARD_LL,proc,inst,0) フックにDLLインスタンスが絡みます。 1〜2をやるボタンAと 4〜5をやるボタンBを配置して ボタンBを押してから6やってみた? >>381 ありがと いや、そういう処理の分解はやってないです。 ただ、[5:]と[6:]の間にSleep(10000)を挟んでみた。OSが手放してくれる時 間?が必要かと思って・・・でも、そういう事じゃないみたい。 ボタンの配置はすぐには難しいですが(汗)。でも、とりあえず、物は試しで ユーザの入力待ちを挟んでみます。getch()あたりで・・・。 あと 6 だけ実行する別のアプリを造っておいて アプリ終了後にそれを呼ぶとか 379です。進展しそうなので、この質問はいったんクローズさせてください。 グーグル先生に「dll_process_detach 呼ばれない」と聞くと、イロイロ出て きそうです。 381、383 ありがとうございました m(_ _)m 呼び出しプロセスが終了するまではDLLの解放はされなかった気がする つまり>>384 が正解なのではないかと 32bitアプリからShellExecuteでバッチファイルを呼ぶと当然ながら32bit環境で実行されるんだけど これを64bitに強制できないかな >>389 Wow64DisableWow64FsRedirectionを使う痛い方法があるけど、推奨できない。64bit processを経由した方が痛みは少ない。 でてきた PVOID m_lpOldVar = 0; Wow64DisableWow64FsRedirection(&m_lpOldVar); //この間に書けばいいらしい ShellExecute(hWnd,"open","cmd","param","dir",SW_SHOWNORMAL); Wow64RevertWow64FsRedirection(&m_lpOldVar); これでいいのかな? ほんとは32bitアプリを64bit化すりゃいいんだけど思いの外大変なので諦めた 以下バッチ側で判定する方法 @echo off if %PROCESSOR_ARCHITECTURE%==x86 ( if "%PROCESSOR_ARCHITEW6432%" == "AMD64" ( rem 32bit WOW64 C:\Windows\sysnative\cmd.exe /c %~dp0%~n0%~x0 exit /b 0 ) else ( goto :PASSX64 ) ) else ( goto :PASSX64 ) :PASSX64 以降x64前提の処理 リダイレクトを無効化して、system32のcmdを指定すればいけるやろ。 プロセス起動だけを担当する小さな64bitアプリを作ればいいじゃない。 379です。 LoadLibrary()でロードしたDLLが呼び出しプロセスから削除できない件のご報告。 方法が正しいのか悪いのか(不具合を発生させる可能性を持つのか)わかりませんが、 削除することには成功しました。383さんがご指摘の「DLL_PROCESS_DETACH が呼ばれてないんじゃない?」とMSDNのFreeLibraryの文書をヒントに、 FreeLibrary()がゼロを返すまで複数回繰り返す方法で対処できました。DLLモ ジュールへの参照数が無くなった時点でDLL_PROCESS_DETACHが発生するよう です。また、ゼロを返した時のGetLastError()値は126(ERROR_MOD_NOT_FOUND) です。 適当にいじりながらモニタしてみるました。すると、DLL_PROCESS_ATTACHは 1回しか呼び出されていないにもかかわらず、DLLモジュールは1または2の参照カ ウントを持っているようです。どのタイミングで2になるのか、また3回目が発生 しないのかは、追い切れていません(汗。エラー検出で正常・・・的な行儀のよい コードとは思えませんが、とりあえず、要モニタリングで先に進みたいとおもいま す。 このあたり、お詳しい方がおられましたらご指導いただけると助かります。今のと ころ困ってはいませんので、お暇なときにでも^^。ありがとうございました。 >FreeLibrary()がゼロを返すまで複数回繰り返す なるほど 自分以外が使ってないことが確実に言えるならこれで良いんじゃないかな 実は自分で 2回 LoadLibrary してましたというオチですね判ります ズコー LoadLibraryをAPIHookしちゃおうぜ 長文で申し訳ない。 396 自分しか使っていないDLLであることは間違い無いので・・・とりあえず現状で すすめる感じ。動いてるからOKは無能感アリアリです。リファレンスカウント を追いかける事もできるようですが、沼入間違いなし。もし3回目が発生したら ワーニング出してるので、報告してもらいます。ありがと。フックの時、DLLイン スタンスを与えてるので、その時、参照が追加されるのかな、などと妄想するわけ です。 297 判りますか!・・・さすがです。あおりでないと思ってマジレス。 ユーザの起因でLoadLibraryが2回実行される可能性として思い当たるのが: 1.メインプロセスが終了していないうちにデバグなどで再度起動しちゃった。 2.LoadLibrary呼び出しのの箇所をプログラムカウンタが2回通過している。 辺りでしょうか。1は、多重起動が排除されていて、かつタスクマネジャにも出て いないので可能性が低い。2は、ブレークポイントとprintf()でモニタしてて多分 ないかなぁ。他アドバイスありましたらよろしくお願いいたしますm(_ _)m。 398 LoadLibraryをフックして、どのタイミングで呼び出されるのかを理解する感じ ですか?APIフックかぁ・・・経験値が足りないのでハマりそうだなぁ。thx。 >>395 FreeLibrary()は何回呼び出して何回目でゼロが返ってきてるの? 400さん、レスありがとう。実際のコードはi値の制限などもちっと複雑なので すが、感じとしては、WinMainに以下のように記されています。 1: i=0; 2: for(;;) { 3: i++; 4: ret=FreeLibrary(dllinst); 5: if(res==0) { 6: n=GetLastError(); 7: break; 8: } 9: } 場合によるのですが、ループを出た時のi値は2か3を示します。nは126です。 i値が2になるケース、3になるケースは、初回投稿 >>379 に示してあります。 フックの初回呼び出しにリファレンスが追加・・・かなぁ(妄想。 >>401 4でretなのに5でresなのは誤字かな? というか>>379 でいうと > 3: ...あれやこれやの処理... ここに原因があるってことでしょ そこ次第じゃないの? まず>>379 の1の直前・直後での山椒カウンターから調べてみるとか LoadLibraryの参照カウンタって簡単に取れたっけ? 昔DDKのヘッダ持ってきてゴニョゴニョやった気がする。 しかもwin7と8以降で構造体定義が違ったり。 成功するまで n回 FreeLibraryを試みて LoadLibrary しなおす? ATTACH と DETACH が副作用おこすかもだけど みなさん、こんな漠然とした質問に答えてくださってありがとう。 404さん、405さん。参照カウンタの取得は、現象の理解に役に立つとは思います が・・・なにぶん日曜プログラマなものですから。 google先生 「win32api how to get dll reference count」 でめぼしい情報はPEB(Process environment block)のLDR_MODULE構造体がう んちゃらで(マト違い?)、ハードルの高さを感じました。 406さん。おっしゃる通りです。ユーザーのコード内でLoadLibraryが1度だけ 呼ばれるのだけど、参照カウンタが2になっている点は重要と認識しています。 3になるかもしれないし、いつ増えるかも妄想の範囲を出ません。FreeLibrary するループ内で参照カウンタが変化かもとかゾッとします。 というわけで、今のところ警告を出して他の現象も集めてみたいと思っています。 が、人に使ってもらうレベルでは、参照カウンタの振る舞いは押さえておかないと いけないという認識です。 追記: LoadLibraryが2度呼ばれてましたとかいうオチの時は、正直に詫びを入れることを お約束いたします。いやマジで、ユーザーコード全部で記述は1ヶ所だけし、2回通っ てないし(大汗。 LoadLibraryしたライブラリの名前は何ですか? exeと同じディレクトリに置かれたtmp.dllという名前です。DLLによっては、 参照が増える可能性があるということでしょうか。私が書いたコードですが中 身はほぼ空っぽです。たまにデバグやモニタの為にprintfします。コピペで手 加工のためtypoしてたらごめんなさい。 * tmpdll.cppの中身 01: BOOL APIENTRY DllMain( 02: HINSTANCE hinst, 03: DWORD reason, 04: LPVOID rsv 05: ) { 06: switch (reason) { 07: case DLL_PROCESS_ATTACH: 08: DisableThreadLibraryCalls(hinst); 09: break; 10: case DLL_PROCESS_DETACH: 11: break; 12: case DLL_THREAD_ATTACH: 13: break; 14: case DLL_THREAD_DETACH: 15: break; 16: default: 17: break; 18: } 19: return TRUE; 20: } * コンパイルコマンド: >cl.exe /LD tmpdll.cpp User32.lib >move tmpdll.dll tmp.dll すいませんが、今日はもうレスできません。作業は停滞から抜け出れてますので、 どうかヒマつぶし程度の軽い気持ちで受け答えください(大汗。 参照カウントは、読み込んだ・実行した回数と関係ないだろ。 複数のプロセスから、参照されていれば、2 以上になるだけだろ 自分が参照すれば1で、 OS も参照すれば、2じゃないの? 自分で参照回数を、0にすべきじゃない。 OSが使っているかも知れないし OSが何をやっているのか、勉強していない香具師は、妙な事を考えない方がよい。 OSの勉強だけで、何十年も掛かるから ウイルス対策ソフトによる介入まで考えたらさらに増える。 >>411 ここで言ってるのはプロセス内の話だぞ? LoadLibraryを2回やったらFreeLibraryも2回呼ばなきゃならんという話。 >OSが何をやっているのか、勉強していない香具師は、妙な事を考えない方がよい。 DLL を2回も呼ぶのか? 一々、DLLをロードしてから、フリーするとか、 そんなに厳密に、メモリを気にしなくても良いのでは? 初心者は、そんな些末な事に、時間を掛けるべきじゃないと思う。 どうせ、1MB とか、ほぼ64 bit では無意味な節約だろ そういうのを気にしていたら、Ruby などでプログラミングできない。 皆、富豪プログラミングなのにw LoadLibraryの参照カウンタはプロセス単位 質問者はDLL_PROCESS_ATTACHが複数回呼ばれると思ってるようだが複数回LoadLibraryしても呼ばれるのは最初の1回だけ おはようさん。ゆるくいってもらえるとうれしい。 413さん。LoadLibraryを複数回呼べば参照カウンタはそれだけ増えます。 その場合、増えた原因がLoadLibraryと明らかなので特に問題ないと考えます。 わからないのはLoadLibraryが1度のつもりなのに、場合によって一つ増える 現象が不思議だなってあたり。 414さん。まったくその通りです。実際、削除してみたいというのは今のところ 技術的な興味だけで、参照カウンタの増加原因がコードのどの部分なのかが わかれば自分が面白いという点だけです。対応策は削除しない策も含めて2、3 考えていて重大ではなくなっています。時間がもったいないですね。 461さん。アプリケーションが起動されたプロセスで、DLL_PROCESS_ATTACH はLoadLibraryの時に1度、DLL_PROCESS_DETACHはFreeLibraryにより参照カ ウンタが0になった時に1度だけです。このように理解していますが^^;。 参照カウンタはプロセス毎に持っていて、自プロセスのFreeLibraryが2回成功 するということは、自プロセスの参照カウンタが2になっていると考えていま す。他のプロセスがtmp.dllをロードしても、自プロセスの持つ参照カウンタは 増えません。予想の範囲をでませんが、自分の書いたコードの中のあるAPI関数 が参照カウンタを増やしている、たぶん初回のフックプロシジャ起動の時に増え てるっぽい。ですので、現象を正しく理解するには、結局の所、参照カウンタを モニタして増減のポイントを押さえ、可否を判断するのが本道かなとも思います ・・・難しそうですよね。APIは勝手に参照カウンタ増やすよ?なんてアドバイ スがあると、よかったのですが。 長文でくどくなって、イラついている人がいそうですね。この辺で、一度この話 題から私は離れたいと思います。アドバイスくださった方がた、どうもありがと うございました。 自発的なdllの削除ってなると >>412 の件は厄介かもな Windows は、クローズドOS だから、何をやってるか分からない ファイルをロックするためにとか、メモリから追い出されないために、 参照回数を増加させるかも知れない だから、自分が1回しか呼んでいなのなら、参照回数を2回減らしてはいけない。 2回減らしても、無視されるだけかも知れないけど 自分が増やした分だけを、減らすべき! なんか参照カウントと単なるカウンタが入り乱れてそうな流れ 全部読まずにresするが 元々自分のexeの依存dllにそのdllを入れちゃってるのに さらにLoadLibraryしてるとかかな DLLのコードもあるなら別にDLLで無くてもいいのではと思ってしまうw >>421 俺もそう思う 静的リンクと動的リンク合わせて2回呼ばれていると思う なんか滅茶苦茶だな 古いMSDNライブラリ2008ではshellコントロールもCE5.0も正しい定義が出てる メンテ止めても表示崩す必要無いだろに... https://docs.microsoft.com/en-us/previous-versions/windows/embedded/aa453326 (v=msdn.10) 久々にapi使ってコード書こうかと思ったがガックリだわ ListView_GetItemCount Retrieves the number of items in a list-view control. You can use this macro or send the LVM_GETITEMCOUNT message explicitly. int ListView_GetItemCount( HWND hwnd ); Parameters hwnd Handle to the list-view control. Return Values Returns the number of items. なぜですか? https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q13212452128 質問者からのお礼コメント 小学生でもわかるような回答ではなく、なぜそうなのかも回答できないと社会人として微妙ですよ。 わかる箇所だけ答えるのはニワカです。 AllocConsoleでコンソールを開いてそこからstdio.hのscanfしたいのですが、 ノンブロッキングではないので呼び出し側が固まってしまいます。 そこでscanf的な機能をもつノンブロッキングな関数をつくりたい。 FILE_FLAG_OVERLAPPEDでCONIN$を開いてReadFileで読むまではいいのですが ReadFileしていない間の入力もバッファリングしてしまいます。そういう機能だから当然ですが。 このバッファリングをうまくオンオフする方法、もしくはバッファを棄てる方法はないでしょうか? それともFILE_FLAG_OVERLAPPEDではない別の方法があるのでしょうか? CONIN$を開いたハンドルをCloseHandleで閉じたりFlushFileBuffersしたりは試しました。 >>434 ありがとうございます sscanfはもちろんするのですが、その前段階、文字列を読み込む部分が上手くいっていないのです。 setvbufやfreopenはcランタイムの関数で、バッファもOVERLAPPEDのバッファとは違う気がします freopenしたstdinからのscanfがブロックされてしまうというのが443の話なので そこからノンブロッキングのAPIを使う必要があるのでは、ということになったわけです ReadConsoleってAPIもブロッキングなので使えないわけですが、低レベルなReadConsoleInputというのもあるらしく OverlappedでのReadFileよりこっちの方が正攻法なようです。 ともかくやってみます。 ありがとうございました。 >>433 プログラム側で「この時点以降の入力を有効とする」タイミングは管理してないの? その時点でバッファされてる物を一旦読み捨てれば良いだけでは? 生のコンソールは制約が多いから コンソールアプリの入出力を奪って適当なコンソールもどき作った方が早そうだけど スレッド作ってブロックさせておけば良いと思うんだけど 正直な話>>442 バージョンによって微妙に動きが異なるし >>443 ありがとうございました ブロックしないじゃなくてブロックしても大丈夫にして上手くいきました バッファの方は事前にFlushConsoleInputBufferで解決しました コマンドプロンプトのコンソールを使う案件は、スクリプト言語とのプロセス通信を使ったほうが幸せになれる確立が高い。 GetProcAddressってAやWないよね? それでいてwcharでもcharでも機能するのは仕様? __declspec(thread)の変数ってスレッドごとに記憶域をもつわけですが APIのTlsGetValue等でどうやって実現してるんでしょうか? TLSインデックスをうまくスレッドハンドルと結び付けなけりゃならないと思うんですが。 正直、このあたりは深く考えないことをオススメするのですが。 興味があるということであれば↓を参照しつつ。 http://www.interq.or.jp/chubu/r6/reasm/PE_FORMAT/6_6.html コンパイラが__declspec(thread)の変数があった時に、どのようなコードを出力しているか 逆アセンブルしてみると理解が深まると思われます >>449 ありがとうございます 昔はTlsGetValueを使ってたのが、今はセクションが新設されてそこに作ってるんですね VirtualAllocとHeapAllocの違いについてなんだけど HeapAllocもヒープ残量以上のサイズを渡したらどの道VirtualAlloc呼ばれるって認識でいい? HeapCreate以上のメモリはHeapAlloc からは作れない。 >>452 ある程度大きいサイズのメモリ確保だとVirtualAllocの方が早くなるからそう勘違いしてたけどそういう訳じゃないんだね 最初に実際にアクセスした時に初めて実際にメモリ割り当てが行われるとかの挙動がHeapAllocでも走るってだけか virtualの方がよりローレベルなので チャンクアロケーターをMSのレベルを 超えて作れるか、ほぼ解放しない前提 のブロックを確保するとかでないと使わない。 普通のmalloc風に使うなら、ヒープ領域毎 あぼーん出来るCreateHeapの方が使い易い あるstaticなlibが既存の場合(.hで関数の引数がTCHAR *使用) それがANSI用にコンパイルされたlibなのか それともUNICODE用にコンパイルされたlibなのか 既存のバイナリのlibだけから判断する方法はありますか? #ソースは.cppでTCHARで書かれているものがあったので #そこから-DUNICODEであらためてコンパイルすれば解決はしたのですが #上の質問の解決策があるなら知りたいです >>455 C++の場合なら、ライブラリをdumpbinなどで調べて関数のマングリン化名を見れば分かる。 plain Cの場合は、実際にライブラリに1文字が2バイト以上のUNICODE文字を送ってみて、 正しく動作するかどうかテストしてみると分かる。 >>456 plain C の場合、他の方法としては、 1. dumpbinでライブラリ関数を逆アセンブルしてみて、引数の扱いがbyteに なっているか、wordになっているかを調べれば分かる。 2. ライブラリの関数を呼び出すコードを書いてみて、デバッガで、ライブラリ関数の 中を逆アセンブルする。 もしかしてマングリの PEB_W とかのが UNICODE 用ってことでしょうか? >>458 デマングラー なるものが(ブラウザ上アプリとして)ネットであって、関数のシンボル名 を入れると関数プロトタイプ宣言に直してくれるので試すべし。 VCにはundnameというツールが標準でついてくるし、gccにはc++filtがある STDAPI と __declspec(dllexport) を同時に使うと怒られるんだけど 原因は何が考えられますか? __declspec(dllexport) を使わずに .def ファイルで .dll を作成することは可能なのですが .def を使わずに __declspec(dllexport) で .dll を作成しようとすると失敗します ■ このスレッドは過去ログ倉庫に格納されています
read.cgi ver 07.5.5 2024/06/08 Walang Kapalit ★ | Donguri System Team 5ちゃんねる