原文地址:microsoft.github.io/react-nativ…
原文作者:twitter.com/qmatteoq
发布时间:2021年8月5日
从React Native for Windows应用程序中使用Win32功能
如果你已经采用React Native来构建你的Windows应用程序,你会知道最终的输出是一个通用的Windows平台应用程序。这个开发平台可以让你获得Windows生态系统的所有最新增强功能(现代UI平台、通知、与墨迹和Windows Hello等功能的集成等),另外由于应用程序在沙盒中运行,安全性和可靠性更高。然而,在某些情况下,UWP是不够的,你需要执行一个或多个只有Win32生态系统支持的任务:在没有用户干预的情况下处理磁盘上的任何文件,从注册表读取一个键,集成一个不支持Windows Runtime的SDK。
在这篇文章中,我们将探讨一个解决方案,使你能够得到两个世界的最好结果:一个React Native for Windows应用程序,它集成了一个经典的Win32进程。我们将建立一个React Native应用程序的样本,它将能够读取注册表键并显示它。目标是向用户显示存储在 \HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion 蜂巢中的值,它包含了许多关于当前安装的Windows版本的信息。
该解决方案将使用以下组件。
一个经典的Windows进程
我们将建立一个.NET 5.0控制台应用程序,通过针对Windows 10和使用Windows兼容包,它将能够与系统注册表互动。React Native应用程序将向该进程发送我们想要检索的键的名称;该进程将读取其值并将其发回给React Native应用程序。
一个React Native for Windows的应用程序
这将是一个传统的React Native for Windows应用程序。然而,正如我们稍后要看到的,我们必须对模板生成的主机应用做一些改变,以处理与经典Windows进程的通信。
应用服务
应用程序服务是一个UWP功能,它使Windows应用程序能够托管一个或多个后台任务,这些任务可以被其他应用程序所使用。你可以把这个功能看作是REST服务,但在本地暴露。一旦一个暴露了应用服务的UWP应用程序被部署在一台机器上,其他应用程序可以使用它的名字和托管应用程序的包族名称连接到它。一旦通信渠道建立起来,调用的应用程序就可以向App Service发送数据,App Service将对其进行处理并将结果发回。
在我们的环境中,App Service将使我们能够在React Native应用程序和经典的Win32进程之间建立一个通信通道。与传统用法相比,主要区别在于App Service将由同一个包来托管和消费,因为两个进程(UWP和经典Win32)将被打包在一起。
应用服务支持两种方法:程序内(当服务的实现被定义在主应用程序本身)和程序外(当服务的实现被定义在外部Windows Runtime组件中,在不同的进程中执行)。对于我们的方案,我们必须使用proc内的方法,因为我们需要与主应用程序保持直接连接。
本机模块
本机模块是向React Native应用的JavaScript层暴露本机功能的方式。在Windows环境下,本地模块是一个Windows运行时组件,通过装饰代码的属性,可以将方法、属性和事件暴露给JavaScript。本机模块需要作为React Native世界和UWP世界之间的胶水。所有与应用程序服务交互的API都是由Windows Runtime暴露的,因此,我们需要一个中间人,使它们能够被JavaScript访问。
Windows应用打包项目
Windows应用程序打包项目(WAP项目,从现在开始)是Visual Studio中的一个模板,它通常被用来打包成MSIX经典的Windows应用程序。在我们的环境中,我们将使用它把经典的Win32进程和React Native for Windows应用程序捆绑在同一个MSIX包中。
下图显示了所有这些组件是如何紧密结合在一起的。
- 用户启动React Native应用程序的同时,也会启动经典的Win32进程。
- Win32进程,在启动时,使用App Service与React Native应用程序建立通信,并存储一个对通道的引用。
- 用户在React Native应用程序中按下一个按钮。
- 该按钮将调用本地模块暴露的一个方法。该方法将推送一个消息到通信通道,这样它就可以被经典的Win32进程接收。该消息将包含Windows进程必须从注册表中检索的键的信息。
- 经典的Win32进程从注册表中获取所需的键,它将其存储在另一个消息中,并将其发送回通信通道。
- 本机模块使用App Service来保持通信通道的畅通,收到带有检索到的键的消息,并将其发回给JavaScript层。
- React Native应用程序现在可以向用户显示注册表键的值了。
让我们开始构建所有的组件吧!
你在下面看到的所有样本(包括本地模块和React Native主机应用程序)都将基于C#,因为这是我最熟悉的语言。然而,同样的目标也可以用C++模板来实现。
经典的Win32进程
为了这个例子的目的,我们将使用一个.NET 5.0控制台应用程序。用Visual Studio打开你的React Native应用程序的windows文件夹中的解决方案,右击它并选择添加->新项目。选择Console应用程序作为模板。
在开始写一些代码之前,我们需要做一些改变。
- 双击该项目,将TargetFramework从net5.0改为net5.0-windows10.0.19041.0。新的目标将使你能够从你的.NET应用程序中利用Windows 10 APIs。在我们的方案中需要这样做,因为我们需要使用App Service APIs。
- 右击项目,选择管理NuGet包,安装Microsoft.Windows.Compatibility包。它将使我们能够访问API,与Windows注册表进行交互。
- 在项目上点击右键,选择属性,将输出类型从控制台应用程序改为Windows应用程序。由于这一改变,我们的应用程序将无头运行,没有任何可见的用户界面。这有助于实现良好的用户体验,因为我们的经典Win32进程将只在后台运行。所有的用户界面和交互都将由React Naive应用程序处理。
该应用程序将在后台持续运行,直到主React Native应用程序被关闭。为了实现这一目标,我们在一个单独的线程中启动主进程,当与App Service的连接关闭时(这意味着应用程序被关闭),该线程将被终止。这种方法确保了经典的Win32应用程序不会成为一个 "僵尸 "进程,即使主应用程序被关闭,它仍然活着。
static AutoResetEvent appServiceExit;
static AppServiceConnection connection = null;
static void Main(string[] args)
{
appServiceExit = new AutoResetEvent(false);
Thread appServiceThread = new Thread(new ThreadStart(ThreadProc));
appServiceThread.Start();
appServiceExit.WaitOne();
}
上面的代码在一个单独的线程中开始执行一个方法(称为ThreadProc)。我们使用一个AutoResetEvent来阻止Main()方法的执行。这样,我们确保该进程将一直运行,直到与App Service创建的通信通道是活的。
ThreadProc方法是进程的真正 "核心",它负责初始化与App Service的连接。
static async void ThreadProc()
{
connection = new AppServiceConnection();
connection.AppServiceName = "RegistryService";
connection.PackageFamilyName = Windows.ApplicationModel.Package.Current.Id.FamilyName;
connection.RequestReceived += Connection_RequestReceived;
connection.ServiceClosed += Connection_ServiceClosed;
//we open the connection
AppServiceConnectionStatus status = await connection.OpenAsync();
if (status != AppServiceConnectionStatus.Success)
{
//if the connection fails, we terminate the Win32 process
appServiceExit.Set();
}
}
我们创建一个新的AppServiceConnection,并按以下方式配置它。
- 我们使用AppServiceName属性指定我们要连接的App服务的名称。这个名字必须与我们在稍后阶段要包含在Windows应用程序打包项目的清单中的名字相匹配。
- 我们指定承载App服务的应用程序的包族名称。在我们的方案中,由于React Native应用程序和经典的Win32进程被托管在同一个包中,我们可以使用Windows.ApplicationModel.Package APIs来检索它。
- 我们订阅了两个事件。RequestReceived,当React Native应用程序发送消息时被触发;ServiceClosed,当App服务通道关闭时被触发。
然后我们使用OpenAsync()方法来打开连接。如果不成功,这个过程就没有必要继续存在,所以我们在AutoResetEvent对象上调用Set(),这样Main()方法就可以终止了。
现在让我们看看这两个事件处理程序的实现。第一个,ServiceClosed,是最简单的一个。
private static void Connection_ServiceClosed(AppServiceConnection sender, AppServiceClosedEventArgs args)
{
//when the connection with the App Service is closed, we terminate the Win32 process
appServiceExit.Set();
}
如果与App服务的通信中断,我们只需在AutoResetEvent对象上调用Set(),这样进程就可以终止了。
第二个,RequestReceived,反而是与React Native应用发生通信的地方。
private static async void Connection_RequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
string key = args.Request.Message["RegistryKeyName"].ToString();
var hive = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", false);
string value = hive.GetValue(key).ToString();
ValueSet valueSet = new ValueSet
{
{ "RegistryKeyValue", value }
};
await args.Request.SendResponseAsync(valueSet);
}
从事件参数的Request.Message字典中,我们可以检索到主React Native应用程序传递的任何信息。在我们的方案中,请求的注册表键的名称将被存储在一个以RegistryKeyName为键的项目中。我们使用这些信息连接到注册表(使用注册表API),并从SOFTWARE\Microsoft\Windows NT\CurrentVersion中检索这个键,这个蜂巢包含了所有关于当前安装的Windows版本的信息。
一旦我们检索到这个键,我们就把它存储在一个新的消息(ValueSet对象)中,键是RegistryKeyValue。最后,我们用SendResponseAsync()方法把它送回去,这样调用者(在我们的例子中,本地模块)就可以使用它。
React Native for Windows应用程序
当经典的Windows应用程序调用OpenAsync()方法连接到App Service时,主机UWP应用程序将被唤醒以作出回应(记得我们使用的是一个proc内的App Service)。这种情况由一个名为OnBackgroundActivated的特殊事件来处理,你可以在主程序的App类中订阅该事件。这就是实现。
protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
{
base.OnBackgroundActivated(args);
if (args.TaskInstance.TriggerDetails is AppServiceTriggerDetails details)
{
appServiceDeferral = args.TaskInstance.GetDeferral();
var ns = ReactPropertyBagHelper.GetNamespace("RegistryChannel");
var name = ReactPropertyBagHelper.GetName(ns, "AppServiceConnection");
InstanceSettings.Properties.Set(name, details.AppServiceConnection);
}
}
首先,我们检查激活的类型是否是AppServiceTriggerDetails,因为应用程序可能有其他后台激活入口。如果是这样的话,我们就检索出一个延时,我们把它存储为一个类变量。
sealed partial class App : ReactApplication
{
private BackgroundTaskDeferral appServiceDeferral;
public App()
{
// ...
}
// ...
}
延迟是通用Windows平台使用的一个概念,用于正确处理后台任务中的异步方法。因为,当一个异步方法被启动时,它的执行被委托给另一个线程,后台任务可能认为操作已经完成,所以Windows会终止它。为了避免这种情况,我们使用一个延迟器,通过在最后调用Complete()方法,手动告诉后台任务操作何时真正完成。然而,在我们的方案中,我们只需要调用GetDeferral()来获得一个,但我们实际上不需要完成它,因为我们希望我们的通道保持活力,直到应用程序运行。如果我们不这样做,通道会打开,但几秒钟后就会关闭。
代码的最后一段介绍了一个新的概念,即React属性包。这是一个包含在React Native中的功能,它可以在整个React Native应用中分享键/值对,包括其模块。我们为什么需要它?因为我们正在建立一个React Native应用程序,UWP应用程序将不包含与App Service交互的实际代码,但它将只是托管JavaScript层。因此,通信将由本地模块处理。感谢React属性包,我们可以在主机中存储对App Service的引用,但让本地模块使用它。
代码创建了一个新的属性包,它由一个命名空间(RegistryChannel)和一个名称(AppServiceConnection)组成。然后,使用InstanceSettings静态类,我们通过调用Set()方法在当前实例中创建属性包,并将我们刚刚创建的App Service通道作为对象进行存储。
本地模块
现在我们已经准备好与我们之前建立的经典Win32进程进行交互。在普通的UWP场景中,我们会在主程序中直接与App Service进行交互。然而,在React Native for Windows中,UWP应用程序只是JavaScript层的宿主,所以我们需要建立一个本地模块,将我们需要的API暴露给JavaScript。
下一步是向你的解决方案添加一个本地模块。这在官方文档中有详细的记录。我将建立的模块是一个C# Windows运行时组件,当然你也可以用C++做同样的事情。
默认情况下,该模块将包含两个类:一个叫ReactPackageProvider,负责处理主应用程序中的注册,一个叫ReactNativeModule,包含模块定义。模板提供的类的默认实现将看起来像这样。
namespace ReactNativeAppServiceModule
{
[ReactModule("ReactNativeAppServiceModule")]
internal sealed class ReactNativeModule
{
private ReactContext _reactContext;
[ReactInitializer]
public void Initialize(ReactContext reactContext)
{
_reactContext = reactContext;
}
[ReactMethod]
public void sampleMethod(string stringArgument, int numberArgument, Action<string> callback)
{
// TODO: Implement some actually useful functionality
callback("Received numberArgument: " + numberArgument + " stringArgument: " + stringArgument);
}
}
}
这个类已经包含了很多我们需要的元素,比如[ReactModule]属性,向JavaScript公开模块;[ReactMethod]属性,向JavaScript公开一个方法;[ReactInitializer]属性,它装饰了让我们访问ReactContext属性的方法。这个对象将包含我们在主程序中设置的属性包。
保持一切原样,但删除sampleMethod()函数。我们将用我们需要暴露给JavaScript的真正的方法来代替它。
让我们从第一个开始。
[ReactMethod("launchFullTrustProcess")]
public async Task LaunchFullTrustProcessAsync()
{
await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync();
}
这个方法使用FullTrustProcessLauncher API来从UWP应用程序中启动一个经典的Win32进程。我们只需要按原样调用它。我们将在清单中指定以后要启动的进程的信息。之后,我们将在应用程序启动时从JavaScript层调用该方法。默认情况下,FullTrustProcessLauncher类不会被发现。原因是这个API默认没有包含在UWP中,但它是桌面的特定扩展的一部分。因此,你必须右击本地模块的项目,选择添加引用,并在Universal Windows -> Extensions部分,点击最新版本的Windows Desktop Extension for UWP。
现在让我们看看第二个方法。
[ReactMethod("getRegistryKey")]
public async Task<string> GetRegistryKey(string key)
{
var ns = ReactPropertyBagHelper.GetNamespace("RegistryChannel");
var name = ReactPropertyBagHelper.GetName(ns, "AppServiceConnection");
var content = _reactContext.Handle.Properties.Get(name);
var _connection = content as AppServiceConnection;
ValueSet valueSet = new ValueSet
{
{ "RegistryKeyName", key }
};
var result = await _connection.SendMessageAsync(valueSet);
string message = result.Message["RegistryKeyValue"].ToString();
return message;
}
这是我们在React Native应用程序中调用的方法,当我们想检索一个注册表键的值时。通过我们在模块初始化时获取的ReactContext对象,我们可以使用属性集合来访问属性包。我们在RegistryChannel命名空间中寻找一个叫做AppServiceConnection的,它包含对我们的App Service通道的引用。一旦我们有了这个通道,我们就可以使用SendMessageAsync()方法向经典的Win32进程发送一条带有我们想要检索的键的名称(封装在ValueSet对象中)的消息。Win32进程将收到该消息,它将从注册表中检索所请求的键的值,然后它将把它作为SendMessageAsync()方法的结果发回给我们的本地模块。响应将包含在消息集合中,键值为RegistryKeyValue:我们只需将这个值返回给JavaScript层,这样React Native应用就可以显示它。
还有最后一步要做。由于我们已经手动创建了这个模块,我们还必须在主程序中手动注册它。第一步,右击主应用程序,选择添加引用,并选择本地模块。然后移动到App类,在构造函数中,在InitializeComponent()方法被调用之前添加以下一行。
PackageProviders.Add(new ReactNativeAppServiceModule.ReactPackageProvider());
如果你用不同的名字创建了本地模块,确保用正确的命名空间替换ReactNativeAppServiceModule。
Windows应用程序打包项目
在我们尝试我们的解决方案之前的最后一步是添加一个Windows应用程序打包项目,这将帮助我们把主机UWP应用程序和经典的Win32进程放在同一个包里。在解决方案上点击右键,选择添加->新项目,寻找名为Windows应用程序打包项目的模板。一旦你添加了它,右击它并选择添加参考。现在你必须从你的解决方案中选择两个项目:React Native主机UWP应用和经典Win32进程。一旦完成,展开应用程序节点,右击主机UWP应用程序的名称,选择设置为入口点。这将确保,当你点击开始菜单中的应用程序图标时,主React Native应用程序将被启动。
现在我们要对清单做一些修改。当你在像我们这样的场景中引入Windows应用程序打包项目时,你必须注意的一个重要区别是,它的清单成为主要清单。因此,如果您之前定制了主机UWP应用的清单(例如,通过为图标添加资产、改变标识或添加一些功能),您就必须将它们移植到WAP项目的清单中。出于这个原因,请确保在WAP项目的Package.appxmanifest文件中,而不是在主UWP应用的文件中做以下修改。
第一个变化是声明应用程序服务。如果你还记得,以前在代码中,我们必须指定两个信息,以便从Win32经典进程连接到App服务:名称和包家族名称。名称是在清单中准确定义的。双击Package.appxmanifest文件,移到Declarations标签,从下拉菜单中选择App Service,然后点击Add。唯一需要的字段是名称,我们必须用之前在代码中指定的相同名称来填写,也就是RegistryService。由于这是一个程序内的应用服务,我们不需要指定任何其他信息。
第二个变化是声明哪个是我们在使用FullTrustLauncher API时要启动的经典Win32进程。这个功能不被Visual Studio UI支持,所以你必须右击Package.appxmanifest文件,选择查看代码。你会发现一个名为Extensions的部分,其中App Service已经被声明。正好在下面,添加以下条目。
<Extensions>
<uap:Extension Category="windows.appService">
<uap:AppService Name="RegistryService"/>
</uap:Extension>
<desktop:Extension Category="windows.fullTrustProcess" Executable="RegistryApp\RegistryApp.exe" />
</Extensions>
Executable属性指定了包内经典Win32进程的路径。默认情况下,WAP项目将所有的构建输出放在与项目同名的文件夹内。因此,我们的经典Win32应用程序将在RegistryApp\RegistryApp.exe这个路径下可用。为了使这个扩展工作,你还必须在顶部声明桌面命名空间,因为它默认不包括在内。
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
IgnorableNamespaces="uap desktop">
我们需要做的最后一个改变是添加一个名为runFullTrust的功能,这使得我们的软件包能够包括一个经典的Win32组件,而不仅仅是一个UWP应用程序。然而,WAP项目中包含的默认清单已经定义了这一点。你可以在清单的Capabilities部分看到它。
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="runFullTrust" />
</Capabilities>
React Native应用程序
最后,我们现在有了所有的拼图碎片,我们可以开始在JavaScript层工作了。作为第一步,我们需要在React Native应用程序启动时启动经典的Win32进程。如果你使用的是功能组件,你可以使用useEffect钩;如果你使用的是类组件,你可以使用componentDidMount事件。在我的案例中,我打算采用第一种方案,所以我在我的应用程序中加入了以下函数。
useEffect(() => {
async function launchProcess() {
await NativeModules.ReactNativeAppServiceModule.launchFullTrustProcess();
}
launchProcess();
}, []);
如果你以前使用过本地模块,这段代码应该很容易理解。我们使用React中的NativeModules API来访问我们的本地模块,通过指定其名称(ReactNativeAppServiceModule,我们使用[ReactModule]属性来设置)和方法(launchFullTrustProcess(),我们使用[ReactMethod]属性设置)。
结果是,当你从开始菜单启动主应用程序时,经典的Win32进程也将被启动。然而,由于它没有任何用户界面,它将在后台运行。你可以用任务管理器看到它。
现在我们需要在组件的状态中存储两个信息:我们想得到的注册表键的名称(将由用户使用TextInput控件填写)和它的值,它将由我们的本地模块返回。因为我使用的是一个功能组件,所以我打算使用useState钩子。
const[registryKeyName, setRegistryKeyName] = useState('');
const[registryKeyValue, setRegistryKeyValue] = useState('');
现在让我们定义一个函数,它将调用本地模块来获取注册表的密钥,并将结果存储在状态中。
const getRegistryKey = async() => {
var result = await NativeModules.ReactNativeAppServiceModule.getRegistryKey(registryKeyName);
setRegistryKeyValue(result);
}
这个方法与之前的方法相同:唯一不同的是,这次我们调用了getRegistryKey()函数,它需要我们想从HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion hive中获取的密钥名称作为参数。
现在让我们建立一个最小的用户界面:一个TextInput控件,它将读取注册表键的名称并将其存储在状态中;一个Button,它将调用getRegistryKey()函数;一个Text,它将显示该函数返回的值。
<View>
<TextInput onChangeText={text => setRegistryKeyName(text)} />
<Button title="Get registry key" onPress={getRegistryKey} />
<Text>{registryKeyValue}</Text>
</View>
运行该应用程序
我们已经准备好测试我们的工作了! 首先,进入Visual Studio,右击Windows应用程序打包项目,选择部署。你可能会得到以下错误。
Error Task 'AddProjectMetadata' failed. The expression "[MSBuild]::MakeRelative(C:\ReactCSharp, *Undefined*)" cannot be evaluated. Illegal characters in path. C:\ReactCSharp\node_modules\react-native-windows\PropertySheets\Autolink.props
ReactCSharp.Package C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\DesktopBridge\Microsoft.DesktopBridge.targets 408
Error MSB4184 The expression "[MSBuild]::MakeRelative(C:\ReactCSharp, *Undefined*)" cannot be evaluated. Illegal characters in path. C:\ReactCSharp\node_modules\react-native-windows\PropertySheets\Autolink.props ReactCSharp.Package C:\ReactCSharp\node_modules\react-native-windows\PropertySheets\Autolink.props 12
如果是这种情况,这是一个已经解决的已知问题,但在最新的稳定的React Native for Windows版本中还没有修复。不过,解决的方法很简单。打开node_modules\react-native-windows\PropertySheets\Autolink.props路径下的文件,并替换以下行。
<AutolinkCommandArgs Condition="'$(AutolinkCommandArgs)' == '' And '$(SolutionPath)' != '' And '$(ProjectPath)' != ''">--check --sln $([MSBuild]::MakeRelative($(AutolinkCommandWorkingDir), $(SolutionPath))) --proj $([MSBuild]::MakeRelative($(AutolinkCommandWorkingDir), $(ProjectPath)))</AutolinkCommandArgs>
与
<AutolinkCommandArgs Condition="'$(AutolinkCommandArgs)' == '' And '$(SolutionPath)' != '' And '$(SolutionPath)' != '*Undefined*' And '$(ProjectPath)' != ''">--check --sln $([MSBuild]::MakeRelative($(AutolinkCommandWorkingDir), $(SolutionPath))) --proj $([MSBuild]::MakeRelative($(AutolinkCommandWorkingDir), $(ProjectPath)))</AutolinkCommandArgs>
如果你是第一次构建项目,这将需要一段时间。一旦部署完毕,你会在 "开始 "菜单中发现两个条目:一个是基础主机应用程序(例如appservicedemo),一个是WAP项目(例如appservicedemo.Package)。不要担心,当你生成一个MSIX包进行发布时,这种情况就不会再发生。然而,如果你想在调试时也消除这种混乱,只要回到Visual Studio,选择Build -> Configuration Manager,并确保只为WAP项目打开部署标志。
在点击WAP项目的 "开始 "菜单项之前,确保在包含你的项目的文件夹上打开一个终端,运行yarn start来启动Metro打包器。如果一切顺利,应用程序的用户界面应该显示出来。在文本框中指定HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion注册表结构中的一个键的名称(例如,BuildLab),然后按下按钮。如果你所做的一切都正确,键值将被显示出来。
如果你想创建一个发布包(在微软商店发布或在其他机器上侧载),请确保从WAP项目而不是从主机应用中启动向导(发布-->创建应用包),否则经典的Win32进程将不会被包括在内。
总结
这是一个漫长的旅程,但现在我们有了一个可以与Win32生态系统交互的React Native for Windows应用程序。在这篇文章中,我们建立了一个基于注册表访问的示例场景,但机会是无限的!你可以整合那些没有注册表的SDK。你可以集成通用Windows平台不支持的SDK;或者用不支持的功能增强你的React Native应用。你可以在GitHub上找到为本文建立的示例项目。
荣誉
架构中使用的图标。
- React扁平图标:Icon Mafia
- Windows 扁平标志图标 Icon Mafia
- 包裹彩色轮廓图标 作者:Vichanon Chaimsuk