Noções básicas de rede

Coisas que deve fazer em qualquer aplicação com rede ativada.

Capabilities

Para usar redes, deve adicionar elementos de capacidade apropriados ao manifesto da sua aplicação. Se não for especificada capacidade de rede no manifesto da sua aplicação, esta não terá capacidade de rede e qualquer tentativa de ligação à rede falhará.

As seguintes são as capacidades de rede mais utilizadas.

Capability Description
internetClient Proporciona acesso de saída à Internet e redes em locais públicos, como aeroportos e cafés. A maioria das aplicações que requerem acesso à Internet deve usar esta funcionalidade.
internetClientServer Dá à aplicação acesso à rede de entrada e saída da Internet e redes em locais públicos como aeroportos e cafés.
privateNetworkClientServer Dá à aplicação acesso à rede de entrada e saída nos locais de confiança do utilizador, como casa e trabalho.

Existem outras funcionalidades que podem ser necessárias para a sua aplicação, em certas circunstâncias.

Capability Description
enterpriseAuthentication Permite que uma aplicação se ligue a recursos de rede que requerem credenciais de domínio. Por exemplo, uma aplicação que recupera dados de servidores SharePoint numa intranet privada. Com esta capacidade, as suas credenciais podem ser usadas para aceder a recursos de rede numa rede que requer credenciais. Uma aplicação com esta funcionalidade pode fazer-se passar por si na rede. Não precisa desta funcionalidade para permitir que a sua aplicação aceda à Internet através de um proxy de autenticação.

Para mais detalhes, consulte a documentação do cenário de capacidades Enterprise em Capacidades Restritas.
Proximidade Necessária para comunicação de proximidade em campo próximo com dispositivos próximos ao computador. A proximidade por campo de curta distância pode ser utilizada para enviar ou estabelecer ligação com uma aplicação num dispositivo próximo.

Esta funcionalidade permite que uma aplicação aceda à rede para se ligar a um dispositivo próximo, com consentimento do utilizador para enviar um convite ou aceitar um convite.
sharedUserCertificates Esta capacidade permite que uma aplicação aceda a certificados de software e hardware, como certificados de cartão inteligente. Quando esta capacidade é invocada em tempo de execução, o utilizador deve agir, como inserir um cartão ou selecionar um certificado.

Com esta capacidade, os seus certificados de software e hardware ou um cartão inteligente são usados para identificação na aplicação. Esta funcionalidade pode ser utilizada pelo seu empregador, banco ou serviços governamentais para identificação.

Comunicar quando a sua aplicação não está em primeiro plano

As tarefas em segundo plano fornecem informações gerais sobre como trabalhar quando a sua aplicação não está em primeiro plano. Mais especificamente, o seu código deve tomar medidas especiais para ser notificado quando não for a aplicação em primeiro plano atual e os dados chegam pela rede para ela. Os acionadores do canal de controlo são suportados pelo Windows para este fim. Todas as informações sobre a utilização dos Acionadores do Canal de Controlo estão disponíveis aqui. Uma tecnologia mais recente oferece melhor funcionalidade com menor sobrecarga para alguns cenários, como sockets de fluxo com suporte para push: o mediador de sockets e os disparadores de atividade de sockets.

Se a sua aplicação usar DatagramSocket, StreamSocket ou StreamSocketListener, então a sua aplicação pode transferir a propriedade de um socket aberto para um broker de sockets fornecido pelo sistema, e depois sair do primeiro plano, ou até terminar. Quando uma ligação é feita no socket transferido, ou o tráfego chega nesse socket, a sua aplicação ou a sua tarefa designada em segundo plano são ativadas. Se a sua aplicação não estiver em execução, é iniciada. O Socket Broker notifica então a sua aplicação através de um SocketActivityTrigger de que chegou novo tráfego. A tua aplicação recupera o socket do socket broker e processa o tráfego nesse socket. Isto significa que a sua aplicação consome muito menos recursos do sistema quando não está a processar ativamente tráfego de rede.

O socket broker destina-se a substituir os Triggers do Canal de Controlo quando aplicável, porque oferece a mesma funcionalidade, mas com menos restrições e uma área de memória menor. O Socket Broker pode ser usado por aplicações que não são de ecrã de bloqueio, e é usado da mesma forma nos telemóveis que noutros dispositivos. As aplicações não precisam de estar a correr quando o tráfego chega para serem ativadas pelo broker de sockets. E o socket broker suporta a escuta em sockets TCP, o que os Control Channel Triggers não suportam.

Escolher um gatilho de rede

Existem alguns cenários em que qualquer tipo de gatilho seria adequado. Ao escolher que tipo de gatilho usar na sua aplicação, considere os seguintes conselhos.

Para detalhes e exemplos de como usar o broker de sockets, consulte comunicações de rede em segundo plano.

Ligações seguras

A Secure Sockets Layer (SSL) e o mais recente Transport Layer Security (TLS) são protocolos criptográficos concebidos para fornecer autenticação e encriptação para comunicação em rede. Estes protocolos foram concebidos para evitar escutas e manipulações ao enviar e receber dados de rede. Estes protocolos utilizam um modelo cliente-servidor para as trocas de protocolos. Estes protocolos também utilizam certificados digitais e autoridades certificadoras para verificar se o servidor é quem afirma ser.

Criação de ligações seguras por soquete

Um objeto StreamSocket pode ser configurado para usar SSL/TLS para comunicações entre o cliente e o servidor. Este suporte para SSL/TLS limita-se ao uso do objeto StreamSocket como cliente na negociação SSL/TLS. Não pode utilizar SSL/TLS com o StreamSocket criado por um StreamSocketListener quando são recebidas comunicações de entrada, porque a negociação SSL/TLS como servidor não é implementada pela classe StreamSocket.

Existem duas formas de garantir uma ligação StreamSocket com SSL/TLS:

  • ConnectAsync - Faça a ligação inicial a um serviço de rede e negocie imediatamente a utilização de SSL/TLS para todas as comunicações.
  • UpgradeToSslAsync - Liga-te inicialmente a um serviço de rede sem encriptação. A aplicação pode enviar ou receber dados. Depois, atualize a ligação para usar SSL/TLS em todas as comunicações futuras.

O SocketProtectionLevel especifica o nível de proteção de socket desejado que a aplicação pretende estabelecer ou atualizar a ligação. No entanto, o nível final de proteção da ligação estabelecida é determinado num processo de negociação entre ambos os extremos da ligação. O resultado pode ser um nível de proteção inferior ao que especificou, se o outro endpoint solicitar um nível inferior.

Depois de a operação assíncrona ser concluída com sucesso, pode recuperar o nível de proteção solicitado usado na chamada ConnectAsync ou UpgradeToSslAsync através da propriedade StreamSocketInformation.ProtectionLevel . No entanto, isto não reflete o nível real de proteção que a ligação está a utilizar.

Observação

O seu código não deve depender implicitamente do uso de um determinado nível de proteção, nem da suposição de que um determinado nível de segurança é usado por defeito. O panorama de segurança muda constantemente, e os protocolos e níveis de proteção padrão mudam ao longo do tempo para evitar o uso de protocolos com fraquezas conhecidas. As predefinições podem variar consoante a configuração individual da máquina ou o software instalado e as correções aplicadas. Se a sua aplicação depende do uso de um determinado nível de segurança, então deve especificar explicitamente esse nível e depois verificar se está realmente em uso na ligação estabelecida.

Use ConnectAsync

O ConnectAsync pode ser usado para estabelecer a ligação inicial com um serviço de rede e depois negociar imediatamente o uso de SSL/TLS para todas as comunicações. Existem dois métodos ConnectAsync que suportam a passagem de um parâmetro protectionLevel :

Se o parâmetro protectionLevel estiver definido para Windows. Networking.Sockets.SocketProtectionLevel.Ssl ao chamar qualquer um dos métodos ConnectAsync acima, o StreamSocket será estabelecido para usar SSL/TLS para encriptação. Este valor requer encriptação e nunca permite a utilização de uma cifra NULL.

A sequência normal a usar com um destes métodos ConnectAsync é a mesma.

O exemplo seguinte cria um StreamSocket e tenta estabelecer uma ligação ao serviço de rede e negociar imediatamente a utilização de SSL/TLS. Se a negociação for bem-sucedida, toda a comunicação de rede usando o StreamSocket entre o cliente e o servidor de rede será encriptada.

