Quantcast
Channel: わんくま同盟
Viewing all 994 articles
Browse latest View live

ネイティブ ADSI オブジェクト

$
0
0

検索して取得したユーザの DirectoryEntry オブジェクトから姓と名を取得するには次のように書きます。

VB

Dim lastName = DirectCast(DirectoryEntry.Properties.Item("sn").Value, String)

Dim firstName = DirectCast(DirectoryEntry.Properties.Item("givenName").Value, String)

C#

var lastName = (string)DirectoryEntry.Properties["sn"].Value;

var firstName = (string)DirectoryEntry.Properties["givenName"].Value;

 

プロパティ名(属性名)が 姓は「sn」、名は「givenName」です。

ADSI(Active Directory Services Interfaces)を使うとネイティブなユーザ(IADsUser)オブジェクトとして扱えるので次のように書くことができます。

VB

Dim lastName = IADsUser.LastName

Dim firstName = IADsUser.FirstName

C#

var lastName = IADsUser.LastName;

var firstName = IADsUser.FirstName;

 

ADSI を使う場合は参照の追加で「COM」タブから「Active DS Type Library」を選びます。

どのように ADSI のオブジェクトを取得するかというと、DirectoryEntry.NativeObject プロパティ(Object 型)を IADsUser にキャストするだけです。

DirectoryEntry がグループなら IADsGroup に、コンピュータなら IADsComputer に、組織単位(OU)なら IADsOU に、プリンタなら IADsPrintQueue にキャストできます。

これらのインターフェイスの基本インターフェイスが IADs インターフェイスです。

 

サンプルアプリではWindowsアプリもWebアプリもユーザやグループの情報を画面に表示するので、コントロールにバインドできるようにライブラリ側にインターフェイスを定義し、それを実装したクラスを用意します。(別途書くかソースを公開します。)

また、扱う Directory オブジェクトの種類を表す列挙体「CategoryType」をライブラリ側の名前空間直下に定義します。

メンバは次の7つです。

User    'ユーザ

Group    'グループ

Computer    'コンピュータ

OrganizationalUnit    '組織単位(OU)

PrintQueue    'プリンタ

Volume    '共有フォルダ

ForeignSecurityPrincipal    '外部のセキュリティ プリンシパル


きた西出口、みなみ西出口

Directoryオブジェクト用のインターフェイス

$
0
0

ユーザ、グループ、コンピュータなどのDirectoryオブジェクト用のインターフェイスを定義します。

ユーザとグループはローカルとドメインの両方があるので、共通用として定義します。

ユーザリスト画面はこんな感じです。(クリックすると新しいウィンドウで拡大図が表示されます。)

UserListWin UserListWeb

この画面上に表示されてる情報のみ扱うこととします。

更新機能はいずれ。まずは表示だけということで。

インターフェイスは次の4つを定義します。

IDirectory    'Directory オブジェクトのプロパティを定義

IDomain    'ドメインの Directory オブジェクトのプロパティを定義

IUser    'User オブジェクトの共通プロパティを定義

IGroup    'Group オブジェクトの共通プロパティを定義

 

VB

Public InterfaceIDirectory

  InheritsIDisposable

  Property Description As String    '説明を取得または設定

  ReadOnly Property Entry AsDirectoryEntry    '関連付けられた DirectoryEntry を取得

  ReadOnly Property IsDisposed As Boolean    'オブジェクトが破棄されているかどうかを取得

  ReadOnly Property Name As String    '名前を取得

End Interface

C#

public interfaceIDirectory : IDisposable

{

  string Description { get; set; }    //説明を取得または設定

  DirectoryEntry Entry { get; }    //関連付けられた DirectoryEntry を取得

  bool IsDisposed { get; }    //オブジェクトが破棄されているかどうかを取得

  string Name { get; }    //名前を取得

}

 

VB

Public InterfaceIDomain

  InheritsIDirectory

  ReadOnly Property DisplayPath As String    '表示用の所属パスを取得

End Interface

C#

public interfaceIDomain : IDirectory

{

  string DisplayPath { get; }    //表示用の所属パスを取得

}

 

VB

Public InterfaceIUser

  InheritsIDirectory

  Property Disabled As Boolean    'アカウントが無効かどうかを取得

  Property FullName As String    'フルネーム・表示名を取得または設定

  ReadOnly Property Native AsIADsUser    'Entry の ADSI User オブジェクトを取得

End Interface

C#

public interfaceIUser : IDirectory

{

  bool Disabled { get; }    //アカウントが無効かどうかを取得

  string FullName { get; set; }    //フルネーム・表示名を取得または設定

  IADsUser Native { get; }    //Entry の ADSI User オブジェクトを取得

}

 

VB

Public InterfaceIGroup

  InheritsIDirectory

  ReadOnly Property Native AsIADsGroup    'Entry の ADSI Group オブジェクトを取得

End Interface

C#

public interfaceIGroup : IDirectory

{

  IADsGroup Native { get; }    //Entry の ADSI Group オブジェクトを取得

}

Directoryオブジェクト用のインターフェイスのソース

「不正でないが正当でない」...って成立するの?

Directoryオブジェクト用のクラス

$
0
0

