2011-06-21 3 views
8

コードは、公開されたSVNツリーの表現に対してHTTP呼び出しを行っています。次に、HTMLを解析し、後で参照用にファイルを追加して、ユーザーにプルダウンしてプッシュします。これはWPFアプリケーション内で行われています。以下は、コードとディレクトリ構造を示すイメージです。再帰的なHTTP呼び出しは、IDE対展開された実行可能ファイルで異なる動作を示します。

private readonly String _baseScriptURL = @"https://xxxxxxxxxx/svn/repos/xxxxxxxxxx/trunk/scripts/vbs/web/"; 

    private void FindScripts(String url, ref ICollection<String> files) 
    { 
     //MyFauxMethod(); 
     StringBuilder output = new StringBuilder(); 

     HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); 
     request.Credentials = new Credentials().GetCredentialCache(url); 

     _logger.Log("Initiating request [" + url + "]", EventType.Debug); 

     try 
     { 
      using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) 
      using (Stream stream = response.GetResponseStream()) 
      { 
       _logger.Log("Response received for request [" + url + "]", EventType.Debug); 

       int count = 0; 
       byte[] buffer = new byte[256]; 
       while ((count = stream.Read(buffer, 0, buffer.Length)) > 0) 
       { 
        if (count < 256) 
        { 
         List<byte> trimmedBuffer = buffer.ToList(); 
         trimmedBuffer.RemoveRange(count, 256 - count); 

         String data = Encoding.ASCII.GetString(trimmedBuffer.ToArray()); 
         output.Append(data); 
        } 
        else 
        { 
         String data = Encoding.ASCII.GetString(buffer); 
         output.Append(data); 
        } 
       } 
      } 

      String html = output.ToString(); 

      HTMLDocument doc = new HTMLDocumentClass(); 
      IHTMLDocument2 doc2 = (IHTMLDocument2)doc; 
      doc2.write(new object[] { html }); 

      IHTMLElementCollection ul = doc.getElementsByTagName("li"); 
      doc2.close(); 
      doc.close();     

      foreach (IHTMLElement item in ul) 
      { 
       if (item != null && 
        item.innerText != null) 
       { 
        String element = item.innerText.Trim().Replace(" ", "%20"); 

        //nothing to do with going up a dir 
        if (element == "..") 
         continue; 

        _logger.Log("Interrogating [" + element + "]", EventType.Debug); 

        String filename = System.IO.Path.GetFileName(element); 
        if (String.IsNullOrEmpty(filename)) 
        { 
         //must be a directory; recursively search if honored dir 
         if (!_ignoredDirectories.Contains(element)) 
         { 
          _logger.Log("Searching directory [" + element + "]", EventType.Debug); 
          FindScripts(url + System.IO.Path.GetDirectoryName(element) + "/", ref files); 
         } 
         else 
          _logger.Log("Ignoring directory [" + element + "]", EventType.Debug); 
        } 
        else 
        { 
         //add honored files to list for parsing meta data later 
         if (_honoredExtensions.Contains(System.IO.Path.GetExtension(filename))) 
         { 
          files.Add(url + filename); 
          _logger.Log("Added file [" + (url + filename) + "]", EventType.Debug); 
         } 
        } 
        //MyFauxMethod(); 
       } 
       //MyFauxMethod(); 
      } 

     } 
     catch (Exception e) 
     { 
      _logger.Log(e); 
     } 
     //MyFauxMethod(); 
    } 


    private void MyFauxMethod() 
    { 
     int one = 1; 
     int two = 2; 
     int three = one + two; 
    } 

Directory structure

まずオフ長いコードブロックのための謝罪。しかし私は完全な方法が理解されたことを確かめたいと思った。存在する問題は、IDEの外部で実行可能なリリースリリースを使用する場合にのみ適用されます。 IDE内でリリースビルドを実行すると、問題なく機能します。

さらに、生成されたデバッグをIDEの外部またはIDE内でビルドすると問題は発生しません。両方のシナリオで適切に機能します。