using Windows.Networking;
using Windows.Networking.Sockets;

    // Define some variables and set values
    StreamSocket clientSocket = new StreamSocket();
     
    HostName serverHost = new HostName("www.contoso.com");
    string serverServiceName = "https";
    
    // For simplicity, the sample omits implementation of the
    // NotifyUser method used to display status and error messages 
    
    // Try to connect to contoso using HTTPS (port 443)
    try {

        // Call ConnectAsync method with SSL
        await clientSocket.ConnectAsync(serverHost, serverServiceName, SocketProtectionLevel.Ssl);

        NotifyUser("Connected");
    }
    catch (Exception exception) {
        // If this is an unknown status it means that the error is fatal and retry will likely fail.
        if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) {
            throw;
        }
        
        NotifyUser("Connect failed with error: " + exception.Message);
        // Could retry the connection, but for this simple example
        // just close the socket.
        
        clientSocket.Dispose();
        clientSocket = null; 
    }
           
    // Add code to send and receive data using the clientSocket
    // and then close the clientSocket
#include <winrt/Windows.Networking.Sockets.h>

using namespace winrt;
...
    // Define some variables, and set values.
    Windows::Networking::Sockets::StreamSocket clientSocket;

    Windows::Networking::HostName serverHost{ L"www.contoso.com" };
    winrt::hstring serverServiceName{ L"https" };

    // For simplicity, the sample omits implementation of the
    // NotifyUser method used to display status and error messages.

    // Try to connect to the server using HTTPS and SSL (port 443).
    try
    {
        co_await clientSocket.ConnectAsync(serverHost, serverServiceName, Windows::Networking::Sockets::SocketProtectionLevel::Tls12);
        NotifyUser(L"Connected");
    }
    catch (winrt::hresult_error const& exception)
    {
        NotifyUser(L"Connect failed with error: " + exception.message());
        clientSocket = nullptr;
    }
    // Add code to send and receive data using the clientSocket,
    // then set the clientSocket to nullptr when done to close it.
using Windows::Networking;
using Windows::Networking::Sockets;

    // Define some variables and set values
    StreamSocket^ clientSocket = ref new StreamSocket();
 
    HostName^ serverHost = ref new HostName("www.contoso.com");
    String^ serverServiceName = "https";

    // For simplicity, the sample omits implementation of the
    // NotifyUser method used to display status and error messages 

    // Try to connect to the server using HTTPS and SSL (port 443)
    task<void>(clientSocket->ConnectAsync(serverHost, serverServiceName, SocketProtectionLevel::Ssl)).then([this] (task<void> previousTask) {
        try
        {
            // Try getting all exceptions from the continuation chain above this point.
            previousTask.Get();
            NotifyUser("Connected");
        }
        catch (Exception^ exception)
        {
            NotifyUser("Connect failed with error: " + exception->Message);
            
            clientSocket->Close();
            clientSocket = null;
        }
    });
    // Add code to send and receive data using the clientSocket
    // Then close the clientSocket when done

Usar UpgradeToSslAsync

Quando o seu código usa UpgradeToSslAsync, estabelece primeiro uma ligação a um serviço de rede sem encriptação. A aplicação pode enviar ou receber alguns dados e depois atualizar a ligação para usar SSL/TLS para todas as comunicações futuras.

O método UpgradeToSslAsync assume dois parâmetros. O parâmetro protectionLevel indica o nível de proteção desejado. O parâmetro validationHostName é o nome de host do destino remoto da rede utilizado para validação ao atualizar para SSL. Normalmente, o validationHostName seria o mesmo hostname que a aplicação utilizou inicialmente para estabelecer a ligação. Se o parâmetro protectionLevel estiver definido para Windows. System.Socket.SocketProtectionLevel.Ssl ao chamar UpgradeToSslAsync, o StreamSocket deve usar o SSL/TLS para encriptação em comunicações futuras através do socket. Este valor requer encriptação e nunca permite a utilização de uma cifra NULL.

A sequência normal a usar com o método UpgradeToSslAsync é a seguinte:

O exemplo seguinte cria um StreamSocket, tenta estabelecer uma ligação ao serviço de rede, envia alguns dados iniciais e depois negocia a utilização de SSL/TLS. Se a negociação for bem-sucedida, toda a comunicação de rede usando o StreamSocket entre o cliente e o servidor de rede será encriptada.

