Hatena::Groupmediaforcelabs

御手洗の開発記録

2010-06-17

Silverlight対応WCFサービスの同期実行について

| 18:27

こんにちは。

Silverlightからバッグ側の処理を実行するために、Silverlight対応WCFサービスを利用しています。

何も考えず実装すると非同期実行になってしまいますが、どうしても同期実行がしたいのでいろいろ調べてみました。

結論から言うと、Silverlight対応WCFサービスでは同期実行が出来ませんでした。

自分は以下の2つの方法を試してみました。


1.AutoResetEventを使って非同期実行の終了を待つ方法

AutoResetEventやManualResetEventを利用して別スレッドで起動している(はず)終了処理の完了を待機する方法を試してみました。

ソースは以下の通りです。

namespace SilverlightWCFTest
{
    public partial class MainPage : UserControl
    {
        private AutoResetEvent are = new AutoResetEvent(false);

        public MainPage()
        {
            InitializeComponent();

            // サービスを非同期で実行する
            var proxy = new EchoServiceClient();
            proxy.GetMessageCompleted += new EventHandler<GetMessageCompletedEventArgs>(proxy_GetMessageCompleted);
            proxy.GetMessageAsync();

            // proxy_GetMessageCompletedメソッド終了まで待機する
            are.WaitOne();
        }

        void proxy_GetMessageCompleted(object sender, GetMessageCompletedEventArgs e)
        {
            // 画面に結果内容を反映する
            MainPageContext context = (MainPageContext)Resources["Context"];
            context.Message = e.Result;

            // 完了を通知する
            are.Set();
        }
    }
}

この方法でやると、なぜかproxy_GetMessageCompletedが実行されません。

スレッドIDを確認すると、別のスレッドになっているようですが、なぜかWaitOne()で止まったままになってしまいます。


2.チャネルモデルでWCFを実行する方法

IAsyncResultにAsyncWaitHandleというプロパティがあったので、そのオブジェクトを使用して同期する方法を試してみました。

ソースは以下の通りです。

namespace SilverlightWCFTest
{
    public partial class MainPage : UserControl
    {
        SynchronizationContext uiThread;

        public MainPage()
        {
            InitializeComponent();

            uiThread = SynchronizationContext.Current;

            // チャネルモデルのサービスを生成して実行する
            var channel = new EchoServiceClient().ChannelFactory.CreateChannel();
            IAsyncResult asyncResult = channel.BeginGetMessage(GetMessageCallback, channel);

            // サービス完了まで待機する
            ((ManualResetEvent)asyncResult.AsyncWaitHandle).WaitOne();
        }

        private void GetMessageCallback(IAsyncResult asyncResult)
        {
            string message = ((EchoService)asyncResult.AsyncState).EndGetMessage(asyncResult);

            // 画面を更新する
            uiThread.Post(UpdateUI, message);

            // 処理の終了を通知する
            ((ManualResetEvent)asyncResult.AsyncWaitHandle).Set();
        }

        private void UpdateUI(object obj)
        {
            MainPageContext context = (MainPageContext)Resources["Context"];
            context.Message = (string)obj;
        }
    }
}

このやり方でもGetMessageCallback()メソッドが実行されず、WaitOne()で止まったままになってしまいます。

その他にもいろいろ調査してみましたが、結局同期実行はできませんでした。

一番不可解なのは、1のやり方で同期実行ができなかったことです。

この方法はWCFというよりも.NET FrameworkAPIを使っているはずなのですが、別スレッドになっているはずの処理が実行されませんでした。

自分の勉強不足で根本的に何か間違えているのかもしれませんが、ちょっと納得がいかないです。

今後、時間のあるときに再度調査していきたいと思います。

それでは、また。

CristinitaCristinita2012/02/20 14:02Now that's stuble! Great to hear from you.

hmrnetspqzzhmrnetspqzz2012/02/21 18:19ysqMY3 <a href="http://pfdwclitxnhy.com/">pfdwclitxnhy</a>

