WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

[デバッグの停止] で止まらない

by WebSurfer 2010年8月31日 16:28

Visual Studio でデバッグ

Visual Studio で ASP.NET の Web アプリケーションをデバッグしている時の話です。

表題に「止まらない」と書きましたが、正確には、デバッガは止まるものの、コードはデバッガのコントロールが外れて実行されていき、メモリが開放されて終了するまで走っていってしまうということです。

従って、DB の更新などのコードがある場合、その手前でブレークポイントを設けて実行を止め、[デバッグの停止(E)]をしても DB の更新が行われてしまいます。デバッガのバグとかではなくて、そういう仕様だそうです。

ウソだと思う人は(笑)、以下に検証するためのコードをアップしておきますので、適当なところにブレークポイントを設けて試してみてください。ブレークポイントで [デバッグの停止] をしても、その後のコードは実行され、ファイル(file1.txt ~ file3.txt)はすべて作成されるはずです。

なお、このサンプルを動作させるには、アプリケーションルート直下に Files という既存のフォルダがあり、IIS のワーカープロセスがそのフォルダに対して必要なアクセス権を持っている必要がありますので注意してください。

<%@ Page Language="C#" %>
<%@ Import Namespace="System.IO" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
  protected void Page_Load(object sender, EventArgs e)
  {
    string path = Server.MapPath("~/Files/file1.txt");
    using (StreamWriter writer = File.CreateText(path))
    {
      writer.WriteLine("Hello World!");
    }

    path = Server.MapPath("~/Files/file2.txt");
    using (StreamWriter writer = File.CreateText(path))
    {
      writer.WriteLine("Welcome!");
    }
  }

  protected void Page_Unload(object sender, EventArgs e)
  {
    string path = Server.MapPath("~/Files/file3.txt");
    using (StreamWriter writer = File.CreateText(path))
    {
      writer.WriteLine("Goodbye!");
    }
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <h1>[デバッグの停止] で止まらない?</h1>
  </div>
  </form>
</body>
</html>

デバッグが止まっても実行が止まらないのは、Visual Studio のデフォルト設定(マネージド・コード用のデバッガを使用)における仕様だそうです。

設定によってデバッグの停止と共に実行を止めることは可能です。

Web サイトプロジェクトの場合、ソリューションエクスプローラのアプリケーションルートの右クリックで表示されるコンテキストメニューから[開始オプション(O)...]を開いて、[デバッガー]のオプションで[ネイティブコード(V)]にチェックを入れてみてください。

(注: Visual Studio Community 2015 の Web サイトプロジェクトの場合、[開始オプション(O)...]⇒[デバッガー]のオプションに[ネイティブコード(V)]がありません。別の設定方法があるかもしれませんが調べ切れてません)

Web アプリケーションプロジェクトの場合は、ソリューションエクスプローラの Properties を右クリックして開き[Web]タブを選択すると[デバッガー]の項目の中に[ネイティブ コード(C)]があります。

これにより、デバッガを起動するたびにネイティブコード用デバッガが読み込まれるそうです。結果、デバッグの停止と共に実行も止まります。

ただし、 [ネイティブコード(V)]にチェックを入れた設定でデバッグを停止すると強制的に Web サーバーを停止することになるそうです。それは好ましいことではないということで、デフォルトではデバッグを停止しても実行は止まらないようになっているようです。

なので、自分の場合、Web サーバーの強制停止による好ましからぬ副作用があるとイヤなので、そういうものだ(デバッグを停止しても実行は止まらない)と理解した上で、デフォルト設定のまま使っています。

Tags:

DevelopmentTools

ダウンロードしたファイルの更新日時

by WebSurfer 2010年8月30日 16:21

クライアントがサーバーからファイルをダウンロードし、自分の HDD などに保存した場合、そのファイルの更新日時は HDD に保存した日時になります。

ファイルのダウンロード

これをサーバー側で設定した日付にすることはできるでしょうか? ActiveX などのプラグインは使わないという条件でです。

結論から言えば、世間一般に使われている解凍ツールと互換性のある zip アーカイブを使えば可能です。

サーバーからは zip アーカイブに更新日時情報を含めて送信し、クライアント側で解凍して保存する時に zip アーカイブに含まれている更新日時に設定してもらうということです。

Web アプリケーションとしては、以下ような手順になります。

  1. データベースまたはファイルシステムからバイト列データを取り出す。
  2. 既存のライブラリを利用して zip アーカイブをバイト列として作成。更新日時やファイル名はその時設定。
  3. 作成したバイト列を Response.BinaryWrite メソッドで HTTP 出力ストリームに書き込み

参考に、フリーのライブラリ SharpZipLib を使った例をアップしておきます。実行するには Bin フォルダに ICSharpCode.SharpZipLib.dll を入れておく必要があります。下記のサイトから入手できます。

The Zip, GZip, BZip2 and Tar Implementation For .NET

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<%@ Import Namespace="System.Configuration" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="ICSharpCode.SharpZipLib.Zip" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
  protected void Page_Load(object sender, EventArgs e)
  {
    // 例として、SQL Server DB からデータを取得する
    // ことで考えています。
    ConnectionStringSettings cs =
      ConfigurationManager.ConnectionStrings["MyDB"];
    string connString = cs.ConnectionString;
    SqlConnection connection = new SqlConnection(connString);
    string query = 
      "SELECT filename, data FROM files WHERE id=@id";
    SqlCommand command = new SqlCommand(query, connection);
    command.Parameters.AddWithValue("@id", 
      Convert.ToInt32(Request.QueryString["id"]));
    string filename = null;
    byte[] data = null;
       
    try
    {
      connection.Open();
      SqlDataReader reader = command.ExecuteReader();

      if (reader.Read())
      {
        filename = (string)reader[0];
        data = (byte[])reader[1];
      }
    }
    finally
    {
      connection.Close();
    }

    if (filename == null || data == null)
    {
      // DB から取得できなかった場合の処置。
      // ここでは単に return
      return;
    }

    Response.AppendHeader("Content-Disposition",
      "attachment; filename=" + 
      HttpUtility.UrlEncode(filename) + ".zip");
    Response.ContentType = "application/x-zip-compressed";

    // 何故かバイト列のサイズを指定した方が圧縮率
    // が高くなる。圧縮前よりは小さくなるはずなの
    // で、とりあえず data.Length とする。
    byte[] zippedData = new byte[data.Length];
    int zippedDataLength = 0;
    using (MemoryStream memoryStream = 
      new MemoryStream(zippedData))
    {
      using (ZipOutputStream zipOutStream = 
        new ZipOutputStream(memoryStream))
      {
        // 圧縮度の設定。9 が最高
        zipOutStream.SetLevel(9);

        ZipEntry entry = new ZipEntry(filename);

        // ここで更新日時を設定
        entry.DateTime = new DateTime(2000, 1, 1);

        zipOutStream.PutNextEntry(entry);
        zipOutStream.Write(data, 0, data.Length);               
        zipOutStream.Finish();

        // 圧縮後のサイズを取得。Finish() の前に置く
        // のは NG。zipOutStream.Length は NG。
        zippedDataLength = (int)zipOutStream.Position;

        zipOutStream.Close();
      }
    }       
    Array.Resize(ref zippedData, zippedDataLength);
    Response.BinaryWrite(zippedData);
    Response.End();
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
  </div>
  </form>
</body>
</html>

そもそも、ファイル名、更新日時その他のメタデータはファイルシステムが保持しているもので、ファイル本体には含まれていません。

ファイル名のみは、ブラウザが自動的に attachment; filename= に指定された名前で保存してくれますが(日本語の文字化けの問題はありますが)、その他の情報はダメなようです。

Content-Disposition ヘッダーフィールドにファイル名、作成日時、更新日時等の情報を追加してクライアントに送ることは可能です。でもそれは送る手段が用意されているというだけで、ブラウザが自動的にそれに書き換えてくれることはありません。

Tags: , ,

Upload Download

Visual Studio 2010

by WebSurfer 2010年8月28日 13:50
Visual Studio 2010

世の中は Visual Studio 2010 への移行が進んでいるようですので、話題に乗り遅れないように、自分も開発環境に Visual Studio 2010 Professional を導入しました。

Web アプリしか作らないし、自分の使っているレンタルサーバーが .NET 3.5 にしか対応していないし、2008 でも何ら問題なく十分使えているし、ちょっと(かなり?)もったいないような気もしますけど。(笑)

まず既存の .NET 2.0, 3.5 ベースの Web アプリを開いて実行してみると、二重定義だとか宣言されてないとかのエラーが出てビルドできません。2008 よりチェックが厳しくなっているようです。でも、それはほんのわずかな部分で、そこを直したら、2008 と同様に使えるようになりました。

使い勝手は 2008 とほとんど同じようです。ただ、ヘルプはブラウザ (IE8) で表示されるようになっており、自分的には非常に使いづらいです。特に困るのが、2008 で使われている専用の Document Explorer にあるキーワードタブがないんです。これは慣れの問題でなんとかなることではなさそうです。

.NET 4.0 ベースで新しい Web サイトを作ると、どうなるか試してみました。デフォルトで、以下のようなフォルダ/ファイルが自動生成されます。

自動生成された Web サイト

うわさに聞いていた通り、jQuery が標準で実装されています。ASP.NET AJAX には見切りをつけて、jQuery に移行しようとしているという話を聞きましたが、本当でしょうか?

その他、Forms 認証に使う Login.aspx などのページ、マスターページ、CSS などが自動生成されます。CSS は空ではなく、Default.aspx を実行すると以下のようになります。至れり尽くせりというよりは、大きなお世話というべきか・・・(笑)

Default.aspx の実行画面

中でも一番気になっていた web.config ですが、以下のように .NET 3.5 の場合と比べて、ずいぶんと簡略化され、見通しがよくなっているようです。

<?xml version="1.0"?>

<!--
  ASP.NET アプリケーションを構成する方法の詳細については、
  http://go.microsoft.com/fwlink/?LinkId=169433 を参照してください
  -->

<configuration>
  <connectionStrings>
    <add name="ApplicationServices"
      connectionString=
       "data source=.\SQLEXPRESS;
        Integrated Security=SSPI;
        AttachDBFilename=|DataDirectory|\aspnetdb.mdf;
        User Instance=true"
      providerName="System.Data.SqlClient" />
  </connectionStrings>

  <system.web>
    <compilation debug="false" targetFramework="4.0" />

    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login.aspx" 
        timeout="2880" />
    </authentication>

    <membership>
      <providers>
        <clear/>
        <add name="AspNetSqlMembershipProvider" 
          type="System.Web.Security.SqlMembershipProvider" 
          connectionStringName="ApplicationServices"
          enablePasswordRetrieval="false" 
          enablePasswordReset="true" 
          requiresQuestionAndAnswer="false" 
          requiresUniqueEmail="false"
          maxInvalidPasswordAttempts="5" 
          minRequiredPasswordLength="6" 
          minRequiredNonalphanumericCharacters="0" 
          passwordAttemptWindow="10"
          applicationName="/" />
      </providers>
    </membership>

    <profile>
      <providers>
        <clear/>
        <add name="AspNetSqlProfileProvider" 
          type="System.Web.Profile.SqlProfileProvider" 
          connectionStringName="ApplicationServices" 
          applicationName="/"/>
      </providers>
    </profile>

    <roleManager enabled="false">
      <providers>
        <clear/>
        <add name="AspNetSqlRoleProvider" 
          type="System.Web.Security.SqlRoleProvider" 
          connectionStringName="ApplicationServices" 
          applicationName="/" />
        <add name="AspNetWindowsTokenRoleProvider" 
          type="System.Web.Security.WindowsTokenRoleProvider" 
          applicationName="/" />
      </providers>
    </roleManager>

  </system.web>

  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>

--------------- 2010/9/10 追記 ---------------

ヘルプが使いづらいと書きましたが、Viewer がありました。以下のサイトからダウンロードできます。

H3Viewer - Help Viewer for VS 2010

ちょっと使ってみた限りでは、Visual Studio 2008 のヘルプの使い勝手に近いものです。ちょっと動きが遅いような感じがしますが、Visual Studio 2010 のブラウザで表示するヘルプに比べれば、はるかに使いやすそうで、お勧めです。

Tags:

DevelopmentTools

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年5月  >>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar