2009年5月21日木曜日

セッション変数で嵌る。


セッション周りのコードを見ていたら、

1.クラスのインスタンスをセッション変数に格納していた。
2.セッション変数から、インスタンスを取り出し、セッション変数に再格納していた。

こんな感じ

CSession.Book cb1 = new CSession.Book();
cb1.Id=bookId;
cb1.Title=TxtBookTitle.Text;
cb1.Author=TxtAuther.Text;
:
Session["BOOK"]=cb1;


別の処理で、
CSession.Book cs = (CSession.Book)Session["BOOK"];
if(cs!=null)
{
cs.Id="...";
cs.Title="....";
cs.Author="...";
:
if(regFlg)
{
Session["BOOK"] = cs;
}
}

気になった点は、CSession.Bookのクラスがシリアル化可能かということと、
Session["BOOK"] = cs;の行。

ASP.NETの状態管理のサービスを立ち上げて、web.configで、
<sessionState mode="StateServer" />
と記述すると、セッション格納時に例外が発生する。

CSession.Bookのクラス定義の上に[Serializable()]をつけることにより、
例外が発生しなくなったが、問題はSession["BOOK"] = csの行。この行は
意味がないように見える。

直前のif文で条件分岐をしているんだけど、cs自体が参照のためcsのメンバー
に値を代入した時点で、セッションに格納したインスタンスのメンバーの値
も変わっていることをプログラム作成者が認識してるかな?

2009年5月20日水曜日

Application_Errorで嵌る。

今回のはまりはGlobal.asaxのApplication_Error()とエラーページについて、

最初の実装では、
Global.asax.csのApplication_Error()でこんな感じにして、

protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
Session["errormessage"] = ex.Message;
Server.Transfer("Error.aspx");
}

Error.aspx.csでは、こんな感じにしてみた。
protected void Page_Load(object sender, EventArgs e)
{
TextBox1.Text = (string)Session["errormessage"];
}

そうすると、なぜか、エラーページのメッセージにはいつもハンドルされていないエラーの旨のメッセージしかでなかった。
なぜかはわからないが、とりあえず、Server.GetLastError()で取得したエラーについて、innerErrorが存在する場合はinnerErrorを発生した例外として扱ってみた。

で、うまくいくように見えたのだけれど、nopage.aspx(存在しないページ)にアクセスすると、Application_Error()の処理のSessionに値を詰め込むところでエラーが発生した。
多分、aspxページが存在しないとSessionが扱える状態になる前にApplication_Errorが呼ばれたからじゃないかな?
というわけで、Application_Errorを以下のような感じに、
Server.Transfer("Error.aspx?errormessage="+ HttpUtility.UrlEncode(ex.Message));

Error.aspx.csのPage_Loadでは以下のような感じにしてみた。
TextBox1.Text = HttpUtility.UrlDecode( Request["errormessage"]);


そうすると、存在しないページにアクセスしたときも意図通りの動作になったが、、、

次は、@Pageディレクティブで、ValidateRequest="true"のページで、<a>などの入力をしてみると、Error.aspx(このページは@PageディレクティブにValidateRequest="false"としているにもかかわらず)の処理で、Request["errormessage"]のタイミングでも、ValidateRequestの例外が発生する。
多分、Server.Transfer()で行っているので、Error.aspxの処理の前の前段パイプライン処理が走っていないから、Error.aspxのページディレクティブを参照していなくて、元ページのValidateRequestの値が引き続き使われているからだろうなぁと想像しますが、かといってResponse.Redirect()にして、URLをさらすわけにもいかず、、、結局web.configでValidateRequest="false"にしてしまって様子見。。。はぁ。

2009年5月10日日曜日

MessageBoxのアイコン

MessageBoxのアイコンの指定を調べるために、こんなサンプルを作ってみました。
(ヘルプは、今回は使わないから調べませんでした。)
で、WindowsVistaHomePremiumで動かしてみてわかったこと。

・MessageBoxで×ボタンが表示される場合は、以下のボタン表示のときで、×ボタンが押されたら、
一番最後のボタンが押されたものと同じ。
OK
OKCancel
YesNoCancel

・×ボタンが表示されているときに限りEscキーで戻ることができ、その場合、戻り値が×ボタンを押したときと同じ。

