環境:Win7、VS2010、MFC、UNICODE
LoadLibrary()にて、251文字以上のパスを渡して実行すると
NULLが帰ってきます。
その際、GetLasterror()をとると123(パスが不正)が帰ってきます。
250文字のパスであれば、正常にハンドルが取得できます。
MSDNを見ても250文字の制限などは書かれて無いようですが、
このような仕様なのでしょうか?
LoadLibrayExを仕様してフラグをつけるとハンドルが取れますが、
フラグを外すと失敗します。(LoadLibrary()と同じ動作になるため)
何か原因などがわかる方がいらっしゃれば教えて頂きたいです。
MSDN
LoadLibrary : http://msdn.microsoft.com/ja-jp/library/cc429241.aspx
LoadLibraryEx: http://msdn.microsoft.com/ja-jp/library/cc429243.aspx
-----------
CString path = _T(【251文字のパス】); //C:\~\aaa.dll まで
HINSTANCE dll_1 = (HINSTANCE)::LoadLibrary(path); //失敗
HINSTANCE dll_2 = ::LoadLibraryEx(path, NULL, 0); //失敗
HINSTANCE dll_3 = ::LoadLibraryEx(path, NULL, DONT_RESOLVE_DLL_REFERENCES); //
成功
明示的に「ファイルのパス」と説明されている引数の場合は
「_MAXPATH」の制限がされている場合がほとんどです。
ただし、この値は260なので、259文字が実質的な制限のはずで、
250というのは少し変ですね。
(参考) http://msdn.microsoft.com/ja-jp/library/930f87yf(v=vs.100).aspx
上記で説明されている通り、_MAXPATH の制限は、
OSとそのファイルシステムの要請なので、不可避です。
使用するファイルパスの文字数が_MAXPATH近傍に近づくような設計を
容認するべきではないかもしれません。
追加 orz.
ただし、下位互換性(FAT32)のための制限の様なので、
LoadLibraryExW()を明示的に使用し、
\\?\・・・
を使用することで回避できるかもしれません。
追記です。
原因は作成したDLLのほうにもあるようです。
DLLはWin32プロジェクトで作成した直後にビルドした空のものですが
プロパティの設定でマニフェスト有りにしてビルドすると
251文字以上のパスで失敗します。
マニフェスト無しならば、251文字以上でもLoadLibrary()に成功します。
なんとなくの憶測ですが、
マニフェストありのDLLをLoadLibrary()をすると
内部で勝手に文字列が追加されてしまい、失敗してしまうのではと思ってます。
↓のような、251文字+9文字=260文字?
C:\~\abc.dll /manifest
手元の環境はWin7しかないのですが、もしやWin7からマニフェスト絡みで
250文字の制限などがかかる様になったのでしょうか?
>仲澤さん
260や256なら納得いくのですが、
250という制限は私も不思議に思っていました。
LoadLibraryEx()は明示的に指定しなくても、
フラグを付ければ250文字以上でもハンドルを取得できます。
LoadLibrary()は、LoadLibraryW()やLoadLibraryA()のように
明示的に指定しても失敗します。
manifestなしのdllで試していて問題なかったので、質問の意味が分かりませんでした。
manifest付で試した所、LoadLibraryA,LoadLibraryWが失敗することを確認しました。
なぞですね。
今回は内臓の様ですが、
マニフェストファイルは外部ファイルとしても使用されるので、
拡張子等の.mafifest(9文字)をは可能性を感じます(vv;)。
もとい.manifestの9文字 orz
デフォルトの設定でもマニフェスト有りになっているので、
マニフェストを付けるのは一般的だと思っていたのですが
そうではないのでしょうか?
割と良くありそうな問題なのですが、みなさんどう対処されてるのでしょうか?
必ずLoadLibraryEx()を使えと言うことなんでしょうかね。
マニフェストを使用するのは推奨されています。
これは著作権や動作権限の明示に使用されています。
マニフェストの役割とLoadLibrary()の動作は原理的に無関係のはずで、
そうでない場合は瑕疵と言っても良いのではないでしょうか。
LoadLibrary()は知る限りWindows2.x(16bit)からありました。
Windowsの基本的動作を柱的いみをもつ関数です。
一方、LoadLibraryEx()は32bitになって、メモリー空間が仮想化されたために
追加(必要になった)されたという認識です。
この時期にHDDなどのフォーマットがFAT16/32からNTFSに置き換わっていったことと
関連があるかもしれません(憶測ですが)。
経過報告
LoadLibraryWがCreateActCtxWを呼び出し,戻り値が0xFFFFFFF(INVALID_HANDLE_VALUE)。
エラーコードを0xc0000033にセットして、これがDebug出力の
SXS: BasepCreateActCtx() Calling csrss server failed. Status = 0xc0000033
になるようです。
調べてくださってるみたいでありがとうございます。
ようは、Windowsのバグなんでしょうかね?
大人しくLoadLibraryEx()を使うのが良いでしょうか。
ちなみに、LoadLibraryEx()のフラグに
DONT_RESOLVE_DLL_REFERENCES を指定しない場合は
DllEntryPoint を呼び出さないみたいですが、
DllEntryPoint を使いたい場合で以下のようにする場合、
どうしようも無いんでしょうかね?
LoadLibraryEx(path, NULL, 0) + マニフェストあり
自分の場合は「暗黙」でやることがほとんどですが、
明示的にロードする必要がある場合は
LoadLibraryEx( パス, 0, 0);
ですね。マニフェストはデフォルトのままです。
DLLはEXEと同じ位置に配置するので、原理的に
_MAXPATHを超えることはありえませんが(vv;)。
とりあえず \\?\ は試してみたのだろうか?気になる・・・
.manifest が原因かそうでないかに関係なく
LoadLibrary(_T(\\\\?\\C:\\long_path\\abc.dll)); とすると
\\?\C:\long_path\abc.dll に .manifest がくっついて
\\?\C:\long_path\abc.dll.manifest となるはずなので
どっちにせよ _MAX_PATH 超のファイル名が扱えてうまくいくかもしれない。
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
C:/long_path/abc.dll は LoadLibrary できても
\\?\C:/long_path/abc.dll だと100%失敗するのでその辺は要注意。
>仲澤@失業者さん
そもそもDLL同じ階層にして、相対パスでやればこんな事は起こらないのですね・・・
絶対パスにしてたのが間違いだったんでしょうか
>tetrapodさん
すいません、見落としてました。
\\?\ をパスの前にくっつけたらLoadLibraryW()で動作しました。
これなら32000文字超の文字まで扱えるので問題ないって事ですよね?
>C:/long_path/abc.dll は LoadLibrary できても
>\\?\C:/long_path/abc.dll だと100%失敗するのでその辺は要注意。
すいません、これはどう言う意味でしょう?
LoadLibraryA()だと失敗するという意味ですかね?
> これなら32000文字超の文字まで扱えるので問題ないって事ですよね?
御意。
Windows (MS-DOS 2.x) は UNIX から「ディレクトリ」の概念を輸入してきた。
UNIX ではパスを / で区切る (/usr/local/lib/libz.so のように)
Windows ではパスを \ で区切る (C:\long_path\abc.dll のように)
UNIX で作ったソフトをほとんどそのまま移植して使う際に便利なように、
Windows もパス区切り / を受け付けることになっている (先の MSDN 解説参照)
> File I/O functions in the Windows API convert / to \ as part of
converting the name to an NT-style name
LoadLibrary(_T(C:/short_path/abc.dll)) は期待通りの動作をすることになっている
わけだ。
ただこの / を \ に読み替えるのは「互換性維持機能」であるため
Windows NT 固有形式のパス名 \\?\ を使うときには読み替えがされなくなる仕様。
> except when using the \\?\ prefix as detailed in the following sections.
LoadLibraryW(L\\\\?\\C:\\long_path\\abc.dll) は期待通りの動作をする
LoadLibraryA(\\\\?\\C:\\long_path\\abc.dll) は動作しない (A 系 API では ??\?
は使えない)
LoadLibrary(_T(\\\\?\\C:\\long_path\\abc.dll)) は MBCS build で動作しなくなる
のでまずい
LoadLibraryW(L\\?\C:\long_path\abc.dll) は× (Cの素人の失敗でありがち)
LoadLibrary(L\\\\?\\C:\\long_path\\abc.dll) は× (MFC 素人の失敗でありがち)
LoadLibraryW(L\\\\?\\C:/long_path/abc.dll) は俺が先にあげた失敗例だ。
C:\long_path\abc.dll というファイルは実在していても
C:/long_path/abc.dll というファイルは実在しないため失敗する
( / → \ の読み替え動作が発生しなくなるため)
俺としては、この250文字問題は Windows のバグだと思っている。
(C:\long_path\abc.dll.manifest が260を超えることを考慮しきれなかった単なるバ
グ)
Windows の側の修正を待つくらいなら \\?\ で回避してしまうのが安上がりだろう。
(開発期間コストや、バグレポート発行の手間とか)