簡體   English   中英

在WinRT中集成虛擬機

[英]Integrating a Virtual Machine in WinRT

我在C#中有一台虛擬機,它有兩個構造函數重載,五個事件處理程序和兩個公共屬性。 我已將此虛擬機集成到WinForms和Silverlight應用程序中,甚至在客戶端/服務器設置中將其用作Web應用程序。

界面如下:

建設者

public Engine(Stream gameFile) { .. }
public Engine(Stream gameFile, Stream saveFile) { .. }

大事記

public OutputReady(object sender, OutputReadyEventArgs e);
public LineWanted(object sender, LineWantedEventArgs e);
public KeyWanted(object sender, KeyWantedEventArgs e);
public SaveRequested(object sender, SaveRestoreEventArgs e);
public LoadRequested(object sender, SaveRestoreEventArgs e);

性質

public Dictionary<string, string> ChanneData { get; set; }
public byte[] SaveData { get; set; }

在WinForms和Silverlight中實現此功能時,我不得不使用Invoke或類似方法寫入UI線程。

在WinRT中,UI和后台線程之間的切換隱藏在異步/等待和任務處理之后,而我實際上還無法以我想要的方式移植VM。

我可以像在Silverlight中一樣將vm嵌入到應用程序的MainForm中,但是我想要更多的MVC / Service實現。 我想將VM刪除到具有簡單界面的服務中,以啟動游戲,發送新命令,保存游戲和加載游戲。 MainForm對vm實現一無所知。

我已經解決了超過幾個小時,但對async / await和Tasks的了解還不夠清楚,因此無法正常工作。 我可以啟動引擎並發送命令,但是保存和加載失敗,我認為這是因為異步內容無法正常工作。

WinForms的簡單實現:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
using System.Threading;

using FyreVM;

namespace FyreVMTest
{
    public partial class MainForm : Form
    {
        private Thread vmThread = null;
        private Engine vm = null;
        private AutoResetEvent inputReadyEvent = new AutoResetEvent(false);
        private string command = "";

        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            Stream stream =
                 assembly.GetManifestResourceStream("FyreVMTest.Game.shadow-w8.ulx");
            vm = new Engine(stream);

            vm.KeyWanted += vm_KeyWanted;
            vm.LineWanted += vm_LineWanted;
            vm.LoadRequested += vm_LoadRequested;
            vm.OutputReady += vm_OutputReady;
            vm.SaveRequested += vm_SaveRequested;

            vmThread = new Thread(VMThreadProc);
            vmThread.IsBackground = true;
            vmThread.Start();
        }

        private void VMThreadProc(object dummy)
        {
            try
            {
                vm.Run();
                this.Invoke(new Action(GameFinished));
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void GameFinished()
        {
            this.Close();
        }

        private void RequestLine()
        {
            CommandLine.Focus();
        }

        private void vm_LineWanted(object sender, LineWantedEventArgs e)
        {
            this.Invoke(new Action(RequestLine));
            inputReadyEvent.WaitOne();
            e.Line = command;
        }

        private void vm_KeyWanted(object sender, KeyWantedEventArgs e)
        {
            //this.Invoke(new Action(RequestKey));
            //inputReadyEvent.WaitOne();
            //e.Char = entry[0];
        }

        private void vm_OutputReady(object sender, OutputReadyEventArgs e)
        {
            this.Invoke(new Action<Dictionary<string,string>>(HandleOutput), e.Package);
        }

        private void vm_SaveRequested(object sender, SaveRestoreEventArgs e)
        {
            e.Stream = (Stream)this.Invoke(new Func<Stream>(RequestSaveStream));
        }

        private void vm_LoadRequested(object sender, SaveRestoreEventArgs e)
        {
            e.Stream = (Stream)this.Invoke(new Func<Stream>(RequestLoadStream));
        }

        private Stream RequestSaveStream()
        {
            using (SaveFileDialog dlg = new SaveFileDialog())
            {
                dlg.Title = "Select Save File";
                dlg.Filter = "Textfyre save files (*.tfq)|*.tfq";

                if (dlg.ShowDialog() == DialogResult.Cancel)
                    return null;
                else
                {
                    return new FileStream(dlg.FileName,
                                    FileMode.Create,FileAccess.Write);
                }
            }
        }

        private Stream RequestLoadStream()
        {
            using (OpenFileDialog dlg = new OpenFileDialog())
            {
                dlg.Title = "Load a Saved Game";
                dlg.Filter = "Textfyre save files (*.tfq)|*.tfq";

                if (dlg.ShowDialog() == DialogResult.Cancel)
                    return null;
                else
                {
                    return new FileStream(dlg.FileName, FileMode.Open, FileAccess.Read);
                }
            }
        }

        private void HandleOutput(Dictionary<string, string> package)
        {
            MainOutput.Clear();
            foreach (string key in package.Keys)
            {
                MainOutput.AppendText(key + ": " + package[key]);
            }

        }

        private void GoButton_Click(object sender, EventArgs e)
        {
            command = CommandLine.Text;
            CommandLine.Text = "";
            inputReadyEvent.Set();
        }

    }
}

因此,我正在尋找有關如何在WinRT中實施此操作的指南。 接口是否有問題,還是需要更改它以使其對WinRT更友好? 如果可以,在類中實現此目的的最佳方法是什么,然后如何從MainForm調用該類?

任何指導,不勝感激。

在WinRT中調用UI線程與Silverlight相似,因為您使用了Dispatcher。 在這種情況下,它是一個CoreDispatcher

有幾種訪問方法,但是最好的方法是:

Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher

注意:您只能在應用啟動后以這種方式訪問​​它。 如果在啟動前進行任何VM初始化,則可能會得到一個空引用。

我建議做的是創建一個具有虛擬Invoke方法的自定義Dispatcher類。 創建一個接受CoreDispatcher屬性的子類。 Invoke方法中,如果CoreDispatchernull ,則只需Invoke Action (或Function ),否則在CoreDispatcherInvoke它。

然后,您可以在PCL或其他組件庫中自由創建便攜式Dispatcher訪問器,並讓您的應用向其中注入新的Dispatcher對象(最好是控制反轉)。

為了幫助您入門:

// In your PCL
public abstract class PortableDispatcher
{
    public abstract void Invoke(Action action);
}

// In your app
public class WinRTDispatcher : PortableDispatcher
{
    public override void Invoke(Action action)
    {
        Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.
            RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(action);
    }
}

然后在您的PCL中,您將擁有一個static ViewModelLocator 給它一個類型PortableDispatcher的屬性。 啟動應用程序時,將Locator中的屬性設置為新的WinRTDispatcher 任何想從PCL調度到UI線程的人都可以通過ViewModelLocatorPortableDispatcher 由於注入了對象,他們無需了解任何內容就可以訪問WinRT CoreDispatcher

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM