Windows」カテゴリーアーカイブ

Clipoli 0.0.1

Windows アプリ作りの為に新しいノートを買うか迷っている。
が、よく考えたら VirtualBox 仮想マシンの Vista があるじゃないか。
Aero が使えず遅いので全然使わなかったから存在を忘れる所だった。

これを使えばホストの Ubuntu で使っているマジェスタッチで打てる。
あぁキーボード入力が楽々!

しかもコイツには Visual Studio 2008 pro をインストールしてある。
Debug が楽々、コード保管で楽々、リファクタで楽々、何もかもが楽々!
流石はアップグレードに六万円も払っただけはある、これぞ統合開発環境。
テキストエディタだけで開発をしばらくやった後で触るとマジで神アプリ。

でも基本的にこんなものをインストールしないで作れるアプリにしたい。
ので bat の W クリックだけでビルドできるようにアプリ作成を続ける。

とにかく clipoli というタスクトレイ常駐するアプリだ。
当面はランチャとクリップボードにテキストを入れる機能を本格的に。
カスタマイズが簡単にできるように何かのファイルからデータを入れる。

よし、INI ファイルだ。

あぁ Opera 使いの性。

Opera 屋以外はアドオンと考えそう…

ま、仕様をアッサリ決められるかどうかがプロと凡人の違い。
なんだけど後悔することのほうが多いのは書くまでもない。

[Launcher]
メモ帳=C:\Windows\notepad.exe
端末=C:\Windows\System32\cmd.exe
MSDN=http://msdn.microsoft.com/ja-jp/library/

[Clipboard]
けいおん=あずにゃん
俺妹=きりの
えむえむ=みおたん
声優=全部同じ人だった...

みたく INI を作り読み込んでメニューの作成という流れでとりあえずいこう。
C# での INI 読み書きは以前作っているから簡単だ。

UTF-8 の ini 読み書き

と思ったけど今見ると書いていることが少々古い…
てか MessageBox をココで使うと WindowsForm だと using が足らない。
Python で作ったの同様に間違いは例外を投げるように変更して書き換えなきゃ。

そんなことより、このアプリの場合はこんな複雑なコードいらないヤン…

iniread.cs

using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

namespace IniFile
{
	public struct Ini
	{
		public string section {get; set;}
		public string key {get; set;}
		public string value {get; set;}
	}
	public static class IniReader
	{
		public static IEnumerable<Ini> ReadLines(string path)
		{
			if (File.Exists(path))
			{
				string section = "";
				using (var sr = new StreamReader(path, Encoding.UTF8))
				{
					string buf;
					while ((buf = sr.ReadLine()) != null)
					{
						if (buf == "") continue;
						if (buf[0] == ';') continue;
						int len = buf.Length;
						if (len > 2 && buf[0] == '[' && buf[len - 1] == ']')
						{
							section = buf.Substring(1, len - 2);
						}
						else if (section == "")
						{
							//何もしない
						}
						else
						{
							int lPos = buf.IndexOf('='); //見つからない場合は -1
							if (lPos > 0)
							{
								Ini ini = new Ini();
								ini.section = section;
								ini.key = buf.Substring(0, lPos);
								ini.value = buf.Substring(lPos + 1, len - lPos - 1);
								yield return ini;
							}
						}
					}
				}
			}
		}
	}
}

これで十分だった、struct を yield で戻せば必要な値は取れるし。
んでコレを展開するコードを。

mainsrc.cs

using System;
using System.IO;
using System.Windows;
using System.Windows.Forms;
using System.Drawing;
using System.Reflection; // MethodInfo
using IniFile;

