カテゴリー
C#

C# で HTTP GET を行う場合の例外処理

C# でHTTPを使ってサーバーからコンテンツを取得する際に起こり得る例外処理を調べて、catch で処理できるようコードを書いてみました。

HTTP ステータスコードの 401(未認証), 403(アクセス拒否) やネットワークケーブルを抜いた場合などは WebException でキャッチできるようです。

参考:
https://msdn.microsoft.com/ja-jp/library/system.net.webrequest(v=vs.80).aspx
https://msdn.microsoft.com/ja-jp/library/system.net.httpwebrequest.getresponse(v=vs.80).aspx
https://www.java2s.com/Code/CSharp/Network/Handlenetworkexceptions.htm
https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

using System;
using System.Net;
using System.IO;

namespace webget
{
    class WebGet
    {
        static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("URL を指定してください。");
                return;
            }

            WebGet w = new WebGet();

            string s = w.HttpGet(args[0], "");
        }

        public string HttpGet(string URI, string Parameters)
        {
            string res = "";
            Stream dataStream = null;
            StreamReader reader = null;
            HttpWebResponse response = null;

            try
            {
                // URLの HTTP リクエストを作成. 		
                WebRequest request = WebRequest.Create(URI);

                // デフォルトのクレデンシャルを設定.
                request.Credentials = CredentialCache.DefaultCredentials;
                
                // HTTP レスポンスを取得.
                response = (HttpWebResponse)request.GetResponse();

                // ステータスの表示.
                Console.WriteLine(response.StatusDescription);
                
                // サーバーからコンテンツを含むストリームを取得.
                dataStream = response.GetResponseStream();
                
                // ストリームを開く
                reader = new StreamReader(dataStream);

                // コンテンツの読み込み.
                string responseFromServer = reader.ReadToEnd();

                // コンテンツの表示.
                Console.WriteLine(responseFromServer);
            }
            catch (WebException e)
            {
                // ネットワークが切断されているような場合 etc...
                Console.WriteLine("Web 例外 - " + e.Status + " : " + e.Message);
            }
            catch (UriFormatException e)
            {
                // https:///www.northwind.mydns.jp/samples のような場合
                //      ^^^
                Console.WriteLine("URI フォーマット不正: " + e.Message);
            }
            catch (System.Security.Authentication.AuthenticationException e)
            {
                Console.WriteLine("セキュリティ認証エラー: " + e.Message);
            }
            catch (InvalidOperationException e)
            {
                Console.WriteLine("無効な操作: " + e.Message);
            }
            catch (NotSupportedException e)
            {
                // htt:///www.northwind.mydns.jp/samples のような場合
                // ^^^
                Console.WriteLine("未サポートのプロトコル: " + e.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine("予期しないエラー: " + e.ToString());
            }
            finally
            {
                // Cleanup the streams and the response.
                if (reader != null)
                    reader.Close();
                if (dataStream != null)
                    dataStream.Close();
                if (response != null)
                    response.Close();
            }
            
            return res;
        }
    }
}
カテゴリー
C++

Windowsの特別なフォルダ(SHFolders)の列挙

Windows XP, Windows Vista, Windows 7 で動作するアプリを作成する上で、アプリ用のデータを保存するフォルダーとか、個人用のデータを保存するフォルダーなどを Windows 既定の場所を使用したいと思い、そのフォルダーの取得方法を調べました。

MSDNによると、CSIDL を使用する方法はWindows Vista 以降は、互換性のために残されていて、今後は、KNOWNFOLDERID を使用するようにとのことです。

CSIDL:https://msdn.microsoft.com/en-us/library/bb762494%28v=vs.85%29.aspx
KNOWNFOLDERID: https://msdn.microsoft.com/en-us/library/dd378457%28v=vs.85%29.aspx

CSIDL の取得

CSIDL の値をすべて配列にいれておいて、ループで実際のフォルダー名を列挙してます。

