.net」タグアーカイブ

MSBuild

Visual C#がインストールされていない環境で、WindowsだけでC#の開発を行う方法について

からなんかウチのネタ元ページにチビチビと人が来ている。
いや、プログラミングに興味を持つ人が増えるのならキッカケなんて何でもいいと思うけーが…
しかし画像だらけで私的にはゲンナリするけど今時の人はこんな感じの解説を望んでいるのかな?
まあそれは関係なくて。

つまり
カレントディレクトリに sln か csproj があれば msbuild コマンドでビルドができるわけです。

単発小物なら csc コマンドに直渡しのほうが簡単だけど少し複雑になるとコッチのほうが楽。
csc コマンドでは XAML のビルドはできないけど csproj を使えば可能になる。
絶対に Visual Studio なんか使いたくないという意地っ張りなところが素敵な人もいる。
GNU make のように利用したい Linux 脳丸出しな人もいる。

ということで、実は MSBuild ならもっとイイ方法がある。

.NETビルド・エンジン「MSBuild」使いこなし術 ? @IT

上記に手段が書いているけど環境変数は単一インスタンス内で追加できる。
何かする毎に環境変数を書き換えるなんて面倒だからその方法を利用する。

実は msbuild コマンドのみでカレントディレクトリの sln, csproj を勝手に見つけてくれる。
GNU make の Makefile と同じ、つまり引数は不要。

MSBuildToolsPath って何?
MSBuild で特定の .NET Framework を対象にする
よく解らないけど WPF はこの指定が無いとビルドできなかった。

と、これだけ理解できれば EmEditor 等の外部ツールで MSBuild が使えると解る。

GNOME 上で Gedit を使い C, Makefile を作成し Ctrl+F8 を行うのと同じことができます。
解説が逆だろと言わない、筆者のメインマシンは Fedora です(Windows7 はミニノート)

残念なことに秀丸や EmEditor は複数行の外部ツールは使えないので bat を作って利用する。
複数行コマンドが利用できるエディタ(あるのか?)なら bat の内容を直書きでいいと思う。
で Windows7,8 に最初から入っている .NET 3.5 を使う手段まで。

一応書くとこのミニノートに Visual Studio は入れていません。
環境変数もデフォルトの状態に戻し再起動を行った状態で以下をやってみる。

@set PATH=%PATH%;%WINDIR%\Microsoft.Net\Framework\v3.5
@set MSBuildToolsPath=%WINDIR%\Microsoft.NET\Framework\v3.5
msbuild
pause

という内容で build35.bat みたいな名前でバッチファイルをまず作成。
.NET 4.0 を使うなら 3.5 を 4.0.30319 に変えて build40.bat なんかに。
これをテキストエディタの外部ツールに登録する。
下記は EmEditor の例、カレントディレクトリの指定に注意。

emediter_set

秀丸なら [プログラム実行] に bat へのフルパスを書き込むだけ。
フルパス取得には minipoli でも使ってくださいw
その他エディタは知らない、まともなプログラマーならこの二択もしくは両方だし。

登録したら MSDN のサンプルコードで試してみよう。
チュートリアル: MSBuild プロジェクト ファイルのゼロからの作成

コピペで helloworld.cs と test.csproj を作成。
helloworld.cs をエディタで開いている状態で外部ツールを実行。

build

普通に exe が作成されるのが解りますね。
日本語を使うなら cs, xaml ファイルはすべて BOM 付き UTF-8 で保存してください。
csc は BOM が無い場合はローケルと同じ文字コードとみなすはずですけど一応。

WPF だと構成ファイルや csproj が超複雑になるので zip で置いておく。
[SDK が見つからない] 警告を出さない方法は残念ながら解らない…
wpf_csproj.zip

VC# を持っているなら csproj と同じディレクトリにある C# コードを開いて外部ツールを実行すればビルドできると確認できる。
これで環境変数すら弄くらなくてもテキストエディタから一発ビルドできる環境の完成。

MSBuild
しかしそのプロジェクトファイルが複雑すぎて手書きで作るのが面倒くさい!
proj ファイルを簡単に作るジェネレータとかどこかに無いかな。

Microsoft としては Visual Studio を買ってほしいのだろうけどさ。
プログラミングに慣れると IDE なんて鬱陶しいだけなんだって。
普段使っているテキストエディタのみ、かつ環境変数すら弄くらないことに意義がある。

それって…
結論、やっぱり Linux で Python のほうがイイ。

ApplicationCommands for IronPython

久々に .NET ネタ。
なかなか面白いことをしている人を見つけたので。
C# + WPF + XAML コマンドのバインドとメニューバー – Symfoware
static の意味をイマイチ解っていないようだけど…
いやコレでも動くんだが。

