<?xml version="1.0" encoding="UTF-8" ?><rdf:RDF 
  xmlns="http://purl.org/rss/1.0/"
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xml:lang="ja">
  <channel rdf:about="http://www28.atwiki.jp/jennychan/">
    <title>メモwiki (主にコンピュータ関連)</title>
    <link>http://www28.atwiki.jp/jennychan/</link>
    <description>メモwiki (主にコンピュータ関連)</description>

    <dc:language>ja</dc:language>
    <dc:date>2009-02-09T20:16:48+09:00</dc:date>

    <items>
      <rdf:Seq>
                <rdf:li rdf:resource="http://www28.atwiki.jp/jennychan/pages/21.html" />
                <rdf:li rdf:resource="http://www28.atwiki.jp/jennychan/pages/9.html" />
                <rdf:li rdf:resource="http://www28.atwiki.jp/jennychan/pages/17.html" />
                <rdf:li rdf:resource="http://www28.atwiki.jp/jennychan/pages/2.html" />
                <rdf:li rdf:resource="http://www28.atwiki.jp/jennychan/pages/7.html" />
                <rdf:li rdf:resource="http://www28.atwiki.jp/jennychan/pages/5.html" />
                <rdf:li rdf:resource="http://www28.atwiki.jp/jennychan/pages/8.html" />
                <rdf:li rdf:resource="http://www28.atwiki.jp/jennychan/pages/20.html" />
                <rdf:li rdf:resource="http://www28.atwiki.jp/jennychan/pages/12.html" />
                <rdf:li rdf:resource="http://www28.atwiki.jp/jennychan/pages/19.html" />
              </rdf:Seq>
    </items>
	
		
    
  </channel>
    <item rdf:about="http://www28.atwiki.jp/jennychan/pages/21.html">
    <title>トップページ/コメントログ</title>
    <link>http://www28.atwiki.jp/jennychan/pages/21.html</link>
    <description>
      - 興味深く、RD-X4のHDD解析読ませてもらいました。当方所持のX4のHDDがよく飛ぶため初期化を良くするのですが、撮りためた番組が死ぬので困ってました。DVDもWD3002は不良品らしく、今度自力でDVDを交換するのでできれば、HDDを初期化する前に、データを吸い出したいため本HPにたどり着いた次第です。貴殿が作成されている抽出用のプログラムって公開してないですよね？もし問題なければいただければ嬉しいのですが・・。ご無理を承知で一応書かせていただきました。どちらにしろ、記事を参考にさせていただきます。 setura@ne92.jp   --  (yan)  &amp;size(80%){2008-02-06 17:09:36} 
- DVDドライブ型番間違えてました。訂正します。SD-W3002でした(_ _)   --  (yan)  &amp;size(80%){2008-02-06 17:11:35} 
- yanさんこんにちは。抽出用のプログラム公開ですか…。抽出をするプログラムはあることはあるのですが、汎用的にしようとするとUIが複雑になるので、抽出する度にソースコードを書き換えていたのです。いずれ公開しようとは思っていたのですが、いつの間にか一年経ってしまいました(苦笑)。今すぐ公開は厳しいですが、何とかしたいとは考えています。ちなみに、この、抽出プログラムをC#で作成したので、「C#メモ」があったりするのですが…。   --  (じぇにぃ)  &amp;size(80%){2008-02-13 19:11:46} 
- 同じく私もＲＤの解析を読ませていただいております。私自身まだＨＤＤＲｅｃは持ってないのですが、知り合いの物が再生・録画ともに不可となったことから情報を集めていました。大変参考になりますが、内容にまだついていけておりません。ダンプを見ながら何日も経過しています。。。   --  (eGu)  &amp;size(80%){2008-02-15 12:54:57} 
- すみません改行してしまいました。ダンプをみるとやはり奇数偶数バイトが入れ替わっていますよね。0000BA01で途中から50ＭＢ程切出した物を単純に２バイトでスワップしても再生が出来ません。動画データだけ抜きたいのですがそんな単純にはいかないんでしょうか。。。   --  (eGu)  &amp;size(80%){2008-02-15 12:59:11} 
- eGuさんこんにちは。バイトスワップを調整出来ても、録画、削除を繰り返す事により、ストリームデータがHDD上で不連続に記録されていくので、Allocation Descriptorなどを解決しないと、再生出来るストリームを抜き出す事は出来ません。   --  (じぇにぃ)  &amp;size(80%){2008-02-18 20:38:43} 
- DVD-RAMのIFOファイルの 0104 04 　「不明1」ブロックの開始位置 はユーザ・定義用プログラムチェイン情報テーブル（ＵＤ＿ＰＧＣＩＴ）と思われます   --  (名無しさん)  &amp;size(80%){2009-02-09 19:48:37} 
- ＶＲ＿ＭＡＮＧＲ．ＩＦＯファイルの論理構造  ディスク管理情報（ＲＴＲ＿ＶＭＧＩ）　 動画ファイル情報（Ｍ＿ＡＶＦＩＴ） 静止画ファイル情報（Ｓ＿ＡＶＦＩＴ） オリジナル・プログラム用プログラムチェイン情報（ＯＰＧ＿ＰＧＣＩ） ユーザ・定義用プログラムチェイン情報テーブル　（ＵＤ＿ＰＧＣＩＴ） テキストデータマネージャ（ＴＸＴＤＴ＿ＭＧ） 製造業者情報テーブル情報（ＭＮＦＩＴ）   --  (名無しさん)  &amp;size(80%){2009-02-09 20:16:48}     </description>
    <dc:date>2009-02-09T20:16:48+09:00</dc:date>
  </item>
    <item rdf:about="http://www28.atwiki.jp/jennychan/pages/9.html">
    <title>.NET固有の機能</title>
    <link>http://www28.atwiki.jp/jennychan/pages/9.html</link>
    <description>
      .NET Framework固有のTipsです。


**アプリケーションの設定値

