エクスポート (0) 印刷
すべて展開

ストレージ アカウントの作成

更新日: 2014年8月

Create Storage Account 非同期操作は、Microsoft Azure で新しいストレージ アカウントを作成します。

Create Storage Account要求は、次のように指定できます。<subscription-id> をサブスクリプション ID に置き換えます。

 

メソッド 要求 URI

POST

https://management.core.windows.net/<subscription-id>/services/storageservices

管理サービスに対して行われる要求をセキュリティで保護する必要があります。詳細については、「サービス管理要求の認証」を参照してください。

要求ヘッダーの説明を次の表に示します。

 

要求ヘッダー 説明

Content-Type

必須。このヘッダーには application/xml を設定します。

x-ms-version

必須。この要求に使用する操作のバージョンを指定します。このヘッダーには、2011/06/01 以降の値を設定する必要があります。バージョン管理ヘッダーの詳細については、「サービス管理のバージョン管理」を参照してください。

要求本文の形式は次のとおりです。

<?xml version="1.0" encoding="utf-8"?>
<CreateStorageServiceInput xmlns="http://schemas.microsoft.com/windowsazure">
  <ServiceName>name-of-storage-account</ServiceName>
  <Description>description-of-storage-account</Description>
  <Label>base64-encoded-label</Label>
  <AffinityGroup>name-of-affinity-group</AffinityGroup>
  <Location>location-of-storage-account</Location>
  <GeoReplicationEnabled>geo-replication-indicator</GeoReplicationEnabled>
  <ExtendedProperties>
    <ExtendedProperty>
      <Name>property-name</Name>
      <Value>property-value</Value>
    </ExtendedProperty>
  </ExtendedProperties>
  <SecondaryReadEnabled>secondary-read-indicator</SecondaryReadEnabled>
</CreateStorageServiceInput>

要求本文の要素を次の表に示します。

 

要素名

説明

サービス名

必須。Azure 内で一意のストレージ アカウントの名前。ストレージ アカウント名の長さは 3 ~ 24 文字にする必要があります。また、使用できるのは数字と小文字だけです。

この名前は DNS プレフィックス名で、ストレージ アカウント内の BLOB、キュー、テーブルへのアクセスに使用されます。

例:http://ServiceName.blob.core.windows.net/mycontainer/

Label

必須。Base64 エンコード文字列として指定されたストレージ アカウントのラベル。ラベルの最大長は 100 文字です。ラベルは、ストレージ アカウントを追跡する場合に、アカウントを識別するために使用できます。

説明

省略可能。ストレージ アカウントの説明。説明の最大長は 1,024 文字です。

場所

AffinityGroup を指定しない場合は必須。ストレージ アカウントが作成された場所。

要求本文には Location 要素または AffinityGroup 要素を含めることができます。ただし、両方は含められません。使用可能な場所のリストを表示するには、List Locations 操作を使用します。

AffinityGroup

Location を指定しない場合は必須。指定されたサブスクリプションの既存のアフィニティ グループの名前。

要求本文には Location 要素または AffinityGroup 要素を含めることができます。ただし、両方は含められません。使用可能なアフィニティ グループを一覧表示するには、List Affinity Groups 操作を使用します。

GeoReplicationEnabled

省略可能。ジオレプリケーションを有効にしてストレージ アカウントが作成されているかどうかを指定します。要素が要求本文に含まれていない場合、既定値は true です。true に設定すると、サービスが壊滅的に失われた場合に回復できるように、ストレージ アカウント内のデータが複数の地理的な場所にレプリケートされます。

GeoReplicationEnabled 要素を使用できるのは、バージョン 2012-03-01 以降を使用している場合だけです。バージョン 2013-11-01 以降、SecondaryReadEnabled が指定されている場合は、GeoReplicationEnabled も指定する必要があります。

名前

省略可能。ストレージ アカウントの拡張プロパティの名前を表します。各拡張プロパティには、定義済みの名前と値の両方が必要です。拡張プロパティの名前と値のペアは最大 50 組使用できます。