とにかくおかげで解ったこと。
下記を利用すればあらかじめ用意されたメニューを勝手に入れてくれるようだ。
ApplicationCommands クラス (System.Windows.Input)

Windows にも全自動で国際化メニューにしてくれる便利なものがあったのね。
WPF のみみたいだけど。
GTK+ みたくリソースであるほうが理解しやすいんだけど。

で、リンク先は多々無駄があるのでもう少し単純なサンプルを書いてみる。

ただ IronPython で、コンパイル面倒クセェ!
一応 C# 屋が見てもなんとなく解るように。

# -*- coding: UTF-8 -*-

"""
    WPF Simple TextEditor
    Read and Write encoding is UTF-8 Non BOM Text
"""

import clr

clr.AddReferenceByPartialName("PresentationCore")
clr.AddReferenceByPartialName("PresentationFramework")
clr.AddReferenceByPartialName("WindowsBase")

from System import *
from System.IO import *
from System.Windows import *
from System.Windows.Controls import *
from System.Windows.Input import *

from System.Windows.Markup import XamlReader
from Microsoft.Win32 import OpenFileDialog, SaveFileDialog

menu_str = """<Menu DockPanel.Dock="Top"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <MenuItem Header="_File">
        <MenuItem Command="New" />
        <Separator />
        <MenuItem Command="Open" />
        <MenuItem Command="Save" />
        <MenuItem Command="SaveAs" />
        <Separator />
        <MenuItem Command="Close" />
    </MenuItem>
    <MenuItem Header="_Edit">
        <MenuItem Command="Undo" />
        <MenuItem Command="Redo" />
        <Separator />
        <MenuItem Command="Cut" />
        <MenuItem Command="Copy" />
        <MenuItem Command="Paste" />
        <Separator />
        <MenuItem Command="SelectAll" />
    </MenuItem>
</Menu>"""

class TextEditor(Window):
    """
        Auto Internationalization menu Sample
        no mnemonic...
    """
    def __init__(self):
        # Menu
        menu = XamlReader.Parse(menu_str)
        # MenuItem Binding
        cb = CommandBinding(ApplicationCommands.New, self.on_new)
        self.CommandBindings.Add(cb)
        cb = CommandBinding(ApplicationCommands.Open, self.on_open)
        self.CommandBindings.Add(cb)
        cb = CommandBinding(ApplicationCommands.Save, self.on_save, self.on_can_execute)
        self.CommandBindings.Add(cb)
        cb = CommandBinding(ApplicationCommands.SaveAs, self.on_save_as, self.on_can_execute)
        self.CommandBindings.Add(cb)
        cb = CommandBinding(ApplicationCommands.Close, self.on_close)
        self.CommandBindings.Add(cb)
        # TextBox
        self.textbox = TextBox()
        self.textbox.TextWrapping = TextWrapping.NoWrap
        self.textbox.AcceptsReturn = True
        self.textbox.AcceptsTab = True
        self.textbox.VerticalScrollBarVisibility = ScrollBarVisibility.Auto
        self.textbox.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto
        # Add
        docpanel = DockPanel()
        docpanel.Children.Add(menu)
        docpanel.Children.Add(self.textbox)
        self.Content = docpanel
        self.Width = 300
        self.Height = 300
        self.textbox.Focus()

    def on_new(self, sender, e):
        self.textbox.Text = ""
        self.Title = ""

    def on_open(self, sender, e):
        dlg = OpenFileDialog()
        if dlg.ShowDialog():
            sw = StreamReader(dlg.FileName) 
            try:
                self.textbox.Text = sw.ReadToEnd()
                self.Title = dlg.FileName
            except Exception, ex:
                MessageBox.Show(ex.Message)
            finally:
                sw.Close()

    def on_save(self, sender, e):
        if self.Title == "":
            self.on_save_as(sender, e)
        else:
            self.save_file(self.Title)

    def on_save_as(self, sender, e):
        dlg = SaveFileDialog()
        if dlg.ShowDialog():
            self.save_file(dlg.FileName)

    def on_close(self, sender, e):
        self.Close()

    def on_can_execute(self, sender, e):
        e.CanExecute = self.textbox.Text != ""

    def save_file(self, filename):
        sw = StreamWriter(filename) 
        try:
            sw.Write(self.textbox.Text)
            self.Title = filename
        except Exception, ex:
            MessageBox.Show(ex.Message)
        finally:
            sw.Close()

if __name__ == "__main__":
    w = TextEditor()
    Application().Run(w)

menu_win

何故メニューだけ XAML なんだ?と言わない。
Python with GTK+ ではコレが普通。
メニューとツールバー – L’Isola di Niente

とりあえずこのバインディングでやったことの解説を少し。

