XNA で文字表示

まずはコチラをご参照あれ。

ひにけにXNA - Content Pipeline その3 そのカスタマイズ:
 http://blogs.msdn.com/ito/archive/2007/05/02/content-pipeline-part3.aspx

 
エラく大変な思いをして文字表示をしているが、その背景として
XNA で日本語が表示できない」
という現状がある。
 
XNAで文字を取り扱う(表示する)場合、スプライトフォントを使用する。
が、単純に MessageBox.Show("てすと") のようなことは出来ない。
ある程度のコーディングを経て文字表示が出来ても、英数字しか取り扱えない。
 
それを解決したサンプルが冒頭で紹介した記事だ。
初心者ながらに「これは素晴らしい!」と思い、早速試してみることにした。
 
私の環境は以下(スペックは別記事参照)
 Visual Studio 2005 Standard Edition
 XNA Game Studio 2.0
 
さて、まず一つ目の躓き。
記事で作成した sln / csproj 他ソース等一式はダウンロードできる。
開いてみたところ、
 

        • -

プロジェクトファイル '*** 略 *** TextMessageSample\Pipeline\Pipeline.csproj' を開けません。
プロジェクトの種類がこのインストールでサポートされていません。

        • -

プロジェクトファイル '*** 略 *** TextMessageSample\Sample\SampleWin.csproj' を開けません。
プロジェクトの種類がこのインストールでサポートされていません。

        • -

プロジェクトファイル '*** 略 *** TextMessageSample\Runtime\RuntimeWin.csproj' を開けません。
プロジェクトの種類がこのインストールでサポートされていません。

        • -

 
だそうな。
記事には「GSE 1.0 Refreshが必要」と記載があった。
GSE 1.0 Refresh と GS 2.0 のバージョン違いの問題かと思い、csproj ファイルの該当部分と思しき文字列(1.0/1.0.0.0)を直してみたが、結果は変わらなかった。
仕方ないので、ソースを参考に自作してみる。
ソリューションとプロジェクトを自作してみる。
ダウンロードしたソリューションは TextMessageSample だったので、TextMessageSample2 でソリューションを新規作成するところから始める。
 