・オプション指定が、DefaultDesktopOnlyやServiceNotificationの場合、MessageBox終了後、呼び出し元のウィンドウにはフォーカスが戻らない。

・アイコン指定は、Error,Hand,Stopは同じ値が振られているため同じ動きをしており、ExclamationとWarningも同じ、InformationとAsteriskも同じ。そもそもMessageBoxIcon.Error.ToString()や、MessageBoxIcon.Stop.ToString()をやっても"Hand"が返ってくる。(MessageBoxIcon.Exclamation.ToString()は"Warning",MessageBox.Information.ToString()は"Asterisk")

・(使うことはないが)オプション指定が、RtlReadingの場合、以下のように表示がわけがわからない。
 1.タイトルは右寄せ、表示文字はそのままの順(逆順に表示されることはない。)
 2.ウィンドウ内のアイコンは、(通常の左ではなく)右に表示される。
 3.ウィンドウ内の文字は、左寄せで表示される。表示文字はそのままの順。
 4.ボタンは、順序が逆転し()の変化が微妙でYesNoCancelを選んだ場合は以下のようになる。
   4-1:通常時:はい(Y),いいえ(N),キャンセル
   4.2:RtlReading選択時:キャンセル,(Nいいえ(,(Yはい(
 5.×ボタンが左に来ている。
ServiceNotificationを選んだ場合、


RtlReadingを選んだ場合、


なおサンプルに使ったデザインとプログラムは、

デザインはこんな感じ。



プログラムはこんな感じ。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();


foreach (MessageBoxButtons b in Enum.GetValues(typeof(MessageBoxButtons))) listBox1.Items.Add(b);
foreach (string s in Enum.GetNames(typeof(MessageBoxIcon))) listBox2.Items.Add(s);
foreach (MessageBoxOptions o in Enum.GetValues(typeof(MessageBoxOptions))) listBox3.Items.Add(o);

listBox1.SelectedIndex = 0;
listBox2.SelectedIndex = 0;
listBox3.SelectedIndex = 0;
}

private void button1_Click(object sender, EventArgs e)
{


MessageBoxButtons b = ((MessageBoxButtons)listBox1.SelectedItem);
MessageBoxIcon i = ((MessageBoxIcon)new EnumConverter(typeof(MessageBoxIcon)).ConvertFromString(listBox2.SelectedItem.ToString()));
MessageBoxOptions o = ((MessageBoxOptions)listBox3.SelectedItem);

label1.Text = MessageBox.Show(
b.ToString()+Environment.NewLine+i.ToString()+Environment.NewLine+o.ToString(),
"MessageBoxテスト",b,i,MessageBoxDefaultButton.Button1,o).ToString();
}
}
}

C#のbase.base

C#のbase.base....について調べてみました。
結局はできないみたい。
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
D d = new D();
C c = new C();
B b = c;
A a = c;

string s01 = a.f(); //="C"
string s02 = b.f(); //="C"
string s03 = c.f(); //="C"

string s04 = ((B)b).f(); //="C" キャストを明示してもダメ
string s05 = ((A)a).f(); //="C" 同上
string s06 = (c as B).f(); //="C" このキャストもダメ
string s07 = d.f(); //="C" base.f()はできるけどbase.base.f()は書けない

D1 d1 = new D1();
C1 c1 = new C1();
B1 b1 = c1;
a = c1;
string s11 = a.f(); //=A" この時点で期待外れ
string s12 = b1.f();//="B1" 以下はどうでもいいや。
string s13 = c1.f();//="C1"
string s14 = ((B1)a).f();//="B1"
string s15 = ((C1)a).f();//="C1"
string s16 = ((A)b1).f();//="A"
string s17 = d1.f(); //="C1"

C21 c21 = new C21();
C22 c22 = new C22();

string s21 = c21.f(); //="A2" 期待どおりだがbaseクラス作成時にはをこんな風には作らない...
string s22 = c22.f(); //="B2"

a = c;
b = c;
string s31 = a.g();//="C"拡張メソッド内ではbaseは使えない...
string s32 = b.g();//同上

//型変換エラーが発生するためコメントアウト
//A aa = ((A)Convert.ChangeType(c, Type.GetType("ConsoleApplication3.A")));

string[] strl = { "aa", "bb" };
}

}