XAML は見ての通り ApplicationCommands を指定するだけ。
そしてコードで CommandBinding オブジェクトを作る。

CommandBinding クラス (System.Windows.Input)

第一引数に利用する ApplicationCommands
第二引数に結びつけたいイベントハンドラ名
第三引数は必要なら任意でメニューの有効無効を決めるハンドラ名

ハンドラってつまり既に有るオブジェクトですので別途で作成する必要は無い。
それを Window に CommandBindings.Add() すればいい。

ついでに TextBox のバッファが空だと保存できないように。
CommandBinding 第三引数でアッサリと実現できるんですね。

Ctrl+C でコピー等は TextBox が提供している機能なので別にメニューに入れなくても使えるんですけどね。

後今頃知ったけど StreamReader.ReadToEnd ってバイナリを読み込めるのね。
せっかく例外処理を入れたけど意味なかった。

var と new と中括弧とセミコロン追加で C# コードにもなるはずw

で、自動的に日本語メニューにはなったけどニーモニックが無いんですけど。
Alt+F, Alt+S みたいなことができないって少し困る。

以下駄文。

久々に IronPython を使ったけど面白い、DLR の初期化さえ早くなれば…
Microsoft は .NET がネイティブ、かつ local が UTF-8 の新規 OS を出してくれ。
ネイティブなら DLR 初期化なんて一瞬のはず、内部は UTF-16 ではなく UCS-4 なら更に嬉しい。
今更 Windows の local 変更なんて無理なのは解っているから新規で。

mono vala

Ubuntu で mono がデフォルトで入らなくなったので覚書ページを書き換える
と随分前に書いて放置していたので本日シコシコと。
Ubuntu 以外を使う人なら説明不要だろうから Ubuntu ページに入れた。

Ubuntu で Vala – L’Isola di Niente
Ubuntu で C# – L’Isola di Niente

Vala をおすすめしているだけ、私はどんだけ mono が嫌いなんだw

つか Vala ってドキュメントのサンプルコードが充実しているにも程があるので私が新たに描くことなんてほとんど無いや。

valadoc.org

ついでに気がついたけど Gtk# って gtk_init() 等を Application.Init() みたくバインドしているが。
Windows に合わせたのだろうけどコレって GtkApplication と勘違いしないだろうか?
つか GTK3 のバインドを作るとしたらどうすんだコレw
やっちまったなぁという感じ。

どうでもいいけど他にもチマチマと覚書を追記。

Windows 7 とのデュアルブート – L’Isola di Niente

ミニノートは結局 Windows 7 に戻した。
起動まで三分はもう体が耐えられないし、タマに Windows を使う時にしか使わないし。
ならばと Ubuntu をと入れてみたらブートすらしない…

というわけで blog はお知らせのみでした。

gdiplus save

Keyleigh をそろそろなんとかしたいと考えた。
拾ったデルヒャァコンポーネントを使っているだけのクソソフトだが。
せっかくなので画像コンバータ機能とかでも追加すれば面白くなるかも。

GDI+ を使うのが一番だよなぁ。
C# で使えば死ぬほど簡単だ。

using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace iconpoli2
{
	class Program
	{
		static void Main(string[] args)
		{
			foreach (var s in args)
			{
				string t = Path.ChangeExtension(s, "jpg");
				Bitmap b = new Bitmap(s);
				b.Save(t, ImageFormat.Jpeg);
			}
		}
	}
}

コンバートならマジでこれだけだ。
いや、せっかくなので C++ でやりたいぞ。
と思ったけど

Gdiplus::Bitmap * img = new Gdiplus::Bitmap(__wargv[i]);
img->Save(outname, &Gdiplus::ImageFormatJPEG);

とやっても全然上手くいかない。

Transforming a JPEG Image Without Loss of Information (Windows)
Retrieving the Class Identifier for an Encoder (Windows)

どうやら自力で CLSID を取ってくるしかないようで。
つか ImageCodecInfo って class なのにサイズを得て malloc なの?
試しに普通な new で作ったら見事にヒープ領域不足、どういう設計だよ。

とにかく MSDN の方法で上記と同じになるよう C++ で作ってみる。
C++ じゃ ChangeExtension なんて便利なものは無いし GDI+ 初期化も必須。

stdafx.h

#pragma once

// 面倒なのでココで参照追加
#pragma comment(lib, "gdiplus.lib")

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600 // Vista
#endif

#include <stdio.h>
//#include <tchar.h>
#include <wchar.h>
#include <windows.h>
#include <gdiplus.h>

