<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Blogs of Han</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/hanxia/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/hanxia/feed.xml</link>
    <description>关注Modern App的开发， Azure, IOT, UWP...</description>
    <lastBuildDate>Wed, 17 Apr 2019 03:21:07 GMT</lastBuildDate>
    <language>zh_CN</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Certificate between IoT hub and devices connection</title>
      <link>https://docs.microsoft.com/archive/blogs/hanxia/certificate-between-iot-hub-and-devices-connection</link>
      <pubDate>Tue, 27 Jun 2017 14:52:43 GMT</pubDate>
      <dc:creator><![CDATA[Han Xia]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/hanxia/?p=435</guid>
      <description><![CDATA[There are a lot of questions from our customers about certificate issues during the TLS connection...]]></description>
      <content:encoded><![CDATA[There are a lot of questions from our customers about certificate issues during the TLS connection between IoT devices and IoT hub. So here I write this article to reveal something that you need to know when you are trying to connect your IoT devices to Azure IoT hub.

IoT Hub requires all device communication to be secured using TLS/SSL (hence, IoT Hub doesn’t support non-secure connections over port 1883). The supported TLS version is TLS 1.2, TLS 1.1 and TLS 1.0, in this order. Support for TLS 1.0 is provided for backward compatibility only. It is recommended to use TLS 1.2 since it provides the most security.

Firstly, let’s have a look at the process of TLS handshake. Here I quote the steps from this link: <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa380513(v=vs.85).aspx">https://msdn.microsoft.com/en-us/library/windows/desktop/aa380513(v=vs.85).aspx</a>
<ol>
 	<li>The client sends a "Client hello" message to the server, along with the client's random value and supported cipher suites.</li>
 	<li>The server responds by sending a "Server hello" message to the client, along with the server's random value.</li>
 	<li>The server sends its certificate to the client for authentication and may request a certificate from the client. The server sends the "Server hello done" message.</li>
 	<li>If the server has requested a certificate from the client, the client sends it.</li>
 	<li>The client creates a random and encrypts it with the <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms721603(v=vs.85).aspx#_security_public_key_gly">public key</a> from the server's certificate, sending the encrypted Pre-Master Secret to the server.</li>
 	<li>The server receives the Pre-Master Secret. The server and client each generate the Master Secret and <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms721625(v=vs.85).aspx#_security_session_key_gly">session keys</a> based on the Pre-Master Secret.</li>
 	<li>The client sends "Change cipher spec" notification to server to indicate that the client will start using the new <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms721625(v=vs.85).aspx#_security_session_key_gly">session keys</a> for <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms721586(v=vs.85).aspx#_security_hash_gly">hashing</a> and encrypting messages. Client also sends "Client finished" message.</li>
 	<li>Server receives "Change cipher spec" and switches its record layer security state to <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms721625(v=vs.85).aspx#_security_symmetric_encryption_gly">symmetric encryption</a> using the <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms721625(v=vs.85).aspx#_security_session_key_gly">session keys</a>. Server sends "Server finished" message to the client.</li>
 	<li>Client and server can now exchange application data over the secured channel they have established. All messages sent from client to server and from server to client are encrypted using session key.</li>
</ol>
In step 3, the server would send server certificate and may request client certificate. There are two types of certificate involved during the TLS handshake:
<ol>
 	<li>Server side certificate. Sent by Azure IoT Hub Server to client.</li>
 	<li>Client side certificate. Sent by client to server. It’s optional.</li>
</ol>
Please note that IoT Hub doesn’t support X.509 mutual authentication at the end of writing this article. So, there isn’t any client certificate required. But we do provide a mechanism to authenticate a device with IoT Hub using X.509 certificate. You can refer to following link for more detail:<a href="https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-security#supported-x509-certificates">https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-security#supported-x509-certificates</a>

Since IoT Hub doesn’t support mutual authentication, here we just need to discuss server authentication. There are 3 certificates in play here during the server authentication. These form part of cert chain and are linked together as a chain.
<table width="710" style="height: 400px">
<tbody>
<tr>
<td width="135">Type</td>
<td width="156">Issued by</td>
<td width="169">Other info</td>
<td width="125">Expiration</td>
</tr>
<tr>
<td width="135">Root CA</td>
<td width="156">Baltimore CyberTrust Root</td>
<td width="169">azure-iot-master\c\certs\ms.der, certs.c, certs.h (part of Azure IoT SDK)</td>
<td width="125">5/13/2025</td>
</tr>
<tr>
<td width="135">Intermediate CA</td>
<td width="156">Baltimore CyberTrust</td>
<td width="169">Sent by Server</td>
<td width="125">12/20/2017</td>
</tr>
<tr>
<td width="135">Wild Card certificate (leaf)</td>
<td width="156">Microsoft IT SSL SHA2</td>
<td width="169">Sent to all host names ending in *.azure-devices.net (wildcard certificate)</td>
<td width="125">8/26/2017</td>
</tr>
</tbody>
</table>
&nbsp;

Only 2 and 3 are sent by the server as part of TLS handshake. Client will normally validate Root CA of the chain and will determine if it is trusted. If the device doesn’t pass the verification of the server certificate, it will generally report “Unknown CA” error in the network log. You can see from above table that Root CA is included in Azure IoT SDK. So you can either install this Root CA in your device or trust this CA in your device application code explicitly. Generally, CyberTrust Root CA has existed in your Windows and most of Linux desktop version. You don’t need to install the CA to trust the certificate from IOT hub. But in a lot of embedded Linux devices which are tailored CyberTrust Root CA is not provided. And you might not even install it to the system. In this case, you should explicitly trust the certificate in you code. In IoT SDK for C，the sample application has included related code like this but not enabled by default.

#ifdef MBED_BUILD_TIMESTAMP

#include "certs.h"

#endif // MBED_BUILD_TIMESTAMP

#ifdef MBED_BUILD_TIMESTAMP

// For mbed add the certificate information

if (IoTHubClient_LL_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK)

{

printf("failure to set option \"TrustedCerts\"\r\n");

}

#endif // MBED_BUILD_TIMESTAMP

Here MBED_BUILD_TIMESTAMP is only defined in mBED system. If you want to enable it in other platform which doesn’t include CyberTrust Root CA, you should remove this ifdef or add the definition before compilation.

The certificates data is hardcoded in certs.c. In your real code, you should provide an interface to adjust it in case there is any certificate change. There aren’t many cases which need to do the change. The certificates sent from IoT hub is not changeable and configurable by the users. But Microsoft might change the certificate for some reason. And the root certificates still may expire or be revoked though they are long-lived. If there is no way of updating the certificate on the device, the device may not be able to subsequently connect to the IoT Hub (or any other cloud service). Having a means to update the root certificate once the IoT device is deployed will effectively mitigate this risk.

Note that China Azure didn’t use CyberTrust Root CA. Instead it still uses WoSign Root CA. This certificate is also included in certs.c so that you don’t need to change anything for China Azure connection. But it only works for IoT SDK for C. In IoT SDK for other language, this certificate might not be included in the sample code. You should add the correct certificate data explicitly.

You can also bypass the certificate validation although it’s definitely not recommended. We don’t provide the surface in the IoT SDK APIs. You can modify the code in OpenSSL or websocket layer to disable SSL verification if needed for development or if you know exactly what your case is.

That’s all I want to discuss in this post. I hope it helps to solve connection issue between device and IoT Hub. .]]></content:encoded>
    </item>
    <item>
      <title>如何在Windows Store应用程序中进行TLS/SSL通讯</title>
      <link>https://docs.microsoft.com/archive/blogs/hanxia/windows-storetlsssl</link>
      <pubDate>Wed, 31 Dec 2014 01:44:16 GMT</pubDate>
      <dc:creator><![CDATA[Han Xia]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/hanxia/2014/12/31/windows-storetlsssl/</guid>
      <description><![CDATA[Windows Store应用程序经常需要使用各种Web服务，为了安全起见，这些服务最好是通过https来访问。那么我们如何在Windows...]]></description>
      <content:encoded><![CDATA[<span style="font-size: small"><span style="font-family: Calibri">Windows Store</span>应用程序经常需要使用各种<span style="font-family: Calibri">Web</span>服务，为了安全起见，这些服务最好是通过<span style="font-family: Calibri">https</span>来访问。那么我们如何在<span style="font-family: Calibri">Windows Store</span>应用中使用证书来进行安全的通讯呢，我这里就做一个简单的小结。</span>

<span style="font-size: small">首先，如果服务端使用了服务端认证，那么<span style="font-family: Calibri">Windows Store</span>应用作为客户端连接服务器的时候，需要验证该服务端证书在客户端是受信任的。那么分两种情况：</span>

<span style="font-family: Calibri;font-size: small">一．</span>         <span style="font-size: small">如果该证书是被受信任的机构签发的，而且该机构的<span style="font-family: Calibri">Root</span>证书已经缺省安装到了客户端，那么，<span style="font-family: Calibri">Windows Store</span>应用程序只需要在<span style="font-family: Calibri">Package.appxmanifest</span>文件中加入对<span style="font-family: Calibri">Shared Uer Certigficates</span>这个<span style="font-family: Calibri">Capability</span>的支持，应用程序会自动到系统的<span style="font-family: Calibri">Certificate Store</span>中找到该<span style="font-family: Calibri">Root</span>证书并通过验证。不过这种方法也有一个问题，由于客户端只验证服务端证书是否来自于同一个<span style="font-family: Calibri">Root</span>，所以我们可以很容易的伪造一张服务端证书来通过验证。</span>

<span style="font-family: Calibri;font-size: small">二．</span>         <span style="font-size: small">如果该证书是一张自签名的证书，那么确实情况下客户端是不会安装这张证书的。那么这时候<span style="font-family: Calibri">Windows Store</span>应用程序就无法通过证书的验证。这个问题也好解决，向第一种情况靠拢呗。如果在客户端已经手动安装了这张自签名的证书，那就没问题啦。所以我们需要做的就是将这个自签名的证书从服务端下载下来，然后一台一台的安装到客户端<span style="font-family: Calibri">Certificate Store</span>的<span style="font-family: Calibri">Root</span>目录下。</span>

<span style="font-size: small">可是，这个方法也太不靠谱啦！在很多情况下，我是没有办法将自签名证书一台一台安装到客户端的呀。有没有其他办法呢？有的！我们可以将证书放在<span style="font-family: Calibri">Windows Store</span>应用程序中，然后通过<span style="font-family: Calibri">Package.appxmanifest</span>的<span style="font-family: Calibri">Declarations</span>添加这张证书：</span>
<ol>
 	<li><span style="font-family: Calibri;font-size: small">1.</span>       <span style="font-size: small">在<span style="font-family: Calibri">Available Declarations</span>中选择<span style="font-family: Calibri">Certificates</span>，点击<span style="font-family: Calibri">Add</span>。</span></li>
 	<li><span style="font-family: Calibri;font-size: small">2.</span>       <span style="font-size: small">在右边的<span style="font-family: Calibri">Trust flags</span>勾选上<span style="font-family: Calibri">Exclusive Trust</span>，加上这个以后，就能够保证不是随随便便用一张服务端证书就能够把客户端应用糊弄过去啦。</span></li>
 	<li><span style="font-family: Calibri;font-size: small">3.</span>       <span style="font-size: small">在右边的<span style="font-family: Calibri">Certificates</span>中点击<span style="font-family: Calibri">Add New</span>，<span style="font-family: Calibri"> Store Name</span>需填写<span style="font-family: Calibri">Root</span>，<span style="font-family: Calibri">Content</span>中添加你所要验证的服务端证书。</span></li>
 	<li><span style="font-family: Calibri;font-size: small">4.</span>       <span style="font-size: small">在添加以后，该服务端证书会自动添加到你的项目中，我们需要在该证书的文件属性中将<span style="font-family: Calibri">Copy to Output Directory</span>改为<span style="font-family: Calibri">Copy if newer</span>。</span></li>
</ol>
<span style="font-size: small">这样，我们就可以很方便的完成服务端验证了，而且也不用担心有人通过伪造服务端证书来截取通讯内容。</span>

<span style="font-size: small">接下来我们看一下需要客户端认证的情形。这种情况下，我们需要做两件事：</span>

<span style="font-size: small"><span style="font-family: Calibri">一．</span>保证客户端已经正确安装了客户端证书。你可以把你的证书文件放在项目中或者远程服务器上。当你的<span style="font-family: Calibri">Windows Store</span>应用获得了<span style="font-family: Calibri">PKCS12</span>格式的证书数据以后，就可以调用</span>

<span style="font-size: small"><span style="font-family: Calibri">CertificateEnrollmentManager.UserCertificateEnrollmentManager.ImportPfxDataAsync</span>或者<span style="font-family: Calibri">CertificateEnrollmentManager.InstallCertificateAsync</span>来安装该证书。</span>

<span style="font-size: small"><span style="font-family: Calibri">二．</span>一旦客户端已经正确安装了客户端证书，那么就需要在<span style="font-family: Calibri">Windows store</span>应用中获得该证书的实例，这可以通过<span style="font-family: Calibri">CertificateStores.FindAllAsync</span>来枚举所有的证书实例，然后找出你所需要的那个，<span style="font-family: Calibri">FindAllAsync</span>也支持传入一个<span style="font-family: Calibri">CertificateQuery</span>参数来进行筛选。</span>

<span style="font-size: small">在获得证书实例之后，我们就可以通过<span style="font-family: Calibri">HttpBaseProtocolFilter</span>来设置客户端证书，然后将<span style="font-family: Calibri">HttpBaseProtocolFilter</span>作为参数传入<span style="font-family: Calibri">HttpClient</span>中实现客户端验证，示例代码如下：</span>

var certificates = await CertificateStores.FindAllAsync();

var certificate = certificates.Where(w =&gt; w.Subject.Equals(SUBJECTNAME)).FirstOrDefault();

HttpBaseProtocolFilter filter = new HttpBaseProtocolFilter();

filter.ClientCertificate = certificate;

Windows.Web.Http.HttpClient client = new Windows.Web.Http.HttpClient(filter);

var response = await client.GetAsync(uri);

<span style="font-size: small">对于需要高度安全的应用，只有双向验证才能保证数据的安全性。在实践过程中，还需要建立<span style="font-family: Calibri">CA</span>服务器，通过向<span style="font-family: Calibri">CA</span>服务器发送<span style="font-family: Calibri">Certificate Request</span>来获得客户端证书，这些大家可以参看文档：</span>

<a href="https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh464962.aspx"><span style="color: #0563c1;font-family: Calibri;font-size: small">https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh464962.aspx</span></a>

<span style="font-family: Calibri;font-size: small"> </span>]]></content:encoded>
    </item>
    <item>
      <title>流媒体发布--在Windows Store应用中使用Azure媒体服务之三</title>
      <link>https://docs.microsoft.com/archive/blogs/hanxia/windows-storeazure</link>
      <pubDate>Fri, 28 Nov 2014 00:53:00 GMT</pubDate>
      <dc:creator><![CDATA[Han Xia]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/hanxia/2014/11/28/windows-storeazure/</guid>
      <description><![CDATA[经过前面的努力，我们终于完成了视频文件的上传和编码，那么接下来的最后一步就是将如何该视频发布出去了。相对应前面的步骤，这一次就要简单的多了。
&nbsp;首先，在确认编码已经成功之后，我们可以获得输...]]></description>
      <content:encoded><![CDATA[<p><span style="font-size: small;">经过<a href="https://blogs.msdn.com/b/hanxia/archive/2014/10/31/windows-azure.aspx">前面</a>的努力，我们终于完成了视频文件的上传和编码，那么接下来的最后一步就是将如何该视频发布出去了。相对应前面的步骤，这一次就要简单的多了。</span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span><span style="font-size: small;">首先，在确认编码已经成功之后，我们可以获得输出的媒体的<span style="font-family: Calibri;">Asset</span>编号：</span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span>private async Task&lt;string&gt; CreateAssetFile(string accessToken, string assetId)</p>
<p align="left">{</p>
<p align="left">&nbsp;&nbsp;&nbsp; var request = (HttpWebRequest)HttpWebRequest.Create("https://wamshknclus001rest-hs.cloudapp.net/api/Files");</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Method = "POST";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.ContentType = "application/json;odata=verbose";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Accept = "application/json;odata=verbose";</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; string requestbody =</p>
<p align="left">&nbsp;&nbsp;&nbsp; "{\"Name\":\"test.wmv\", \"ContentFileSize\":\"0\",\"MimeType\":\"video/x-ms-wmv\",\"ParentAssetId\":\"" + assetId + "\"}";</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["DataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["MaxDataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["x-ms-version"] = "2.7";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["Authorization"] = "Bearer " + accessToken;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var requestBytes = Encoding.UTF8.GetBytes(requestbody);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var requestStream = await request.GetRequestStreamAsync();</p>
<p align="left">&nbsp;&nbsp;&nbsp; await requestStream.WriteAsync(requestBytes, 0, requestBytes.Length);</p>
<p align="left">&nbsp;&nbsp;&nbsp; await requestStream.FlushAsync();</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var response = await request.GetResponseAsync();</p>
<p align="left">&nbsp;&nbsp;&nbsp; var responseStream = response.GetResponseStream();</p>
<p align="left">&nbsp;&nbsp;&nbsp; var stream = new StreamReader(responseStream);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var returnBody = stream.ReadToEnd();</p>
<p align="left">&nbsp;&nbsp;&nbsp; JObject responseJsonObject = JObject.Parse(returnBody);</p>
<p align="left">&nbsp;&nbsp;&nbsp; var d = responseJsonObject["d"];</p>
<p align="left">&nbsp;&nbsp;&nbsp; return d.Value&lt;string&gt;("Id");</p>
<p>}</p>
<p>然后我们需要得到一个可以下载该Asset的policy编号</p>
<p align="left">private async Task&lt;string&gt; DownloadAccessPolicy(string accessToken)</p>
<p align="left">{</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var request = (HttpWebRequest)HttpWebRequest.Create("https://wamshknclus001rest-hs.cloudapp.net/api/AccessPolicies");</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.Method = "POST";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.ContentType = "application/json;odata=verbose";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.Accept = "application/json;odata=verbose";</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string name = "DownloadPolicy" + DateTime.UtcNow.ToString("s") + "Z";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string dura = "3600";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int permission = 1;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var requestbody = "{ \"Name\" : \"" + name + "\", \"DurationInMinutes\" : \"" + dura + "\", \"Permissions\" : " + permission + "}";</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.Headers["DataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.Headers["MaxDataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.Headers["x-ms-version"] = "2.5";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;request.Headers["Authorization"] = "Bearer " + accessToken;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var requestBytes = Encoding.UTF8.GetBytes(requestbody);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var requestStream = await request.GetRequestStreamAsync();</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await requestStream.WriteAsync(requestBytes, 0, requestBytes.Length);</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await requestStream.FlushAsync();</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var response = await request.GetResponseAsync();</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var responseStream = response.GetResponseStream();</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var stream = new StreamReader(responseStream);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var returnBody = stream.ReadToEnd();</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var responseJsonObject = JObject.Parse(returnBody);</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var d = responseJsonObject["d"];</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return d.Value&lt;string&gt;("Id");</p>
<p>}</p>
<p>根据这个Asset编号以及相应的Policy编号，就可以得到Stream的路径了：</p>
<p align="left">public async Task&lt;string&gt; GetOriginURL(string policyId, string assetId, string accessToken)</p>
<p align="left">{</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://wamshknclus001rest-hs.cloudapp.net/api/Locators");</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.Method = "POST";</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.ContentType = "application/json;odata=verbose";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.Accept = "application/json;odata=verbose";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string starttime = DateTime.UtcNow.ToString("s") + "Z";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int type = 2;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String requestbody = "{ \"AccessPolicyId\" : \"" + policyId + "\", \"AssetId\" : \"" + assetId + "\", \"StartTime\" : \"" + starttime + "\", \"Type\" : " + type + "}";</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.Headers["DataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.Headers["MaxDataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.Headers["x-ms-version"] = "2.5";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; request.Headers["Authorization"] = "Bearer " + accessToken;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var requestBytes = Encoding.UTF8.GetBytes(requestbody);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var requestStream = await request.GetRequestStreamAsync();</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await requestStream.WriteAsync(requestBytes, 0, requestBytes.Length);</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await requestStream.FlushAsync();</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WebResponse response = await request.GetResponseAsync();</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Stream responseStream = response.GetResponseStream();</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StreamReader stream = new StreamReader(responseStream);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var returnBody = stream.ReadToEnd();</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JObject responseJsonObject = JObject.Parse(returnBody);</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var d = responseJsonObject["d"];</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return d.Value&lt;string&gt;("Path");</p>
<p>}</p>
<p>这个路径是不包含文件名的，另外，为了支持不同的平台，我们需要在该路径后面加上不同的manifest标志，由于我们前面是将媒体文件编码为Smooth Streaming流的，所以这里只需要加上manifest就可以了，最终在Button_Click中的代码就是：</p>
<p align="left">var outputAssetId=await GetOutputAssetId(Id,accessToken);</p>
<p align="left">policyId = await DownloadAccessPolicy(accessToken);</p>
<p align="left">var streamPath = await GetOriginURL(policyId, outputAssetId, accessToken);</p>
<p>var streamFullPath=streamPath+"Wildlife.ism/manifest";</p>
<p>这里得到的streamFullPath就是可以用来播放的流媒体路径了。你可以把它放到任意一个支持Smooth Streaming流媒体的播放器中播放。那么在Windows Store应用中该如何播放呢？其实微软已经提供了支持Smooth Streaming的Windows Store应用控件，你可以首先下载Microsoft Smooth Streaming Client SDK for Windows 8.1：</p>
<p><a href="https://visualstudiogallery.msdn.microsoft.com/0170c67c-c183-4fee-8dd4-c2b44d710d40"><span style="color: #0000ff; font-family: Calibri; font-size: small;">https://visualstudiogallery.msdn.microsoft.com/0170c67c-c183-4fee-8dd4-c2b44d710d40</span></a></p>
<p><span style="font-size: small;">以及<span style="font-family: Calibri;">Player Framework for Windows and WP v2.0:</span></span></p>
<p><a href="https://playerframework.codeplex.com/releases"><span style="color: #0000ff; font-family: Calibri; font-size: small;">https://playerframework.codeplex.com/releases</span></a></p>
<p><span style="font-size: small;">然后在您的应用的<span style="font-family: Calibri;">Reference</span>中添加三个引用：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Microsoft Smooth Streaming Client SDK For Windows 8.1</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Microsoft Player Framework</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Microsoft Player Framework Adaptive Streaming Plugin</span></span></p>
<p><span style="font-size: small;">注意，在添加了这三个引用以后，您的应用就不能使用<span style="font-family: Calibri;">Any CPU</span>了，而必须的显式的设置<span style="font-family: Calibri;">x86</span>或者<span style="font-family: Calibri;">x64</span>。</span></p>
<p><span style="font-size: small;">然后在<span style="font-family: Calibri;">Toolbox</span>里面就可以看到一个新的控件<span style="font-family: Calibri;">MediaPlayer</span>，将该<span style="font-family: Calibri;">Media Player</span>加到我们的<span style="font-family: Calibri;">XAML</span>文件中</span></p>
<p>&lt;PlayerFramework:MediaPlayer x:Name="mediaPlayer"/&gt;</p>
<p><span style="font-size: small;">然后在后台的<span style="font-family: Calibri;">Button_Click</span>的事件处理函数中添加代码来使<span style="font-family: Calibri;">Media player</span>支持<span style="font-family: Calibri;">Smooth Streaming </span>并且设置它的<span style="font-family: Calibri;">Source</span>，这样我们得到的流媒体才可以在<span style="font-family: Calibri;">Windows Store</span>应用中播放：</span></p>
<p align="left">var adaptivePlugin = new Microsoft.PlayerFramework.Adaptive.AdaptivePlugin();</p>
<p align="left">mediaPlayer.Plugins.Add(adaptivePlugin);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p align="left">mediaPlayer.Source = new Uri(streamFullPath);</p>
<p>mediaPlayer.Play();</p>
<p><span style="font-size: small;">终于，所有的工作都已经大告成功，我们现在可以享受<span style="font-family: Calibri;">Smooth Streaming</span>的流畅播放体验了。当然不管是<span style="font-family: Calibri;">Azure Mobile</span>以及<span style="font-family: Calibri;">Microsoft Player Framework</span>都是支持<span style="font-family: Calibri;">HLS</span>以及<span style="font-family: Calibri;">MPEG DASH</span>，所以如果你想采用这两种格式的话也是完全没问题的，区别就在于编码时格式选择的不同以及</span>streamFullPath中manifest后缀的不同，有兴趣的朋友可以去自己尝试一下，这里我不重复了。</p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>]]></content:encoded>
    </item>
    <item>
      <title>媒体编码--在Windows Store应用中使用Azure媒体服务之二</title>
      <link>https://docs.microsoft.com/archive/blogs/hanxia/windows-storeazure-2</link>
      <pubDate>Fri, 31 Oct 2014 02:19:00 GMT</pubDate>
      <dc:creator><![CDATA[Han Xia]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/hanxia/2014/10/31/windows-storeazure-2/</guid>
      <description><![CDATA[在上一篇文章里，我们已经了解了如何使用REST...]]></description>
      <content:encoded><![CDATA[<p><span style="font-size: small;">在<a title="上一篇文章" href="https://blogs.msdn.com/b/hanxia/archive/2014/09/30/windows-store-windows-azure.aspx">上一篇文章</a>里，我们已经了解了如何使用<span style="font-family: Calibri;">REST API</span>将多媒体数据上传给<span style="font-family: Calibri;">Azure</span>的媒体服务，但这只是万里长征走完了第一步，为了能够使用多媒体数据，我们需要使用<span style="font-family: Calibri;">Azure</span>强大的处理功能将数据编码成我们合适的格式，然后分发出去，在这一篇里我们就来讨论多媒体数据编码的问题。</span></p>
<p><span style="font-size: small;">使用用<span style="font-family: Calibri;">REST API</span>，我们可以将任意格式的文件上传到服务器上，但是为了使媒体数据能够正确的发布，<span style="font-family: Calibri;">Azure Media</span>需要安装了合适的编码器来做编解码的工作。<span style="font-family: Calibri;">Azure Media</span>上已经内置了缺省的编码器，可以支持市面上主流的流媒体格式，同时，第三方也可以为<span style="font-family: Calibri;">Azure Media</span>开发自己的编码器。</span></p>
<p><span style="font-size: small;">上一篇文章中我们已经把媒体文件上传到<span style="font-family: Calibri;">Azure Media</span>服务器上，但是如果需要<span style="font-family: Calibri;">encoder</span>的话，这个上传还是不完全的，我们需要在上传之前建立一个<span style="font-family: Calibri;">Asset</span>文件：</span></p>
<p align="left">private async Task&lt;string&gt; CreateAssetFile(string accessToken, string assetId)</p>
<p align="left">{</p>
<p align="left">&nbsp;&nbsp;&nbsp; var request = (HttpWebRequest)HttpWebRequest.Create("https://wamshknclus001rest-hs.cloudapp.net/api/Files");</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Method = "POST";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.ContentType = "application/json;odata=verbose";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Accept = "application/json;odata=verbose";</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; string requestbody =</p>
<p align="left">&nbsp;&nbsp;&nbsp; "{\"Name\":\"test.wmv\", \"ContentFileSize\":\"0\",\"MimeType\":\"video/x-ms-wmv\",\"ParentAssetId\":\"" + assetId + "\"}";</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["DataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["MaxDataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["x-ms-version"] = "2.7";</p>
<p align="left">&nbsp; &nbsp;&nbsp;request.Headers["Authorization"] = "Bearer " + accessToken;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var requestBytes = Encoding.UTF8.GetBytes(requestbody);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var requestStream = await request.GetRequestStreamAsync();</p>
<p align="left">&nbsp;&nbsp;&nbsp; await requestStream.WriteAsync(requestBytes, 0, requestBytes.Length);</p>
<p align="left">&nbsp;&nbsp;&nbsp; await requestStream.FlushAsync();</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var response = await request.GetResponseAsync();</p>
<p align="left">&nbsp;&nbsp;&nbsp; var responseStream = response.GetResponseStream();</p>
<p align="left">&nbsp;&nbsp;&nbsp; var stream = new StreamReader(responseStream);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var returnBody = stream.ReadToEnd();</p>
<p align="left">&nbsp;&nbsp;&nbsp; JObject responseJsonObject = JObject.Parse(returnBody);</p>
<p align="left">&nbsp;&nbsp;&nbsp; var d = responseJsonObject["d"];</p>
<p align="left">&nbsp;&nbsp;&nbsp; return d.Value&lt;string&gt;("Id");</p>
<p>}</p>
<p>然后在上传之后更新这个Asset文件信息：</p>
<p align="left">private async Task MergeAssetFile(string accessToken, string fileId, string assetId, ulong size)</p>
<p align="left">{</p>
<p align="left">&nbsp;&nbsp;&nbsp; var request = (HttpWebRequest)HttpWebRequest.Create("https://wamshknclus001rest-hs.cloudapp.net/api/Files('"+fileId+"')");</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Method = "MERGE";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.ContentType = "application/json;odata=verbose";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Accept = "application/json;odata=verbose";</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; string requestbody = "{\"ContentFileSize\":\""+size+"\",\"MimeType\":\"video/x-ms-wmv\",\"Name\":\"test.wmv\",\"ParentAssetId\":\"" + assetId + "\"}";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["DataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["MaxDataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["x-ms-version"] = "2.7";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["Authorization"] = "Bearer " + accessToken;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var requestBytes = Encoding.UTF8.GetBytes(requestbody);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var requestStream = await request.GetRequestStreamAsync();</p>
<p align="left">&nbsp;&nbsp;&nbsp; await requestStream.WriteAsync(requestBytes, 0, requestBytes.Length);</p>
<p align="left">&nbsp;&nbsp;&nbsp; await requestStream.FlushAsync();</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var response = await request.GetResponseAsync();</p>
<p align="left">&nbsp;</p>
<p align="left">}</p>
<p><span style="font-size: small;">这样才算让<span style="font-family: Calibri;">Azure Media</span>真正的得到上传的媒体文件的信息。接下来就可以进行编码工作了。我们首先获得一个编码处理单元，</span>这里我们<span style="font-size: small;">选择的处理单元是<span style="font-family: Calibri;">Windows Azure Media Encoder:</span></span></p>
<p align="left">public async Task&lt;String&gt; GetProcessorId(string accessToken)</p>
<p align="left">{</p>
<p align="left">&nbsp;&nbsp;&nbsp; HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://wamshknclus001rest-hs.cloudapp.net/api/MediaProcessors()?$filter=Name%20eq%20'Windows%20Azure%20Media%20Encoder'");</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Method = "GET";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.ContentType = "application/json;odata=verbose";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Accept = "application/json;odata=verbose";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["DataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["MaxDataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["x-ms-version"] = "2.5";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["Authorization"] = "Bearer " + accessToken;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; WebResponse response = await request.GetResponseAsync();</p>
<p align="left">&nbsp;&nbsp;&nbsp; Stream responseStream = response.GetResponseStream();</p>
<p align="left">&nbsp;&nbsp;&nbsp; StreamReader stream = new StreamReader(responseStream);</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var returnBody = stream.ReadToEnd();</p>
<p align="left">&nbsp;&nbsp;&nbsp; JObject responseJsonObject = JObject.Parse(returnBody);</p>
<p align="left">var results = responseJsonObject["d"]["results"][0];</p>
<p align="left">&nbsp;&nbsp;&nbsp; return results.Value&lt;string&gt;("Id");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p>}</p>
<p><span style="font-size: small;">在得到了编码处理单元以后，我们就可以创建一个编码任务了<span style="font-family: Calibri;">:</span></span></p>
<p align="left">public async Task&lt;String&gt; CreateEncodeJob(string jobname, string assetId, string processorId, string accessToken)</p>
<p align="left">{</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var assertURI = "https://media.windows.net/api/Assets('" + assetId + "')";</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://wamshknclus001rest-hs.cloudapp.net/api/Jobs");</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Method = "POST";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.ContentType = "application/json;odata=verbose";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Accept = "application/json;odata=verbose";</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; string name = "UploadPolicy" + DateTime.UtcNow.ToString("s") + "Z";</p>
<p align="left">&nbsp;&nbsp;&nbsp; string dura = "300";</p>
<p align="left">&nbsp;&nbsp;&nbsp; int permission = 2;</p>
<p align="left">&nbsp;&nbsp;&nbsp; String requestbody = "{ \"Name\" : \"" + name + "\"," +</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; " \"DurationInMinutes\" : \"" + dura + "\", " +</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" \"Permissions\" : " + permission + "}";</p>
<p align="left">&nbsp;&nbsp;&nbsp; requestbody ="{\"Name\" : \"" + jobname +&nbsp; "\"," +</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; " \"InputMediaAssets\" : [{\"__metadata\" : {\"uri\" : \"" + assertURI + "\"}}]," +</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; " \"Tasks\" : [{\"Configuration\" : \"H264 Smooth Streaming 720p\"," +</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; " \"MediaProcessorId\" : \"" + processorId + "\"," +</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; " \"TaskBody\" : \"&lt;?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"?&gt;&lt;taskBody&gt;&lt;inputAsset&gt;JobInputAsset(0)&lt;/inputAsset&gt;&lt;outputAsset&gt;JobOutputAsset(0)&lt;/outputAsset&gt;&lt;/taskBody&gt;\"}]}";</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["DataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["MaxDataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["x-ms-version"] = "2.2";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["Authorization"] = "Bearer " + accessToken;</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var requestBytes = Encoding.UTF8.GetBytes(requestbody);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var requestStream = await request.GetRequestStreamAsync();</p>
<p align="left">&nbsp;&nbsp;&nbsp; await requestStream.WriteAsync(requestBytes, 0, requestBytes.Length);</p>
<p align="left">&nbsp;&nbsp;&nbsp; await requestStream.FlushAsync();</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var response = await request.GetResponseAsync();</p>
<p align="left">&nbsp;&nbsp;&nbsp; var responseStream = response.GetResponseStream();</p>
<p align="left">&nbsp;&nbsp;&nbsp; var stream = new StreamReader(responseStream);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var returnBody = stream.ReadToEnd();</p>
<p align="left">&nbsp;&nbsp;&nbsp; JObject responseJsonObject = JObject.Parse(returnBody);</p>
<p align="left">&nbsp;&nbsp;&nbsp; var d = responseJsonObject["d"];</p>
<p align="left">&nbsp;&nbsp;&nbsp; return d.Value&lt;string&gt;("Id");</p>
<p>}</p>
<p><span style="font-size: small;">这里我们选择将媒体文件编码为<span style="font-family: Calibri;">H264 Smooth Streaming 720p</span>。该流媒体格式是主要针对<span style="font-family: Calibri;">Windows</span>平台的，<span style="font-family: Calibri;">Azure Mobile</span>也提供了跨平台的支持，比如说<span style="font-family: Calibri;">HLS</span>或者<span style="font-family: Calibri;">MPEG-DASH</span>。</span></p>
<p><span style="font-size: small;">在创建完成编码任务以后，<span style="font-family: Calibri;">Azure Media</span>就开始在后台进行编码工作了。您可以等待一会儿，或者在你需要的时候查询编码状态，如果编码完成的话就换返回状态值<span style="font-family: Calibri;">3.</span>首先我们实现查询编码状态的函数：</span></p>
<p align="left">public async Task&lt;int&gt; GetJobState(string Id, string accessToken)</p>
<p align="left">{</p>
<p align="left">&nbsp;&nbsp;&nbsp; String uir = "https://wamshknclus001rest-hs.cloudapp.net/api/Jobs('" + Id + "')/State";</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uir);</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Method = "GET";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.ContentType = "application/json;odata=verbose";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Accept = "application/json;odata=verbose";</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["DataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["MaxDataServiceVersion"] = "3.0";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["x-ms-version"] = "2.5";</p>
<p align="left">&nbsp;&nbsp;&nbsp; request.Headers["Authorization"] = "Bearer " + acsBearerToken;</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; WebResponse response = await request.GetResponseAsync();</p>
<p align="left">&nbsp;&nbsp;&nbsp; Stream responseStream = response.GetResponseStream();</p>
<p align="left">&nbsp;&nbsp;&nbsp; StreamReader stream = new StreamReader(responseStream);</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var returnBody = stream.ReadToEnd();</p>
<p align="left">&nbsp;&nbsp;&nbsp; JObject responseJsonObject = JObject.Parse(returnBody);</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var d = responseJsonObject["d"];</p>
<p align="left">&nbsp;&nbsp;&nbsp; return d.Value&lt;int&gt;("State");</p>
<p>}</p>
<p>&nbsp;</p>
<p><span style="font-size: small;">然后我们在前一篇文章的<span style="font-family: Calibri;">Button_Click</span>事件处理函数中添加代码如下：</span></p>
<p align="left">private async void Button_Click(object sender, RoutedEventArgs e)</p>
<p align="left">{</p>
<p align="left">&nbsp;&nbsp;&nbsp; StorageFolder library = Windows.Storage.KnownFolders.VideosLibrary;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var video = await library.GetFileAsync("test.wmv");</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; string accessToken = await GetAccessToken();</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; string assetId = await CreateAsset(accessToken);</p>
<p align="left">&nbsp;&nbsp;&nbsp; string fileId = await CreateAssetFile(accessToken, assetId);</p>
<p align="left">&nbsp;&nbsp;&nbsp; string policyId = await UpdateAccessPolicy(accessToken);</p>
<p align="left">&nbsp;&nbsp;&nbsp; string containerName = await GetContainerName(policyId, assetId, accessToken);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; //use storage API to upload</p>
<p align="left">&nbsp;&nbsp;&nbsp; StorageCredentials credentials = new StorageCredentials(storageName, storageKey);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var storageAccount = new CloudStorageAccount(credentials, false);</p>
<p align="left">&nbsp;&nbsp;&nbsp; var cloudBlobClient = storageAccount.CreateCloudBlobClient();</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var container = cloudBlobClient.GetContainerReference(containerName);</p>
<p align="left">&nbsp;&nbsp;&nbsp; await container.CreateIfNotExistsAsync();</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; CloudBlockBlob blockBlob = container.GetBlockBlobReference("Wildlife.wmv");</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; await blockBlob.UploadFromFileAsync(video);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var bp = await video.GetBasicPropertiesAsync();</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; await MergeAssetFile(accessToken, fileId, assetId, bp.Size);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; var processorId = await GetProcessorId(accessToken);</p>
<p align="left">&nbsp;&nbsp;&nbsp; var Id = await CreateEncodeJob("testJob", assetId, processorId, accessToken);</p>
<p align="left">&nbsp;</p>
<p align="left">&nbsp;&nbsp;&nbsp; int jobState = 0;</p>
<p align="left">&nbsp;&nbsp;&nbsp; while (jobState != 3)</p>
<p align="left">&nbsp;&nbsp;&nbsp; {</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jobState = await GetJobState(Id, accessToken);</p>
<p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await Task.Delay(TimeSpan.FromSeconds(2));</p>
<p align="left">&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p><span style="font-size: small;">这里我们通过轮询编码任务的状态来等待任务结束，为了避免等待过于频繁，我这边设定每次轮询时间为<span style="font-family: Calibri;">5</span>秒。</span></p>
<p><span style="font-size: small;">这样我们已经完成了媒体文件的编码了，执行上述代码，当任务完成后，我们可以到<span style="font-family: Calibri;">Azure Portal</span>上查看结果，看到这个媒体文件的编码已经成功了。</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/1452.result.jpg" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/1452.result.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/1452.result.jpg" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/1452.result.jpg" alt="" width="607" height="53" border="0" /></a></p>
<p><span style="font-size: small;">在这一篇中，我们实现了对媒体文件的编码，后面我还会继续讲如何发布编码后的流媒体，尽请关注。</span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>]]></content:encoded>
    </item>
    <item>
      <title>媒体上传--在Windows Store应用中使用Azure媒体服务之一</title>
      <link>https://docs.microsoft.com/archive/blogs/hanxia/windows-storeazure-3</link>
      <pubDate>Tue, 30 Sep 2014 06:26:00 GMT</pubDate>
      <dc:creator><![CDATA[Han Xia]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/hanxia/2014/09/30/windows-storeazure-3/</guid>
      <description><![CDATA[Windows Azure的媒体服务预览已经于8月15日在中国上线，我已经迫不及待的想在在Windows Store应用中加入媒体服务功能带来的便利了。可是，一个很让人沮丧的事情是，目前的Azure...]]></description>
      <content:encoded><![CDATA[<p><span style="font-size: small;"><span style="font-family: Calibri;">Windows Azure</span>的媒体服务预览已经于<span style="font-family: Calibri;">8</span>月<span style="font-family: Calibri;">15</span>日在中国上线，我已经迫不及待的想在在<span style="font-family: Calibri;">Windows Store</span>应用中加入媒体服务功能带来的便利了。可是，一个很让人沮丧的事情是，目前的<span style="font-family: Calibri;">Azure Media</span>的<span style="font-family: Calibri;">SDK</span>还不支持<span style="font-family: Calibri;">windows Store</span>应用。不过没关系，我们还有<span style="font-family: Calibri;">REST API</span>可以使用，这里我就讲解一下如何在<span style="font-family: Calibri;">Windows store</span>应用中完成媒体文件的上传，编码及发布。在这一篇<span style="font-family: Calibri;">blog</span>中，我们首先来看一下媒体文件的上传。</span></p>
<p><span style="font-size: small;">为了使用<span style="font-family: Calibri;">Azure Media</span>，你首先需要在<span style="font-family: Calibri;">Azure</span>上创建一个媒体服务账号。该账号用以提供访问该媒体服务所需的信息。下面是创建媒体服务账号的步骤：</span></p>
<p><span style="font-family: Calibri; font-size: small;">1.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: small;">登录进入<span style="font-family: Calibri;">Azure</span>管理门户：</span></p>
<p><a href="https://manage.windowsazure.com/"><span style="color: #0563c1; font-size: small;">https://manage.windowsazure.com/</span></a></p>
<p><span style="font-size: small;">如果你还没有Windows Azure的账号,那么有两种办法可以免费试用Windows Azure:</span></p>
<p><span style="font-size: small;">a.</span>&nbsp; <span style="font-size: small;">因为Windows Azure已经在中国落地，你可以到</span><a href="https://www.windowsazure.cn/"><span style="color: #0563c1; font-size: small;">https://www.windowsazure.cn</span></a><span style="font-size: small;">上去申请试用，这段时间1元试用可是有大礼包送的哦。</span></p>
<p><span style="font-size: small;">b.</span>&nbsp; <span style="font-size: small;">你可以登录全球的Windows Azure网站</span><a href="https://www.windowsazure.com/"><span style="color: #0563c1; font-size: small;">https://www.windowsazure.com</span></a><span style="font-size: small;">来建立一个试用账号。在建立使用账号过程中如果无法进行电话或者短信验证的话，可以到以下网址提交一个问题来要求免去电话和短信验证步骤：</span></p>
<p><a href="https://support.microsoft.com/oas/default.aspx?prid=14238&amp;ln=en-us&amp;st=1&amp;wfxredirect=1"><span style="color: #0563c1;">https://support.microsoft.com/oas/default.aspx?prid=14238&amp;ln=en-us&amp;st=1&amp;wfxredirect=1</span></a></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-size: small;">在提交问题的时候，你需要设定如下支持类型</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/0714.1.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/0714.1.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/0714.1.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/0714.1.png" alt="" border="0" /></a></p>
<p><span style="font-size: small;">然后再后续的邮件中再描述一下你遇到的问题，要求免去电话和短信验证步骤就可以了。</span></p>
<p><span style="font-size: small;">2.</span>&nbsp; <span style="font-size: small;">点击新建-&gt;应用服务-&gt;媒体服务-&gt;快速创建，输入所需的名称，选择媒体服务存放的地点以及存储服务的账户。如果你之前没有建立存储服务的账户的话，你也可以选择&ldquo;新建存储账户&rdquo;。然后点击&ldquo;创建媒体服务&rdquo;就创建了一个媒体服务账号：</span></p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/0535.2.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/0535.2.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/0535.2.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/0535.2.png" alt="" border="0" /></a>&nbsp;</p>
<p><span style="font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri; font-size: small;">3.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="font-size: small;">点击新创建的媒体服务账号<span style="font-family: Calibri;">-&gt;</span>管理密钥，你就可以获得访问该媒体服务所需的账户名称以及访问密钥，请将这两个字符串拷贝出来，后面我们会在编程的时候使用到：</span></p>
<ol><ol>&nbsp;</ol></ol>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/3513.3.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/3513.3.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/3513.3.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/3513.3.png" alt="" border="0" /></a></p>
<p><span style="font-size: small;">在配置完Azure Media以后，我们就可以在Windows Store应用程序中通过REST API来传文件了。</span></p>
<p><span style="font-size: small;">首先，我们创建一个新的Windows Store应用，然后为项目添加两个引用：</span></p>
<p>Microsoft.WindowsAzure.Storage</p>
<p>Newtonsoft.Json</p>
<p>&nbsp;</p>
<p><span style="font-size: small;">为了演示视频文件的上传，我们可以在示例代码中加入一个Button,然后在Button的处理函数中添加相关的代码。我们需要定义两个string来存放前面获得的Account Name和Access Key。另外，我们还需要定义两个string来存放对应的存储账户的Account Name和Access Key以便于后续对存储容器的直接访问。对应的存储账户信息可以从媒体服务的仪表板中获得：</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/3240.4.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/3240.4.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/3240.4.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/3240.4.png" alt="" border="0" /></a></p>
<p><span style="font-size: small;">接下来就是具体的实现代码了，对于要发送的<span style="font-family: Calibri;">http</span>报文的内容，下面的<span style="font-family: Calibri;">MSDN</span>链接已经有详细的描述，我这边就不再重复了：</span></p>
<p><a href="https://msdn.microsoft.com/en-us/library/azure/jj129593.aspx"><span style="color: #0563c1; font-family: Calibri; font-size: small;">https://msdn.microsoft.com/en-us/library/azure/jj129593.aspx</span></a></p>
<p><span style="font-size: small;">根据上述的描述，我们首先要获得一个<span style="font-family: Calibri;">Access Token</span>，代码如下：</span></p>
<p>public class AcsToken</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; public string token_type { get; set;}</p>
<p>&nbsp;&nbsp;&nbsp; public string access_token{ get;set;}</p>
<p>&nbsp;&nbsp;&nbsp; public int expires_in {get;set;}</p>
<p>&nbsp;&nbsp;&nbsp; public string scope { get;set;}</p>
<p>}</p>
<p>&nbsp;</p>
<p>private async Task&lt;string&gt; GetAccessToken()</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; string scope = "urn:WindowsAzureMediaServices";</p>
<p>&nbsp;&nbsp;&nbsp; string accessControlServiceUri = "https://wamsprodglobal001acs.accesscontrol.windows.net/v2/OAuth2-13";</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(accessControlServiceUri);</p>
<p>&nbsp;&nbsp;&nbsp; request.Method = "POST";</p>
<p>&nbsp;&nbsp;&nbsp; request.ContentType = "application/x-www-form-urlencoded";</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; var requestBytes = Encoding.UTF8.GetBytes("grant_type=client_credentials&amp;client_id=" +</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mediaServicesAccountName + "&amp;client_secret=" + System.Net.WebUtility.UrlEncode(mediaServicesAccountKey) +</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "&amp;scope=" + System.Net.WebUtility.UrlEncode(scope));</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; var requestStream = await request.GetRequestStreamAsync();</p>
<p>&nbsp;&nbsp;&nbsp; await requestStream.WriteAsync(requestBytes, 0, requestBytes.Length);</p>
<p>&nbsp;&nbsp;&nbsp; await requestStream.FlushAsync();</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; WebResponse response = await request.GetResponseAsync();</p>
<p>&nbsp;&nbsp;&nbsp; Stream responseStream = response.GetResponseStream();</p>
<p>&nbsp;&nbsp;&nbsp; DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(AcsToken));</p>
<p>&nbsp;&nbsp;&nbsp; var acsToken = (AcsToken)ser.ReadObject(responseStream);</p>
<p>&nbsp;&nbsp;&nbsp; return acsToken.access_token;</p>
<p>}</p>
<p>&nbsp;&nbsp;</p>
<p>在得到访问令牌后，就可以创建Asset了：</p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">private async Task&lt;string&gt; CreateAsset(string accessToken)</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">{</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var request = (HttpWebRequest)HttpWebRequest.Create("https://wamshknclus001rest-hs.cloudapp.net/api/Assets");</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Method = "POST";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.ContentType = "application/json;odata=verbose";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Accept = "application/json;odata=verbose";</span></span></p>
<p>&nbsp;</p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; string assertName = "myAssert";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; string requestbody = "{ \"Name\" : \"" + assertName + "\" }";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Headers["DataServiceVersion"] = "3.0";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Headers["MaxDataServiceVersion"] = "3.0";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Headers["x-ms-version"] = "2.5";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Headers["Authorization"] = "Bearer " + accessToken;</span></span></p>
<p>&nbsp;</p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var requestBytes = Encoding.UTF8.GetBytes(requestbody);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var requestStream = await request.GetRequestStreamAsync();</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; await requestStream.WriteAsync(requestBytes, 0, requestBytes.Length);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; await requestStream.FlushAsync();</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var response = await request.GetResponseAsync();</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var responseStream = response.GetResponseStream();</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var stream = new StreamReader(responseStream);</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var returnBody = stream.ReadToEnd();</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; JObject responseJsonObject = JObject.Parse(returnBody);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var d = responseJsonObject["d"];</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; return d.Value&lt;string&gt;("Id");</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">}</span></span></p>
<p><span style="font-size: small;">接下来我们需要更新<span style="font-family: Calibri;">Access Policy</span>，允许对<span style="font-family: Calibri;">Asset</span>进行写操作：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">private async Task&lt;string&gt; UpdateAccessPolicy(string accessToken)</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">{</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var request = (HttpWebRequest)HttpWebRequest.Create("https://wamshknclus001rest-hs.cloudapp.net/api/AccessPolicies");</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Method = "POST";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.ContentType = "application/json;odata=verbose";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Accept = "application/json;odata=verbose";</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; string name = "UploadPolicy" + DateTime.UtcNow.ToString("s") + "Z";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; string dura = "300";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; int permission = 2;</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var requestbody = "{ \"Name\" : \"" + name + "\", \"DurationInMinutes\" : \"" + dura + "\", \"Permissions\" : " + permission + "}";</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Headers["DataServiceVersion"] = "3.0";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Headers["MaxDataServiceVersion"] = "3.0";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Headers["x-ms-version"] = "2.5";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Headers["Authorization"] = "Bearer " + accessToken;</span></span></p>
<p>&nbsp;</p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var requestBytes = Encoding.UTF8.GetBytes(requestbody);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var requestStream = await request.GetRequestStreamAsync();</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; await requestStream.WriteAsync(requestBytes, 0, requestBytes.Length);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; await requestStream.FlushAsync();</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var response = await request.GetResponseAsync();</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var responseStream = response.GetResponseStream();</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var stream = new StreamReader(responseStream);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var returnBody = stream.ReadToEnd();</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var responseJsonObject = JObject.Parse(returnBody);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var d = responseJsonObject["d"];</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; return d.Value&lt;string&gt;("Id");</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">}</span></span></p>
<p><span style="font-size: small;">得到了所获得的权限以后，我们就可以通过<span style="font-family: Calibri;">Assert</span>的存储容器来上传媒体文件了。首先来获得<span style="font-family: Calibri;">Asset</span>对用的存储容器的名称：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">private async Task&lt;string&gt; GetContainerName(string policyId, string assetId, string accessToken)</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">{</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var request = (HttpWebRequest)HttpWebRequest.Create("https://wamshknclus001rest-hs.cloudapp.net/api/Locators");</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Method = "POST";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.ContentType = "application/json;odata=verbose";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Accept = "application/json;odata=verbose";</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; string starttime = DateTime.UtcNow.AddMinutes(-5).ToString("s") + "Z";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; int type = 1;</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var requestbody = "{ \"AccessPolicyId\" : \"" + policyId + "\", \"AssetId\" : \"" + assetId + "\", \"StartTime\" : \"" + starttime + "\", \"Type\" : " + type + "}";</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Headers["MaxDataServiceVersion"] = "3.0";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Headers["x-ms-version"] = "2.5";</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; request.Headers["Authorization"] = "Bearer " + accessToken;</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var requestBytes = Encoding.UTF8.GetBytes(requestbody);</span></span></p>
<p>&nbsp;</p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var requestStream = await request.GetRequestStreamAsync();</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; await requestStream.WriteAsync(requestBytes, 0, requestBytes.Length);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; await requestStream.FlushAsync();</span></span></p>
<p>&nbsp;</p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var response = await request.GetResponseAsync();</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var responseStream = response.GetResponseStream();</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; &nbsp;var stream = new StreamReader(responseStream);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var returnBody = stream.ReadToEnd();</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var responseJsonObject = JObject.Parse(returnBody);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var d = responseJsonObject["d"];</span></span></p>
<p>&nbsp;</p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; string path = d.Value&lt;string&gt;("Path");</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; return (new Uri(path)).Segments[1];</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">}</span></span></p>
<p><span style="font-size: small;">接下来就是通过存储服务的<span style="font-family: Calibri;">API</span>来上传视频文件了，因为存储服务的<span style="font-family: Calibri;">SDK</span>支持<span style="font-family: Calibri;">Windows Store</span>应用，所以这里就不用<span style="font-family: Calibri;">REST API</span>来实现了，直接调用<span style="font-family: Calibri;">SDK</span>提供的接口，这样整个的<span style="font-family: Calibri;">Button</span>响应函数代码如下：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">private async void Button_Click(object sender, RoutedEventArgs e)</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">{</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; StorageFolder library = Windows.Storage.KnownFolders.VideosLibrary;</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var video = await library.GetFileAsync("Wildlife.wmv");</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; string accessToken = await GetAccessToken();</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; string assetId = await CreateAsset(accessToken);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; string policyId = await UpdateAccessPolicy(accessToken);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; string containerName = await GetContainerName(policyId, assetId, accessToken);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; StorageCredentials credentials = new StorageCredentials(storageName, storageKey);</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var storageAccount = new CloudStorageAccount(credentials, false);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var cloudBlobClient = storageAccount.CreateCloudBlobClient();</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; var container = cloudBlobClient.GetContainerReference(containerName);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; await container.CreateIfNotExistsAsync();</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; CloudBlockBlob blockBlob = container.GetBlockBlobReference("videoblob");</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; await blockBlob.UploadFromFileAsync(video);</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">}</span></span></p>
<p><span style="font-size: small;">通过以上代码，我们就完成了对视频文件的上传，后续我还将讲述如何对媒体文件进行转码及播放，尽情期待。</span></p>]]></content:encoded>
    </item>
    <item>
      <title>Turn off Notification Hub in Azure Mobile Service to use Legacy Push</title>
      <link>https://docs.microsoft.com/archive/blogs/hanxia/turn-off-notification-hub-in-azure-mobile-service-to-use-legacy-push</link>
      <pubDate>Fri, 01 Aug 2014 02:17:55 GMT</pubDate>
      <dc:creator><![CDATA[JennyLi-MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/hanxia/2014/08/01/turn-off-notification-hub-in-azure-mobile-service-to-use-legacy-push/</guid>
      <description><![CDATA[Currently when you create a new Windows Azure Mobile Service to send push notification(detailed...]]></description>
      <content:encoded><![CDATA[<p>Currently when you create a new Windows Azure Mobile Service to send push notification(detailed information can be found in <a href="https://azure.microsoft.com/en-us/documentation/articles/mobile-services-javascript-backend-windows-store-dotnet-get-started-push/">Get started with push notifications in Mobile Services</a> ), &nbsp;the&nbsp; notification hub is by default integrated with your mobile service, we really want people to move away from the legacy push, however, for the rare cases where people still want to use legacy push, so how can we do that?</p>
<p>The default change to use Notification Hub for push only applies when creating a mobile service via the portal. &nbsp;We can make one with the CLI resulting in&nbsp;one not using notification hubs. If you install the <a href="https://azure.microsoft.com/en-us/documentation/articles/xplat-cli/">AzureCLI</a> and run:&nbsp; "azure mobile create &lt;appname&gt;" , and follow the prompts, you can then continue to use&nbsp;the scripts in the old way.</p>
<p>After you installed the Azure CLI, run command prompt with administrator privilege(you can do this via pressing windows logo key + X key, and choose &ldquo;Command Prompt(Admin)&rdquo;), then type following commands:</p>
<ol>
<li>azure account download , which will navigate you to the web browser to download the setting profile</li>
<li>azure account import &ldquo;path\**.publishsetting&rdquo; , which will import your azure account credential</li>
<li>azure mobile create &lt;appname&gt;</li>
</ol>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/3125.steps.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/3125.steps.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/3125.steps.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/3125.steps.png" alt="" border="0" /></a><span style="color: #1f4e79; font-family: 'Calibri','sans-serif'; font-size: 11pt; mso-fareast-font-family: 宋体; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;"><br />&nbsp;<br /> &nbsp;&nbsp;<br />&nbsp;&nbsp;<br />&nbsp;&nbsp;<br />&nbsp;&nbsp;<br />&nbsp;&nbsp;<br />&nbsp;&nbsp;<br />&nbsp;&nbsp;<br />&nbsp;&nbsp;<br />&nbsp;&nbsp;<br />&nbsp;&nbsp;<br />&nbsp;&nbsp;<br />&nbsp;&nbsp;<br />&nbsp;<br />&nbsp;<br />&nbsp;<br /><br /> <br /></span></p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>注意Windows Phone代码文件的大小</title>
      <link>https://docs.microsoft.com/archive/blogs/hanxia/windows-phone</link>
      <pubDate>Thu, 12 Jun 2014 00:54:00 GMT</pubDate>
      <dc:creator><![CDATA[Han Xia]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/hanxia/2014/06/12/windows-phone/</guid>
      <description><![CDATA[我们收到一些反馈说某些Windows...]]></description>
      <content:encoded><![CDATA[<p><span style="font-size: small;">我们收到一些反馈说某些<span style="font-family: Calibri;">Windows Phone</span>应用程序无法通过应用程序商店进行安装。最终用户在安装应用的时候可能会遇到以下错误：</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/0218.error.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/0218.error.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/0218.error.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/0218.error.png" alt="" border="0" /></a></p>
<p><span style="font-size: small;">这个问题是因为过大的程序集文件导致的。如果你在开发应用的时候将所有的代码放在一个项目中，然后生成了一个很大的<span style="font-family: Calibri;">dll</span>文件，这个<span style="font-family: Calibri;">dll</span>可能会因为内存限制无法通过安装流程。目前为止，您可以通过将该<span style="font-family: Calibri;">dll</span>分割成功多个小的<span style="font-family: Calibri;">dll</span>来解决该问题。</span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>]]></content:encoded>
    </item>
    <item>
      <title>Take care of your Windows Phone code assembly size.</title>
      <link>https://docs.microsoft.com/archive/blogs/hanxia/take-care-of-your-windows-phone-code-assembly-size</link>
      <pubDate>Thu, 12 Jun 2014 00:44:12 GMT</pubDate>
      <dc:creator><![CDATA[Han Xia]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/hanxia/2014/06/12/take-care-of-your-windows-phone-code-assembly-size/</guid>
      <description><![CDATA[It&rsquo;s reported that there are some Windows Phone applications which&nbsp;can&rsquo;t be...]]></description>
      <content:encoded><![CDATA[<p><span style="font-size: small;"><span style="font-family: Calibri;">It&rsquo;s reported that there are some Windows Phone applications which&nbsp;can&rsquo;t be deployed onto the phones via Windows Phone store. The end user may encounter following error:</span></span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/5635.error.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/5635.error.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/5635.error.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/5635.error.png" alt="" border="0" /></a></p>
<p><span style="font-size: small;"><span style="font-family: Calibri;">This problem can be caused by large size of the assembly. If you&nbsp;put all the code into one Widnwos Phone&nbsp;project and generate a single large dll. The dll might not be able to pass the deployment process because of the memory limitation. So far you can refactor the DLL into multiple smaller ones to fix this problem.</span></span></p>]]></content:encoded>
    </item>
    <item>
      <title>使用Windbg分析Windows Store 应用的崩溃问题</title>
      <link>https://docs.microsoft.com/archive/blogs/hanxia/windbgwindows-store</link>
      <pubDate>Thu, 22 May 2014 15:22:00 GMT</pubDate>
      <dc:creator><![CDATA[Han Xia]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/hanxia/2014/05/22/windbgwindows-store/</guid>
      <description><![CDATA[对于上架的windows store应用程序，微软提供了反馈机制让用户在程序崩溃的时候可以自动上传迷你转储(mini...]]></description>
      <content:encoded><![CDATA[<p><span style="font-size: small;">对于上架的<span style="font-family: Calibri;">windows store</span>应用程序，微软提供了反馈机制让用户在程序崩溃的时候可以自动上传迷你转储<span style="font-family: Calibri;">(mini dump)</span>文件。这些<span style="font-family: Calibri;">dump</span>文件对于分析用户遇到的问题是相当有帮助的，这里我就如何使用<span style="font-family: Calibri;">windbg</span>分析转储文件来做一个示范，我的目标是让即使是没用过<span style="font-family: Calibri;">windbg</span>的人也能够使用它来分析转储文件，所以如果你发现文章中哪里有让你迷惑的地方，请告诉我。:)</span></p>
<p><span style="font-size: small;">首先，你需要有<span style="font-family: Calibri;">windows debugger tools</span>，也就是<span style="font-family: Calibri;">windbg</span>。这是一个强大的系统级调试工具，它附属于<span style="font-family: Calibri;">Windows WDK</span>和<span style="font-family: Calibri;">Windows SDK</span>安装包中，<span style="font-family: Calibri;">WDK</span>这玩意儿太小众了，我们这边暂且不提，所以安装<span style="font-family: Calibri;">windbg</span>的最佳办法就是安装<span style="font-family: Calibri;">Windows SDK</span>了，<span style="font-family: Calibri;">Windows SDK</span>很大，你在安装的时候你可以选择只安装<span style="font-family: Calibri;">Debugging Tools for Windows</span>。<span style="font-family: Calibri;">Windows SDK for Windows 8.1</span>的下载地址如下：</span></p>
<p><a href="https://msdn.microsoft.com/en-US/windows/desktop/bg162891"><span style="color: #0563c1; font-family: Calibri; font-size: small;">https://msdn.microsoft.com/en-US/windows/desktop/bg162891</span></a></p>
<p><span style="font-size: small;">在安装完<span style="font-family: Calibri;">windbg</span>以后，我们就可以来分析转储文件了。但是且慢，转储文件呢，转储文件到哪里去找<span style="font-family: Calibri;">? </span>如果你找不到的话，那么请随我来。当你的<span style="font-family: Calibri;">windows store</span>应用上架以后，你就可以看到一个&ldquo;报告&rdquo;按钮。</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/1731.1.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/1731.1.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/1731.1.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/1731.1.png" alt="" border="0" /></a></p>
<p><span style="font-size: small;">点击这个&ldquo;报告&rdquo;按钮，你就可以看到应用程序的统计数据以及左边菜单栏中的&ldquo;质量&rdquo;链接：</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/0602.2.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/0602.2.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/0602.2.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/0602.2.png" alt="" width="215" height="331" border="0" /></a></p>
<p><span style="font-size: small;">点击&ldquo;质量&rdquo;链接你会看到程序关于质量方面统计数据，这里直接忽略前面的三项看第四项&ldquo;最常见的崩溃情况&rdquo;，这里就提供了客户上传的转储文件。</span></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/3125.3.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/3125.3.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/3125.3.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/3125.3.png" alt="" border="0" /></a></p>
<p><span style="font-size: small;">选取你想要看的转储文件，然后下载，下载的文件是一个<span style="font-family: Calibri;">cab</span>格式的压缩包，将它解压缩就得到了<span style="font-family: Calibri;">dmp</span>后缀的转储文件了。我们会在这里演示两个转储文件的分析。</span></p>
<p><span style="font-size: small;">用<span style="font-family: Calibri;">windbg</span>打开第一个转储文件，<span style="font-family: Calibri;">windbg</span>会在命令窗口中显示报错的代码，这通常是在系统的组件中，对我们查抄问题并没有帮助。比如以下就是载入转储文件后显示的内容：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Windows 8.1 Version 9600 MP (4 procs) Free x86 compatible</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Product: WinNt, suite: SingleUserTS Personal</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Built by: 6.3.9600.16384 (winblue_rtm.130821-1623)</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Machine Name:</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Debug session time: Tue Jan 28 21:19:13.000 2014 (UTC + 8:00)</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">System Uptime: 0 days 13:25:15.561</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Process Uptime: 0 days 0:06:15.000</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">................................................................</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">................................................................</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">.............................................</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Loading unloaded module list</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">........</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">This dump file has an exception of interest stored in it.</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">The stored exception information can be accessed via .ecxr.</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">(2054.2efc): Unknown exception - code c000027b (first/second chance not available)</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">eax=aaaaaaaa ebx=00000000 ecx=00000000 edx=00000000 esi=050eeea0 edi=aaaaaaaa</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">eip=75789bcf esp=050eee5c ebp=050eeef8 iopl=0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nv up ei pl nz ac po nc</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">cs=0023&nbsp; ss=002b&nbsp; ds=002b&nbsp; es=002b&nbsp; fs=0053&nbsp; gs=002b&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; efl=00000212</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">combase!RoFailFastWithErrorContextInternal+0x10a:</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">75789bcf 6a03&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; 3</span></span></p>
<p><span style="font-size: small;">为了帮助我们查看转储文件中的内容，首先我们需要告诉<span style="font-family: Calibri;">windbg</span>到哪里去找符号<span style="font-family: Calibri;">(symbol)</span>文件，你可以在<span style="font-family: Calibri;">windbg</span>的命令栏中依次键入以下命令：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; .sympath+ srv*https://msdl.microsoft.com/download/symbols</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Symbol search path is: srv*https://msdl.microsoft.com/download/symbols</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Expanded Symbol search path is: srv*https://msdl.microsoft.com/download/symbols</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">************* Symbol Path validation summary **************</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Response&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Time (ms)&nbsp;&nbsp;&nbsp;&nbsp; Location</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Deferred&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; srv*https://msdl.microsoft.com/download/symbols</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; .reload</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">................................................................</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">................................................................</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">.............................................</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Loading unloaded module list</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">........</span></span></p>
<p><span style="font-size: small;">通常你也需要用以上命令加入你的应用程序的符号文件路径，符号文件的后缀名为<span style="font-family: Calibri;">pdb</span>，通常由编译器在生成最后的二进制文件时一起生成，所以这里你可以将路径设为<span style="font-family: Calibri;">Visual Studio</span>输出可执行文件的目录。注意这里的<span style="font-family: Calibri;">pdb</span>文件必须和你要查看的转储文件使用的可执行文件版本完全一致，所以为了方便起见，你最好妥善保留每一个需要维护的版本的<span style="font-family: Calibri;">pdb</span>文件。</span></p>
<p><span style="font-size: small;">首先我们用<span style="font-family: Calibri;">k</span>命令来看一看出问题时的调用堆栈<span style="font-family: Calibri;">(call stack)</span>：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; k</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">ChildEBP RetAddr&nbsp; </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">050eeef8 5fa44fec combase!RoFailFastWithErrorContextInternal+0x10a </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">050eef20 5fa44c62 Windows_UI_Xaml!DirectUI::ErrorHelper::ProcessUnhandledError+0xb8 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">050eef84 5fa44af6 Windows_UI_Xaml!DirectUI::FinalUnhandledErrorDetectedRegistration::OnFinalUnhandledErrorDetected+0xad </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">050eef98 6ab37e13 Windows_UI_Xaml!Microsoft::WRL::Details::InvokeHelper&lt;Microsoft::WRL::Implements&lt;Microsoft::WRL::RuntimeClassFlags&lt;2&gt;,Windows::Foundation::IEventHandler&lt;Windows::ApplicationModel::Core::UnhandledErrorDetectedEventArgs *&gt;,Microsoft::WRL::FtmBase,Microsoft::WRL::Details::Nil,Microsoft::WRL::Details::Nil,Microsoft::WRL::Details::Nil,Microsoft::WRL::Details::Nil,Microsoft::WRL::Details::Nil,Microsoft::WRL::Details::Nil,Microsoft::WRL::Details::Nil&gt;,long (__stdcall*)(IInspectable *,Windows::ApplicationModel::Core::IUnhandledErrorDetectedEventArgs *),2&gt;::Invoke+0x12 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">050eefb4 6ab37e44 twinapi_appcore!Windows::Internal::Details::GitInvokeHelper&lt;Windows::Foundation::IEventHandler&lt;Windows::ApplicationModel::Core::UnhandledErrorDetectedEventArgs *&gt;,Windows::Internal::GitPtr,2&gt;::Invoke+0x3c</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">050eefcc 6ab30d3e twinapi_appcore!Windows::ApplicationModel::Core::UnhandledErrorInvokeHelper::Invoke+0x19</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&hellip;</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-size: small;">从调用栈中可以看出，这时候系统已经在处理产生的异常<span style="font-family: Calibri;">(exception)</span>，这完全不是我们所想要的，我们想要看到的是这个异常是什么以及是谁报出来的。请不要着急，我们可以先看一下最后一个异常的详细信息，这通常就是我们想要找的异常<span style="font-family: Calibri;">:</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; .exr -1</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">ExceptionAddress: 5fa44fec (Windows_UI_Xaml!DirectUI::ErrorHelper::ProcessUnhandledError+0x000000b8)</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; ExceptionCode: <span style="background-color: #ffff00;">c000027b</span></span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; ExceptionFlags: 00000001</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">NumberParameters: 2</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; Parameter[0]: <span style="background-color: #ffff00;">1089daa0</span></span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; Parameter[1]: 00000001</span></span></p>
<p><span style="font-size: small;">这里给出了异常的错误代码，快看一下这是什么错误：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; !error c000027b</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Error code: (NTSTATUS) 0xc000027b (3221226107) - An application-internal exception has occurred.</span></span></p>
<p><span style="font-size: small;">天阿，应用程序内部异常，这等于还是什么都不知道么。别急，看到第一个参数了没有，这个参数里面包含了进一步的信息，让我们一步一步来把其中的秘密给找出来。</span></p>
<p><span style="font-size: small;">第一个参数<span style="font-family: Calibri;">0x1089daa0</span>实质上是一个内部结构的指针，这个内部结构通常有两个版本，首先我们需要确定这里用的是哪一个版本，我们用以下命令得到结构头部分的输出然后进行格式化：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; dt 1089daa0 combase!_STOWED_EXCEPTION_INFORMATION_HEADER*</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0x171e0f74</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x000 Size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0x20</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x004 Signature&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :<span style="background-color: #ffff00;"> 0x53453031</span></span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; .formats 0x53453031</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Evaluate expression:</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Hex:&nbsp;&nbsp;&nbsp;&nbsp; 53453031</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Decimal: 1397043249</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Octal:&nbsp;&nbsp; 12321230061</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Binary:&nbsp; 01010011 01000101 00110000 00110001</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Chars:&nbsp;&nbsp; <span style="background-color: #ffff00;">SE01</span></span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Time:&nbsp;&nbsp;&nbsp; Wed Apr 09 19:34:09 2014</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Float:&nbsp;&nbsp; low 8.46917e+011 high 0</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Double:&nbsp; 6.90231e-315</span></span></p>
<p><span style="font-size: small;">这里可以看出这是一个<span style="font-family: Calibri;">SE01</span>版本的结构，所以我们可以重新解析地址<span style="font-family: Calibri;">0x1089daa0</span>：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; dt 1089daa0 combase!_STOWED_EXCEPTION_INFORMATION_V1*</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0x171e0f74</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x000 Header&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : _STOWED_EXCEPTION_INFORMATION_HEADER</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x008 ResultCode&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : <span style="background-color: #ffff00;">80070057</span></span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x00c ExceptionForm&nbsp;&nbsp;&nbsp; : 0y01</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x00c ThreadId&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0y000000000000000000101110111111 (0xbbf)</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x010 ExceptionAddress : (null) </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x014 StackTraceWordSize : 4</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x018 StackTraceWords&nbsp; : 0x30</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x01c StackTrace&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : <span style="background-color: #ffff00;">0x171dff6c</span> Void</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x010 ErrorText&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : (null)</span></span></p>
<p><span style="font-size: small;">当然如果输出的结果是<span style="font-family: Calibri;">SE02</span>的话，那么就需要使用<span style="font-family: Calibri;">_STOWED_EXCEPTION_INFORMATION_V2</span>这个数据结构了。这里我们终于看到了真正的错误代码<span style="font-family: Calibri;">80070057</span>，这是一个参数错误：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; !error 80070057</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Error code: (HRESULT) 0x80070057 (2147942487) - The parameter is incorrect.</span></span></p>
<p><span style="font-size: small;">那么它是在什么地方发生的呢，<span style="font-family: Calibri;">_STOWED_EXCEPTION_INFORMATION_V1</span>结构也给出了调用栈的指针<span style="font-family: Calibri;">0x171dff6c</span>，让我们看看调用栈是什么样的：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; dps 0x171dff6c L30</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dff6c&nbsp; 5f8a8b50 Windows_UI_Xaml!DirectUI::Selector::OnSelectedIndexChanged+0x3f8b1c</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dff70&nbsp; 5f4b0020 Windows_UI_Xaml!DirectUI::Selector::OnPropertyChanged+0x7d </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dff74&nbsp; 5fa80fc7 Windows_UI_Xaml!DirectUI::FlipView::OnPropertyChanged+0x59 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dff78&nbsp; 5f73641f Windows_UI_Xaml!DirectUI::DependencyObject::UpdateEffectiveValue+0x122 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dff7c&nbsp; 5f7361c7 Windows_UI_Xaml!DirectUI::DependencyObject::SetValueInternal+0x4f5 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dff80&nbsp; 5f4af548 Windows_UI_Xaml!DirectUI::SelectorGenerated::put_SelectedIndex+0x6d </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dff84&nbsp; 5fa81fa8 Windows_UI_Xaml!DirectUI::FlipView::UpdateSelectedIndex+0x13 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dff88&nbsp; 5fa7f9c5 Windows_UI_Xaml!DirectUI::FlipView::MovePrevious+0x53 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dff8c&nbsp; 5fa80e92 Windows_UI_Xaml!DirectUI::FlipView::OnPointerWheelChanged+0x211 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dff90&nbsp; 5fbda3ea Windows_UI_Xaml!DirectUI::ControlGenerated::OnPointerWheelChangedProtected+0x59 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dff94&nbsp; 5f860630 Windows_UI_Xaml!DirectUI::Control::FireEvent+0x4c1b36 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dff98&nbsp; 5f39ea11 Windows_UI_Xaml!DirectUI::DXamlCore::FireEvent+0x200 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dff9c&nbsp; 5f39db3d Windows_UI_Xaml!AgCoreCallbacks::FireEvent+0x40 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dffa0&nbsp; 5f39da7b Windows_UI_Xaml!CCoreServices::CLR_FireEvent+0x9b </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dffa4&nbsp; 5f39d9c1 Windows_UI_Xaml!CommonBrowserHost::CLR_FireEvent+0x20 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">171dffa8&nbsp; 5f39d2f3 Windows_UI_Xaml!CControlBase::ScriptCallback+0x9a </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&hellip;</span></span></p>
<p><span style="font-size: small;">通过观察这个调用栈关系，结果就很明显了，这个问题发生在<span style="font-family: Calibri;">FlipView</span>的<span style="font-family: Calibri;">SelectedIndex</span>属性发生变化的时候，错误原因是参数不对，也就是说赋给<span style="font-family: Calibri;">SelectedIndex</span>的值有问题，我们需要找到给<span style="font-family: Calibri;">SelectedIndex</span>赋值的地方，看看是不是有不合适的数据赋给了它。</span></p>
<p><span style="font-size: small;">接下来让我们看一看另一个转储文件，同样，在<span style="font-family: Calibri;">windbg</span>中加载转储文件，设置<span style="font-family: Calibri;">symbol</span>路径以后，先用<span style="font-family: Calibri;">k</span>命令看一下当前的调用栈：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; k</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">ChildEBP RetAddr&nbsp; </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af5fc 7696a5bd combase!RoFailFastWithErrorContextInternal+0x10a </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af610 60ee5863 combase!RoFailFastWithErrorContext+0x11 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af630 60ee58b3 twinapi_appcore!Windows::ApplicationModel::Core::CoreApplication::ForwardLocalError+0x77</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af648 7696a1e4 twinapi_appcore!Windows::ApplicationModel::Core::CoreApplicationFactory::ForwardLocalError+0x30</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af690 7696a6ac combase!CallErrorForwarder+0x9d </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af69c 723c2d42 combase!RoReportUnhandledError+0x18 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af6fc 7245ffc1 mscorlib_ni+0x9b2d42</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af71c 7245fd35 mscorlib_ni+0xa4ffc1</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af74c 5b09f9df mscorlib_ni+0xa4fd35</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af83c 5b09f965 System_Runtime_WindowsRuntime_ni+0x1f9df</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af8a4 71d33156 System_Runtime_WindowsRuntime_ni+0x1f965</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af8b8 5b09f934 mscorlib_ni+0x323156</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af8d4 5b1aff16 System_Runtime_WindowsRuntime_ni+0x1f934</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af8f8 72b92a36 Windows_UI_ni+0x9ff16</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af978 5d783fb5 clr!COMToCLRDispatchHelper+0x28</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051af9f8 5d7837e7 Windows_UI!Windows::UI::Core::CDispatcher::ProcessInvokeItem+0x4d0 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">(Inline) -------- Windows_UI!Windows::UI::Core::CDispatcher::ProcessMessage+0xffffff8a </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051afa94 5d7816dc Windows_UI!Windows::UI::Core::CDispatcher::WaitAndProcessMessages+0x271 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051afaf4 5c949931 Windows_UI!Windows::UI::Core::CDispatcher::ProcessEvents+0x60 </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051afb3c 60ebf45e Windows_UI_Xaml!DirectUI::FrameworkView::Run+0x5e</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">051afb48 60ebf322</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&hellip;</span></span></p>
<p><span style="font-size: small;">显然，这也不是我们想要看的调用栈，那么像第一个例子一样，看看最近的那个异常：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; .exr -1</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">ExceptionAddress: 7696a5bd (combase!RoFailFastWithErrorContext+0x00000011)</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; ExceptionCode:<span style="background-color: #ffff00;"> c000027b</span></span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; ExceptionFlags: 00000001</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">NumberParameters: 2</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; Parameter[0]: <span style="background-color: #ffff00;">051af574</span></span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; Parameter[1]: 00000001</span></span></p>
<p><span style="font-size: small;">错误码仍然是<span style="font-family: Calibri;">c000027b</span>。这次我们不怕了，因为我们从前面的例子中学了一招，按照前面的办法重新做一遍：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; dt 051af574 combase!_STOWED_EXCEPTION_INFORMATION_HEADER*</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0x0d331aac</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x000 Size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0x20</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x004 Signature&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : <span style="background-color: #ffff00;">0x53453031</span></span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; .formats 0x53453031</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Evaluate expression:</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Hex:&nbsp;&nbsp;&nbsp;&nbsp; 53453031</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Decimal: 1397043249</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Octal:&nbsp;&nbsp; 12321230061</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Binary:&nbsp; 01010011 01000101 00110000 00110001</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Chars:&nbsp;&nbsp; <span style="background-color: #ffff00;">SE01</span></span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Time:&nbsp;&nbsp;&nbsp; Wed Apr 09 19:34:09 2014</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Float:&nbsp;&nbsp; low 8.46917e+011 high 0</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp; Double:&nbsp; 6.90231e-315</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; dt 051af574 combase!_STOWED_EXCEPTION_INFORMATION_V1*</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0x0d331aac</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x000 Header&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : _STOWED_EXCEPTION_INFORMATION_HEADER</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x008 ResultCode&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : <span style="background-color: #ffff00;">80070102</span></span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x00c ExceptionForm&nbsp;&nbsp;&nbsp; : 0y01</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x00c ThreadId&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0y000000000000000000001101000110 (0x346)</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x010 ExceptionAddress : <span style="background-color: #ffff00;">0x768ea9d7</span> Void</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x014 StackTraceWordSize : 4</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x018 StackTraceWords&nbsp; : 0xa</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp; +0x01c StackTrace&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0x076dc300 Void</span></span></p>
<p><span style="font-size: small;"><span style="font-family: Calibri;">&nbsp;&nbsp; +0x010 ErrorText&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0x768ea9d7&nbsp; "</span>趍ﯰ<span style="font-family: Calibri;">???"</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; !error 80070102</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Error code: (HRESULT) 0x80070102 (2147942658) - The wait operation timed out.</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; dps 0x076dc300 La</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">076dc300&nbsp; 768ff132 combase!RoOriginateLanguageException+0x3b </span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">076dc304&nbsp; 723c2c6a mscorlib_ni+0x9b2c6a</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">076dc308&nbsp; 7245ff62 mscorlib_ni+0xa4ff62</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">076dc30c&nbsp; 7245fd21 mscorlib_ni+0xa4fd21</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">076dc310&nbsp; 5b09f9df System_Runtime_WindowsRuntime_ni+0x1f9df</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">076dc314&nbsp; 5b09f965 System_Runtime_WindowsRuntime_ni+0x1f965</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">076dc318&nbsp; 71d33156 mscorlib_ni+0x323156</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">076dc31c&nbsp; 5b09f934 System_Runtime_WindowsRuntime_ni+0x1f934</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">076dc320&nbsp; 5b1aff16 Windows_UI_ni+0x9ff16</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">076dc324&nbsp; 72b92a36 clr!COMToCLRDispatchHelper+0x28</span></span></p>
<p><span style="font-size: small;">这回错误代码找到了，是<span style="font-family: Calibri;">0x80070102</span>，但是调用栈中显示的怎么仍然是一个无意义的<span style="font-family: Calibri;">RoOriginateLanguageException</span>呢<span style="font-family: Calibri;">?</span>别急，这说明这个异常是在<span style="font-family: Calibri;">.net</span>代码中产生的，我们用调试<span style="font-family: Calibri;">.net</span>代码的插件<span style="font-family: Calibri;">sos</span>来看一看，首先，在<span style="font-family: Calibri;">windbg</span>中加载<span style="font-family: Calibri;">sos</span>插件：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; .loadby sos clr</span></span></p>
<p><span style="font-size: small;">接下来就可以使用<span style="font-family: Calibri;">sos</span>的命令<span style="font-family: Calibri;">PrintException (pe)</span>来查看<span style="font-family: Calibri;">.net</span>代码报出来的异常了：</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">0:004&gt; !sos.pe</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Exception object: 02807538</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Exception type:&nbsp;&nbsp; System.Exception</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">Message:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;Invalid Object&gt;</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">InnerException:&nbsp;&nbsp; &lt;none&gt;</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">StackTrace (generated):</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; SP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Function</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; 051AF710 71D9D17A mscorlib_ni!System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task)+0x5e</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; 051AF720 71D9D115 mscorlib_ni!System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)+0x35</span></span></p>
<p><span style="font-family: Calibri; background-color: #ffff00;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; 051AF72C 04E9DA2D OurCalendar!OurCalendar.ViewModel.LoginViewModel+&lt;Login&gt;d__7.MoveNext()+0x29d</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; 051AF814 7251458F mscorlib_ni!System.Runtime.CompilerServices.AsyncMethodBuilderCore.&lt;ThrowAsync&gt;b__4(System.Object)+0x33</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; 051AF81C 5B09F994 System_Runtime_WindowsRuntime_ni!System.Threading.WinRTSynchronizationContext+Invoker.InvokeCore()+0x24</span></span></p>
<p><span style="font-family: Calibri; font-size: small;">&nbsp;</span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">StackTraceString: &lt;none&gt;</span></span></p>
<p><span style="font-family: Calibri;"><span style="font-size: small;">HResult: 80070102</span></span></p>
<p><span style="font-size: small;">接下来的情况就和明显了，这是在应用程序的代码中报的错，当然，我们的代码实际上是没有<span style="font-family: Calibri;">MoveNext</span>这个函数的，这个函数是编译器在编译异函数<span style="font-family: Calibri;">Login</span>的异步处理的时候自动生成的，所以下一步我们就应该去查看<span style="font-family: Calibri;">OurCalendar.ViewModel.LoginViewModel.Login</span>函数中为什么会报这个异常了。</span></p>
<p><span style="font-size: small;">以上就是我们在分析用户报告的<span style="font-family: Calibri;">Windows Store</span>应用崩溃时常用的办法。这里我用两个实际的例子做了一个演示，分别针对<span style="font-family: Calibri;">Winrt</span>的本地代码中报出的异常和<span style="font-family: Calibri;">.NET</span>代码中报出的异常，希望对大家提高<span style="font-family: Calibri;">Window Store</span>应用的稳定性有所帮助。</span></p>]]></content:encoded>
    </item>
    <item>
      <title>如何在WP8应用中安全的使用Azure Blob存储</title>
      <link>https://docs.microsoft.com/archive/blogs/hanxia/wp8azure-blob</link>
      <pubDate>Sun, 23 Mar 2014 17:32:00 GMT</pubDate>
      <dc:creator><![CDATA[Han Xia]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/hanxia/2014/03/23/wp8azure-blob/</guid>
      <description><![CDATA[在前面一篇文章中，我们演示了在Windows store应用中安全的使用Azure Blob存储的步骤。Windows...]]></description>
      <content:encoded><![CDATA[<p><span style="font-size: small;"><span style="line-height: 107%; mso-ascii-theme-font: minor-fareast; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-fareast; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;" lang="ZH-CN">在<a href="https://blogs.msdn.com/b/hanxia/archive/2014/03/20/windows-store-azure-blob-i.aspx">前面一篇文章</a>中，我们演示了</span><span style="line-height: 107%; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri;" lang="ZH-CN">在</span><span style="line-height: 107%; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体;">Windows store</span><span style="line-height: 107%; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri;" lang="ZH-CN">应用中安全的使用</span><span style="line-height: 107%; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体;">Azure Blob</span><span style="line-height: 107%; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri;" lang="ZH-CN">存储的步骤</span><span style="line-height: 107%; mso-ascii-theme-font: minor-fareast; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-fareast; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;" lang="ZH-CN">。<span style="line-height: 107%; mso-ascii-theme-font: minor-fareast; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-fareast; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA;">Windows Phone<span lang="ZH-CN">上的步骤与此类似，只是在客户端代码以及设置方面有一些区别。但是为了方便读者阅读，我这里将就</span>Windows Phone<span lang="ZH-CN">应用中如何</span></span><span style="line-height: 107%; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri;" lang="ZH-CN">安全的使用</span><span style="line-height: 107%; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体;">Azure Blob</span><span style="line-height: 107%; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri;" lang="ZH-CN">存储单独写一遍。这样对于</span><span style="line-height: 107%; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-fareast-font-family: 宋体;">Windows Phone</span><span style="line-height: 107%; mso-ascii-theme-font: minor-latin; mso-fareast-theme-font: minor-fareast; mso-hansi-theme-font: minor-latin; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA; mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri;" lang="ZH-CN">开发者来说，只需要看这篇文章就够了。<br /><br /></span></span>我们已经在<a href="https://blogs.msdn.com/b/hanxia/archive/2014/03/12/windows-phone-azure-blob.aspx">这篇文章</a>中演示了在Windows Phone应用中使用Azure Blob存储的基本步骤，但是，对于一个商业应用来说，保证数据的安全性是很重要的一环。上次文章的代码中，对Blob的访问权限是通过PublicAccess来控制的，理论上如果PublicAccess设置为OFF，那么第三方就应该无法访问该Blob。但是这里有一个明显的安全隐患：我们的代码中明文存储了Access Key字符串，而通过一些反编译工具第三方能够很容易的获得这个字符串。如果这个Access Key被暴露的话，那么Blob中的内容就毫无秘密可言了。为此，我们需要找到一个可靠的办法来保证验证信息的安全。</span></p>
<p><span style="font-size: small;">为了解决这个问题，我们需要做到以下两条：</span></p>
<p><span style="font-size: small;">1.保证用户在未经授权的情况下无法获得验证信息。</span></p>
<p><span style="font-size: small;">2.用户通过授权后获得的验证信息不能被重复使用。</span></p>
<p><span style="font-size: small;">对于第一条，我们可以自己建立一个服务器，与该服务器的连接需要先进行身份验证，然后从服务器上获得用于连接Windows Azure 存储服务的验证信息。不过我们既然已经使用了Windows Azure，那么完全可以使用Windows Azure &nbsp;云服务来为我们做同样的事情。</span></p>
<p><span style="font-size: small;">对于第二条，Windows Azure 存储服务提供了共享访问签名（Shared Access Signature）来保证验证信息的时效性。共享访问签名是一个在特定时间间隔内授予容器、Blob以及其他存储对象受限访问权限的 URI。也就是说，共享访问签名是一个URI, 客户端通过这个URI能够在规定时间内访问容器和Blob, 而超过了时间段的话这个URI就无效了，需要重新获取。</span></p>
<p><span style="font-size: small;">结合这两种方式，那么我们就能够实现对验证信息的保护了。我们将生成共享访问签名的代码放在Windows Azure云服务上，客户端通过访问云服务接口获得共享访问签名来访问Windows Azure存储服务商的Blob。</span></p>
<p><span style="font-size: small;">那么让我们来看看如果要安全的实现与前一篇文章相同的功能所需要完成的步骤。</span></p>
<p><span style="font-size: small;">一.&nbsp;&nbsp; 创建云服务并实现服务接口：</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">1. 从以下链接下载并安装Azure Cloud Service SDK For .NET:</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;"><a href="https://www.windowsazure.com/en-us/downloads/?sdk=net"><span style="color: #0563c1;">https://www.windowsazure.com/en-us/downloads/?sdk=net</span></a></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">针对不同的Visual Studio版本，您需要安装相应的SDK，这样在您的Visual Studio的项目模板中会出现Azure Cloud Service的模板。</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">2.&nbsp;通过Azure Cloud Service模板创建一个Cloud服务，在项目向导中加入WCF Service Web Role：</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/0385.1.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/0385.1.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/0385.1.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/0385.1.png" alt="" width="636" height="378" border="0" /></a></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">完成后项目向导会自动建立两个项目，一个是Cloud Service项目WindowsAzure1，另一个是提供WCF Service的Web Role项目WCFServiceWebRole1。WCFServiceWebRole1中会生成一个名为IService1的接口及实现该接口Service1类。</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/6327.2.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/6327.2.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/6327.2.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/6327.2.png" alt="" width="364" height="569" border="0" /></a></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">3.&nbsp; 修改WCFServiceWebRole1项目中的IService1接口添加用于获取共享访问签名的接口函数：</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; public interface IService1</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; {</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [OperationContract]</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string[] GetUploadSAS(string ContainerName, string BlobName);</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [OperationContract]</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string[] GetDownloadSAS(string ContainerName, string BlobName);</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; }</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">这两个接口函数一个用来获得用于上传的共享访问签名，一个用来获得用于下载的共享访问签名，它们都接收两个参数：容器名称和Blob名称,返回一个含有两个字符串的字符串数组，第一个字符串用来存放用于访问Blob的URI，第二个字符串用来存放共享访问签名。</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">4.&nbsp; 在WCFServiceWebRole1项目的Service1类中实现GetUploadSAS函数用来返回用于上传的共享访问签名，与上一篇演示的图片上传代码类似，我们首先使用账号名与访问密钥建立Blob客户端对象实例，然后根据容器名得到容器的对象实例，如果容器不存在的话则建立容器并且关闭对匿名用户的访问权限。然后，我们通过Blob名获得Blob块对象实例，通过该实例调用GetSharedAccessSignature获得一个具有5分钟读写访问权限的共享访问签名。</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string[] GetUploadSAS(string ContainerName, string BlobName)</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string[] blobInfo = new string[2];</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (string.IsNullOrEmpty(ContainerName) || string.IsNullOrEmpty(BlobName))</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new ArgumentNullException();</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="padding-left: 30px;">&nbsp;</p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var credentials = new StorageCredentials(accountName, accessKey);</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var account = new CloudStorageAccount(credentials, true);</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var blobClient = account.CreateCloudBlobClient();</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var container = blobClient.GetContainerReference(ContainerName);</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; container.CreateIfNotExists();</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BlobContainerPermissions containerPermissions = new BlobContainerPermissions();</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; containerPermissions.PublicAccess = BlobContainerPublicAccessType.Off;</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;container.SetPermissions(containerPermissions);</span></p>
<p style="padding-left: 30px;">&nbsp;</p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var blob = container.GetBlockBlobReference(BlobName);</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blobInfo[0] = blob.Uri.AbsoluteUri;</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blobInfo[1] = blob.GetSharedAccessSignature(new SharedAccessBlobPolicy()</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(5),</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Permissions = SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Read</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return blobInfo;</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;5.&nbsp; 在WCFServiceWebRole1项目的Service1类中实现GetDownloadSAS函数用来返回用于下载图片的共享访问签名，代码与步骤4类似，首先使用账号名与访问密钥建立Blob客户端对象实例，然后根据容器名得到容器的对象实例，接下来就可以通过Blob名获得Blob块对象实例，调用该实例的GetSharedAccessSignature函数来获得一个具有5分钟只读访问权限的共享访问签名：</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public string[] GetDownloadSAS(string ContainerName, string BlobName)</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string[] blobInfo = new string[2];</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (string.IsNullOrEmpty(ContainerName) || string.IsNullOrEmpty(BlobName))</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new ArgumentNullException();</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="padding-left: 30px;">&nbsp;</p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var credentials = new StorageCredentials(accountName, accessKey);</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var account = new CloudStorageAccount(credentials, true);</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var blobClient = account.CreateCloudBlobClient();</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var container = blobClient.GetContainerReference(ContainerName);</span></p>
<p style="padding-left: 30px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var blob = container.GetBlockBlobReference(BlobName);</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blobInfo[0] = blob.Uri.AbsoluteUri;</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blobInfo[1] = blob.GetSharedAccessSignature(new SharedAccessBlobPolicy()</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(5),</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Permissions = SharedAccessBlobPermissions.Read</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return blobInfo;</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="padding-left: 30px;">&nbsp;</p>
<p style="padding-left: 30px;"><span style="font-size: small;">6.&nbsp; 完成上述代码以后，就可以对该Azure应用点击Build-&gt;Publish将该服务发布Windows Azure上去了：</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp; a.&nbsp; 发布向导首先会让您提供您的Windows Azure账号，如果您已经登录过，那么可以直接在下拉框中选中，如果您是第一次登录，那么需要按Sign In按钮输入账号信息。然后按Next进入下一页：</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/2766.3.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/2766.3.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/2766.3.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/2766.3.png" alt="" width="600" height="412" border="0" /></a></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp; b.&nbsp; 如果您的Windows Azure上没有创建过Cloud Service，那么您需要创建一个新的Cloud Service，并且设定服务器位置。或者您也可以选择覆盖一个已有的Cloud Service。这里我们将新建的Cloud Service取名为SASService：</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/4786.4.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/4786.4.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/4786.4.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/4786.4.png" alt="" width="622" height="441" border="0" /></a></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp; c.&nbsp; 完成设置之后，发布向导会将该Cloud Service及应用部署到您的Windows Azure上去，您可以通过Windows Azure管理门户确认该Cloud Service以及应用是否已经部署成功。当Cloud Service部署完成后，Service Status会显示Created, Production会显示Running：</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/5810.5.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/5810.5.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/5810.5.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/5810.5.png" alt="" width="614" height="181" border="0" /></a></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; d.&nbsp; 点击该Cloud service的URL进入提供WCF Service的网站，我们可以看到，该网站提供的WCF Service是Service1.svc。所以访问该WCF Service的URL就是<a href="https://sasservice.cloudapp.net/Service1.svc"><span style="color: #0563c1;">https://sasservice.cloudapp.net/Service1.svc</span></a>。</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/8004.6.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/8004.6.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/8004.6.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/8004.6.png" alt="" width="627" height="389" border="0" /></a></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp; e.&nbsp; 打开这个URL，确认该WCF Service可用，并且可以得到在客户端调用该服务的C#示例代码。</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/3782.7.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/3782.7.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/3782.7.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/3782.7.png" alt="" width="645" height="495" border="0" /></a></span></p>
<p style="padding-left: 30px;">&nbsp;</p>
<p><span style="font-size: small;">二.&nbsp;&nbsp; 在客户端使用共享访问签名来上传和下载图片。</span></p>
<p><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1.&nbsp;在Windows Phone应用项目中点击Project-&gt;Add service Reference，将刚才获得的service URL填入Address中,按确定添加该Service Reference：</span></p>
<p><span style="font-size: small;">&nbsp;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/0434.8.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/0434.8.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/0434.8.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/0434.8.png" alt="" width="657" height="537" border="0" /></a></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">2.&nbsp; 在Windows Phone应用中添加一个用于图片上传的按钮，在Click的处理函数中加入上传代码。这里需要注意的是，和Windows Store应用不同，WCF Service对Windows Phone客户端暴露出来的接口是同步的而不是异步的。因此这边要用事件响应的方式获得WCF Service接口的返回值。我们首先创建WCF Service客户端实例，然后添加对GetUploadedSASCompleted事件的响应函数，然后再调用GetUploadSASAsync函数。在GetUploadedSASCompleted事件的响应函数中，我们可以获得访问Blob的URI和共享访问签名。根据共享访问签名，我们可以生成存储证明进而通过Blob的URI访问该Blob。得到了该Blob的实例之后，就可以调用UploadFromFileAsync或UploadFromStreamAsync函数来上传文件或流数据到Windows Azure的存储服务了。</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void btnUpload_Click(object sender, RoutedEventArgs e)</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Service1Client client = new Service1Client();</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; client.GetUploadSASCompleted +=client_GetUploadSASCompleted;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;client.GetUploadSASAsync("imagecontainer", "imageblob");</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private async void client_GetUploadSASCompleted(object sender, GetUploadSASCompletedEventArgs e)</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var blobInfo = e.Result;</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StorageCredentials credentials = new StorageCredentials(blobInfo[1]);</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CloudBlockBlob blockBlob = new CloudBlockBlob(new Uri(blobInfo[0]), credentials);</span></p>
<p style="padding-left: 30px;">&nbsp;</p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //upload from the local folder</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await blockBlob.UploadFromFileAsync(local.Path + @"\image.jpg", FileMode.Open);</span></p>
<p style="padding-left: 30px;">&nbsp;</p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //upload to the stream&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var img = await local.GetFileAsync("image.jpg");</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var stream = await img.OpenStreamForReadAsync();</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await blockBlob.UploadFromStreamAsync(stream);</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">3. 同样，再添加一个用于下载图片的按钮，在Click的处理函数中加入下载代码，过程与步骤二类似，只是这里需要调用GetDownloadSASAsync来获得Blob的URI和共享访问签名。在获得了Blob实例之后需要调用DownloadToFileAsync和DownloadToStreamAsync来下载Blob数据到文件或者数据流中：</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private void btnDownload_Click(object sender, RoutedEventArgs e)</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Service1Client client = new Service1Client();</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;client.GetDownloadSASCompleted +=client_GetDownloadSASCompleted;</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; client.GetDownloadSASAsync("imagecontainer", "imageblob");</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private async void client_GetDownloadSASCompleted(object sender, GetDownloadSASCompletedEventArgs e)</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var blobInfo = e.Result;</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StorageCredentials credentials = new StorageCredentials(blobInfo[1]);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CloudBlockBlob blockBlob = new CloudBlockBlob(new Uri(blobInfo[0]), credentials);</span></p>
<p style="padding-left: 30px;">&nbsp;</p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //download to the local folder</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await blockBlob.DownloadToFileAsync(local.Path + @"\image.jpg", FileMode.CreateNew);</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //download to the stream</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StorageFile img = await local.CreateFileAsync("image.tmp");</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Stream stream = await img.OpenStreamForWriteAsync();</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await blockBlob.DownloadToStreamAsync(stream);</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p><span style="font-size: small;">完成了以上两大步骤以后，我们就可以通过共享签名的方式来安全的存取Windows Azure上的Blob数据了。</span></p>
<p><span style="font-size: small;">&nbsp;三． 进一步提高WCF Service的可靠性和安全性</span></p>
<p><span style="font-size: small;">我们已经实现了对Windows Azure上Blob数据的安全存取。但是这还不足以应用到生产环境。在生产环境，我们还需要保证WCF Service的高可靠性。设置服务器的负载均衡是一个很好的解决办法。下面就让我们来介绍一下如何修改<span style="font-family: Calibri;">WCFServiceWebRole1</span>的设定以实现负载均衡。</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">1.在Cloud Service项目WindowsAzure1的Roles文件夹中，右键点击WCFServiceWebRole1选择Properties打开属性页。</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/0310.9.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/0310.9.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/0310.9.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/0310.9.png" alt="" width="656" height="412" border="0" /></a></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">2.在Configuration页面中将Instance count设为2或更多，Windows Azure会该为Cloud Service创建相应数目虚拟机做负载平衡。这样如果其中一台虚拟机出问题的时候其他虚拟机还能继续服务，从而保证了服务的稳定性。</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/8623.10.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/8623.10.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/8623.10.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/8623.10.png" alt="" width="656" height="428" border="0" /></a></span></p>
<p><span style="font-size: small;">另外，第三方还有可能通过抓包的方式来获取身份验证及数据信息，虽然因为我们使用了共享访问签名，可以设置该身份验证信息的有效时间，但还是可能形成隐患。为了避免这种情况，我们需要使用https与Windows Azure进行连接。由于Windows Phone客户端和Storage Service的连接已经是https的了，所以这里只需要关心Windows Phone客户端和Cloud Service的连接设置。下面就是设定与<span style="font-family: Calibri;">Cloud Service的通讯</span>为<span style="font-family: Calibri;">https</span>连接的步骤：</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">1.为了采用https连接，我们需要一张SSL证书。而在实际生产环境中，您需要使用一张受信任的证书或者公司颁发的证书。出于演示目的，这里我使用了一张自签名的证书，在Developer Command Prompt for VS2013中以管理员身份运行makecert命令可以帮助我们创建一张自签名证书：</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;"><span style="font-family: Calibri;">C:\&gt;makecert -r -pe -n CN="sasservice.cloudapp.net " -b 03/13/2014 -e 03/13/2015</span><span style="font-family: Calibri;">&nbsp;-eku 1.3.6.1.5.5.7.3.1 -sky exchange -sp "Microsoft RSA SChannel Cryptographic </span><span style="font-family: Calibri;">Provider" -sy 12 -sv sasCert.pvk sasCert.cer</span></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">2.期间您需要设定并输入私钥的保护口令。生成的证书包含两个文件，一个是pvk文件，用于存储证书私钥，一个是cer文件，用于存储证书公钥。关于如何使用makecert命令的详细信息，您可以参考以下链接：</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;"><a href="https://msdn.microsoft.com/zh-cn/library/bfsktky3(v=vs.110).aspx"><span style="color: #0563c1; font-family: Calibri;">https://msdn.microsoft.com/zh-cn/library/bfsktky3(v=vs.110).aspx</span></a></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">3.接下来您需要用pvk2pfx命令生成pfx文件以供后用：</span></p>
<p style="padding-left: 60px;"><span style="font-family: Calibri; font-size: small;">C:\&gt;pvk2pfx -pvk sasCert.pvk &nbsp;-pi &ldquo;&lt;pvk password&gt;&rdquo; -spc sasCert.cer -pfx sasCert.pfx -po &ldquo;&lt;pfx password&gt;&rdquo;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">pfx文件中包含了证书公钥和私钥。-<span style="font-family: Calibri;">pi</span>参数用来设定前面输入的私钥的保护口令，<span style="font-family: Calibri;">-po</span>参数用来设定输出的<span style="font-family: Calibri;">pfx</span>文件的私钥的保护口令。</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">4.在客户端右键点击sasCert.cer文件，选择Install certificate安装该证书，按照导入向导一步步将证书安装到Local Machine的Personal存储中。</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">5. 在Windows Azure管理门户进入前面建立的名为sasservice的云服务管理界面，点击Certificates标签页，在该页面中点击Upload弹出上传向导，根据向导将第3步生成的pfx文件上传到服务端。</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/8585.11.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/8585.11.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/8585.11.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/8585.11.png" alt="" width="669" height="616" border="0" /></a></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">&nbsp;6.回到在Cloud Service项目WindowsAzure1的Roles文件夹，右键点击WCFServiceWebRole1选择Properties打开属性页，在Certificates标签页中添加证书，Store Location选择Local Machine, Store Name选择My，Thumbprint选择步骤4中已经安装到客户端的sasservice证书。Cloud Service项目需要改Thumprint信息以便能够在服务端找到步骤5中上传的那张带私钥的证书。</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/1185.12.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/1185.12.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/1185.12.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/1185.12.png" alt="" width="674" height="237" border="0" /></a></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">7.在<span style="font-family: Calibri;">Endpoints</span>标签页中，将<span style="font-family: Calibri;">Endpoint</span>的<span style="font-family: Calibri;">Protocol</span>改为<span style="font-family: Calibri;">https, Public Port</span>改为<span style="font-family: Calibri;">443. SSL Certificate Name</span>选择步骤<span style="font-family: Calibri;">6</span>中添加的证书名<span style="font-family: Calibri;">Certificate1:</span></span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/1832.13.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/1832.13.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/61/60/1832.13.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-61-60/1832.13.png" alt="" width="677" height="265" border="0" /></a></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;"><span style="font-family: Calibri;">8.</span>&nbsp;打开<span style="font-family: Calibri;">WCFServiceWebRole1</span>的<span style="font-family: Calibri;">web.config</span>文件，加入以下设置使得<span style="font-family: Calibri;">WCF</span>服务支持<span style="font-family: Calibri;">https</span>：</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&lt;configuration&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&lt;system.serviceModel&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; &lt;services&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;service name="WCFServiceWebRole1.Service1"&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;endpoint address=""</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; binding="basicHttpBinding"</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bindingConfiguration="secureHttpBinding"</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; contract="WCFServiceWebRole1.IService1"/&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;endpoint address="mex"</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; binding="mexHttpsBinding"</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; contract="IMetadataExchange" /&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/service&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; &lt;/services&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; &lt;bindings&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;basicHttpBinding&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;binding name="secureHttpBinding"&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;security mode="Transport"&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;transport clientCredentialType="None"/&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/security&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/binding&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/basicHttpBinding&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; &lt;/bindings&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; &lt;behaviors&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;serviceBehaviors&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;behavior&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;serviceMetadata httpGetEnabled="false" httpsGetEnabled="true"/&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/behavior&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/serviceBehaviors&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; &lt;/behaviors&gt;</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&lt;/configuration&gt;</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;"><span style="font-family: Calibri;">&nbsp;</span><span style="font-family: Calibri;">9.</span>&nbsp; 对WindowsAzure1项目点击Build-&gt;Publish将该服务重新发布Windows Azure上，这样Cloud Service就只允许通过https来访问了。</span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">10.&nbsp;在Windows Phone应用客户端更新Service Reference,将原来的支持http的Service Renference删除，使用以下支持https的URL为应用重新添加Service Reference:</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;"><a href="https://sasservice.cloudapp.net/Service1.svc"><span style="color: #0563c1;">https://sasservice.cloudapp.net/Service1.svc</span></a></span></p>
<p style="padding-left: 30px;"><span style="font-size: small;">11.&nbsp;由于Windows Phone应用并没有可以直接设置证书的地方，我们需要在应用中使用代码安装前面生成的那张证书。为了演示起见，我首先将证书加入到项目中去，在属性中设置Build Action为Content, Copy to Output Directory为Copy if newer。在界面上添加了一个Button用于安装该证书：</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">private async void btnCertificate_Click(object sender, RoutedEventArgs e)</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">{</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var cert =await Package.Current.InstalledLocation.GetFileAsync(@"Assets\sasCert.cer");</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; await Launcher.LaunchFileAsync(cert);</span></p>
<p style="padding-left: 60px;"><span style="font-size: small;">}</span></p>
<p><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在安装了证书以后，Windows Phone应用客户端就可以通过https来与Cloud Service进行通讯了。</span></p>
<p>&nbsp;<span style="font-family: arial,helvetica,sans-serif; font-size: small;">通过以上一系列操作，我们终于将可以在Windows Phone应用中安全的使用Windows Azure上的storage service了。由于步骤比较复杂，我这里附上了完整的<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/10/51/03/21/AzureStorageDemoWP.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-10-51-03-21/AzureStorageDemoWP.zip">示例代码</a>以供大家参考。</span></p>
<p>&nbsp;</p>
<p style="padding-left: 60px;">&nbsp;</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/10/51/03/21/AzureStorageDemoWP.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-10-51-03-21/AzureStorageDemoWP.zip">AzureStorageDemoWP.zip</a></p>]]></content:encoded>
    </item>
  </channel>
</rss>