WindowsアプリとWebアプリのフォーム上のコントロールにバインドしたり、フォーム側から楽に処理したりできるように、定義したDirectoryオブジェクト用のインターフェイスを実装したクラスを作成していきます。

長くなるので宣言部のみ書きます。大事なとこはちゃんと内部実装を書きます。

最初は DirectoryObject クラス。このクラスはサンプルアプリで扱う Directory オブジェクトの最も基本となるクラスです。

VB

Public MustInherit ClassDirectoryObject

  ImplementsIDirectory

  'コンストラクタ

  'DirectoryEntry を指定して DirectoryObject クラスの新しいインスタンスを初期化します。

  Protected Sub New(entry AsDirectoryEntry)

 

  'プロパティ

  'Directory オブジェクトの種類を取得します。

  Public ReadOnly Property Category AsCategoryType

  '説明を取得または設定します。

  Public Property Description As String ImplementsIDirectory.Description

  '関連付けられた DirectoryEntry を取得します。

  Public ReadOnly Property Entry AsDirectoryEntryImplementsIDirectory.Entry

  'オブジェクトが破棄されているかどうかを取得します。

  Public ReadOnly Property IsDisposed As Boolean ImplementsIDirectory.IsDisposed

  '名前を取得します。

  Public Overridable ReadOnly Property Name As String ImplementsIDirectory.Name

 

  'メソッド

  '使用されているリソースを解放します。

  Public Sub Dispose() ImplementsIDisposable.Dispose

  '名前を返します。

  Public Overrides Function ToString() As String

  '使用されているリソースを解放し、オプションでマネージ リソースも解放します。

  Protected Overridable Sub Dispose(disposing As Boolean)

 

C#

public abstract classDirectoryObject : IDirectory

  //コンストラクタ

  //DirectoryEntry を指定して DirectoryObject クラスの新しいインスタンスを初期化します。

  protected DirectoryObject(DirectoryEntry entry)

 

  //プロパティ

  //Directory オブジェクトの種類を取得します。

  publicCategoryType Category

  //説明を取得または設定します。

  public string Description

  //関連付けられた DirectoryEntry を取得します。

  publicDirectoryEntry Entry

  //オブジェクトが破棄されているかどうかを取得します。

  public bool IsDisposed

  //名前を取得します。

  public virtual string Name

 

  //メソッド

  //使用されているリソースを解放します。

  public void Dispose()

  //名前を返します。

  public override string ToString()

  //使用されているリソースを解放し、オプションでマネージ リソースも解放します。

  protected virtual void Dispose(bool disposing)

 

コンストラクタ内で引数のチェックをしてます。

VB

If entry Is Nothing Then

  Throw NewArgumentNullException("entry", "entry が Nothing です。")

End If

If[Enum].TryParse(OfCategoryType)(entry.SchemaClassName, True, _category) = False Then

  Throw NewArgumentException("entry の種類が CategoryType に該当しません。", "entry")

End If

C#

if (entry == null)

{

  throw newArgumentNullException("entry", "entry が null です。");

}

if (Enum.TryParse<CategoryType>(entry.SchemaClassName, true, out _category) == false)

{

  throw newArgumentException("entry の種類が CategoryType に該当しません。", "entry");

}

 

DirectoryEntry.SchemaClassName プロパティの値を CategoryType 列挙体の値に変換できなかったら、サンプルアプリで扱わない Directory オブジェクトとし ArgumentException をスローしてます。

 

Description プロパティの Get 内はこんな感じ。

VB

If _disposed Then

  Throw NewObjectDisposedException(Me.GetType().Name)

End If

Return DirectCast(_entry.Properties.Item("description").Value, String)

C#

if (_disposed)

{

  throw newObjectDisposedException(this.GetType().Name);

}

return (string)_entry.Properties["description"].Value;

 

DirectoryEntry.Properties プロパティの description 属性の値を返してます。

Set 内はこの値を設定してるので、何か値をセットして例えば更新ボタンクリック時の処理として DirectoryEntry.CommitChanges メソッドを呼び出すと、そのオブジェクトの「説明」が更新されます。

ドメインオブジェクト用のクラス

$
0
0

DirectoryObject クラスの次は DomainObject クラスです。このクラスはドメインの Directory オブジェクト共通のクラスになる抽象クラスで、DirectoryObject クラスを継承し IDomain インターフェイスを実装します。

Windowsアプリではローカルのユーザとグループも扱うため、ローカルのクラスは Directory オブジェクト用のクラスを継承して作成することになります。

ローカルのユーザとグループを扱わないならクラス(と実装インターフェイス)を分ける必要はないですし、IUser と IGroupのインターフェイスもいらないです。

 

VB

Public MustInherit ClassDomainObject

  InheritsDirectoryObject

  ImplementsIDomain

 

#Region" プライベートフィールド "

  Private ReadOnly _path, _name As String

#End Region

 

#Region" プロテクトコンストラクタ "

  'DirectoryEntry を指定して DirectoryObject クラスの新しいインスタンスを初期化します。

  Protected Sub New(entry AsDirectoryEntry)

    MyBase.New(entry)

    _path = DirectoryAccess.GetBelongPath(entry)    '所属パスを取得

    _name = entry.Properties.Item("name").Value.ToString()

  End Sub

