2012年12月25日火曜日

[Windows] システム周りな話

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


開発言語にとらわれないシステム共通な部分の覚書です。

画面サイズの事
GetSystemMetrics で諸々取得可能
SM_CYSMCAPTION タイトルバー 21pixel
SM_CXSIZEFRAME 枠 8pixel


2012年12月12日水曜日

[C#] WinAPI を呼んでみる

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


以前の投稿内では普通に使っているのですが、特に記載していないようだったので別途投稿してみようと思います。
C# は便利ですが、細かいことをやろうとするとどうしても API に頼ることになります。
では API ってどうやって呼びだすのな部分を今回はネタにしてみました。

using System.Runtime.InteropServices;   // DllImportなど、API呼び出し
namespace CSWinFormTest2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        // API を利用するのに必要な宣言(インポートライブラリの宣言)
        [DllImport("user32.dll")]
        // API を利用するのに必要な宣言(インポートライブラリの宣言、オプション指定はこんな感じ)
//        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        // API を利用するのに必要な宣言(関数定義の宣言)
        // static extern は必須。アクセス演算子は任意
        private static extern UInt32 MessageBox(IntPtr hWnd, String lpText, String lpCaption, UInt32 uType);

        // MessageBox() Flags  winuser.h より(一部抜粋)
        private const UInt32 MB_OK                   = 0x00000000;
        private const UInt32 MB_ICONASTERISK         = 0x00000040;
        private const UInt32 MB_ICONINFORMATION      = MB_ICONASTERISK;

        private void button1_Click(object sender, EventArgs e)
        {
            // API の利用方法を確認
            MessageBox(this.Handle, "メッセージ本文", "タイトル部", MB_OK | MB_ICONINFORMATION);

            // 余談:C# では MessageBox.Show があるので、本来上記のような手間は必要はない
            System.Windows.Forms.MessageBox.Show("メッセージ本文", "タイトル部",
                MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
}
短いので全部まとめて書いています。
まぁコメント見ていただければわかるかと思います。
DllImport で使用するライブラリを指定して関数定義を宣言。
あとは普通に利用するだけです。

ちなみに複数種類の API を利用したい場合は、その関数毎に定義を各必要があります。
オーバーロードするような場合でも DllImport から書く必要があります。
これがめんどくさいので結局 C++/CLI で作るかぁ~?ってなっちゃいます。
まぁ DllImport から関数定義までが一つの宣言だと思ってあきらめるしかありません。

全体的に見れば C# の方が生産性いいはずなので、できるだけ C# でやっていこうかな、っと。

--- 2012/12/27追記 ----------
よく見に行くサイトをいちいち検索するのがめんどくさいので、こちらに転記させていただいた。
毎度お世話になっております(@IT .NET TIPS Win32 APIやDLL関数を呼び出すには?
APIでの型名
(括弧内は対応するC言語の型)
対応するC#の型
(括弧内は.NET Frameworkでの型名)
HANDLE (void *)System.IntPtr
BYTE (unsigned char)byte (System.Byte)
SHORT (short)short (System.Int16)
WORD (unsigned short)ushort (System.UInt16)
INT (int)
LONG (long)
int (System.Int32)
UINT (unsigned int)
DWORD, ULONG (unsigned long)
uint (System.UInt32)
BOOL (long)bool (System.Boolean)
CHAR (char)char (System.Char)
LPSTR (char *)
LPWSTR (wchar_t *)
System.Text.StringBuilder
LPCSTR (const char *)
LPCWSTR (const wchar_t *)
string (System.String)
FLOAT (float)float (System.Single)
DOUBLE (double)double (System.Double)
Win32 APIでの型名と対応するC#の型 WindowsのDLL(Win32 API)と.NET Frameworkとでは型の管理方法が違うため、実際には型の相互変換(マーシャリング)が行われる。なお、BOOL型の実体はLONG型と同じなので、boolの代わりにintを指定することも可能である。

2012年11月29日木曜日

[C#] 画像描画と背景透過

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


画像描画と銘打っていますが、実のところ先行して投稿した下記2記事の続きと言ってもいいかもしれません。

[C#] ListView のカラム部分に画像表示

[C#] ListView のカラム部分に画像表示(其の2:HDITEM版)

其の2の中で、実は背景が透過していないため色の指定次第で画像背景が目立ってしまうと問題定義しておいたことに対する対策方法となります。
タイトルを引き続き利用しようかとも考えたのですが、基本的に内容が異なるために別タイトルとしました。

前置きはこのくらいにして本題に入りましょう。
今回は前提無しです。
でいきなりコードとなります(説明はのちほど)
// OdrIdx:表示する数字
// Odr   :三角形の向き
private Bitmap CreateSortOrderBmp(int OdrIdx, int Odr)
{
    // やっていること
    // 背景塗りつぶし → ソート順により三角形作成 → フォント描画(上下中央右寄せ)
    Bitmap img = new Bitmap(16, 16);
    Graphics g = Graphics.FromImage(img);

    // 背景を塗りつぶし(透過色)
    g.FillRectangle(new SolidBrush(this.TransparencyKey), g.VisibleClipBounds); // ①

    // 三角描画
    Point[] pnt = new Point[4];
    switch (Odr)
    {
        case 1: // 上三角
            pnt = new Point[4] { new Point(4, 2), new Point(0, 10), new Point(8, 10), new Point(4, 2) };
            break;
        case 2: // 下三角
            pnt = new Point[4] { new Point(4, 13), new Point(0, 5), new Point(8, 5), new Point(4, 13) };
            break;
        case 0: // 非表示化
        default:
            // 無印化するので描画処理をやらずに終了
            g.Dispose();
            return img;
    }
    g.FillPolygon(Brushes.Gray, pnt);   // 先に塗りつぶし
    g.DrawPolygon(Pens.Black, pnt);     // 次に、線描画

    // 文字を描画
    Font fnt = new Font("MS ゴシック", 8, FontStyle.Bold);
    StringFormat sfmt = new StringFormat();

    sfmt.Alignment = StringAlignment.Far;           // 横の表示位置
    sfmt.LineAlignment = StringAlignment.Center;    // 縦の表示位置

    g.DrawString(OdrIdx.ToString(), fnt, Brushes.Red, new RectangleF(new PointF(0, 0), new SizeF(16, 16)), sfmt);

    // 描画作業が完了したのでグラフィッククラスを解放
    g.Dispose();

    // 作成した画像を戻り値に
    return img;
}
御覧の通り、今回はメソッドとなります。
画像描画を書いたことある人には、なんてことない内容となっています。
やりたかったことは、リストのカラムをクリックすることでソートするので、それの昇順と降順、選択順番を明記するために画像を表示しようと考えていました。
そんなこんなの描画部分についてはソースを見ていただくとして、今回メインとなる背景透過の部分について。
ズバリ①の部分です。
コメントにも書いてあるのでわかるかと思いますが対象画像の背景をフォームで透過色と指定されている色で塗りつぶしています。
こうすることでカラム部分に表示しても昇降を示す三角と数字以外は表示されないことになります。
あとはそうして作成した Bitmap を ImageList の該当する部分に登録してあげれば対策完了です。
当然ですが ImageList の項目数 = カラム数と言うことになるので、他にもアイコンやらを使う予定がある場合はあらかじめ ImageList の配置仕様を検討しておく必要があると思います。

あと当たり前の話ですが、事前に ImageList に画像を登録して、それを利用する場合は登録する画像の背景を this.TransparencyKey で指定する色と同じにしておく必要があるので注意です。

[C#] ListView のカラム部分に画像表示(其の2:HDITEM版)

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


先日、ほぼ同様のタイトルでカラムに画像を表示する方法を投稿しました。
[C#] ListView のカラム部分に画像表示
この時は ImageList を利用して表示する方法となっていましたが、今回は別の方法で画像を表示してみたいと思います。

表示パターンが確定していて動作中に変更が必要無い場合は、画像ファイルを準備した状態で前回の方法を利用してもらうのが一番だと思います。
しかし状況により表示内容を変更したい(動的に描画する)場合は、少々不便です。
確かに該当する ImageList の内容を書き換えてしまえば前回の方法で表示することは可能ですが、それだと ImageList を利用して準備している意味がありません。

そこで今回は動的に描画した画像をカラムに表示させてみたいと思います。
(妙に説明調になっているのは、自分が忘れそうだからです)
Header Control (Windows)


まずは宣言。
// 前回も宣言したもの
using System.Runtime.InteropServices;

public const UInt32 LVM_GETHEADER = 0x101F;
public const int HDF_BITMAP_ON_RIGHT = 0x1000;

[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, UInt32 wParam, UInt32 lParam);

// 以下前回より追加宣言したもの
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct HDITEM
{
 public uint mask;
 public int cxy;
 public IntPtr pszText;
 public IntPtr hbm;
 public int cchTextMax;
 public int fmt;
 public int lParam;
 public int iImage;
 public int iOrder;
 public uint type;
 public IntPtr pvFilter;
 public uint state;
}

public const int HDF_BITMAP = 0x2000;
public const uint HDI_FORMAT = 0x0004;
public const uint HDI_BITMAP = 0x0010;
public const uint HDM_FIRST = 0x1200;
public const uint HDM_GETITEM = HDM_FIRST + 3;
public const uint HDM_SETITEM = HDM_FIRST + 4;

[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, UInt32 wParam, ref HDITEM lParam);
前回と似たような感じですが、新規宣言したものがあります。
相変わらず情報源は CommCtrl.h となっています。

次はコード本体です。
今回もボタン押下イベントに書いています。
IntPtr colHeader;

// リストビューのカラム部分のハンドルを取得
colHeader = SendMessage(listView1.Handle, LVM_GETHEADER, (UInt32)0, (UInt32)0);

// 設定対象のカラム情報を取得
HDITEM hdItem = new HDITEM();
hdItem.mask = HDI_FORMAT;
SendMessage(colHeader, HDM_GETITEM, 0, ref hdItem);

// 設定対象のカラム情報を変更、反映
hdItem.mask = HDI_FORMAT | HDI_BITMAP;
hdItem.fmt |= HDF_BITMAP | HDF_BITMAP_ON_RIGHT;
Bitmap bmp = CreateSortOrderBmp(1, 1);
hdItem.hbm = bmp.GetHbitmap(SystemColors.Window);
SendMessage(colHeader, HDM_SETITEM, 0, ref hdItem);

/* 参考:Header_GetItem と Header_SetItem の定義内容
#define Header_GetItem(hwndHD, i, phdi) \
    (BOOL)SNDMSG((hwndHD), HDM_GETITEM, (WPARAM)(int)(i), (LPARAM)(HD_ITEM *)(phdi))
            
#define Header_SetItem(hwndHD, i, phdi) \
    (BOOL)SNDMSG((hwndHD), HDM_SETITEM, (WPARAM)(int)(i), (LPARAM)(const HD_ITEM *)(phdi))
*/
CreateSortOrderBmp(1, 1)の中でBMP画像を作成しています。
利用される場合は、その辺を適当に変更していただければいいかと。

このコードでリストのカラムテキストの右側に画像を表示することはできるのですが、少し後に問題があることに気付きました。
ほとんど保護色になっていて気付かなかったのですが、背景色が透過していないため、画面の色指定次第で画像部分が目立って表示されることがあります。
それなりに考えた背景色にしておけば気づかれない可能性はありますが、完璧を求めるならばこの方法では駄目だということになります。
その辺の対策は ImageList を利用した前回のパターンで解消できます。

じゃ何でこの記事投稿したんだ?って話になるのですが、そこはそれメモです。
せっかく調べたのに消すのはもったいないですからね。

上記理由から、特に理由がないようでしたら前回か次回(予定)の投稿を参照してください。

2012年11月22日木曜日

[C#] 文字列まわりの基本

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


2016/03/18 ⇒ 追記

今更ながらに文字列まわりの基本をいくつか確認したのでメモしておく。

●シングルクォーテーション、ダブルクォーテーション
シングルが文字でダブルが文字列。
・・・ホント今更。
これだけだと、ちょっとおバカさんなので他にも。

文字コード指定は\uXXXXで指定。
文字列の場合「逐語的文字列リテラル」というのがあってエスケープシーケンスを無視させる書式がある。
シングルやダブルの前に @ をつければいい。
string str = @"aaa\nbbb\nccc";
Console.Write(str);
@ を使わない場合
aaa
bbb
ccc
と表示されるが @ があると
aaa\nbbb\nccc
と表示される。
ちなみに複数行にわたっての記述に対応していて
string str = @"この下の行も文字列扱いになる。
string str = ""ここも文字列扱い"";
ここまで文字列";
Console.WriteLine(str);
これを実行すると 3 行にわたり文書が表示される。
対象になる行の改行、スペースもそのまま表示されるのでコード上ではインデントで空白を入れたくてもいれられないので注意(入れちゃうとその分しっかりと空白が表示されます)


●文字列が null かどうか情報参照元
いろいろ手段がある。
string str = "ほげほげ";
if(str == "") Console.write("空白です");
if(str == string.Enpry) Console.write("空白です");
if(str.Equals("")) Console.write("空白です");
if(str.Equals(string.Enpry)) Console.write("空白です");
if(string.IsNullOrEmpty(str)) Console.write("空白です");
if(str.Length == 0 ) Console.write("空白です");
どれで判断しても結果として空白かどうかの判断ができる。
対象が空白の場合に、ただ空白か否かの判定をする場合は str.Length == 0, String.Equals, == "" の順が早いようです(詳しくは情報参照元やMSDNにて)
ただ普段使いとしては IsNullOrEmpty がいいようです(MS的に推奨されているとのこと)

余談だが調べている時にnull合体演算子というものがあるのを知った。
string str = null;
str = str ?? "nullです";
?? の左辺が null でないなら左辺値を null であるなら右辺値を返すらしい(詳しくはMSDNを参照のこと)
使い方次第ではすっきりしたコードを書くことができそう。


●デバッグ時のログ出力
デバッグ中とかにブレークするほどではないけど値を確認したい場合に出力画面に結果を表示する方法。
System.Diagnostics.Trace.WriteLine("");


●デバッグ時の時間計測
動作時間を計測したい場合はStopwatchを利用すると便利。
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
sw.Stop();
sw.Restart();
sw.Reset();


以上、確認事項メモでした。

2012年11月20日火曜日

[C#] ListView のカラム部分に画像表示

更新履歴:
2012/11/29:宣言部分で DLL の宣言が抜けていたので追記。

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


タイトル通り ListView のカラム部分に画像を表示する方法です。
ただしカラム文字列の左に表示するだけなら SmallImageList にImageList を登録して Columns.ImageIndex を設定するだけで表示できるので、取り立てて記事にするほどでもありません。
←これは簡単

今回記事にした理由は、文字列の右側に画像を表示しようとしているためです。
たとえば列でソートする機能を作成した時や、何らかの印を表示したい時などに便利かと。
ではさっそく。

環境は Form 上に Button, ListView, ImageList をそれぞれ一つずつ配置しています。
ImageList には適当に画像を登録しておいてください。
カラムに登録するサイズなのでフォントサイズにもよりますが、縦16 x 横8 程度が見栄えいいかと。
とりあえず、この投稿では以下のような感じで登録しています。

参照したサイトは以下の通り。
●PRB: Cannot Assign Images to a ColumnHeader Control in a Windows Forms ListView Control in Visual C# .NET
●Common Control Versions (Windows)
●LVCOLUMN structure (Windows)

おまけの類似項目
●HDITEM structure (Windows)

とりあえず宣言部分です
using System.Runtime.InteropServices;

public const UInt32 LVM_GETHEADER = 4127;
public const UInt32 HDM_SETIMAGELIST = 4616;
public const UInt32 LVM_SETCOLUMN = 4122;
public const uint LVCF_FMT = 1;
public const uint LVCF_IMAGE = 16;
public const int LVCFMT_IMAGE = 2048;

// ListView のカラム情報設定用構造体
[StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Auto)]
public struct LVCOLUMN
{
    public uint mask;
    public int fmt;
    public int cx;
    public IntPtr pszText;
    public int cchTextMax;
    public int iSubItem;
    public int iImage;
    public int iOrder;
}

// 以下 fmt で利用する定義で参照元に無かったやつ( CommCtrl.h より定義参照)
// ComCtl32.dll ver4.70 以降でのみ利用可能らしい
// ただし 4.70 というのは Internet Explorer 3.0 って事なので今時では制約にならない
public const int HDF_IMAGE = 0x0800;      // Same as LVCFMT_IMAGE
public const int HDF_BITMAP_ON_RIGHT = 0x1000;      // Same as LVCFMT_BITMAP_ON_RIGHT
// おまけ:参考サイトのコメント欄にて発見。隠し?項目らしいです(CommCtrl.h には定義あり)
public const int HDF_SORTUP = 0x0400;      // Windows7 Explorer のソート順表示(上)
public const int HDF_SORTDOWN = 0x0200;      // Windows7 Explorer のソート順表示(下)

[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, UInt32 wParam, UInt32 lParam);

[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 msg, UInt32 wParam, ref LVCOLUMN lParam);
覚書みたいなものはコメントにゴチャゴチャ書いているので省略します。

次はボタン押下イベントです。
IntPtr colHeader;
int i;

// リストビューのカラム部分のハンドルを取得
colHeader = SendMessage(listView1.Handle, LVM_GETHEADER, (UInt32)0, (UInt32)0);

// カラム部のハンドルにイメージリストを割り当て
SendMessage(colHeader, HDM_SETIMAGELIST, (UInt32)0, (UInt32)imageList1.Handle);

// カラムの設定
for (i = 0; i < listView1.Columns.Count; i++)
{
    // Use the LVM_SETCOLUMN message to set the column's image index. 
    LVCOLUMN col;
    //  col.mask: include LVCF_FMT | LVCF_IMAGE 
    col.mask = LVCF_FMT | LVCF_IMAGE;

    // LVCFMT_IMAGE 
    col.fmt = LVCFMT_IMAGE | HDF_BITMAP_ON_RIGHT;
//    col.fmt = LVCFMT_IMAGE | HDF_SORTUP;

    // 表示するイメージの番号(仮に固定値で ImageList の 1 番目をしていしている)
    col.iImage = 1;

    //  Initialize the rest to zero.
    col.pszText = (IntPtr)0;
    col.cchTextMax = 0;
    col.cx = 0;
    col.iSubItem = 0;
    col.iOrder = 0;

    // Send the LVM_SETCOLUMN message.
    // The column that we are assigning the image to is defined in the third parameter.
    SendMessage(listView1.Handle, LVM_SETCOLUMN, (UInt32)i, ref col);
}
まぁ何というか、ほぼ MS サイトのコードまる写しです、はい。
col.fmt 部分の設定がキモになっています。
上記通りにやればリンクしてある ImageList の 1 番目の画像が表示されます。
コメントになっているように記述すると Windows7 の explorer の詳細表示部分のソート表記と同じにできます。
左:LVCFMT_IMAGE | HDF_BITMAP_ON_RIGHT
右:LVCFMT_IMAGE | HDF_SORTUP

何にしろ画像は表示できたので目的達成です。

2012年10月24日水曜日

[java]パッケージについて

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


インナークラスについて勉強中
 ↓
アクセス制御について再度確認
 ↓
パッケージ部分について再確認するためにコード書いてみる
 ↓
見事にハマって実行できないorz ← 投稿の原点

こんな流れでパッケージの書き方、ファイルの置き方どうすればいいのか正しくわからなかったので、いろいろ調べた結果を書き出しておく。
なお私の環境はカレントディレクトリとして D:\Data\java 、システム環境には JAVA_HOME は定義していますが CLASSPATH は定義していません。
D:\Data\java>set JAVA_HOME
JAVA_HOME=C:\Java\jdk1.7.0_07

D:\Data\java>set CLASSPATH
環境変数 CLASSPATH が定義されていません

●定義方法と格納場所
パッケージの作成と言うのは別にむずかしいことはない。
普通にコーディングをしてコンパイルをすればいいだけです。
注意するのは以下の部分

・ファイルの先頭でキーワードを宣言する。
hoge.java
// hoge はパッケージ名です、これが必要
package hoge;

public class testPackage{
 public void show(){
  System.out.println("testPackage Message");
 }
}

・キーワードと同じ名称のフォルダーにファイルを格納する
たとえば上のキーワード宣言をしているファイルを格納するなら hoge フォルダーに入れること。
とりあえずこれだけ。
細かいことを言うとフォルダー名とかはコンパイルオプションでなんとかなりそうですが、一意性の保証など諸々の理由から、イレギュラーな手法については考えず、この方法を利用した方がいいでしょう。

ちなみに個別にコンパイルも当然できますが、呼び出し元をコンパイルすることで関連するパッケージファイルも勝手にコンパイルされるようです。


●呼び出し元での記述方法
さて呼び出し元。
こちらも難しい事はありません。
普通に java のコーディングができるなら、いつも通りコードを記述していくだけです。
hogecall.java
class hogeCall{
 public static void main(String[] ages){
  hoge.testPackage tp = new hoge.testPackage();
  tp.show();
 }
}

ちなみに hoge の記述を省略したいのなら hoge をインポートしてしまうことで実現できます。
import hoge.*;

class hogeCall{
 public static void main(String[] ages){
  testPackage tp = new testPackage();
  tp.show();
 }
}


●具体的な配置
上記二つのファイルの具体的な配置は以下の通り。
+カレントディレクトリ
 |hogecall.java
  +hoge
   |hoge.java

こんな感じです。
カレント配下に hoge ディレクトリ
あとはカレントに hogecall.java 、hoge ディレクトリ内に hoge.java を配置するだけです。
この状態で hogecall.java をコンパイルすれば、ちゃんとメッセージが表示されるはずです。
D:\Data\java>javac hogecall.java

D:\Data\java>java hogecall
testPkgMain Message


実は本題のアクセス制御やらの確認ができていないのですが、まぁ次回にでも。

2012年10月19日金曜日

[Apache][Tomcat][Java]環境作成

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


以前いくつか類似投稿をしているが自分自身わかりづらかったので一つにまとめてみる。

環境を整えるのに参考にしたサイトリンク
@IT総合トップ > Java Agile > やり直し「JSPとTomcat」(1)
●Tomcat-1.インストール(Windows環境)


01)javaのダウンロード
http://www.oracle.com/technetwork/java/javase/downloads/index.html
上記アドレスから最新の JDK をダウンロードする。
ダウンロードするバージョンを選択すると、選択したバージョンが提供されているプラットフォームの選択リストが表示される。
そこでまずライセンスへの同意意思を示すために「Accept License Agreement」を選択後、ダウンロードしたいプラットフォームのリンクを選択する。
これをしないと警告メッセージが表示されるだけでダウンロードできません(よく引っ掛かるトラップ)


02)インストール
インストール先をデフォルトの program files 配下から C:\Java\~ に変更しただけ。
windows7 などではセキュリティ系の問題とかでめんどくさいことが発生する場合もあるので私はここに変更しています(フルパスにスペースが入るのも回避したいですし)

あとはウィザード通りです。


03)システム変数追加
以下の二つを追加
  JAVA_HOME=C:\Java\jdk1.7.0_07
  PATH=%PATH%;%JAVA_HOME%\Bin


04)Apache Tomcat のダウンロード
http://tomcat.apache.org/download-70.cgi
上記アドレスから最新版を取得する。
「32-bit/64-bit Windows Service Installer」というやつね。


05)インストール
基本ウィザード通りに進行。
変更がある手順は3か所。

1か所目:Configuration
以下の項目を追加

  User Name  admin
  Password  root

2か所目:インストール先
例によって program files 直下回避です。
あとスペースがやたら入っているので、めんどくさいですが全てなくしています。
(確かにスペースあるほうが見やすいとは思います)

  C:\ApacheSoftwareFoundation\Tomcat7.0

3か所目:完了画面
チェックを両方とも外す。
普段常用するならつけていてもいいですが、そうだとしても設定が完了するまでは起動されても困るので外した方がいいかと。


05)システム変数追加
以下を追加
  CATALINA_HOME=C:\Apache Software Foundation\Tomcat 7.0


06)Tomcat起動
スタートメニューから Configure Tomcat を起動
サービスをスタートする
動作確認のため http://localhost:8080/ にアクセス


 これで正しくインストールできたらしく問題なく動作しておりまする。


余談:
テスト中などに発生する問題。
こんなメッセージが表示された場合(主に Win7 とか Vista など UAC が原因らしく)

unable to open the service 'tomcat7'

Tomcat インストール先直下内 Bin 配下にある Tomcat7w.exe のプロパティで互換性タブを選択。
特権レベルグループ内の「管理者としてこのプログラムを実行する」を選択状態にする。

2012年10月15日月曜日

[C#] 非同期処理

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


ファイルの比較処理をテストで作ったのだが、処理が長くなると UI が固まるため、対策として非同期処理を導入することにしました。
今回は web 上で公開されているサンプルなども充実していたので苦労しませんでした。
っと言いたいのですが VisualStudio (っと言うか .NET のバージョンですかね)の違いなどにより使える機能が限定されているため最新の解説ページの情報が役に立たず。
何だかんだと調べてみた結果 VS2008 + C# の組み合わせでは最適と思われる方法で作ってみました。
環境制限などで必要に迫られているようでしたら参考にどうぞ。

今回のテストフォーム上は少しコントロール多めです。
TextBox x 2
Label x 2
Button x 1
ListBox x 1
TextBox に比較対象となるファイルのフルパスを入力しておきます(実行時に入力してもいいですがめんどくさい)
そしてボタンを押すと処理開始となります。
処理結果は ListBox に出力、途中経過は Label に出力します。

とりあえず using 宣言。
using System.IO;            // ファイル操作で使用
using System.Threading;     // スレッド系で使用

次にデリゲートの宣言。
スレッド対象のメソッド(って言う言い方が正しいのか?)を宣言します。
ちなみに宣言位置はクラス内です。
// 非同期処理に利用するデリゲートの定義宣言
private delegate int checkDlgt(string file1, string file2);

// 非同期側スレッド内からフォーム上のコントロールを操作するために利用するデリゲートの定義宣言
private delegate void addStrLog3dlgt(int tgt, string str);

そして非同期側からフォームコントロールを操作するためのメソッド
フォームコントロールは別スレッドからは操作できないため、デリゲート経由で操作することになります。
今回は途中経過を表示したいので、作成しました。
// 非同期側(子プロセス)から listBox3 に追記するためのメソッド
private void addStrLog3(int tgt, string str)
{
    switch (tgt)
    {
        case 1:
            label1.Text = str;      // 処理位置
            break;
        case 2:
            label2.Text = str;      // ファイルサイズ
            break;
        case 3:
        default:
            listBox3.Items.Add(str);
            break;
    }
}

やっとサブスレッドの本体です。
この中でファイルの比較を行います。
// 非同期処理の本体
private int binaryChecking(string file1, string file2)
{
    // もろもろメッセージ出力のための処理
    if (!this.InvokeRequired)
        return 0;

    addStrLog3dlgt asl3 = new addStrLog3dlgt(addStrLog3);

    if (File.Exists(file1) && File.Exists(file2))
    {
        // 指定された2ファイルをバイナリ形式で開く
        byte[] f1 = File.ReadAllBytes(file1);
        byte[] f2 = File.ReadAllBytes(file2);
        // 進捗率表示用
        int iProgress = f1.Count() / 10;

        // 比較元のファイルサイズを表示
        this.Invoke(asl3, 2, f1.Count().ToString());
        this.Invoke(asl3, 3, "start   :" + DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString());
        for (int i = 0; f1.Count() > i; i++)
        {
            if (!(f1[i] == f2[i]))
                break;

            // 進捗状況表示
            if (i % iProgress == 0)
                this.Invoke(asl3, 3, i.ToString("D8") + ":" + DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString());

            // 処理状況表示(非常に処理速度に影響を与えるので下記値を参考に変更)
            //   1=トイレ休憩可能、10=遅い、1000=まぁ待てる、10000=実用範囲
            if( i % 1000 == 0)
                this.Invoke(asl3, 1, i.ToString());
        }
    }
    else
    {
        return 0;
    }

    return 1;
}

そしてコールバックメソッドです。
呼び出したのはいいですが、非同期なのでスレッドいつ終わるかわかりません。
メインスレッドでサブスレッドの終了監視を行うことも可能ですが、それでは結局 UI が固まり本末転倒。
そんなわけで、終わったら自分から申告してもらう必要があります。
そのために利用します。
// コールバックメソッド
private void CallBackMethod(IAsyncResult iAR)
{
    addStrLog3dlgt asl3 = new addStrLog3dlgt(addStrLog3);
    checkDlgt cDlgt = (checkDlgt)iAR.AsyncState;

    string strWork;
    int ret = cDlgt.EndInvoke(iAR);

    if (ret == 1)
        strWork = "finish  :";
    else
        strWork = "abort   :";
    
    this.Invoke(asl3, 3, strWork + DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString());
}

最後にイベント起動部分。
ボタン押下イベントです。
// バイナリファイルの比較
private void button4_Click(object sender, EventArgs e)
{
    // 非同期で動作させるメソッドを指定
    checkDlgt cDlgt = new checkDlgt(binaryChecking);

    // 非同期処理の起動(先方で利用する引数値もここで指定)
    IAsyncResult iAR = cDlgt.BeginInvoke(textBox1.Text, textBox2.Text, new AsyncCallback(CallBackMethod), cDlgt);
}

本当は別途テストプログラムを作成するつもりだったのですが、なんとなく勢いで考えていた処理をそのまま作ってしまいました。
サブスレッドの処理状況表示 if 文の数値を小さくすると格段に処理が遅くなるため、メインスレッド(画面側)の UI が死んでいる様、もしくは死なない様の比較がしやすいかと思います。

余談ですが VS2012 など投稿時点での最新バージョンでは atwait などの処理を利用することで、もっと簡単に高機能な処理を実現できるようです。
バージョン制約が無い方は、その辺も調べられてみてはどうでしょうか。

今回はこれまで。

2012年10月10日水曜日

[C#] App.config の利用方法

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


C#で開発を行っていてアプリの固有情報を保存するにあたり、何の制約もないのに ini ファイルを使うことも無かろう・・・って事で C# スタンダードらしい「アプリケーション構成ファイル( App.config )」を利用することにした。
しかし探し方が下手なのか、もっとも単純な使用例(書いて読む)が見当たらなかったので、自分で書いておく。

コードを書く前に
事前準備として system.configuration.dll への参照を追加する必要がある。
ソリューション エクスプローラで右クリック →「参照の追加(R)...」で表示される「参照の追加」ウィンドウが開く。
.NET タブの中から「System.Configuration」を選択して「 OK 」を押下。

お試し環境は、いつもの Button x 2 と ListBox があるフォーム画面です。

// 下記宣言を追加
using System.Configuration;

// app.config の appSettings に情報を設定
private void button1_Click(object sender, EventArgs e)
{
    // System.Configuration.ConfigurationManager を利用するにあたり
    // System.Configuration.dll への参照を追加する必要がある
    // (ソリューション エクスプローラで右クリック → 参照の追加)

    // Configuration オブジェクトを開く(対象ユーザーは「全てのユーザー」を指定)
    Configuration conf = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

    // 登録情報の作成
    // キー名:KeyName の後ろに通番を付与した書式
    // データ:日付と時間を登録する
    string keyStr = "KeyName" + ConfigurationManager.AppSettings.Count.ToString();
    string valueStr = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();

    // 情報の登録 → 保存 → 情報更新(次回アクセス時)
    conf.AppSettings.Settings.Add(keyStr, valueStr);
    conf.Save(ConfigurationSaveMode.Modified);
    ConfigurationManager.RefreshSection("appSettings");
}

// app.config の appSettings から情報を取得
private void button2_Click(object sender, EventArgs e)
{
    listBox1.Items.Add("--- start reading ----------");

    // App.Config からの読み込み
    // AllKeys は全てのキー名を取得してくれる
    foreach (string keyStr in ConfigurationManager.AppSettings.AllKeys)
        // ピンポイントで取得するだけなら下記1行だけで ok (取得先のキー名がわかっている時など)
        listBox1.Items.Add(ConfigurationManager.AppSettings[keyStr]);

    listBox1.Items.Add("--- end reading ----------");
    listBox1.SelectedIndex = listBox1.Items.Count - 1;
}

Button1 を押すことでデータを登録します。
Button2 を押すことで appSettings よりデータを取得します。
取得したデータは ListBox に追加されます。

データが膨大になった場合のセクション分けもできるらしいのですが、時間なので今回はこれまで。

2012年9月18日火曜日

[C++/CLI] イベント関数の引数についてくるobjectを各コンポーネントにしてみる

[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


たとえば DateTimePicker を使用して期間を指定できるデザインの画面の場合・・・
2種類あったら合計4イベント書くことになるけど、これを使えば1つのイベントを使いまわせる。

System::Void DateValueChanged(System::Object^  sender, System::EventArgs^  e)
{
    System::Windows::Forms::DateTimePicker^  dtWork;
    System::Windows::Forms::DateTimePicker^  dtFrom;
    System::Windows::Forms::DateTimePicker^  dtTo;

    dtWork = safe_cast(sender);

    // 呼び出し元のコンポーネントを判断し、処理対象となるコンポーネントを選定する
    if((dtWork->Name == Date1From->Name) || (dtWork->Name == Date1To->Name))
    {
        dtFrom = Date1From;
        dtTo = Date1To;
    }

    if((dtWork->Name == Date2From->Name) || (dtWork->Name == Date2To->Name))
    {
        dtFrom = Date2From;
        dtTo = Date2To;
    }

    // ・From, To 共に有効な場合 From < To となるように入力後にデータ確認を行う。
    // ・どちらか片方のみ有効の場合は入力制約を設けない。
    // ・片側入力から両側入力になった場合、後から有効になったコンポーネントの指定日付を
    //  先に有効であったコンポーネントの指定日付と合わせる。
    if((dtFrom->Checked && dtTo->Checked) && (dtFrom->Value > dtTo->Value))
        if(dtWork->Name == dtFrom->Name)
            dtFrom->Value = dtTo->Value;
        else
            dtTo->Value = dtFrom->Value;
}

[C++/CLI] 別のプロセスにメッセージを送信する


[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


例によって経過などはリストボックスに追加する方向で

・投げるほう
FindWindow で対象画面を見つけて SendMessage でメッセージを送信。
// stdafx.h に追加したやつら
#include <windows.h>
#pragma comment(lib, "user32.lib")

// 対象ウィンドウの検索
HWND  hWND;
hWND = FindWindow(nullptr, L"送信先画面名");
if(hWND == nullptr)
{
    LBox->Items->Add("not find...");
    return;
}
else
    LBox->Items->Add("find...");

// メッセージ送信
COPYDATASTRUCT cds;

cds.dwData = 0;
cds.cbData = sizeof("送信文字列 ") + 1;
cds.lpData = L"送信文字列";
::SendMessage(hWND, WM_COPYDATA, 0, (LPARAM)&cds);

・受けるほう
WndProc をオーバーライドして独自処理を作成。
その中で送信メッセージを処理する。
// stdafx.h に追加したやつら
#include <windows.h>

// protectedに追加(デストラクタの後ろとか?)
protected:
virtual void WndProc(Message% m) override
{
    switch(m.Msg)
    {
        case WM_COPYDATA:
            // 処理記述
            COPYDATASTRUCT *cds = (COPYDATASTRUCT*)m.LParam.ToInt32();
            String^ str = gcnew String( (wchar_t*)cds->lpData);
            listBox1->Items->Insert(0, str);
            break;
    }
    Form::WndProc(m);
}

[共通] 当 Blog の投稿に関する基本事項について

当 Blog 内に投稿されているすべての記事に適用する基本事項を記載します。

この投稿の内容は当 Blog 内のすべての投稿に適用されます。

特に断りがない限り、投稿主である「AL(ある)」は本投稿の条件を適用した状態で記事を投稿しているとお考えください。
この投稿の内容は状況変化に応じ随時変更いたします。
変更後の内容は(変更以前に投稿したものを含め)すべての投稿に適用いたします。

1)記載内容の真偽について
この Blog は技術系情報の個人的メモ書きサイトとして立ち上げました。
この性質上、投稿内に記載されているコード類は動作確認をしたうえで記載しているはずですが、まれに最終的なコードが反映されていない(=間違っている)可能性があります。
このため記載コードについては参照程度に留め、最終的な動作確認等は閲覧者個人でお願いいたします。

2)事故について
閲覧者が投稿内容を参照し、自身のプログラム等に反映後、結果としてリカバリ不能な事態が発生したとしても投稿主は責任を持ちません。
閲覧した投稿の内容を反映される場合は、閲覧者個人の責任で行ってください。

3)意見・要望について
投稿内容に関する質問や指摘、要望は受け付けております。
もしこれらの連絡を取りたい場合はコメント欄に記載していただくか、投稿主宛にメールを送ってください。
ただし頂いた質問や要望についての回答義務は負わないものとします。
(公開する以上、読みやすい、意味のあるものにしたいため前向きに対応はいたします)

2012年8月25日土曜日

[C++/CLI] いろいろメモ


[共通] 当 Blog の投稿に関する基本事項について
→ http://fuunyan.blogspot.jp/2012/09/blog.html


え~新作業場にてVC++で画面作成をすることになり四苦八苦しならが作成しているときに使った、非常に個人的なメモ投稿となっております。
まぁそれでも似たような事を知りたい人がいるかもしれないと公開しました。
この辺の事情を察して、ご覧ください。
当分の間、随時更新されていくはずです。
後付けし続けると大変長い投稿になるので項目ごとに投稿するように修正予定。
そんなわけで追加分は下書きとして保存中・・・。

●関連・面白そうサイトメモ
http://www.plustarnet.com/aspil/Programming/

http://www.atmarkit.co.jp/fdotnet/special/cppcli/cppcli_03.html

・前田稔(Maeda Minoru)の超初心者のプログラム入門
http://www.eonet.ne.jp/~maeda/index.html

方法 : System::String を wchar_t* または char* に変換する
http://msdn.microsoft.com/ja-jp/library/d1ae6tz5(v=vs.80)


公式に面白そうな企画があったっぽい
Visual Studio 2008 でゲームを作ろう 第 1 回プロジェクトファイル
http://www.microsoft.com/ja-jp/download/details.aspx?id=14288


・C++/CLI: とある文字列の相互変換(コンバージョン)
http://codezine.jp/article/detail/4774

・Programing Enlightened 文字列操作
http://vene.wankuma.com/prog/CppCli_strings.aspx


方法 : さまざまな文字列型間で変換する
http://msdn.microsoft.com/ja-jp/library/ms235631(v=vs.80).aspx

●さすがに個別記事化するほどでもない技術情報

マネージ?アンマネージ?
#pragma unmanaged
#pragma managed


コマンドライン引数の値を取得
// 以下の順で指定されているのを想定
// [1] iniファイルへのフルパス
// [2] 
// [3] 
array<String^>^ args = System::Environment::GetCommandLineArgs();

if(args->Length > 1)
    iniPath = args[1];

2012年7月16日月曜日

まずはDB環境

環境もできたので早速 Java でアクセス・・・と言いたいところですが、データが無ければアクセスも何もあったものではない。
そんなわけで、まずは DB のデータ環境を作成することにした。
手持ちの参考資料に構成が掲載されているため DB 設計は省略。
も少し余裕出てきてから、そこには着手しましょう。

ちなみに参考にしている資料はこちら (・ω・ つ 「新標準 PostgreSQL」

購入時にいろいろ見回った結果、いろんな言語からのアプローチが書いてあったためこれにしてみました。

余談はこれくらいにして、早速作業開始


01)ユーザー作成(P53)

まずやることはユーザー作成。
いつまでも管理権限でやるわけにはいきません。

一般ユーザーを作成します。
ちなみに今後、特に断り無ければ SQL Shell の psql を使用していきます(スタートメニューの PostgreSQl 内にショートカットあり)

postgres=# create role normal1 createdb;
CREATE ROLE
postgres=# \password normal1
新しいパスワード:n1
もう一度入力してください:n1
postgres=#

こんな感じでユーザー作成完了です。
ちなみにパスワード部分は本来可視されませんが、備忘録兼ねているため表示しています。

さて気持ちよくテーブル作成に移ろうかと思ったのだが、ここで早速トラブル。
テーブルを作成するに辺り作成したユーザーでログインしようとしたら、できないと言われたのだ。
PostgreSQLに詳しい方なら、上のコマンドを見ればすぐに理由が判明するでしょう。

postgres-# \c postgres normal1
ユーザ normal1 のパスワード:
FATAL:  ロール"normal1"はログインすることが許されていません
以前の接続は保持されています。

さて詳しくない僕は何とか理由を突き止めなければなりません。
作成をミスった・・・訳ではないらしい。
「許されていません」と言うことは権限判定はされているのでユーザーは作れているはず。
そんなわけでユーザー一覧を確認するコマンドを調べて実行してみた。

postgres-# \dg
                                         ロール一覧
 ロール名 |                                 属性                                 | メンバー
----------+----------------------------------------------------------------------+----------
 normal1  | DBを作成できる, ログインできない                                     | {}
 postgres | スーパーユーザ, ロールを作成できる, DBを作成できる, レプリケーション | {}

ログインできない・・・って、おぃ(^^;
どー考えても権限問題ですね。
っとなると作成時のコマンドミスって事になります。
とりあえず \h コマンドを実行して、どんなコマンドがあるのか確認。
すると CREATE ROLE 以外に CREATE USER なんていうのもあります。
今回の問題に対する回答は CREATE USER のリファレンス内にありました。

PostgreSQL 9.1.4文書 CREATE USER

要するに CREATE ROLE だとログイン権限が付与されないって訳です。
はい、ロールの権限について勉強になりましたね。
さっさと権限を付与しましょう。

postgres-# \dg normal1
                       ロール一覧
 ロール名 |               属性               | メンバー
----------+----------------------------------+----------
 normal1  | DBを作成できる, ログインできない | {}

postgres=# alter role normal1 LOGIN;
ALTER ROLE
postgres=# \dg normal1
              ロール一覧
 ロール名 |      属性      | メンバー
----------+----------------+----------
 normal1  | DBを作成できる | {}

postgres=# \c postgres normal1
ユーザ normal1 のパスワード:
データベース "postgres" にユーザ"normal1"として接続しました。
postgres=>

・・・やっとユーザー作成完了です
ある程度のトラブルとか知識不足は覚悟していましたが、これはまったく先が思いやられますね。
とにかく先に進みましょう。

02)データベースの作成(P56)

所有者にしたいロールでのログインは終わっているので、いきなり CREATE DATABASE です。

postgres=> create database study1;
CREATE DATABASE
postgres=> \l
                                           データベース一覧
   名前    |  所有者  | エンコーディング |    照合順序     | Ctype(変換演算子) |      アクセス権
-----------+----------+------------------+-----------------+-------------------+-----------------------
 postgres  | postgres | UTF8             | Japanese, Japan | Japanese, Japan   |
 study1    | normal1  | UTF8             | Japanese, Japan | Japanese, Japan   |
 template0 | postgres | UTF8             | Japanese, Japan | Japanese, Japan   | =c/postgres          +
           |          |                  |                 |                   | postgres=CTc/postgres
 template1 | postgres | UTF8             | Japanese, Japan | Japanese, Japan   | =c/postgres          +
           |          |                  |                 |                   | postgres=CTc/postgres
(4 行)

ざっくりオプションを省略しましたが、ちゃんとできているようです。
さて、倉庫はできたので、次に倉庫の中に入れる箱を用意しましょう。

03)テーブルの作成(P61)

やっと本題とも言えるテーブル作成です。

postgres=> create table Company_Detail(
postgres(>     Code    int PRIMARY KEY,
postgres(>     Name    varchar NOT NULL,
postgres(>     Address varchar);
NOTICE:  CREATE TABLE / PRIMARY KEYはテーブル"company_detail"に暗黙的なインデックス"company_detail_pkey"を作成します
CREATE TABLE
postgres=>
postgres=> create table Post_Name(
postgres(>     Code    int PRIMARY KEY,
postgres(>     Name    varchar NOT NULL);
NOTICE:  CREATE TABLE / PRIMARY KEYはテーブル"post_name"に暗黙的なインデックス"post_name_pkey"を作成します
CREATE TABLE
postgres=>
postgres=> create table userinfo(
postgres(>     No           serial PRIMARY KEY,
postgres(>     First_Name   varchar NOT NULL,
postgres(>     Last_Name    varchar,
postgres(>     Company_Code int REFERENCES Company_Detail(Code),
postgres(>     Post_Code    int REFERENCES Post_Name(Code),
postgres(>     User_ID      varchar NOT NULL,
postgres(>     Password     varchar NOT NULL);
NOTICE:  CREATE TABLEはシリアル列"userinfo.no"用に暗黙的なシーケンス"userinfo_no_seq"を作成します。
NOTICE:  CREATE TABLE / PRIMARY KEYはテーブル"userinfo"に暗黙的なインデックス"userinfo_pkey"を作成します
CREATE TABLE
postgres=> \d
                リレーションの一覧
 スキーマ |      名前       |     型     | 所有者
----------+-----------------+------------+---------
 public   | company_detail  | テーブル   | normal1
 public   | post_name       | テーブル   | normal1
 public   | userinfo        | テーブル   | normal1
 public   | userinfo_no_seq | シーケンス | normal1
(4 行)

とりあえずできたようです。
いきなり3テーブルも作ったのに、うまくいったものです。
今のところエラーは無いようですし、この調子でデータの登録もやってしまいましょう。

04)データの登録(P62)

データの登録です。
テーブル作成時に REFERENCES を指定しているので順番を注意しないといけません。
postgres=> insert into Company_detail values( 1, '株式会社 風娘工房', 'ディオン教会脇' );
INSERT 0 1
postgres=> insert into Company_detail values( 2, 'カナリス コーポレーション', 'グルーディオ城の村1ー2' );
INSERT 0 1
postgres=> insert into Company_detail values( 3, '有限会社 雷娘電気' );
INSERT 0 1
postgres=>
postgres=> insert into Post_Name values
postgres->     ( 1, '会長' ),
postgres->     ( 5, '社長' ),
postgres->     ( 10, '部長' ),
postgres->     ( 99, '一般' );
INSERT 0 4
postgres=>
postgres=> insert into userinfo (First_Name, Last_Name, Company_Code, Post_Code, User_ID, Password) values
postgres->     ('カナリス', '', 2, 1, 'Canaris','Cana'),
postgres->     ('風娘', '', 1, 1, 'Fuunyan', 'Fuu'),
postgres->     ('雷娘', '', 3, 5, 'Rainyan', 'Rai'),
postgres->     ('マーニャ', '', 1, 99, 'Marnya', 'Mar'),
postgres->     ('フィルビス', 'ホーリーウッド', 1, 10, 'Filvis', 'Fil');
INSERT 0 5
postgres=> select * from Company_Detail;
 code |            name            |         address
------+----------------------------+--------------------------
    1 | 株式会社 風娘工房         | ディオン教会脇
    2 | カナリス コーポレーション | グルーディオ城の村1ー2
    3 | 有限会社 雷娘電気         |
(3 行)

またエラー無しで登録できました。
内容を確認してみましょう。

postgres=> select * from Company_Detail;
 code |            name            |         address
------+----------------------------+--------------------------
    1 | 株式会社 風娘工房         | ディオン教会脇
    2 | カナリス コーポレーション | グルーディオ城の村1ー2
    3 | 有限会社 雷娘電気         |
(3 行)

postgres=> select * from Post_Name;
 code | name
------+------
    1 | 会長
    5 | 社長
   10 | 部長
   99 | 一般
(4 行)

postgres=> select * from userinfo;
 no | first_name |   last_name    | company_code | post_code | user_id | password
----+------------+----------------+--------------+-----------+---------+----------
  1 | カナリス   |                |            2 |         1 | Canaris | Cana
  2 | 風娘       |                |            1 |         1 | Fuunyan | Fuu
  3 | 雷娘       |                |            3 |         5 | Rainyan | Rai
  4 | マーニャ   |                |            1 |        99 | Marnya  | Mar
  5 | フィルビス | ホーリーウッド |            1 |        10 | Filvis  | Fil
(5 行)

company_code post_code REFERENCES オプションを指定しているため、まずは Company_Detail Post_Name にデータを登録しています。
こうしないと参照したいデータがないと言われてエラーが発生します(まぁ回避策あるようなのですが、それはまた別途)

postgres=> insert into userinfo (First_Name, Last_Name, Company_Code, Post_Code, User_ID, Password) values ('ほげ', 'ほげほげ', 10, 1, 'hoge','hoge');

ERROR:  テーブル"userinfo"への挿入、更新は外部キー制約"userinfo_company_code_fkey"に違反しています
DETAIL:  テーブル"company_detail"にキー(company_code)=(10)がありません
postgres=>

エラーに出ているとおり会社コードに 10 なんて登録していませんから、エラーになります。

これでデータ側の事前準備完了・・・ですかね。
さーて、やっとアプリ作って、アクセスできるようになりましたよ。

そのあたりは次回。

とりあえず環境作成してみる

仕事や開発関係の記事はプライベート記事とは分けて管理したかったので、もう一つ Blog を作ってみた。

そんなわけで本題。
いろんな事情から PostgreSQL とか Java とか PHP とか、まぁその辺りのことについてある程度知っておいた方がいいって事になって(自分の中で)本格的に勉強することにした。
ぶっちゃけると Windows アプリの作成ノウハウはあるんだけど Web 系のノウハウは、無いに等しく今更知りません・・・って訳にもいかない訳でして・・・。

・・・ってこういう記事は何度か投稿しているので、続けないとね。

とりあえず手持ちの資料とかの都合から Java と PostgreSQL の環境で勉強してみることにする。
ちなみに作業環境は Win7 の 64bit です。

とりあえず Java は JDK6 update27 が入っていたので、それを使うことに。
PostgreSQL は入っていないので(正確には裏に放置している XP にはインストール済み)最新版を落としてきてインストール。
まずは、そのあたりのメモ書き(備忘録ともいう)

01)入手
まずは日本PostgreSQLユーザ会のトップからリンクを辿って Win64版のインストーラーを入手。
http://www.enterprisedb.com/products-services-training/pgdownload#windows
これを書いているときは ver.9.1.4 が最新のようなので、それを落としました。

02)インストール開始
ダウンロードが完了したら早速実行。
基本ウィザードなので Next クリックのみ。

インストール先
C:\Program Files\PostgreSQL\9.1

データディレクトリ
D:\PostgreSQL\9.1\data

password(ローカル勉強用なので公開して問題なし!)
  pg

Port
5432

Advanced Option
Japanese, Japan
※どうも C を選んだ方がいい、みたいなサイトもあるのですが、とりあえず失敗も経験なのでこの設定で行きます。

これでインストール前設定完了。
インストールが完了するのを待って終了押下。

終了画面でチェックを外さなかったので、スタックビルダなるものが起動する。
要するに便利ツールのインストールをしてくれるらしいのですが、今回はとりあえずキャンセル。
後でインストールできる・・・はず。

03)スタックビルダでの設定
手持ち資料を参照すると、ここから Apache やらをインストールするらしいです(^^;
っと言うわけで早速スタートメニューから起動(ほら後からでも設定できる・・・)
コンボボックスからインストール間もない設定を選択して次へ。
インストールした物は以下の通り

Database Drivers
pgJDBC v9.1-901-1

Web Development
Apache/PHP v2.2.22-5.3.10-1
phpPgAdmin v5.0.4-1

04)Apache/PHP のインストール
スタックビルダがダウンロードしてくれたファイルを元にインストール開始。

インストール先
C:\Program Files (x86)\PostgreSQL\EnterpriseDB-ApachePHP
ポート
8080

05)pgJDBCのインストール

インストール先
C:\Program Files (x86)\PostgreSQL\pgJDBC

06)phpPgAdminのインストール

ホスト
127.0.0.1

ポート
5432

PostgreSQLホームディレクトリ
C:\Program Files\PostgreSQL\9.1

っとここまでで、やっとインストールが完了。
とりあえず phpPgAdmin にログインしてみる。
phpPgAdmin をメニューから開いて(もしくは http://localhost:8080/phpPgAdmin/ とアドレスを入力)左にあるツリーのトップから「サーバー」を選択。
すると右側に現在登録されているサーバーの一覧がでるので PostgreSQL を選択するとログイン画面になります。
ここでユーザーとパスワード入れるのですがパスワードはともかくユーザーは?
っと思って調べたら postgres っていうのがいわゆる root とか administrator みたいなやつらしいです。
ササッと入力してログイン押下すれば、ログイン完了。
とりあえず PostgreSQL の内容が見られるようになったはず。

・・・とりあえずここまで。
この先も問題山積のはずだけど、無理はよくない。