VMware Server

VirtualBox はなんつーかもう耐えられない。
Mono を使ってプログラミングを始めたら気になる部分が我慢できなくなった。

文字入力で無意識に「半角/全角」キーを押して SCIM がチラツキしてしまう。
流石にプログラミングをやっていると意識しているつもりでも何度もやってしまう。
回避する方法も無いようだしキビシイ。

それに Linux ゲストだとマウス統合を無効にできないことが辛過ぎる。
私的には意図的にゲストを使っている状態では決してホストに戻ってはいけない。
VMware Player では当然無効にして使っていた。

つーことで、もういいかげんに専用マシンを用意したほうが良さそうだとも思うんだが…
ものは試しで VMware Server を導入してみた。

VMware Serverのダウンロード、無償のVMware、仮想サーバ – VMware

ちなみに Opera ではユーザー登録ができませんので Firefox か IE で。
インストールは下記を参考にした。

仮想サーバの構築(VMware Server: WindowsXP編)

まあインストールはすんなり終わった。
しかしなんか最初からマウス統合されていて OS 間を跨げた、何故?
どうやら 8.04 は VMware 関連のドライバが標準装備のようである。

/usr/X11/xorg.conf でマウスの設定を見るとあらかじめ vmmouse になっていた。
単なる mouse に書き換えて Ctrl+Alt+BackSpace で X の再起動!
よしよし、コレで邪魔なだけのマウス統合は無効になったぞと。

解像度はなにもしなくても沢山登録されていた。
xorg.conf には何も指定していないんだけど…よく解らない。
まあ都合がいいのでこのまま使おう。

せっかくなので 1440×900 に解像度アップ。
ツールバーとステータスバーを非表示にすれば丁度いいサイズになった、よしよし。

VMware Tools はあえてインストールしないで使う予定。

うん!「半角/全角」キーでのイライラは解消したぞと。
それと SCIM の設定を開いて

・全体設定のホットキー
・Anthy のキーバインド

から Ctrl+Space を削除しておきましょう。
Visual Studio 同様 Mono Developer のコード補完もこのキーでイケます。
消さないと横取りされてしまう、両方消さないと???になります。

あぁこれで Mono Developer によるプログラミングが快適になりそうだ。
しかしクリップボードの共有が使えなくなったのは痛いかも。

out

using System;

namespace test_cs1
{
    class MainClass
    {
        private static void Seifuku(out string a, out string b)
        {
            a = "水色の";
            b = "セーラー服";
        }
        public static void Main(string[] args)
        {
            string s = "紺色の";
            string t = "ブレザー";
            Seifuku(out s, out t);
            Console.WriteLine(s + t);
            Console.Read(); //閉じないように
        }
    }
}

なんだ、ref で string を参照渡しがどーのとか以前書いたがこうすればイイんだ。
引数指定の両方に out を付けるだけね、ふんふん。

この方法ならば C/C++ っぽく書ける、無理に C/C++ っぽくする必要は無いと思うけど。
どうしても戻り値に string を使いたくない場合があるのよ。
とにかくコレで IniFile8 C# 版も同様の処理が可能になるな。

常識だったりして…
個人的には yield return 並に衝撃だったんだけど…
C# はまだ始めて三ヶ月なのでまあこんな感じです。

#include <stdio.h>

void Seifuku(char *a, char *b)
{
	sprintf_s(a, 256, "%s", "水色の");
	sprintf_s(b, 256, "%s", "セーラー服");
}
int main(int argc, char* argv[])
{
	char s[256];
	char t[256];
	sprintf_s(s, 256, "%s", "紺色の");
	sprintf_s(t, 256, "%s", "ブレザー");
	Seifuku(s, t);
	printf("%s%s\n", s, t);
	getchar(); //閉じないように
}

こんなコードに比べたら遙かに解りやすい構文だし。

試しに mono でもやってみたけど普通にコンパイル完了。
なるほど、C# 言語仕様は見事にトレースしているんだ。

つーか

Mono って StreamReader も foreach 文も Windows と全く同様に扱える。
それどころか Generic も同じように問題無く使える。
ココまで再現していると思わなかった、違うのは GUI 部分だけだ。
こりゃどっちでも使えるライブラリを作らなきゃ損って気分。

何をやろうとしているかは何となく解るでしょう。

大嘘を書いた

Mono での GUI 開発の続き。

オイラはアホウだった…

Gtk# 2.0 プロジェクトでとりあえずプロジェクトを作ると
MainWindow.cs ってファイルが作成されとるじゃないの!
このファイルをWクリックすれば普通に GUI 開発が…

DeleteEventHandler もリソース内で…自分で追加する必要は無かった。
気を取り直して、、、、、

コンテナから Fixed を選んでデザイナにD&D。
すると従来の Windows RAD 開発のような配置でウイジェットが置ける。
ウイジェットツリーで編集したいパーツを選んでプロパティからラベルとかを編集。
異様に CPU 負荷が高いんだが…仮想マシンなせいかもしれないけど。