#End Region

 

#Region" パブリックプロパティ "

  '表示用の所属パスを取得します。

  Public ReadOnly Property DisplayPath As String ImplementsIDomain.DisplayPath

    Get

      Return _path

    End Get

  End Property

 

  '表示用の名前を取得します。

  Public Overrides ReadOnly Property Name As String

    Get

      Return _name

    End Get

  End Property

#End Region

End Class

 

C#

public abstract classDomainObject : DirectoryObject, IDomain

{

  #regionプライベートフィールド

  private readonly string _path, _name;

  #endregion

 

  #regionプロテクトコンストラクタ

  //DirectoryEntry を指定して DirectoryObject クラスの新しいインスタンスを初期化します。

  protected DomainObject(DirectoryEntry entry)

    : base(entry)

  {

    _path = DirectoryAccess.GetBelongPath(entry);    //所属パスを取得

    _name = entry.Properties["name"].Value.ToString();

  }

  #endregion

 

  #regionパブリックプロパティ

  //表示用の所属パスを取得します。

  public string DisplayPath

  {

    get

    {

      return _path;

    }

  }

 

  //表示用の名前を取得します。

  public override string Name

  {

    get

    {

      return _name;

    }

  }

  #endregion

}

 

コンストラクタ内で表示用の所属パスをセットするために DirectoryAccess.GetBelongPath メソッドを呼んでます。

このメソッドは引数で受け取った DirectoryEntry の Path プロパティの値を、表示用の所属パスにして返します。

例えば virtual.testdom.com ドメインの本社OU内の営業部OUにある SalesUser というオブジェクトの DirectoryEntry の場合、Path プロパティは次の値になります。

LDAP://virtual.testdom.com/CN=SalesUser,OU=営業部,OU=本社,DC=virtual,DC=testdom,DC=com

この値から「本社/営業部」という値を返します。

とうもろこし


[WindowsPhone8]ELM327でOBD2ポートから車載ECUから情報取得

$
0
0

最近の車にはECUが積まれています。このECUを使って車の診断をするためのポートがOBD2ポートになります。

このOBD2ポートからのインターフェース規格がELM327というもので、Bluetoothでデータを飛ばしてくれる格安のユニットが多数存在します。

WP_20130706_005

ただし、どれもが付属のソフト自体はかなり怪しく、ほぼ100%の確率でなんらかのウィルスに完成しています。

私が購入したものも、しっかりInfostealer.Gampassが検出されました。

付属CDはそのままゴミ箱へというのが正しい処置のようです。

 

私の車の場合、ヒューズボックスの中にODB2ポートがありました。

WP_20130707_002

 

カチッとはめれば、もうそれだけでずーっとBluetoothの電波を出しっぱなしです。

WP_20130707_003

もちろんエンジン切っていてもバッテリ駆動で電波出し続けますので、これ、間にケーブルはさんでスイッチつけないと困ったことになりそうですね(消費電力は微弱だとはいえ)。

 

今回、@_ganchiさんのご好意でELM327対応のWindows Phone 8アプリをテストさせていただきました。

 

ECUによってサポートされている範囲がいろいろあるようですが、自車ではこんな感じのサポートのようです。

wp_ss_20130707_0002

Fuel system statusがとれるってことはガス残量が計測できるので燃費計算ができそうですね。

0x05~0x07あたりの温度関連の情報取得とかも便利層です。

エンジン回転数、車速とかもとれるようですね。

 

ただし、診断情報の取得はできませんでした。これできればディーラーに持ち込まなくても診断できたのに、残念。

wp_ss_20130707_0003

 

まずはスピードメーター表示(※同乗者に操作してもらいました)

wp_ss_20130707_0004

 

タコメーター表示

wp_ss_20130707_0005

 

燃費表示

wp_ss_20130707_0006

 

いろいろ取れて面白いですね。

置き場の関係で横置きができると更にうれしかったですね。

で、ついでに横置きの時は、スピードメーターとタコメーター、もしくは、タコメーターとエコ情報の2表示とかできたらと。

 

なんとか時間の都合をつけてロングドライブでも使ってみたいと思います。

 

--

追記:

設定で横向き設定(もちろんオートも)ありました。

横向きかっこいーっ!

 

ステップラー縫合

ドメインユーザ用のクラス

$
0
0

ドメインユーザを表すクラス DomainUser クラスは DomainObject クラスを継承し IUser インターフェイスを実装します。

Webアプリでデータソースとして使えるようにします。

C#のコードはのちほど。まずは VBのコード。

 

<DataObject()>

Public NotInheritable ClassDomainUser

  InheritsDomainObject

  ImplementsIUser

 

#Region" プライベートフィールド "

  Private ReadOnly _name As String

#End Region

 

#Region" フレンドコンストラクタ "

  'DirectoryEntry を指定して DomainUser クラスの新しいインスタンスを初期化します。

  Friend Sub New(entry AsDirectoryEntry)

    MyBase.New(entry)

    _name = entry.Properties.Item("sAMAccountName").Value.ToString()

  End Sub

#End Region

 