Name 要素の最大長は 64 文字です。Name では英数字とアンダースコアのみが有効であり、名前の先頭にはアルファベットを使用する必要があります。他の文字を使用しようとした場合、Name の先頭がアルファベット以外の文字の場合、または同じストレージ アカウントが所有する別の拡張プロパティの名前と同一の名前を入力した場合は、ステータス コード 400 (Bad Request) のエラーが発生します。

Name 要素を使用できるのは、バージョン 2012-03-01 以降を使用している場合だけです。

省略可能。ストレージ アカウントの拡張プロパティの値を表します。各拡張プロパティには、定義済みの名前と値の両方が必要です。拡張プロパティの名前と値のペアは最大 50 組使用できます。各拡張プロパティ値の最大長は 255 文字です。

Value 要素を使用できるのは、バージョン 2012-03-01 以降を使用している場合だけです。

SecondaryReadEnabled

省略可能。ストレージ アカウントでセカンダリ読み取りが有効になっていることを示します。

次の値をとります。

  • true

  • false

SecondaryReadEnabled 要素を使用できるのは、バージョン 2013-11-01 以降を使用している場合だけです。バージョン 2013-11-01 以降、SecondaryReadEnabled が指定されている場合は、GeoReplicationEnabled も指定する必要があります。

応答には、HTTP 状態コード、一連の応答ヘッダー、および応答本文が含まれています。

操作が正常に終了すると、ステータス コード 200 (OK) が返されます。ステータス コードの詳細については、「サービス管理のステータス コードとエラー コード」を参照してください。

この操作の応答には、次のヘッダーが含まれています。応答に追加の標準 HTTP ヘッダーが含まれる場合もあります。標準ヘッダーはすべて、HTTP/1.1 プロトコル仕様に準拠しています。

 

応答ヘッダー 説明

x-ms-request-id

管理サービスに対して行われた要求を一意に識別する値。非同期操作の場合は、ヘッダーの値を指定して Get Operation Status を呼び出して、操作が正常に終了したか、失敗したか、または処理中かを確認できます。

なし。

Create Storage Account 操作では、サブスクリプションで指定されている上限までプログラムによってストレージ アカウントを作成できます。サブスクリプションに対するストレージ アカウント数の初期上限は 20 です。この操作を実行すると、要求 ID を伴ってすぐに制御が戻され、ストレージ アカウント作成操作が Azure によって非同期的に実行されます。これは、新しいストレージ アカウントのプロビジョニングには数分かかる場合があるからです。ストレージ アカウントの作成操作がいつ完了したかを判断するには、要求 ID を使用して Get Operation Status 操作をポーリングできます。このときに返される XML 本文の Operation 要素には、Status 要素が格納されています。この要素の値は、ストレージ アカウント作成のステータスに応じて、InProgressFailed、または Succeeded になります。ステータスが Failed または Succeeded になるまでポーリングを続けた場合、Operation 要素の StatusCode 要素にステータス コードが格納されます。操作が失敗した場合は、Error 要素に別途エラー情報が格納されます。詳細については、「Get Operation Status」を参照してください。

x-ms-version の値が 2012-03-01 以降の場合、Anywhere USAnywhere Europe、または Anywhere Asia 地域でストレージ アカウントを作成することはできません (これらの地域は現在使用されていません)。ただし、使用している既存のアフィニティ グループが、これらの地域のいずれかに設定されている場合を除きます。

次のコンソール プログラムは、Create Storage Account 操作を使用して新しいストレージ アカウントを作成する例です。ストレージ アカウントには、説明、ラベル、場所、およびジオレプリケーションの状態を設定しています。その後、Create Storage Account 操作から返された要求 ID を使用して Get Operation Status 操作をポーリングします。ポーリングは、呼び出しが成功するか、失敗するか、またはポーリングがタイムアウトになるまで続けます。最後に、Get Storage Account Properties を呼び出して、新しいストレージ アカウントのプロパティを表示します。このサンプルを実行するには、Version 文字列に x-ms-version ヘッダーの値を、SubscriptionId にサブスクリプション識別子を、そして Thumbprint に管理証明書サムプリントをそれぞれ設定してください。また、ServiceName は一意のストレージ アカウント名に設定してください。

