UWP AppService 到 C++ SendRequestAsync 挂起/从未得到响应
Posted
技术标签:
【中文标题】UWP AppService 到 C++ SendRequestAsync 挂起/从未得到响应【英文标题】:UWP AppService to C++ SendRequestAsync hangs / never gets response 【发布时间】: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) 的内容?
【问题讨论】:
【参考方案1】:在 C++ 桌面应用中调用 args.Request().SendResponseAsync(result);
方法后,请在 C# UWP 应用中处理 App.Connection.RequestReceived 事件。当消息从应用服务连接的另一个端点到达时,AppServiceConnection.RequestReceived event 将被触发。那是您可以获取从 C++ 应用程序发送的消息的地方。
像这样:
AppServiceConnection.RequestReceived += AppServiceConnection_RequestReceived;
private async void AppServiceConnection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
var value = args.Request.Message["KEY"];
// do you own logic
【讨论】:
我相信在上面的例子中函数设置为 OnAppServiceRequestReceived,我在 C# 端的那个函数中设置了一个断点,它从来没有被命中,所以我不认为它在这种情况下被调用C++ 代码正在响应请求。我的理解是,这只适用于不请自来的消息?我这么说是因为 C# 端的调用函数也永远不会返回。对 AppServiceConnection.SendMessageAsync() 的调用仍在等待调用者响应,因此该线程仍将永远阻塞。 好的。请您再做一个简单的测试来检查 C++ 组件是否可以成功发送应用服务请求?请使用SendMessageAsync()
方法从C++组件主动发送应用服务请求。然后检查RequestReceived
事件是否被触发。
是的,C++ 端能够向 C# 端发送请求。值集结果; result.Insert(L"status_code", box_value(L"1")); auto rsp = co_await connection.SendMessageAsync(result);导致 OnAppServiceRequestReceived() 被接收,在 C# 端,响应(在 C# 中使用 Request.SendResponseAsync(response) 发送)然后在 C++ 端接收,就像我期望的那样,在上面的“rsp”变量中,它包含响应变量集的内容。
我觉得问题一定出在 C++ 方面。所以我在那里简化了几次函数,最简单的两个选项:``` fire_and_forget OnRequestReceived(AppServiceConnection const&, AppServiceRequestReceivedEventArgs const& args) auto messageDeferral = args.GetDeferral();值集结果; result.Insert(L"status_code", box_value(200)); co_await args.Request().SendResponseAsync(result); messageDeferral.Complete(); ``` 我尝试了使用和不使用 SendResponseAsync,C# 永远不会返回,永远挂起。
根据您的描述,C++端可以成功发送应用服务请求并收到来自C#端的响应,但该问题仅在C++组件发送响应时出现。那很有意思。普通的 C# 控制台应用程序怎么样,它可以正常工作吗?如果使用普通的 C# 控制台应用程序有效,则问题应该与您所说的 C++ 组件有关。以上是关于UWP AppService 到 C++ SendRequestAsync 挂起/从未得到响应的主要内容,如果未能解决你的问题,请参考以下文章
UWP使用AppService向另一个UWP客户端应用程序提供服务
在 UWP 应用中创建使用调试 App Service (应用服务)