Aracılığıyla paylaş


Unreal'da WinRT

HoloLens geliştirme sürecinde WinRT kullanarak bir özellik yazmanız gerekebilir. Örneğin, HoloLens uygulamasında dosya iletişim kutusu açmak için winrt/Windows.Storage.Pickers.h üst bilgi dosyasında FileSavePicker gerekir. WinRT, Unreal'ın derleme sisteminde sürüm 4.26'dan itibaren desteklenir.

Standart WinRT API'leri

WinRT kullanmanın en yaygın ve en kolay yolu WinSDK'den yöntemleri çağırmaktır. Bunu yapmak için YourModule.Build.cs dosyasını açın ve aşağıdaki satırları ekleyin:

if (Target.Platform == UnrealTargetPlatform.Win64 || Target.Platform == UnrealTargetPlatform.HoloLens)
{
	// These parameters are mandatory for winrt support
	bEnableExceptions = true;
	bUseUnity = false;
	CppStandard = CppStandardVersion.Cpp17;
	PublicSystemLibraries.AddRange(new string[] { "shlwapi.lib", "runtimeobject.lib" });
	PrivateIncludePaths.Add(Path.Combine(Target.WindowsPlatform.WindowsSdkDir,        
                                        "Include", 
                                        Target.WindowsPlatform.WindowsSdkVersion, 
                                        "cppwinrt"));
}

Ardından, aşağıdaki WinRT üst bilgilerini eklemeniz gerekir:

#if (PLATFORM_WINDOWS || PLATFORM_HOLOLENS) 
//Before writing any code, you need to disable common warnings in WinRT headers
#pragma warning(disable : 5205 4265 4268 4946)

#include "Windows/AllowWindowsPlatformTypes.h"
#include "Windows/AllowWindowsPlatformAtomics.h"
#include "Windows/PreWindowsApi.h"

#include <unknwn.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Perception.Spatial.h>
#include <winrt/Windows.Foundation.Collections.h>

#include "Windows/PostWindowsApi.h"
#include "Windows/HideWindowsPlatformAtomics.h"
#include "Windows/HideWindowsPlatformTypes.h"
#endif

WinRT kodu yalnızca Win64 ve HoloLens platformlarında derlenebilir, bu nedenle if deyimi WinRT kitaplıklarının diğer platformlara eklenmesini engeller. unknwn.h, IUnknown arabirimine sahip olduğu için eklendi.

NuGet paketinden WinRT

WinRT desteğine sahip bir NuGet paketi eklemeniz gerekiyorsa bu biraz daha karmaşıktır. Bu durumda Visual Studio neredeyse tüm işleri sizin yernize yapabilir, ancak Unreal derleme sistemi bunu yapamaz. Neyse ki çok zor değil. Aşağıda Microsoft.MixedReality.QR paketini indirme işlemine ilişkin bir örnek verilmiştir. Bunu başka bir dosyayla değiştirebilirsiniz, winmd dosyasını kaybetmediğinizden emin olun ve doğru dll dosyasını kopyalayın.

Önceki bölümdeki Windows SDK dll'leri işletim sistemi tarafından işlenir. NuGet'in dll'leri modülünüzdeki kod tarafından yönetilmelidir. Bunları indirmek, ikili dosyalar klasörüne kopyalamak ve modül başlangıcında işlem belleğine yüklemek için kod eklemenizi öneririz.

İlk adımda, modülünüzün kök klasörüne bir packages.config (/nuget/reference/packages-config) eklemeniz gerekir. Burada, tüm bağımlılıkları dahil olmak üzere indirmek istediğiniz tüm paketleri eklemeniz gerekir. Burada Microsoft.MixedReality.QR dosyasını birincil yük olarak ve iki diğerini de bağımlılık olarak ekledim. Bu dosyanın biçimi Visual Studio'dakiyle aynıdır:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Microsoft.MixedReality.QR" version="0.5.2102" targetFramework="native" />
  <package id="Microsoft.VCRTForwarders.140" version="1.0.6" targetFramework="native" />
  <package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
</packages>

Artık NuGet'i, gerekli paketleri indirebilir veya NuGet belgelerine bakabilirsiniz.

YourModule.Build.cs dosyasını açın ve aşağıdaki kodu ekleyin:

