2025-11-28 00:35:46 +09:00

554 lines
22 KiB
C++

//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#include "stdafx.h"
#include "WlanHostedNetworkWinRT.h"
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Foundation::Collections;
using namespace ABI::Windows::Devices::Enumeration;
using namespace ABI::Windows::Devices::WiFiDirect;
using namespace ABI::Windows::Security::Credentials;
using namespace ABI::Windows::Networking;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
typedef __FITypedEventHandler_2_Windows__CDevices__CWiFiDirect__CWiFiDirectConnectionListener_Windows__CDevices__CWiFiDirect__CWiFiDirectConnectionRequestedEventArgs ConnectionRequestedHandler;
typedef __FITypedEventHandler_2_Windows__CDevices__CWiFiDirect__CWiFiDirectAdvertisementPublisher_Windows__CDevices__CWiFiDirect__CWiFiDirectAdvertisementPublisherStatusChangedEventArgs StatusChangedHandler;
typedef __FITypedEventHandler_2_Windows__CDevices__CWiFiDirect__CWiFiDirectDevice_IInspectable ConnectionStatusChangedHandler;
typedef __FIAsyncOperationCompletedHandler_1_Windows__CDevices__CWiFiDirect__CWiFiDirectDevice FromIdAsyncHandler;
typedef __FIVectorView_1_Windows__CNetworking__CEndpointPair EndpointPairCollection;
WlanHostedNetworkHelper::WlanHostedNetworkHelper()
: _ssidProvided(false),
_passphraseProvided(false),
_listener(nullptr),
_autoAccept(true)
{
}
WlanHostedNetworkHelper::~WlanHostedNetworkHelper()
{
if (_publisher.Get() != nullptr)
{
_publisher->Stop();
}
Reset();
}
void WlanHostedNetworkHelper::Start()
{
HRESULT hr = S_OK;
// Clean up old state
Reset();
// Create WiFiDirectAdvertisementPublisher
hr = Windows::Foundation::ActivateInstance(HStringReference(RuntimeClass_Windows_Devices_WiFiDirect_WiFiDirectAdvertisementPublisher).Get(), &_publisher);
if (FAILED(hr))
{
throw WlanHostedNetworkException("ActivateInstance for WiFiDirectAdvertisementPublisher failed", hr);
}
// Add event handler for advertisement StatusChanged
hr = _publisher->add_StatusChanged(
Callback<StatusChangedHandler>([this](IWiFiDirectAdvertisementPublisher* sender, IWiFiDirectAdvertisementPublisherStatusChangedEventArgs* args) -> HRESULT
{
HRESULT hr = S_OK;
WiFiDirectAdvertisementPublisherStatus status;
WiFiDirectError error;
try
{
hr = args->get_Status(&status);
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get Status for AdvertisementPubliserStatusChangedEventArgs failed", hr);
}
switch (status)
{
case WiFiDirectAdvertisementPublisherStatus_Started:
{
// Begin listening for connections and notify listener that the advertisement started
StartListener();
if (_listener != nullptr)
{
_listener->OnAdvertisementStarted();
}
break;
}
case WiFiDirectAdvertisementPublisherStatus_Aborted:
{
// Check error and notify listener that the advertisement stopped
hr = args->get_Error(&error);
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get Error for AdvertisementPubliserStatusChangedEventArgs failed", hr);
}
if (_listener != nullptr)
{
std::wstring message;
switch (error)
{
case WiFiDirectError_RadioNotAvailable:
message = L"Advertisement aborted, Wi-Fi radio is turned off";
break;
case WiFiDirectError_ResourceInUse:
message = L"Advertisement aborted, Resource In Use";
break;
default:
message = L"Advertisement aborted, unknown reason";
break;
}
_listener->OnAdvertisementAborted(message);
}
break;
}
case WiFiDirectAdvertisementPublisherStatus_Stopped:
{
// Notify listener that the advertisement is stopped
if (_listener != nullptr)
{
_listener->OnAdvertisementStopped(L"Advertisement stopped");
}
break;
}
}
}
catch (WlanHostedNetworkException& e)
{
if (_listener != nullptr)
{
std::wostringstream ss;
ss << e.what() << ": " << e.GetErrorCode();
_listener->OnAsyncException(ss.str());
}
return e.GetErrorCode();
}
return hr;
}).Get(), &_statusChangedToken);
// Set Advertisement required settings
hr = _publisher->get_Advertisement(_advertisement.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get advertisement for WiFiDirectAdvertisementPublisher failed", hr);
}
// Must set the autonomous group owner (GO) enabled flag
// Legacy Wi-Fi Direct advertisement uses a Wi-Fi Direct GO to act as an access point to legacy settings
hr = _advertisement->put_IsAutonomousGroupOwnerEnabled(true);
if (FAILED(hr))
{
throw WlanHostedNetworkException("Set is autonomous group owner for WiFiDirectAdvertisement failed", hr);
}
hr = _advertisement->get_LegacySettings(_legacySettings.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get legacy settings for WiFiDirectAdvertisement failed", hr);
}
// Must enable legacy settings so that non-Wi-Fi Direct peers can connect in legacy mode
hr = _legacySettings->put_IsEnabled(true);
if (FAILED(hr))
{
throw WlanHostedNetworkException("Set is enabled for WiFiDirectLegacySettings failed", hr);
}
HString hstrSSID;
HString hstrPassphrase;
// Either specify an SSID, or read the randomly generated one
if (_ssidProvided)
{
hr = hstrSSID.Set(_ssid.c_str());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Failed to create HSTRING representation for SSID", hr);
}
hr = _legacySettings->put_Ssid(hstrSSID.Get());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Set SSID for WiFiDirectLegacySettings failed", hr);
}
}
else
{
hr = _legacySettings->get_Ssid(hstrSSID.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get SSID for WiFiDirectLegacySettings failed", hr);
}
_ssid = hstrSSID.GetRawBuffer(nullptr);
}
// Either specify a passphrase, or read the randomly generated one
ComPtr<IPasswordCredential> passwordCredential;
hr = _legacySettings->get_Passphrase(passwordCredential.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get Passphrase for WiFiDirectLegacySettings failed", hr);
}
if (_passphraseProvided)
{
hr = hstrPassphrase.Set(_passphrase.c_str());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Failed to create HSTRING representation for Passphrase", hr);
}
hr = passwordCredential->put_Password(hstrPassphrase.Get());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Set Passphrase for WiFiDirectLegacySettings failed", hr);
}
}
else
{
hr = passwordCredential->get_Password(hstrPassphrase.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get Passphrase for WiFiDirectLegacySettings failed", hr);
}
_passphrase = hstrPassphrase.GetRawBuffer(nullptr);
}
// Start the advertisement, which will create an access point that other peers can connect to
hr = _publisher->Start();
if (FAILED(hr))
{
throw WlanHostedNetworkException("Start WiFiDirectAdvertisementPublisher failed", hr);
}
}
void WlanHostedNetworkHelper::Stop()
{
HRESULT hr = S_OK;
// Call stop on the publisher and expect the status changed callback
if (_publisher.Get() != nullptr)
{
hr = _publisher->Stop();
if (FAILED(hr))
{
throw WlanHostedNetworkException("Stop WiFiDirectAdvertisementPublisher failed", hr);
}
}
else
{
throw WlanHostedNetworkException("WiFiDirectAdvertisementPublisher is not running", hr);
}
}
void WlanHostedNetworkHelper::StartListener()
{
HRESULT hr = S_OK;
// Create WiFiDirectConnectionListener
hr = Windows::Foundation::ActivateInstance(HStringReference(RuntimeClass_Windows_Devices_WiFiDirect_WiFiDirectConnectionListener).Get(), &_connectionListener);
if (FAILED(hr))
{
throw WlanHostedNetworkException("ActivateInstance for WiFiDirectConnectionListener failed", hr);
}
hr = _connectionListener->add_ConnectionRequested(
Callback<ConnectionRequestedHandler>([this](IWiFiDirectConnectionListener* sender, IWiFiDirectConnectionRequestedEventArgs* args) -> HRESULT
{
HRESULT hr = S_OK;
ComPtr<IWiFiDirectConnectionRequest> request;
ComPtr<IDeviceInformation> deviceInformation;
HString deviceId;
ComPtr<IWiFiDirectDeviceStatics> wfdStatics;
if (_listener != nullptr)
{
_listener->LogMessage(L"Connection Requested...");
}
bool acceptConnection = true;
if (!_autoAccept && _prompt != nullptr)
{
acceptConnection = _prompt->AcceptIncommingConnection();
}
try
{
hr = args->GetConnectionRequest(request.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get connection request for ConnectionRequestedEventArgs failed", hr);
}
if (acceptConnection)
{
hr = request->get_DeviceInformation(deviceInformation.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get device information for ConnectionRequest failed", hr);
}
hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Devices_WiFiDirect_WiFiDirectDevice).Get(), &wfdStatics);
if (FAILED(hr))
{
throw WlanHostedNetworkException("GetActivationFactory for WiFiDirectDevice failed", hr);
}
hr = deviceInformation->get_Id(deviceId.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get ID for DeviceInformation failed", hr);
}
ComPtr<IAsyncOperation<WiFiDirectDevice*>> asyncAction;
hr = wfdStatics->FromIdAsync(deviceId.Get(), &asyncAction);
if (FAILED(hr))
{
throw WlanHostedNetworkException("From ID Async for WiFiDirectDevice failed", hr);
}
hr = asyncAction->put_Completed(Callback<FromIdAsyncHandler>([this](IAsyncOperation<WiFiDirectDevice*>* pHandler, AsyncStatus status) -> HRESULT
{
HRESULT hr = S_OK;
ComPtr<IWiFiDirectDevice> wfdDevice;
ComPtr<EndpointPairCollection> endpointPairs;
ComPtr<IEndpointPair> endpointPair;
ComPtr<IHostName> remoteHostName;
HString remoteHostNameDisplay;
HString deviceId;
try
{
if (status == AsyncStatus::Completed)
{
// Get the WiFiDirectDevice object
hr = pHandler->GetResults(wfdDevice.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Put Completed for FromIDAsync operation failed", hr);
}
// Now retrieve the endpoint pairs, which includes the IP address assigned to the peer
hr = wfdDevice->GetConnectionEndpointPairs(endpointPairs.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get EndpointPairs for WiFiDirectDevice failed", hr);
}
hr = endpointPairs->GetAt(0, endpointPair.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get first EndpointPair in collection failed", hr);
}
hr = endpointPair->get_RemoteHostName(remoteHostName.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get Remote HostName for EndpointPair failed", hr);
}
hr = remoteHostName->get_DisplayName(remoteHostNameDisplay.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get Display Name for Remote HostName failed", hr);
}
// Add handler for connection status changed
EventRegistrationToken statusChangedToken;
hr = wfdDevice->add_ConnectionStatusChanged(Callback<ConnectionStatusChangedHandler>([this](IWiFiDirectDevice* sender, IInspectable*) -> HRESULT
{
WiFiDirectConnectionStatus status;
HString deviceId;
HRESULT hr = S_OK;
try
{
hr = sender->get_ConnectionStatus(&status);
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get connection status for peer failed", hr);
}
switch (status)
{
case WiFiDirectConnectionStatus_Connected:
// NO-OP
break;
case WiFiDirectConnectionStatus_Disconnected:
// Clean-up state
hr = sender->get_DeviceId(deviceId.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get Device ID failed", hr);
}
auto itDevice = _connectedDevices.find(deviceId.GetRawBuffer(nullptr));
auto itToken = _connectedDeviceStatusChangedTokens.find(deviceId.GetRawBuffer(nullptr));
if (itToken != _connectedDeviceStatusChangedTokens.end())
{
if (itDevice != _connectedDevices.end())
{
itDevice->second->remove_ConnectionStatusChanged(itToken->second);
}
_connectedDeviceStatusChangedTokens.erase(itToken);
}
if (itDevice != _connectedDevices.end())
{
_connectedDevices.erase(itDevice);
}
// Notify listener of disconnect
if (_listener != nullptr)
{
_listener->OnDeviceDisconnected(deviceId.GetRawBuffer(nullptr));
}
break;
}
}
catch (WlanHostedNetworkException& e)
{
if (_listener != nullptr)
{
std::wostringstream ss;
ss << e.what() << ": " << e.GetErrorCode();
_listener->OnAsyncException(ss.str());
}
return e.GetErrorCode();
}
return hr;
}).Get(), &statusChangedToken);
// Store the connected peer
hr = wfdDevice->get_DeviceId(deviceId.GetAddressOf());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Get Device ID failed", hr);
}
_connectedDevices.insert(std::make_pair(deviceId.GetRawBuffer(nullptr), wfdDevice));
_connectedDeviceStatusChangedTokens.insert(std::make_pair(deviceId.GetRawBuffer(nullptr), statusChangedToken));
// Notify Listener
if (_listener != nullptr)
{
_listener->OnDeviceConnected(remoteHostNameDisplay.GetRawBuffer(nullptr));
}
}
else
{
if (_listener != nullptr)
{
switch (status)
{
case AsyncStatus::Started:
_listener->LogMessage(L"Device connected, status=Started");
break;
case AsyncStatus::Canceled:
_listener->LogMessage(L"Device connected, status=Canceled");
break;
case AsyncStatus::Error:
_listener->LogMessage(L"Device connected, status=Error");
break;
}
}
}
}
catch (WlanHostedNetworkException& e)
{
if (_listener != nullptr)
{
std::wostringstream ss;
ss << e.what() << ": " << e.GetErrorCode();
_listener->OnAsyncException(ss.str());
}
return e.GetErrorCode();
}
return hr;
}).Get());
if (FAILED(hr))
{
throw WlanHostedNetworkException("Put Completed for FromIDAsync operation failed", hr);
}
}
else
{
if (_listener != nullptr)
{
_listener->LogMessage(L"Declined");
}
}
}
catch (WlanHostedNetworkException& e)
{
if (_listener != nullptr)
{
std::wostringstream ss;
ss << e.what() << ": " << e.GetErrorCode();
_listener->OnAsyncException(ss.str());
}
return e.GetErrorCode();
}
return hr;
}).Get(), &_connectionRequestedToken);
if (FAILED(hr))
{
throw WlanHostedNetworkException("Add ConnectionRequested handler for WiFiDirectConnectionListener failed", hr);
}
if (_listener != nullptr)
{
_listener->LogMessage(L"Connection Listener is ready");
}
}
void WlanHostedNetworkHelper::Reset()
{
if (_connectionListener.Get() != nullptr)
{
_connectionListener->remove_ConnectionRequested(_connectionRequestedToken);
}
if (_publisher.Get() != nullptr)
{
_publisher->remove_StatusChanged(_statusChangedToken);
}
_legacySettings.Reset();
_advertisement.Reset();
_publisher.Reset();
_connectionListener.Reset();
_connectedDevices.clear();
}