namespace Microsoft.WindowsAzure.ServiceManagementRESTAPI.Samples
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    using System.Threading;
    using System.Xml;
    using System.Xml.Linq;
 
    public class Program
    {
        // Set these constants with your values to run the sample.
        private const string Version = "2011-12-01";
        private const string Thumbprint = "management-certificate-thumbprint";
        private const string SubscriptionId = "subscription-identifier";
        private const string ServiceName = "unique-storage-account-name";
 
        // This is the common namespace for all Service Management REST API XML data.
        private static XNamespace wa = "http://schemas.microsoft.com/windowsazure";
 
        /// <summary>
        /// The operation status values from PollGetOperationStatus.
        /// </summary>
        private enum OperationStatus
        {
            InProgress,
            Failed,
            Succeeded,
            TimedOut
        }
 
        /// <summary>
        /// Gets or sets the certificate that matches the Thumbprint value.
        /// </summary>
        private static X509Certificate2 Certificate { getset; }
 
        static void Main(string[] args)
        {
            try
            {
                Certificate = GetStoreCertificate(Thumbprint);
 
                // Create the new storage account with the following values:
                string description = "Description for my example storage account";
                string label = "My example storage account label";
                string location = "North Central US";
                bool? enableGeoReplication = true;
                string requestId = CreateStorageAccount(
                    ServiceName, 
                    description, 
                    label, 
                    null, 
                    location, 
                    enableGeoReplication);
                Console.WriteLine(
                    "Called Create Storage Account operation: requestId {0}",
                    requestId);
 
                // Loop on Get Operation Status for result of storage creation
                OperationResult result = PollGetOperationStatus(
                    requestId, 
                    pollIntervalSeconds: 20,
                    timeoutSeconds: 180);
                switch (result.Status)
                {
                    case OperationStatus.TimedOut:
                        Console.WriteLine(
                            "Poll of Get Operation Status timed out: " +
                            "Operation {0} is still in progress after {1} seconds.",
                            requestId, 
                            (int)result.RunningTime.TotalSeconds);
                        break;
 
                    case OperationStatus.Failed:
                        Console.WriteLine(
                            "Failed: Operation {0} failed after " + 
                            "{1} seconds with status {2} ({3}) - {4}: {5}",
                            requestId, 
                            (int)result.RunningTime.TotalSeconds,
                            (int)result.StatusCode, 
                            result.StatusCode, 
                            result.Code, 
                            result.Message);
                        break;
 
                    case OperationStatus.Succeeded:
                        Console.WriteLine(
                            "Succeeded: Operation {0} completed " +
                            "after {1} seconds with status {2} ({3})",
                            requestId, 
                            (int)result.RunningTime.TotalSeconds, 
                            (int)result.StatusCode, 
                            result.StatusCode);
                        break;
                }
 
                // Display the property values for the new storage account.
                // Convert the Label property to a readable value for display.
                XElement updatedProperties =
                    GetStorageAccountProperties(ServiceName);
                XElement labelElement = updatedProperties.Descendants(wa + "Label").First();
                labelElement.Value = labelElement.Value.FromBase64();
                Console.WriteLine(
                    "New Storage Account Properties for {0}:{1}{2}",
                    ServiceName,
                    Environment.NewLine,
                    updatedProperties.ToString(SaveOptions.OmitDuplicateNamespaces));
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception caught in Main:");
                Console.WriteLine(ex.Message);
            }
 
            Console.Write("Press any key to continue:");
            Console.ReadKey();
        }
 
        /// <summary>
        /// Calls the Get Storage Account Properties operation in the Service 
        /// Management REST API for the specified subscription and storage account 
        /// name and returns the StorageService XML element from the response.
        /// </summary>
        /// <param name="serviceName">The name of the storage account.</param>
        /// <returns>The StorageService XML element from the response.</returns>
        private static XElement GetStorageAccountProperties(
            string serviceName)
        {
            string uriFormat = "https://management.core.windows.net/{0}" +
                "/services/storageservices/{1}";
            Uri uri = new Uri(String.Format(uriFormat, SubscriptionId, serviceName));
            XDocument responseBody;
            InvokeRequest(uri, "GET", HttpStatusCode.OK, nullout responseBody);
            return responseBody.Element(wa + "StorageService");
        }
 
        /// <summary>
        /// Calls the Create Storage Account operation in the Service Management 
        /// REST API for the specified subscription, storage account name, 
        /// description, label, location or affinity group, and geo-replication 
        /// enabled setting.
        /// </summary>
        /// <param name="serviceName">The name of the storage account to update.</param>
        /// <param name="description">The new description for the storage account.</param>
        /// <param name="label">The new label for the storage account.</param>
        /// <param name="affinityGroup">The affinity group name, or null to use a location.</param>
        /// <param name="location">The location name, or null to use an affinity group.</param>
        /// <param name="geoReplicationEnabled">The new geo-replication setting, if applicable. 
        /// This optional parameter defaults to null.</param>
        /// <returns>The requestId for the operation.</returns>
        private static string CreateStorageAccount(
            string serviceName,
            string description,
            string label,
            string affinityGroup,
            string location,
            bool? geoReplicationEnabled = null)
        {
            string uriFormat = "https://management.core.windows.net/{0}" +
                "/services/storageservices";
            Uri uri = new Uri(String.Format(uriFormat, SubscriptionId));
 
            // Location and Affinity Group are mutually exclusive. 
            // Use the location if it isn't null or empty.
            XElement locationOrAffinityGroup = String.IsNullOrEmpty(location) ?
                new XElement(wa + "AffinityGroup", affinityGroup) :
                new XElement(wa + "Location", location);
 
            // Create the request XML document
            XDocument requestBody = new XDocument(
                new XDeclaration("1.0""UTF-8""no"),
                new XElement(
                    wa + "CreateStorageServiceInput",
                    new XElement(wa + "ServiceName", serviceName),
                    new XElement(wa + "Description", description),
                    new XElement(wa + "Label", label.ToBase64()),
                    locationOrAffinityGroup));
 
            // Add the GeoReplicationEnabled element if the version supports it.
            if ((geoReplicationEnabled != null) &&
                (String.CompareOrdinal(Version, "2011-12-01") >= 0))
            {
                requestBody.Element(
                    wa + "CreateStorageServiceInput").Add(
                        new XElement(
                            wa + "GeoReplicationEnabled",
                            geoReplicationEnabled.ToString().ToLowerInvariant()));
            }
 
            XDocument responseBody;
            return InvokeRequest(
                uri, "POST", HttpStatusCode.Accepted, requestBody, out responseBody);
        }
 
        /// <summary>
        /// Calls the Get Operation Status operation in the Service 
        /// Management REST API for the specified subscription and requestId 
        /// and returns the Operation XML element from the response.
        /// </summary>
        /// <param name="requestId">The requestId of the operation to track.</param>
        /// <returns>The Operation XML element from the response.</returns>
        private static XElement GetOperationStatus(
            string requestId)
        {
            string uriFormat = "https://management.core.windows.net/{0}" +
                "/operations/{1}";
            Uri uri = new Uri(String.Format(uriFormat, SubscriptionId, requestId));
            XDocument responseBody;
            InvokeRequest(uri, "GET", HttpStatusCode.OK, nullout responseBody);
            return responseBody.Element(wa + "Operation");
        }
 
        /// <summary>
        /// The results from PollGetOperationStatus are passed in this struct.
        /// </summary>
        private struct OperationResult
        {
            // The status: InProgress, Failed, Succeeded, or TimedOut.
            public OperationStatus Status { getset; }
 
            // The http status code of the requestId operation, if any.
            public HttpStatusCode StatusCode { getset; }
 
            // The approximate running time for PollGetOperationStatus.
            public TimeSpan RunningTime { getset; }
 
            // The error code for the failed operation.
            public string Code { getset; }
 
            // The message for the failed operation.
            public string Message { getset; }
        }
 
        /// <summary>
        /// Polls Get Operation Status for the operation specified by requestId
        /// every pollIntervalSeconds until timeoutSeconds have passed or the
        /// operation has returned a Failed or Succeeded status. 
        /// </summary>
        /// <param name="requestId">The requestId of the operation to get status for.</param>
        /// <param name="pollIntervalSeconds">The interval between calls to Get Operation Status.</param>
        /// <param name="timeoutSeconds">The maximum number of seconds to poll.</param>
        /// <returns>An OperationResult structure with status or error information.</returns>
        private static OperationResult PollGetOperationStatus(
            string requestId,
            int pollIntervalSeconds,
            int timeoutSeconds)
        {
            OperationResult result = new OperationResult();
            DateTime beginPollTime = DateTime.UtcNow;
            TimeSpan pollInterval = new TimeSpan(0, 0, pollIntervalSeconds);
            DateTime endPollTime = beginPollTime + new TimeSpan(0, 0, timeoutSeconds);
 
            bool done = false;
            while (!done)
            {
                XElement operation = GetOperationStatus(requestId);
                result.RunningTime = DateTime.UtcNow - beginPollTime;
                try
                {
                    // Turn the Status string into an OperationStatus value
                    result.Status = (OperationStatus)Enum.Parse(
                        typeof(OperationStatus),
                        operation.Element(wa + "Status").Value);
                }
                catch (Exception)
                {
                    throw new ApplicationException(string.Format(
                        "Get Operation Status {0} returned unexpected status: {1}{2}",
                        requestId,
                        Environment.NewLine,
                        operation.ToString(SaveOptions.OmitDuplicateNamespaces)));
                }
 
                switch (result.Status)
                {
                    case OperationStatus.InProgress:
                        Console.WriteLine(
                            "In progress for {0} seconds", 
                            (int)result.RunningTime.TotalSeconds);
                        Thread.Sleep((int)pollInterval.TotalMilliseconds);
                        break;
 
                    case OperationStatus.Failed:
                        result.StatusCode = (HttpStatusCode)Convert.ToInt32(
                            operation.Element(wa + "HttpStatusCode").Value);
                        XElement error = operation.Element(wa + "Error");
                        result.Code = error.Element(wa + "Code").Value;
                        result.Message = error.Element(wa + "Message").Value;
                        done = true;
                        break;
 
                    case OperationStatus.Succeeded:
                        result.StatusCode = (HttpStatusCode)Convert.ToInt32(
                            operation.Element(wa + "HttpStatusCode").Value);
                        done = true;
                        break;
                }
 
                if (!done && DateTime.UtcNow > endPollTime)
                {
                    result.Status = OperationStatus.TimedOut;
                    done = true;
                }
            }
 
            return result;
        }
 
        /// <summary>
        /// Gets the certificate matching the thumbprint from the local store.
        /// Throws an ArgumentException if a matching certificate is not found.
        /// </summary>
        /// <param name="thumbprint">The thumbprint of the certificate to find.</param>
        /// <returns>The certificate with the specified thumbprint.</returns>
        private static X509Certificate2 GetStoreCertificate(string thumbprint)
        {
            List<StoreLocation> locations = new List<StoreLocation> 
            { 
                StoreLocation.CurrentUser, 
                StoreLocation.LocalMachine 
            };
 
            foreach (var location in locations)
            {
                X509Store store = new X509Store("My", location);
                try
                {
                    store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
                    X509Certificate2Collection certificates = store.Certificates.Find(
                        X509FindType.FindByThumbprint, thumbprint, false);
                    if (certificates.Count == 1)
                    {
                        return certificates[0];
                    }
                }
                finally
                {
                    store.Close();
                }
            }
 
            throw new ArgumentException(string.Format(
                "A Certificate with thumbprint '{0}' could not be located.",
                thumbprint));
        }
 
        /// <summary>
        /// A helper function to invoke a Service Management REST API operation.
        /// Throws an ApplicationException on unexpected status code results.
        /// </summary>
        /// <param name="uri">The URI of the operation to invoke using a web request.</param>
        /// <param name="method">The method of the web request, GET, PUT, POST, or DELETE.</param>
        /// <param name="expectedCode">The expected status code.</param>
        /// <param name="requestBody">The XML body to send with the web request. Use null to send no request body.</param>
        /// <param name="responseBody">The XML body returned by the request, if any.</param>
        /// <returns>The requestId returned by the operation.</returns>
        private static string InvokeRequest(
            Uri uri,
            string method,
            HttpStatusCode expectedCode,
            XDocument requestBody,
            out XDocument responseBody)
        {
            responseBody = null;
            string requestId = String.Empty;
 
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
            request.Method = method;
            request.Headers.Add("x-ms-Version", Version);
            request.ClientCertificates.Add(Certificate);
            request.ContentType = "application/xml";
 
            if (requestBody != null)
            {
                using (Stream requestStream = request.GetRequestStream())
                {
                    using (StreamWriter streamWriter = new StreamWriter(
                        requestStream, System.Text.UTF8Encoding.UTF8))
                    {
                        requestBody.Save(streamWriter, SaveOptions.DisableFormatting);
                    }
                }
            }
 
            HttpWebResponse response;
            HttpStatusCode statusCode = HttpStatusCode.Unused;
            try
            {
                response = (HttpWebResponse)request.GetResponse();
            }
            catch (WebException ex)
            {
                // GetResponse throws a WebException for 4XX and 5XX status codes
                response = (HttpWebResponse)ex.Response;
            }
 
            try
            {
                statusCode = response.StatusCode;
                if (response.ContentLength > 0)
                {
                    using (XmlReader reader = XmlReader.Create(response.GetResponseStream()))
                    {
                        responseBody = XDocument.Load(reader);
                    }
                }
 
                if (response.Headers != null)
                {
                    requestId = response.Headers["x-ms-request-id"];
                }
            }
            finally
            {
                response.Close();
            }
 
            if (!statusCode.Equals(expectedCode))
            {
                throw new ApplicationException(string.Format(
                    "Call to {0} returned an error:{1}Status Code: {2} ({3}):{1}{4}",
                    uri.ToString(),
                    Environment.NewLine,
                    (int)statusCode,
                    statusCode,
                    responseBody.ToString(SaveOptions.OmitDuplicateNamespaces)));
            }
 
            return requestId;
        }
    }
 
    /// <summary>
    /// Helpful extension methods for converting strings to and from Base-64.
    /// </summary>
    public static class StringExtensions
    {
        /// <summary>
        /// Converts a UTF-8 string to a Base-64 version of the string.
        /// </summary>
        /// <param name="s">The string to convert to Base-64.</param>
        /// <returns>The Base-64 converted string.</returns>
        public static string ToBase64(this string s)
        {
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(s);
            return Convert.ToBase64String(bytes);
        }
 
        /// <summary>
        /// Converts a Base-64 encoded string to UTF-8.
        /// </summary>
        /// <param name="s">The string to convert from Base-64.</param>
        /// <returns>The converted UTF-8 string.</returns>
        public static string FromBase64(this string s)
        {
            byte[] bytes = Convert.FromBase64String(s);
            return System.Text.Encoding.UTF8.GetString(bytes);
        }
    }
}