// SHFolders.cpp : Known Folders の列挙 
//

#include "stdafx.h"
#include <shlobj.h>


int _tmain(int argc, _TCHAR* argv[])
{
    TCHAR szPath[MAX_PATH];
    typedef struct {
        int csidl;
        TCHAR szId[50];
    } SHFOLDER;

    // CSIDL https://msdn.microsoft.com/en-us/library/bb762494%28v=vs.85%29.aspx

    SHFOLDER folders[] = {
        { CSIDL_ADMINTOOLS, _T("CSIDL_ADMINTOOLS") },
        { CSIDL_ALTSTARTUP, _T("CSIDL_ALTSTARTUP") },
        { CSIDL_APPDATA, _T("CSIDL_APPDATA") },
        { CSIDL_BITBUCKET, _T("CSIDL_BITBUCKET") },
        { CSIDL_CDBURN_AREA, _T("CSIDL_CDBURN_AREA") },
        { CSIDL_COMMON_ADMINTOOLS, _T("CSIDL_COMMON_ADMINTOOLS") },
        { CSIDL_COMMON_ALTSTARTUP, _T("CSIDL_COMMON_ALTSTARTUP") },
        { CSIDL_COMMON_APPDATA, _T("CSIDL_COMMON_APPDATA") },
        { CSIDL_COMMON_DESKTOPDIRECTORY, _T("CSIDL_COMMON_DESKTOPDIRECTORY") },
        { CSIDL_COMMON_DOCUMENTS, _T("CSIDL_COMMON_DOCUMENTS") },
        { CSIDL_COMMON_FAVORITES, _T("CSIDL_COMMON_FAVORITES") },
        { CSIDL_COMMON_MUSIC, _T("CSIDL_COMMON_MUSIC") },
        { CSIDL_COMMON_OEM_LINKS, _T("CSIDL_COMMON_OEM_LINKS") },
        { CSIDL_COMMON_PICTURES, _T("CSIDL_COMMON_PICTURES") },
        { CSIDL_COMMON_PROGRAMS, _T("CSIDL_COMMON_PROGRAMS") },
        { CSIDL_COMMON_STARTMENU, _T("CSIDL_COMMON_STARTMENU") },
        { CSIDL_COMMON_STARTUP, _T("CSIDL_COMMON_STARTUP") },
        { CSIDL_COMMON_TEMPLATES, _T("CSIDL_COMMON_TEMPLATES") },
        { CSIDL_COMMON_VIDEO, _T("CSIDL_COMMON_VIDEO") },
        { CSIDL_COMPUTERSNEARME, _T("CSIDL_COMPUTERSNEARME") },
        { CSIDL_CONNECTIONS, _T("CSIDL_CONNECTIONS") },
        { CSIDL_CONTROLS, _T("CSIDL_CONTROLS") },
        { CSIDL_COOKIES, _T("CSIDL_COOKIES") },
        { CSIDL_DESKTOP, _T("CSIDL_DESKTOP") },
        { CSIDL_DESKTOPDIRECTORY, _T("CSIDL_DESKTOPDIRECTORY") },
        { CSIDL_DRIVES, _T("CSIDL_DRIVES") },
        { CSIDL_FAVORITES, _T("CSIDL_FAVORITES") },
        { CSIDL_FONTS, _T("CSIDL_FONTS") },
        { CSIDL_HISTORY, _T("CSIDL_HISTORY") },
        { CSIDL_INTERNET, _T("CSIDL_INTERNET") },
        { CSIDL_INTERNET_CACHE, _T("CSIDL_INTERNET_CACHE") },
        { CSIDL_LOCAL_APPDATA, _T("CSIDL_LOCAL_APPDATA") },
        { CSIDL_MYDOCUMENTS, _T("CSIDL_MYDOCUMENTS") },
        { CSIDL_MYMUSIC, _T("CSIDL_MYMUSIC") },
        { CSIDL_MYPICTURES, _T("CSIDL_MYPICTURES") },
        { CSIDL_MYVIDEO, _T("CSIDL_MYVIDEO") },
        { CSIDL_NETHOOD, _T("CSIDL_NETHOOD") },
        { CSIDL_NETWORK, _T("CSIDL_NETWORK") },
        { CSIDL_PERSONAL, _T("CSIDL_PERSONAL") },
        { CSIDL_PRINTERS, _T("CSIDL_PRINTERS") },
        { CSIDL_PRINTHOOD, _T("CSIDL_PRINTHOOD") },
        { CSIDL_PROFILE, _T("CSIDL_PROFILE") },
        { CSIDL_PROGRAM_FILES, _T("CSIDL_PROGRAM_FILES") },
        { CSIDL_PROGRAM_FILESX86, _T("CSIDL_PROGRAM_FILESX86") },
        { CSIDL_PROGRAM_FILES_COMMON, _T("CSIDL_PROGRAM_FILES_COMMON") },
        { CSIDL_PROGRAM_FILES_COMMONX86, _T("CSIDL_PROGRAM_FILES_COMMONX86") },
        { CSIDL_PROGRAMS, _T("CSIDL_PROGRAMS") },
        { CSIDL_RECENT, _T("CSIDL_RECENT") },
        { CSIDL_RESOURCES, _T("CSIDL_RESOURCES") },
        { CSIDL_RESOURCES_LOCALIZED, _T("CSIDL_RESOURCES_LOCALIZED") },
        { CSIDL_SENDTO, _T("CSIDL_SENDTO") },
        { CSIDL_STARTMENU, _T("CSIDL_STARTMENU") },
        { CSIDL_STARTUP, _T("CSIDL_STARTUP") },
        { CSIDL_SYSTEM, _T("CSIDL_SYSTEM") },
        { CSIDL_SYSTEMX86, _T("CSIDL_SYSTEMX86") },
        { CSIDL_TEMPLATES, _T("CSIDL_TEMPLATES") },
        { CSIDL_WINDOWS, _T("CSIDL_WINDOWS") }
    };

    // フォルダーの列挙
    for (int i = 0; i < _countof(folders); i++)
    {
        if (SUCCEEDED(SHGetFolderPath(NULL, 
                                       folders[i].csidl | CSIDL_FLAG_DONT_VERIFY, 
                                       NULL, 
                                      0, 
                                      szPath))) 
        {
            _tprintf(_T("%s:%sn"), folders[i].szId, szPath);
        }
    }

    return 0;
}

