Ctrl+PrintScreen でアクティブなウィンドウをキャプチャする

手順書などを作るために画面のキャプチャをする時に、Alt+PrintScreen でアクティブな画面をキャプチャします。
が、メニューをキャプチャしたいときに【Alt】キーを押すと、そのメニューが消えてしまうんですよね。

なので、各種ツールを探すんですが…Ctrl+PrintScreen に割り当てればいいんじゃないの?ということで自作します。
Ctrl キーの場合は、メニューが閉じられませんからね。

こんな感じで、手軽にメニューもキャプチャできます。

■アクティブウィンドウのキャプチャ

using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
    public int left;
    public int top;
    public int right;
    public int bottom;
}
[DllImport("user32.dll")]
private static extern IntPtr GetWindowDC(IntPtr hwnd);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern int GetWindowRect(IntPtr hwnd, ref  RECT lpRect);
private const int SRCCOPY = 13369376;
[DllImport("user32.dll")]
private static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("gdi32.dll")]
private static extern int BitBlt(IntPtr hDestDC,
    int x,
    int y,
    int nWidth,
    int nHeight,
    IntPtr hSrcDC,
    int xSrc,
    int ySrc,
    int dwRop);
[DllImport("user32.dll")]
private static extern IntPtr ReleaseDC(IntPtr hwnd, IntPtr hdc);

///
/// アクティブウィンドウをキャプチャ
///
///
private Bitmap ActiveWindowCapture()
{
    IntPtr hWnd = GetForegroundWindow();
    IntPtr hDC = GetWindowDC(hWnd);
    //ウィンドウの大きさを取得
    RECT rect = new RECT();
    GetWindowRect(hWnd, ref rect);
    Bitmap bmp = new Bitmap(rect.right - rect.left, rect.bottom - rect.top);
    Graphics g = Graphics.FromImage(bmp);
    IntPtr gDC = g.GetHdc();
    BitBlt(gDC, 0, 0, bmp.Width, bmp.Height, hDC, 0, 0, SRCCOPY);
    g.ReleaseHdc(gDC);
    g.Dispose();
    ReleaseDC(hWnd, hDC);
    return bmp;
}

win api を多用しないといけないので、DllImport が多いですが、こんな感じ。
# BitBlt とか使いたくないけど、他にいい方法がありませんかね?

■ホットキーの登録/解除

ホットキーは、RegisterHotKey と UnregisterHotKey を使います。
HOTKEY_ID のほうは、他のアプリケーションとダブらないように適当に変えてください。

// 
[DllImport("user32.dll")]
extern static int RegisterHotKey(IntPtr hWnd, int id, int modKey, int key);
[DllImport("user32.dll")]
extern static int UnregisterHotKey(IntPtr hWnd, int id);
const int MOD_ALT = 0x0001;
const int MOD_CONTROL = 0x0002;
const int MOD_SHIFT = 0x0004;
const int HOTKEY_ID = 0x1234;
//
// HotKeyのイベントを示すメッセージID
//
const int WM_HOTKEY = 0x0312;
///
/// ホットキーを登録
///
/// <param name="sender" />
/// <param name="e" />
private void button5_Click(object sender, EventArgs e)
{
    if (RegisterHotKey(this.Handle, HOTKEY_ID,  MOD_CONTROL, (int)Keys.PrintScreen) == 0)
    {
        MessageBox.Show("既に他のアプリで使用されています。");
    }
}
///
/// ホットキーを解除
///
/// <param name="sender" />
/// <param name="e" />
private void button6_Click(object sender, EventArgs e)
{
    UnregisterHotKey(this.Handle, HOTKEY_ID);
}
///
/// Window Proc のオーバーライド
///
/// <param name="message" />
protected override void WndProc(ref Message message)
{
    if (message.Msg == WM_HOTKEY && (int)message.WParam == HOTKEY_ID)
    {
        Bitmap bmp = ActiveWindowCapture();
        // bmp.Save(@"c:\work\bmp\" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".bmp");
        // クリップボードへコピー
        Clipboard.SetImage(bmp);
        return;
    }
    base.WndProc(ref message);
}