イベントハンドラは「シグナル」のタブを選ぶ。

のイベントのトコに希望するハンドラ名を記述するわけだ。
Visual Studio のようにWクリックしても自動で名前は付けてくれない。
Ctrl+A で全選択して自分で書き込んでくだされですね。
でも書き込みさえ行えばソースやリソースへのへの追加は自動でやってくれる。

using System;
using Gtk;

public partial class MainWindow: Gtk.Window
{    
    public MainWindow (): base (Gtk.WindowType.Toplevel)
    {
        Build ();
    }
    
    protected void OnDeleteEvent (object sender, DeleteEventArgs a)
    {
        Application.Quit ();
        a.RetVal = true;
    }
    //↑までは最初から作成されている
    //上記操作でハンドラが作成されるので内容を自前で書く
    protected virtual void button1_Click (object sender, System.EventArgs e)
    {
        MessageDialog dlg = new MessageDialog(
            (Window)this,
            Gtk.DialogFlags.Modal,
            Gtk.MessageType.Info,
            Gtk.ButtonsType.Ok,
            "スカートも水色!\n復活希望!");
        dlg.Run();
        dlg.Destroy();
    }
}

これだけで良かったのね、ウソを書いてごめんなチャイ!

ちなみにハンドラ名を変更したい場合はシグナルの所で変更すれば自動反映される。
自動でハンドラ名を付けてくれない以外は Windows の開発と変わらないねコレ。

Gtk#

Gtk# とやらで Linux の GUI 作りを試してみる。
実体は思いっきりというか単なる Gtk+ のラッパーらしいんだが…

とりあえず Gtk# 2.0 プロジェクトを新規で作成する。
static void Main だけの雛形コードが作成された。
そのままビルドすればとりあえずウインドは表示されるようである。

とりあえずボタンを押すとメッセージを表示する初心者アプリでも。
色々探したが情報少なすぎ、見つけたコードを色々試してみる。

Gtk.MessageDialog 関数を使えばいいようだとまでは解ったのだが…
一番迷ったのは引数の一番最初に指定する Gtk.Window をどう指定するか。
それ以外はコード補完機能のおかげでなんとなく解ったがココでどうしても詰まる。

後で解ったんだけどメインの class では static メソッドは Main 一つにすべし!

GtkSharp – Mono

ココのスクリーンショットを見ると RAD での GUI デザインできそうなんだが…
方法が解らない、情報が全く見つからない、まあコードでなんとかしよう。

CodeProject: A google search application using Gtk#. Free source code and programming help

Glade なんてのがあるんだ。
「追加と削除」から Glade3 をインストール。
でもよく解らない…今回は下の方にあるコードを見てなんか作ってみよう。

using System;
using Gtk;
//using Glade; //後で

namespace gtkApp2
{
    class Test
    {
        private Window win;

        public static void Main (string[] args)
        {
            new Test(args);
        }
        public Test(string[] args)
        {
            Application.Init();
            win = new MainWindow();
            win.DeleteEvent += new DeleteEventHandler(win_DeleteEvent);
            Button btn = new Button();
            btn.Label = "水色の";
            btn.Clicked += new EventHandler(btn_Clicked);
            win.Add(btn);
            win.Show ();
            btn.Show();
            Application.Run ();
        }
        private void win_DeleteEvent(object o, DeleteEventArgs args)
        {
            Application.Quit();
            args.RetVal = true;
        }
        private void btn_Clicked(object sender, EventArgs e)
        {
            MessageDialog dlg = new MessageDialog(
                          this.win,
                          Gtk.DialogFlags.Modal,
                          Gtk.MessageType.Info,
                          Gtk.ButtonsType.Ok,
                          "セーラー服");
            dlg.Run();
            dlg.Destroy();
        }
    }
}

で、ウインドサイズに広がったボタンを押すとメッセージダイアログが出る。
というしょーもないアプリを作るのに三日も使った情けないプログラマーであった。

Gtk ってレイアウトという概念があるんだね。
ボタンがウインドサイズになった時に思わず「おまえは WPF か!」と思ったけど
よく考えたら Java にもレイアウタがあったっけか。

そうか、インターフェイスデザインはそういう方向に向かっているんだなぁと。
Windows も WPF でやっと時代に追いついたってことだね。

まあなんとか Gtk# はどういうもんか解ってきたぞと。

OPENFILENAME は古い 2

SHCreateItemFromParsingName についてだが3番目の引数に

IShellItem *psi = NULL;
SHCreateItemFromParsingName(szDoc, NULL, IID_IShellItem, (LPVOID *)&psi);

なんてありがちな IID を付けたらすんなり通ってしまった。
しかしどこを探しても引数が3つなのは何故?
まあこれで IFileDialog 関連はなんとかなりそうだ。

自前関数とかそのままだけーがこんな感じで。

/*
 * (*.mpg|*.mpeg) を (*.mpg;*.mpeg) に変換
 * セミコロンって INI の仕様ではコメントなのよね
 */