namespace Clipoli
{
	class TrayIcon
	{
		private NotifyIcon _icon;
		private ContextMenuStrip _menu;
		public TrayIcon()
		{
			_menu = new ContextMenuStrip();
			int i = 0;
			foreach (var ini in IniReader.ReadLines("default.ini"))
			{
				// Load Launcher
				if (ini.section == "Launcher")
				{
					_menu.Items.Add(ini.key, null, on_launcher);
					_menu.Items[i].Name = ini.value;
					i++;
				}
				// Load Clipboard
				else if (ini.section == "Clipboard")
				{
					_menu.Items.Add(ini.key, null, on_clipboard);
					_menu.Items[i].Name = ini.value;
					i++;
				}
				else
				{
					MessageBox.Show("現在 Launcher と Clipboard 以外のセクションは無効です");
				}
			}
			_menu.Items.Add("-");
			_menu.Items.Add("バージョン情報", null, on_about);
			_menu.Items.Add("-");
			_menu.Items.Add("終了", null, on_exit);
			// create tray icon
			_icon = new NotifyIcon();
			_icon.ContextMenuStrip = _menu;
			_icon.Icon = Icon.ExtractAssociatedIcon("clipoli.exe");
			_icon.Text = "Description Text";
			_icon.Visible = true;
			_icon.MouseUp += on_click;
		}