■参考先

画面をキャプチャする: .NET Tips: C#, VB.NET, Visual Studio
http://dobon.net/vb/dotnet/graphics/screencapture.html
ホットキー(HotKey)の設定 (DllImport, InteropServices, RegisterHotKey, UnRegisterHotKey) – いろいろ備忘録日記
http://d.hatena.ne.jp/gsf_zero1/20070416/p1

カテゴリー: 開発, C# | 6件のコメント

Windows Azure for Visual Studio v1.4 でAzureアプリを作成して、そのままアップするとエラーになる

ダウンロード詳細 Windows Azure Tools for Microsoft Visual Studio 2010 1.4 (2011 年 8 月)
http://www.microsoft.com/downloads/ja-jp/details.aspx?FamilyID=f6d1609f-08aa-40d6-abd1-119503ecb1f9

をダウンロードして、日本語版の v1.4 を使うわけですが、そのままASP.NET Webアプリを作ってAzureにデプロイをするとエラーが発生します。

Windows Azure ASP.NET MVC 3 Web Role で使っているプロバイダ ≪ ブチザッキ
http://buchizo.wordpress.com/2011/08/04/windows-azure-asp-net-mvc-3-web-role-%e3%81%ab%e3%81%8a%e3%81%91%e3%82%8b%e3%83%87%e3%83%97%e3%83%ad%e3%82%a4%e6%99%82%e3%81%ae%e5%95%8f%e9%a1%8c/

Deploying the Windows Azure ASP.NET MVC 3 Web Role | Wade Wegner
http://www.wadewegner.com/2011/08/deploying-the-windows-azure-asp-net-mvc-3-web-role/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+WadeWegner+%28Wade+Wegner+-+Technical%29&utm_content=Google+Reader

これ、セッション情報を設定する sessionState のところが、Custom になっていて、このデータベース先が .\sqlexpress なのが原因なのですね。

<sessionState mode="Custom" customProvider="DefaultSessionProvider">
  <providers>
    <add name="DefaultSessionProvider"
         type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
         connectionStringName="DefaultConnection"
         applicationName="/" />
  </providers>
</sessionState>

確かに、複数のワーカープロセスでセッション情報を共有するので、SQL Serverを使う必要があるのは分かるんだけど、v1.3 には無かった記述が、いきなり v1.4 になって入っていて、しかも「何もせずにそのままデプロイしても落ちてる」っていうのは、駄目かなぁと。

なので、v1.4 で Azure アプリを作った時は、web.config を開いて、sessionState を削除します。
sessionState が無い時は、セッション情報はインプロセスになるので、Azure にデプロイしても正常に動作します。

で、

実運用的には、このセッション情報をどうするのか?が問題でして、案しては

1.SQL Azure に接続させる。
2.セッションサーバーを別途用意する。
3.Table Strage にセッション情報を用意する。

ってのが考えられます。

1の方法は、
Deploying the Windows Azure ASP.NET MVC 3 Web Role | Wade Wegner
http://www.wadewegner.com/2011/08/deploying-the-windows-azure-asp-net-mvc-3-web-role/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+WadeWegner+%28Wade+Wegner+-+Technical%29&utm_content=Google+Reader

に書いてある通り。SQL Azure に dbo.Sessions    テーブルを作成します。
ただし、SQL Azure を使うので課金が発生するんですよね。。。普通に Windows Azure アプリを作るだけなのに、SQL Azure への課金はちょっと痛い。

2.のほうは、ちと見当たらず。

問題は、セッションサーバー自身を Azure に置くわけですが、じゃあそのセッション情報は、SQL Azure を使うことになるのか?ということ。

で、3.の Table Strage にセッション情報を保存するのが現実的なのかなぁと。

Exercise 3: Using the Azure ASP.NET Providers with Web Form Applications
http://msdn.microsoft.com/en-us/wazplatformtrainingcourse_buildingaspnetappswithwindowsazure_topic4#_Toc300153323
に「Microsoft.Samples.ServiceHosting.AspProviders.TableStorageRoleProvider」の記述があるので、できるのかなと。