特別なフォルダの列挙

上記プログラムの実行結果は以下のとおりです。(フォルダ名の一部に個人名が使用されているところは編集しました。)

CSIDL_ADMINTOOLS:C:Documents and Settingsスタート メニュープログラム管理ツール
CSIDL_ALTSTARTUP:
CSIDL_APPDATA:C:Documents and SettingsApplication Data
CSIDL_CDBURN_AREA:C:Documents and SettingsLocal SettingsApplication DataMicrosoftCD Burning
CSIDL_COMMON_ADMINTOOLS:C:Documents and SettingsAll Usersスタート メニュープログラム管理ツール
CSIDL_COMMON_ALTSTARTUP:
CSIDL_COMMON_APPDATA:C:Documents and SettingsAll UsersApplication Data
CSIDL_COMMON_DESKTOPDIRECTORY:C:Documents and SettingsAll Usersデスクトップ
CSIDL_COMMON_DOCUMENTS:C:Documents and SettingsAll UsersDocuments
CSIDL_COMMON_FAVORITES:C:Documents and SettingsAll UsersFavorites
CSIDL_COMMON_MUSIC:C:Documents and SettingsAll UsersDocumentsMy Music
CSIDL_COMMON_OEM_LINKS:
CSIDL_COMMON_PICTURES:C:Documents and SettingsAll UsersDocumentsMy Pictures
CSIDL_COMMON_PROGRAMS:C:Documents and SettingsAll Usersスタート メニュープログラム
CSIDL_COMMON_STARTMENU:C:Documents and SettingsAll Usersスタート メニュー
CSIDL_COMMON_STARTUP:C:Documents and SettingsAll Usersスタート メニュープログラムスタートアップ
CSIDL_COMMON_TEMPLATES:C:Documents and SettingsAll UsersTemplates
CSIDL_COMMON_VIDEO:C:Documents and SettingsAll UsersDocumentsMy Videos
CSIDL_COOKIES:C:Documents and SettingsCookies
CSIDL_DESKTOP:C:Documents and Settingsデスクトップ
CSIDL_DESKTOPDIRECTORY:C:Documents and Settingsデスクトップ
CSIDL_FAVORITES:C:Documents and SettingsFavorites
CSIDL_FONTS:C:WINDOWSFonts
CSIDL_HISTORY:C:Documents and SettingsLocal SettingsHistory
CSIDL_INTERNET_CACHE:C:Documents and SettingsLocal SettingsTemporary Internet Files
CSIDL_LOCAL_APPDATA:C:Documents and SettingsLocal SettingsApplication Data
CSIDL_MYMUSIC:C:Documents and SettingsMy DocumentsMy Music
CSIDL_MYPICTURES:C:Documents and SettingsMy DocumentsMy Pictures
CSIDL_MYVIDEO:C:Documents and SettingsMy DocumentsMy Videos
CSIDL_NETHOOD:C:Documents and SettingsNetHood
CSIDL_PERSONAL:C:Documents and SettingsMy Documents
CSIDL_PRINTHOOD:C:Documents and SettingsPrintHood
CSIDL_PROFILE:C:Documents and Settings
CSIDL_PROGRAM_FILES:C:Program Files
CSIDL_PROGRAM_FILES_COMMON:C:Program FilesCommon Files
CSIDL_PROGRAMS:C:Documents and Settingsスタート メニュープログラム
CSIDL_RECENT:C:Documents and SettingsRecent
CSIDL_RESOURCES:C:WINDOWSresources
CSIDL_RESOURCES_LOCALIZED:C:WINDOWSresources411
CSIDL_SENDTO:C:Documents and SettingsSendTo
CSIDL_STARTMENU:C:Documents and Settingsスタート メニュー
CSIDL_STARTUP:C:Documents and Settingsスタート メニュープログラムスタートアップ
CSIDL_SYSTEM:C:WINDOWSsystem32
CSIDL_SYSTEMX86:C:WINDOWSsystem32
CSIDL_TEMPLATES:C:Documents and SettingsTemplates
CSIDL_WINDOWS:C:WINDOWS

