2014年8月24日 星期日

About the SynchronizationContext with UI threading, and WCF with STA threading

There has a new WPF project of my job is using multiple UI threads interact with each other and background thread. When I got a while of research and finally found a good introduction about the SynchronizationContext from CodeProject.

Understanding SynchronizationContext Part I, Part II and Part III.

These articles are very simple and easy to understand that I don't need any additional example code to scribe it.

2014年7月30日 星期三

Scheduler, Task and Cancellation in dot NET programming

There has many opportunities using Task (or Thread) to program our UI related applications. About the basic usage of Task, the MSDN has many sample code and some coding forum also has lots of discussion. So, this article will mainly point some kinds of situation using task methods between fluent UI with heavy loading work, but not how to use Task class.

The first sample is: there has two actions that we want to run in background sequentially. When the primary action was done, it would updates the UI likes changes text content or enables button. And then the secondary action was done, it needs to refresh UI again.
Maybe there were many solutions to design implementation code using thread invoke or whatever others in past, but in .NET 4.0 or later version, it could be programmed more easily and clearly, following is the sample.

  public MainWindow()  
  {  
    InitializeComponent();  
    var backgroundScheduler = TaskScheduler.Default;  
    var currentScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
    Task.Factory.StartNew  
      (delegate { PrimaryAction(); }, backgroundScheduler).ContinueWith  
      (delegate { UpdateUI(); }, currentScheduler).ContinueWith  
      (delegate { SecondaryAction(); }, backgroundScheduler).ContinueWith  
      (delegate { RefresUIAgain(); }, currentScheduler);  
  }  
  private void PrimaryAction()  
  {  
    //do something in background  
  }  
  private void UpdateUI()  
  {  
    //update ui controls  
  }  
  private void SecondaryAction()  
  {  
    //another action to do in background  
  }  
  private void RefresUIAgain()  
  {  
    //update ui controls again  
  }  

The TaskScheduler provides different context for running thread that can helps us scheduling sequential tasks with Task.ContinueWith method.

The second sample is description of task's status in cases of action. It made confused me for a long time because I did not clarify the "cancel"'s meaning. There are some unit test methods to scribe it.

  [TestMethod]
  public void TaskRespondsCompletionWhenTokenRequestsCancellation()
  {
    CancellationTokenSource cts = new CancellationTokenSource();
    var token = cts.Token;
    Task task = new Task(()=>
      {
        while (!token.IsCancellationRequested)
          { /* do something repeatedly */ }
      }, token);
    task.Start();
    while (task.Status != TaskStatus.Running)
      { /* util task is ready and running */ }
    cts.Cancel();  //token requested cancellation
    task.Wait();  //task wait for while loop
    Assert.IsFalse(task.Status == TaskStatus.Canceled);
    Assert.IsTrue(task.Status == TaskStatus.RanToCompletion);
  }

Although the token is requested cancellation, but it means completion for the running task. The situation of task's status will be signed "Canceled" is requesting cancellation before task running.

  [TestMethod]
  public void TaskRespondsCanceledWhenTokenRequestsCancellation()
  {
    CancellationTokenSource cts = new CancellationTokenSource();
    var token = cts.Token;
    Task task = new Task(()=>
      {
        while (!token.IsCancellationRequested)
          { /* do something repeatedly */ }
      }, token);
    task.Start();
    if (task.Status != TaskStatus.Running)
    {
      cts.Cancel();  //requests cancellation before task running
      bool taskWaitThrowException = false;
      try { task.Wait(); }
      catch (Exception ex)
      {
        Assert.IsInstanceOfType(ex, typeof(AggregateException));
        Assert.IsInstanceOfType(ex.InnerException, typeof(TaskCanceledException));
        taskWaitThrowException = true;
      }
      Assert.IsTrue(task.Status == TaskStatus.Canceled);
      Assert.IsTrue(taskWaitThrowException);
    }
    else
    {
      Assert.Inconclusive("Task was running before requesting cancellation");
      try { cts.Cancel(); /* Clean up then manually re-test again */ }
      catch { }
    }
  }

So we can using this behavior for purpose that there are two difference actions when a main task was canceled before its running or after.

But it seems not so much useful when I write down this article...

2013年8月13日 星期二

MFC application in Windows Preinstallation Environment

There is a common issue when deploying Windows system via WinPE. Even though the deployment process is closed to automation, it still has some UI applications in WinPE, likes test tools or quality analyzers which are used by operator.

The later version of WinPE4.0 has .NET component that can be used basically .NET framework 4.0/4.5 function. But I believe unmanaged code is still mostly used for the simply operation system just like WinPE.