カテゴリー: 開発, Azure | Windows Azure for Visual Studio v1.4 でAzureアプリを作成して、そのままアップするとエラーになる はコメントを受け付けていません

IE9上で、Windows Azure Platform が正常に動かない

何故か、IE9 上で windows azure platform を動かすと、「新規ホステッドサービス」を作成できません。
「ホステッドサービス」のリストも出ないし、「ストレージアカウント」のリストもでない。
windows azure platfrom が正常に動きません。

ハンドルされていないアプリケーションエラーが発生しました…と言われましても。

で、Firefox 6 で動かしてみると動く。

う~む。IE9 って microsoft の製品だよなぁ。azure って microsoft の製品だよなぁ。
書籍の画面キャプチャを、Firefox でやってもいいでしょうか?>某社

と思ったら、解決したッ!!!

一度、言語設定を「English」にして「ホステッドサービス」を表示させた後(なぜか、英語版だと表示できる)、その後「日本語」に戻すと表示される。。。うーん。管理ポータルが勝手にアップデートされたっぽい挙動が。

で、Windows Azure Tools for Microsoft Visual Studio v1.4 の日本語版がこんなところに↓

テスターですが何か?
http://david9142.wordpress.com/
ダウンロード詳細 Windows Azure Tools for Microsoft Visual Studio 2010 1.4 (2011 年 8 月)
http://www.microsoft.com/downloads/ja-jp/details.aspx?FamilyID=f6d1609f-08aa-40d6-abd1-119503ecb1f9

公開日が 2011/08/23 だそうなので、仕方がない原稿を書き直しますか。

カテゴリー: 開発, Azure | IE9上で、Windows Azure Platform が正常に動かない はコメントを受け付けていません

Excel で 書式をクリップボード経由にすると遅くなる

実験メモ。

とあるプロジェクトで、Excel の書式コピーのパフォーマンスが良くない。ので調査。
とあるソースコードでは、

  1. Excel のテンプレートファイルを開いている。
  2. 書式を揃えるために、2行目から1000行までをコピーしている。
  3. データベースを検索して、セルにちまちま書き込む。

ってことをやっていました。で「遅いのは何処か?」ってな感じなのですが、普通は3を疑うところなのですが、いえいえ実際は2でした、というオチです。
Excel で帳票を作って「いまいち遅いんだよなぁ」って方は、試してみてください。.NET から操作する場合は、データベースアクセスよりも、Excel への操作のほうがネックになることが多いのです。

■いちいち、copy and paste しているのが駄目

最初のコードは、こんな感じです。実は Range のところ、もっと駄目なのですが(column毎にループしていたり)サンプル作るのが面倒なので、ちょっと高速化。

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
	Dim xapp As New Microsoft.Office.Interop.Excel.Application
	Dim path As String = System.IO.Directory.GetCurrentDirectory() + "\" + "SampleTemplate.xlsx"
	xapp.Workbooks.Open(path)
	Dim sh As Worksheet = xapp.Workbooks(1).Sheets(1)

	' 先頭行はタイトル、2行目からコピーする
	Dim start As Date = Date.Now
	For r = 3 To 1000
		Dim src As Range = sh.Range(sh.Cells(2, 1), sh.Cells(2, 6))
		src.Copy()
		Dim dest As Range = sh.Range(sh.Cells(r, 1), sh.Cells(r, 6))
		dest.PasteSpecial()
	Next
	TextBox1.Text = (Date.Now - start).ToString()

	path = System.IO.Directory.GetCurrentDirectory() + "\" + "range1.xlsx"
	System.IO.File.Delete(path)
	xapp.Workbooks(1).SaveAs(path)
	xapp.Quit()
	MessageBox.Show("クリップボード経由版 保存しました")
End Sub

これを実行すると、2分強かかります。ええッ!!! 単にコピーしているだけなのに…と思うでしょう。

■copy を 1回だけにする

書式として使っている2行目は不変なわけですから、何度も copy する必要はありません。
なので、copy するところを1回にして、後は何度も paste すれば ok。

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
	Dim xapp As New Microsoft.Office.Interop.Excel.Application
	Dim path As String = System.IO.Directory.GetCurrentDirectory() + "\" + "SampleTemplate.xlsx"
	xapp.Workbooks.Open(path)
	Dim sh As Worksheet = xapp.Workbooks(1).Sheets(1)

	' 先頭行はタイトル、2行目からコピーする
	Dim start As Date = Date.Now
	Dim src As Range = sh.Range(sh.Cells(2, 1), sh.Cells(2, 6))
	src.Copy()
	For r = 3 To 1000
		Dim dest As Range = sh.Range(sh.Cells(r, 1), sh.Cells(r, 6))
		dest.PasteSpecial()
	Next
	TextBox2.Text = (Date.Now - start).ToString()

	path = System.IO.Directory.GetCurrentDirectory() + "\" + "range2.xlsx"
	System.IO.File.Delete(path)
	xapp.Workbooks(1).SaveAs(path)
	xapp.Quit()
	MessageBox.Show("Range経由版 保存しました")
End Sub

これを実行すると、13秒になります。ええ、10倍早くなりましたね。

■pasteする時に range で範囲を指定する

実は、コピー先は3行から1000行の範囲なので、Range で指定して一気にペーストができます。
これはよく Excel でやると思うのですが、何故かプログラムコードに直すと、なかなか思い付かないようです。

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
	Dim xapp As New Microsoft.Office.Interop.Excel.Application
	Dim path As String = System.IO.Directory.GetCurrentDirectory() + "\" + "SampleTemplate.xlsx"
	xapp.Workbooks.Open(path)
	Dim sh As Worksheet = xapp.Workbooks(1).Sheets(1)

	' 先頭行はタイトル、2行目からコピーする
	Dim start As Date = Date.Now
	Dim src As Range = sh.Range(sh.Cells(2, 1), sh.Cells(2, 6))
	src.Copy()
	Dim dest As Range = sh.Range(sh.Cells(3, 1), sh.Cells(1000, 6))
	dest.PasteSpecial()
	TextBox3.Text = (Date.Now - start).ToString()

	path = System.IO.Directory.GetCurrentDirectory() + "\" + "range3.xlsx"
	System.IO.File.Delete(path)
	xapp.Workbooks(1).SaveAs(path)
	xapp.Quit()
	MessageBox.Show("Range経由版 保存しました")
End Sub

これを実行すると、0.04秒になります。ええ、1000倍ぐらい早くなります…つーか、一瞬で終わります。

■クリップボードを経由させない。

これまでは copy, paste で、クリップボードを使っていたわけですが、実は copy の引数にはコピー先の range を入れることができます。

Office TANAKA – Excel VBA講座:セルの操作[セルのコピー]
http://officetanaka.net/excel/vba/cell/cell09.htm

こうすると、クリップボードを経由しないので早くなるんですね(多分)。

Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
	Dim xapp As New Microsoft.Office.Interop.Excel.Application
	Dim path As String = System.IO.Directory.GetCurrentDirectory() + "\" + "SampleTemplate.xlsx"
	xapp.Workbooks.Open(path)
	Dim sh As Worksheet = xapp.Workbooks(1).Sheets(1)

	' 先頭行はタイトル、2行目からコピーする
	Dim start As Date = Date.Now
	Dim src As Range = sh.Range(sh.Cells(2, 1), sh.Cells(2, 6))
	Dim dest As Range = sh.Range(sh.Cells(3, 1), sh.Cells(1000, 6))
	' クリップボードを媒介しない
	src.Copy(dest)
	TextBox4.Text = (Date.Now - start).ToString()

	path = System.IO.Directory.GetCurrentDirectory() + "\" + "range4.xlsx"
	System.IO.File.Delete(path)
	xapp.Workbooks(1).SaveAs(path)
	xapp.Quit()
	MessageBox.Show("Range直接版 保存しました")
End Sub

これを実行すると、0.03秒になる。非常に高速化、と言いますか既に誤差の範囲ですね。
まぁ、1000行ぐらいだと差はつかないのかもしれません。

実行結果の画像などはこちら。

カテゴリー: 開発, VB | 2件のコメント

VB/C#でトレースログを出力する方法