using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;

    // Define some variables and set values
    StreamSocket clientSocket = new StreamSocket();
 
    HostName serverHost = new HostName("www.contoso.com");
    string serverServiceName = "http";

    // For simplicity, the sample omits implementation of the
    // NotifyUser method used to display status and error messages 

    // Try to connect to contoso using HTTP (port 80)
    try {
        // Call ConnectAsync method with a plain socket
        await clientSocket.ConnectAsync(serverHost, serverServiceName, SocketProtectionLevel.PlainSocket);

        NotifyUser("Connected");

    }
    catch (Exception exception) {
        // If this is an unknown status it means that the error is fatal and retry will likely fail.
        if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) {
            throw;
        }

        NotifyUser("Connect failed with error: " + exception.Message, NotifyType.ErrorMessage);
        // Could retry the connection, but for this simple example
        // just close the socket.

        clientSocket.Dispose();
        clientSocket = null; 
        return;
    }

    // Now try to send some data
    DataWriter writer = new DataWriter(clientSocket.OutputStream);
    string hello = "Hello, World! ☺ ";
    Int32 len = (int) writer.MeasureString(hello); // Gets the UTF-8 string length.
    writer.WriteInt32(len);
    writer.WriteString(hello);
    NotifyUser("Client: sending hello");

    try {
        // Call StoreAsync method to store the hello message
        await writer.StoreAsync();

        NotifyUser("Client: sent data");

        writer.DetachStream(); // Detach stream, if not, DataWriter destructor will close it.
    }
    catch (Exception exception) {
        NotifyUser("Store failed with error: " + exception.Message);
        // Could retry the store, but for this simple example
            // just close the socket.

            clientSocket.Dispose();
            clientSocket = null; 
            return;
    }

    // Now upgrade the client to use SSL
    try {
        // Try to upgrade to SSL
        await clientSocket.UpgradeToSslAsync(SocketProtectionLevel.Ssl, serverHost);

        NotifyUser("Client: upgrade to SSL completed");
           
        // Add code to send and receive data 
        // The close clientSocket when done
    }
    catch (Exception exception) {
        // If this is an unknown status it means that the error is fatal and retry will likely fail.
        if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) {
            throw;
        }

        NotifyUser("Upgrade to SSL failed with error: " + exception.Message);

        clientSocket.Dispose();
        clientSocket = null; 
        return;
    }
#include <winrt/Windows.Networking.Sockets.h>
#include <winrt/Windows.Storage.Streams.h>

using namespace winrt;
using namespace Windows::Storage::Streams;
...
    // Define some variables, and set values.
    Windows::Networking::Sockets::StreamSocket clientSocket;

    Windows::Networking::HostName serverHost{ L"www.contoso.com" };
    winrt::hstring serverServiceName{ L"https" };

    // For simplicity, the sample omits implementation of the
    // NotifyUser method used to display status and error messages. 

    // Try to connect to the server using HTTP (port 80).
    try
    {
        co_await clientSocket.ConnectAsync(serverHost, serverServiceName, Windows::Networking::Sockets::SocketProtectionLevel::PlainSocket);
        NotifyUser(L"Connected");
    }
    catch (winrt::hresult_error const& exception)
    {
        NotifyUser(L"Connect failed with error: " + exception.message());
        clientSocket = nullptr;
    }

    // Now, try to send some data.
    DataWriter writer{ clientSocket.OutputStream() };
    winrt::hstring hello{ L"Hello, World! ☺ " };
    uint32_t len{ writer.MeasureString(hello) }; // Gets the size of the string, in bytes.
    writer.WriteInt32(len);
    writer.WriteString(hello);
    NotifyUser(L"Client: sending hello");

    try
    {
        co_await writer.StoreAsync();
        NotifyUser(L"Client: sent hello");

        writer.DetachStream(); // Detach the stream when you want to continue using it; otherwise, the DataWriter destructor closes it.
    }
    catch (winrt::hresult_error const& exception)
    {
        NotifyUser(L"Store failed with error: " + exception.message());
        // We could retry the store operation. But, for this simple example, just close the socket by setting it to nullptr.
        clientSocket = nullptr;
        co_return;
    }

    // Now, upgrade the client to use SSL.
    try
    {
        co_await clientSocket.UpgradeToSslAsync(Windows::Networking::Sockets::SocketProtectionLevel::Tls12, serverHost);
        NotifyUser(L"Client: upgrade to SSL completed");

        // Add code to send and receive data using the clientSocket,
        // then set the clientSocket to nullptr when done to close it.
    }
    catch (winrt::hresult_error const& exception)
    {
        // If this is an unknown status, then the error is fatal and retry will likely fail.
        Windows::Networking::Sockets::SocketErrorStatus socketErrorStatus{ Windows::Networking::Sockets::SocketError::GetStatus(exception.to_abi()) };
        if (socketErrorStatus == Windows::Networking::Sockets::SocketErrorStatus::Unknown)
        {
            throw;
        }

        NotifyUser(L"Upgrade to SSL failed with error: " + exception.message());
        // We could retry the store operation. But for this simple example, just close the socket by setting it to nullptr.
        clientSocket = nullptr;
        co_return;
    }