以上です。

カテゴリー
C++

Windows アプリケーション タスクトレイ表示

Windows アプリケーションのメインウィンドウを表示しないで、タスクトレイ(下記画像)へアイコンとして表示しておいて、マウスでクリックしたら何か表示して、処理をするというのをやってみたいと思います。
(※呼び方がタスクトレイで良いのかわかりませんが、タスクバーの通知領域またはシステムトレイとも言われるようです。)

タスクトレイ アイコン
タスクトレイ アイコン

手順として:
1.Visual Studio で Windows ダイアログ アプリケーションを作ります
2.起動時にアイコンをタスクトレイへ表示します。
3.メインウィンドウを非表示にします。
4.タスクトレイのアイコンがクリックされたらメインウィンドウを表示します。
5.終了時にアイコンをタスクトレイから削除

といった感じで作成します。 なお、1については割愛します。

起動時にアイコンをタスクトレイへ表示

アイコンをタスクトレイへ表示するには、Shell_NotifyIcon API で簡単にできます。パラメータは「追加」、「変更」、「削除」があり、ウィンドウの作成時に追加して、クローズ時に削除すればよいです。

Shell_NotifyIcon を使うために便利な関数を自分のメインウィンドウのクラスに用意してみました。(下記参照)
アイコンなどのリソースはあらかじめ作成しておきます。ここでは、 IDR_TRAYICON というアイコンがある前提です。