凄く無駄だと解っているけど tchar を利用しないで UNICODE 関数をまんま使う。
てか今となっては Ansi 関数を併用する必要は無いと思うんだが。
WCHAR や UINT のマクロも利用したくない。
だって Visual Studio は大文字マクロの型名では色が付かないんだよね。
というかなるべく小文字にしたいと思うのは多分 Python のやりすぎ。

iconpoli.cpp

#include "stdafx.h"

using namespace Gdiplus;

int GetEncoderClsid(const wchar_t* format, CLSID* pClsid)
{
	unsigned int  num = 0;
	unsigned int  size = 0;

	ImageCodecInfo * pImageCodecInfo = NULL;

	GetImageEncodersSize(&num, &size);
	if(size == 0)
		return -1;  // Failure

	// コレだとヒープ領域不足になる
	//ImageCodecInfo * pImageCodecInfo = new ImageCodecInfo();
	pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
	if(pImageCodecInfo == NULL)
		return -1;  // Failure

	GetImageEncoders(num, size, pImageCodecInfo);

	for(unsigned int j = 0; j < num; ++j)
	{
		if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
		{
			*pClsid = pImageCodecInfo[j].Clsid;
			free(pImageCodecInfo);
			return j;  // Success
		}    
	}

	free(pImageCodecInfo);
	return -1;  // Failure
}

bool ChangeExtension(const wchar_t* src, wchar_t* dst, const wchar_t* ext)
{
	wchar_t path[4][256];
	_wsplitpath_s(src, path[0], 256, path[1], 256, path[2], 256, path[3], 256);
	_wmakepath_s(dst, 1024, path[0], path[1], path[2], ext);
	return TRUE;
}

int wmain(int argc, wchar_t* argv[])
{
	// GDI+ 初期化
	GdiplusStartupInput gdiplusStartupInput;
	ULONG_PTR gdiplusToken;
	GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

	for (int i=1; i < __argc; i++)
	{
		// makepath
		wchar_t outname[1024];
		ChangeExtension(__wargv[i], outname, L".jpg");

		Gdiplus::Bitmap * img = new Gdiplus::Bitmap(__wargv[i]);

		CLSID encoderClsid;
		int r = GetEncoderClsid(L"image/jpeg", &encoderClsid);
		if (r == -1)
		{
			wprintf(L"no CLSID");
			return 0;
		}
		// コレは上手くいかない
		//int result = img->Save(outname, &Gdiplus::ImageFormatJPEG);
		int result = img->Save(outname, &encoderClsid);
		if (result == Gdiplus::Ok)
			wprintf(L"Encode Success");
		else
			wprintf(L"Encode Failure");
		delete img;
	}
	// 後片付け
	Gdiplus::GdiplusShutdown(gdiplusToken);
	return 0;
}

長い。。。。。

たったこれだけのモンにえらく時間が掛かった、やっぱり C# のほうがいいかなと。
それと Python や C# ばかりやっていると開放処理をつい忘れそうになるよね。

Python and .NET Methods

.NET と Python メソッド対応表 – L’Isola di Niente

自分で利用したかったので作ってみた。
ついでに覚書ページのも少しまとめた、整理進んでねぇ…

表を作るので何年かぶりに table タグを使おうと思ったけど面倒くさい。
せっかく PHP なんだからコードで作ってしまえ!
ということでこんなのを作ってみた、PHP に関しては私はサルです。

<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type" />
<title>PHP table</title>
<?php
function put_table($items, $count, $separator) {
    echo "<table border=1 cellspacing=0 cellpadding=3>\n";
    $header = true;
    $array1 = explode("\n", $items);
    foreach ($array1 as $s1) {
        $array = explode($separator, $s1);
        echo "<tr>\n";
        for ($i=0; $i<$count; $i++) {
            if ($header) {
                echo "<th bgcolor=\"#eeeeee\">$array[$i]</th>";
            } else {
                echo "<td>$array[$i]</td>";
            }
        }
        echo "</tr>\n";
        $header = false;
    }
    echo "</table>\n";
}
?>
</head>
<body>
<?php
$items = <<<ITEMS
名前|デスクトップ|リリース予定
Ubuntu 11.04|Unity|2011-04-28
Fedora 15|Gnome 3|2011-05-24(2011-04-19 Beta)
Mandriva 2011|KDE 4|2011-06-13
ITEMS;
put_table($items, 3, "|");
?>

</body>
</html>

みたくヒア・ドキュメントにしてセル数とセパレータ指定にしてみた。
ヒア・ドキュメントなら後で書きたしや書き換えをやりたくなっても簡単だ。

PHP は HTML 中にそのまんま書き込めるから楽でいいよね。
まぁ言語仕様はどうかとは思うけど…

しかし次の Linux はドレでいくかなぁ?
GTK+ 3.0 がやりたいので Fedora にしたいけど初版は不安定かもしれないし…