[英]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
方法中,如果CoreDispatcher
為null
,則只需Invoke
Action
(或Function
),否則在CoreDispatcher
上Invoke
它。
然后,您可以在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線程的人都可以通過ViewModelLocator
的PortableDispatcher
。 由於注入了對象,他們無需了解任何內容就可以訪問WinRT CoreDispatcher
!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.