青島のしま〜Blue Islands〜


C# Tips

URLエンコードする

string encodedString = System.Web.HttpUtility.UrlEncode(urlString)

string文字列を byte の配列に変換する

System.Text.Encoding.Unicode.GetBytes()メソッドを使えばよい.
using System.Text;

string str = "ほげ";
byte[] byteArray = Encoding.Unicode.GetBytes(str);
逆に,byte の配列から string に変換するには System.Text.Encoding.Unicode.GetString()メソッドを使えばよい.

乱数を使う

Random rnd = new Random();
// 0〜99までの値をランダムに取得
int randomNumber = rnd.Next(100);

FTPクライアントを作る

.NET Framework 2.0から,WebClientやWebRequestクラスがFTP対応しています.
FTPでファイルをダウンロードする例が以下のとおり.
using System.Net;

class WebClientFtpGet {
  static void Main() {
    WebClient wc = new WebClient();
    wc.Credentials = new NetworkCredential("user01", "mypassword");
    wc.DownloadFile("ftp://servername/pub/secret.png", "tmp.png");
  }
}
参考サイト
http://www.atmarkit.co.jp/fdotnet/dotnettips/460ftpwebreqres/ftpwebreqres.html

http://www.atmarkit.co.jp/fdotnet/dotnettips/459ftpwc/ftpwc.html

ms単位の精度で経過時間を計る

System.Diagnostics.Stopwatch クラスを使います.
Win32APIではGetTickCount()を使うことができましたが,代わりに,
.NET2.0では,Stopwatchクラスというのが使えます.
Start()を呼び出してからStop()を呼び出すまでの時間を図ることができます.
ちなみに,Datetime.Nowでは,せいぜい10msぐらいまでの精度しか出ないので注意.

インストーラを作る

「新規作成」→「プロジェクト」の
 「その他のプロジェクトと種類」から
  「セットアップと配置」で,
   「セットアップ ウィザード」か「セットアップ プロジェクト」を選択

あとは,以下を行って,コンパイルすれば,インストール用のexeとmsiが出来上がり!

  • プロジェクトのプロパティページに,作成者などの情報を入れる
  • ソリューションエクスプローラから,プロジェクトファイルを右クリックして「表示」→「ファイルシステム」
    • 「アプリケーション フォルダ」に,中身のファイルをドラッグする
    • ショートカットを作りたかったら,「ユーザーのデスクトップ」や「ユーザーのプログラム メニュー」で,右クリック→「新しいショートカットの作成」で,対象のexeを指定する

Windowのサイズを固定する

Formプロパティの「FormBorderStyle」を「FixedSingle」とかにすれば固定されます
#名前がわかりにくいので探すのに苦労した..

DLLのAPI関数を呼び出す

以下のように宣言すると使えるようになります.
using System.Runtime.InteropServices;

[DllImport("dll名")]
private extern static 戻り値 関数名(引数);
C#にはない型の変換など困るかもしれませんが,
ここにいくとWin32APIの宣言に必要な文字列が書いてあります.
http://www.pinvoke.net/index.aspx

// beep.cs

using System;
using System.Runtime.InteropServices;

class BeepProgram {
  [DllImport("kernel32.dll")]
  private extern static bool Beep(uint dwFreq, uint dwDuration);

  private static void Main() {
    Beep(262, 500);  // ド
    Beep(294, 500);  // レ
    Beep(330, 500);  // ミ
    Beep(349, 500);  // ファ
    Beep(392, 500);  // ソ
    Beep(440, 500);  // ラ
    Beep(494, 500);  // シ
    Beep(523, 500);  // ド
  }
}

ファイルコピーでプログレスバー(プログレスダイアログ)を表示する

通常のSystem.IO.File.Copy()などを使うと,進捗状況を見ることができません.
そこで,WindowsのShellのAPIを使います.
以下のクラスのCopy関数を呼び出すことで,残り時間を表示しながらコピーすることができます.
using System;
using System.Runtime.InteropServices;

namespace daigo_ao.util
{
    /// <summary>
    /// ShellFileOperation用のラッパークラス
    /// </summary>
    public class ShellFileOperation
    {
        public enum FOFunc : uint
        {
            FO_UNDEFINED = 0x0000,
            FO_MOVE = 0x0001,
            FO_COPY = 0x0002,
            FO_DELETE = 0x0003,
            FO_RENAME = 0x0004
        }

        public enum FOFlags : ushort
        {
            FOF_MULTIDESTFILES = 0x0001,
            FOF_CONFIRMMOUSE = 0x0002,
            FOF_SILENT = 0x0004,  // don't create progress/report
            FOF_RENAMEONCOLLISION = 0x0008,
            FOF_NOCONFIRMATION = 0x0010,  // Don't prompt the user.
            FOF_WANTMAPPINGHANDLE = 0x0020,  // Fill in SHFILEOPSTRUCT.hNameMappings
            // Must be freed using SHFreeNameMappings
            FOF_ALLOWUNDO = 0x0040,
            FOF_FILESONLY = 0x0080,  // on *.*, do only files
            FOF_SIMPLEPROGRESS = 0x0100,  // means don't show names of files
            FOF_NOCONFIRMMKDIR = 0x0200,  // don't confirm making any needed dirs
            FOF_NOERRORUI = 0x0400,  // don't put up error UI
            FOF_NOCOPYSECURITYATTRIBS = 0x0800,  // dont copy NT file Security Attributes
            FOF_NORECURSION = 0x1000,  // don't recurse into directories.
            FOF_NO_CONNECTED_ELEMENTS = 0x2000,  // don't operate on connected elements.
            FOF_WANTNUKEWARNING = 0x4000,  // during delete operation,
                                           // warn if nuking instead of recycling
                                           // (partially overrides FOF_NOCONFIRMATION)
            FOF_NORECURSEREPARSE = 0x8000  // treat reparse points as objects, not containers
        }


        // typedef struct _SHFILEOPSTRUCT 
        //              {
        //                      HWND hwnd;
        //                      UINT wFunc;
        //                      LPCTSTR pFrom;
        //                      LPCTSTR pTo;
        //                      FILEOP_FLAGS fFlags;
        //                      BOOL fAnyOperationsAborted;
        //                      LPVOID hNameMappings;
        //                      LPCTSTR lpszProgressTitle;
        //              } SHFILEOPSTRUCT, *LPSHFILEOPSTRUCT;
        [StructLayout(LayoutKind.Sequential)]
        public struct SHFILEOPSTRUCT
        {
            public IntPtr hwnd;
            public FOFunc wFunc;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string pFrom;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string pTo;
            public FOFlags fFlags;
            public bool fAnyOperationsAborted;
            public IntPtr hNameMappings;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string lpszProgressTitle;
        }

        // Shell32.dllをインポート
        //   int SHFileOperation(LPSHFILEOPSTRUCT lpFileOp);
        // Unicode を明示的に指定する必要がある
        [DllImport("Shell32.dll", CharSet = CharSet.Unicode)]
        public static extern int SHFileOperation(ref SHFILEOPSTRUCT lpFileOp);


        // ==== メンバ変数の定義 ====
        public SHFILEOPSTRUCT shFile;

        // ==== コンストラクタの定義 ====
        public ShellFileOperation()
        {
            shFile = new SHFILEOPSTRUCT();
            shFile.wFunc = FOFunc.FO_UNDEFINED; // 未定義
        }

        // ==== メソッドの定義 ====
        /// <summary>
        /// ファイルをコピーする
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <returns></returns>
        public int Copy(string from, string to, IntPtr hwnd)
        {
            shFile.hwnd = hwnd;
            shFile.wFunc = ShellFileOperation.FOFunc.FO_COPY;
            shFile.pFrom = from + '\0'; // '\0'を最後につける必要がある
            shFile.pTo = to + '\0';
            shFile.fAnyOperationsAborted = false;
            shFile.hNameMappings = IntPtr.Zero;
            shFile.lpszProgressTitle = "copying...";
            shFile.fFlags = 0;//ShellFileOperation.FOFlags.FOF_FILESONLY;

            return Execute();
        }

        /// <summary>
        /// ファイルを移動する
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <param name="hwnd"></param>
        /// <returns></returns>
        public int Move(string from, string to, IntPtr hwnd)
        {
            shFile.hwnd = hwnd;
            shFile.wFunc = ShellFileOperation.FOFunc.FO_MOVE;
            shFile.pFrom = from + '\0'; // '\0'を最後につける必要がある
            shFile.pTo = to + '\0';
            shFile.fAnyOperationsAborted = false;
            shFile.hNameMappings = IntPtr.Zero;
            shFile.lpszProgressTitle = "moving...";
            shFile.fFlags = 0;// ShellFileOperation.FOFlags.FOF_FILESONLY;

            return Execute();
        }

        /// <summary>
        /// ファイルを削除する
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <param name="hwnd"></param>
        /// <returns></returns>
        public int Delete(string from, IntPtr hwnd)
        {
            shFile.hwnd = hwnd;
            shFile.wFunc = ShellFileOperation.FOFunc.FO_DELETE;
            shFile.pFrom = from + '\0'; // '\0'を最後につける必要がある
            shFile.pTo = null;
            shFile.fAnyOperationsAborted = false;
            shFile.hNameMappings = IntPtr.Zero;
            shFile.lpszProgressTitle = "deleting...";
            shFile.fFlags = ShellFileOperation.FOFlags.FOF_ALLOWUNDO;//ゴミ箱に残す

            return Execute();
        }

        /// <summary>
        /// その他のオペレーションを実行したい場合は,
        /// shFileのパラメータを設定して,このメソッドを呼び出してください.
        /// </summary>
        /// <returns></returns>
        public int Execute()
        {
            if (shFile.wFunc == FOFunc.FO_UNDEFINED) {
                // オペレーション未定義エラー
                System.Windows.Forms.MessageBox.Show("オペレーションが未定義です");
                return -1;
            }

            return SHFileOperation(ref shFile);
        }


    }
}
呼び出し側は,次のように呼び出します.
private void button1_Click(object sender, EventArgs e)
{
    ShellFileOperation shFileOp = new ShellFileOperation();
    // コピー元ファイル,コピー先フォルダ
    int result = shFileOp.Copy("c:\\hoge.txt", "c:\\moge\", this.Handle);
}

RS-232C経由でシリアル通信をする

  • まず最初に MSComm コントロールへの参照をプロジェクトに追加します。
    • メニューから「プロジェクト」→「参照の追加」を開く
    • 「COM」タブから「Microsoft Comm Control 6.0」 (ファイル名は MSComm32.ocx) をダブルクリック
      • ※VB6がインストールされている,または,MSComm32.ocxがregsvr32によって登録されている必要があります
    • 「OK」 ボタンをクリック
	public class RS232C_Receiver
	{
		// MSComm(シリアル通信用) オブジェクト
		private MSCommLib.MSComm m_comm = new MSCommLib.MSCommClass();

		// コンストラクタ
		public RS232C_Receiver()
		{
			InitComPort(1); // COMポートオープン
		}

		// COM ポートの設定&オープン
		public void InitComPort(short portNum) 
		{
			// ポート番号の指定
			m_comm.CommPort = portNum;
			// 通信条件(baudrate,パリティの有無,length,StopBit)
			m_comm.Settings = "9600,N,8,1";
			// テキストモードで受信(バイナリモードのときは,「.comInputModeBinary」を指定する)
			m_comm.InputMode = MSCommLib.InputModeConstants.comInputModeText;
			// ハンドシェイクなし
			m_comm.Handshaking = MSCommLib.HandshakeConstants.comNone;
			// 4 バイト受信するごとに OnComm() が呼び出される
			m_comm.RThreshold = 4;
			// Input で受信バッファから全バイト取得
			m_comm.InputLen = 0;
			// イベントハンドラを設定
			m_comm.OnComm += new MSCommLib.DMSCommEvents_OnCommEventHandler(this.OnComm);
			try 
			{
				// ポートオープン
				m_comm.PortOpen = true;
			} 
			catch (Exception exp) 
			{
				System.Windows.Forms.MessageBox.Show("ポート" + portNum + "のオープンに失敗:" + exp.Message);
			}
		}

		// MSComm のイベントハンドラ(受信時に呼ばれる)
		private void OnComm() 
		{
			if (m_comm.InBufferCount > 0) 
			{
				// Input プロパティにデータが格納される
				OnCommReceive((byte[]) m_comm.Input);
			}
		}

		// COM ポートからの受信
		private void OnCommReceive(string  input) 
		//private void OnCommReceive(byte[] input) // バイナリモードのときはこちら
		{
			// デバッグ出力
			System.Diagnostics.Debug.WriteLine("OnCommReceive: " + input);
		}
	}

バイナリ形式でファイルに書き込む

string filename = "hoge.dat";
System.IO.BinaryWriter writer = new System.IO.BinaryWriter(
                                   new System.IO.FileStream(filename, System.IO.FileMode.Create));
// FileMode.Create:常に新しいファイルを作成する.ファイルが既に存在する場合は古いのが削除される.

VS.NETでデバッグ・メッセージを出力する

デバッグ出力をIDE上の出力ウィンドウに表示するには,
System.Diagnostics.Debug.WriteLine("hogehoge");

現在の日時を得る

System.DateTime.Nowを使います.
// 「年月日_時分秒」の形式の文字列を作成
string filename = System.DateTime.Now.ToString("yyyyMMdd_hhmmss");
// 「年/月/日 時:分:秒」の形式の文字列を作成
string dateAndHour = System.DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss");

共用体(union)を使う

C++で以下のような構造体+共用体を
struct Union1
{
  union
  {
    long a;
    struct
    {
      char b0;
      char b1;
      char b2;
      char b3;
    }
}
上記の例では,メンバ a と,メンバ b0 〜 b3 は同じメモリ領域を共有している.
これを C# で実現するには,FieldOffset を利用すればよい.
[StructLayout(LayoutKind.Explicit)]
struct Union1
{
    [FieldOffset(0)] public int a;
 
    [FieldOffset(0)] public byte b0;
    [FieldOffset(1)] public byte b1;
    [FieldOffset(2)] public byte b2;
    [FieldOffset(3)] public byte b3;
}

列挙型を使う

public enum DaysOfTheWeek
{
   mydaySaturday = 1,
   mydaySunday = 2,
   mydayMonday = 3,
   mydayTuesday = 4,
   mydayWednesday = 5,
   mydayThursday = 6,
   mydayFriday = 7
}

// データ型をint以外に変えることもできます
// C#
enum AccountType : byte
{
   myAccountSavings = 1,
   myAccountChecking = 2
}

プリミティブな値型の変数を関数の引数として参照渡しする

関数の中で複数の値を書き換えて,それを関数から戻ってきてから使いたいことがあります.
C++では,ポインタや参照私をしてやればよいですが,C#でもintや構造体の参照渡しができます.

関数呼び出しと関数の引数に「ref」をつけます.

  static void Main()
  {
    int a = 100;
    System.Console.Write("{0} → ", a);
    Test(ref a);
    System.Console.Write("{0}\n", a);
  }

  static void Test(ref int value)
  {
    value = 10; // メソッド内で値を書き換えることができる
  }

C# における構造体とクラスの違い

  • 構造体はプリミティブな変数宣言と同じように実体化するのに対して,クラスは new で実体化する.(構造体を new して実体化することもできる)
  • 構造体は値型であり,クラスのインスタンスは参照型である

忘れがちな演算子

is演算子

  • 1 is int = True
  • 1.0 is int = False

論理演算子(XOR)

  • a ^ b

as演算子

キャストと似たような感じ
object instance1 = new Class3();
Console.WriteLine(instance1);
Class2 instance2 = instance1 as Class2;

演算子の定義

クラスごとに演算子の挙動を定義できる
class Class2
{
  public string str;
  public Class2( string s ) { str = s; }
  public static Class2 operator -(Class2 x)
  {
    return new Class2( "-" + x.str );
  }
  public static Class2 operator +(Class2 x, Class2 y)
  {
    return new Class2( x.str + "+" + y.str );
  }
}

XMLファイルを読み込む

Imports System.xml      ' XMLの作成・操作用ライブラリ

Private Sub LoadXml()
    ' 読み込むXMLファイル名(アプリケーションと同じディレクトリにあるtest.xml)
    Dim filename As String = Application.StartupPath + "\test.xml"
    ' XMLドキュメントの作成
    Dim xmlDoc As New XmlDocument
    ' XMLファイルの読み込み
    xmlDoc.Load(filename)

    ' 指定した要素名のデータを検索する
    Dim nodeList As XmlNodeList = xmlDoc.SelectNodes("/user/name")
    If nodeList Is Nothing Then ' なければ
        Exit Sub
    End If
    ' 要素毎に処理する
    Dim node As XmlNode
    For Each node In nodeList
        ' 表示
        MessageBox.Show(node.InnerText + node.Attributes.GetNamedItem("age").Value)
    Next
End Sub
ちなみに,namespaceが定義されている場合は,
Dim nsmanager as XmlNamespaceManager = new XmlNamespaceManager(doc.NameTable)
して,SelectNodes()に渡してやらないとうまく動かないので注意です.

XMLファイルに保存(出力)する

Public Sub SaveXml()
    ''' XMLドキュメントとして保存する
    ' XML出力用オブジェクト
    Dim doc As New Xml.XmlTextWriter(m_path, System.Text.Encoding.UTF8)
    doc.Formatting = Xml.Formatting.Indented
    doc.WriteStartDocument(True)
    ' ルートエレメントの書き込み
    doc.WriteStartElement("UserList")

    ' users1要素を書き込む
    doc.WriteComment("users1")
    doc.WriteStartElement("users1")
    Dim i As Integer
    For i = 0 To m_memoList.Count - 1
        doc.WriteStartElement("user")
        doc.WriteAttributeString("age", "23")
        doc.WriteString("Hogehoge Taro")
        doc.WriteEndElement()
    Next
    doc.WriteEndElement() ' エンドエレメントの書き込み

    doc.WriteEndElement()
    doc.WriteEndDocument()
    doc.Close()
End Function

ASP.NETのプロジェクトを作成したときにエラーが出る

開発環境:Win XP Pro, .NET 2003, .NET Framework 1.1
「Webアプリケーションの新規作成」を行うと,下記エラーが発生する.
「指定されたWebサーバでASP.NET Version1.1が実行されていません。 
 ASP.NET Webアプリケーションまたはサービスを実行することはできなくなります。」 
解決法: コマンドプロンプトで以下を実行すればよい
"%windir%\Microsoft.NET\Framework\(version)\aspnet_regiis.exe" -i
http://support.microsoft.com/default.aspx?scid=kb;ja;306005&Product=iisJPN より

外部プロセスの実行(アプリケーションやファイル)

private void button1_Click(object sender, System.EventArgs e)
{
    // 電卓(calc.exe)の起動
    System.Diagnostics.Process proc = new System.Diagnostics.Process();
    proc.EnableRaisingEvents=false;
    proc.StartInfo.FileName="calc.exe";
    proc.Start();
}

private void button2_Click(object sender, System.EventArgs e)
{
    // テキストファイルをデフォルトの(関連付けられた)テキストエディタで開く
    System.Diagnostics.Process proc = new System.Diagnostics.Process();
    proc.EnableRaisingEvents=false;
    proc.StartInfo.FileName="c:\\home\\hoge.txt";
    proc.Start();
}

private void button3_Click(object sender, System.EventArgs e)
{
    // InternetExplorerで指定したページを開く
    System.Diagnostics.Process proc = new System.Diagnostics.Process();
    proc.EnableRaisingEvents=false;
    proc.StartInfo.FileName="iexplore";
    proc.StartInfo.Arguments="http://www.microsoft.com";
    proc.Start();
    // アプリの終了を待つ
    proc.WaitForExit();
    MessageBox.Show("アプリが閉じられました");
}

WindowsネイティブのDLLの関数を呼び出す

using System.Runtime.InteropServices;
namespace Hoge
{
    public class HogeHoge
    {
        [DllImport("user32.dll")]
            public static extern System.IntPtr SendMessage(
                System.IntPtr hWnd, // 送信先ウィンドウのハンドル
                System.UInt32 Msg, // メッセージ
                System.IntPtr wParam, // メッセージの最初のパラメータ
                System.IntPtr lParam // メッセージの 2 番目のパラメータ
            );
        private void DoHoge()
        {
            // WM_COMMAND, 0, 0)
            SendMessage(hogehoge.Handle, 0x0111, (IntPtr)0x0, (IntPtr)0x0);
        }
    }
}

カレントディレクトリを取得する(得る)

// カレントディレクトリの取得
System.IO.Directory.GetCurrentDirectory()

// カレントディレクトリの設定
System.IO.Directory.SetCurrentDirectory("C:/hoge")

メッセージボックスの表示

using System.Windows.Forms;

MessageBox.Show("めっせーじ", "たいとる");

.NETのアプリケーションを高速に動作させる.

.NETのアプリケーションは,中間言語MSILの形になっているために,
ネイティブコードのアプリケーションよりも動作が遅くなります.
ただJITコンパイラを使用するため,2回目以降は動作が速くなります.
しかし,起動時はやはり遅いのが問題となります.
これを解決する方法として,
ngen.exe(ネイティブ・イメージ・ジェネレータ)
を利用するという手があります.
これを実行しておくと,
JITコンパイルしたキャッシュをアセンブリに組み込んで,
実行が早くなります.
ただ,PCの環境によってJITコンパイルの結果が変わってくるので,
配布する側の立場としては,インストーラなどに組み込む形態がよいそうです.
参考: http://www.atmarkit.co.jp/fdotnet/vbcheer/vbcheer08/vbcheer08.html


作成日: 2006年12月05日10時25日10秒
更新履歴
2008年07月22日 2008年02月08日 2008年02月06日 2008年02月05日 2007年10月25日 2006年12月20日 2006年12月19日 2006年12月05日 2006年09月20日 2006年06月18日 2006年06月12日 2006年04月22日 2006年03月30日 2006年02月28日 2006年02月23日 2006年02月18日 2006年01月13日 2005年12月21日