在现代软件开发中,跨平台技术越来越受到重视。Windows Phone作为一个有潜力的平台,其开发环境允许开发者利用C#和C++技术来构建应用。本文将探讨如何将桌面代码迁移到Windows Phone,并展示如何结合C#和C++来实现高效的数据处理和UI更新。
笔者一直在iPhone平台上进行开发,但对Windows Phone的能力感到好奇。通过阅读微软提供的资料和示例,发现了一种创建DLL的方法,该DLL可以服务于C#用户界面。
为了更直观地描述软件架构,提供了一张清晰的图片。对于真正好奇的读者,可以将xap文件重命名为zip并解压以查看内容。
代码中有许多内容,但主要目标是展示如何访问C++线程并以非阻塞方式将数据获取到C#UI。
为了连接UI,设置了一个回调,以便在工作线程到达数据时获得更新。
worker.OnNewData += worker_OnNewData;
回调是从C++线程中的NativeWorker类调用的。
OnNewData(this, new PropertyChangedEventArgs("DataBuffer"));
在新数据读取并准备好呈现后,更有趣的是通过Buffer类进行数据传输。在C#中看起来简单,但在C++中却变得棘手。
Windows.Storage.Streams.Buffer iBuffer = new Windows.Storage.Streams.Buffer((uint)count);
runtime.FillBuffer(iBuffer);
DataReader reader = DataReader.FromBuffer(iBuffer);
为了获得响应式的UI,需要线程。这也使得这种架构对于流处理或通信项目变得有趣。
Thread workerThread = new Thread(runtime.StartStream);
workerThread.Start();
现在,准备好进行更困难的部分了。
C++组件公开了一个接口和类,通过引用导入。这样就可以创建一个对象并访问函数。
public static NativeBackend runtime = null;
runtime = new NativeBackend();
runtime.setCallback(this);
runtime.SetUrl(s);
实现了接口,以便在新数据到达时可以从C++调用。
namespace WPComponent
{
public interface class INativeInterface
{
void OnNewState(Platform::String^ state);
void OnSendData(int count);
};
}
这个接口也在C#中实现:
class NativeWorker : INativeInterface
public ref class NativeBackend sealed
神秘的关键字是必要的,以便在Windows Runtime中正确公开类。
C++中的简单部分是C#中的接口。它是对C#“可见”的层:接口和类。类内部运行原生C++。重要的是,最顶层的命名空间与组件的名称相同。微软知道为什么 - 不知道。
C++中的困难部分是Run()函数。在演示中,它只是等待并填充缓冲区。这是所有编码人员都可以进行繁重工作的地方:下载、处理或上传。对于上传,Buffer传输只需要反向。
do
{
// do the hard work
i++;
DWORD wait = WaitForSingleObjectEx(hWait, 1000, 0);
::SetEvent(hWait);
// prepare some output
int count = sprintf_s(buffer, "%s. Here it comes from C++ %ld\n", Data, i);
SendData((BYTE*)buffer, count);
} while(Running);
C++中最难的部分是从C++到C#接口复制位的函数。原因是将真正的内存原生位复制到托管运行时是一个大问题。这是“COM的最佳表现”,并且使用智能指针。在这里找到了这个杰出的解决方案。
ComPtr bufferByteAccess;
reinterpret_cast(iBuffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess));
if (bufferByteAccess != NULL)
{
byte* dest = nullptr;
bufferByteAccess->Buffer(&dest);
memcpy(dest, m_pBuffer, m_nBuffer);
iBuffer->Length = m_nBuffer;
}
要理解它,需要知道IInspectable,它在“代码世界”之间架起了桥梁。这段代码真的很强大。
调试工作得很好,但需要选择是调试托管代码还是原生代码。这里有一张截图,可以切换。