このサンプル プログラムからは、次のようなコンソール出力が生成されます。

Called Create Storage Account operation: requestId 8ba8bd9cdc50472892a0b3cd3659b297
In progress for 0 seconds
In progress for 20 seconds
In progress for 41 seconds
In progress for 61 seconds
In progress for 82 seconds
In progress for 103 seconds
Succeeded: Operation 8ba8bd9cdc50472892a0b3cd3659b297 completed after 123 seconds with status 200 (OK)
New Storage Account Properties for myexamplestorage1:
<StorageService xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Url>https://management.core.windows.net/01234567-89ab-cdef-0123-456789abcdef/services/storageservices/myexamplestorage1</Url>
  <ServiceName>myexamplestorage1</ServiceName>
  <StorageServiceProperties>
    <Description>Description for my example storage account</Description>
    <Location>North Central US</Location>
    <Label>My example storage account label</Label>
    <Status>Created</Status>
    <Endpoints>
      <Endpoint>http://myexamplestorage1.blob.core.windows.net/</Endpoint>
      <Endpoint>http://myexamplestorage1.queue.core.windows.net/</Endpoint>
      <Endpoint>http://myexamplestorage1.table.core.windows.net/</Endpoint>
    </Endpoints>
    <GeoReplicationEnabled>true</GeoReplicationEnabled>
    <GeoPrimaryRegion>usnorth</GeoPrimaryRegion>
    <StatusOfPrimary>Available</StatusOfPrimary>
    <GeoSecondaryRegion>ussouth</GeoSecondaryRegion>
    <StatusOfSecondary>Available</StatusOfSecondary>
  </StorageServiceProperties>
</StorageService>
Press any key to continue:

表示:
© 2014 Microsoft