using Windows::Networking;
using Windows::Networking::Sockets;
using Windows::Storage::Streams;

    // Define some variables and set values
    StreamSocket^ clientSocket = ref new StreamSocket();
 
    HostName^ serverHost = ref new HostName("www.contoso.com");
    String^ serverServiceName = "http";

    // For simplicity, the sample omits implementation of the
    // NotifyUser method used to display status and error messages 

    // Try to connect to contoso using HTTP (port 80)
    task<void>(clientSocket->ConnectAsync(serverHost, serverServiceName, SocketProtectionLevel::PlainSocket)).then([this] (task<void> previousTask) {
        try
        {
            // Try getting all exceptions from the continuation chain above this point.
            previousTask.Get();
            NotifyUser("Connected");
        }
        catch (Exception^ exception)
        {
            NotifyUser("Connect failed with error: " + exception->Message);
 
            clientSocket->Close();
            clientSocket = null;
        }
    });
       
    // Now try to send some data
    DataWriter^ writer = ref new DataWriter(clientSocket->OutputStream);
    String^ hello = "Hello, World! ☺ ";
    Int32 len = (int) writer->MeasureString(hello); // Gets the UTF-8 string length.
    writer->WriteInt32(len);
    writer->WriteString(hello);
    NotifyUser("Client: sending hello");

    task<void>(writer->StoreAsync()).then([this] (task<void> previousTask) {
        try {
            // Try getting all exceptions from the continuation chain above this point.
            previousTask.Get();

            NotifyUser("Client: sent hello");

            writer->DetachStream(); // Detach stream, if not, DataWriter destructor will close it.
       }
       catch (Exception^ exception) {
               NotifyUser("Store failed with error: " + exception->Message);
               // Could retry the store, but for this simple example
               // just close the socket.
 
               clientSocket->Close();
               clientSocket = null;
               return;
       }
    });

    // Now upgrade the client to use SSL
    task<void>(clientSocket->UpgradeToSslAsync(SocketProtectionLevel::Ssl, serverHost)).then([this] (task<void> previousTask) {
        try {
            // Try getting all exceptions from the continuation chain above this point.
            previousTask.Get();

           NotifyUser("Client: upgrade to SSL completed");
           
           // Add code to send and receive data 
           // Then close clientSocket when done
        }
        catch (Exception^ exception) {
            // If this is an unknown status it means that the error is fatal and retry will likely fail.
            if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown) {
                throw;
            }

            NotifyUser("Upgrade to SSL failed with error: " + exception.Message);

            clientSocket->Close();
            clientSocket = null; 
            return;
        }
    });

Criação de ligações WebSocket seguras

Tal como as ligações socket tradicionais, as ligações WebSocket também podem ser encriptadas com Transport Layer Security (TLS)/Secure Sockets Layer (SSL) ao utilizar as funcionalidades StreamWebSocket e MessageWebSocket para uma aplicação Windows. Na maioria dos casos, vai querer usar uma ligação WebSocket segura. Isto aumentará as hipóteses de a sua ligação ter sucesso, pois muitos proxies rejeitarão ligações WebSocket não encriptadas.

Para exemplos de como criar ou atualizar para uma ligação socket segura a um serviço de rede, veja Como proteger ligações WebSocket com TLS/SSL.