MFC is a popular and easy use for UI based unmanaged application, but when we create a basic MFC project using Visual Studio, then put the exe file to WinPE, it must be failed to launch. The problem was default MFC project will initialize ActiveX control container, so as to let developer using OLE objects.

So we want to use MFC application in WinPE, put the oledlg.dll and exe file together. The other way is remove AfxEnableControlContainer(); from the source code, if it does not have to use ActiveX controls.

2012年12月23日 星期日

Issue of DISM.exe in Windows Deployment

DISM(Deployment Image Servicing and Management) is a image tools from Microsoft, it can apply, capture or other operations like add packages and features to create or modify Windows image even didn't  using the default of Windows setup process to install their operation system.

But it still has some issue about the DISM of Windows 8 and WindowsPE 4.0 environment, maybe it is a bug of DISM tool, or there're still having some issue from the newest operation system of Microsoft.

The issue was that when we want to add packages to the image, likes applies language packs, hotfix or the metro style apps to some specific images. Then using command parameters /LogLevel and /LogPath to export DISM working log for tracing event. The operation result is successful but still has some unreadable logs in logging files.

Originally there works fine in the previous image just only applies language packs, until found side effect in applying hotfix and metro apps to specific images. it makes some in-box drivers to unsigned status that we can not passing check logo requirement of Microsoft.

After doing some testing, we found the workaround was removes the /LogLevel and /LogPath command parameters can solving this issue.

Maybe there still has other side effects just we don't know yet....

2012年12月18日 星期二

Tips of Windows Runtime Component

Different with .NET dll, the Windows Runtime Component has some limitations when we want to build it using managed code. Here is something talking about creating Windows Runtime Component in managed code from MSDN.

In the section of Declaring types in Windows Runtime Components, we can know Windows Runtime only allowed Windows Runtime types for returned type of public method in the component, and something we should notice that the implementation of component. Following sample code may be help us knowing more easily.

 public sealed class WinRTClass  
 {  
   private List<string> _frusts;  
   public WinRTClass()  
   {  
     _frusts = new List<string> { "Apple", "Pear", "Strawberry", "Blueberry", "Plum" };  
   }  
   public List<string> GetFrusts()  
   {  
     return _frusts;  
   }  
 }  
This is very simple class for Windows Runtime Component, it has a private list to saving strings and a method to return that object. But it will show a compiler error when we build this project.
 Method 'testRTCompoment.WinRTClass.GetFrusts()' has a parameter of type 'System.Collections.Generic.List<System.String>' in its signature. Although this generic type is not a valid Windows Runtime type, the type or its generic parameters implement interfaces that are valid Windows Runtime types. Consider changing the type 'System.Collections.Generic.List<T>' in the method signature to one of the following types instead: 'System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyList<T>, System.Collections.Generic.IEnumerable<T>'.  
It shows the return type List<T> in its signature, this generic type is not a valid Windows Runtime type.

Because of coding managed code in Windows Store app, the compiler (Visual Studio 2012) will enumerate .NET framework types to Windows Runtime types in compiling phase, likes IList<T> will be transfer to IEnumerable<T> automatically. So we can use .NET framework type to coding Windows Store app, but can not use them for component return type to other applications.

We can modify the return type to IEnumerable<T> to commit the requirement of Windows Runtime.
  public sealed class WinRTClass   
  {   
   private List<string> _frusts;   
   public WinRTClass()   
   {   
    _frusts = new List<string> { "Apple", "Pear", "Strawberry", "Blueberry", "Plum" };   
   }   
   public IEnumerable<string> GetEnumFrusts()  
   {  
     return _frusts;  
   }  
  }   

In another sample, we want to use asynchronous task for return type in functional method. But the reason is same of List<T>, the Task<T> is also a .NET framework type not a valid Windows Runtime type.
So we can design the codes like.
 /// it will fail to compiling stage because Task<T> is not a valid Windows Runtime component  
 /*  
 public async Task<IEnumerable<string>> GetBerriesAsync()  
 {  
   return await Task.Run(() => _frusts.Where(x => x.Contains("berry")));  
 }  
 */  
 ///complies OK because it is private  
 private async Task<IEnumerable<string>> GetBerriesAsync()  
 {  
   return await Task.Run(() => _frusts.Where(x => x.Contains("berry")));  
 }  
 ///using WinRT type for accessing method to GetBerriesAsync()  
 public IAsyncOperation<IEnumerable<string>> GetBerriesAsyncOperation()  
 {  
   return GetBerriesAsync().AsAsyncOperation();  
 }  
In GetBerriesAsyncOpeartion method, it uses extension method of Task object which named AsAsyncOperation to enumerate IAsyncOperation.