エラーメッセージからもわかる通り、ソリューションにプロジェクトは3つ。
初期プロジェクト作成はSampleプロジェクトと判断し、Windows Game プロジェクトで新規作成。
(アイコンファイルとかがあったので。。。
 
Game1.cs を SampleGame.cs に変更。
Pipeline と Runtime は、Sampleプロジェクトと同列のディレクトリとソースだけコピって準備しておいた。
で、Visual Studio のメニューから「ファイル」→「新規作成」→「既存のコードからプロジェクトを作成」。
Visual C# を指定し、共にクラスライブラリとしてプロジェクトを作成。
sln/suo ファイルなどは削除、Sampleプロジェクトのソリューションに Pipeline/Runtime プロジェクトを追加。
で、とりあえず枠は出来たのかな。
 
ここからどうやってコンパイル・ビルドを通すか、が次なる課題。
パッと見の問題は、
・Sampleプロジェクトの SampleGame.cs が元々のTextMessageSampleプロジェクトの SampleGame.cs を踏襲していないこと
・Pipeline/Runtime プロジェクトが Visual C# Class Library(非XNA)プロジェクトとして作成されていること。
 
とか思って、まずは Pipeline のビルドを通すのに参照設定とか色々…
 
TextImporter.cs

using Microsoft.Xna.Framework.Content.Pipeline;

 
そんな矢先、こんな using を発見して、オブジェクトブラウザから XNA Framework の中身を覗いてみる。
 
Microsoft.Xna.Framework.Content
+ContentLoadException
+ContentManager
+ContentReader
+ContentSerializerAttribute
+ContentSerializerCollectionItemNameAttribute
+ContentSerializerIgnoreAttribute
+ContentTypeReader
+ContentTypeReader
+ContentTypeReaderManager
+ResourceContentMananger
 
…ないじゃん。
Pipeline なくなってる!!
 
別のクラスに代替なのかもしれない。
とか思って諦めかけたら違った。
参照設定で追加しなくちゃいけなかった。
 
Pipeline プロジェクトに Microsoft.Xna.Framework 関連の参照と、System / System.Xml 参照を追加。
すると、以下のビルドエラーになった。
 

        • -

'〜略〜\TextMessageSample2\Runtime\obj\Debug\Runtime.exe' は、エントリ ポイントに適切な静的 'Main' メソッドを含んでいません
'〜略〜\TextMessageSample2\Runtime\bin\Debug\Runtime.exe' メタデータが見つかりませんでした。

        • -

 
これはまだ Runtime のビルド通ってないから当然。
概ね良好なのではないかと思う。
よって、Runtime プロジェクトのビルドにチャレンジ。
 

        • -

'〜略〜\TextMessageSample2\Runtime\obj\Debug\Runtime.exe' は、エントリ ポイントに適切な静的 'Main' メソッドを含んでいません

        • -

 
Main がないということで、Sampleプロジェクト側の調整がまだだ。
そっちが先か。
SampleGame.cs をとりあえず、TextMessageSample のコードに合わせてみた。
 

        • -

'〜略〜\TextMessageSample2\Runtime\obj\Debug\Runtime.exe' は、エントリ ポイントに適切な静的 'Main' メソッドを含んでいません
'〜略〜\TextMessageSample2\Runtime\bin\Debug\Runtime.exe' メタデータが見つかりませんでした。

        • -

 
んー、よくよく見れば、というか、気づいてはいたが、Runtime.exe に Main がないって言ってる。
元々クラスライブラリを指定して作ったが、XNA のクラスライブラリではない。
というか、exe を探しに行くはずじゃないんだけど。
XNA 初心者とか言う前に、Visual Studio に関する知識不足がここで祟った気がする。
 
いやいや、もうちょっと食い下がろう。
exeを参照しようとしていること自体おかしい。
ということで、Runtime プロジェクトをクラスライブラリで作り直したらすんなりビルドが通ってしまった。
なんてこった。。。
(記事を書きながらチャレンジしているわけだが、ここで時間を浪費したことは言うまでもない…
 
Pipeline / Runtime プロジェクトのビルドは通った。
2つのdllが正しく参照できる状態になった、ということだ。
そして、Sample プロジェクトのリビルド。
 

        • -

メンバ 'Sample.SampleGame.LoadGraphicsContent(bool)' は 古い形式の メンバ 'Microsoft.Xna.Framework.Game.LoadGraphicsContent(bool)' をオーバーライドします。Obsolete 属性を 'Sample.SampleGame.LoadGraphicsContent(bool)' に追加してください。 D:\01.data\01_40.for_application\vsproject\cs\XNAGS2.0\TextMessageSample2\Sample\SampleGame.cs
メンバ 'Sample.SampleGame.UnloadGraphicsContent(bool)' は 古い形式の メンバ 'Microsoft.Xna.Framework.Game.UnloadGraphicsContent(bool)' をオーバーライドします。Obsolete 属性を 'Sample.SampleGame.UnloadGraphicsContent(bool)' に追加してください。 D:\01.data\01_40.for_application\vsproject\cs\XNAGS2.0\TextMessageSample2\Sample\SampleGame.cs

        • -

 
上記二つの警告が出た。
 

@IT - C#入門 - 第20回 実行時に参照可能な属性:
 http://www.atmarkit.co.jp/fdotnet/csharp_abc/csharp_abc_020/csharp_abc04.html

 
まあ、Obsolete とはいえ、現段階では問題なく動くはず。
上記警告メソッドにそれぞれ Obsolete 属性を付加することで、警告は消えた。

// LoadGraphicsContent用Obsolete
[Obsolete("Called when graphics resources need to be loaded. Override this method to load any game-specific graphics resources.")]
// UnloadGraphicsContent用Obsolete
[Obsolete("Called when graphics resources need to be unloaded. Override this method to unload any game-specifc graphics resources.")]

 
さて実行してみる。
 

    protected override void LoadGraphicsContent(bool loadAllContent)
    {
      GraphicsDevice gd = graphics.GraphicsDevice;

      if (loadAllContent)
      {
        spriteBatch = new SpriteBatch(gd);
        textMessage = content.Load<TextMessage>("SampleMessage");
      }

    }

 
content.Load("SampleMessage");
 
で例外が発生する。
 

Microsoft.Xna.Framework.Content.ContentLoadException はハンドルされませんでした。
Message="Error loading \"SampleMessage\". File not found."
Source="Microsoft.Xna.Framework"
StackTrace:
場所 Microsoft.Xna.Framework.Content.ContentManager.OpenStream(String assetName)
場所 Microsoft.Xna.Framework.Content.ContentManager.ReadAsset[T](String assetName, Action`1 recordDisposableObject)
場所 Microsoft.Xna.Framework.Content.ContentManager.Load[T](String assetName)
場所 Sample.SampleGame.LoadGraphicsContent(Boolean loadAllContent) 場所 〜略〜\TextMessageSample2\Sample\SampleGame.cs:行 81
場所 Microsoft.Xna.Framework.Game.Initialize()
場所 Sample.SampleGame.Initialize() 場所 〜略〜\TextMessageSample2\Sample\SampleGame.cs:行 64
場所 Microsoft.Xna.Framework.Game.Run()
場所 Sample.Program.Main(String[] args) 場所 〜略〜\TextMessageSample2\Sample\Program.cs:行 14

 
つまり、SampleMessage が存在しない、と。
更に詳細に見てみると、xnbファイルが見つけられない、とのこと。
 
元記事に立ち帰ってみる。
(ここではSample)プロジェクトのプロパティで、Content Pipelineタブ画面で"Add"ボタンを押し、作ったパイプライン用のアセンブリを追加…。
って、プロジェクトのプロパティにそんなタブ画面がないっ!
 
記事自体もそうだけど、Blog全体を(つまり他の記事も含め)見てみると、まだまだわからないことだらけ。
xnbを作る流れ自体、今回XNA GSE 1.0 Refresh からXNA GS 2.0 に移植したコード自体の理解、などが絶対的に足りない。
せっかくデバッグ出来るところまで到達したので、まだ色々やってみたいが、今日はココマデ。
 
 
 
P.S.
 ホントに挑戦しながら書いた記事なので、読み返すとわかりづらい。。。
 もしコレを読む方がいたらゴメンナサイ。

XNA キー押下検知

前回の

XNA ことはじめ
http://d.hatena.ne.jp/levin_gsp/20071212/1197440515

に続いて、XNA/C# で勝手に一人で盛り上がっていく。
 
今回は XNA におけるキーボード入力の検知を題材として取り上げる。
ソースコードレベルで手元にあるのは Spacewar Project と Empty Project (共にWin版)のみ。
ここから自力で解析しつつ、考察しつつ、色々試してみたい。
 
まず、Spacewar を少しプレイしてみると、“およ?”という印象の一つにキー操作がある。
XBOX360 を想定したキー配列になっているのだが、その設定がどこにあるのか。
探してみると、まずこんなのが見つかった。
 
Debug ビルドした後であれば、Debug フォルダ(Releaseも同様)の下に settings.xml がある。

<?xml version="1.0"?>
<Settings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <MediaPath>content\</MediaPath>
  <WindowTitle>Spacewar</WindowTitle>
  
  〜中略〜
  
  <Player1Start>LeftControl</Player1Start>
  <Player1Back>LeftShift</Player1Back>
  <Player1A>V</Player1A>
  <Player1B>G</Player1B>
  <Player1X>F</Player1X>
  <Player1Y>T</Player1Y>
  <Player1ThumbstickLeftXmin>A</Player1ThumbstickLeftXmin>
  <Player1ThumbstickLeftXmax>D</Player1ThumbstickLeftXmax>
  <Player1ThumbstickLeftYmin>S</Player1ThumbstickLeftYmin>
  <Player1ThumbstickLeftYmax>W</Player1ThumbstickLeftYmax>
  <Player1Left>A</Player1Left>
  <Player1Right>D</Player1Right>
  <Player1Down>S</Player1Down>
  <Player1Up>W</Player1Up>
  <Player1LeftTrigger>Q</Player1LeftTrigger>
  <Player1RightTrigger>E</Player1RightTrigger>

  <Player2Start>RightControl</Player2Start>
  <Player2Back>RightShift</Player2Back>
  <Player2A>Home</Player2A>
  <Player2B>End</Player2B>
  <Player2X>PageUp</Player2X>
  <Player2Y>PageDown</Player2Y>
  <Player2ThumbstickLeftXmin>Left</Player2ThumbstickLeftXmin>
  <Player2ThumbstickLeftXmax>Right</Player2ThumbstickLeftXmax>
  <Player2ThumbstickLeftYmin>Down</Player2ThumbstickLeftYmin>
  <Player2ThumbstickLeftYmax>Up</Player2ThumbstickLeftYmax>
  <Player2Left>Left</Player2Left>
  <Player2Right>Right</Player2Right>
  <Player2Down>Down</Player2Down>
  <Player2Up>Up</Player2Up>
  <Player2LeftTrigger>Insert</Player2LeftTrigger>
  <Player2RightTrigger>Delete</Player2RightTrigger>
</Settings>

パッと見からして、キー配列以外にも数多くの(というより動作に必要なほとんどの)設定が記載された XML ファイルだとわかる。
settings.xml の名の通り、という感じ。
 
しかし、これはソースではなく、ビルドによって出力された“実行時に入力するための設定ファイル”だ。
(同パスに出来ている exe を実行する前に、上記の内容を変更しておけばそれに準じた設定となる(と思う
 
じゃあこれらの設定はどこで行っているのか。
とりあえず、キーの配列(XBOX360コントローラ用のボタン割り当て)はどこで決めているのか。
探してみた。
Program.cs や SpacewarGame.cs より一階層深いパス「common」フォルダの下にある4つのソースがどうもそれっぽい。
 
GamePadHelper.cs
GamePads.cs
Keymap.cs
XInputHelper.cs
 
ここでキー配列を含むキーパッド関連のクラスを定義してるみたい。
 
さて、本題。
XNA におけるキー押下通知、非常に気になる機構。
XNA Game Studio 2.0 で追加されたキーパッド関連のメソッドがある。
 
IsButtonDown
IsButtonUp
 
である。
これによるコードレベルでの変化を、XNA Game Studio Express 1.x までと比較してみる。
 
1.x

// AボタンとBボタンの同時押しを検出!
if (padState.Buttons.A == ButtonState.Pressed && padState.Buttons.B == ButtonState.Pressed ) ...

2.0

// AボタンとBボタンの同時押しを検出!
if (padState.IsButtonDown(Buttons.A&Buttons.B) ) ...

 
うん、スッキリ書ける。
2つのキー同時押しでこれだから、それ以上になれば更に。
同時押しに限らず、どちらか(いずれか)でも '&' が '|' の判定になるだけで、処理の簡素化の程度は同様。
これはイイ。
 
この判定方法って、1.x にせよ 2.0 にせよ「押されているかどうか」を見ている。
「押されたことを検知した」わけではない。
つまり、キー押下(KeyPress)イベントを受け取るような WinForms 的な発想とは異なるわけだ。
これはどうしてだろう。
 
個人的解釈を書いてみる。
XNA はゲームを開発するための Framework だ。
そのための機構が色々とデフォルトで組み込まれている。
その一つは Update メソッドの実装ではないかと思う。
 
空プロジェクトでも頻繁に呼ばれる Update メソッド。
実行して、どっか(Updateメソッドが妥当か)で BreakPoint 張って止めてみる。
 
this.base.TargetElapsedTime が Update のコール周期。
{00:00:00.0166667}
 
となっていた。
Default では、0.0166667 秒周期、つまり「秒間60回」頻度でコールされている。
設定変更はプロパティの値を変えることで出来るが、そういうことを言っているのではなくて。
「秒間60回」呼ばれるように初期設定されていることこそが、「ゲームを開発するための Framework 」ならではの設定だと感じる。
Windows アプリケーションや、Web アプリケーションと違って、ゲームの場合、画面に対して何かしら目に見える変化がある。
そうでなければ成り立たないからだ。(ゲームが
 
XNA は、KeyPress を検出するような機構を提供していない。
それは、Update メソッドで「キーが押されているかどうか」を判定することで、「キーが押されたことを検出する」のと同様の検知能力を提供できる、という意図の現れではないかと思う。
ふーむ。
XNA ならではの入力検知処理を垣間見た気がした。

XNA ことはじめ

先日、個人的な Blog ではお伝えした通り、最終的に到達したい目標を実現するためのフレームワークとして目をつけていた XNA Framework に触れてしまった。
 

れびさんのおうち - XNA Game Studio 2.0β:
 http://myhome.cururu.jp/aquacity/blog/article/91001641605

れびさんのおうち - XNA Game Studio 2.0 β Empty Template:
 http://myhome.cururu.jp/aquacity/blog/article/91001641826

 
で、更にもう少し触れてみた。
Windows 版、XNA サンプルプロジェクト Spacewar を創って動かしてみたり、ソース見てみたり。
それだけでも色々学ぶことがある。
 
Spacewar プロジェクトは MS が提供しているサンプルプロジェクト。
プロジェクト新規作成後、ビルドするだけで動かすことが出来る。
これを元に空のプロジェクトに手を加えて行きながら、C#/XNA を勉強して行けたらいいな、という安易な(しかし期待に充ち溢れた)考えの元、ちまちまといたずらをしていってみようと思う。
(空プロジェクト(WindowsGameプロジェクト)作成直後のソースは Game1.cs 及び Program.cs のみで、内容は前述の別Blog記事参照
 
Program.cs については Game クラスのインスタンスを作成し、Run() するのみ。
Spacewar プロジェクトにおいても流れは同じだが、一つだけ大きな相違を確認。
 
インスタンス作成するクラスが Game クラスではない。
Game クラスを継承した SpacewarGame クラス、となっている。
 
確かに、空プロジェクトで言う Game1.cs が Spacewar プロジェクトでは SpacewarGame.cs となっており、拡張されている。
まずはファイル名の変更から試してみますか。
 
今回作成したソリューション及びプロジェクトは xna_jape という名称であることを事前に明記しておく。
ソリューションエクスプローラで Game1.cs を右クリック「名前の変更」で JapeGame.cs というファイル名に変更してみる。
ダイアログが出てきた。
 
「ファイルの名前を変更しようとしています。このプロジェクトのすべての参照をコード要素 'Game1' に名前を変更しますか?」
 
イマイチわかりにくい表現だが、おそらく名称の変更による影響範囲も自動的に修正しようとしてくれているのだろう。
リファクタの出力ウィンドウが以下を表示した。
(一部ディレクトリなど非公開に
 

プロジェクト 'D:\ほげほげ\xna_jape\xna_jape\obj\x86\Debug\Refactor\xna_jape.exe' でシンボルの参照を探しています
'Game1' から 'JapeGame' へ名前を変更

D:\ほげほげ\xna_jape\xna_jape\JapeGame.cs(17,15): 更新された定義
D:\ほげほげ\xna_jape\xna_jape\JapeGame.cs(22,10): 更新された参照
D:\ほげほげ\xna_jape\xna_jape\Program.cs(12,11): 更新された参照
D:\ほげほげ\xna_jape\xna_jape\Program.cs(12,28): 更新された参照
プロジェクト 'D:\ほげほげ\xna_jape\xna_jape\obj\x86\Debug\Refactor\xna_jape.exe' でシンボルの参照を探しています

 
なるほど。
Game1.cs の Game1 クラスが自動的に JapeGame という名のクラスに変更になっている。
更に、影響範囲を全部リファクタリングしている。
 
ファイル名変更前の Program.cs

    static void Main(string[] args)
    {
      using (Game1 game = new Game1())
      {
        game.Run();
      }
    }

 
ファイル名変更後の Program.cs から抜粋

    static void Main(string[] args)
    {
      using (JapeGame game = new JapeGame())
      {
        game.Run();
      }
    }

 
ここまで勉強をしてきたとはいえ、C#も、というか Visual Studio 2005 を扱うレベルですら初心者に近いため、こんな挙動一つでも学ぶことがあったりして。
ちなみに、これだけの変更を行ったプロジェクトのビルドでエラーや警告は発生しなかった。
リファクタリングがバッチリ行われたようだ。
 
と、ここで一つ補足。
JapGame.cs の冒頭の using を紹介しておく。

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

 
XNA Game Studio 2.0 からは、System.Windows.Forms をサポートしなくなった。
あくまでも XNA Framework の Window を使うわけであり、ゲームを創る上で通常の Windows アプリの Form 用コントロールを使うべきではない、ということか。
それはある程度納得が行くが、じゃあ実際ラベルやテキストボックスやボタンが使いたくなった場合はどうするんだろう、と。
XBOX360 を主な対象プラットホームとして捉えているが、やっぱり Windows アプリも創れるわけなので。
その辺りもおいおい調べて行きたかったり。
(既にこの辺りで経験や知識の不足を痛感するわけだが…
 
とまあ、それはそれ。
.NET Framework の根幹である System 名前空間は当然のごとく、C#2.0 のジェネリックしょっぱなから using されている。
が、それ以外は XNA Framework が提供する、まさにゲーム用のライブラリ群という感が強い。
もちろん、XNA Framework は .NET Framework の一部(だと捉えていいんだよね?)なので、System.Windows.Forms を使ってもコンパイルエラーにはならないんだろうけど、XNA Framework の Window とは一線を画するのだろう。
 
さて、とりあえず MessageBox でも出せないだろうか。
この時点で発想がゲームアプリというより Windows アプリなのだが、とにかくやってみる。
 
一発で仮説が覆された。

    protected override void Update(GameTime gameTime)
    {
      if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
        this.Exit();
      // この2行を追加
      if (GamePad.GetState(PlayerIndex.One).Buttons.A == ButtonState.Pressed)
        System.Windows.Forms.MessageBox.Show("test");
      
      base.Update(gameTime);
    }

 
System.Wi …まで書いたところで手が止まった。
System.Windows 名前空間が見えない。
サポートされない、ってそういうことなのね。
そもそも XBOX360 向けのアプリに対して MessageBox なんてありえねーよ、ってことか。
ゴメンナサイ。
 

Microsoft XNA Unleashed:
 http://myhome.cururu.jp/aquacity/blog/article/91001640072

 
コレが欲しい。
ああ、絶対的に色々足りない。
本が読みたい。
 
とにかくめげずに行こうと思う。
今回の記事の内容でわかる通り、ハッキリ言ってまだスタートラインにすら立てないレベルだ。
が、C#/XNA 共に地道に学んで行きたい。

C#でMySQL操作

Rafysta - C#MySQLに接続:
 http://www2.pf-x.net/~rafysta/memo/wiki.cgi?page=C%23%A4%C7MySQL%A4%CB%C0%DC%C2%B3

 
どうもMySQLを扱うツールがイマイチ。
phpMyAdminとかが割と一般的なようだけど、サーバにインストールできない場合などもあり。
クライアント側にインストールして、接続設定だけすれば繋げるようなの創れないかな、と思ったのが発端。
 
結局、時間がなさ過ぎて断念(笑
 
とはいえ、イイ線までは割と簡単にイケそう。
冒頭で紹介した記事を参考に、.NET ライブラリとして MySQL から提供されていた MySQL Connector をインストールしてみた。
 
Windows Form にボタンを一つだけ配置。
押下した時に接続して、DB選択して、テストSQLを投げて、結果を受け取るところまでは作れた。
 
GUI的に使いやすいものにまで昇華させようと思ったら、色んなこと考えなきゃいけないし。
良い勉強にはなりました。
前回の時計同様、C#ならではテクのようなものはほとんど使っていない初歩プログラムレベルですが、そんなでも一応掲載しておこうかと。
 
Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using MySql.Data.MySqlClient;

namespace MySqlConnector
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }
    
    private MySqlConnection hMySql = null;
    
    private void OnClick(object sender, EventArgs e)
    {
      string connectionString = String.Format(
        "server={0};user id={1}; password={2}; database=mysql; pooling=false",
        "サーバ名","ユーザ名","パスワード");
      try
      {
        hMySql = new MySqlConnection(connectionString);
        hMySql.Open();
        
        hMySql.GetSchema();
        hMySql.ChangeDatabase("sugarcrmdb");
        
        MessageBox.Show("Connection Success!");
        
        MySqlDataReader reader = null;
        MySqlCommand cmd = new MySqlCommand("select * from テーブル名", hMySql);
        try
        {
          reader = cmd.ExecuteReader();
          while (reader.Read())
          {
            MessageBox.Show(reader.GetString(0));
            MessageBox.Show(reader.GetString(1));
            MessageBox.Show(reader.GetString(2));
          }
        }
        catch (MySqlException ex)
        {
          MessageBox.Show("Failed to populate database list: " + ex.Message);
        }
        finally
        {
          if (reader != null) reader.Close();
        }
      }
      catch (MySqlException ex)
      {
        MessageBox.Show("Error connecting to the server: " + ex.Message);
      }
    }
  }
}

 
「サーバ名」「ユーザ名」「パスワード」「テーブル名」は、置き換えてあるので注意。
 
まず参照したページの第一歩で、参照設定を追加して、using で躓く。
こんな簡単なことでどうして躓くんだ。。。
インストール後に参照設定を追加するところまでは問題なく出来たのに、using したらリンクエラーとなった。
 
型または名前空間名 'MySQL' が見つかりませんでした。
using ディレクティブまたはアセンブリ参照が不足しています。
 
上に提示したソースでは勿論修正してありますが、using ディレクティブに指定した Namespace が間違ってた。
× MySQL.***
MySql.***
 
単なる大文字と小文字の違い。
10分ほどもわからなくて悩んだ自分にガッカリ。
 
その他にも、いろいろ直した。
以下は一例。
 
○ GetSchema()
× GetDatabases()
 
どっかのバージョンでメソッド名を変えたものと思われる。
Form1.design.cs の Dispose も追加したが、割愛。
 
抽出データの確認に MessageBox.Show() (しかも固定添え字で連発)しているのは、ホントの確認のためだけ。
実際にカスタマイズするなら、データベースの選択やテーブルの選択もプログラムで抽出したものの中からプルダウンで選ばせるぐらいはしたいところ。
 
これがターミナルログインが必要(サーバ想定)となると、もっと色々手間がかかりそうだという印象も。
とにかくホントのサンプルとして。

サンプル時計

創ってみた。
むかーしにVC++で創ったものの焼き直し。
とはいえ、プログラミングは勿論勉強を兼ねて一から。
VC++版のソースを紛失してるから、とも。。。
 
さて、csOriginalClock というプロジェクト名でプロジェクトを新規作成するところから。
まずは Form のプロパティを色々いじったんだけど、細かい部分はコードをご覧あれ。
 

namespace csOriginalClock
{
  partial class baseForm
  {
    /// <summary>
    /// 必要なデザイナ変数です。
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// 使用中のリソースをすべてクリーンアップします。
    /// </summary>
    /// <param name="disposing">マネージ リソースが破棄される場合 true、破棄されない場合は false です。</param>
    protected override void Dispose(bool disposing)
    {
      if (disposing && (components != null))
      {
        components.Dispose();
      }
      base.Dispose(disposing);
    }

    #region Windows フォーム デザイナで生成されたコード

    /// <summary>
    /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
    /// コード エディタで変更しないでください。
    /// </summary>
    private void InitializeComponent()
    {
      this.components = new System.ComponentModel.Container();
      System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(baseForm));
      this.nowLabel = new System.Windows.Forms.Label();
      this.timer = new System.Windows.Forms.Timer(this.components);
      this.SuspendLayout();
      // 
      // nowLabel
      // 
      this.nowLabel.AutoSize = true;
      this.nowLabel.BackColor = System.Drawing.Color.Transparent;
      this.nowLabel.Font = new System.Drawing.Font("Nina", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
      this.nowLabel.ForeColor = System.Drawing.Color.Cyan;
      this.nowLabel.Location = new System.Drawing.Point(5, 8);
      this.nowLabel.Name = "nowLabel";
      this.nowLabel.Size = new System.Drawing.Size(114, 16);
      this.nowLabel.TabIndex = 0;
      this.nowLabel.Text = "1999/12/31 23:59:59";
      this.nowLabel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.OnMouseDown);
      this.nowLabel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.OnMouseMove);
      this.nowLabel.MouseClick += new System.Windows.Forms.MouseEventHandler(this.OnMouseClick);
      this.nowLabel.MouseUp += new System.Windows.Forms.MouseEventHandler(this.OnMouseUp);
      // 
      // timer
      // 
      this.timer.Enabled = true;
      this.timer.Interval = 4;
      this.timer.Tick += new System.EventHandler(this.OnTick);
      // 
      // baseForm
      // 
      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
      this.BackColor = System.Drawing.Color.DimGray;
      this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
      this.ClientSize = new System.Drawing.Size(124, 33);
      this.Controls.Add(this.nowLabel);
      this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
      this.MaximizeBox = false;
      this.MinimizeBox = false;
      this.Name = "baseForm";
      this.Opacity = 0.8;
      this.ShowIcon = false;
      this.ShowInTaskbar = false;
      this.Text = "Form1";
      this.TopMost = true;
      this.TransparencyKey = System.Drawing.Color.White;
      this.MouseClick += new System.Windows.Forms.MouseEventHandler(this.OnMouseClick);
      this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.OnMouseUp);
      this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.OnKeyPress);
      this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.OnMouseMove);
      this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.OnMouseDown);
      this.ResumeLayout(false);
      this.PerformLayout();

    }

    #endregion

    private System.Windows.Forms.Label nowLabel;
    private System.Windows.Forms.Timer timer;
  }
}

 
いくつかポイントになりそうな部分だけ解説しておく。
見ての通り、コントロールは三つ。
Form / Timer / Label のみの至ってシンプルな構造。
 
Form。
BackgroundImage は自力で作った手抜きの画像をローカルリソースからインポートした。
TopMost を true にすることで、他のウィンドウよりも前面に。
 
Lable。
Text に指定しているのは、幅の検証に使った無意味な値。
Font は単なる好み。
マウス系の各イベントハンドラは、Form に合わせておく。
これをしないと、ラベルにフォーカスがある(見えないけど)と、ドラッグでのウィンドウ移動などが出来ない。
 
さて、次に Form1.cs を提示。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace csOriginalClock
{
  public partial class baseForm : Form
  {
    public baseForm()
    {
      InitializeComponent();
    }

    private bool mouseIsDown = false;
    private Point mouseDownPoint;

    private void OnMouseDown(object sender, MouseEventArgs e)
    {
      if (e.Button == System.Windows.Forms.MouseButtons.Left)
      {
        mouseDownPoint = new Point(e.X, e.Y);
        mouseIsDown = true;
      }
    }

    private void OnMouseUp(object sender, MouseEventArgs e)
    {
      if (e.Button == System.Windows.Forms.MouseButtons.Left)
      {
        mouseIsDown = false;
      }
    }

    private void OnMouseMove(object sender, MouseEventArgs e)
    {
      if (mouseIsDown && e.Button == System.Windows.Forms.MouseButtons.Left)
      {
        this.Left += e.X - mouseDownPoint.X;
        this.Top += e.Y - mouseDownPoint.Y;
      }
    }

    private void OnTick(object sender, EventArgs e)
    {
      this.nowLabel.Text = System.DateTime.Now.ToString();
      this.Invalidate();
    }

    private void OnKeyPress(object sender, KeyPressEventArgs e)
    {
      e.KeyChar = Char.ToUpper(e.KeyChar);
      if (e.KeyChar == Convert.ToChar(System.Windows.Forms.Keys.U))
      {
        if (this.Opacity < 1)
        {
          this.Opacity += 0.1;
        }
        this.Invalidate();
      }
      if (e.KeyChar == Convert.ToChar(System.Windows.Forms.Keys.D))
      {
        if (this.Opacity > 0.3)
        {
          this.Opacity -= 0.1;
        }
        this.Invalidate();
      }
    }

    private void OnMouseClick(object sender, MouseEventArgs e)
    {
      if (e.Button == System.Windows.Forms.MouseButtons.Right)
      {
        Application.Exit();
      }
    }
  }
}

 
難しい処理はほとんどない、と思う。
ウィンドウ移動処理に関しては、以下を参考にした。
 

宇宙仮面のC#プログラミング - アナログクロック その2(自由な形のウィンドウを作る):
 http://uchukamen.com/Programming1/AnalogClock/TransparentClock.htm

 
MouseDown / MouseUp / MouseMove の各イベントを捕まえてウィンドウの移動処理を実装。
Timer の Tick イベントで日時の更新。
MouseClick で右クリックの場合、終了の処理。
最後におまけ。
KeyPress イベントで'U','D'の各キーを押した時に時計ウィンドウの透明度を10%ずつ上昇、下降。
ん?逆か…?
 
C#に不慣れなため、もっと苦戦するかと思ったが、意外と早く出来た、かな?
自分で書いたコードなんてたかが知れているが、約一時間程度で完成。
 
デザイナやウィザードの恩恵に与ってしまった部分も多い。
CreateWindow でごちゃごちゃやっていた時代が懐かしい気もする(笑
内部的には同じようなことをしているはずではあるものの。
 
ちょうど Jitta さんがそんなことをテーマにBlog記事を書かれていた。
 

何となく Blog by Jitta - 簡単になるのはいいことなのか?:
 http://blogs.wankuma.com/jitta/archive/2007/11/29/110975.aspx

 
肯定的にも否定的にも受け取る方がいるようだけど。
私個人としては、考えさせられるテーマではあるけど、肯定派でも否定派でもない。
(人類の、とか、文明が、とか多少議題の飛躍は感じるが、違和感はさほど感じない
 
一応、意味があるアプリをC#で創るのは初めてかもしれないので、大したことはしていないが公開。
 
シンプルでサンプルな時計:
 http://www.grimsagaproject.com/tools/csOriginalClock.zip
 ※サーバから直で実行されないように圧縮してるので、DLして使ってネ
 
これまでに取り上げてきた技術や言語仕様を生かせてるわけではないので、もう少し精進して色々なものを創ってみたい。

ラムダ

Λ:ギリシャ語大文字
λ:ギリシャ語小文字
 
顔文字(絵文字?)で見るような造形の文字だが、「ラムダ式」というプログラミング上の記法を聞いたことがある方も多いのではないだろうか。
開発に使用する言語に依存してか、それとも自分の知識と力量不足か、私自身はこの「ラムダ式」なるものを用いたことがない。
もっと言えば、ラムダ式って何?な感じだった。
気にはなっている語句だったので、これは勉強がてら調べてみよう、と。
 
ジェネリックもさわりのさわりにだけしか触れていないので、続きを書きたいところだが、ちょっと気分を変えて。
さて、「ラムダ式」とは。
 
英語表記では、Lambda Expression(s)、となる。
はてなキーワードの解説では「名前のない関数」的な記法だと捉えるのが良い、とつづられていた。
となると、実はココでも取り上げているではないか。
匿名メソッド(delegate)である。
 
まさにその通りで、ある処理(機能)の固まりを名前付きの関数化せずに表現するような方法を指しているようだ。
言語的には、歴史上 Lisp が古くから採用している方式であり、現在は Python などでも用いられる。
もちろん、VB / C# でも可能。
 
C# を中心に勉強をすることを目的とした Blog なので、C# に注目にしてみる。
どうやら、C# は 2.0 や 3.0 にバージョンアップするに従い、ラムダ・匿名メソッドの辺りの言語仕様にもまだメスが入っている状況のようだ。
その経緯を端的にうまく説明している記事を見つけた。
 

窓際プログラマーの独り言 - C#3.0:ラムダ式
(Gushwellさんという方のBlog)
 http://blog.livedoor.jp/gushwell/archives/51061352.html

 
見事にC#1.1→2.0→3.0での処理の記述変化を説明しているではないか。
他の言語でも見られる傾向だが、=> という演算子(?)を使って、ラムダ式を表わすことが多いようだ。
 
その実用性が如何ほどのものなのか、実践してみたいところだ。
どういったケースに適用できるのか。
他の機能説明でも同じようなことを言っているが、真新しい機能を学んだ時にどういう場合に使用するのが良いのかまで掴み切れないことがある。
サンプルコードを書くクセをつけて、積み重ねないとダメなんだろうなぁ。

ジェネリック

今回はジェネリック
一度では、ちょっとした勉強では、語りきれない機能。
そんな印象。
@ITでも以下のように、多数のページを割いて特集を組んでいるほど。
なので、今回はさわりだけでも、という思いでチャレンジしてみることにする。
 

連載:C# 2.0入門
第2回 ジェネリック
http://www.atmarkit.co.jp/fdotnet/csharp20/csharp20_02/csharp20_02_01.html

C#VBジェネリック超入門(前編)
ジェネリック・クラスで変わるC#VBのコレクション
http://www.atmarkit.co.jp/fdotnet/special/generics01/generics01_01.html

C#VBジェネリック超入門(後編)
ジェネリックなメソッドやデリゲートがもたらす新スタイル
http://www.atmarkit.co.jp/fdotnet/special/generics02/generics02_01.html

 
まず、ジェネリックという機能がどうして必要か、という部分。
コレです、と一つだけ理由を挙げるわけにはいかない。
が、共通して語られているのは、キャストの問題。
 
下記は ArrayList の例。
 

  System.Collections.ArrayList al = new System.Collections.ArrayList();
  al.Add(100);
  string str = (string)al[0];
  Console.WriteLine(str);

 
実行すると例外が起きた。
「型 'System.Int32' のオブジェクトを型 'System.String' にキャストできません。」
 
ArrayList は要素に何でも追加できる。
それは内部的に Object 型で持っているということであり、同時に、取り出しにはキャストが必要であることを指してしまう。
 
C#2.0 からは ArrayList クラスのジェネリック版 List クラスが用意されている。
もっと言えば、System.Collections 名前空間にあったコレクションクラスの多くが System.Collections.Generic 名前空間以下にジェネリック版として構築されている。
 
今度は List クラスの例。
 

  List<string> l = new List<string>();
  l.Add(100);
  string str = l[0];
  Console.WriteLine(str);

 
エラー 1:'System.Collections.Generic.List.Add(string)' に最も適しているオーバーロード メソッドには無効な引数がいくつか含まれています。
エラー 2:引数 '1': 'int' から 'string' に変換できません。
 
実行する前にコンパイルでエラーが出力された。
これはジェネリック・コレクションが型を指定しているため、コンパイル時に厳密な判断を行うことが出来ることによる恩恵。
もちろん正しい要素を与えれば問題なく実行できる。
 
もう一つのポイントは、キャストが不要なこと。
これも同様の理由で、不要となることがわかるだろう。
 
 
 
さて、ジェネリック・コレクションのさわりを紹介した。
実際にはこれだけの機能ではない。
 
コレクション以外にも、クラス・構造体・インターフェース・デリゲート・メソッドなど様々な対象に実装することが出来る。
記述的に何となく C++ の template 機能を思い出すが、実は私は STL(Standard Template Library) をあまり使ってこなかったので良く知らない。
学びたい学びたいと思ってるうちに、タイミングを逃したのだが、そんなことはどうでも良い。
今回こそはジェネリックをモノにしたいと思う次第。