C/C++ で言うところの、__FILE__ や __LINE__ を拾ってログ出力したい、という場合
次のような関数を作っておきます。

Public Class DebugTrace
    Public Shared Sub Trace()

        Dim sf = New StackFrame(1, True)
        Dim methodName As String = sf.GetMethod().ToString()
        Dim fileName As String = sf.GetFileName()
        Dim lineNumber As Integer = sf.GetFileLineNumber()

        MessageBox.Show(String.Format( _
            "場所 {0} 場所 {1}:行 {2}", methodName, fileName, lineNumber))
    End Sub
End Class

で、使いたいときは、

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    DebugTrace.Trace()
End Sub

のように書けばOK.

StackFrameクラスでは、Exceptionのように呼び出し元を再帰的に呼べるはずですが、ひとまずこれで用は足りるからよいかな、と。

カテゴリー: 開発, C#, VB | VB/C#でトレースログを出力する方法 はコメントを受け付けていません

ActiveDirecotryでログインユーザーがどのグループに属しているか調べる

実は、ActiveDirectry を扱うために DirectoryEntry, DirectorySearch なんかを駆使しないと駄目なのかなぁ、と思ったのですが、単純に現在ログインしているユーザーに関してならば、ローカルの Windows ユーザーをチェックすれば OK でした、というオチです。

# ActiveDirecotry 特有のプロパティを調べる場合は、DirectoryEntry などが必要になるのですが、 カレントユーザーのグループ名の列記だけならば、結構簡単に、という話です。

ユーザ名やドメイン名は、Environment のスタティックプロパティを利用します。
グループ名の一覧を取得する時は、NTAccount クラスにキャストして使います。

という訳で簡単にソースをば。

private void Form1_Load(object sender, EventArgs e)
{
	string username = Environment.UserName;
	string domainname = Environment.UserDomainName;

	labelUser.Text = username;
	labelDomain.Text = domainname;

	// using System.Security.Principal;
	WindowsIdentity user =
		WindowsIdentity.GetCurrent();

	// 属しているグループ名一覧を取得
	listBox1.Items.Clear();
	foreach (var group in user.Groups)
	{
		// NTアカウントに変換
		NTAccount ac =
			(NTAccount)group.Translate(typeof(NTAccount));
		listBox1.Items.Add(ac.Value);
	}
	listBox1.Sorted = true;
}
カテゴリー: 開発, C# | ActiveDirecotryでログインユーザーがどのグループに属しているか調べる はコメントを受け付けていません

DataGridView から Excel へコピーすると文字化けする

Visual Studio 2010 と Excel 2010 の組み合わせだと発生しないのですが、
Visual Studio 2005 と Excel XP の間だと文字化けが発生します。

3流プログラマのメモ書き : (VB.Net)DataGridViewのショートカットキーでのコピー(Ctrl+C)をExcelに貼り付けると文字化けする
http://jehupc.exblog.jp/9119842/

な感じで、Ctrl+C をフックしないと駄目か…と思っていたのですが、良い方法があったので up してきます。

1.DataGridView を継承して、ExDataGridView を作る。
2.以下のように GetClipboardContent をオーバーライドして Text フォーマットのみ返すようにする。

Public Overrides Function GetClipboardContent() As System.Windows.Forms.DataObject
    Dim org As DataObject = MyBase.GetClipboardContent()
    Dim dat As New DataObject
    dat.SetText(org.GetText(TextDataFormat.Text))
    Return dat
End Function

どうやら、HTML形式で clipboard へコピーしているので、Excel のほうがこの HTML 形式を貼り付けようとして文字化けしています。VB のほうは(多分)UTF-8 でコピーしているのに、Excel のほうで SJIS しか対応していない、というのが主原因ですね。

Excel 2010 を使っている場合は、文字化けしないので ok なんですが。

こんな感じで、クリップボードにコピーすると、

こんな感じで、文字化けしてしまいます。

なので、GetClipboardContent をオーバーライドして変更

カテゴリー: 開発, VB | 2件のコメント

Assembly.LoadFrom を使って別のアセンブリにあるフォームを起動する

試してみると、意外と素直に書けたのでメモ。

