Как добавить элементы в IVector, который связан с XAML из другого потока

В настоящее время я работаю над проектом Windows 10 UWP C ++ в Visual Studio Community 2017 для Windows 10 версии 10.0.16299.0.

Следующая процедура: у меня на компьютере работает TCP-сервер, который отправляет сообщение подключенным клиентам при определенных событиях. В проекте, над которым я сейчас работаю, есть мой TCPClient, который получает сообщение и должен отображать значения в пользовательском интерфейсе.

В конструкторе MainPage я создаю std :: thread, который вызывает мою функцию «clientThread». В этой функции я создаю экземпляр объекта моего клиентского класса. Как только я подключился к серверу, я создаю новый std :: thread, который выполняет бесконечный цикл. Этот цикл вызывает функцию «processPacketType», которая просто вызывает коммутатор и спрашивает, является ли полученный пакет пакетом ChatMessage или нет. Если так, сообщение получено.

Теперь к моей проблеме: у класса MainPage есть частный объект AudioSessionViewModel из моего созданного класса, который возвращает IVector ^. Теперь я хотел бы расширить Вектор полученным сообщением. С моей функцией «processPacketType» я просто хочу создать новый объект моего класса AudioSession и добавить его в вектор (который я передал из потока в поток в качестве ссылки). Тем не менее я получаю следующее сообщение об ошибке: Возникла исключительная ситуация на 0x778B08C2 в SoundManager — Client. exe: исключение Microsoft C ++: Platform :: InvalidCastException ^ для места хранения 0x0F15EC3C. HRESULT: 0x80004002 Интерфейс не поддерживается
Информация WinRT: интерфейс не поддерживается

Самое смешное: моя функция processPacketType вызывается из моего бесконечного цикла. Если я передаю вновь созданный объект в вектор вне цикла, он работает. Но если я сделаю это сам в цикле, появится сообщение об ошибке.

Проблему действительно сложно объяснить, но я надеюсь, вы понимаете, что мне нужно.

//MainPage.xaml.h
namespace SoundManager___Client {
public ref class MainPage sealed {
public:
MainPage();

property AudioSessionViewModel^ ViewModel {
AudioSessionViewModel^ get() { return this->viewModel; };
}

private:
AudioSessionViewModel^ viewModel;
};
}

//MainPage.xaml.cpp
#include "pch.h"#include "MainPage.xaml.h"
using namespace SoundManager___Client;

using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;

void createClient(AudioSessionViewModel^ audioSessionViewModel);

MainPage::MainPage() {
InitializeComponent();

this->viewModel = ref new AudioSessionViewModel();

std::thread client(createClient, this->viewModel);
client.detach();
}

void createClient(AudioSessionViewModel^ audioSessionViewModel) {
Client myClient("127.0.0.1", 1111, audioSessionViewModel);
myClient.Connect();

std::string buffer;

while (true) {
Sleep(10000);
}
}

namespace SoundManager___Client {
bool Client::processPacketType(const PacketType _packetType, AudioSessionViewModel^ audioSessionViewModel) {
switch (_packetType) {
case PacketType::ChatMessage: {
std::string message;
if (!getString(message))
return false;

OutputDebugStringA((LPCSTR)message.c_str());
OutputDebugString(L"\n");
audioSessionViewModel->AudioSessions->Append(ref new AudioSession(L"Test", L"20")); //<--------- Here I get the error

break;
}
default:
OutputDebugString(L"Unrecognized PacketType.\n");
break;
}
return true;
}

void Client::clientThread(Client &_client, AudioSessionViewModel^ audioSessionViewModel) {
PacketType packetType;
//If I put the code to add the element over here, it works. But it doesn't inside the while loop or in the processPacketType which get called inside the loop
while (true) {
if (_client.mTerminateThreads == true)
break;

if (!_client.getPacketType(packetType))
break;

if (!_client.processPacketType(packetType, audioSessionViewModel))
break;
}

OutputDebugString(L"Lost connection to the server.\n");
_client.mTerminateThreads = true;
if (!_client.closeConnection())
OutputDebugString(L"Socket to the server was closed successfully.\n");
else
OutputDebugString(L"Socket was not able to be closed.\n");
}

#pragma once

#include <sstream>

namespace SoundManager___Client {
public ref class AudioSession sealed {
private:
Platform::String^ sessionName;
Platform::String^ sessionVolume;

public:
AudioSession(Platform::String^ sessionName, Platform::String^ sessionVolume) : sessionName{ sessionName }, sessionVolume{ sessionVolume } { }

property Platform::String^ SessionName {
Platform::String^ get() { return this->sessionName; }
}
property Platform::String^ SessionVolume {
Platform::String^ get() { return this->sessionVolume; }
}
property Platform::String^ OneLineSummary {
Platform::String^ get() {
std::wstringstream wstringstream;
wstringstream << this->SessionName->Data();
wstringstream << this->SessionVolume->Data();
return ref new Platform::String(wstringstream.str().c_str());
}
}
};

public ref class AudioSessionViewModel sealed {
private:
Windows::Foundation::Collections::IVector<AudioSession^>^ audioSessions;

public:
AudioSessionViewModel() {
this->audioSessions = ref new Platform::Collections::Vector<AudioSession^>();

AudioSession^ audioSession = ref new AudioSession(L"MASTER", L"100");
this->audioSessions->Append(audioSession);
audioSession = ref new AudioSession(L"CHROME", L"50");
this->audioSessions->Append(audioSession);
}

property Windows::Foundation::Collections::IVector<AudioSession^>^ AudioSessions {
Windows::Foundation::Collections::IVector<AudioSession^>^ get() { return this->audioSessions; }
}
};
}

Решение-Update:
Я наконец понял, как вызвать диспетчер в C ++ / CX:

Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([this]() {

}));

И теперь я наконец понял, почему мой вызов сработал, но только вне цикла:

Я предполагаю, по крайней мере, что вызов из std :: thread выполняет первое выполнение в потоке пользовательского интерфейса, и только когда он достигает .detach (), код выполняется в новом потоке. Если я не прав, прошу исправления в комментариях!

0

Решение

Я думаю, проблема в том, что вы добавляете элементы в AudioSessions из фонового потока, пока он ограничен xaml; Чтобы изменить пользовательский интерфейс, вы должны быть в UI Threads. Используйте Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync, чтобы получить поток пользовательского интерфейса, а затем добавить его в коллекцию.

1

Другие решения

Обновил основной пост с решением и объяснением!

0

По вопросам рекламы [email protected]