2015年12月7日月曜日

HttpClient, WebClient, WebRequest での CancellationToken によるキャンセル操作のノウハウの寄せ集め。

// --------------------------------
HttpClient after dotNet4.5

http://stackoverflow.com/questions/9746182/where-is-webclient-downloadstringtaskasyncuri-cancellationtoken-in-vs11
c# - Where is WebClient.DownloadStringTaskAsync(Uri,CancellationToken) in VS11 - Stack Overflow

GetAsync(String, CancellarationToken) があるそうです。

でもなぜか、
    var result = await client.GetStringAsync();
ではなく
    var tskRes = client.GetStringAsync();
    while(!tskRes.Wait(TimeSpan.FromSeconds(0.2))) {
        ctoken.ThrowIfCancellarationRequested();
    }
    result = tskRes.Result;
のコードが記載されています。


// --------------------------------
HttpClient after dotNet4.5

http://stackoverflow.com/questions/30053792/async-await-with-cancellationtoken-doesnt-cancel-the-operation
c# - Async/await with CancellationToken doesn't cancel the operation - Stack Overflow

ここでは HttpClient は CancellarationToken が使えるので簡単だよね、となっているようです。


// --------------------------------
WebClient

http://stackoverflow.com/questions/30053792/async-await-with-cancellationtoken-doesnt-cancel-the-operation
c# - Async/await with CancellationToken doesn't cancel the operation - Stack Overflow

WebClient.CancelAsync メソッドで Cancel できるらしい。

    myToken.Register(myWebclient.CancelAsync);
としておくと token で操作可能らしい。


// --------------------------------
WebRequest

http://stackoverflow.com/questions/30053792/async-await-with-cancellationtoken-doesnt-cancel-the-operation
c# - Async/await with CancellationToken doesn't cancel the operation - Stack Overflow

ストリームをコピーするメソッドに token を受け付けるものがある。
ということのようです。
    var stream = response.GetResponseStream()
    var destStream = new MemoryStream()
    await stream.CopyToAsync(destStream, 4096, cancelToken);
    return Encoding.UTF8.GetString(destStream.ToArray());

http://stackoverflow.com/questions/9746182/where-is-webclient-downloadstringtaskasyncuri-cancellationtoken-in-vs11
c# - Where is WebClient.DownloadStringTaskAsync(Uri,CancellationToken) in VS11 - Stack Overflow

ですが、サーバーから待っているときもキャンセルしたいので、
上記のアイデアは使えるかもです。

つまり
    var response = await req.GetResponseAsync();
ではなく
    tskRes = req.GetResponseAsync();
    while(!tskRes.Wait(TimeSpan.FromSeconds(0.2))) {
        ctoken.ThrowIfCancellarationRequested();
    }
    result = tskRes.Result;
のように。


// --------------------------------
WebClient

.NET Framework/WebClientクラスでタイムアウトを変更する

では、継承してタイムアウトを追加する方法が記載されています。


// --------------------------------


C# で await/async を使ってみた。 何故か常に Task.IsCompleted == true になる。 動いているか、の判定に使えないので、困った。 dotNet4.5 で。

C# で await/async を使ってみた。 何故か常に Task.IsCompleted == true になる。 動いているか、の判定に使えないので、困った。 dotNet4.5 で。

// ----------------------------------------
// MainWindow.xaml.cs