Além da encriptação TLS/SSL, um servidor pode necessitar de um valor de cabeçalho Sec-WebSocket-Protocol para completar o handshake inicial. Este valor, representado pelas propriedades StreamWebSocketInformation.Protocol e MessageWebSocketInformation.Protocol , indica a versão do protocolo da ligação e permite ao servidor interpretar corretamente o handshake de abertura e os dados trocados posteriormente. Usando esta informação do protocolo, se em algum momento o servidor não conseguir interpretar os dados recebidos de forma segura, a ligação pode ser encerrada.

Se o pedido inicial do cliente não contiver esse valor, ou fornecer um valor que não corresponde ao que o servidor espera, o valor esperado é enviado do servidor para o cliente através do erro de handshake WebSocket.

Autenticação

Como fornecer credenciais de autenticação ao conectar-se através da rede.

Fornecer um certificado de cliente com a classe StreamSocket

A classe Windows.Networking.Sockets.StreamSocket suporta a utilização de SSL/TLS para autenticar o servidor com o qual a aplicação está a comunicar. Em certos casos, a aplicação também precisa de se autenticar no servidor usando um certificado cliente TLS. No Windows 10, pode fornecer um certificado de cliente no objeto StreamSocket.Control (isto deve ser definido antes de iniciar o handshake TLS). Se o servidor solicitar o certificado do cliente, o Windows responderá com o certificado fornecido.

Aqui está um excerto de código que mostra como implementar isto:

var socket = new StreamSocket();
Windows.Security.Cryptography.Certificates.Certificate certificate = await GetClientCert();
socket.Control.ClientCertificate = certificate;
await socket.ConnectAsync(destination, SocketProtectionLevel.Tls12);

Fornecimento de credenciais de autenticação a um serviço web

As APIs de rede que permitem que as aplicações interajam com serviços web seguros fornecem cada uma os seus próprios métodos para inicializar um cliente ou definir um cabeçalho de pedido com credenciais de autenticação de servidor e proxy. Cada método é definido com um objeto PasswordCredential que indica um nome de utilizador, palavra-passe e o recurso para o qual essas credenciais são usadas. A tabela seguinte fornece um mapeamento destas APIs:

WebSockets MessageWebSocketControl.ServerCredential
MessageWebSocketControl.ProxyCredential
StreamWebSocketControl.ServerCredential
StreamWebSocketControl.ProxyCredential
Transferência de Antecedentes BackgroundDownloader.ServerCredential
BackgroundDownloader.ProxyCredential
BackgroundUploader.ServerCredential
BackgroundUploader.ProxyCredencial
Sindicalização SyndicationClient(PasswordCredential)
SyndicationClient.ServerCredential
SyndicationClient.ProxyCredencial
AtomPub AtomPubClient(PasswordCredential)
AtomPubClient.ServerCredential
AtomPubClient.ProxyCredential

Gestão de exceções de rede

Na maioria das áreas da programação, uma exceção indica um problema ou falha significativa, causada por alguma falha no programa. Na programação de rede, existe uma fonte adicional para exceções: a própria rede e a natureza das comunicações em rede. As comunicações de rede são inerentemente pouco fiáveis e propensas a falhas inesperadas. Para cada uma das formas como a sua aplicação utiliza a rede, deve manter alguma informação de estado; E o seu código de aplicação deve lidar com exceções de rede atualizando essa informação de estado e iniciando a lógica apropriada para que a sua aplicação restabelece ou tente novamente falhas de comunicação.

Quando as aplicações Universal Windows lançam uma exceção, o seu gestor de exceções pode obter informações mais detalhadas sobre a causa da exceção para melhor compreender a falha e tomar decisões apropriadas.

Cada projeção linguística suporta um método para aceder a esta informação mais detalhada. Uma exceção projeta-se como um valor HRESULT em aplicações Windows Universais. O ficheiro de inclusão Winerror.h contém uma lista muito extensa de possíveis valores HRESULT que inclui erros de rede.

As APIs de rede suportam diferentes métodos para recuperar esta informação detalhada sobre a causa de uma exceção.

  • Algumas APIs fornecem um método auxiliar que converte o valor HRESULT da exceção para um valor de enumeração.
  • Outras APIs fornecem um método para recuperar o valor real do HRESULT .