MYWM_TRAYNOTIFY というメッセージID を nid.uCallbackMessage に設定してます。これは WM_USER + 1 で定義したユーザー定義メッセージで、タスクトレイのアイコンがクリックされたらメインウィンドウに通知されてきます。

... (省略) ...

#define MYWM_TRAYNOTIFY        WM_USER + 1

... (省略) ...

//
// TrayNotifyIconMessage
//
// dwMessage:
//        NIM_ADD
//        NIM_MODIFY
//        NIM_DELETE
//
BOOL CMyDialog::TrayNotifyIconMessage(DWORD dwMessage)
{
    CString sTip = _T("ポイントされたら表示するテキストを指定します");
    NOTIFYICONDATA nid;

    nid.cbSize = sizeof(NOTIFYICONDATA);
    nid.hWnd   = GetSafeHwnd();
    nid.uID    = IDR_TRAYICON;        // <-- アイコンIDを指定してます
    nid.uFlags = NIF_MESSAGE | NIF_ICON;
    nid.uCallbackMessage = MYWM_TRAYNOTIFY;    // <-- WM_USER + 1 とかでメッセージ定義
    nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
    nid.hIcon  = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_TRAYICON));
    _tcscpy_s(nid.szTip, _countof(nid.szTip), (LPCTSTR)sTip);
    
    return Shell_NotifyIcon(dwMessage, &nid);
}

起動時にアイコンをタスクトレイへ追加します。ダイアログアプリケーションでは OnInitDialog 内で上記の TrayNotifyIconMessage を NIM_ADD をパラメータで指定するだけです。これで、起動時にタスクトレイにアイコンが表示されます。

BOOL CMyDialog::OnInitDialog()
{
    CDialog::OnInitDialog();

... (省略) ...

    // タスクトレイへアイコンを追加
    TrayNotifyIconMessage(NIM_ADD);    

    return TRUE;  // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
}

メインウィンドウを非表示にする

アプリケーション最小化時にタスクトレイに表示させて、タスクバーには表示されないようにするようなので、それを実現するために、メインウィンドウを最小化時に非表示にします。(もちろん、起動時に非表示にしてしまうこともできますが。。。)

WM_SIZE のメッセージハンドラ OnSize 内でパラメータの nType が SIZE_MINIMIZED だった時に ShowWIndow で非表示にします。

void CMyDialog::OnSize(UINT nType, int cx, int cy)
{
    CDialog::OnSize(nType, cx, cy);

    if (nType == SIZE_MINIMIZED)
    {
        ShowWindow(SW_HIDE);
    }
}

このままだと、二度とメインウィンドウが表示されることがないので、タスクトレイのアイコンがダブルクリックされたらメインウィンドウを再表示するようにします。

タスクトレイのアイコンがクリックされたらメインウィンドウを表示

まずは、最初に定義済みの MYWM_TRAYNOTIFY というユーザー定義メッセージをハンドルする OnTrayNotify 関数を用意します。

パラメータの lParam にイベントの種類が入ってきますので、ダブルクリックされたのを判定する WM_LBUTTONDBLCLK と自アプリケーションのアイコンかを判定し、処理を行います。

ここでは、メインウィンドウを表示してフォアグラウンドに持ってきます。

... (省略) ...

BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    //}}AFX_MSG_MAP
    ON_WM_SIZE()
    ON_WM_CLOSE()
    ON_MESSAGE(MYWM_TRAYNOTIFY, OnTrayNotify)
END_MESSAGE_MAP()

... (省略) ...