		private void on_click(object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Left)
			{
				MethodInfo mi = typeof(NotifyIcon).GetMethod("ShowContextMenu",
								BindingFlags.Instance | BindingFlags.NonPublic);
				mi.Invoke(_icon, null);
			}
		}
		private void on_launcher(object sender, EventArgs e)
		{
			try
			{
				string s = (sender as ToolStripMenuItem).Name;
				System.Diagnostics.Process.Start(s);
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}
		private void on_clipboard(object sender, EventArgs e)
		{
			try
			{
				string s = (sender as ToolStripMenuItem).Name;
				Clipboard.SetText(s);
				System.Media.SystemSounds.Beep.Play();
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}
		private void on_about(object sender, EventArgs e)
		{
			var about = new About(_icon.Icon);
			about.ShowDialog();
		}
		private void on_exit(object sender, EventArgs e)
		{
			_icon.Visible = false;
			Application.Exit();
		}
	}
	class __main__
	{
		[STAThread]
		public static void Main(string[] args)
		{
			var n = new TrayIcon();
			Application.Run();
		}
	}
}

ContextMenu は ContextMenuStrip に変更したほうが扱いやすいみたいだったので。

その ContextMenuStrip.Name プロパティに文字列を入れるというウソみたいなことをやった。
だってこれで動くもの、sender から簡単に文字列が得られるし。

トレイアイコンの左クリックでメニューを出す方法は下記をパクった。
NotifyIconの左クリックでコンテキストメニューを表示させる:Gushwell’s C# Dev Notes

見えない Form をアクティブにするなんて方法も見つけたけど上手くいかなかった。
というか、こんなに強引なのに一番簡単かつ短いコードで終わるという。

想像していたより遥かに短いコードでこれだけ実装できてしまった。
私的にはもうコレで十分、ini の書き方が解らない人なんていないだろうし。
後は何を付けよう、というわけでまとめて zip

clipoli001.zip

しかしマジで Windows マシンはどうしよう?
キーボードでノートを選ぶなら東芝 R730 か Lenovo X201s がよさそうだが…
11.6 型の今でも持ち運びする時に少々大きいし…
1366×768 は正直狭いのでせめて 1600×900 以上にしたいけど…
かといってデスクトップでは使用歩度から考えて邪魔だし…
ブルーレイの再生をしたくなったら動画再生支援機能に問題が無い Windows のほうが…
結局決まらないのでミニノート 7 と仮想マシンの XP, Vista のままになりそう。

Clipoli 0.0.0

IronPython を Windows 7 から削除の予定。
言語としては素晴らしいのだがなにせ遅い、我慢するのはもう限界だ。
さて困った、こいつは自分で利用しているからなんとかしなければ。

NotifyIcon to use from IronPython

C# で作り変えするしか無い、いや C++ でも作れるけど何故かやりたくない。
WindowsForm を使うので同じなんだが .NET で作りたいので。

せっかく作り変えるのだし exe にまとめるのだし、もう少し拘りたい。

まずはアイコンを埋め込みしたい、これは Python じゃ無理だったので。
しかし Form は使わないので this.Icon からトレイ用のアイコンを取得できない。

Icon.ExtractAssociatedIcon メソッド (System.Drawing)

なんだ、exe からアッサリ取得できるみたい。
ということは自分自身の exe 名を指定だけでイケそうだ。
アイコンの指定は csc.exe への引数で埋め込みできるもんね。

どうでもいいかもしれないけど exe 名の名前空間に収めたいな。
exe 名は、minipoli 同様なミニアプリだから clipoli にしよう。

そう、Windows 用の新規アプリで公開してしまおうと、三年ぶりか。
Cinema とかのもう自分が使っていないアプリの公開を終了するか悩む所。

そうそう、バージョン情報ダイアログも追加しなきゃ。
これは SeeMe v4 の時に作ったのを持ってきて、って SeeMe v4 は WPF だ。
流用すると WindowsForm と混在になるので名前空間がややこしくなる…

しかたがない、 Form でテキトーに作ってみるか。

about.cs

using System;
using System.Windows;
using System.Windows.Forms;
using System.Drawing;

namespace Clipoli
{
	class About : Form
	{
		public About(Icon icon)
		{
			this.Text = "About Clipoli";
			this.Icon = icon;
			this.Width = 300;
			this.Height = 200;
			var label = new Label();
			label.Location = new Point(10, 10);
			label.Text = "情報";
			this.Controls.Add(label);
			var button = new Button();
			button.Location = new Point(10, 100);
			button.Text = ("OK");
			button.Click += on_ok;
			this.Controls.Add(button);
		}
		void on_ok(object sender, EventArgs e)
		{
			Close();
		}
	}
}

WindowsForm が嫌いだからといえ我ながらテキトーすぎる。
それはそれとしてメインソース。

mainsrc.cs

using System;
using System.IO;
using System.Windows;
using System.Windows.Forms;
using System.Drawing;

namespace Clipoli
{
	class TrayIcon
	{
		private NotifyIcon _icon;
		public TrayIcon()
		{
			var menu = new ContextMenu();
			menu.MenuItems.Add("メモ帳", on_notepad);
			menu.MenuItems.Add("クリップボード", on_clipboard);
			menu.MenuItems.Add("-");
			menu.MenuItems.Add("バージョン情報", on_about);
			menu.MenuItems.Add("-");
			menu.MenuItems.Add("終了", on_exit);
			// create tray icon
			_icon = new NotifyIcon();
			_icon.ContextMenu = menu;
			_icon.Icon = Icon.ExtractAssociatedIcon("clipoli.exe");
			_icon.Text = "Description Text";
			_icon.Visible = true;
		}
		private void on_notepad(object sender, EventArgs e)
		{
			System.Diagnostics.Process.Start("notepad.exe");
		}
		private void on_clipboard(object sender, EventArgs e)
		{
			try
			{
				Clipboard.SetText("あずにゃん");
				System.Media.SystemSounds.Beep.Play();
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
		}
		private void on_about(object sender, EventArgs e)
		{
			var about = new About(_icon.Icon);
			about.ShowDialog();
		}
		private void on_exit(object sender, EventArgs e)
		{
			_icon.Visible = false;
			Application.Exit();
		}
	}
	class __main__
	{
		[STAThread]
		public static void Main(string[] args)
		{
			var n = new TrayIcon();
			Application.Run();
		}
	}
}

アイコンは IronPython 用に作ったのを仮で置いてビルドバッチ。
WindowsForm で .net 4.0 は馬鹿馬鹿しいので 3.5 の csc exe で。

build.bat

C:\Windows\Microsoft.NET\Framework\v3.5\csc.exe ^
/reference:System.Windows.Forms.dll ^
/reference:System.Drawing.dll ^
/target:winexe ^
/out:clipoli.exe ^
/win32icon:icon.ico ^
/optimize+ ^
*.cs
PAUSE

まとめて ZIP
clipoli000.zip

まだテスト段階だしこんな感じでいいだろう。
動かしてみる、まあ IronPython 作の時とほぼ同じだし当然動くか。

問題はトレイアイコンの右クリックしか受け付けないことなんだよな。
自分で使うだけの分にはそれでいいのだが公開するとなると…

というか、ミニノートで作るのが少々辛くなってきた。
意地をはってもしかたがないし、もう少しマシな Windows を買おうかなと。

Fake System.IO

System.IO.File.ReadLines は .NET 3.5 や mono で使えない。
だったら自分で作ってしまえ、と思ったのでやってみた。

どうでもいいけど gmcs も csc.exe と同じオプションが使えるんだね。
/out:FILENAME は -out:FILENAME みたくハイフンにするだけ。

いや、yield を使ったことが無いのでやってみようかと。
こんな強引というよりアホなメソッド追加なんてできるのかな?

using System;
using System.Collections.Generic; //IEnumerable

namespace System.IO
{
    public static partial class File
    {
        public static IEnumerable ReadLines(string path)
        {
            using(var sr = new System.IO.StreamReader(path))
            {
                string buf;
                while ((buf = sr.ReadLine()) != null)
                {
                    yield return buf;
                }
            }
        }
    }
}

File クラス (System.IO)
File クラスは partial class では無いみたいだけど。

コンフリクトか、やっぱりダメだった。
Windows でもやってみたけど同じ、こんなことをやろうと考えるほうがおかしい。
しかたがない、名前空間には追加できるはずだから違うクラス名にしてみる。

fakeio.cs

using System;
using System.Collections.Generic; //IEnumerable

namespace System.IO
{
    public static class FakeFile
    {
        public static IEnumerable ReadLines(string path)
        {
            using(var sr = new System.IO.StreamReader(path))
            {
                string buf;
                while ((buf = sr.ReadLine()) != null)
                {
                    yield return buf;
                }
            }
        }
    }
}

readlines.cs

using System;
using System.IO;

class __main__
{
    [STAThread]
    public static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            Console.WriteLine("no arg...");
            return;
        }
        int n = 0;
        foreach (var line in FakeFile.ReadLines(args[0]))
        {
            n++;
            Console.WriteLine("{0}: {1}", n.ToString("000"), line);
        }
    }
}