#Region" パブリックプロパティ "

  '電子メールを取得または設定します。

  Public Property EmailAddress As String

    Get

      If MyBase.IsDisposed Then

        Throw NewObjectDisposedException(Me.GetType().Name)

      End If

      Return DirectCast(MyBase.Entry.Properties.Item("mail").Value, String)

    End Get

    Set(value As String)

      If MyBase.IsDisposed Then

        Throw NewObjectDisposedException(Me.GetType().Name)

      End If

      MyBase.Entry.Properties.Item("mail").Value = value

    End Set

  End Property

'***** 長くなるので以降プロパティの Dispse チェックを省略(DisplayName と Name プロパティは元々なし)

  'アカウントが無効かどうかを取得または設定します。

  Public Property Disabled As Boolean ImplementsIUser.Disabled

    Get

      Return DirectCast(MyBase.Entry.NativeObject, IADsUser).AccountDisabled

    End Get

    Set(value As Boolean)

      DirectCast(MyBase.Entry.NativeObject, IADsUser).AccountDisabled = value

    End Set

  End Property

 

  '表示用の名前を取得します。

  Public ReadOnly Property DisplayName As String

    Get

      Return MyBase.Name

    End Get

  End Property

 

  '名を取得または設定します。

  Public Property FirstName As String

    Get

      Return DirectCast(MyBase.Entry.Properties.Item("givenName").Value, String)

    End Get

    Set(value As String)

      MyBase.Entry.Properties.Item("givenName").Value = value

    End Set

  End Property

 

  '表示名を取得または設定します。

  Public Property FullName As String Implements IUser.FullName

    Get

      Return DirectCast(MyBase.Entry.Properties.Item("displayName").Value, String)

    End Get

    Set(value As String)

      MyBase.Entry.Properties.Item("displayName").Value = value

    End Set

  End Property

 

  '姓を取得または設定します。

  Public Property LastName As String

    Get

      Return DirectCast(MyBase.Entry.Properties.Item("sn").Value, String)

    End Get

    Set(value As String)

      MyBase.Entry.Properties.Item("sn").Value = value

    End Set

  End Property

 

  'アカウント名を取得します。

  Public Overrides ReadOnly Property Name As String

    Get

      Return _name

    End Get

  End Property

 

  'Entry の ADSI User オブジェクトを取得します。

  Public ReadOnly Property Native AsIADsUserImplementsIUser.Native

    Get

      Return DirectCast(MyBase.Entry.NativeObject, IADsUser)

    End Get

  End Property

 

  '事業所を取得または設定します。

  Public Property OfficeName As String

    Get

      Return DirectCast(MyBase.Entry.Properties.Item("physicalDeliveryOfficeName").Value, String)

    End Get

    Set(value As String)

      MyBase.Entry.Properties.Item("physicalDeliveryOfficeName").Value = value

    End Set

  End Property

 

  'プライマリグループ ID を取得します。

  Public ReadOnly Property PrimaryGroupId As Integer

    Get

      Return Convert.ToInt32(MyBase.Entry.Properties.Item("primaryGroupID").Value)

    End Get

  End Property

 

  '電話番号を取得または設定します。

  Public Property TelephoneNumber As String

    Get

      Return DirectCast(MyBase.Entry.Properties.Item("telephoneNumber").Value, String)

    End Get

    Set(value As String)

      MyBase.Entry.Properties.Item("telephoneNumber").Value = value

    End Set

  End Property

#End Region

 

#Region" パブリックメソッド "

  '指定したアカウント名のユーザを検索します。このメソッドはデータバインド用です。

  <DataObjectMethod(DataObjectMethodType.Select)>

  Public Shared Function FindByName(name As String) AsDomainUser

    Return DirectCast(DirectoryAccess.FindDirectoryObject(name, CategoryType.User), DomainUser)

  End Function

 

  '指定したユーザの所属するグループを取得します。このメソッドはデータバインド用です。

  <DataObjectMethod(DataObjectMethodType.Select)>

  Public Shared Function GetBelongGroups(userName As String) AsReadOnlyCollection(Of String)

    Dim user = FindByName(userName)    'ユーザを検索

    Return DirectoryAccess.GetBelongGroups(user)    'ユーザの所属するグループを取得

  End Function

 

  'ユーザの一覧を取得します。このメソッドはデータバインド用です。

  <DataObjectMethod(DataObjectMethodType.Select)>

  Public Shared Function GetUsers() AsIList(OfDomainUser)

    Return DirectoryAccess.GetUsers(OfDomainUser)()    'ユーザを取得

  End Function

 

  '表示用の名前を返します。

  Public Overrides Function ToString() As String

    Return Me.DisplayName

  End Function

#End Region

 

継承元の Name プロパティは表示用の名前を取得するよう name 属性の値を返すようになってるんですが、ユーザの場合はアカウント名を返したいので、sAMAccountName 属性の値を返すようオーバーライドしてます。

表示用の名前は別途 DisplayName プロパティ(Webアプリ用)と オーバーライドした ToString メソッド(Windowsアプリ用)で取得します。

ユーザアカウントを作る時ににちゃんと氏名を指定していれば、それが表示される方が見た目に判りやすいと思います。

 