問題は、再帰呼び出しが再帰メソッドを過ぎてもコードを停止してしまうことです。スレッド内で例外はスローされません。他のビルドと同じように、すべてのディレクトリに移動する前にすべてが停止します。リクエスト を開始

このようなリリースビルド見た目のログの行が...

[https://xxxxxxxxx/svn/repos/xxxxxxxxx/trunk/scripts/vbs/web/]
応答が要求 のために受信[https://xxxxxxxxx/svn/repos/xxxxxxxxx/trunk/scripts/vbs/web/]
ディレクトリを検索[BEQ /]
を問い合わせます[beq /]
開始 要求 [https://xxxxxxxxx/svn/repos/xxxxxxxxx/trunk/scripts/vbs/web/beq/]
応答受信VED要求 [https://xxxxxxxxx/svn/repos/xxxxxxxxx/trunk/scripts/vbs/web/beq/]
問い合わせるため[コア/]
は[コア/】
は 要求 を[https://xxxxxxxxx/svn/repos/xxxxxxxxx/trunk/scripts/vbs/web/beq/core/]
レスポンス [BEQコア%20Libraryを問い合わせる要求 [https://xxxxxxxxx/svn/repos/xxxxxxxxx/trunk/scripts/vbs/web/beq/core/]
ために受信 ディレクトリ開始検索します。再帰的にスクリプトがかかっ見つけVBS]
追加要求 を開始 ファイル [https://xxxxxxxxx/svn/repos/xxxxxxxxx/trunk/scripts/vbs/web/beq/core/BEQ-Core%20Library.vbs]
ゲー[1オフ/]ディレクトリを検索
[1オフ/]
[https://xxxxxxxxx/svn/repos/xxxxxxxxx/trunk/scripts/vbs/web/beq/one-offs/]
レスポンスが要求 のために受信[https://xxxxxxxxx/svn/repos/xxxxxxxxx/trunk/scripts/vbs/web/beq/one-offs/]
[0] m [0] s [906] ms [1]
合計時間[0] m [7] s [46] s [ ms

UPDATEは:

デバッグ中に約3追加のログラインに追加した後、それは今それが必要として機能しています。顕著な問題はなぜですか?別のアプリケーションで問題のコードを分離しようとすると、否定的な結果は生じません。

これはなぜ起こっているのでしょうか?

UPDATE:のどのメソッドを呼び出すためにログ行を変更すると、

同じ結果が得られました。私はfauxメソッドとfauxメソッドへの呼び出しを上記のソースに追加しました.1はメソッドのエントリに、3はbottomの近くにあります。呼び出し自体は、場所を特定しやすくするためにコメントされています。彼らはではないは、実際のコードでコメントされています。

私が4つの追加fauxメソッド呼び出しのいずれかをコメントアウトすると、機能しなくなります。これもまたにCTRL + F5を押すか、またはIDEの外部でのみ行うことができます。

UPDATE:fubaarあたりHtmlDocumentインスタンスに

追加しまし.close();依然として同じ挙動が示されている。

UPDATE:fubaarあたりGCに

を追加しました明示的な呼び出し。依然として同じ挙動が示されている。

+0

競合他のタイミングの問題のように聞こえます。 –

+0

@ Jakubこれはすべて同じワーカースレッドで起こっています。私はそれをUIスレッドに移動し、ログ行が存在しないときと同じ動作を経験しました。私はそれがタイミングの問題であることを知っている、私は可能性がどこに由来する可能性があるかを把握しようとしています。 –

答えて

1

作成しているHtmlDocumentインスタンスで.close()を呼び出さないことに気付きました。これは、mshtmlがwrite()の後で正しくクリーンアップされるようにするための最初の試みです。

また、HtmlDocumentのファイル名リストをビルドしてファイル名リストに戻す前に、HtmlDocumentを解放して、HtmlDocumentインスタンスの数が増えていないようにすることもできます。単に記憶上の問題ですが、mshtmlを使用してCOM相互運用機能を使用している場合は、経験を積んだほうがずっと慎重に踏み出す価値があります。我々は、WPFが日常初期化/終了メッセージをポンプしないHTMLDocumentのインスタンスを大量に処理するとの問題をヒット私たちのWPFアプリで

:編集など

を追加しましたが、コメントのために長すぎたようCOMから。私たちの実装では、メモリリークと最終的なCOMエラーが発生しました。これは明らかにあなたが見ている動作ではありませんが、COM相互運用機能が正しくクリーンアップできないという問題があるのだろうかと思います。あなたは、あなたのCOMオブジェクトをで行われ(そして完全に解放)されている場合、これらの行を追加して試してみる価値は何を次のようになります。

GC.Collect() 
GC.WaitForPendingFinalizers() 
(.Collectコール)ファイナライザキューに任意のCOM相互運用機能オブジェクトを追加すると発生します

COMメッセージをポンピングする.NET(WaitForPendingFinalizers呼び出しの副作用)。

私は知っている暗闇の中で少し刺すようですが、この物は本質的にCOM相互運用性(.NETオブジェクトで覆われていますが)でも問題になるかもしれません。

+0

HtmlDocumentの両方のインスタンスで.close()を追加するのを忘れました。ありがとうございます。残念ながら、同じ動作がまだ出現しています。オリジナルの質問に.close()ビヘイビアが追加されたコードを更新しました。 –

+0

下記のフォローアップを追加しました。(コメントが長すぎます) – fubaar

+0

これだけを残しておきます。 GCへの明示的な呼び出しを追加しました。 HTMLDocumentインスタンスを使用するコードを削除し、HTMLをXmlDocumentにプッシュする代わりに、この問題は存在しません。それは確かにCOMの動作に関して何かです。正確にはまだ分かりません。 –

1

これが純粋に投機的なので正しいかどうかはわかりません。 mshtml.dll内のCOM APIはどこにも記述されていないように見え、.Netラッパーは許可された動作に関して曖昧です。

.Net documentationは、ホストされているWebBrowserコントロールからHtmlDocumentのインスタンスを取得していることを前提としています。ドキュメントには、独自のインスタンスを作成する必要があることが示唆されるものは何もありません。予期しない方法で使用された場合、基礎となるデータ管理が妥協する可能性があるので、常にadapter patternsの周りに注意してください。あなたはこれをすることを妨げられることを願っていますが、.NetがCOMをどのように包み込むかについての省略や人為的なものかもしれません。

IHTMLElementCollectionは、直接HtmlDocumentに戻ります。そのクラスがWebControlの共有インスタンスを使用している場合(独自のWebコントロールを作成したことがないため、実装はフレームワークによって異なります)、ドキュメントが変更されるとコレクションは無効になります。

デバッグとリリースの動作がログ行とともに変化する理由は何ですか?いい考えはありません。 Internet Explorerのコードで、あらゆる種類の興味深いメモリ/キャッシュ管理が可能です。 GCコールはネイティブIEコードではなく、ラッパーにのみ影響します。

テストとして、関連するリストデータのコピーを作成してから、HtmlDocumentで再利用することができます。

このAPIの使用は潜在的に間違っていると思うし、WebControlからの作業が唯一の正しい使用法かもしれないと思う。しかし、mshtmlのCOMインターフェイスがどのように動作すべきかを記述したドキュメントは見つかりませんでした(ドキュメント化されていないSDKであることを示すいくつかのWebページが見つかりました)。

+0

mshtmlは確かに.NET FWで利用できる唯一のHTMLパーサーです。 Html Agility Packではこれを検討していましたが、現行のエンタープライズ標準では現時点でそのライブラリの使用を禁止しています。現在のルートはXmlDocumentを使用していたため、SDKの一部であるHTMLとmshtmlを解析するためです。理想的ではありませんが、Debug/Releaseの両方で問題なく動作します。この問題は確かにHtmlDocument内に存在します。 XmlDocumentアプローチを使用して下流に問題が発生した場合、Html Agility Packへの移行は避けられません。 –

1

ちょっと考えましたが、コンパイラの最適化によって悪影響を受ける可能性がありますか?最適化をオフにしてもエラーは発生しますか?

関連する問題