アプリケーション/ユーザごとの設定を簡単に扱える。
[[マイクロソフト情報(C# で設定を使用する)&gt;http://www.microsoft.com/japan/msdn/vs05/vcsharp/SettingsCS_RL.aspx]]

***要約
-ugingに、「System.Configuration」を追加
 using System.Configuration;
-プロジェクトの設定(ソリューションエクスプローラで、プロジェクトを右クリック→プロパティ→設定タブクリック)で、設定値のプロパティを作成する。この時に既定値も指定できる。
-データの取得は、Properties.Settings.Default.プロパティ名
 string s = Properties.Settings.Default.test1;
-データの保存は、
 Properties.Settings.Default.test1 = &quot;hoge&quot;;
 Properties.Settings.Default.Save();
※Saveメソッドを実行しないと保存されない。
-設定ファイルの位置は、「Documents and Settings\&lt;ユーザ名&gt;\Local Settings\Application Data\&lt;会社名&gt;\&lt;アセンブリ名_ハッシュ値&gt;\&lt;アセンブリバージョン&gt;\user.config」となるが、会社名を指定しなかった場合は、アセンブリ名が使われる。


**DBアクセス(ADO.NET 2.0)

様々なデータベースに対し、統一的に処理を行うために、冗長な手順を踏む必要がある。そのため、どうしても、とっつきにくい構造になってしまっている。
ただし、JDBCも同じような構造のプログラムになっているので、一つ覚えてしまえば、つぶしがが効くとも言える。
接続情報などは、コードに直接記述するのではなく、アプリケーションの設定値を利用するのが望ましい。
小規模なデータで、リレーションを行わないような場合は、DBを使うよりも、XMLを使用して、XPathで検索したほうが手軽な気がする。

***使用するクラス
-DbProviderFactories
システムに登録されているDbProviderFactoryインスタンスを管理しているクラス
-DbProviderFactory
プロバイダ情報を格納し、下記クラスのインスタンスを作成するためのクラス
JDBCのjava.sql.DriverManagerみないなもの??
-DbConnection
データベースへの接続状態を表すクラス
DbProviderFactoryから取得する
JDBCのjava.sql.Connectionに相当する
-DbCommand
DBに対して実行する命令(SQL文など)を表すクラス
DbProviderFactoryから取得する
JDBCのjava.sql.PreparedStatementに相当する
-DbDataReader
DBに対する問い合わせ結果を表すクラス
DbCommand.ExceuteQueryから取得する
JDBCのjava.sql.ResultSetに相当する
-DbDataAdapter
DBに対する命令、接続、結果データの格納を表すクラス
DbCommandとDataSetの橋渡し役
-DataSet
メモリ上に展開された、データを階層的に管理するクラス
DBの問い合わせ結果などのデータを格納(データはDbDataAdapterから得られる)し、問い合わせ処理に問題が無ければ、1つ以上のDataTableクラスオブジェクトを持つ事になる
なお、問い合わせ結果が1万行あれば、1万行分のデータをメモリに展開する
-DataTable
一つのテーブルに対するデータを格納するクラス
-DataColumn
列情報を格納するクラス
-DataColumnCollection
テーブル内の全ての列情報を格納するクラス
DataColumnのコレクション
-DataRow
行情報を格納するクラス
-DataRowCollection
テーブル内の全ての行情報を格納するクラス
DataRowのコレクション


***流れ(問い合わせの場合)
 // DbProviderFactoryのインスタンスを作成
 DbProviderFactory dbProvider = DbProviderFactories.GetFactory(&quot;System.Data.OleDb&quot;);
 
 // DbProviderFactoryのインスタンスからDbConnectionを取得
 DbConnection dbCon = dbProvider.CreateConnection();
 
 // DbConnectionに、ConnectionStringを設定し、Openする
 // DBファイルはtest.mdb(Microsoft Access MDB)
 dbCon.ConnectionString = &quot;Provider=Microsoft.Jet.OLEDB.4.0;Data source=test.mdb&quot;;
 dbCon.Open();
 // この時点で、DBとの接続が完了
 
 // DbProviderFactoryのインスタンスからDbCommandを取得
 DbCommand dbCmd = dbProvider.CreateCommand();
 
 // DbCommandに、接続情報(DbConnection)をセット
 dbCmd.Connection = dbCon;
 
 // DbCommandに、問い合わせ文をセット(サンプルなので適当なselect文)
 dbCmd.CommandText = &quot;select * from foo&quot;;
 
 // DbDataAdapterを取得
 DbDataAdapter dbAdap = dbProvider.CreateDataAdapter();
 
 // DbDataAdapterに、DbCommandを結びつける
 dbAdap.SelectCommand = dbCmd;
 
 // 問い合わせ結果を受け取るための、DataSetクラスのインスタンスを作成する
 DataSet ds = new DataSet();
 
 // 問い合わせ結果を取得する
 dbAdap.Fill(ds);
 
 // DataSetは複数のテーブルに対するデータが保存できるので、
 // 1つ以上のテーブルデータが保存されているか確認する
 if (ds.Tables.Count &gt; 0)
 {
     // 最初のテーブル情報を利用する
     DataTable dt = ds.Tables[0];
 
     // 問い合わせ結果が1行以上存在するか確認する
     DataRowCollection rows = dt.Rows;
     if (rows.Count &gt; 0)
     {
         // 問い合わせ結果の最初の行の、最初の列の値を出力する
         System.Console.WriteLine(rows[0][0]);
         // 問い合わせ結果の最初の行の、列名が「FIELD1」の列の値を出力する
         System.Console.WriteLine(rows[0][&quot;FIELD1&quot;]);
     }
 }
 
 // 接続を切断する
 dbCon.Close();

ということらしい…


***DataSetとDbDataReader
-DataSetは、全ての問い合わせ結果をメモリ上にコレクション的に格納する
-DbDataReaderは、データベースのカーソル的な動作を行う

上記のDataSetを使用した問い合わせ処理
 // DbCommandに、問い合わせ文をセット(サンプルなので適当なselect文)
 dbCmd.CommandText = &quot;select * from foo&quot;;
 
 // DbDataAdapterを取得
 DbDataAdapter dbAdap = dbProvider.CreateDataAdapter();
 
 // DbDataAdapterに、DbCommandを結びつける
 dbAdap.SelectCommand = dbCmd;
 
 // 問い合わせ結果を受け取るための、DataSetクラスのインスタンスを作成する
 DataSet ds = new DataSet();
 
 // 問い合わせ結果を取得する
 dbAdap.Fill(ds);
をDbDataReaderを使用すると
 // DbCommandに、問い合わせ文をセット(サンプルなので適当なselect文)
 dbCmd.CommandText = &quot;select * from foo&quot;;
 
 // 問い合わせ結果を取得する
 DbDataReader dbReader = cmd.ExecuteReader();
 
 // 一行ずつ処理を行う
 do
 {
     while (dbReader.Read())
     {
         // 列情報を取得する
         //  列名では取得できない?
         //  実際の型と異なる形式で取得できない?
         int f1 = dbReader.GetInt32(0);
         string f2 = dbReader.GetString(1);
         string f3 = dbReader.GetString(2);
         ・・・
     }
 } while (dbReader.NextResult());
 
 // DbDataReaderを使い終わったらCloseする
 dbReader.Close();
となる。


***挿入、更新、削除などのNonQuery処理
DbCommand.ExceuteNonQueryでSQL文の実行が行える。
 // DbCommandに、実行したいSQL文をセット(サンプルなので適当なupdate文)
 dbCmd.CommandText = &quot;update foo set update=&#039;2007-01-02&#039; where id=1&quot;;
 
 // SQL文の実行
 cmd.ExecuteNonQuery();


***パラメータ化されたSQL文
java.sql.PreparedStatementの様に、SQL文中でパラメータを使用する事が可能となる。
 // DbCommandに、実行したいSQL文をセット(サンプルなので適当なupdate文)
 // パラメータマーカーは、DBに依存するそうだが、OLE DBでも、@idの様な記述で問題なく動く
 dbCmd.CommandText = &quot;update foo set update=@update where id=@id&quot;;
 
 // パラメータの情報を設定
 //  DBに依存しないコードは記述できない???
 //   OLD DBの場合、パラメータを名前で判断していないので、
 //   パラメータの出現順とAddメソッドが同期していなければいけない
 //   また、実際のパラメータ名と異なる名前を使用してもエラーにならず、正しく処理される
 dbCmd.Parameters.Add(new OleDbParameter(&quot;@update&quot;, OleDbType.Date));
 dbCmd.Parameters.Add(new OleDbParameter(&quot;@id&quot;, OleDbType.Numeric));
 
 // パラメータに値を設定
 //  dbCmd.Parameters[0].Value = &#039;2007-01-02&#039; としてもよいが、パラメータ名を使用して値を設定できる
 dbCmd.Parameters[&quot;@update&quot;].Value = &#039;2007-01-02&#039;;
 dbCmd.Parameters[&quot;@id&quot;].Value = 1;
 
 // SQL文の実行
 cmd.ExecuteNonQuery();
ADO.NET 2.0でOLE DB、Oracle、SqlServerなど、個々のDBに依存したクラスを使用せずに、DbXxxxクラスでコードが記述できると言っているが、パラメータの型指定などでは、使用するDBに依存するコードとなってしまうらしい。

また、複数の行に対する処理の場合、
 // DbCommandに、実行したいSQL文をセット(サンプルなので適当なupdate文)
 dbCmd.CommandText = &quot;update foo set update=@update where id=@id&quot;;
 
 // パラメータの情報を設定
 dbCmd.Parameters.Add(new OleDbParameter(&quot;@update&quot;, OleDbType.Date));
 dbCmd.Parameters.Add(new OleDbParameter(&quot;@id&quot;, OleDbType.Numeric));
 
 // パラメータに値を設定
 dbCmd.Parameters[&quot;@update&quot;].Value = &#039;2007-01-02&#039;;
 dbCmd.Parameters[&quot;@id&quot;].Value = 1;
 
 // SQL文の実行
 cmd.ExecuteNonQuery();
 
 // 次の処理のためのパラメータに値を設定
 //  直前の処理と同じSQL文であれば、パラメータ情報を設定しなおす必要は無い
 dbCmd.Parameters[&quot;@update&quot;].Value = DateTime.Now;
 dbCmd.Parameters[&quot;@id&quot;].Value = 2;
 
 // SQL文の実行
 cmd.ExecuteNonQuery();
とすることも出来る
日付型のパラメータと定義してあれば、DataTime型のデータを設定しても大丈夫らしい。


**DataGridView

.NET Framework version 2.0から、DataGridコントロールに代わるコントロールとして追加されたもので、DataSourceにデータオブジェクトを指定し、データの表示、編集作業を行う。
DataSourceには、
-Visual StudioのGUI操作で作成したデータソースオブジェクト
-DataSetオブジェクト
-DataTableオブジェクト
が使われる。
データの参照であれば、Visual StudioのGUI操作(データソース構成ウィザード)だけで大抵の処理が実現できてしまう。
Microsoftとしては、データの編集作業も含めて、Visual StudioのGUI操作だけで出来ると主張しているが、一般的なアプリケーションであれば、データの追加、修正作業を行う際には、データの内容、更新確認などの確認処理を経てから実際に更新処理を行うはずなのだが、これらのUI処理まで面倒見てはくれていない。

DataGridViewのDataSourceにDataSetを指定する際には、DataSet内に一つしかテーブルが存在しない状態であっても、「どのテーブルを表示させるのか」という指定が必要になる。
 // DataGridViewに表示したいテーブル名
 string sMemberName = &quot;foo&quot;;
 
 // 問い合わせ結果を受け取るための、DataSetクラスのインスタンスを作成する
 DataSet ds = new DataSet();
 
 // 問い合わせ結果を取得する
 dbAdap.Fill(ds, sMemberName);
 
 // 対象となるテーブルオブジェクトの存在確認
 if (ds.Tables.IndexOf(sMemberName) &gt;= 0)
 {
     // DataGridViewのDataSourceとDataSetを結びつける
     dataGridView1.DataSource = ds;
 
     // DataMemberプロパティが設定されていないと表示されない
     dataGridView1.DataMember = sMemberName;
 }

DataGridViewのDataSourceにDataTableを指定する場合は、
 // DataGridViewに表示したいテーブル名
 string sMemberName = &quot;foo&quot;;
 
 // 問い合わせ結果を受け取るための、DataSetクラスのインスタンスを作成する
 DataSet ds = new DataSet();
 
 // 問い合わせ結果を取得する
 dbAdap.Fill(ds, sMemberName);
 
 // 対象となるテーブルオブジェクトの存在確認
 if (ds.Tables.IndexOf(sMemberName) &gt;= 0)
 {
     // DataGridViewのDataSourceとDataTableを結びつける
     dataGridView1.DataSource = ds.Tables[sMemberName];
 }
となる。

さらに、DataAdapterのFillメソッドには、DataTableを指定できるので
 // 問い合わせ結果を受け取るための、DataTableクラスのインスタンスを作成する
 DataTable dt = new DataTable();
 
 // 問い合わせ結果を取得する
 dbAdap.Fill(dt);
 
 // DataGridViewのDataSourceとDataTableを結びつける
 dataGridView1.DataSource = dt;
と出来る。



**データソース

Visual Studio プロジェクトのデータソースとは、アプリケーションから利用可能な
-データベース
-オブジェクト
-Webサービス
などのデータを指し、型指定されたDataSetとTableAdapterが用意される。

型指定されたDataSetを使用すると、
 ds.Tables[&quot;foo&quot;].rows[0][&quot;FIELD1&quot;];
と記述していたコードが
 ds.foo[0].FIELD1;
となる。
フィールド値のNULLチェック(DBNull判定)は、「Is + 列名 + Null」形式のメソッドで判定する。
 if ( !ds.foo[0].IsFIELD1Null() )
     f1 = ds.foo[0].FIELD1;
コレクションのキー部分がプロパティ化されるわけだが、プロパティ化されたということは、単なるObject型データの連想配列として管理していた従来のDataSetと異なり、型が定義されているので、数値列に対して文字列を指定した様な場合、コンパイル時にエラーが出るのでコードの信頼性が高まる。

TableAdapterは、DataAdapterに型指定を追加したバージョン。
このTableAdapterオブジェクトには、挿入、削除、更新、問い合わせなどの機能をGUI操作により作成できる。
パラメータ付クエリは、「TableAdapter クエリの構成ウィザード」を使用して対話的に作成する事が出来、「クエリ ビルダ」ダイアログの「フィルタ」セルにパラメータを指定することで、=条件だけでなく、like条件なども定義できるが、パラメータ名については、使用するDBに依存したものになる。
MSDNのドキュメントを始め、大抵のサンプルでは、フィルタ名として「@param」などの、「@」を冠した名前が使われているが、これは、SQL Serverのパラメータマーカーで、OLE DBの場合は、「?」以外はパラメータとして認識されない。
DbCommandクラスでも、パラメータマーカーはDB依存度が低くなっているのに、TableAdapterでは、DB依存度が高い。

また、DataGridViewとデータソースを結び付けている場合、既定の処理では、FormLoadのイベント処理にデータソースからデータを取得するコードが自動的に追加されるのだが、データソースが存在していない場合、当然、例外が発生する。
自動的に追加されるコードは、try～catchで囲まれた形で追加されるわけでもなく、また、接続文字列変更や、再接続のためのサポートメソッドも見当たらない。
接続文字列プロパティは、読み取り専用なので直接XMLファイルを書き換えないと変更できないらしい。開発環境と、実稼働環境の接続設定が異なる場合に面倒な事になる。
まともなプロジェクトでは、開発時には実稼働環境を使うことはしないと思うので、この点の使い勝手は悪いとしか言いようが無いが、TableAdapterインスタンスのConnectionプロパティのConnectionStringを再設定するとうまく動いてくれるので、アプリケーションの最初で、接続文字列を設定するコードを記述しろという事か?


**データベース作成

ADOX.CatalogClassを使用する事でデータベースを作成できるらしい。
ただし、データベースによっては作成できないものもあると思われる。
ADOX namespaceは、「Microsoft ADO Ext. 2.x for DDL and Security」に含まれているらしい。

ADOX.CatalogClassクラスのCreateメソッドの引数に、データベースへの接続文字列を指定することで作成する。
 string sConnectionString = &quot;Provider=Microsoft.Jet.OLEDB.4.0;Data source=test.mdb&quot;;
 ADOX.CatalogClass cat = new ADOX.CatalogClass();
 cat.Create(sConnectionString );


**XML

従来からInternet Explorerの一部として配布されていたMSXMLがあったが、.NET FrameworkのXMLクラスは、様々な点で使い勝手が良くなっている。

-C言語から使用する場合、文字列をBSTRなどの形にしないといけなかったが、C#言語の場合は、メモリ管理は自動だし、始めからUnicode対応なので、そのまま扱える
-何も指定しなくても、改行やインデントされた状態でファイルへ出力できる
-ほぼCOMインターフェース直接アクセスに近いMSXMLと違い、自動でメモリ管理をしてくれるので、Nodeの参照カウンタ管理などを気にせずに使える
など

ところで、.NET FrameworkのXMLクラスは、ファイルの先頭がXML宣言(&lt;?xml…)でないとエラーになるらしい。コメントであっても。
MS-XMLでは問題なかったのだが…。

また、二つのXMLオブジェクトの結合を行う場合、自身の子ノードでないノードをAppendChildなどで追加させる事になるのだが、自分がCreateしたノード以外のノードを追加する事が出来ないので、ImportNodeメソッドでコピーを作成した後にノードを追加する。
 XmlDocument doc1 = new XmlDocument();
 XmlDocument doc2 = new XmlDocument();
 
 doc1.Load(&quot;test1.xml&quot;);
 doc2.Load(&quot;test2.xml&quot;);
 
 XmlNodeList lst = doc1.SelectNodes(&quot;/foo/*&quot;);
 XmlNode parent = doc2.SelectSingleNode(&quot;/foo&quot;);
 
 foreach (XmlNode node in lst)
 {
     parent.AppendChild(doc2.ImportNode(node, true));
 }


**LINQ

LINQ(Language Integrated Query: 統合言語クエリ)とは、.Net Framework 3.5から導入された、プログラム言語に統合されたクエリ機能。

一般的な問い合わせであれば、LINQだけで記述できるらしいので、複数のデータベースシステムへの対応が容易になる(可能性がある)。
また、プログラム言語の構文の一つと言う事から、表の列と、変数の比較処理も記述できる。

今までは、存在しない表、列を使用したSQL文を記述しても、実行時エラーが発生することはあっても、コンパイルエラーが発生することは無かった。
しかし、プログラム言語に統合される事により、存在しない表、列を指定した問い合わせ処理に対して、コンパイル時にエラーを検出する事が可能となる。
また、型指定されたDataSetと同様、列の型も管理しているので、数値列に対して文字列との比較、代入を行うような処理に対してもコンパイルエラーが発生する。

マイクロソフトのサイトでは、DataContextの作成は
 // DataContext で接続文字列を取得します。
 DataContext db = new DataContext(&quot;c:\\northwind\\northwnd.mdf&quot;);
と記述されているが、これは、SQL Serverでしか有効ではない。
つまり、AccessのMDBファイルを使用したいときなどは、この様な記述では例外が発生し正しく動作しない。
SQL Server以外のDBに対してDataContextを作成する場合は、IDbConnectionを引数にしたコンストラクタを使用することで可能となる。
 // DbProviderFactoryのインスタンスを作成
 DbProviderFactory dbProvider = DbProviderFactories.GetFactory (&quot;System.Data.OleDb&quot;);
 
 // DbProviderFactoryのインスタンスからDbConnectionを取得
 DbConnection dbCon = dbProvider.CreateConnection();
 
 // DbConnectionに、ConnectionStringを設定し、Openする
 // DBファイルはtest.mdb(Microsoft Access MDB)
 dbCon.ConnectionString = &quot;Provider=Microsoft.Jet.OLEDB.4.0;Data source=test.mdb&quot;;
 
 // DbConnectionのオブジェクトから、DataContextを作成する
 DataContext db = new DataContext(dbCon);

※こんな面倒な事をしなくても、
 // 接続文字列
 string sCon = &quot;Provider=Microsoft.Jet.OLEDB.4.0;Data source=test.mdb&quot;;
 
 // System.Data.OleDb.OleDbConnectionのインスタンスを作成
 DbConnection dbCon = new System.Data.OleDb.OleDbConnection(sCon);
 
 // DbConnectionのオブジェクトから、DataContextを作成する
 DataContext db = new DataContext(dbCon);
で出来る。

LINQは、.Net Frameworkで扱えるオブジェクトに対する「汎用クエリ機能」なので、データベースやXML以外にも適用できる。
たとえば、以下の様なちょっと複雑なソート処理は
 private static int Compare(int x, int y)
 {
     int result = (x % 3) - (y % 3);
     if (result == 0)
     {
         result = y - x;
     }
     return result;
 }
 
 private void Test()
 {
     int[] numbers = { 1, 3, 5, 7, 9, 2, 4, 6, 8 };
     Array.Sort(numbers, Compare);
     foreach (var i in numbers)
     {
         Console.Write(&quot;{0} &quot;, i);
     }
     Console.WriteLine();
 }
LINQを使うと
 private void Test()
 {
     int[] numbers = { 1, 3, 5, 7, 9, 2, 4, 6, 8 };
     foreach (var i in numbers.OrderBy(i =&gt; i % 3).ThenByDescending(i =&gt; i))
     {
         Console.Write(&quot;{0} &quot;, i);
     }
     Console.WriteLine();
 }
と記述できる。
ただし、LINQはあくまでも「クエリ」処理を行うものなので、Array.Sortメソッドの様に、データソース自体を変更する事は無い。
上記の処理で、データソースを変更したい場合は、
 numbers = numbers.OrderBy(i =&gt; i % 3).ThenByDescending(i =&gt; i).ToArray();
と並べ替えメソッドの最後に、ToArray()をつけ、その時点での問い合わせ結果を配列オブジェクトとして生成する。

また、
 private void Test()
 {
     int[] numbers = { 1, 3, 5, 7, 9, 2, 4, 6, 8 };
     // 配列全体の平均値
     Console.WriteLine(numbers.Average());
     // 配列の要素数 (numbers.Lengthと同じ)
     Console.WriteLine(numbers.Count());
    // 配列内の5以下の数値の平均値
     Console.WriteLine(numbers.Where(i=&gt;(i&lt;=5)).Average());
    // 配列内の偶数値の要素数
     Console.WriteLine(numbers.Where(i =&gt; (i&lt;=5)).Count());
 }
の様な事も出来る。


**ファイル名検索
System.IO.DirectoryクラスのGetFilesメソッドを使うとファイル名を手軽に指定したディレクトリ下のファイルをフルパスで文字列配列として取得できる。
 string[] sFiles = Directory.GetFiles(@&quot;c:\temp&quot;);
とすると、c:\tempディレクトリ下のファイル名を取得できる。

 string[] sFiles = Directory.GetFiles(@&quot;c:\temp&quot;, &quot;*.txt&quot;);
とすると、c:\tempディレクトリ下の、拡張子がtxtのファイル名を取得できる。

ただし、上記の方法では、サブディレクトリ下を検索していない。
そこで、サブディレクトリも検索対象とする場合は、SearchOptionを指定し、
 string[] sFiles = Directory.GetFiles(@&quot;c:\temp&quot;, &quot;*.txt&quot;, SearchOption.AllDirectories);
とする事で、c:\temp以下の全てのディレクトリ下にある、拡張子がtxtのファイルを取得できる。

なお、無効なディレクトリを指定した場合は、例外が発生する。


**ファイルやディレクトリの存在確認
System.IO.FileクラスのExistsメソッドを使うとファイルの存在確認が手軽に実現できる。
 if (File.Exists(@&quot;c:\temp\foo.txt&quot;))
 {
     // ファイルが存在する
 }

また、System.IO.DirectoryクラスのExistsメソッドを使えば、ディレクトリの存在確認を行える。


**Drag &amp; Drop
Drag &amp;　Dropを実現するためには
-フォームのAllowDropプロパティをtrueにする
-Drag &amp; Dropの準備を行うDragEnterイベントを作成する
-Drag &amp; Dropされたファイル名などを受け取るDragDropイベントを作成する
を行う必要がある。

Drag &amp; Dropで複数のファイルを受け取る処理の場合、
 private void Form1_DragEnter(object sender, DragEventArgs e)
 {
     // ファイルをDrag &amp; Dropの処理対象にする場合
     if (e.Data.GetDataPresent(DataFormats.FileDrop))
         e.Effect = DragDropEffects.All;
 }
 
 private void Form1_DragDrop(object sender, DragEventArgs e)
 {
     if (e.Data.GetDataPresent(DataFormats.FileDrop))
     {
         string[] sFiles = (string[])e.Data.GetData(DataFormats.FileDrop);
         Array.Sort(sFiles);  // ファイル名を並べ替えたいときには必要
         foreach (string sFileName in sFiles)
             Console.WriteLine(sFileName);
     }
 }
の様になる。


**コレクション
コレクションの中身を順次処理する場合、
 Dictionary&lt;string, string&gt; dic = new Dictionary&lt;string, string&gt;();
 dic.Add(&quot;Jan&quot;, &quot;睦月&quot;);
 dic.Add(&quot;Feb&quot;, &quot;如月&quot;);
    …
 foreach (string sKey in dic.Keys)
 {
     Console.WriteLine(dic[sKey]);
 }
の様に、foreachを使用する事が多いと思うが、foreach中でコレクションを処理しながら、削除処理を行う
 foreach (string sKey in dic.Keys)
 {
     Console.WriteLine(dic[sKey]);
     dic.Remove(sKey);
 }
の様な処理を実行すると、コレクションが変更されたと言う事で、InvalidOperationExceptionの例外が発生する。

この様なときには、
 string[] sKeys = new string[dic.Count];
 dic.Keys.CopyTo(sKeys, 0);
 foreach (string sKey in sKeys)
 {
     Console.WriteLine(dic[sKey]);
     dic.Remove(sKey);
 }
と、キー(dic.Keys)の値をCopyToメソッドを使用して一旦取り出して処理をすると良い?    </description>
    <dc:date>2008-07-25T20:18:23+09:00</dc:date>
  </item>
    <item rdf:about="http://www28.atwiki.jp/jennychan/pages/17.html">
    <title>Ｃ＃言語でWin32 API</title>
    <link>http://www28.atwiki.jp/jennychan/pages/17.html</link>
    <description>
      大抵の場合は、Win32 APIを使用しなくても済んでしまうのですが、たまに必要なときがあります。

**ファイルIO

.NET FrameworkのSystem.IOクラスでは、「\\.\physicaldrive0」などの物理ドライブに対するアクセスが出来ないので、CreateFile APIを使用する事になります。
参考資料は、[[Windows の ReadFile 関数を使用する&gt;http://msdn2.microsoft.com/ja-jp/library/2d9wy99d(vs.80).aspx]]で、ポイントは、
-DllImportでDLLとAPIを指定する
-プロジェクトのビルドプロパティで、「アンセーフコードの許可」をチェック
となります。
ただし、MSのサンプルでは、CreateFileメソッドの返却値が0以外のときは成功としているのですが、無効なドライブを指定した時は、0xffffffffが返され、正常終了扱いとなってしまいます。

なお、「\\.\c:」の様な、論理ドライブに対する処理は、System.IO.DriveInfoクラスを使用すれば可能です。
 System.IO.DriveInfo = new System.IO.DriveInfo(&quot;c&quot;);
 Console.WriteLine(string.Format(&quot;{0} {1} {2}Byte(s)&quot;,
      info.ToString(), info.DriveFormat, info.TotalSize));

全てのドライブを取得するには、staticメソッドのGetDrivesを使用します。
 foreach (System.IO.DriveInfo info in System.IO.DriveInfo.GetDrives())
 {
     Console.WriteLine(string.Format(&quot;{0} {1} {2}Byte(s)&quot;,
          info.ToString(), info.DriveFormat, info.TotalSize));
 }


MSの[[Windows の ReadFile 関数を使用する&gt;http://msdn2.microsoft.com/ja-jp/library/2d9wy99d(vs.80).aspx]]の、CreateFileのエラー判定処理を修正すると共に、ドライブの容量を取得するDeviceIoControl処理を追加した、C#のコンソールアプリケーションベースのコードをメモとして載せておきます。
なお、DeviceIoControl処理の詳細は、Microsoft Platform SDKのWinIoCtl.hを見るのが一番かもしれません。

 class FileReader
 {
     const uint GENERIC_READ = 0x80000000;
     const uint FILE_SHARE_WRITE = 0x00000002;
     // 物理ディスクを指定する場合、OPEN_EXISTINGを指定しなければいけないらしい 
     const uint OPEN_EXISTING = 3;
 
     System.IntPtr handle;
 
     [System.Runtime.InteropServices.DllImport(&quot;kernel32&quot;, SetLastError = true)]
     static extern unsafe System.IntPtr CreateFile
     (
         string FileName,          // file name
         uint DesiredAccess,       // access mode
         uint ShareMode,           // share mode
         uint SecurityAttributes,  // Security Attributes
         uint CreationDisposition, // how to create
         uint FlagsAndAttributes,  // file attributes
         int hTemplateFile         // handle to template file
     );
 
     [System.Runtime.InteropServices.DllImport(&quot;kernel32&quot;, SetLastError = true)]
     static extern unsafe bool ReadFile
     (
         System.IntPtr hFile,      // handle to file
         void* pBuffer,            // data buffer
         int NumberOfBytesToRead,  // number of bytes to read
         int* pNumberOfBytesRead,  // number of bytes read
         int Overlapped            // overlapped buffer
     );
 
     [System.Runtime.InteropServices.DllImport(&quot;kernel32&quot;, SetLastError = true)]
     static extern unsafe bool CloseHandle
     (
         System.IntPtr hObject // handle to object
     );
 
 
     // ドライブの容量を取得するためにDeviceIoControlを使用する
     const uint IOCTL_DISK_GET_LENGTH_INFO = 0x7405c;
     [System.Runtime.InteropServices.DllImport(&quot;kernel32&quot;, SetLastError = true)]
     static extern unsafe bool DeviceIoControl(
         System.IntPtr hFile,      // handle to file
         uint dwIoControlCode,     // 実行する動作の制御コード
         void* lpInBuffer,         // 入力データを供給するバッファへのポインタ
         uint nInBufferSize,       // 入力バッファのバイト単位のサイズ
         void* lpOutBuffer,        // 出力データを受け取るバッファへのポインタ
         uint nOutBufferSize,      // 出力バッファのバイト単位のサイズ
         uint* lpBytesReturned,    // バイト数を受け取る変数へのポインタ
         int Overlapped            // overlapped buffer
     );
 
 
     public bool Open(string FileName)
     {
         // open the existing file for reading       
         handle = CreateFile
         (
             FileName,
             GENERIC_READ,
             FILE_SHARE_WRITE,
             0,
             OPEN_EXISTING,
             0,
             0
         );
 
         // 物理ドライブ処理のエラーは、0xffffffffとなる
          if (handle != System.IntPtr.Zero
              &amp;&amp; (uint)handle.ToInt32() != (uint)0xffffffff)
         {
             return true;
         }
         else
         {
             return false;
         }
     }
 
     public unsafe int Read(byte[] buffer, int index, int count)
     {
         int n = 0;
         fixed (byte* p = buffer)
         {
             if (!ReadFile(handle, p + index, count, &amp;n, 0))
             {
                 return 0;
             }
         }
         return n;
     }
 
     public bool Close()
     {
         return CloseHandle(handle);
     }
 
     // ディスクの容量を取得する
     public unsafe long GetDiskSize()
     {
        long lDiskSize = 0;
         uint nBytes = sizeof(long);
         bool bResult = DeviceIoControl(handle,
             IOCTL_DISK_GET_LENGTH_INFO, null, 0,
             &amp;lDiskSize, nBytes, &amp;nBytes, 0);
         return lDiskSize;
     }
 
 }
 
 class Program
 {
     static void Main(string[] args)
     {
         FileReader fr = new FileReader();
         for (int i = 0; i &lt;= 9; i++)
         {
             string sDrivePath = string.Format(@&quot;\\.\physicaldrive{0}&quot;, i);
             if (fr.Open(sDrivePath))
             {
                 long lDiskSize = fr.GetDiskSize();
                 fr.Close();
                 double dSize = lDiskSize / 1000000;
                 if (dSize &gt; 1000)
                 {
                     dSize /= 1000;
                     System.Console.WriteLine(
                         string.Format(&quot;Drive{0} {1,7:F1}GB&quot;, i, dSize));
                 }
                 else
                     System.Console.WriteLine(
                         string.Format(&quot;Drive{0} {1,7:F1}MB&quot;, i, dSize));
             }
         }
         return;
     }
 }


**レジストリ

.NET Frameworkでは、Microsoft.Win32.Registryからルートレジストリをアクセスできます。

たとえば、ADO.NET 2.0では、Microsoft Data Access Components(MDAC)2.8以降のバージョンが必要とされています。
MDACのバージョンは、レジストリのHKEY_LOCAL_MACHINE\Software\Microsoft\DataAccess\FullInstallVerを取得する事で確認できます。
そこで、MDACのバージョンを確認するメソッドは、
 private bool ChkMDAC()
 {
     string sFullVer = (string)Microsoft.Win32.Registry.GetValue
               (@&quot;HKEY_LOCAL_MACHINE\Software\Microsoft\DataAccess&quot;, &quot;FullInstallVer&quot;, &quot;&quot;);
     bool bResult = false;
     if (!string.IsNullOrEmpty(sFullVer))
     {
         string[] sVer = sFullVer.Split(&#039;.&#039;);
 
         int iVer1 = Convert.ToInt16(sVer[0]);
         int iVer2 = Convert.ToInt16(sVer[1]);
         if (iVer1 &lt; 2 || (iVer1 == 2 &amp;&amp; iVer2 &lt; 80))
             bResult = false;
         else
             bResult = true;
     }
 
     return bResult;
 }
の様になります。

※ マイクロソフトのサポート情報では、レジストリ上のバージョン情報はあてにならない場合があるので、Component Checkerというツールを使って確認する事を推奨していますが、不特定多数のユーザ向けのソフトウェアの場合、Component Checkerツールのインストールも要求するような事は非現実的だと思います。
また、レジストリのバージョン情報を参照するときも、FullInstallVer値と、Version値の両方の値を確認するように推奨していますが、手元のマシンの場合、
FullInstallVer: &quot;2.81.1117.0&quot;
Version: &quot;2.0.0&quot;
となっています    </description>
    <dc:date>2008-01-04T01:38:52+09:00</dc:date>
  </item>
    <item rdf:about="http://www28.atwiki.jp/jennychan/pages/2.html">
    <title>メニュー</title>
    <link>http://www28.atwiki.jp/jennychan/pages/2.html</link>
    <description>
          </description>
    <dc:date>2008-01-04T01:38:16+09:00</dc:date>
  </item>
    <item rdf:about="http://www28.atwiki.jp/jennychan/pages/7.html">
    <title>Ｃ＃言語でC言語の関数を実現</title>
    <link>http://www28.atwiki.jp/jennychan/pages/7.html</link>
    <description>
      C言語の関数と同じ動作を、C#言語で実現しようとした場合のTipsです。


**文字列比較
Basic言語などの様に
 if ( str1 == str2 )
として、同一の文字列か比較できる。
また、strcmpの様な、文字列の大小比較は、stringクラスのCompareメソッドや、CompareToメソッドを使う。
 if ( str1.CompareTo(str2) == 0 )
or
 if ( string.Compare(str1, str2) == 0 )
Compareメソッドは、staticメソッドなので、比較元(str1)の文字列変数がnullであっても例外が発生しないところがJavaと比較したときの優位な点かな。

なお、stricmpの様に、英大文字小文字を同一視した比較は、
 if ( string.Compare(str1, str2, true) == 0 )
とすれば可能。
また、Compareメソッドは、カルチャ(いわゆる地域化)に対応した文字列の大小比較も出来るらしい。


**memcpy
C言語で、char配列のmemcpyに相当する処理を行うには、System.ArrayクラスのCopyメソッドを使う。
ただし、クラスのデータをメモリに展開するような用途には使えない。

Copyメソッドstaticメソッドなので、Bufferクラスのインスタンスを作成する必要は無い。
 memcmp(dst, src, len);
は
 Array.Copy(src, dst, len);

また、C言語では、ポインタを使ってメモリブロックの任意の位置を指し示す事が簡単に出来るが、C#言語では、一般的にはポインタを意識させない作りなので、メモリブロックの任意の位置を指定してコピーさせる場合、ConstrainedCopyメソッドを使う。
ConstrainedCopyメソッドもstaticメソッドなので、Bufferクラスのインスタンスを作成する必要は無い。
 memcmp(&amp;dst[10], &amp;src[20], len);
は
 Array.ConstrainedCopy(src, 20, dst, 10, len);
ただし、この方法でもポインタを使うことは出来ないらしい。

なお、byte型などのプリミティブ型の配列の場合、System.BufferクラスのBlockCopyメソッドを使った方が処理が速いらしい。
BlockCopyメソッドもstaticメソッドなので、Bufferクラスのインスタンスを作成する必要は無い。
 memcmp(dst, src, len);
は
 Buffer.BlockCopy(src, 0, dst, 0, len);

 memcmp(&amp;dst[10], &amp;src[20], len);
は
 Buffer.BlockCopy(src, 20, dst, 10, len);


**realloc
(厳密には違うかも知れないが…)
一度確保したメモリブロックの大きさを変更する場合、System.ArrayクラスのResizeメソッドを使う。
 byte [] arr = new byte[10];
 Array.Resize&lt;byte&gt;(ref arr, 20);
の様な使い方をするらしい。
なお、
 byte [] arr = null;
 Array.Resize&lt;byte&gt;(ref arr, 20);
でも例外は発生せず、望み通りの結果が得られる。


**memcmp
無いの…???
[[Microsoftのサーポートページ(JP307020)&gt;http://support.microsoft.com/kb/307020/ja]]
の「2 つのハッシュ値の比較」によれば、
&gt;2. 2 つのバイト配列を比較する最もわかりやすい方法は、各要素を第 2 の値の対応する要素と比較しながら配列全体をループすることです。
だそうだ。

文字列中にNULL文字があっても構わないので、バイト配列であれば、Latin-1の様な8bitの1バイト文字に見立てて、
 string str1 = Encoding.GetEncoding(850).GetString(arr1);
 string str2 = Encoding.GetEncoding(850).GetString(arr2);
 if (  str1 == str2  )
の様に文字列として比較するとか…。って、頭悪すぎる。
世間的には、memcmpの様な処理が標準で存在する言語って珍しいような気もするのだけど、やっぱり必要でしょ???


**memset
Clearメソッドで0クリアできるから無い????
確かに、C言語でmemsetを使うケースの大多数は0クリアのような気がするけど、それって、auto変数の中身が初期化されないC言語固有の問題では?


**書式指定
printfなどの書式指定は、Formatメソッドを使用するが、C言語の書式指定に比べて強力なものとなっている。
 printf(&quot;0x%x(%d)\n&quot;, addr, addr);
は、
 System.Console.WriteLine(string.Format(&quot;0x{0:X}({0})&quot;, addr));
と、同じ値を複数出力したい場合に簡潔に記述できる。

また、書式指定の指定では、
|C言語|C#言語|
|&quot;%d\n&quot;|&quot;{0}&quot; or &quot;{0:D}&quot;|
|&quot;%5d\n&quot;|&quot;{0,5}&quot; or &quot;{0,5:D}&quot;|
|&quot;%-5d\n&quot;|&quot;{0,-5}&quot; or &quot;{0,-5:D}&quot;|
|&quot;%05d\n&quot;|&quot;{0:D5}&quot;|
|&quot;%s\n&quot;|&quot;{0}&quot;|
|&quot;%10s\n&quot;|&quot;{0,10}&quot;|
|&quot;%-10s\n&quot;|&quot;{0,-10}&quot;|
|&quot;%8.2f\n&quot;|&quot;{0,8:F2}&quot;|
|&quot;%.2f\n&quot;|&quot;{0:F2}&quot;|
など、C言語で出来る事は一通り出来そう。

また、書式指定文字列を動的に作成するときなど、書式指定文字列の中に中括弧を入れたい場合は、中括弧を二重に記述する。
 string.Format(&quot;{{0:{0}}}&quot;, &quot;D&quot;);
の実行結果は、
 &quot;{0:D}&quot;
となる。


**文字列の数値変換
C言語では、
-基数を指定しない数値変換 atoi atol atof
-基数を指定する数値変換 strtol strtoul strtod
などの文字列を数値に変換するための関数がある。

このとき、文字列として、10進数(&quot;123&quot;)や、0xを先頭につけた16進数(&quot;0x123&quot;)の文字列を一つの処理で変換させようとすると、strtoXXX関数の基数を0として
 long l = strtol(&quot;0x123&quot;, 0);
の様に記述すると正しく数値変換できる。(MSC拡張仕様???)

ところが、
- Convert.ToInt32(&quot;0x123&quot;, 10); は、System.FormatException例外が発生
- Convert.ToInt32(&quot;0x123&quot;, 0); は、System.ArgumentException例外が発生
となり、上手く行かない。

結局、先頭の文字列を見て、基数を別途指定するようにしないといけない???

なお、
- Convert.ToInt32(&quot;0x123&quot;, 16);
- Convert.ToInt32(&quot;123&quot;, 16);
のどちらも、0x123(291)と変換する。    </description>
    <dc:date>2008-01-04T01:37:02+09:00</dc:date>
  </item>
    <item rdf:about="http://www28.atwiki.jp/jennychan/pages/5.html">
    <title>Ｃ＃メモ</title>
    <link>http://www28.atwiki.jp/jennychan/pages/5.html</link>
    <description>
      C#言語を勉強(?)中に気になった点をまとめたものです。
ただし、C言語との比較が多いので、C言語を知らない人には分りにくいかもしれませんが…。


-[[C#言語のデータ形式とアクセス&gt;Ｃ＃言語のデータ形式とアクセス]]
C言語と比較して、C#言語固有と思われるデータ構造、アクセス方法のTipsです。 

-[[C#言語でC言語の関数を実現&gt;C#言語でC言語の関数を実現]]
C言語の関数と同じ動作を、C#言語で実現しようとした場合のTipsです。 

-[[.NET固有の機能&gt;.NET固有の機能]]
.NET Framework固有の機能についてのTipsです。

-[[C#言語でWin32 API&gt;C#言語でWin32 API]]
C#言語からのWin32 API 使用例    </description>
    <dc:date>2008-01-04T01:34:38+09:00</dc:date>
  </item>
    <item rdf:about="http://www28.atwiki.jp/jennychan/pages/8.html">
    <title>Ｃ＃言語のデータ形式とアクセス</title>
    <link>http://www28.atwiki.jp/jennychan/pages/8.html</link>
    <description>
      C言語と比較して、C#言語固有と思われるデータ構造、アクセス方法のTipsです。


**文字列中のNULL文字
C#言語(というか.NET)では、文字列中にNULL文字(\x00)が入っても良いらしい。
つまり、
 string s = &quot;hoge\x00\x00&quot;;
 s += &quot;hoge&quot;;
とすると、string型変数sは、
 &quot;hoge\x00\x00hoge&quot;
となる。
が、この文字列に対して、
 Debug.Print(s);
とすると、NULL文字以降が無視され、
 hoge
としか出力しない。
良いのか????
(Visual C# 2005/2008 Express Edition)


**メモリブロック中の整数値取得など
C#には、TCP/IPのパケットヘッダなど、バイト列中の数値取得に便利なSystem.BitConverterクラスがある。
このクラスには、メモリブロックから、(現在のシステムのバイトオーダーにしたがって)数値を取得したり、メモリブロック中のデータの並びを反転させたりするメソッドが含まれている。

たとえば、メモリブロックからLittleEndianの32bit符号なし整数を取得する場合、C言語では
 unsigned char buff[] = {0x12, 0x34, 0x56, 0x78};
 unsigned long result = 0;
 for ( int i=3; i &gt;= 0; i --)
 {
     result &lt;&lt;= 8;
     result += buff[i];
 }
などとして取得するが、System.BitConverterクラスを使用すると
 byte buff[] = {0x12, 0x34, 0x56, 0x78};
 // 現在のシステムがBig Endianならデータの並び順を反転させる
 if ( !BitConverter.IsLittleEndian )
     Array.Reverse(buff);
 // バイト列の内容を符号なし32bit整数として取得する
 uint result = BitConverter.ToUInt32( buff, 0 );
で取得できる。

一般的な(と思われる)アプリケーションではこのような処理とは無縁だと思われるのですが、個人的には使用頻度が高い処理なので、かなり有効に使わせてもらっています。
でも、BCDデータには対応していないし、3バイト長の整数データなど、基本データ型の長さと異なる値は扱えないので、結局、昔ながらの処理は残りますが…
&#039;&#039;そもそも、3バイトや、1.5バイト(12ビット)の符号付整数なんてものを扱う事自体が世間的には特殊なのですが…&#039;&#039;


**ToStringメソッド
IDE(Integrated Development Environment)の変数ウィンドウなどで表示されるクラスの値は、ToStringメソッドの実行結果なので、デバッグ効率が良くなるような表示内容をToStringメソッドで実装すると便利。
&#039;&#039;ただし、VBではこのような動作にならないらしい。C#への誘導政策と言う事か?&#039;&#039;


**文字列のエンコード
C#言語では、バイト配列から文字列を作成する場合、Encodingクラスを使用し、
 byte[] bTemp = { 0x41, 0x82, 0xa0, 0x83, 0x41, 0x88, 0x9f };
 Encoding encoding = Encoding.Default; // OSの既定のコードページを使用する
 Console.WriteLine(encoding.GetString(bTemp));

 byte[] bTemp = { 0x41, 0x82, 0xa0, 0x83, 0x41, 0x88, 0x9f };
 Encoding encoding = Encoding.GetEncoding(932); // シフトJISのコードページは932
 Console.WriteLine(encoding.GetString(bTemp));
の様に記述する。日本語Windowsであれば、上の二つの処理はどちらも同じ意味になる。
なお、日本国内でも、業務上の必要に迫られて英語版Windowsを使用している環境もあるので、汎用的なアプリケーションを作成しようと思うのなら、既定のエンコードに頼らず、面倒でもコードページを指定したほうが良いと思われる。

ところで、Java言語では、Stringクラスのコンストラクタでバイト配列を指定できる。大抵の場合は、Java言語よりもC#言語の方が簡潔に記述できる場合が多いのだが、文字列のエンコードに関しては、Java言語の方が簡潔な記述が可能となっている。



**文字列の有効判定
文字列変数sが有効な文字列か判定する場合
 if (s != null &amp;&amp; s.Length &gt; 0)
     …
とすることが多いが、string.IsNullOrEmptyメソッドを使用して
 if (!string.IsNullOrEmpty(s))
     …
とすればすっきりする。



**NULL許容型
 int i = null
とすると普通はコンパイルエラーになるが、
 int? i = null
と、型名の後ろに「?」をつけると、NULL許容型になり、nullを代入してもエラーにならない。

bool型のデータに対してNULL許容型を使用することによって、
・有効 (true)
・無効 (false)
のほかに、
・未定義 (null)
の三つの状態を持たせる事が出来る。

と、ドキュメントには書かれているが、正直、この例以外の適用方法が良く分らない代物であったが、C# 3.0でサポートされた、LINQのテーブル列定義には必要不可欠な物である。



**??演算子
ドキュメントによると、
 ?? 演算子は、左側のオペランドが null 値でない場合にはこのオペランドを返し、
 null 値である場合には右側のオペランドを返します。
とある。

要するに、
 if (s == null)
     Console.WriteLine(&quot;Unspecified&quot;);
 else
    Console.WriteLine(s);
というコードが、
 Console.WriteLine(s ?? &quot;Unspecified&quot;);
と書けるという事になる。

なお、
 if (string.IsNullOrEmpty(s))
     Console.WriteLine(&quot;Unspecified&quot;);
 else
    Console.WriteLine(s);
と
 Console.WriteLine(s ?? &quot;Unspecified&quot;);
では、s = &quot;&quot;の時の動作が異なる。    </description>
    <dc:date>2007-12-25T18:06:21+09:00</dc:date>
  </item>
    <item rdf:about="http://www28.atwiki.jp/jennychan/pages/20.html">
    <title>RD-X4のIFOファイル</title>
    <link>http://www28.atwiki.jp/jennychan/pages/20.html</link>
    <description>
      DVD-RAMのIFOファイルの情報を元に、RD-X4のIFOファイル(TS_HDDMG.IFO)を見てみると、かなり似通った構造である事が分った。

IFOファイルのブロックは、DVD-RAMのものと若干異なり、
-ヘッダ
-プレイリスト
-クリップリスト
-不明1
-タイトルリスト
-ベンダ情報?
-不明2
と、ベンダ情報と思われるデータブロックの後ろ、というか、IFO全体の長さの先に、不明なデータブロックが見受けられる。


**ヘッダ
データブロックが若干異なる事もあり、ヘッダ情報は、以下の様な構造となっている。
|　位置　|長さ|　内容　|
|　0000　|　0C　|　識別子(&#039;DVD_RTR_VMG0&#039;) |
|　000C　|　04　|　このIFOファイルの終端位置(= ファイル長 - 1) |
|　0010　|　04　|　「プレイリスト」ブロックの終端位置(ここまでが、本来のIFOブロックなのか?) |
|　0104　|　04　|　「クリップリスト」ブロックの開始位置 |
|　0108　|　04　|　「不明1」ブロックの開始位置 |
|　0134　|　04　|　「タイトルリスト」ブロックの開始位置 |
|　0138　|　04　|　「ベンダ情報?」ブロックの開始位置 |
|　0168　|　04　|　「不明2」ブロックの開始位置(0x000cで指定された位置の後ろを指し示している) |


**クリップリスト
DVD-RAMと若干異なり、フラグ情報が0x00000102となっており、
|　位置　|長さ|　内容　|
|　0000　|　04　|　フラグ?(0x00000102) |
|　0004　|　04　|　このブロックの長さ相対的な終端位置(= ブロック長 - 1) |
|　0008　|　78　|　不明なデータ列 |
|　0080　|　02　|　クリップ数 |
|　0082　|　04 * クリップ数　|　クリップ情報への相対位置配列 |
の様な構造になっている

DVD-RAMのIFOと比較したとき、
|　　|　DVD-RAM　|　RD-X4 HDD　|
|　フラグ　|　0x00000101　|　0x00000102　|
|　0x08からの不明なデータ列長　|　0x3c(60)　|　0x78(120)　|
と、フラグの下位8bitの値と、不明なデータ列長に関連性が読み取れる。

したがって、クリップリストの構造は、
|　位置　|長さ|　内容　|
|　0000　|　04　|　フラグ |
|　0004　|　04　|　このブロックの長さ相対的な終端位置(= ブロック長 - 1) |
|　0008　|　3C * フラグの下位8bitの値　|　不明なデータ列 |
|　0008 + 3C * フラグの下位8bitの値　|　02　|　クリップ数 |
|　000A + 3C * フラグの下位8bitの値　|　04 * クリップ数　|　クリップ情報への相対位置配列 |
と思われる。

とろで、「ディスクに問題がある」といわれたHDDのクリップ数は、0x3E8、つまり、1000となっていた。切がいいのか悪いのか微妙な値。
この手のRDシリーズのトラブルの原因として、ストリームデータ領域のフラグメント問題がよく話題になっているけれど、クリップ数が多すぎる事が原因なんじゃないのか???
クリップ数が900を超えたあたりで警告でも出してくれれば、このような被害は防げるんじゃないのか???


と言う事で、ここまでくれば、タイトルと、チャプタ情報を生かした状態でデータを取り出すことが可能なはず。
しかし、取り出せると言っても、パソコン上でのこと。

このデータをDVD-RAMへ書き出したり、DLNA(Digital Living Network Alliance、RD的に言えば、ネットdeダビング)で、HDDレコーダに転送できれば良いのだけれど、DVD-VRや、DLNAの資料が見当たらない…。    </description>
    <dc:date>2007-03-28T22:07:48+09:00</dc:date>
  </item>
    <item rdf:about="http://www28.atwiki.jp/jennychan/pages/12.html">
    <title>RD-X4 HDD解析</title>
    <link>http://www28.atwiki.jp/jennychan/pages/12.html</link>
    <description>
      ある日、「ディスクに問題があり、再生以外できません」というメッセージが出てしまいました。
「見るナビ」でタイトル一覧を見ようとすると、「表示する内容がありません。」と表示されるので、DVDディスクに書き出す事も出来ず。
しかし、HDD残量は少ないので、ファイルシステム的には録画データが残っている状態らしい。
メーカー的には、録画データをあきらめて、ハードディスクを初期化しなさいということらしいが、諦めが悪い性格と、仕事でMPEG-2 Systemsの経験があるので、データの吸出しにチャレンジしてみました。

ちなみに、RD-X4は、非EXです。

-[[部品交換]]
-[[MPEG-2 Program Stream]]
-[[HDDの中を見る]]
-[[UDF(Universal Disk Format)]]
-[[HDDの中を見る(UDF編)]]
-[[タイトルとチャプタ]]
-[[DVD-RAMのIFOファイル]]
-[[RD-X4のIFOファイル]]    </description>
    <dc:date>2007-03-28T22:03:15+09:00</dc:date>
  </item>
    <item rdf:about="http://www28.atwiki.jp/jennychan/pages/19.html">
    <title>DVD-RAMのIFOファイル</title>
    <link>http://www28.atwiki.jp/jennychan/pages/19.html</link>
    <description>
      タイトルとクリップの紐付け情報が記録されていると思われるIFOファイルの構造を調べてみるために、DVD-RAMのIFOファイル(VR_MANGR.IFO)を見る。
この構造は、DVD-VR(DVD Video Recording Format)仕様のものだと思われる。しかし、DVD-VRの仕様書を見つける事が出来なかったので、地道に色々なデータを書き込んで検証する事に…。

とりあえず、色々な形式のIFOファイルの資料を見ていると、IFOファイルの内部は、複数のデータブロックの寄せ集めらしい事が分った。
また、これらのデータブロックに対するアドレス指定が、ヘッダの中に記述されているらしい事も。

と言う事で、以下のブロックがあると思われる(当然のことながら正式名称は不明)
-ヘッダ
-プレイリスト
-クリップリスト
-不明1
-タイトルリスト
-ベンダ情報

※これらのブロック中の数値はBig Endian

**ヘッダ
|　位置　|長さ|　内容　|
|　0000　|　0C　|　識別子(&#039;DVD_RTR_VMG0&#039;) |
|　000C　|　04　|　このIFOファイルの終端位置(= ファイル長 - 1) |
|　0010　|　04　|　「プレイリスト」ブロックの終端位置(ここまでが、本来のIFOブロックなのか?) |
|　006C　|　04　|　ディスク番号? |
|　00A2　|　3D　|　ディスクタイトル |
|　0100　|　04　|　「クリップリスト」ブロックの開始位置 |
|　0104　|　04　|　「不明1」ブロックの開始位置 |
|　0130　|　04　|　「タイトルリスト」ブロックの開始位置 |
|　0164　|　04　|　「ベンダ情報」ブロックの開始位置 |


**プレイリスト
ヘッダ中に、このブロックの開始位置を示す数値は見当たらず、ファイルの先頭から0x200の位置から始まる。
したがって、このブロックは、ヘッダの一部と解釈するほうが正しいのかも。
|　位置　|長さ|　内容　|
|　0000　|　04　|　有効フラグ? (1: プレイリストあり、0: プレイリストなし) |
|　0004　|　04　|　このブロックの長さ相対的な終端位置(= ブロック長 - 1) |

プレイリストが存在しない場合、「00 00 00 00 00 00 00 07」の8バイトのバイト列となる。


**クリップリスト
|　位置　|長さ|　内容　|
|　0000　|　04　|　フラグ?(0x00000101) |
|　0004　|　04　|　このブロックの長さ相対的な終端位置(= ブロック長 - 1) |
|　0008　|　3C　|　不明なデータ列 |
|　0044　|　02　|　クリップ数 |
|　0046　|　04 * クリップ数　|　クリップ情報への相対位置配列 |

***クリップ情報
|　位置　|長さ|　内容　|
|　0000　|　02　|　0固定?(世界時、地方時識別フラグ?) |
|　0002　|　05　|　日時情報(*) |
|　0007　|　02　|　音声モード |
|　0009　|　06　|　再生開始タイムスタンプ(*SCR) |
|　000F　|　06　|　再生終了タイムスタンプ(*SCR) |
|　001D　|　04　|　ストリームのセクタ位置 |
|　0021　|　??　|　不明 |

※日時情報は、5バイトの値を、b39-b26:年 b25-b22:月 b21-b17:日 b18-b12:時 b11-b06:分 b05-b00:秒として表現したもの
※SCRは、0x00からの4バイト整数値 * 300 + 0x04からの2バイト整数値で表される27MHzのタイムスタンプ


**不明1
ヘッダの0x0104で指し示されたアドレスからのデータが、「00 00 00 00 00 00 00 07」となっているので、プレイリストブロックの先頭8バイトと同じ構造と思われる。
|　位置　|長さ|　内容　|
|　0000　|　04　|　有効フラグ? (0: 無効) |
|　0004　|　04　|　このブロックの長さ相対的な終端位置(= ブロック長 - 1) |


**タイトルリスト
|　位置　|長さ|　内容　|
|　0000　|　02　|　タイトル数 |
|　0002　|　02　|　クリップ数 |
|　0004　|　8E * タイトル数　|　タイトルの情報 * タイトル数 |
|　0004 + 8E * タイトル数　|　4 * クリップ数　|　タイトルクリップ情報への相対位置配列 |

***タイトル情報
|　位置　|長さ|　内容　|
|　0000　|　04　|　このタイトルに含まれるクリップ数 |
|　0004　|　40　|　記録日時や、ビットレートなどの情報 |
|　0044　|　40　|　タイトル名(シフトJIS) |
|　0084　|　04　|　タイトルサムネイルのフレームが含まれるクリップ番号 |
|　0088　|　08　|　タイトルサムネイルのフレームのタイムスタンプ |

***タイトルクリップ情報
&#039;&#039;この情報は、クリップリストのクリップ情報とは異なる&#039;&#039;
|　位置　|長さ|　内容　|
|　0000　|　04　|　クリップ番号(1～) |
|　0004　|　02　|　このクリップに含まれるチャプタ数 |
|　0006　|　06　|　再生開始タイムスタンプ(*SCR) |
|　000C　|　06　|　再生終了タイムスタンプ(*SCR) |
|　0012　|　any　|　チャプタ情報の配列 |

***チャプタ情報
|　位置　|長さ|　内容　|
|　0000　|　01　|　チャプタ情報文字列の長さ(未定義の場合0) |
|　0001　|　06　|　チャプタ位置のタイムスタンプ(*SCR) |
|　0007　|　チャプタ情報文字列の長さ　|　チャプタタイトル |
|　0007 + チャプタ情報文字列の長さ　|　チャプタ情報文字列の長さ　|　チャプタの詳細 |


ということらしい。    </description>
    <dc:date>2007-03-28T21:40:51+09:00</dc:date>
  </item>
  </rdf:RDF>