LRESULT CMyDialog::OnTrayNotify(WPARAM wParam, LPARAM lParam)
{
    switch (lParam)
    {
    case WM_LBUTTONDBLCLK: 
        if (wParam == IDR_TRAYICON)
        {
            ShowWindow(SW_NORMAL);
            SetForegroundWindow();
            SetFocus();
        } 
        break;

    default:
        break;
    } 

    return 0;
}

終了時の処理

そして、最後にメインアプリケーションがクローズされるときに、タスクトレイのアイコンを削除します。

void CMyDialog::OnClose()
{
    // タスクトレイからアイコンを削除
    TrayNotifyIconMessage(NIM_DELETE);    

    CDialog::OnClose();
}

以上です。

あとは、必要に応じて、ポップアップメニューを表示したりする処理を OnTrayNotify 内に追加するなど応用できます。

カテゴリー
C++

Visual C++/MFCライブラリ CSVカンマ区切りの処理

Visual C++ と MFC ライブラリは長い間使っているけど、他の言語のライブラリにはカンマ区切りの文字列を一発で配列に収めて返してくれる split 関数なるものが普通にあります。あらためて MFC CString にはそれがないことを認識しました。むかし、そのような処理をやったことがあったので、そのコードを抜粋しました。

以下、コード抜粋

    CString str = "111,222,333,444,555,,aaa,bbb,ccc,ddd,eee,fff";
    CArray<CString,CString> arr;

    // カンマ区切りを配列に格納
    arr.RemoveAll();
        
    int iStart = 0;
    int iPos = -1;

    CString restoken;
    iPos = str.Find(',', iStart);

    while(iPos > -1)
    {
        restoken = str.Mid(iStart, iPos - iStart);
        arr.Add(restoken);
        printf("Resulting token: %sn", restoken);

        iStart = iPos + 1;
        iPos = str.Find(',', iStart);

        if (iPos == -1)
        {
            restoken = str.Mid(iStart);
            arr.Add(restoken);
            printf("Resulting token: %sn", restoken);
        }
    }

出力結果

Resulting token: 111
Resulting token: 222
Resulting token: 333
Resulting token: 444
Resulting token: 555
Resulting token:
Resulting token: aaa
Resulting token: bbb
Resulting token: ccc
Resulting token: ddd
Resulting token: eee
Resulting token: fff

CSVファイルによっては、各列データををダブルクォーテーションで囲む場合もあるので、それは対応していません。;-)

また、MSDN CStringT::Tokenize のサンプル トークンの区切り文字をカンマにすると良さそうです。
※ 但し、カンマとカンマに文字が何もない場合(赤字の部分)は、その列は無視して次の列をトークンとして読み込むようです。CSV ファイルの処理で、空き列も処理する必要がある場合は要注意です。

以下、MSDN サンプルを一部変更して

    CAtlString str( "111,222,333,444,555,,aaa,bbb,ccc,ddd,eee,fff" );
    CAtlString resToken;
    int curPos= 0;

    resToken= str.Tokenize(",",curPos);
    while (resToken != "")
    {
        printf("Resulting token: %sn", resToken);
        resToken= str.Tokenize(",",curPos);
    }

出力結果

Resulting token: 111
Resulting token: 222
Resulting token: 333
Resulting token: 444
Resulting token: 555
Resulting token: aaa
Resulting token: bbb
Resulting token: ccc
Resulting token: ddd
Resulting token: eee
Resulting token: fff
カテゴリー
C#

C# データテーブルの内容をCSV出力

C#/ASP.NET でデータテーブルの内容を CSV 出力するサンプルを作成しました。
VisualStudio 2008 で動作確認しています。

using System;
using System.Data;
using System.IO;

namespace WebApplication1
{
    public class DT : System.Data.DataTable
    {
        private string lastError = string.Empty;

        public string LastError
        {
            get { return lastError; }
            set { lastError = value; }
        }