//オーバーライド
class A { public virtual string f() { return "A"; } }
class B : A { public override string f() { return "B"; } }
class C : B { public override string f() { return "C"; } }
class D : C { public override string f() { return base.f(); } }

//ある意味オーバーロード?
class B1 : A { public new string f() { return "B1"; } }
class C1 : B1 { public new string f() { return "C1"; } }
class D1 : C1 { public new string f() { return base.f(); } }

//ベースの呼び出しを継承クラスから呼び出せるようにしたもの
//ただA2を作る時点でこんなつくりにすることは考えられないなあ
class A2
{
public virtual string f() { return A2f(); }
protected string A2f() { return "A2"; }
}
class B2 : A2 { public override string f() { return "B2"; } }
class C21 : B2 { public override string f() { return A2f(); } }
class C22 : B2 { public override string f() { return base.f(); } }

//C#3.0からの拡張メソッド g
internal static class ClassUtility { internal static string g(this A a) { return a.f(); } }

}

久しぶりの.netメモ form開発の取り決め

今回windowsformを使った開発がありました。
んで、そのときのメモ。
windowsアプリケーションでは、1画面ごと、Formを使って作るみたい。
一番最初に、
program.csのMain()からApplication.Run(new Form1())が呼ばれて、Form1の処理が始まる。

Mainで、
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler( f);
:

public static void f(object sender, System.Threading.ThreadExceptionEventArgs e)
{
}
で、asp.netのglobal.asaxのerrと同じようなことができる。
ただ、このイベントは、Thread中の例外を捕捉するものなので、Form1のコンストラクタ処理中に発生したの例外は(Run評価前でスレッド開始前なので)これでは取れないので、try/catchしないといけない。

次画面遷移については、親画面(Form1)で、子画面(Form2)をnew→form2.Ownerにthis(つまりform1自身)をセット→form2の引継ぎプロパティをセット→、form2.Show(),form1.Hide()を行って次画面遷移。ダイアログ表示は、form2.ShowDialog()で、戻り値によるダイアログ処理結果取得。

情報共有については、static変数(Application共通の場合のみ)
親画面(Form1)から子画面(Form2)への情報受け渡しは、
1.Owner.親画面のプロパティ
2.親画面でセットされた自画面のプロパティ
3.子画面のコンストラクタを呼ばれたときの引数
このうち1,2は、コンストラクタ生成→子画面のプロパティセット(Ownerプロパティ含む)→Showなので、子画面のコンストラクタやloadでは親画面のプロパティセット前のためプロパティアクセスは不可。
でも、いまのコーディングルール上は、Showをする場合は、2.の子画面側で、自分の画面のプロパティを参照することと、ShowDialogは親画面(Owner)のプロパティを参照することとなっている。

子画面→親画面への引渡し方法は、
1.子画面の処理中に行う親画面へのプロパティセット、
2.ShowDialog()の戻り値(DialogResult)

で、Formの削除、生成のタイミングは、
次画面へ遷移する場合、親画面側で、newすることとなっている。
戻る場合は、子画面側から親画面のShowを行い、自画面をdisposeするとなっている。(親画面側からしかdisposeが利かないような気がするが。。)

2008年9月14日日曜日

またまた本購入

今日は、”プログラミングMicrosoft ASP.NET 3.5”っていう本。約1万円。
仕事では使わないんだけど、買っちゃいました。
それにしても、コンピュータの本ってちょっと高すぎないかなぁ?

2008年8月31日日曜日

衝動買いしたものが届いた。

DSで操作するシンセサイザ。
何で買っちゃったんだろう。

アナログシンセサイザーなんて見たことないのに。

その昔、X1というマシンにFM音源ボードをつけて、Oh!XやベーマガのMMLをつかって、音楽を鳴らしていた覚えはあるけど、そのときは活用テクニックという本(楽器名と似た音を作り出すパラメータのシートがいっぱいあるような本)とかから、微調整して音色を作ってたけれど、、、、、どうやらアナログシンセっていうのは、感覚が違うようです。

たぶん、音を自在に作れる前に飽きちゃうんだろうなぁ。