wiqvtymmrwiqvtymmr2012/02/21 22:20DmQpO9 , [url=http://ozqvrzwpumgd.com/]ozqvrzwpumgd[/url], [link=http://pfanabyotmfu.com/]pfanabyotmfu[/link], http://enlbfxbqqkol.com/

yzehriftghyzehriftgh2012/02/29 02:25B00Kkc <a href="http://vsykehbryolr.com/">vsykehbryolr</a>

2010-06-10

System.Collections.Generic.SortedListがSilverlightアプリケーションプロジェクトから参照できません。

| 17:13

こんにちは。

あまりの衝撃で、ちょっとタイトルが長くなってしまいました。

Visual Studio 2010でSilverlightアプリケーションのプロジェクトを作成したのですが、System.Collections.Generic.SortedListが参照できません。

これは、プロジェクトが参照しているDLLに問題があるようです。

オブジェクトブラウザを見てみて気付いたのですが、Silverlightアプリケーションで参照しているプロジェクトは「Microsoft\Framework\Silverlight\v4.0\system.dll」を参照しています。

そのDLLでは以下のクラスしかありません。

f:id:mitarai2009:20100610165741p:image

SortedListが見当たりません。

で、先ほどの画像をよく見てみると、下の方に同じSystemパッケージが見えます。

f:id:mitarai2009:20100610165742p:image

こちらのほうには、SortedListがあります。

参照しているDLLは「Microsoft\Framework\.NETFramework\v4.0\System.dll」となっています。

「なるほど、参照しているDLLを変更すればいいのか」と思い、DLLを「Microsoft\Framework\.NETFramework\v4.0\System.dll」に変更してみましたが、エラーが発生します。

f:id:mitarai2009:20100610165743p:image

英語は読めないので勘を働かせてこの文字列を翻訳してみました。

おそらく、「SilverlightランタイムではSystem.dllは使用できません。」みたいなことを言ってるんでしょう。

うーーん。

で、どうしよう。←いまここ

なんか、こういうのを質問する窓口みたいなのはないんでしょうか?

まあ、あったとしても英文が書けないので質問できないんですけどねw

なにか、やり方があれば教えてください。

それでは、また。

LuisanaLuisana2012/03/23 05:23It's about time somneoe wrote about this.

armbfyharmbfyh2012/03/23 20:37NOBsrd , [url=http://xytdwgdiexdp.com/]xytdwgdiexdp[/url], [link=http://dxkcralfmkxl.com/]dxkcralfmkxl[/link], http://qauccajpabbv.com/

xtzwitxtzwit2012/03/25 07:257LY1xP <a href="http://lukresuiqkro.com/">lukresuiqkro</a>

sjfsdyrwhsjfsdyrwh2012/03/25 10:14DZbdH3 , [url=http://dwxfnbbcvune.com/]dwxfnbbcvune[/url], [link=http://smnsqhmplsxz.com/]smnsqhmplsxz[/link], http://aebcvuusdfii.com/

2010-05-27

Silverlightのフォーム認証について(Active Directoryとの認証)

| 15:15

今日は、Silverlightでフォーム認証を実装する方法をまとめておきます。

システム構成

まずは、システムの構成から説明します。

Silverlight自体はブラウザ上で動作するクライアントアプリケーションなので、利用者情報を取得する際は、サーバ側にリクエストしてデータを取得します。

その際、WCFを利用します。

WCFとは、Windows Communication Foundationの略で、簡単にいうとWebサービスです。

今回は利用者情報などの個人情報を扱うので認証が必要になります。

認証は当然、独自の実装をすることもできるのですが、今回はフォーム認証を利用することにしました。

WCFの認証サービスについては以下の通りです。

サービスを使用するには、認証サービスにユーザーの資格情報を渡します。認証サービスでは、ASP.NET メンバーシップを使用して渡された資格情報の妥当性を検査します。


既定では、認証サービスは、ユーザー名およびパスワードを既定のメンバーシップ プロバイダーに渡すことで、妥当性を検査します。ユーザーが認証されると、ASP.NET 認証サービスは、ASP.NET フォーム認証と互換性がある HTTP Cookie として認証チケットを発行します。


以降の要求では、毎回ユーザーが資格情報を入力しなくてもよいように、このチケットが Web アプリケーションに渡されます。

参考URLhttp://msdn.microsoft.com/ja-jp/library/bb386582.aspx

図に表すとこんな感じです。

f:id:mitarai2009:20100527151436p:image

このような処理が設定だけで実現することができます。

サーバ側の設定

今回は、

サーバ側の設定は以下の通りです。

基本は、MSDNを参考にして書いています。

参考URLhttp://msdn.microsoft.com/ja-jp/library/dd560704(v=VS.95).aspx

1.Silverlight対応のWCFサービスを作成します。

ソリューション エクスプローラーで、サービス プロジェクトを右クリックし、[追加]、[新しい項目] の順にクリックして、[Silverlight] カテゴリから [Silverlight 対応の WCF サービス] テンプレートを選択します。

その際、[名前] ボックスに「Authentication.svc」と入力し、[追加] ボタンをクリックします。(サービス名はなんでもいいと思うが、今回はマニュアルに従いました)

2.Authentication.svc.cs ファイルを削除します。
3.Authentication.svc の内容を次のコードに置き換えます。
<%@ ServiceHost Language="C#" Debug="true" Service="System.Web.ApplicationServices.AuthenticationService" %>
4.Web.ConfigファイルにauthenticationService要素を追加し、認証サービスを有効化します。

以下の設定をWeb.Configファイルに追加してください。(configuration要素の子要素として追加してください)

<system.web.extensions>
  <scripting>
    <webServices>
      <authenticationService enabled="true" requireSSL="false" />
    </webServices>
  </scripting>
</system.web.extensions>

まだ、開発途中なので、requireSSL属性がfalseになってますが、リリース時にはtrueに変更する必要があります。

5.Web.config ファイルに記述されているservice要素の設定を変更します。

この要素は、1の時点でVisualStudioが自動で追加してくれています。

そのため、service属性のname属性の値と、service属性の子要素であるendpoint要素のcontract属性の値をSystem.Web.ApplicationServices.AuthenticationServiceに変更してください。

<service name="System.Web.ApplicationServices.AuthenticationService">
  <endpoint address="" binding="customBinding"
    bindingConfiguration="WorkManagementApplication.Web.Services.Authentication.customBinding0"
    contract="System.Web.ApplicationServices.AuthenticationService" />
  <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
6.フォーム認証を有効化します。

system.web要素の子要素としてauthentication要素を追加します。

その際、mode属性はFormsとします。

また、authentication要素の子要素としてforms要素を追加します。

その際、cookielessを追加し、値はUseCookiesを設定してください。

<authentication mode="Forms">
  <forms cookieless="UseCookies" />
</authentication>
7.メンバシッププロバイタをWeb.Configに設定します。

System.Web.ApplicationServices.AuthenticationServiceは認証の際にメンバシッププロバイダを利用して認証や資格情報の作成を行っています。

今回は、Active Directoryを使用して認証を実施するため、メンバシッププロバイダにはSystem.Web.Security.ActiveDirectoryMembershipProviderを使用します。

まずは、Active Directoryの接続情報を追加します。

<connectionStrings>
  <add name="ADConnectionString" connectionString="LDAP://%サーバアドレス%/%BaseDN%" />
</connectionStrings>

connectionStrings要素はconfiguration要素の子要素として追加します。

%サーバアドレス%にはActive Directoryサーバ名(IPアドレス)を設定し、%BaseDN%には利用者情報が格納されているディレクトリのDNを設定してください。

次に、メンバシッププロバイダをWeb.Configに追加します。

<membership defaultProvider="MembershipADProvider">
  <providers>
    <add name="MembershipADProvider"
      type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
      connectionStringName="ADConnectionString"
      connectionUsername="%ユーザ名%"
      connectionPassword="%パスワード%"
      attributeMapUsername="sAMAccountName" />
  </providers>
</membership>

membership要素はsystem.web要素の子要素として追加します。

connectionStringNameには、addのname属性で指定した文字列を設定してください。

%ユーザ名%と%パスワード%はActive Directoryの管理者ユーザを設定してください。

そして、attributeMapUsername属性は必ず指定してください。

この属性は、ユーザ名がどの属性に設定されているかどうか指定する属性となります。

この属性を設定しないと認証に成功しません。

このおかげで、結構はまりました。

ホント、MSDNは一言足りないと思います!!!

ちなみに、値はsAMAccountNameを設定しておけばまず間違いありません。

8.サービスのアクセス権限を設定します。

制限をかけたいサービスのアクセス権限を設定します。

今回は、指定したディレクトリ配下のサービスに匿名ユーザからのアクセスを禁止する設定を行いました。

方法は以下の通りです。

サービスを専用のディレクトリ ("secure/MyService.svc" など) に置き、そのディレクトリの構成ファイル ("secure/Web.config" など) を以下のような設定にします。

<configuration>
    <system.web>
      <authorization>
        <deny users="?" />
      </authorization>
    </system.web>
</configuration>

ちなみに、この設定を行うと、Silverlightプロジェクト側でサービス参照の追加や更新ができなくなります。

そのため、開発時にはまずサービスを作成し、インタフェースが定まったらWeb.Configを追加するという流れにしないめんどうです。

一応、authorizationの設定をコメントアウトにして更新して、コメントを元に戻すというやり方でサービス参照の更新は可能ですが、めんどくさいですよね。

なんかいい方法があるのかもしれませんが、それほど優先順位は高くないのでこのまま突き進みます。

以上でサーバ側の設定が完了です。

クライアント側(Silverlight)の設定

クライアント側は特に特別なことをする必要はありません。

認証サービスと制限のかかったサービスをサービス参照に追加し、サービスの実行を実装します。

サンプルとしては以下の通りです。

private void loginButton_Click(object sender, RoutedEventArgs e)
{
    // ログイン情報を取得する
    LoginPageContext loginPageContext = (LoginPageContext) Resources["loginPageContext"];

    // 認証処理を行う
    var authProxy = new AuthenticationServiceClient();
    authProxy.LoginCompleted += new EventHandler<LoginCompletedEventArgs>(authProxy_LoginCompleted);
    authProxy.LoginAsync(loginPageContext.UserId, loginPageContext.UserPassword, null, false);
}

private void authProxy_LoginCompleted(object sender, LoginCompletedEventArgs e)
{
    if (e.Error != null) throw e.Error; // TODO とりあえずそのまま返却する

    // ログイン結果を判定する
    if (e.Result)
    {
        // ログイン成功
        var userProxy = new UserAccessServiceClient();
        userProxy.GetUserDataCompleted += new EventHandler<GetUserDataCompletedEventArgs>(userProxy_GetUserDataCompleted);
        userProxy.GetUserDataAsync();
    }
    else
    {
        // TODO ログイン失敗
        ChildWindow cw = new ChildWindow();
        cw.Width = 400;
        cw.Height = 100;
        cw.Content = "ログインに失敗しました。";
        cw.Show();
    }
}

private void userProxy_GetUserDataCompleted(object sender, GetUserDataCompletedEventArgs e)
{
    // ユーザ情報を取得する
    UserContainer userContainer = e.Result as UserContainer;

    // ユーザ情報を保持する
    LoginUserContext userContext = (LoginUserContext)Application.Current.Resources["loginUserContext"];
    userContext.LoginDate = DateTime.Now;
    userContext.UserName = userContainer.lastNamek__BackingField + userContainer.firstNamek__BackingField;
    userContext.userContainer = userContainer;

    // 基底画面へ遷移する
    NavigationService.Navigate(new Uri("/Base", UriKind.Relative));
}

めんどくさいので説明は省略します。

以上で、終了です。

ThomasThomas2012/02/18 04:01I went to tons of links bferoe this, what was I thinking?

rmoamyljzgdrmoamyljzgd2012/02/19 17:33CBLouT <a href="http://mcdwkixxzhoe.com/">mcdwkixxzhoe</a>

hwapkecbkjhwapkecbkj2012/02/19 23:49CDGcQo , [url=http://urwxdpeyhwgr.com/]urwxdpeyhwgr[/url], [link=http://senmheftsdll.com/]senmheftsdll[/link], http://bmcegdmdhnth.com/

2010-03-26

Row展開処理メモ 第二弾-DataGridの仮想モードに注意

| 17:53

お久しぶりです。

御手洗です。

ずいぶんブログをご無沙汰してしまいました。

現場作業が落ち着いてきたので、不定期ではありますがブログの更新を再開したいと思います。

今回は、ずっと書きたかったDataGridの仮想モードについてです。

DataGridの仮想モードに注意

DataGridやListBoxなどには、仮想モードという機能が実装されています。

仮想モードとは、非常に大きな表形式のデータをDataGridに表示する際に、表示に必要な行のみインスタンスを作成し、必要になったらそのインスタンスを再利用するという機能です。*1

この機能自体はいいと思うのですが、現在のSilverlight3ではこの仮想モードをオフにすることができないのです。

そのおかげで苦労してCellの展開をできるように実装したDataGrid(前回の記事を参照)に問題が発生してしまいました。

問題とは以下の通りです。

まず、何個かCellを展開しておきます。

f:id:mitarai2009:20100326175044p:image

そしてスクロールします。

すると、展開してないはずのCellもスクロールされてしまいます。

f:id:mitarai2009:20100326175038p:image

f:id:mitarai2009:20100326175034p:image

これには、かなりハマりました。

いろいろなところにVirtualizationMode.Standardを設定しても全然この設定が有効になりませんでした。(VirtualizationModeの設定方法が間違えていたのかもしれませんが・・・)

そもそも、VirtualizationMode.Standardはこの仮想モードを無効にする設定ではないのでしょうか。

結局、無効にする方法はわからなかった(ソースも見れないし・・・)のでDataGridのスクロールバーを無効にして、ScollViewerをかぶせることによってこの問題を回避しました。

f:id:mitarai2009:20100326175028p:image

f:id:mitarai2009:20100326175026p:image

f:id:mitarai2009:20100326175024p:image

たかだか30行ぐらいの表示で仮想モードなんて使わなくてもいいのに、無理やり使わされるのはどうなんでしょう。

簡単に無効にするオプションもないし、できればSilverlight4では簡単に無効にするオプションをつけてほしいですね。

ちなみに、Cellを取得してVirtualizingStackPanelのイベントからインスタンスを随時生成する、なんてやり方をすればもしかしたらこの問題を解決できるのかもしれません。

しかし、無効にしたいだけなのにそんなコードを書かないといけないのは面倒ですよね。

できれば、プロパティで指定するだけで簡単に有効/無効にしたいです。

まとめ

DataGridやListBoxなどのコントロールには仮想モードという機能があるというのを頭に置いて開発したほうがよさそうです。

全然まとめられてないですが、今日はこの辺で。

ulnmbvkcseulnmbvkcse2014/12/03 18:27rnksynfejbgpsdfmbct, <a href="http://www.qyfholrlen.com/">kgncggjfnt</a> , [url=http://www.jspmrjzsyk.com/]viurciywqy[/url], http://www.jijegsukbg.com/ kgncggjfnt

2009-12-10

Row展開処理メモ 第一弾-DataGridの編集モードに注意

| 11:30

こんにちは。

御手洗です。

DataGridのRowを展開する処理を実装しました。

DataGridのRowを展開というのは以下のような画面です。

まずは、通常の画面。

f:id:mitarai2009:20091210112107p:image

このDataGridの初めのCellをクリックするとRowが展開されて明細行が表示されます。

f:id:mitarai2009:20091210112104p:image

こんな感じの画面を作ってみました。

ちょっと画面イメージは変わってしまいますが、RowDetailsTemplateを使っても行詳細を表示することができるので、こんなめんどくさいことやりたくないと思う方はそちらを利用してください。

参考URLhttp://msdn.microsoft.com/ja-jp/library/cc903938(VS.95).aspx

今回は、この画面を実装中に困ったことシリーズ第一弾(全二回)をブログに残しておこうと思います。

この処理は、一番最初のCellをクリックした際に、関係のあるCellの高さを変更しています。

private void OpenMonthlyReportGrid(
    DataGrid dataGrid,
    DataGridPreparingCellForEditEventArgs e,
    MonthlyReportItem item)
{
    e.Row.Height = 30 + (item.WorkTimeDetailList.Count * 32);
    item.ExpandFlag = true;
    Grid dateGrid = (Grid)dataGrid.Columns[0].GetCellContent(e.Row);
    dateGrid.RowDefinitions[1].Height = GridLength.Auto;

    Grid projectGrid = (Grid)dataGrid.Columns[1].GetCellContent(e.Row);
    projectGrid.RowDefinitions[1].Height = GridLength.Auto;

    Grid workTimeGrid = (Grid)dataGrid.Columns[2].GetCellContent(e.Row);
    workTimeGrid.RowDefinitions[1].Height = GridLength.Auto;

    Grid subTotalGrid = (Grid)dataGrid.Columns[3].GetCellContent(e.Row);
    subTotalGrid.RowDefinitions[1].Height = GridLength.Auto;
}

GetCellContentを使用して基底パネル(Gridパネル)を取得し、高さを変更しています。

閉じるほうはこんな感じ。

private void CloseMonthlyReportGrid(
    DataGrid dataGrid,
    DataGridPreparingCellForEditEventArgs e,
    MonthlyReportItem item)
{
    e.Row.Height = 30;
    dataGrid.RowHeight = 30;
    item.ExpandFlag = false;
    Grid dateGrid = (Grid)dataGrid.Columns[0].GetCellContent(e.Row);
    dateGrid.RowDefinitions[1].Height = new GridLength(0);

    Grid projectGrid = (Grid)dataGrid.Columns[1].GetCellContent(e.Row);
    projectGrid.RowDefinitions[1].Height = new GridLength(0);

    Grid workTimeGrid = (Grid)dataGrid.Columns[2].GetCellContent(e.Row);
    workTimeGrid.RowDefinitions[1].Height = new GridLength(0);

    Grid subTotalGrid = (Grid)dataGrid.Columns[3].GetCellContent(e.Row);
    subTotalGrid.RowDefinitions[1].Height = new GridLength(0);
}

ここで注意していただきたいのは以下の箇所です。

    dataGrid.RowHeight = 30;

この処理がないと、一度展開したRowの高さがそのままになってしまい、展開していない場合の高さに戻りません。

この処理があるだけで全体のRowの高さを展開していない場合の高さに戻せます。

この処理を行っても、既に展開しているRowの高さが変わることはありません。

多分、Rowの高さはDataGridRowクラスのHeightを優先しているからだと思いますが、今後実装が変わった場合(Silverlight4になる。など)動きが変わってしまうかもしれないので注意する必要がありますね。

今回ブログにメモしておきたかったのはこのことではありません。

その内容は以下に記載します。

DataGridの編集モードに注意

はじめ、この画面を実装したときにCellをクリックして高さを変更しているにも関わらず内部のGridパネルが変更してくれませんでした。

こんな感じになっちゃいます。

f:id:mitarai2009:20091210112731p:image

初めのCellが展開される前と変わっていないのがわかると思います。

自分が最初にハマったときとちょっと動きは違うのですが、原因は同じです。

原因はCellの編集モードにありました。

Cellクリックすると編集モードになります。

Rowの高さを変える処理もその編集モードになる際のイベントで処理を行っています。(自分がハマったときはBeginningEditイベントに処理を追加していました。)

編集モードになると編集モード用のDataTemplateを使用するのですが、今回は指定していなかったので問題なく動作するはずだと思っていました。

しかし、指定していなくても同じインスタンスのDataTemplateを使用しているのではなく、同じDataTemplateで別のインスタンスを利用しているみたいなんです。

なので、編集モード用のDataTemplateインスタンスが適用され、高さ変更がうまくいきませんでした。

これを解決するためにとった方法は、Cellの編集モードをキャンセルする方法です。

ReadOnlyにしてしまうと編集イベントが取れないのでこの方法にしました。

実装したソースは以下の通りです。

private void monthlyReportGrid_PreparingCellForEdit(
    object sender,
    DataGridPreparingCellForEditEventArgs e)
{
    DataGrid dataGrid = (DataGrid)sender;
    if (e.Column.DisplayIndex == 0)
    {
        dataGrid.CancelEdit();
        ExpandMonthlyReportGrid(dataGrid, e);
    }
}

BeginningEditイベントではキャンセル処理がうまく動作しなかったので、PreparingCellForEditイベントになっています。

編集モードに移行したCellが0番目だった場合(初めのCell)、DataGridのCancelEdit()メソッドで編集モードをキャンセルしています。

この後に、Rowの高さを変更するとうまく動作してくれました。

まとめ

ソースを調べていないのではっきりしたことは言えませんが、編集モードのDataTemplateを指定していなくても編集モードに切り替わると別のインスタンスになるので注意してください。




ということで、次回に続きます。

LhilLhil2012/03/23 04:58I'm not easliy impressed but you've done it with that posting.

jwrbtxjwrbtx2012/03/23 15:55QvGCzg <a href="http://fdsqbxvuohwc.com/">fdsqbxvuohwc</a>

chwhzuptchwhzupt2012/03/23 18:52uGrs7H , [url=http://zvyjjesojtgp.com/]zvyjjesojtgp[/url], [link=http://xrjmffqaogkt.com/]xrjmffqaogkt[/link], http://ttedrtjlooto.com/