//#define BY_THE_SLEEP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfAppTestOnDotNet45
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
       

        //
        // Task.IsCompleted が常に true を返すようです。
        // そのため完了しているか否かは volatile 変数などで手動で管理する必要がありそうです。
        //

        private Task taskForCancelTestAsync = null;
        private System.Threading.CancellationTokenSource ctsForCancelTestAsync = null;

        private void btnCancelForCancelTestAsync_Click(object sender, RoutedEventArgs e)
        {
            var tsk = taskForCancelTestAsync;
            var cts = ctsForCancelTestAsync;

            if (tsk == null || cts == null)
            {
                MessageBox.Show("実行されていません。");
                return;
            }
            else if (tsk != null && (tsk.IsCanceled || /* tsk.IsCompleted || */ tsk.IsFaulted))
            {
                // 常に tsk.IsCompleted == true になります。
                // また tsk.Status == TaskStatus.RanToCompletion になっています。
                // よって
                // Cancel 出来るか、とか、Runできるか、といった判定に使えないようです。
                MessageBox.Show("既に完了しています。");
                return;
            }
            else if (cts != null && cts.IsCancellationRequested)
            {
                MessageBox.Show("キャンセル中です。");
                return;
            }
            else
            {
                //MessageBox.Show("実行中です。");
                //return;
            }

            try
            {
                var syncContextUI = System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext();

#if true
                tsk.ContinueWith((curTask) => {
                    MessageBox.Show("キャンセルできました。");
                }, syncContextUI);
                cts.Cancel();
#endif
#if false
                // これは「Cancel 時に実行」という意図があるが、MessageBox.Showが動かない。
                var cts2nd = new System.Threading.CancellationTokenSource();
                var ctoken2nd = cts2nd.Token;
                tsk.ContinueWith((curTask) => {
                    MessageBox.Show("キャンセルできました。[CancelOnly]");
                }, ctoken2nd, TaskContinuationOptions.OnlyOnCanceled, syncContextUI);
                cts.Cancel();
#endif
            }
            catch (TaskCanceledException ex)
            {
                // たぶんキャンセルする側のスレッドで呼ばれる例外。
                //      Sleep のときも Task.Delay のときも呼ばれなかった。
                MessageBox.Show("[btnCancel_Click]Error: TaskCanceledException");
                Console.WriteLine("[btnCancel_Click]TaskCanceledException:" + ex.ToString());
            }
            catch (OperationCanceledException ex)
            {
                // たぶんキャンセルされたスレッドで呼ばれる例外。
                //      Sleep のときも Task.Delay のときも呼ばれなかった。
                MessageBox.Show("[btnCancel_Click]Error: OperationCanceledException");
                Console.WriteLine("[btnCancel_Click]OperationCanceledException:" + ex.ToString());
            }
            catch (AggregateException exg)
            {
                //      Sleep のときも Task.Delay のときも呼ばれなかった。
                MessageBox.Show("[btnCancel_Click]Error: AggregateException");
                Console.WriteLine("[btnCancel_Click]AggregateException:" + exg.ToString());
                foreach (Exception ex in exg.InnerExceptions)
                {
                    Console.WriteLine("\t" + ex.ToString());
                }
            }
            catch (Exception ex)
            {
                //      Sleep のときも Task.Delay のときも呼ばれなかった。
                MessageBox.Show("[btnCancel_Click]Error: OperationCanceledException");
                Console.WriteLine("[btnCancel_Click]Exception:" + ex.ToString());
                throw;
            }

        }

        private void btnRunForCancelTestAsync_Click(object sender, RoutedEventArgs e)
        {
            var tsk = taskForCancelTestAsync;
            var cts = ctsForCancelTestAsync;

            if (tsk == null || cts == null)
            {
                //MessageBox.Show("実行されていません。");
                //return;
            }
            else if (tsk != null && (tsk.IsCanceled || tsk.IsCompleted || tsk.IsFaulted))
            {
                //MessageBox.Show("既に完了しています。");
                //return;
            }
            else if (cts != null && cts.IsCancellationRequested)
            {
                MessageBox.Show("キャンセル中です。");
                return;
            }
            else
            {
                MessageBox.Show("実行中です。");
                return;
            }

            if (tsk != null)
            {
                tsk.Dispose();
                tsk = null;
            }
            if (cts != null)
            {
                cts.Dispose();
                cts = null;
            }

            taskForCancelTestAsync = null;
            ctsForCancelTestAsync = null;

            try
            {

            }
            catch (Exception ex)
            {
                MessageBox.Show("[btnRun_Click]Error: OperationCanceledException");
                Console.WriteLine("[btnRun_Click]Exception:" + ex.ToString());
                throw;
            }
            ctsForCancelTestAsync = new System.Threading.CancellationTokenSource();
            var ctoken = ctsForCancelTestAsync.Token;
#if BY_THE_SLEEP
            taskForCancelTestAsync = System.Threading.Tasks.Task.Factory.StartNew(
                () =>
                {
                    CancelTestForAsync.Run(ctoken);
                }, ctoken);
#else
            taskForCancelTestAsync = System.Threading.Tasks.Task.Factory.StartNew(
                async () =>
                {
                    await CancelTestForAsync.Run(ctoken);
                }, ctoken);
#endif
        }
    }
}

// ----------------------------------------
// CancelTestForAsync.cs