2012年11月14日 星期三

Skydrive REST API for Windows desktop app (2)

Previous article was presented how to use REST API for user log-on and authentication. In the next, we want to know how to retrieve file system information on Skydrive and how to upload files to it.

Step 3: retrieve file system information from Skydrive

API supported GET method for retrieving data
GET https://apis.live.net/v5.0/me/skydrive/files?access_token=ACCESS_TOKEN
We also can set filter on fragment for getting folders, albums, audio, photos or videos.
GET https://apis.live.net/v5.0/me/skydrive/files?filter=folders&access_token=ACCESS_TOKEN


After sending request to Live Connect REST API, it returns json struct for scripting the file system information on your Skydrive, it looks like
{ "data": [
        {
            "id": "photo.a6b2a7e8f2515e5e.A6B2A7E8F2515E5E!131", 
            "upload_location": "https://apis.live.net/v5.0/photo.a6b2a7e8f2515e5e.A6B2A7E8F2515E5E!110/files/", 
            ...
            "type": "folder",
            ...
        }, {
            "id": "file.a6b2a7e8f2515e5e.A6B2A7E8F2515E5E!119", 
            ...             
            "upload_location": "https://apis.live.net/v5.0/file.a6b2a7e8f2515e5e.A6B2A7E8F2515E5E!119/content/", 
            ...
            "type": "file", 
            ...
        }
    ] }

There is a important thing that the REST API returns json characters were encoded by Normal ASCII(code 1252). But in the .Net framework environment, the default decoder for String builder is using Unicode, so we need to convert code page for retrieved json characters.


It can be used both WebClient or HttpWebRequest classes to implement the http request in .Net framework. following is a sample code using WebClient to retrieve all folders on Skydrive root.

 private void getSkyDriveAccess()  
 {  
   if (App.Current.Properties.Contains("oauthData"))  
     makeRequest(@"https://apis.live.net/v5.0/me/skydrive/files?filter=folders&" + App.Current.Properties["access_token"]);  
 }  
 private void makeRequest(string requestUrl)  
 {  
   WebClient wc = new WebClient();  
   wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);  
   wc.DownloadStringAsync(new Uri(requestUrl));  
 }  
 void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)  
 {  
   IDictionary<string , object> data = deserObjJson(e.Result);  
   ArrayList dataitems = (ArrayList)data["data" ];  
   string upload_location = "";  
   foreach ( object datajson in dataitems)  
   {  
     IDictionary<string , object > item = datajson as IDictionary< string, object>;  
     string id = item[ "id"].ToString();  
     string name = Encoding.UTF8.GetString(Encoding.GetEncoding(1252).GetBytes(item["name"].ToString()));  
     if (name.ToLower().Equals("testfolder"))  
     {  
       upload_location = item["upload_location"].ToString();  
     }  
   }  
 }  


Step 4: uploading files

Before starting access folders and files on Skydrive, the article of reference about Folders and files operating would be help us knowing how to use API methods and formats to access them.

There are two methods for uploading files, one is PUT method
PUT https://apis.live.net/v5.0/me/skydrive/files/HelloWorld.txt?access_token=ACCESS_TOKEN

Hello, World!
And another is POST method
POST https://apis.live.net/v5.0me/skydrive/files?access_token=ACCESS_TOKEN
Content-Type: multipart/form-data; boundary=AaB03x

--AaB03x
Content-Disposition: form-data; name="file"; filename="MyNewFile.txt"
Content-Type: text/plain

These are the contents of my new file.
--AaB03x--