で、cat.exe という実行ファイルを作るコマンド。

gmcs -out:cat.exe *.cs

何故か exe という拡張子を付けないとエラーになる。
Linux でも強制するんかい、まぁ解りやすいと思えばいいし。
アスタリスクでディレクトリ内をまるごとコンパイルしてくれるのはありがたい。

なるほど、yield はこういう時に使うと確かに便利だ。
C#4.0 の ReadLines って上記を自作する以上のメリットがあるのかな?

Default argument and ReadLines

何を今頃なのだが C# 4.0 を調べてみた。
なんと 4.0 では関数にデフォルト引数を指定できるようになっていた。

てか、今まで無かったのかよ…

Default argument
で可能とかウソを書いてしまった、はずかしい。
引数に数の差だけのためにオーバーロードをする必要はコレで無くなるわけだ。

それと、System.IO.File.ReadLines() というイテレーション関数も追加らしい。
StreamReader.ReadLine() じゃダメなの?と思うんだが…

他は興味がわかないので今回はパス。

ということで 3.5 と 4.0 の csc.exe で試しコードを書く。

default_param.cs

using System;

class Test
{
    public static int Func(int x=5, int y=7)
    {
        return x+y;
    }
}

class __main__
{
    [STAThread]
    public static void Main(string[] args)
    {
        int n = Test.Func(y:20);
        Console.WriteLine(n);
    }
}

readlines.cs

using System;
using System.IO;

class __main__
{
    [STAThread]
    public static void Main(string[] args)
    {
        int n = 0;
        //foreach (var line in File.ReadAllLines(args[0]))
        foreach (var line in File.ReadLines(args[0]))
        {
            n++;
            Console.WriteLine("{0}: {1}", n.ToString("000"), line);
        }
    }
}

ちなみに私がパスを通しているのは 3.5 のみなのでこうなる。
うん、やっぱりどちらも 4.0 でしかビルドできないや。

これだけではツマラナイので Ubuntu でもやってみる。

あれ?デフォルト引数のほうだけ問題なくビルドできちゃった。
どっちなんだよ Ubuntu に最初から入っている mono は。

Readlines は「そんなメソッドありません」ですね、そりゃそうだ。
ReadAlllines とコメントアウトを変更すれば普通にビルドできた。
StreamReader で while ループしたほうが圧倒的効率だけどテストです。