データバインド用のメソッドは Webアプリで必要になります。内部で DirectoryAccess クラスのメソッドを呼んでますが、これについては別途書きます。

ドメインユーザ用のクラス(C#)

$
0
0

ドメインユーザを表すクラス DomainUser クラスのC#のコードです。

 

[DataObject()]

public sealed classDomainUser : DomainObject, IUser

  #regionプライベートフィールド

  private readonly string _name;

  #endregion

 

  #regionインターナルコンストラクタ

  'DirectoryEntry を指定して DomainUser クラスの新しいインスタンスを初期化します。

  internal DomainUser(DirectoryEntry entry)

    : base(entry)

  {

    _name = entry.Properties["sAMAccountName"].Value.ToString();

  }

  #endregion

 

  #regionパブリックプロパティ

  '電子メールを取得または設定します。

  public string EmailAddress

  {

    get

    {

      if (base.IsDisposed)

      {

        throw newObjectDisposedException(this.GetType().Name);

      }

      return (string)base.Entry.Properties["mail"].Value;

    }

    set

    {

      if (base.IsDisposed)

      {

        throw newObjectDisposedException(this.GetType().Name);

      }

      base.Entry.Properties["mail"].Value = value;

    }

  }

//***** 長くなるので以降プロパティの Dispse チェックを省略(DisplayName と Name プロパティは元々なし)

  'アカウントが無効かどうかを取得または設定します。

  public bool Disabled

  {

    get

    {

      return ((IADsUser)base.Entry.NativeObject).AccountDisabled;

    }

    set

    {

      ((IADsUser)base.Entry.NativeObject).AccountDisabled = value;

    }

  }

 

  '表示用の名前を取得します。

  public string DisplayName

  {

    get

    {

      returnbase.Name;

    }

  }

 

  '名を取得または設定します。

  public string FirstName

  {

    get

    {

      return (string)base.Entry.Properties["givenName"].Value;

    }

    set

    {

      base.Entry.Properties["givenName"].Value = value;

    }

  }

 

  '表示名を取得または設定します。

  public string FullName

  {

    get

    {

      return (string)base.Entry.Properties["displayName"].Value;

    }

    set

    {

      base.Entry.Properties["displayName"].Value = value;

    }

  }

 

  '姓を取得または設定します。

  public string LastName

  {

    get

    {

      return (string)base.Entry.Properties["sn"].Value;

    }

    set

    {

      base.Entry.Properties["sn"].Value = value;

    }

  }

 

  'アカウント名を取得します。

  public override string Name

  {

    get

    {

      return _name;

    }

  }

 

  'Entry の ADSI User オブジェクトを取得します。

  public IADsUser Native

  {

    get

    {

      return (IADsUser)base.Entry.NativeObject;

    }

  }

 

  '事業所を取得または設定します。

  public string OfficeName

  {

    get

    {

      return (string)base.Entry.Properties["physicalDeliveryOfficeName"].Value;

    }

    set

    {

      base.Entry.Properties["physicalDeliveryOfficeName"].Value = value;

    }

  }

 

  'プライマリグループ ID を取得します。

  public int PrimaryGroupId

  {

    get

    {

      returnConvert.ToInt32(base.Entry.Properties["primaryGroupID"].Value);

    }

  }

 

  '電話番号を取得または設定します。

  public string TelephoneNumber

  {

    get

    {

      return (string)base.Entry.Properties["telephoneNumber"].Value;

    }

    set

    {

      base.Entry.Properties["telephoneNumber"].Value = value;

    }

  }

  #endregion

 

  #regionパブリックメソッド

  '指定したアカウント名のユーザを検索します。このメソッドはデータバインド用です。

  [DataObjectMethod(DataObjectMethodType.Select)]

  public staticDomainUser FindByName(string name)

  {

    return (DomainUser)DirectoryAccess.FindDirectoryObject(name, CategoryType.User);

  }

 

  '指定したユーザの所属するグループを取得します。このメソッドはデータバインド用です。

  [DataObjectMethod(DataObjectMethodType.Select)]

  public staticReadOnlyCollection<string> GetBelongGroups(string userName)

  {

    var user = FindByName(userName);    //ユーザを検索

    returnDirectoryAccess.GetBelongGroups(user);    //ユーザの所属するグループを取得

  }

 

  'ユーザの一覧を取得します。このメソッドはデータバインド用です。

  [DataObjectMethod(DataObjectMethodType.Select)]

  public staticIList<DomainUser> GetUsers()

  {

    returnDirectoryAccess.GetUsers<DomainUser>();    //ユーザを取得

  }

 

  '表示用の名前を返します。

  public override string ToString()

  {

    return this.DisplayName;

  }

  #endregion

}

 

少しですが説明はVBのコードの方に書いてます。

らくだ

DirectoryAccessクラスに追加したメソッド

$
0
0

ドメインユーザを表す DomainUser クラスのデータバインド用のメソッドから呼び出してる DirectoryAccess クラスのメソッドです。

先にVBのコードを。

※引数チェックは省略します。

 

'指定した名前と種類の Directory オブジェクトを検索します。