とあるプロジェクトで、フォームが100画面ほどある VB プロジェクトを作りました。
その VB プロジェクトを起動するときに、なんらかの形で、main window を切り替えたいという要望です。

# フォームごとにアセンブリを分ければ良いのですが、
# フォーム間で参照設定が多くて、結局ひとつのアセンブリにしないと循環参照が解決されない…という現象です。
# 本当は、メモリ使用量を減らすために、個別に exe を作るのがいいんでしょうが。

フォームが 100 画面ほどある exe にパラメータを指定して、

FormCollection Form1 

で起動します。

Module Startup
    Public Sub main(ByVal args() As String)
        ' 引数からクラス名を取得
        Dim cname As String = args(0)
        cname = "FormCollection." + cname
        ' 指定フォームを作成
        Dim t As Type = Type.GetType(cname)
        If t IsNot Nothing Then
            Dim obj As Object = System.Activator.CreateInstance(t)
            If obj IsNot Nothing Then
                ' アプリケーションを起動
                Dim frm As Form = CType(obj, Form)
                Application.Run(frm)
            End If
        End If
    End Sub
End Module

ここでは、namespace が FormCollection なので、これを追加しています。

さて、本体 exe の名前を変えて Form1.exe, Form2.exe, Form3.exe … と作ってもよいのですが、本体 exe のサイズが大きい場合には、コピーすると HDD 容量が必要になります。

なので、起動用に別のプロジェクトを作って、次のような startup にします。

