◀ANSI版開発トップへ
  • 5363 長過ぎるパス名で落ちる
    • 5364 Re:長過ぎるパス名で落ちる
      • 5365 Re2:長過ぎるパス名で落ちる
    • 5366 Re:長過ぎるパス名で落ちる
      • 5367 Re2:長過ぎるパス名で落ちる
        • 5368 Re3:長過ぎるパス名で落ちる
          • 5369 Re4:長過ぎるパス名で落ちる
    • 5370 Re:長過ぎるパス名で落ちる
      • 5376 Re2:長過ぎるパス名で落ちる
        • 5377 Re3:長過ぎるパス名で落ちる
    • 5388 そろそろコミットしたい
      • 5396 Re:そろそろコミットしたい
  • [5363] 長過ぎるパス名で落ちる なすこじ 2008年06月23日 04:07

    >>data:6689も同様と思われる、長過ぎるパスのファイルを開くと落ちる既知の不具合についてです。

    下記4箇所で_MAX_PATHを .nMaxFile に設定しますが、_MAX_PATH以上のファイルパスとなるとバッファに'\0'が無い状態となって突き抜けています。

    CDlgOpenFile.cpp
     DoModal_GetOpenFileName()
     DoModal_GetSaveFileName()
     DoModalOpenDlg()
     DoModalSaveDlg()

    下記のようにすることで取りあえず落ちるのは回避できるんですが、真っ当な方法じゃないような気もします。
     m_ofn.nMaxFile = _MAX_PATH - 1;
     memset( m_ofn.lpstrFile, 0, _MAX_PATH );  // 最後2文字を0にするだけでもOK

    どうすべきでしょうか?
    • [5364] Re:長過ぎるパス名で落ちる じゅうじ 2008年06月23日 11:02

      ▼ なすこじさん
      >  DoModal_GetOpenFileName()
      >  DoModal_GetSaveFileName()
      >  DoModalOpenDlg()
      >  DoModalSaveDlg()
      この、4個のメソッドを呼び出している所だけでしたら、28ヶ所のみでした。
      呼び出すすべての前の所で0クリアしてはどうでしょう。
      char szPath[_MAX_PATH + 1];
      szPath[sizeof(szPath)-1] = '\0';

      私ちなみに、よく分かっていないのですが、
      なぜ最後1バイトでなく、2バイトクリアするのでしょうか?
      • [5365] Re2:長過ぎるパス名で落ちる なすこじ 2008年06月23日 21:17

        ▼ じゅうじさん
        > なぜ最後1バイトでなく、2バイトクリアするのでしょうか?

        2バイト文字で切れた場合もう1つ前もゴミとなることがあるためだったのですが、>>data:6694にて要望されたようにメッセージを表示してあげないとまずいということで、前回のものは撤回して下記のようにしようかと思います。

        下記関数において、GetOpenFileNameRecover()またはGetSaveFileNameRecover()の戻り値がTRUEの時、バッファ内に'\0'が存在しなければエラーを表示してFALSEを返す。
         DoModal_GetOpenFileName()
        DoModal_GetSaveFileName()
        DoModalOpenDlg()
        DoModalSaveDlg()

        上記の4箇所で.nMaxFileに_MAX_PATHを設定しているので、ここでチェックするのが良いと思ったのですが、さらに上位でチェックすべきでしょうか?
    • [5366] Re:長過ぎるパス名で落ちる kobake 2008年06月23日 23:31

      ▼ なすこじさん
      > 下記のようにすることで取りあえず落ちるのは回避できるんですが、真っ当な方法じゃないような気もします。
      >  m_ofn.nMaxFile = _MAX_PATH - 1;
      >  memset( m_ofn.lpstrFile, 0, _MAX_PATH );  // 最後2文字を0にするだけでもOK
      >
      > どうすべきでしょうか?

      MSDNを見ましょう。
      GetOpenFileName が FALSE を返したときは lpstrFile を見るのが真っ当な作法ではないでしょうか。確保すべき容量がわかるので、バッファを動的に確保して再試行すれば良いです。たぶん。

      MSDN引用
      > lpstrFile
      > …(略)…
      > If the buffer is too small, the function returns FALSE. In this case, the first two bytes of the lpstrFile buffer contain the required size, in bytes or characters.
      • [5367] Re2:長過ぎるパス名で落ちる なすこじ 2008年06月24日 12:25

        ▼ kobakeさん
        いえ、FALSEは返ってきません。
        エラー処理は元々実装されているので、FALSEが来れば最初から問題無いです。
        APIがおかしいのか .nMaxFile をよほど小さくしないとtoo smallは返ってこないので別の対応が必要です。まぁ使い方がおかしいだけかもしれませんが……

        ANSIなので絶対パスは260バイト以下なわけですが、260バイト目まで文字を詰め込んできます。切れ目が2バイト文字の途中の場合、259バイト目まで詰め込んで260バイト目に0を書かずに返ってきます。

        仮に今回too smallが来たとしても、ANSIで260バイトを超える絶対パスはMSの取り決めに反します。
        260バイトを超えた絶対パスでいくとどうなるかは私は知りません。
        • [5368] Re3:長過ぎるパス名で落ちる kobake 2008年06月24日 23:33

          ▼ なすこじさん
          > ▼ kobakeさん
          > いえ、FALSEは返ってきません。
          > エラー処理は元々実装されているので、FALSEが来れば最初から問題無いです。
          > APIがおかしいのか .nMaxFile をよほど小さくしないとtoo smallは返ってこないので別の対応が必要です。まぁ使い方がおかしいだけかもしれませんが……
          >
          > ANSIなので絶対パスは260バイト以下なわけですが、260バイト目まで文字を詰め込んできます。切れ目が2バイト文字の途中の場合、259バイト目まで詰め込んで260バイト目に0を書かずに返ってきます。
          >
          > 仮に今回too smallが来たとしても、ANSIで260バイトを超える絶対パスはMSの取り決めに反します。
          > 260バイトを超えた絶対パスでいくとどうなるかは私は知りません。

          なるほど、失礼しました。確かに試すとTRUEが返りますね。
          がっつりエラーチェックをするならこんな↓感じですかね (ここまでやるか、っつー極端な例かもしれませんが)。

          OPENFILENAME ofn;
          …
          BOOL bRet = GetOpenFileName(&ofn);
          DWORD dwErr = GetLastError();
          if(dwErr==ERROR_INVALID_PARAMETER){
          //想定外。プログラム側の問題 (だと思う)。
          }
          else if(dwErr==ERROR_OUTOFMEMORY){
          //メモリ不足。
          }
          else if(dwErr==ERROR_INSUFFICIENT_BUFFER){
          //バッファが足りない。バッファをもうちょい確保して再試行する or 失敗メッセージを出す?
          }
          else if(bRet){
          //成功。ユーザの入力したパスが正常に取得できた。
          }
          else{
          //ユーザキャンセル。
          }


          ちなみに環境依存かもしれないですけど(?)、Windows Vista Home Edition の環境では、
          バッファを最初から _MAX_PATH*2 確保しておくと、んまぁ一応正常動作しました。結果論ですが。
          • [5369] Re4:長過ぎるパス名で落ちる なすこじ 2008年06月25日 12:43

            ▼ kobakeさん
            API側がANSIなのに文字カウントをunicodeで行なっている様な変な動作をしているので、おっしゃる様な処理をしないと厳密にはできないですね。

            > ちなみに環境依存かもしれないですけど(?)、Windows Vista Home Edition の環境では、
            > バッファを最初から _MAX_PATH*2 確保しておくと、んまぁ一応正常動作しました。結果論ですが。

            確認ありがとうございます。98,NT以降で大丈夫なのかもしれませんが、
            ・undocumentedな動作である
            ・沢山の所で_MAX_PATHが使われているので拡張すると確認が大変
            ・unicode版なら長いパスを正しく扱えるのでANSI版が対応してなくても大丈夫

            ということで、バッファの拡張は行なわずにオーバーフローチェック&エラー表示するだけのパッチを作りました。
    • [5370] Re:長過ぎるパス名で落ちる なすこじ 2008年06月25日 12:52

      長過ぎるパスが指定されたらエラーを表示して処理をキャンセルするパッチを作成しました。

      Patches #2002211
      • [5376] Re2:長過ぎるパス名で落ちる もか 2008年06月26日 23:20

        >Patches #2002211
        コマンドラインから_MAX_PATH以上のファイル名を送ると落ちるので、
        その対策パッチを追加しておきました。
        なすこじさんのパッチを確認したかったのですがXP SP3しか環境が無いのでパスします。。

        ダイアログの方は、「DBCSのファイル名でMAX_PATH以上」でないと変にならないんですね。
        ASCIIだけでMAX_PATH越えにしたら、エクスプローラの右クリックがおかしいし、
        ダイアログで「ファイル名が無効」とでてそもそも開けなくなりました。
        • [5377] Re3:長過ぎるパス名で落ちる なすこじ 2008年06月27日 22:14

          ▼ もかさん
          > >Patches #2002211
          > コマンドラインから_MAX_PATH以上のファイル名を送ると落ちるので、
          > その対策パッチを追加しておきました。

          どうもありがとうございます。
          ソースコードおよび動作を確認しました。

          あと、私の修正の方で260バイト目がSJISの1バイト目だった時にエラーとならないことがあったので修正しました。

          Windows98はちょっと動作が違うようで本修正が有効に働きませんでした。
          異常終了しないような感じなので、取りあえずはそのままです (^^;
    • [5388] そろそろコミットしたい なすこじ 2008年07月16日 19:10

      まだ対応に抜けがあるかもしれませんが、ターゲットにした操作へのコードに変な所が無ければコミットしたいです。

      その後、落ちる操作が見つかる度にしらみつぶしにしていけば良いのでは?と思います。

      というわけで、どなたか構って下さい (^^;
      • [5396] Re:そろそろコミットしたい なすこじ 2008年07月28日 02:09

        編集ウィンドウへのドロップでも落ちていたので対策を施しました。

        Patchesの方でも書きましたが、Win98では'\0'を含めて261バイトのパスの時に落ちてしまう事があります。
        WinAPIの中で落ちるため本対策では救えません。

        それ以外では多分大丈夫です。
        一応Win2k SP4, WinXP SP2, Vistaで確認しました。

        落ちるという致命的な不具合なので、速やかにコミットまで行きたいのですが……