//#define BY_THE_SLEEP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WpfAppTestOnDotNet45
{
    class CancelTestForAsync
    {
#if BY_THE_SLEEP
        public static void Run(System.Threading.CancellationToken ctoken) // ← Sleep の場合の定義。
#else
        public static async Task Run(System.Threading.CancellationToken ctoken) // ← Delay の場合の定義。
#endif
        {
            Console.WriteLine("[CancelTestForAsync::Run]Start");
            for (int i = 0; i < 20; i++)
            {
                try
                {
#if BY_THE_SLEEP
                    System.Threading.Thread.Sleep(millisecondsTimeout: 20 * 1000);
#else
                    //     Task.Delay(millisecondsDelay: 20 * 1000, cancellationToken: ctoken);
                    // のように await が無い場合は、別スレッドを実行した後、すぐに続行するので意味が無いことに注意。
                    await Task.Delay(millisecondsDelay: 20 * 1000, cancellationToken: ctoken);
#endif
                    if (ctoken.IsCancellationRequested)
                    {
                        // Sleep の場合、ここに来る。 System.Threading.CancellationToken では Cancel 即座のキャンセルが出来ない。
                        Console.WriteLine("[CancelTestForAsync::Run]ctoken.ThrowIfCancellationRequested();");
                        ctoken.ThrowIfCancellationRequested();
                    }
                    Console.WriteLine("[CancelTestForAsync::Run]Delayed");
                }
                catch (TaskCanceledException ex)
                {
                    // たぶんキャンセルする側のスレッドで呼ばれる例外。
                    //      Sleep の場合、想定どおり、ここに来ない。
                    //      Task.Delay の場合、ここに来る。 await の中で Cancel が検知されると、ここに来るということ。
                    Console.WriteLine("[CancelTestForAsync::Run]TaskCanceledException:" + ex.ToString());
                    throw;
                }
                catch (OperationCanceledException ex)
                {
                    // たぶんキャンセルされたスレッドで呼ばれる例外。
                    //      Sleep の場合、想定どおり、ここで発生。 ctoken.ThrowIfCancellationRequested(); で呼ばれる、ということ。
                    Console.WriteLine("[CancelTestForAsync::Run]OperationCanceledException:" + ex.ToString());
                    throw;
                }
                catch (AggregateException exg)
                {
                    // Sleep の場合、想定どおり、ここに来ない。
                    Console.WriteLine("[CancelTestForAsync::Run]AggregateException:" + exg.ToString());
                    foreach (Exception ex in exg.InnerExceptions)
                    {
                        Console.WriteLine("\t" + ex.ToString());
                    }
                    throw;
                }
                catch (Exception ex)
                {
                    // Sleep の場合、想定どおり、ここに来ない。
                    Console.WriteLine("[CancelTestForAsync::Run]Exception:" + ex.ToString());
                    throw;
                }
            }
        }
    }
}

// ----------------------------------------

2015年12月6日日曜日

OfficeReadOnlyOpen.vbs - office read only open written by vb script. for microsoft excel, microsoft powerpoint, microsoft word.

WHAT IS THIS?
office read only open written by vb script. for microsoft excel, microsoft powerpoint, microsoft word.

HOW TO USE?
copy this file to "SendTo" folder, etc.
ex: C:\Users\<YourAccount>\AppData\Roaming\Microsoft\Windows\SendTo


'--------------------------------
Rem
Rem OfficeReadOnlyOpen.vbs
Rem


'--------------------------------
'----------------
'Check Argument
'----------------
'Check argument.
If WScript.Arguments.Count <> 1 Then WScript.Quit

'Get file name.
Dim strFileName
strFileName = WScript.Arguments(0)


'----------------
'Check Extension
'----------------
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")

Dim extName
extName = fso.GetExtensionName(strFileName)

Dim strRegularFileName
If extName <> "lnk" Then
    strRegularFileName = strFileName
Else
    'Get Shortcut TargetPath.
    If Not fso.FileExists(strFileName) Then
        strRegularFileName = strFileName
    Else
        Dim oWshShell
        Set oWshShell = CreateObject("WScript.Shell")
     
        Dim oShellLink
        Set oShellLink = oWshShell.CreateShortcut(strFileName)
     
        strRegularFileName = oShellLink.TargetPath
     
        extName = fso.GetExtensionName(strRegularFileName)
    End If
End If


'----------------
'Execute
'----------------
Dim objOfficeApp
If extName = "ppt" Or extName = "pptx" Then

    Set objOfficeApp = GetOrCreateObject("Powerpoint.Application")
    objOfficeApp.Visible = True
    'Open As ReadOnly.
    Call objOfficeApp.Presentations.Open(strRegularFileName, True)
 
ElseIf extName = "doc" Or extName = "docx" Then
 
    Set objOfficeApp = GetOrCreateObject("Word.Application")
    objOfficeApp.Visible = True
    'Open As ReadOnly.
    Call objOfficeApp.Documents.Open(strRegularFileName, , True)
 
ElseIf extName = "xls" Or extName = "xlsx" Or extName = "xlsm" Or extName = "xlsb" Then

    Set objOfficeApp = GetOrCreateObject("Excel.Application")
    objOfficeApp.Visible = True
    'Open As ReadOnly.
    Call objOfficeApp.Workbooks.Open(strRegularFileName, , True)
 
End If


'----------------
'Ending
'----------------
Set objOfficeApp = Nothing
WScript.Quit



'--------------------------------
Function GetOrCreateObject(ClassName)
    Dim objOfficeApp
    Dim isGetObjectError
 
    '----------------
    ' VBScript version GetObject(, class): return Existing instance. but occur ERROR if no instance is.
    ' WScript.GetObject(ProgID)          : return always NEW instance.
 
    '----------------
    'Get Existing application instance.
    On Error Resume Next
    Set objOfficeApp = GetObject(, ClassName)
    If Err.Number <> 0 Then
        isGetObjectError = True
    ElseIf objOfficeApp Is Nothing Then
        isGetObjectError = True
    End If
    On Error GoTo 0
 
    '----------------
    'Get New application instance.
    If isGetObjectError Then
        Set objOfficeApp = CreateObject(ClassName)
    End If
 
    '----------------
    'return instance.
    Set GetOrCreateObject = objOfficeApp
End Function
'--------------------------------