Public Shared Function FindDirectoryObject(name As String, objectCategory AsCategoryType) AsDirectoryObject

  Using root = GetRootEntry()    'ルートのDirectoryEntryを取得    参考:ユーザやグループの検索

    If CanConnectDomain Then    'ドメインに接続できる時

      Dim filter As String

      Select Case objectCategory

        CaseCategoryType.User

          filter = String.Format("(&(objectCategory={0})(sAMAccountName={1}))", objectCategory, name)

        CaseCategoryType.PrintQueue

          filter = String.Format("(&(objectCategory={0})(printerName={1}))", objectCategory, name)

        Case Else

          filter = String.Format("(&(objectCategory={0})(name={1}))", objectCategory, name)

      End Select

      Using searcher As NewDirectorySearcher(root, filter)

        Dim result = searcher.FindOne()

        Return If(result Is Nothing, Nothing, CreateInstance(result.GetDirectoryEntry()))

      End Using

    Else    'ドメインに接続できない時  <-- こっちはローカル

      Return CreateInstance(root.Children.Find(name, objectCategory.ToString()))

    End If

  End Using

End Function

 

'ユーザの所属するグループを取得します。

Public Shared Function GetBelongGroups(user AsIUser) AsReadOnlyCollection(Of String)

  Dim groups As NewList(Of String)()

  For Each group AsIADsIn user.Native.Groups()    '所属するグループ数分(プライマリ グループ以外)

    groups.Add(PathToCn(group.Name))    'PathToCn メソッドは、LDAPパスから名前の部分を切り出します。

  Next

  If TypeOf user IsDomainUserThen    'ドメインユーザの時

    Dim primaryGroupId = DirectCast(user, DomainUser).PrimaryGroupId

    groups.Add(GroupTokens.Item(primaryGroupId))

    'GroupTokens プロパティ:IDictionary(Of Integer, String) は下のLoadGroupTokens メソッド内で要素がセットされます。

  End If

  groups.Sort()

  Return groups.AsReadOnly()

End Function

 

'ユーザを取得します。

Public Shared Function GetUsers(Of T As {DirectoryObject, IUser})() AsIList(OfT)

  Dim users As NewList(OfT)()

  Using root = GetRootEntry() 'ルートのDirectoryEntryを取得

    If CanConnectDomain Then    'ドメインに接続できる時

      Dim filter = String.Format("(objectCategory={0})", CategoryType.User)

      Using searcher As NewDirectorySearcher(root, filter)

        Using results = searcher.FindAll()

          For Each res AsSearchResultIn results

            users.Add(DirectCast(CreateInstance(res.GetDirectoryEntry()), T))

          Next

        End Using

        If GroupTokens.Count = 0Then    'ドメイングループのPrimaryGroupTokenと名前の組のリストがない時

          LoadGroupTokens(searcher)    'ドメイングループのPrimaryGroupTokenと名前の組のリストをロード

        End If

      End Using

    Else    'ドメインに接続できない時  <-- こっちはローカル

      root.Children.SchemaFilter.Add(CategoryType.User.ToString())

      For Each entry AsDirectoryEntryIn root.Children

        users.Add(DirectCast(CreateInstance(entry), T))

      Next

    End If

  End Using

  Return users

End Function

 

'DirectoryObject のインスタンスを作成します。

Private Shared Function CreateInstance(entry AsDirectoryEntry) AsDirectoryObject

  Dim category AsCategoryType

  If[Enum].TryParse(OfCategoryType)(entry.SchemaClassName, True, category) = False Then

    Throw NewArgumentException("entry の種類が CategoryType に該当しません。", "entry")

  End If

  Select Case category

    CaseCategoryType.User

      If CanConnectDomain Then    'ドメインに接続できる時

        Return NewDomainUser(entry)

      Else    'ドメインに接続できない時

        Return NewLocalUser(entry)

      End If

    Case Else

      Throw NewNotImplementedException()

  End Select

End Function

 

'ドメイングループの PrimaryGroupToken と名前の組のリストをロードします。

Private Shared Sub LoadGroupTokens(searcher AsDirectorySearcher)

  searcher.Filter = String.Format("(objectCategory={0})", CategoryType.Group)

  Using results = searcher.FindAll()

    For Each res AsSearchResultIn results

      Dim entry = res.GetDirectoryEntry()

      entry.Invoke("GetInfoEx", New Object() {"primaryGroupToken"}, 0)

      Dim token = Convert.ToInt32(entry.Properties.Item("primaryGroupToken").Value)

      GroupTokens.Add(token, PathToCn(entry.Name))

    Next

  End Using

End Sub

 

ユーザの所属するグループの取得に関する説明は別途書きます。

[WindowsPhone8]Windows Phone 8にアップデートがくる?

$
0
0

http://blogs.windows.com/windows_phone/b/windowsphone/archive/2013/07/10/making-windows-phone-an-even-better-choice-for-business.aspx

2014年の第一四半期にenterprise feature pack が提供されるようです。

  • S/MIMEサポート
  • オートトリガーVPN(アプリから自動起動されるVPN)
  • EAP-TLSサポートのWiFi
  • モバイルデバイス管理の拡張
  • 証明書の管理

そのほかにもサポートライフサイクルが18ヶ月から36ヶ月になったので3年間はセキュリティアップデートがくるようです。