つまり何が言いたいかというと。
コンパイルするのがメンドイけど実用無視で遊ぶなら C# は最強。
Linux で Python は単なる実用ツールでしかないもの、私はだが。

EmEditor C# snippet

正月から Windows 7 を使っているけど Opera と EmEditor しか使っていない…

まぁ Ubuntu でもあまり変わらないけど。
画像と動画は Ubuntu でしか見ない、てか Windows 7 はミニノートだし。

もう少し EmEditor v10 をカスタマイズしなければ今はチト苦しい。
EmEditor 用に <>& をエスケープするマクロを最低でも作らねば。
Blog に XAML 等の XML を書き込む等の時に必要なんだもの。
Linux Gedit 用は自分で公開しているし秀丸では他人が作ったマクロを利用していたが。

実は簡易で作っていた、JavaScript が得意じゃない私なので酷い処理。
ぶっちゃけ document.selection.Replace() を 3 回繰り返すというギャグw
これではあんまりなので今度は JavaScript の勉強。

html_escape.js

if (!document.selection.IsEmpty) {
	var ss = document.selection.Text;
	var s, result = "";
	var n = ss.length;
	for (var i=0; i<n; i++) {
		s = ss.charAt(i);
		if (s=="&") {s = "&amp;"};
		else if (s=="<") {s = "&lt;"};
		else if (s==">") {s = "&gt;"};
		result += s;
	};
	document.selection.Text = result;
}

でいいみたい、得意じゃないので無駄が多いかもしれないけど。
String は charAt メソッドで日本語でも一文字づつ取り出しできるんだね。
ソレさえ理解すれば後はどうとでも作れるはず、今までどの言語でもそうだった。
JavaScript も今年はもう少し勉強しよう。

それよりデフォルトは C# が C/C++ と同じスニペットになっている…
全然同じじゃ無いだろ、これ私の環境だけなのかな?
C# 専用スニペット郡を作って最低でも以下を追加しないと。

トリガ doc

/// <summary>
/// $0
/// </summary>

トリガ main

class __main__
{
	[STAThread]
	public static void Main(string[] args)
	{
		var app = new Application();
		app.Run(new ${0:/*ClassName*/}());
	}
}

トリガ try

try
{
	$0
}
catch (Exception e)
{
	
}
finally
{
	
}

トリガ proc

private void ${0:/*procedure*/}(object sender, RoutedEventArgs e)

これだけ作っておけば結構楽にコードが書けると思う。
VisualStudio を使わないなら summary はあまり意味が無いけど一応。
Python で docstring を使うようになったので基本に合わせたくなるのよ。

後はたとえば main と打ち込み tab キーを叩けば

Linux ではスニペットって常識以前なんだが何故 Windows では流行しないのか?
おかげで cmd.exe は Tab キーで補完できることを Linux を使うまで知らなかったぞ。
cd do と打ち込んで Tab キーで cd Documents まで補完、こんなに移動は楽なのに。

というか秀丸にスニペット機能が付けば秀丸に戻るんだけどな。
Windows 版 Gedit が存在するのは知っているが大半のプラグインが動かないと思うし。
試すつもりも無いが。

沢山のアプリを試す暇があったら一つを徹底的に使いこなしたほうがイイ。
機能に不満で乗り換えて、を繰り返すと結局最後は Em か秀丸になるのだから。
と元フリーソフト作者自身が言うのだから間違いない。

******************************

そうだ、Windows アプリといえば

Qonoha

この人だけは続くだろうとは思っていたけどまさか今でも更新しているとは。
てか他のプレイヤー作者って誰がいたっけ?マジで忘れたw

私なんて今や Windows は VLC media player だ、Cinema 使っていない…
だってあんまり Windows を使わないのに別途でデコーダーを入れるなんて面倒臭い。

というか、ココは今では何もインストールしないでプログラミングのブログだし。
なるべく最小限構成な Windows のままで使いたいので単体な VLC が一番なのよね。
Linux では Y901x を使っているからいいじゃん、みんな Linux においで。