After finishing method process, API will return the uploaded file information on Skydrive.
Following is a sample code using HttpWebRequest class to implement uploading function.
 private void UploadFileByPutMethod(string fileName)  
 {  
   string uri = @"https://apis.live.net/v5.0/me/skydrive/files/" + fileName + "?" + App.Current.Properties["access_token"];  
   HttpWebRequest req = WebRequest .CreateHttp(uri);  
   req.Method = "PUT";  
   using (Stream reqStream = req.GetRequestStream())  
   {  
     byte[] buff = Encoding.UTF8.GetBytes("Hello, SkyDrive!加上中文");  
     reqStream.Write(buff, 0, buff.Length);  
   }  
   string resJson = "";  
   using (WebResponse res = req.GetResponse())  
   {  
     StreamReader sr = new StreamReader(res.GetResponseStream());  
     resJson = sr.ReadToEnd();  
   }  
   MessageBox.Show(resJson);  
 }  
 private void UploadFileByPostMethod(string uploadLocation, string filePath, string mimeType)  
 {  
   string uri = uploadLocation + "?" + App.Current.Properties["access_token"];  
   HttpWebRequest req = WebRequest.CreateHttp(uri);  
   req.Method = "POST";  
   req.ContentType = "multipart/form-data; boundary=AaB03x" ;  
   string requestHeader =  
     "--AaB03x" + Environment.NewLine +  
     "Content-Disposition: form-data; name=\"file\"; filename=\"" + System.IO.Path.GetFileName(filePath) + "\"" + Environment.NewLine +  
     "Content-Type: " + mimeType + Environment.NewLine + Environment.NewLine;  
   string requestTrailer =  
     "--AaB03x--";  
   byte[] buffHeader = Encoding.UTF8.GetBytes(requestHeader);  
   byte[] buffTrailer = Encoding.UTF8.GetBytes(requestTrailer);  
   byte[] buffFile = null;  
   using (BinaryReader br = new BinaryReader(File .OpenRead(filePath)))  
   {  
     buffFile = br.ReadBytes((int)br.BaseStream.Length);  
   }  
   req.ContentLength = buffHeader.Length + buffTrailer.Length + buffFile.Length;  
   using (Stream reqStream = req.GetRequestStream())  
   {  
     reqStream.Write(buffHeader, 0, buffHeader.Length);  
     reqStream.Write(buffFile, 0, buffFile.Length);  
     reqStream.Write(buffTrailer, 0, buffTrailer.Length);  
   }  
   string resJson = "";  
   using (WebResponse res = req.GetResponse())  
   {  
     StreamReader sr = new StreamReader(res.GetResponseStream());  
     resJson = sr.ReadToEnd();  
   }  
   MessageBox.Show(resJson);  
 }  

Because this project was cancelled, I have to stop study forward in this topic. The planning next step is multiple uploading or scripting of specified data format, likes photos, albums and etc.
But maybe I can post article talking something about the REST architecture when I have time. :P


2012年11月8日 星期四

SkyDrive REST API for Windows desktop app (1)

I researched for a week to figure out that has there any source code or samples for this issue. But it seems like difficult build-on the desktop apps, and very difference with previous version of Live SDK. Finally, I found and followed the Getting Start of REST API in Microsoft Live Connect Center to build my desktop application.


Step 1: Create and manage application on Live Connect Center

In first, you must sign-on the manager page of Live Connect Center, then create and manage your applications.

Fill out all of required information, it shows the Client ID for your client-side application.

Then now, we can start to write client-side application for using SkyDrive REST API.


Step 2: Implement client-side authentication

Difference with the guideline of user signing, I would like to use access token for client-side authentication, so there is a little change that I set the response_type=token .
Following is the request URL of Live Connect Oauth 2.0

https://login.live.com/oauth20_authorize.srf?client_id=CLIENT_ID&scope=SCOPE_STRING&response_type=token&redirect_uri=https://login.live.com/oauth20_desktop.srf

CLIENT_ID : the client id of managed application on Live Connect Center
SCOPE_STRING : permissions which gets from the user to access their info on app, see also guideline of scopes and permissions.

After user agree the consent, Oauth 2.0 API will return a URL that contains token and expired time, looks like

https://login.live.com/oauth20_desktop.srf?lc=1033#access_token=AsDfGhJkL1234&token_type=bearer&expires_in=3600&scope=wl.signin%20wl.skydrive

The access_token would be found in the URL fragment if user sign-in and authorized successfully.

Here is a sample code which I implement the authentication by using WebBrowser class in C#
 private static string scope = "wl.signin%20wl.skydrive%20wl.skydrive_update";  
 private const string client_id = "0000000000012345";  
 private static Uri signInUrl = new Uri(String .Format(@"https://login.live.com/oauth20_authorize.srf?client_id={0}&scope={1}&response_type=token&redirect_uri=https://login.live.com/oauth20_desktop.srf", client_id, scope));  
 webBrowser.Navigate(signInUrl);  
 webBrowser.LoadCompleted += webBrowser_LoadCompleted;  
 private void webBrowser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)  
 {  
   if (e.Uri.Fragment.Contains("access_token"))  
   {  
     if (App.Current.Properties.Contains("oauthData"))  
       App.Current.Properties.Clear();  
     App.Current.Properties.Add("oauthData", 1);  
     string[] responseOauth = Regex.Split(e.Uri.Fragment.Remove(0, 1), "&");  
     for (int i = 0; i < responseOauth.Count(); i++)  
     {  
       string[] nvPair = Regex.Split(responseOauth[i], "=");  
       App.Current.Properties.Add(nvPair[0], responseOauth[i]);  
     }  
     this.Close();  
   }  
 }  

#file retrieving and uploading will be continued in the next article