特定のページだけ、Spamが来る

ユーザの所属するグループの取得

$
0
0

DirectoryAccess クラスに追加したメソッドの1つ、ユーザの所属するグループを取得する GetBelongGroups メソッドの実装ですが、ユーザの所属するグループは ADSI の IADsUser.Groups メソッドで取得してます。

このメソッドの戻り値の型は IADsMembers で、IEnumerable を継承してるので For Each で回せます。

要素の型は基本インターフェイスである IADs を指定してます。

ドメインユーザの場合はこれでプライマリグループ(通常は Domain Users グループ)以外の所属するグループを取得できるんですが、プライマリグループが取得できないんです。

なので次のようにして、プライマリグループを取得してます。

 

1. グループの primaryGroupToken/グループ名 の組を GroupTokens プロパティ(キーは primaryGroupToken)に保持

2. ドメインユーザは primaryGroupID 属性を持っている(DomainUser.PrimaryGroupId プロパティに保持)

3. ドメインユーザの primaryGroupID = グループの primaryGroupToken なら、そのグループがユーザのプライマリグループなので、primaryGroupID をキーに GroupTokens プロパティからグループ名を取得

 

但し、グループの DirectoryEntry.Properties(PropertyCollection)内には primaryGroupToken プロパティがないので、LoadGroupTokens メソッド内で DirectoryEntry.Invoke メソッドを使ってプロパティを取得してます。

entry.Invoke("GetInfoEx", New Object() {"primaryGroupToken"}, 0)

これは IADs.GetInfoEx(New Object() {"primaryGroupToken"}, 0) を呼び出したのと同じです。

つまり、次のように書くこともできます。

DirectCast(entry.NativeObject, IADs).GetInfoEx(New Object() {"primaryGroupToken"}, 0)

 

第1引数(vProperties パラメータ)は Object 型なんですが、取得したい属性の名前を Object 型の配列にして渡す必要があります。

第2引数(lnReserved パラメータ)は 0 を指定してますが理由は判らないのでお約束ということでお願いしますw

インターネットテレビ御難

[Windows8]誤解を恐れずいえば、Windowsストアアプリの技術者は2つに大別され交流がない

$
0
0

結論から言おう。

Windowsストアアプリが組めるデザイン思考の人ともっと交流したいし、「アプリソムリエとつくる~」は素晴らしいバランスだったということだ。

 

現状、勉強会、セミナー、カンファレンスという名称でWindowsストアアプリと取り上げたオフラインイベントが、公式・非公式にかかわらず頻繁に開催されています。

マイクロソフト主催のは、マイクロソフトの決算年度が変わって組織替えもあった直後だからか、今月は少ないように見えるがまた増えてくるでしょう。

 

Windows8のベータの頃から先月(2013/06)まででそれらに参加したり募集ページをみて参加しなかったりして感じた率直な感想を述べたいとおもいます。

 

それは、タイトルにもあるように「Windowsストアアプリの技術者は2つに大別され交流がない」という点です。

 

2つとは、XAML+VB.NET/C#系、そして、HTML+JS系です。

これは言語の違いというよりもそれまでの開発対象の違いに起因しているように想像しています。

 

私は前者に属していますが、傾向としては次のような傾向にあると思います。

  • ストアアプリ以前からMS系プラットフォームでの開発やコミュ活動をしていた
  • XAML層よりもVB.NET/C#層に造詣が深い人が多い
  • 業務開発をしていたり、企業内開発者も多い
  • コードで語るスピーカーさんが多い

一方、後者は自分が属していないのですが、次のような印象をもっています

  • Web開発していて、WindowsストアアプリからMS系開発環境を使い始める
  • HTML5なデザインとかにも造詣が深い
  • 少人数の会社さんや個人開発者さんの比率が多い

そして、観測範囲が偏っているのかもしれませんが、後者のイベントだとコードで語る方よりも自社製品や市場分析、成功事例紹介というイベントプログラムが多いという印象があります。

 

自社製品や市場分析、成功事例紹介とか1つくらいは時々あってもいいけど、その時間使ってコード書いていた方がいいやーとか思ってしまいます。

どうしてなんでしょうね、この偏りって。

 

「アプリソムリエとつくる~」に参加してみて思ったのは、HTML+JS系の人のつくるストアアプリのデザインとか本当に洗練されているんですよね。

お話を聞くと過去につくっていましたとか、HTML5とかJSの公開されているテンプレを組み合わせてとか学んでとか、要は下地が違う訳なんです。

すごいなーと思う判明、「うーん、XAMLだとそういったサンプル少ないなー、みつばさんのところくらいか?」などとちょっと寂しい気持ちにもなってしまいます。

 

一方、GPS以外のいわゆるデバイスセンサー周りの活用というと、XAML+VB.NET/C#系の方が充実しています。

私自身にも言えることですね。なのでXAMLの知識をもっとつけたいし、その入り口としてHTML+JSのコードとかにも興味があったりします(自分で組むのはちょっと…なのすが)。

 

とはいえ、私の観測範囲のHTML+JS系なイベントでは、そういったコードで語る系のイベントプログラムのところが見つかってこないが残念でなりません。

