FTPでファイルのダウンロードと進行状況の表示

C#Windows上のフォームから、FTPでファイルをダウンロードする必要があったので、以下で実装しました。

 

前提条件として、

  • ダウンロード中の進行状況の表示
  • ユーザによる途中キャンセル

が、ありました。

 

以下、フォームに

が、貼ってあるものとします。

 

キャンセルボタンのEnable化などは省略

 

>|cs|

        //ダウンロード中のフラグ
  private bool onDownloading = false;
  //ダウンロード用のWebClient
        private WebClient wc = null;

        //ダウンロードボタンクリック
        private void downloadButton_Click(object sender, EventArgs e)
        {
            Uri uri = new Uri("ダウンロード先のパス ftp://example.com/example.txt");
            if (wc == null)
            {
                wc = new WebClient();               

               //webClientに進行状況、完了時のイベントハンドラを追加
                wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged);
                wc.DownloadFileCompleted += new AsyncCompletedEventHandler(wc_DownloadFileCompleted);
            }

   //ダウンロード開始
             wc.DownloadFileAsync(uri, "保存先のローカルパス")
             onDownloading = true;
        }

        //ダウンロードの進歩イベント
        private void wc_DownloadProgressChanged(Object sender, DownloadProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage;
        }

        //完了時のイベント
        private void wc_DownloadFileCompleted(Object sender, AsyncCompletedEventArgs e)
        {
            if ( (e.Error != null) && (!e.Cancelled) )
            {
                MessageBox.Show("ダウンロード中にエラーが発生しました。内容:" + e.Error.Message, "エラー",
                            MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else if (e.Cancelled)
            {
                MessageBox.Show("ダウンロードがキャンセルされました。", "完了", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                MessageBox.Show("データのダウンロードが完了しました。", "完了", MessageBoxButtons.OK, MessageBoxIcon.Information); 
            }
            onDownloading = false;
        }

 

        //キャンセルボタンクリック
        private void cancelButton_Click(object sender, EventArgs e)
        {
            //ダウンロード中なら、ダウンロードをキャンセルする
            if (onDownloading)
            {
                if (wc != null)
                {
                    wc.CancelAsync();
                }
            }
        }

ClickOnceで困った・・・

過去の遺物だと思っていた「dll hell」の再来か??????

 

ClickOnceって、内部でどのように動いているのかまで理解していない(もっとも、普通のmsiのセットアップだとて理解出来ていない)が、非常に便利なので、標準で使っている。

 

特に、修正を行ったとき、「サーバーに上げてお仕舞い!」の手軽さが最高だった。

 

しかし、本日は、困った。

 

いつものように、

 ・バージョン番号を確認して

 ・発行して

 ・サーバーに上げて

 ・テストマシンで、対象アプリを起動すると

 ・いつものように、アップデートのダイアログが出て

 ・OKをクリックすると

 

エラーになった・・・・・・・

 

 

詳細ボタンをクリックすると

エラーの詳細
    この操作中に次のエラーが検出されました。
    * [2014/03/13 10:59:11] System.ArgumentException
        - 値が有効な範囲にありません。
        - ソース:System.Deployment
        - スタック トレース:
            場所 System.Deployment.Internal.Isolation.IStore.LockApplicationPath(UInt32 Flags, IDefinitionAppId ApId, IntPtr& Cookie)
            場所 System.Deployment.Application.ComponentStore.LockApplicationPath(DefinitionAppId definitionAppId)
            場所 System.Deployment.Application.SubscriptionStore.LockApplicationPath(DefinitionAppId definitionAppId)
            場所 System.Deployment.Application.FileDownloader.PatchFiles(SubscriptionState subState)
            場所 System.Deployment.Application.FileDownloader.Download(SubscriptionState subState)

 

こんなスタックトレースでは、わからん。

 

ぐぐっても、

インストールされる

C:\Users\ユーザ名\AppData\Local\Apps\2.0

以下のフォルダを削除しろ!

 

とか、バカなことしか見つからない。

 

削除したら、他のClickOnceアプリとデータが吹っ飛ぶでしょうが?

 

 

Gitで、以前のアップデートリリース時点まで戻り

再度、ファイルを修正して発行しても同じ。

 

 

発行したセットアップファイルに問題があるのか?

インストールしたテストマシンに問題があるのか?

 

誰か、教えて下さい。

 

追記

 

エラーの出るマシンと出ないマシンとがある。

結局は、エラーの出るマシンは、アンインストール、再インストールで対応する。

 

しかし、データをClickOnceに任せていたら、一緒にアンインストールされるので、データは別なところへ置かないと、気軽にアンインストール出来なくなる。

 

 

 

PCにインストールされているブラウザをC#で自動操縦する

PCにインストールされているブラウザをC#で自動操縦する

 

まずは、Windowsなのだから、IEはインストールされているとして、

 ・Firefox

 ・Chrome

が、インストールされているかを確認する。

 

方法は、ぐぐった結果レジストリ

HKLM\Software\Microsoft\Windows\CurrentVersion\App Paths\

に、[firefox.exe]、[chrome.exe]があれば、インストールされていると判断する。

 

 

後は、Nugetで

 ・Selenium WebDriver

 ・Selenium WebDriver Support Classes

 ・Selenium.WebDriver.ChromeDriver

 ・Selenium.WebDriver.IEDriver

をインストールする。

 

早速

 IWebDriver driver = new ChromeDriver();

 driver.Url = "www.example.com";

 

と、やるとChromeが起動して、指定したURLにアクセスする。

 

だけど・・・・・

ログを吐くようなコマンドプロンプトのウィンドウも出てくる。

 

オブジェクトブラウザで見て、

 

IE

>|cs|

                    InternetExplorerOptions ieOptions = new InternetExplorerOptions();

                    InternetExplorerDriverService ieDriverService = InternetExplorerDriverService.CreateDefaultService();
                    ieDriverService.HideCommandPromptWindow = true;
                    driver = new InternetExplorerDriver(ieDriverService, ieOptions);

||<

 

Firefox

 だと出ない

 

Chrome

>|cs|

                    ChromeOptions chromeOptions = new ChromeOptions();

                    ChromeDriverService chromeService = ChromeDriverService.CreateDefaultService();
                    chromeService.HideCommandPromptWindow = true;
                    driver = new ChromeDriver(chromeService, chromeOptions);

||<

 

で、出ないようになった。

 

ブラウザに表示されるフォームに自由に値を設定したり、

ボタンをクリックしたり、javascriptの起動と何でも出来る。

 

これで遠隔操作でもされると、マズいね。

 

 

 

 

 

VS2013 と SQL Server Compact 4.0 SP1

[2015/09/29 追記]

 Windows10 + VS2015に対して

 

[2015/04/16 追記]

 最後に追記あり

 

Windows XPが、後1ヶ月のサポートになったので、環境を

  「Windows8.1 + IE11 + VS2013」

で、新しく構築。

 

早速、以前のプロジェクトを読み込んでみたが、いつものVSとは違い

アップグレートウィザードが走らずに普通に読み込めた。

 

自分は、SQL Server Compactが大のお気に入りで、

クライアントPCで完結するアプリで、DBが必要な場合には

SQL Server Compactで決まり。

 

 

以前のプロジェクトのDBテーブルの変更があったので、

VS2013を起動して、[ツール] - [データベースへの接続]を選択したが、

データソースに、SQL Server Compactがない・・・・・・。

 

 

こりゃ、別途インストールが必要なのかと思い、

MSのサイトから、SQL Server Compact 4.0 SP1をインストールした。

 

 

さて、DBに繋ごうと、[データベースへの接続]を選択したが

「じぇ、じぇ、じぇ・・・!」

 

相変わらず、データソースがない。

 

 

で、ぐぐってみると、

VS2013から、大好きなSQL Server Compactはサポートされないらしい。

MSDN Blog

http://blogs.msdn.com/b/sqlexpress/archive/2011/07/12/introducing-localdb-a-better-sql-express.aspx

 

ローカルでDB使いたければ、LocalDBを使えと、いうことらしい。

 

 

でも、ネット上では、LocalDBは重いとか、配布が大変だとかで

よい評判は聞かない。

(開発者がSQL Serverをターゲットの開発に使うのは良いらしい。)

 

 

MSに対して、引き続きVS2013でも、サポートするように嘆願しているらしい。

 

 

嘆いていても、はじまらないので、ぐぐってみました。

 

 

捨てる神あれば、拾う神ありで、CodePlexで公開されていました。


SQL Server Compact Toolbox
http://sqlcetoolbox.codeplex.com/documentation

 

これをインストールすると、今までのサーバーエクスプローラとは違うウィンドウが開くが、今までと同じように使える。

 

これで問題解決かと思いきや、

なんと、

 

なんと、

セットアップファイルを作成する段階で、

必須コンポーネントのところに、「必要なブートストラップが見つかりません。」と警告が出る。

 

構わず、セットアップファイルを作るとエラーにはならず作成される。

 

実行環境にインストールしてみると、案の定

ファイルまたはアセンブリ 'System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91'、またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。

と、エラーになる。

 

実行環境にユーザ自身で、MSのサイトからインストールしてもらうしかないのか????

 

で、調べた結果

VS2012 のマシンの

Program Files (x86)\Microsoft SDKs\Windows\v8.0A\Bootstrapper\Packages

にある

 

SQL Server Compact Edition 4.0

 

を、

 

VS2013のマシンの

\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\Bootstrapper\Packages

 

にコピーしてみた。

 

 

とりあえず、これで必須コンポーネントの警告は消えた。

ライセンス的に問題があるのか、現在調査中・・・・・

 

だれか知りませんか~

 

 

 追記:

警告は消えたけど、manifestには含まない・・・・・・orz

 

 

[2015/04/16 追記]

上記の件が、

Visual Studio 2013 で SQL Server Compact がサポートされない問題に対応 | SHIN-ICHI の技術ブログ

にありましたので、本日追試。

 

もう一度、

BootStrapを
Windows8 VS2012 → Windows 8.1 VS2013

へコピー。

 

ClickOnce発行時に、[発行]-[必須コンポーネント]で

SQL Server Compact 4.0 SP1

がチェックできて、警告がないのを確認。

 

普通に、ClickOnceの発行。

 

Windows7のクリーンなマシンを用意して、インストールURLへアクセスして、インストールボタンをクリック。

 

無事に、SQL Server Comact 4.0 SP1がインストールされました。

 

又、ライセンス的にも問題がないようです。

 

[2015/09/29 追記]

環境をWindows10 + VS2015をクリーンインストール

 

VS2013と同じようにBootStrapをコピーしようとしましたが、Windows8.1とはで入れクトリ構成が違うようで、

 

C:\Program Files (x86)\Microsoft Visual Studio 14.0\SDK\Bootstrapper\Packages

 

に、コピーすると、警告が消えました。

 

[2017/10/05 追記]

VS2017では、Bootstrapsのパスが変わっています。

 

C:\Program Files (x86)\Microsoft SDKs\ClickOnce Bootstrapper\Packages

 

にコピーすると警告が消えました。

 

 

 

 

 

 

log4netでformのtextBoxにlogを出力

log4net関連で、備忘録。

 

WindowsFormに貼り付けたTextBoxに、log4netを使ってLogを出力する。

 

パクリ元はStackOverflow

http://stackoverflow.com/questions/14114614/configuring-log4net-textboxappender-custom-appender-via-xml-file

 

何のため?

ユーザに今、何が起こっているのかを知らせる。

プログレスバーは、作業全体のどれくらいが終わっているかのみを表示するが、こちらは、今、何をしているかを表示できる。

 

 

log4netのconfig

>|xml

    <!-- 通常ログ:FormXXXのTextBox1に出力 -->
    <appender name="OutToTextBox1" type="appName.FormXXX,  appName">
      <formName value="FormXXX"/>
      <textBoxName value="TextBox1"/>
<!-- このあたりは同じ -->
      <filter type="log4net.Filter.LevelRangeFilter">
        <param name="LevelMax" value="Error" />
        <param name="LevelMin" value="Trace" />
      </filter>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="&gt;&gt; - %message" />
      </layout>
    </appender>
 <root>
    <level value="INFO"/>
    <appender-refref="OutToTextBox1"/>
</root>

||<
 
 
TextBox1へ書き出すClassを作成(log4netのAppenderSkeltonを継承)
>|cs
public class TextBoxAppender : AppenderSkeleton
{
private TextBox _textBox;
public TextBox AppenderTextBox
{
get
{
return _textBox;
}
set
{
_textBox = value;
}
}
public string FormName { get; set; }
public string TextBoxName { get; set; }

private Control FindControlRecursive(Control root, string textBoxName)
{
if (root.Name == textBoxName) return root;
foreach (Control c in root.Controls)
{
Control t = FindControlRecursive(c, textBoxName);
if (t != null) return t;
}
return null;
}

protected override void Append(log4net.Core.LoggingEvent loggingEvent)
{
if (_textBox == null)
{
if (String.IsNullOrEmpty(FormName) ||
String.IsNullOrEmpty(TextBoxName))
return;

Form form = Application.OpenForms[FormName];
if (form == null)
return;

_textBox = (TextBox)FindControlRecursive(form, TextBoxName);
if (_textBox == null)
return;

form.FormClosing += (s, e) => _textBox = null;
}
_textBox.Invoke((MethodInvoker)delegate
{
_textBox.AppendText(loggingEvent.RenderedMessage + Environment.NewLine);
});
}
}

||<
 
 
実際に書き出すフォームで
 
>|cs
var textBoxAppender =newTextBoxAppender();
        textBoxAppender.TextBoxName="textBox1";
        textBoxAppender.FormName="FormXXX";
        textBoxAppender.Threshold= log4net.Core.Level.All;var consoleAppender =new log4net.Appender.ConsoleAppender{Layout=new log4net.Layout.SimpleLayout()};varlist=newAppenderSkeleton[]{ textBoxAppender, consoleAppender };
        log4net.Config.BasicConfigurator.Configure(list);

||<

以上でファイルに出力するLogをtextBoxにも出力出来た。

 

log4netでハマった・・・・

log4net

 Nugetからインストールして、使い回しのconfig取り込んでと、

メチャ簡単なんで使いました。

 

でも・・・・・・

 開発マシンには、キチンとログが出る。

 実行環境へインストールすると、何も出ない?????なんで~

 

出力されないからと、設定ファイルを疑って半日。

 

もしかして設定ファイルが読み込まれていないのか??????

 

 

 

原因は、

 AssemblyInfo.csに

[assembly: log4net.Config.XmlConfigurator(ConfigFile = "./Log4net.Config.xml", Watch = true)]

と、相対パスlog4netの設定ファイル位置を書いたのだが、

セットアップで、アプリケーションファイルを見ると、

なんと、、、

 

データファイル(自動)となってました・・・・・・・

含むに変えると、キチンと出力された。

 

読み込まれないんなら、実行時にファイルがないとエラーになって欲しい。

 

これだけで、半日ツブした。

はい、バカなのは私です。

 

 

怒りをブツけるところがないので、ここへ。