Module Startup
    Public Sub main(ByVal args() As String)
        ' クラス名を実行ファイルから取得
        Dim cname As String = Application.ExecutablePath
        cname = cname.Substring( _
            cname.LastIndexOf("\") + 1, _
            cname.Length - cname.LastIndexOf("\") - 1)
        cname = cname.Substring(0, cname.LastIndexOf("."))
        cname = "FormCollection." + cname
        ' アセンブリのロード
        Dim asm As System.Reflection.Assembly = _
            System.Reflection.Assembly.LoadFrom("FormCollection.exe")
        ' 指定フォームを作成
        Dim t As Type = asm.GetType(cname)
        If t IsNot Nothing Then
            Dim obj As Object = System.Activator.CreateInstance(t)
            If obj IsNot Nothing Then
                ' アプリケーションを起動
                Dim frm As Form = CType(obj, Form)
                Application.Run(frm)
            End If
        End If
    End Sub
End Module

ってな風なのを考えた訳ですが、本体のロード時間を考えると必要なフォームだけをかき集めた exe を作るのがベストですね。
まぁ、本体が 3 MB 程度なので、これでよしとしますか。

カテゴリー: 開発, VB | Assembly.LoadFrom を使って別のアセンブリにあるフォームを起動する はコメントを受け付けていません

Visual Basic の if 文は歴史を覆すことができる…訳はない

技術メルマガネタ

青木「歴史に if は無いというが、Visual Basic には if があるッ!!!」
田中「まぁ、そりゃそうでしょうね。if 文がないと条件分岐ができないし…」
青木「しかし、if が無ければ、前だけ向いて進めばいいという先生の話も理解できるし」
田中「いや、それ、ラジオの CM ですし…」
青木「しかし、if が無ければ、管直人の支持率もアップアップだったわけだし」
田中「if というか、それ現実ですし…というか、アップアップって…」
青木「しかし、if が無ければ、小学校の思い出が、今現実のものとなり、今ここで発現されたチェレンコフ光が」
田中「if というか、訳わからないし…しかも、最後が微妙に危ないし…」
青木「まぁ、あれだよな。if とか select とか無いほうが、物事一直線で安全だよな」
田中「いや、猪突猛進管内閣自暴自棄暴走茫漠ということで」

これも却下ネタ。
技術メルマガでは、政治ネタはオミット。

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim i1 As Integer = 0
        Dim i2 As Integer = 2

        ' 単純な = 演算子
        If i1 = 0 Then

        End If
        ' 否定のための <> 演算子
        If i1 <> 0 Then

        End If
        ' 比較演算子
        If i1 < 0 Then

        End If
        ' Else も使うよ
        If i1 = 0 Then
            ' マッチする場合
        Else
            ' マッチしない場合
        End If
        ' ElseIf も使うよ
        If i1 = 0 Then
            ' 最初の条件にマッチする場合
        ElseIf i1 = 1 Then
            ' 2番目の条件にマッチする場合
        Else
            ' それ以外
        End If

    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

        ' 2 つの条件を指定する
        Dim s1 As String = "tonny"
        Dim s2 As String = "masuda"

        ' 両方の条件を一度に満たす
        If s1 = "tonny" And s2 = "masuda" Then

        End If
        If s1 = "tonny" AndAlso s2 = "masuda" Then

        End If
        ' どちらか一方の条件を満たす
        If s1 = "tonny" Or s2 = "masuda" Then

        End If
        If s1 = "tonny" OrElse s2 = "masuda" Then

        End If

        ' ベン図を使う
        If s1 <> "tonny" Or s2 <> "masuda" Then

        End If
        ' 複雑なので not を使う
        If Not (s1 = "tonny" And s2 = "masuda") Then

        End If
        ' 条件が多くて分かりづらい場合は、if 文を重ねると良い
        If s1 <> "tonny" Then
            If s2 <> "masuda" Then

            End If
        End If

    End Sub

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click

        Dim s1 As String = "tonny"
        Select Case s1
            Case "tonny"
                ' tonny の場合
            Case "masuda"
                ' masuda の場合
            Case Else
                ' 上記以外
        End Select

        ' 実は if else と同じ
        If s1 = "tonny" Then
            ' tonny の場合
        ElseIf s1 = "masuda" Then
            ' masuda の場合
        Else
            ' 上記以外
        End If

        ' : を使って短く書ける
        Dim s2 As String = ""
        Select Case s1
            Case "tonny" : s2 = "トニー"
            Case "masuda" : s2 = "増田"
            Case Else : s2 = "トニー 増田"
        End Select

        ' 関数を使ってシンプルに
        Dim s3 As String = Check(s1)

        ' 文字列の場合は、Dictonary を使う方法もある
        ' あらかじめ設定しておく
        Dim dic As New Dictionary(Of String, String)
        dic.Add("tonny", "トニー")
        dic.Add("masuda", "増田")
        ' 辞書から検索
        If dic.ContainsKey(s1) = True Then
            s3 = dic(s1)
        Else
            s3 = "トニー 増田"
        End If

    End Sub

    ''' <summary>
    ''' SELECT 内で Return を使う
    ''' </summary>
    ''' <param name="str"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function Check(ByVal str As String)
        Select Case str
            Case "tonny" : Return "トニー"
            Case "masuda" : Return "増田"
            Case Else : Return "トニー 増田"
        End Select
    End Function

End Class
カテゴリー: VB, 技術メルマガ | 1件のコメント

Visual Basic の文字列をこよなく愛すれば、咳してもひとり

技術メルマガのネタ

青木「文○列と言えば、な~んだ?」
田中「先輩…伏字にしても、文字列としてか読めないんですけど」
青木「まあぁ、そうかもな。伏字にすると、アレだから、文♡列」っていうのはどうだ?」
田中「先輩…伏字というか、なんというか、文字列ですよね。やっぱり」
青木「そうかぁ、そうなると、文字列♡ っていうのは、どうだ?」
田中「…」
青木「ほら、あだち充が開発した、なんでもラブコメになってしまうという、技を君は知らないのか?」
田中「…」
青木「コホン…ああ、秋空。空が高いなぁ」
田中「盆には早いですが…」

だいたい、これだけ覚えておけば大丈夫よ、ってな感じです。
個人的には String.IsNllOrEmpty は使いたくないんですけどね。確かに String 型は Nothing/Null が入るので、null を代入したときに Length プロパティでチェックすると例外が発生してしまう訳ですが、データベースでの NULL を扱うならば、明示的に Nullable を使うほうが他との対応(nullable(of integer)とか)がよいかなぁと。

Imports System.Data.SqlClient

Public Class Form1

    '''
    ''' 基本的なところをちょろっと
    '''
    ''' <param name="sender" />
    ''' <param name="e" />
    '''
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim s1 As String = "普通の初期化"
        ' NULL を入れることができる
        Dim s2 As String = Nothing
        ' Empty を入れることができる
        Dim s3 As String = String.Empty
#If False Then
        If s2.Length = 0 Then
            ' 例外発生
        End If
#End If
        If s3 = "" Then
            ' 実は空白と同じ
            MessageBox.Show("実は空白と同じ")
        End If

        If String.IsNullOrEmpty(s2) = True Then
            MessageBox.Show("IsNullOrEmpty を使う")
        End If

        ' 直接文字列に対してメソッドを使える
        Dim s4 As String = "tonny masuda".ToUpper
        MessageBox.Show(s4)

    End Sub

    '''
    ''' String は値型扱いになる
    '''
    ''' <param name="sender" />
    ''' <param name="e" />
    '''
    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim src As String = "tonny"
        Dim dest As String = ""
        ' 文字列はコピーされる
        dest = src
        ' コピー元を変えても、コピー先は変わらない
        src = "TONNY"
        MessageBox.Show( _
            String.Format("src is {0}. dest is {1}", src, dest))
    End Sub

    '''
    ''' ToString を使ってフォーマットする
    '''
    ''' <param name="sender" />
    ''' <param name="e" />
    '''
    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        ' 日付をフォーマット
        MessageBox.Show(Date.Now.ToString("yyyy年MM月dd日"))
        ' 数値をフォーマット
        Dim i As Integer = 1000
        MessageBox.Show(i.ToString("#,###円"))
        ' クラス名など
        Dim cn As New SqlConnection
        MessageBox.Show(cn.ToString)
        ' ToString を overrides する
        Dim hello As New Hello
        hello.age = 40
        hello.name = "tonny masuda"
        MessageBox.Show(hello.ToString)
    End Sub

    '''
    ''' 文字列の連結諸々
    '''
    ''' <param name="sender" />
    ''' <param name="e" />
    '''
    Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click

        ' + 演算子で連結
        Dim s1 As String = "tonny" + " " + "masuda"
        ' & 演算子で連結
        Dim s2 As String = "tonny" & " " & "masuda"
        ' 数値から暗黙の変換
        Dim s3 As String = 1234
        ' 数値へ暗黙の変換
        Dim i4 As Integer = "1234"
    End Sub

    '''
    ''' よく使われる String のメソッド
    '''
    ''' <param name="sender" />
    ''' <param name="e" />
    '''
    Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click
        Dim s1 As String = "tonny masuda"

        ' 文字列の長さ
        Dim i2 As Integer = s1.Length
        ' 先頭の文字
        Dim s3 As String = s1.Substring(0, 1)
        Dim s4 As String = s1(0)
        ' 最後の文字
        Dim s5 As String = s1.Substring(s1.Length - 1, 1)
        Dim s6 As String = s1(s1.Length - 1)
        ' 大文字/小文字
        Dim s7 As String = s1.ToUpper
        Dim s8 As String = s1.ToLower
        ' 前後の空白を削除
        Dim s9 As String = s1.Trim
        ' 置き換え
        Dim s10 As String = s1.Replace("masuda", "増田")
        ' 指定文字列を削除
        Dim s11 As String = s1.Replace("禁則事項ですッ!!!", "")
        ' 先頭の5文字だけ残す
        Dim s12 As String = s1.Substring(0, 5)
        Dim s13 As String = s1.Remove(5)
        ' 左寄せ/右寄せ
        Dim s14 As String = s1.PadRight(20)
        Dim s15 As String = s1.PadLeft(20, "○")
        MessageBox.Show(s15)

    End Sub
End Class

'''
''' ToString を overrides したクラス
'''
'''
Public Class Hello
    Public age As Integer
    Public name As String

    Public Overrides Function ToString() As String
        Return String.Format("オラ {0}. 年齢 {1} 歳だけど、よろしくなッ!!!", name, age)
    End Function
End Class

カテゴリー: VB, 技術メルマガ | Visual Basic の文字列をこよなく愛すれば、咳してもひとり はコメントを受け付けていません