もっと、この2つの技術者がコード主体で交流できれば、そこで何か化学反応が起こるんじゃないかと思います。

まあ、その足かせが前述した通り、マネタイズやなんか技術以外のものが多くて…観測範囲広げたいですね。

DirectoryAccessクラスに追加したメソッド(C#)

$
0
0

ドメインユーザを表す DomainUser クラスのデータバインド用のメソッドから呼び出してる DirectoryAccess クラスのメソッドのC#のコードです。

※引数チェックは省略します。

 

//指定した名前と種類の Directory オブジェクトを検索します。

public staticDirectoryObject FindDirectoryObject(string name, CategoryType objectCategory)

{

  using (var root = GetRootEntry())    //ルートのDirectoryEntryを取得    参考:ユーザやグループの検索

  {

    if (CanConnectDomain)    //ドメインに接続できる時

    {

      string filter;

      switch (objectCategory)

      {

        caseCategoryType.User:

          filter = String.Format("(&(objectCategory={0})(sAMAccountName={1}))", objectCategory, name);

          break;

        caseCategoryType.PrintQueue:

          filter = String.Format("(&(objectCategory={0})(printerName={1}))", objectCategory, name);

          break;

        default:

          filter = String.Format("(&(objectCategory={0})(name={1}))", objectCategory, name);

          break;

      }

      using (var searcher = newDirectorySearcher(root, filter))

      {

        var result = searcher.FindOne();

        return (result == null) ? null : CreateInstance(result.GetDirectoryEntry());

      }

    }

    else    //ドメインに接続できない時  <-- こっちはローカル

    {

      return CreateInstance(root.Children.Find(name, objectCategory.ToString()));

    }

  }

}

 

//ユーザの所属するグループを取得します。

public staticReadOnlyCollection<string> GetBelongGroups(IUser user)

{

  var groups = newList<string>();

  foreach (IADs group in user.Native.Groups())    //所属するグループ数分(プライマリ グループ以外)

  {

    groups.Add(PathToCn(group.Name));    //PathToCn メソッドは、LDAPパスから名前の部分を切り出します。

  }

  if (user isDomainUser)    //ドメインユーザの時

  {

    var primaryGroupId = ((DomainUser)user).PrimaryGroupId;

    groups.Add(GroupTokens[primaryGroupId]);

    //GroupTokens プロパティ:IDictionary<int, string> は下のLoadGroupTokens メソッド内で要素がセットされます。

  }

  groups.Sort();

  return groups.AsReadOnly();

}

 

//ユーザを取得します。

public static IList<T> GetUsers<T>() where T : DirectoryObject, IUser

{

  var users = newList<T>();

  using (var root = GetRootEntry())    //ルートのDirectoryEntryを取得

  {

    if (CanConnectDomain)    //ドメインに接続できる時

    {

      var filter = String.Format("(objectCategory={0})", CategoryType.User);

      using (var searcher = newDirectorySearcher(root, filter))

      {

        using (var results = searcher.FindAll())

        {

          foreach (SearchResult res in results)

          {

            users.Add((T)CreateInstance(res.GetDirectoryEntry()));

          }

        }

        if (GroupTokens.Count == 0)    //ドメイングループのPrimaryGroupTokenと名前の組のリストがない時

        {

          LoadGroupTokens(searcher);    //ドメイングループのPrimaryGroupTokenと名前の組のリストをロード

        }

      }

    }

    else    //ドメインに接続できない時  <-- こっちはローカル

    {

      root.Children.SchemaFilter.Add(CategoryType.User.ToString());

      foreach (DirectoryEntry entry in root.Children)

      {

        users.Add((T)CreateInstance(entry));

      }

    }

  }

  return users;

}

 

//DirectoryObject のインスタンスを作成します。

private staticDirectoryObject CreateInstance(DirectoryEntry entry)

{

  CategoryType category;

  if (Enum.TryParse<CategoryType>(entry.SchemaClassName, true, out category) == false)

  {

    throw newArgumentException("entry の種類が CategoryType に該当しません。", "entry");

  }

  switch (category)

  {

    caseCategoryType.User:

      if (CanConnectDomain)    //ドメインに接続できる時

      {

        return newDomainUser(entry);

      }

      else    //ドメインに接続できない時

      {

        return newLocalUser(entry);

      }

    default:

      throw newNotImplementedException();

  }

}

 

//ドメイングループの PrimaryGroupToken と名前の組のリストをロードします。

private static void LoadGroupTokens(DirectorySearcher searcher)

{

  searcher.Filter = String.Format("(objectCategory={0})", CategoryType.Group);

  using (var results = searcher.FindAll())

  {

    foreach (SearchResult res in results)

    {

      var entry = res.GetDirectoryEntry();

      entry.Invoke("GetInfoEx", new object[] { "primaryGroupToken" }, 0);

      var token = Convert.ToInt32(entry.Properties["primaryGroupToken"].Value);

      GroupTokens.Add(token, PathToCn(entry.Name));

    }

  }

}

 

所属するグループの取得については「ユーザの所属するグループの取得」を参照してください。

インスタンスの作成で、ドメインに接続できない時(対象がローカルの時)は LocalUser を作成してますが、このクラスについては次回書きます。

Viewing all 994 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>