【发布时间】:2021-12-04 02:09:41
【问题描述】:
我这里有 2 个有问题的应用程序,一个是 C# UWP 的 UI 应用程序,它通过 LaunchFullTrustProcessForCurrentAppAsync API 启动后端 EXE(C++、WinRT)。后端 EXE 然后创建一个进程内 AppService 连接返回到 UI 以进行来回通信。组件建立连接,当 UWP UI App 调用 SendRequestAsync 时,后端在其 RequestReceived 回调中接收它。然后它构建一个响应并从 OnRequestReceived 返回。但是调用 UI SendRequestAsync() 永远不会返回,它永远挂起。
来自 UWP C# 应用的代码
class FrontendAppService
{
private AppServiceConnection AppServiceConnection { get; set; }
private BackgroundTaskDeferral AppServiceDeferral { get; set; }
public event EventHandler<string> MessageReceivedEvent;
public bool Connected
{
get
{
return AppServiceConnection != null;
}
}
private static FrontendAppService instance;
public static FrontendAppService Instance
{
get
{
if (instance == null)
{
instance = new FrontendAppService();
}
return instance;
}
}
private FrontendAppService()
{
AppServiceConnection = null;
}
public void BackgroundActivated(IBackgroundTaskInstance taskInstance)
{
if (taskInstance.TriggerDetails is AppServiceTriggerDetails)
{
AppServiceTriggerDetails appService = taskInstance.TriggerDetails as AppServiceTriggerDetails;
AppServiceDeferral = taskInstance.GetDeferral();
AppServiceConnection = appService.AppServiceConnection;
AppServiceConnection.RequestReceived += OnAppServiceRequestReceived;
AppServiceConnection.ServiceClosed += AppServiceConnection_ServiceClosed;
}
}
private void OnAppServiceRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
}
private void AppServiceConnection_ServiceClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
{
AppServiceDeferral.Complete();
AppServiceConnection = null;
}
public async Task<AppServiceResponse> SendRequestAsync(Windows.Foundation.Collections.ValueSet message)
{
return await AppServiceConnection.SendMessageAsync(message);
}
}
来自 C++ 后端的代码
struct AppServiceServerImpl : winrt::implements<AppServiceServerImpl, IInspectable>
{
private:
Windows::ApplicationModel::AppService::AppServiceConnection connection{ nullptr };
fire_and_forget OnServiceClosed(AppServiceConnection const&, AppServiceClosedEventArgs const&)
{
LOG(AixLog::Severity::info) << "AppService connection lost" << std::endl;
auto lifetime = get_strong();
//Close the connection reference we're holding
if (connection != nullptr)
{
connection.Close();
connection = nullptr;
}
co_return;
}
fire_and_forget OnRequestReceived(AppServiceConnection const&, AppServiceRequestReceivedEventArgs const& args)
{
LOG(AixLog::Severity::info) << "AppService message received" << std::endl;
//Get a deferral so we can use an awaitable API to respond to the message
auto messageDeferral = args.GetDeferral();
try
{
ValueSet input = args.Request().Message();
winrt::hstring action;
winrt::hstring path;
winrt::hstring body;
winrt::com_array<winrt::hstring> headerNames;
winrt::com_array<winrt::hstring> headerValues;
// Parse out the HTTP data
if( input.HasKey(L"action") )
action = input.TryLookup(L"action").try_as<IReference<winrt::hstring>>().GetString();
if (input.HasKey(L"path"))
path = input.TryLookup(L"path").try_as<IReference<winrt::hstring>>().GetString();
if (input.HasKey(L"body"))
body = input.TryLookup(L"body").try_as<IReference<winrt::hstring>>().GetString();
std::string url = "http://localhost:" + std::to_string(BackendConfig::GlobalBackendConfig.BackendApiPort) + "/" + winrt::to_string(path);
Windows::Foundation::Uri uri{ winrt::to_hstring(url) };
Windows::Web::Http::HttpClient httpClient{};
std::wstring httpResponseBody;
int httpStatusCode;
std::wstring httpStatusText;
// Always catch network exceptions for async methods
try
{
Windows::Web::Http::HttpResponseMessage httpResponseMessage;
Windows::Web::Http::HttpStringContent postContent{ winrt::to_hstring(body) };
try
{
if (action == L"GET")
{
httpResponseMessage = httpClient.GetAsync(uri).get();
}
else
{
LOG(AixLog::Severity::error) << "AppService unknown action received! " << winrt::to_string(action) << std::endl;
throw std::exception("AppService unknown action received!");
}
httpResponseBody = httpResponseMessage.Content().ReadAsStringAsync().get();
httpStatusCode = (int)httpResponseMessage.StatusCode();
httpStatusText = httpResponseMessage.ReasonPhrase();
}
catch (winrt::hresult_error const& ex)
{
httpResponseBody = ex.message();
httpStatusCode = 500;
httpStatusText = L"Exception handling request";
}
}
catch (winrt::hresult_error const& ex)
{
httpResponseBody = ex.message();
httpStatusCode = 500;
httpStatusText = L"Exception handling request";
}
//Create the response
ValueSet result;
result.Insert(L"status_code", box_value(httpStatusCode));
result.Insert(L"status_text", box_value(httpStatusText));
result.Insert(L"body", box_value(httpResponseBody));
//Send the response
auto r = co_await args.Request().SendResponseAsync(result);
if (r != AppServiceResponseStatus::Success)
{
LOG(AixLog::Severity::info) << "Response send failed, status=" << (int)r << std::endl;
}
}
catch (std::exception e)
{
LOG(AixLog::Severity::error) << "Exception dealing with a response on AppService: " << e.what() << std::endl;
}
LOG(AixLog::Severity::info) << "Response sent! " << std::endl;
// Signal when complete
try
{
messageDeferral.Complete();
}
catch( std::exception e )
{
LOG(AixLog::Severity::error) << "Failed too complete deferral! " << e.what() << std::endl;
}
}
public:
fire_and_forget ConnectToAppServiceAsync(std::string frontendAppFamily)
{
auto lifetime = get_strong();
bool connected = false;
//Is a connection already open?
if (connection != nullptr)
{
LOG(AixLog::Severity::error) << "AppService connection already exists" << std::endl;
co_return;
}
//Set up a new app service connection
connection = AppServiceConnection();
connection.AppServiceName(L"com.frontend");
connection.PackageFamilyName(winrt::to_hstring(frontendAppFamily));
connection.ServiceClosed({ get_weak(), &AppServiceServerImpl::OnServiceClosed });
connection.RequestReceived({ get_weak(), &AppServiceServerImpl::OnRequestReceived });
while (!connected)
{
AppServiceConnectionStatus status = co_await connection.OpenAsync();
//"connection" may have been nulled out while we were awaiting.
if (connection == nullptr)
{
LOG(AixLog::Severity::error) << "AppService Connection was closed" << std::endl;
co_return;
}
//If the new connection opened successfully we're done here
if (status == AppServiceConnectionStatus::Success)
{
LOG(AixLog::Severity::info) << "AppService Connection is open" << std::endl;
connected = true;
}
else
{
//Something went wrong. Lets figure out what it was and show the
//user a meaningful message
switch (status)
{
case AppServiceConnectionStatus::AppNotInstalled:
LOG(AixLog::Severity::error) << "The app AppServicesProvider is not installed. Reinstall on this device and try again." << std::endl;
break;
case AppServiceConnectionStatus::AppUnavailable:
LOG(AixLog::Severity::error) << "The app AppServicesProvider is not available. This could be because it is currently being updated or was installed to a removable device that is no longer available." << std::endl;
break;
case AppServiceConnectionStatus::AppServiceUnavailable:
LOG(AixLog::Severity::error) << "The app AppServicesProvider is installed but it does not provide the app service " << connection.AppServiceName().c_str() << std::endl;
break;
default:
case AppServiceConnectionStatus::Unknown:
LOG(AixLog::Severity::error) << "An unknown error occurred while we were trying to open an AppServiceConnection." << std::endl;
break;
}
//Clean up before we go
//connection.Close();
//connection = nullptr;
}
Sleep(500);
}
}
void CloseAppServiceAsync()
{
LOG(AixLog::Severity::info) << "AppService Connection is now closing" << std::endl;
if (connection == nullptr)
{
LOG(AixLog::Severity::error) << "There's no open connection to close" << std::endl;
return;
}
connection.Close();
connection = nullptr;
}
};
就本示例而言,它只是复制一个 HTTP GET 请求,一切正常。后端的控制台输出如下所示:
Starting AppServiceServer Server
AppService Connection is open
AppService message received
Response sent!
从代码来看,C#调用
var response = await FrontendAppService.Instance.SendRequestAsync(input);
这是永远不会返回的函数。当后端应用程序在 OnRequestReceived 中完成延迟时,我会期望它返回?理想情况下是调用 Request().SendResponseAsync(result) 的内容?
【问题讨论】:
标签: c# c++ uwp windows-runtime