// WinRT with Nuget support
if (Target.Platform == UnrealTargetPlatform.Win64 || Target.Platform == UnrealTargetPlatform.HoloLens)
{
	// these parameters mandatory for winrt support
	bEnableExceptions = true;
	bUseUnity = false;
	CppStandard = CppStandardVersion.Cpp17;
	PublicSystemLibraries.AddRange(new string [] { "shlwapi.lib", "runtimeobject.lib" });

	// prepare everything for nuget
	string MyModuleName = GetType().Name;
	string NugetFolder = Path.Combine(PluginDirectory, "Intermediate", "Nuget", MyModuleName);
	Directory.CreateDirectory(NugetFolder);

	string BinariesSubFolder = Path.Combine("Binaries", "ThirdParty", Target.Type.ToString(), Target.Platform.ToString(), Target.Architecture);

	PrivateDefinitions.Add(string.Format("THIRDPARTY_BINARY_SUBFOLDER=\"{0}\"", BinariesSubFolder.Replace(@"\", @"\\")));

	string BinariesFolder = Path.Combine(PluginDirectory, BinariesSubFolder);
	Directory.CreateDirectory(BinariesFolder);

	ExternalDependencies.Add("packages.config");

	// download nuget
	string NugetExe = Path.Combine(NugetFolder, "nuget.exe");
	if (!File.Exists(NugetExe))
	{
		using (System.Net.WebClient myWebClient = new System.Net.WebClient())
		{
			// we aren't focusing on a specific nuget version, we can use any of them but the latest one is preferable
			myWebClient.DownloadFile(@"https://dist.nuget.org/win-x86-commandline/latest/nuget.exe", NugetExe);
		}
	}

	// run nuget to update the packages
	{
		var StartInfo = new System.Diagnostics.ProcessStartInfo(NugetExe, string.Format("install \"{0}\" -OutputDirectory \"{1}\"", Path.Combine(ModuleDirectory, "packages.config"), NugetFolder));
		StartInfo.UseShellExecute = false;
		StartInfo.CreateNoWindow = true;
		var ExitCode = Utils.RunLocalProcessAndPrintfOutput(StartInfo);
		if (ExitCode < 0)
		{
			throw new BuildException("Failed to get nuget packages.  See log for details.");
		}
	}

	// get list of the installed packages, that's needed because the code should get particular versions of the installed packages
	string[] InstalledPackages = Utils.RunLocalProcessAndReturnStdOut(NugetExe, string.Format("list -Source \"{0}\"", NugetFolder)).Split(new char[] { '\r', '\n' });

	// winmd files of the packages
	List<string> WinMDFiles = new List<string>();

	// WinRT lib for some job
	string QRPackage = InstalledPackages.FirstOrDefault(x => x.StartsWith("Microsoft.MixedReality.QR"));
	if (!string.IsNullOrEmpty(QRPackage))
	{
		string QRFolderName = QRPackage.Replace(" ", ".");

		// copying dll and winmd binaries to our local binaries folder
		// !!!!! please make sure that you use the path of file! Unreal can't do it for you !!!!!
		string WinMDFile = Path.Combine(NugetFolder, QRFolderName, @"lib\uap10.0.18362\Microsoft.MixedReality.QR.winmd");
		SafeCopy(WinMDFile, Path.Combine(BinariesFolder, "Microsoft.MixedReality.QR.winmd"));

		SafeCopy(Path.Combine(NugetFolder, QRFolderName, string.Format(@"runtimes\win10-{0}\native\Microsoft.MixedReality.QR.dll", Target.WindowsPlatform.Architecture.ToString())),
			Path.Combine(BinariesFolder, "Microsoft.MixedReality.QR.dll"));

		// also both both binaries must be in RuntimeDependencies, unless you get failures in Hololens platform
		RuntimeDependencies.Add(Path.Combine(BinariesFolder, "Microsoft.MixedReality.QR.dll"));
		RuntimeDependencies.Add(Path.Combine(BinariesFolder, "Microsoft.MixedReality.QR.winmd"));

		//add winmd file to the list for further processing using cppwinrt.exe
		WinMDFiles.Add(WinMDFile);
	}

	if (Target.Platform == UnrealTargetPlatform.Win64)
	{
		// Microsoft.VCRTForwarders.140 is needed to run WinRT dlls in Win64 platforms
		string VCRTForwardersPackage = InstalledPackages.FirstOrDefault(x => x.StartsWith("Microsoft.VCRTForwarders.140"));
		if (!string.IsNullOrEmpty(VCRTForwardersPackage))
		{
			string VCRTForwardersName = VCRTForwardersPackage.Replace(" ", ".");
			foreach (var Dll in Directory.EnumerateFiles(Path.Combine(NugetFolder, VCRTForwardersName, "runtimes/win10-x64/native/release"), "*_app.dll"))
			{
				string newDll = Path.Combine(BinariesFolder, Path.GetFileName(Dll));
				SafeCopy(Dll, newDll);
				RuntimeDependencies.Add(newDll);
			}
		}
	}

	// get WinRT package 
	string CppWinRTPackage = InstalledPackages.FirstOrDefault(x => x.StartsWith("Microsoft.Windows.CppWinRT"));
	if (!string.IsNullOrEmpty(CppWinRTPackage))
	{
		string CppWinRTName = CppWinRTPackage.Replace(" ", ".");
		string CppWinRTExe = Path.Combine(NugetFolder, CppWinRTName, "bin", "cppwinrt.exe");
		string CppWinRTFolder = Path.Combine(PluginDirectory, "Intermediate", CppWinRTName, MyModuleName);
		Directory.CreateDirectory(CppWinRTFolder);

		// all downloaded winmd file with WinSDK to be processed by cppwinrt.exe
		var WinMDFilesStringbuilder = new System.Text.StringBuilder();
		foreach (var winmd in WinMDFiles)
		{
			WinMDFilesStringbuilder.Append(" -input \"");
			WinMDFilesStringbuilder.Append(winmd);
			WinMDFilesStringbuilder.Append("\"");
		}

		// generate winrt headers and add them into include paths
		var StartInfo = new System.Diagnostics.ProcessStartInfo(CppWinRTExe, string.Format("{0} -input \"{1}\" -output \"{2}\"", WinMDFilesStringbuilder, Target.WindowsPlatform.WindowsSdkVersion, CppWinRTFolder));
		StartInfo.UseShellExecute = false;
		StartInfo.CreateNoWindow = true;
		var ExitCode = Utils.RunLocalProcessAndPrintfOutput(StartInfo);
		if (ExitCode < 0)
		{
			throw new BuildException("Failed to get generate WinRT headers.  See log for details.");
		}

		PrivateIncludePaths.Add(CppWinRTFolder);
	}
	else
	{
		// fall back to default WinSDK headers if no winrt package in our list
		PrivateIncludePaths.Add(Path.Combine(Target.WindowsPlatform.WindowsSdkDir, "Include", Target.WindowsPlatform.WindowsSdkVersion, "cppwinrt"));
	}
}

SafeCopy yöntemini aşağıdaki gibi tanımlamanız gerekir:

private void SafeCopy(string source, string destination)
{
	if(!File.Exists(source))
	{
		Log.TraceError("Class {0} can't find {1} file for copying", this.GetType().Name, source);
		return;
	}

	try
	{
		File.Copy(source, destination, true);
	}
	catch(IOException ex)
	{
		Log.TraceWarning("Failed to copy {0} to {1} with exception: {2}", source, destination, ex.Message);
		if (!File.Exists(destination))
		{
			Log.TraceError("Destination file {0} does not exist", destination);
			return;
		}

		Log.TraceWarning("Destination file {0} already existed and is probably in use.  The old file will be used for the runtime dependency.  This may happen when packaging a Win64 exe from the editor.", destination);
	}
}

NuGet DLL'lerinin Win32 işlem belleğinize el ile yüklenmesi gerekir; modülünüzün başlangıç yöntemine el ile yükleme eklemenizi öneririz:

void StartupModule() override
{
#if PLATFORM_WINDOWS
	const FString LibrariesDir = FPaths::ProjectPluginsDir() / "MyModule" / THIRDPARTY_BINARY_SUBFOLDER;
	FPlatformProcess::PushDllDirectory(*LibrariesDir);

	const FString DllName = "Microsoft.MixedReality.QR.dll";
	if (!FPlatformProcess::GetDllHandle(*DllName))
	{
		UE_LOG(LogHMD, Warning, TEXT("Dll \'%s\' can't be loaded from \'%s\'"), *DllName, *LibrariesDir);
	}

	FPlatformProcess::PopDllDirectory(*LibrariesDir);
#endif
}

Son olarak, önceki bölümde açıklandığı gibi kodunuza WinRT üst bilgileri ekleyebilirsiniz.

Sonraki Geliştirme Denetim Noktası

Ortaya koyduğumuz Gerçek olmayan geliştirme yolculuğunu takip ediyorsanız, Karma Gerçeklik platformu özelliklerini ve API'lerini keşfetmenin tam ortasındasınız demektir. Buradan herhangi bir konuya devam edebilir veya doğrudan uygulamanızı bir cihaza veya öykünücüye dağıtmaya atlayabilirsiniz.

Ayrıca bkz.