BOOL SemicolonReplace(LPWSTR _Dst, LPWSTR _Src)
{
	for (int i=0; i<256; i++)
	{
		if (_Src[i] == '\0') {
			_Dst[i] = '\0';
			return TRUE;
		} else if (_Src[i] == '|') {
			_Dst[i] = ';';
		} else {
			_Dst[i] = _Src[i];
		}
	}
	return FALSE;
}

void CCinemaWindow::DoOpenDlg()
{
	IFileDialog *pDlg = NULL;
	IShellItem *psi = NULL;
	HRESULT hr;

	hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_IFileDialog, (LPVOID *)&pDlg);
	if (FAILED(hr)) {
		DoMessage(hWnd, L"Errer IFileDialog Create");
		pDlg->Release();
		return;
	}
	//拡張子フィルタの登録
	wchar_t filter[3][256];
	SemicolonReplace(filter[0], IniSetting.szExt01);
	SemicolonReplace(filter[1], IniSetting.szExt02);
	SemicolonReplace(filter[2], IniSetting.szExt03);
	COMDLG_FILTERSPEC filterspec[] =
	{
		{IniSetting.szExtA, filter[0]},
		{IniSetting.szExtB, filter[1]},
		{IniSetting.szExtC, filter[2]}
	};
	hr = pDlg->SetFileTypes(3, filterspec);
	if (FAILED(hr)) {
		DoMessage(hWnd, L"Errer SetFileTypes1");
		pDlg->Release();
		return;
	}
	//フィルタの選択インデックス変更(先頭は 1 である)
	hr = pDlg->SetFileTypeIndex((UINT)IniSetting.nExtNo + 1);
	if (FAILED(hr)) {
		DoMessage(hWnd, L"Errer SetFileTypeIndex");
		pDlg->Release();
		return;
	}
	//初期選択フォルダ
	if (fname::FileExists(PlayFileInfo.FullPath))
	{
		hr = SHCreateItemFromParsingName(PlayFileInfo.FilePath, NULL, IID_IShellItem, (LPVOID *)&psi); //IID_PPV_ARGS(&si)
	} else if (fname::DirectryExists(IniSetting.szSFolder)) {
		hr = SHCreateItemFromParsingName(IniSetting.szSFolder, NULL, IID_IShellItem, (LPVOID *)&psi);
	} else {
		wchar_t szDoc[256];
		SysUtils::get_MyDocumentString(szDoc);
		hr = SHCreateItemFromParsingName(szDoc, NULL, IID_IShellItem, (LPVOID *)&psi);
	}
	if (FAILED(hr)) {
		DoMessage(hWnd, L"Errer CreateItemFromParsingName");
		psi->Release();
		pDlg->Release();
		return;
	}
	hr = pDlg->SetFolder(psi);
	if (FAILED(hr)) {
		DoMessage(hWnd, L"Errer SetFolder");
		psi->Release();
		pDlg->Release();
		return;
	}
	psi->Release();
	psi = NULL;
	//ダイアログの表示
	hr = pDlg->Show(hWnd);
	if (SUCCEEDED(hr))	// FAILED ではキャンセルを拾ってしまう
	{
		wchar_t *pFileName = NULL;
		hr = pDlg->GetResult(&psi);
		if (FAILED(hr)) {
			DoMessage(hWnd, L"Errer GetResult");
			psi->Release();
			pDlg->Release();
			return;
		}
		hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pFileName);
		if (FAILED(hr)) {
			DoMessage(hWnd, L"Errer GetFileName");
			psi->Release();
			pDlg->Release();
			CoTaskMemFree(pFileName);
			return;
		}
		UINT nIndex;
		hr = pDlg->GetFileTypeIndex(&nIndex);
		if (FAILED(hr)) {
			DoMessage(hWnd, L"Errer GetFileTypeIndex");
			psi->Release();
			pDlg->Release();
			CoTaskMemFree(pFileName);
			return;
		}
		hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pFileName);
		if (FAILED(hr)) {
			DoMessage(hWnd, L"Errer GetDisplayName");
			psi->Release();
			pDlg->Release();
			CoTaskMemFree(pFileName);
			return;
		}
		//完走したので処理
		int nExtNo = (int)nIndex - 1;
		if (IniSetting.nExtNo != nExtNo)
		{
			SetPlayFileInfo(L"c:\\a\\a"); //拡張子が無いのでクリアになる
			SetListupMenu(nExtNo, TRUE);  //本体のセレクトを変更
		}
		DirectA->SetURL(hWnd, pFileName);
		//end
		CoTaskMemFree(pFileName);
		psi->Release();
	}
	pDlg->Release();
}

長いよぉ!だから COM は…

を選択したら本体のリストアップ選択も変更したかっただけなのに。

さて、問題は XP 版をどうするかだ…又 #ifdef で分けるしかないか。
こっちに都合がイイように仕様変更しまくったのだどうしよう…