        ///
        /// CSV形式で出力
        ///
        public bool SaveToStream(MemoryStream ms, string rowDelim,
                                 char colDelim, char celDelim, int codepage)
        {
            bool result = true;

            try
            {
                // ヘッダ部
                string line = string.Empty;

                foreach (DataColumn col in this.Columns)
                {
                    line += celDelim;

                    line += col.Caption;

                    line += celDelim;

                    line += colDelim;
                }
                // 末尾の ',' 取り除く
                line = line.TrimEnd(colDelim);
                // 改行の付加
                line += rowDelim;

                // ストリームへ出力
                byte[] byteArray = System.Text.Encoding.GetEncoding(codepage).GetBytes(line);
                ms.Write(byteArray, 0, byteArray.Length);

                // データ部
                int colCount = this.Columns.Count;

                foreach (DataRow row in this.Rows)
                {
                    line = string.Empty;

                    for (int i = 0; i < colCount; i++)
                    {
                        // フィールドの取得
                        string field = row[i].ToString();

                        line += celDelim;

                        line += field;

                        line += celDelim;

                        line += colDelim;
                    }
                    // 末尾の ',' 取り除く
                    line = line.TrimEnd(colDelim);
                    // 改行の付加
                    line += rowDelim;

                    // ストリームへ出力
                    byteArray = System.Text.Encoding.GetEncoding(codepage).GetBytes(line);
                    ms.Write(byteArray, 0, byteArray.Length);
                }
            }
            catch (Exception e)
            {
                LastError = e.ToString();
                result = false;
            }

            return result;
        }
        ///
        /// サンプル データ作成
        ///
        public void MakeSampleTable()
        {
            DataColumn column;
            DataRow row;

            // DataColumn の作成
            // 1列目を作成
            column = new DataColumn();
            column.DataType = System.Type.GetType("System.Int32");
            column.ColumnName = "ID";
            column.ReadOnly = true;
            column.Unique = true;
            this.Columns.Add(column);

            // 2列目を作成.
            column = new DataColumn();
            column.DataType = System.Type.GetType("System.String");
            column.ColumnName = "データ";
            column.AutoIncrement = false;
            column.Caption = "データ";
            column.ReadOnly = false;
            column.Unique = false;
            this.Columns.Add(column);

            // ID列を主キーに設定
            DataColumn[] PrimaryKeyColumns = new DataColumn[1];
            PrimaryKeyColumns[0] = this.Columns["ID"];
            this.PrimaryKey = PrimaryKeyColumns;

            // データセットを作成してデータを作成
            DataSet dataSet = new DataSet();
            dataSet.Tables.Add(this);
            // データを10件追加
            for (int i = 0; i <= 10; i++)
            {
                row = this.NewRow();
                row["ID"] = i;
                row["データ"] = "データ " + i;
                this.Rows.Add(row);
            }
        }
    }

    public partial class _Default : System.Web.UI.Page
    {
        const string rowDelim = "rn";
        const char colDelim = ',';
        const char celDelim = '"';
        const int codepage = 932;
        const string csvname = "sample.csv";

        protected void Page_Load(object sender, EventArgs e)
        {

        }

        ///
        /// クライアントへCSV出力
        ///
        protected void Button1_Click(object sender, EventArgs e)
        {
            DT dt = new DT();
            dt.MakeSampleTable();

            // サンプル データを MemoryStream にへ出力
            System.IO.MemoryStream ms = new System.IO.MemoryStream();

            if (!dt.SaveToStream(ms, rowDelim, colDelim, celDelim, codepage))
            {
                System.Diagnostics.Debug.Print(dt.LastError);
            }

            // MemoryStream をクライアント側へ送信
             Response.Clear();
            Response.ContentType = "application/octet-stream";
            Response.AddHeader("Content-Disposition", "attachment;filename=" + Server.UrlEncode(csvname));
            Response.BinaryWrite(ms.ToArray());

            ms.Close();
            Response.Close();
        }
    }
}