タグ:.Net ( 213 ) タグの人気記事
(.Net,ADSI)識別名(DN)のエスケープ

ActiveDirectoryというかLDAPの仕様だと思うんですが、重複した識別名(DN)のオブジェクトは作成できません。
で、.Netから新規ActiveDirectoryユーザを作成するツールを作ったんですが、DNの重複チェックができていませんでした。(ログインアカウント名の重複チェックはできてたんですけどね。。)

それで、新規作成するユーザ名からDNを生成して、それがActiveDirectoryに存在するかどうかのチェックをすることにしました。

単純にユーザ名、OU名、ドメイン名からDNを生成して、生成されたDNが既にあるかどうかをチェックすればいいだけなんですが、どうやらDNにはエスケープすべき特殊文字があるようです。

それは、「#」、「,」、「+」、「"」、「\」、「<」、「>」、「;」のようです。

ということで、ユーザ名を¥でエスケープしてActiveDirectoryから検索するコードを書いてみました。(C#)
(既にOU名とドメイン名は分かってることとします。)

string username = "hoge#<> ,hoge+";
string ou = "OU=testou";
string domain ="DC=hogedomain,DC=jp";
 
//識別名(DN)を作るにあたって「#」、「,」、「+」、「"」、「\」、「<」、「>」、「;」はエスケープ
string strDN = return System.Text.RegularExpressions.Regex.Replace(username, "#|,|\\+|\"|\\|<|>|;", "\\$&");
//DN名生成
strDN = "CN=" + strDN + "," + ou + "," + domain;
 
DirectoryEntry mDrctEntry;
mDrctEntry = new DirectoryEntry(@"LDAP://domaincontroler/DC=hogedomain,DC=jp", "administrator", "Passwd");
// LDAP検索オブジェクトを作成
DirectorySearcher drSearch = new DirectorySearcher(mDrctEntry);
 
// アカウントフィルターを設定 識別名で検索
drSearch.Filter = "(distinguishedName=" + strDN + ")";
 
// 検索する
SearchResultCollection scn = drSearch.FindAll();
//見つからなければnullを返す。
if (scn == null || scn.Count <= 0){
//見つからなかった時の処理
}else{
//見つかった時の処理
}

特殊文字をエスケープする部分は正規表現を使って置換しています。
正規表現での置換は、Text.RegularExpressions.Regex クラスの Replace メソッドを使えばいいようです。
置換する文字列に $& を指定すると、パターンで引っ掛かった文字を使うことができるようです。
久しぶりに正規表現つかましたが、しょっちゅう触ってないと難しいですね。

もしかしたら、エスケープしてくれるようなメソッドがあるのかもしれませんが、見当たらなかったので今回は手で書きました。


参考:
2.4.4 特殊文字を含むDNの指定
TechNet:オブジェクト名
dobon:正規表現の基本
dobon:正規表現を使って文字列を置換する
[PR]
by jehoshaphat | 2010-10-24 21:35 | .Net開発 | Trackback | Comments(0)
(.Net)別ユーザでプロセスを起動する
C++ で Windows API を使って別のユーザでアプリケーションを書く方法は、(C++)別ユーザでプロセスを起動するで書きました。
今回は .Net でそれを実現する方法です。
基本的には、ProcessStartInfo. Domain , ProcessStartInfo.UserName , ProcessStartInfo.Password プロパティを指定して、Process.Start すればいいだけのようですね。

ただし、 ProcessStartInfo.Password は SecureString オブジェクトを指定しないといけないのが曲者です。
SecureString に文字列をセットするには、SecureString.AppendChar(char型) で一文字ずつセットしないといけないようです。

で、今回コンソールアプケーションを作成し、引数に指定されたユーザ名とパスワードで別アプリケーション(今回はバッチファイル)を起動するというコードを書く必要がありました。
下記のような感じすればいいみたいです。(C#)

static void Main(string[] args)
{
//コマンドライン引数に下記のように情報が入ることとする。
//1:ユーザ名 2:パスワード 3:実行ファイル名 4:ドメイン名 ちなみに0には必ず自身のexeのパスが入るらしい
//コマンドライン引数配列取得
string[] cmds;
cmds = System.Environment.GetCommandLineArgs();
 
//プロセスオブジェクト生成
Process proc = new Process();
// 起動するアプリケーションを設定する
proc.StartInfo.FileName = cmds[3];
// 起動するアプリケーションに対するコマンドライン引数を設定する
proc.StartInfo.Arguments = "args";
// ウィンドウを作成しないかどうかを設定する (初期値 false)trueにするとウィンドウ表示なしとなる
proc.StartInfo.CreateNoWindow = false;
// シェルを使用するかどうか設定する (初期値 true)falseにするとコンソールウィンドウが表示なしとなる
proc.StartInfo.UseShellExecute = true;
// 起動できなかった時にエラーダイアログを表示するかどうかを設定する (初期値 false)
proc.StartInfo.ErrorDialog = false;
 
//プロセス実行時のドメイン名(ローカルユーザの場合はnullを指定)
proc.StartInfo.Domain = cmds[4];
//プロセス実行時のユーザ名
proc.StartInfo.UserName = cmds[1];
//パスワード(ProcessStartInfo.PasswordはSecureStringで指定しないといけない)
SecureString password = new SecureString();
foreach (char c in cmds[2].ToCharArray())
{
//SecureStringの文字追加
password.AppendChar(c);
}
//プロセス実行時のユーザのパスワード指定
proc.StartInfo.Password = password;
 
// 起動時のウィンドウの状態を設定する
proc.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal; //通常
// 起動する
proc.Start();
//起動したプロセスが終わるまで待つ。(60秒待つ)
proc.WaitForExit(60000);
 
//終了コード取得
int iExitCode = proc.ExitCode;
Console.WriteLine(iExitCode.ToString());
//終了コードをOSに返して終了
Environment.Exit(iExitCode);
}

上記で、ProcessStartInfoクラスに情報をセットする方法でしたが、それとは別に Process.Start のスタティックメソッドでプロセスを簡単に起動したい場合は、Process.Start(実行ファイル名,ユーザ名,パスワード,ドメイン名) としてもいいようです。


参考:
@IT:別のユーザーでほかのアプリケーションを実行するには?
@IT:コマンド・プロンプトを表示せずにコンソール・アプリケーションを実行するには?ProcessStartInfo.CreateNoWindow、ProcessStartInfo.UseShellExecuteプロパティがキックしたプロセスの表示にどう影響するかが参考になります。
C# - 細かい条件を指定してプログラムを起動する
[PR]
by jehoshaphat | 2010-10-24 21:33 | .Net開発 | Trackback | Comments(0)
(.Net)コマンドライン引数を取得する方法

コマンドライン引数の取得ですが大きく二つあるようです。

1.Environmentクラスを使う方法
Environment.CommandLine プロパティに自身のパスも含めたコマンドライン引数全体が格納されているようです。
ただこれだと使い辛いので普通は Environment.GetCommandLineArgs メソッドを使います。
Environment.GetCommandLineArgs メソッドを使うと自身のパスも含めたコマンドライン引数を文字列の配列として取得できるようです。(0番目は自身のexeのパス名となる)


2.Mainメソッドのパラメータを使う方法
これはCの時代から有る方法ですね。エントリポイントとなる Main メソッドの引数がコマンドライン引数となります。(自身のパスが引数となることはありません)


ちなみに、VisualStudioでデバッグ時にコマンドライン引数を渡したい場合はプロジェクトのプロパティの デバッグ → コマンドライン引数 に設定することで可能です。


参考:
dobon:起動時のコマンドライン引数を取得する
[PR]
by jehoshaphat | 2010-10-24 10:43 | .Net開発 | Trackback | Comments(0)
(.Net)アプリケーションの終了コードを返す方法、取得する方法
.Net のコンソールアプリケーションでOSに終了コードを返す方法です。

いくつか有るようですが、一番オーソドックスなのはエントリポイントであるMainメソッドの戻り値を指定する方法です。
ただ、Visual Studio で新規にコンソールアプリケーションのProject作成すると、Main メソッドの戻り値が void になっているので、これを int 型に直す必要があります。

こんな感じです。(C#)

class Program
{
static int Main(string[] args)
{
return 1;
}
}

また、Environment.Exit メソッドを使う方法もあるようです。引数に終了コードを指定するようですね。
この場合、プロセスを殺すとともにOSに終了コードを渡せます。(Main メソッドの戻り値の型が void でも使えるみたいです。)


Environment.Exit(1);


さらに、Environment.ExitCode プロパティに終了コードを設定するという方法もあるようですね。

Environment.ExitCode = 1;
return;


参考:
@IT:コンソール・アプリケーションで終了コードを返すには?


逆に、.Net アプリ自身から起動したプロセスの終了コードを取得こともできるようです。

Process オブジェクトを使ってアプリケーション起動後、WaitForExitメソッドで終了まで待ち、Process.ExitCode プロパティで起動したプロセスの終了コードを取得できます。

具体的にはこんな感じ。(C#)

using System.Diagnostics;
 
//プロセスオブジェクト生成
Process proc = new Process();
// 起動するアプリケーションを設定する
proc.StartInfo.FileName = "test.exe";

// 起動する
proc.Start();
//起動したプロセスが終了するまで待つ。
proc.WaitForExit();
//終了コード取得
int iExitCode = proc.ExitCode;


参考:
@IT:ほかのアプリを実行して終了コードを得るには?
@IT:.NET TIPS ほかのアプリケーションを実行してその終了を待つには? Process.WaitForExitメソッドでプロセス終了を待つ説明が書いてます。

なお、バッチファイル内で直前の処理の終了コードを参照する方法はバッチファイルで直前のコマンドの終了コード判定で書いてます。
[PR]
by jehoshaphat | 2010-10-23 10:04 | .Net開発 | Trackback | Comments(0)
(.Net)数値型を書式指定子を使って3桁区切りで出力する
今まで int.ToString("###,###,###,##0#) 的な出力をしてたんですが、標準数値書式指定文字列を使うともっと楽にできるようです。
一応標準数値書式指定文字列の "N" を使うと3桁区切りで出ることは知ってたんですが、小数点以下2桁まで出力されるのが問題でした。

この標準数値書式指定文字列ですが、書式指定子の後に、数値を入れることで小数点の桁数を制御できるようです。
なので、整数3桁区切りで出したいときは下記のようすればいいようですね。

int i = 1234567890;
string s = i.ToString("N0");
//1,234,567,890として出力


参考:
.NET Framework - 書式指定文字列
MSDN:標準の数値書式指定文字列
[PR]
by jehoshaphat | 2010-09-25 03:15 | .Net開発 | Trackback | Comments(0)
(.Net,OpenOffice).NetからCalcの行の挿入、削除を行う
(.NetからOpenOfficeを操作する方法は(.Net,OpenOffice).Net から OpenOffice ドキュメントを操作するを参照。OpenOfficeは3.2です。)

Calc操作時に、行を挿入、削除する方法です。


OpenOffice SDKのサンプルにやりたいことが書いていました。
サンプルは、OpenOffice.org_3.2_SDK\sdk\examples\CLI\CSharp\Spreadsheet\GeneralTableSample.cs の doFormattingSamples メソッドに書いてあります。

ほぼサンプルそのままですが、一応ソース載せておきます。(C#)

//下記の型インポート必要
using uno.util;
using unoidl.com.sun.star.frame;
using unoidl.com.sun.star.lang;
using unoidl.com.sun.star.sheet;
using unoidl.com.sun.star.table;
using unoidl.com.sun.star.uno;
using unoidl.com.sun.star.beans;
using unoidl.com.sun.star.util;
using unoidl.com.sun.star.container;
 
string filename = @"d:\a.ods";
 
//ファイルパスを変換
Uri uriCalcFile;
Uri.TryCreate(filename, UriKind.Absolute, out uriCalcFile);
filename = uriCalcFile.ToString();
//コンポーネントコンテキストオブジェクト取得
XComponentContext context = Bootstrap.bootstrap();
//サービスマネージャ取得
XMultiServiceFactory factory = (XMultiServiceFactory)context.getServiceManager();
//コンポーネントローダオブジェクト取得
XComponentLoader loader = (XComponentLoader)factory.createInstance("com.sun.star.frame.Desktop");
//ドキュメントオブジェクト取得
XSpreadsheetDocument doc = (XSpreadsheetDocument)loader.loadComponentFromURL(filename, "_blank", 0, null);
//シート取得
XSpreadsheets sheets = doc.getSheets();
XSpreadsheet xSheet = (XSpreadsheet)sheets.getByName("Sheet1").Value;
 
//行オブジェクト取得
XColumnRowRange xCRRange = (XColumnRowRange)sheet;
XTableRows xRows = xCRRange.getRows();
 
//行挿入(5行目から3行挿入する)
xRows.insertByIndex(5, 3);
 
 
//行削除(10行目から2行削除する)
xRows.removeByIndex(10, 2);

insertByIndex,removeByIndex の行インデックスは0から始まっていることに注意です。
[PR]
by jehoshaphat | 2010-09-23 03:16 | .Net開発 | Trackback | Comments(0)
(.Net)UDPの送受信テストツール
連続したUDPパケットが送信側と受信側でちゃんとやり取りされているかを確認したいと思ってツールを探してみたんですが、いいものが無かったのでちょっと.Net Framework で自作してみました。

とりあえず急ぎでパケットの送受信さえ確認できればいいというものなので、作りは非常に雑です。
しかし、こんなに簡単にネットワーク通信アプリが作れる .Net というフレームワークはやはり評価すべきだなと思いました。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
 
namespace UDPtest
{
public partial class Form1 : Form
{
private int iCnt = 10000;
private bool flgStop = true;
 
public Form1()
{
InitializeComponent();
}
 
private void button1_Click(object sender, EventArgs e)
{
richTextBox1.Clear();
 
//文字コードを指定する
System.Text.Encoding enc = System.Text.Encoding.UTF8;
 
//データを送信するリモートホストとポート番号
string remoteHost = "192.168.0.100";
int remotePort = 55001;
//バインドするローカルポート番号
int localPort = 55000;
 
//ローカルポート番号localPortにバインドする
System.Net.Sockets.UdpClient udp =
new System.Net.Sockets.UdpClient(localPort);
 
int imtu = 10000;
 
for (int i = 0; i < iCnt; i++)
{
StringBuilder strMsg = new StringBuilder();
strMsg.Append(i.ToString("000000000"));
richTextBox1.AppendText(strMsg.ToString() + Environment.NewLine);
for (int j = 0; j < 1000; j++)
{
strMsg.Append("a");
}
 
byte[] sendBytes = enc.GetBytes(strMsg.ToString());
udp.Send(sendBytes, sendBytes.Length, remoteHost, remotePort);
//System.Threading.Thread.Sleep(1);
 
}
//UDP接続を終了
udp.Close();
Console.ReadLine();
}
 
private void button2_Click(object sender, EventArgs e)
{
richTextBox1.Clear();
//文字コードを指定する
System.Text.Encoding enc = System.Text.Encoding.UTF8;
 
//バインドするローカルポート番号
int localPort = 55001;
 
//ローカルポート番号localPortにバインドする
System.Net.Sockets.UdpClient udp =
new System.Net.Sockets.UdpClient(localPort);
 
for (int i = 0; i < iCnt - 1; i++)
{
//データを受信する
System.Net.IPEndPoint remoteEP = null;
byte[] rcvBytes = udp.Receive(ref remoteEP);
string rcvMsg = enc.GetString(rcvBytes);
rcvMsg = rcvMsg.Substring(0, 9);
 
System.IO.File.AppendAllText("res.txt", rcvMsg + Environment.NewLine);
}
udp.Close();
}
}
}

送信側で Thread.Sleep をしないと、結構な数のパケットが受信側に届きませんでした。
逆に Thread.Sleep(1) とすると、ほとんどのパケットが受信側に届きました。
どこでパケットロスしているのかがわかるといいんですが。。。。

参考:
dobon:UDPによりデータの送受信を行う
[PR]
by jehoshaphat | 2010-09-20 20:20 | .Net開発 | Trackback | Comments(0)
(.Net)long→object→intへのキャスト時の注意
DataTable.Compute("SUM(clm1)") を使って、DataTableの集計値を求めたいと思ってました。
(DataTableのclm1列は int 型です。DataTable.Computeについては、(.Net)DataTableで特定の列の値を集計する参照。)

で下記のようなコードにすると InvalidCastException 例外が発生し、"指定されたキャストは有効ではありません。"と怒られます。。(C#)

//tblはDataTableインスタンス
int i = tbl.Compute("SUM(clm1"),null);

デバッガのウォッチ式で覗いてみると、DataTable.Compute は object 型で返り、内部では long 型となっているようです。
で、ちょっと実験をしてみました。

object o = new object() ;
long l = 50000;
int i = 0;
 
i = (int)l;
Console.WriteLine("long→int:" + i.ToString());
 
o = l;
Console.WriteLine("long→object:" + o.ToString());
 
i = (int)o; // ← ここで例外発生
Console.WriteLine("long→object→int:" + i.ToString());

long から int へは暗黙的キャストされますが、内部的に long 型の値を object に入れると object から int へのキャストはダメなようです。

下記のように、一旦 object から long に、そして int にキャストするとちゃんと動くようになりました。

i = (int)(long)o;
Console.WriteLine("long→object→long→long→int:" + i.ToString());


まぁ、当たり前といえば当たり前かもしれませんが、ちょっとハマりましたね。
DataTable.Compute が long を返すのも驚きです。(集計列は int なのに。。)

また、最近まで知りませんでしたが、 C# にはキャストに as 演算子というものが使えるようです。(参照型でしか使えませんが。。。)
as 演算子については@IT:.NET TIPS as演算子とキャストの違いはがわかりやすいです。
[PR]
by jehoshaphat | 2010-09-20 20:11 | .Net開発 | Trackback | Comments(0)
(.Net)DataGridViewでリセットする方法
DataGridViewにDataTableをバインドし、ユーザがリセットボタンを押したタイミングで、初期値に戻すための方法です。

DataGridView のメンバでそれらしいを探してみたんですが、見当たりません。
仕方なく下記のような方法をとることとしました。(C#)
DataTable mTblBkup;
DataTable tbl ; //tblには既にインスタンスが生成されてることとします。
 
public void Form1_Load(DataTable tbl)
{
mTblBkup = tbl.Copy();
 
datagridview1.DataSource = tbl;
 
}
 
private void btnReset_Click(object sender, EventArgs e)
{
tbl = mTblBkup.Copy();
dgvFilter.DataSource = tbl;
}

DataTable.Copy で DataTable のバックアップを作成し、リセットボタンを押下したら、バックアップを戻すというような方法です。
あまり、きれいな方法ではありませんが、とりあえずできました。
[PR]
by jehoshaphat | 2010-09-16 20:04 | .Net開発 | Trackback | Comments(1)
(.Net)自作クラスに終了処理を実装する
ファイル操作を行う自作クラスでインスタンスを利用し終わった後にリソース解放の終了処理をしたいと思っています。

C++ のときはデストラクタに終了処理を書けばよかったんですが、.Net Framework の場合はその方法は使わない方がいいようです。
(ガーベージコレクションでいつ解放されるかわからないので....)


で、自作クラスに終了処理を実装にするには IDisposable インターフェイスを実装して、そこに終了処理を書けばいいようです。

こんな感じです。(C#)
public class TestClass : IDisposable {
public TestClass() {
//コンストラクタ
}
 
public void Dispose() {
// 終了処理
}
}

このクラスを呼び出して使う側でもいくつか手順が必要なようです。

一つは finally で Dispose メソッドを呼び出す方法です。
TestClass cls = new TestClass();
try{
// 処理
}finally{
cls.Dispose();
}

C# にはもう一つ using 句を使う方法があるようです。
(using といっても名前空間とは別物のようです。)
使い方はこんな感じです。
using (TestClass cls = new TestClass()) {
// 処理
}

finally を明示的に書かなくて済むので解放忘れは防げそうですね。
今まで using 句はほとんど使ってこなかったでこれからは使っていこうかと思います。
ただ個人的には、インデントがついてしまうのが気になります。。


補足ですが、Disposeメソッドと共に終了処理として Close メソッドを実装しているクラスがいくつかありますよね。(Connection 系や IO Stream 系)
これらのクラスを Open メソッドで使った時は明示的 finally 句で Close してやらないといけないようです。
つまり、using だけではダメということです。
結局こうなります。
using (StreamReader rd = new StreamReader(@"C:\Hoge.txt", System.Text.Encoding.Default)) {

try {
//処理...
} finally {
// rd を閉じる
if (rd != null) {

rd.Close();
}
}
}

参考:
MSDN:Dispose メソッドの実装
@IT:.NET TIPS 確実な終了処理を行うには? - C#
C# の using ステートメントによる Dispose()
IDisposableとDispose - はらぐろブログラマン
C# オブジェクトの破棄を保証する
[PR]
by jehoshaphat | 2010-08-19 23:46 | Trackback | Comments(0)