【问题标题】:How to add Elements to IVector which is bound to XAML from another thread如何将元素添加到从另一个线程绑定到 XAML 的 IVector
【发布时间】:2018-08-21 03:58:29
【问题描述】:

我目前正在 Visual Studio Community 2017 下为 Windows 10 版本 10.0.16299.0 开发一个 Windows 10 UWP C++ 项目。

以下过程:我的计算机上运行着一个 TCP 服务器,它在某些事件时向连接的客户端发送消息。我正在处理的项目现在有我的 TCPClient,它接收消息并应在 UI 中显示值。

在 MainPage 构造函数中,我创建了一个 std:: 线程,它调用我的“clientThread”函数。在这个函数中,我实例化了我的客户端类的一个对象。连接到服务器后,我创建了一个新的 std:: 线程,该线程执行无限循环。此循环调用“processPacketType”函数,该函数简单地调用一个开关并询问接收到的数据包是否为 ChatMessage 数据包。如果是,则检索该消息。

现在我的问题是:MainPage 类有一个来自我创建的类的私有 AudioSessionViewModel 对象,它返回一个 IVector^。现在我想用我收到的消息来扩展 Vector。使用我的“processPacketType”函数,我只想实例化我的 AudioSession 类的一个新对象并将其添加到向量(我从一个线程传递到另一个线程作为参考)。但是,我收到以下错误消息:SoundManager - Client 中的 0x778B08C2 上引发异常。 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; }
        }
    };
}

解决方案更新: 我终于弄清楚了如何在 C++/CX 中调用调度程序:

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

}));

现在我终于明白为什么我的调用有效了,但只是在循环之外:

我至少假设来自 std:: 线程的调用在 UI 线程中执行第一次执行,并且只有当它到达 .detach() 时,代码才会在新线程中执行。如有错误,请在cmets中指正!

【问题讨论】:

    标签: c++ windows uwp windows-runtime uwp-xaml


    【解决方案1】:

    我认为问题在于您将项目从后台线程添加到 AudioSessions 而它绑定到 xaml;要更改 UI,您需要在 UI 线程中。使用 Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync 进入 UI 线程,然后将其添加到集合中

    【讨论】:

    • 这也是我的第一个想法,但有两个问题:一方面我没有找到任何关于如何在 C++/Cx 中调用调度程序的信息,因为我的 UWP 应用程序是 C++项目。另一方面:在我的“oid Client::clientThread(Client &_client......”中,我写了一个关于这个函数的注释。如果我将项目发送到 While 循环之外的向量,它工作正常,它也显示在UI中。在循环中我收到错误消息,当然只有当我收到来自服务器的消息时。但是完整的功能在另一个线程中,所以我不太明白。
    【解决方案2】:

    用解决方案和解释更新了主要帖子!

    【讨论】:

      猜你喜欢
      • 2011-11-24
      • 2023-04-04
      • 2016-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-04
      相关资源
      最近更新 更多