【Windows 10 IoT】为Win10 IoT镜像添加默认应用(树莓派)
【Windows 10 IoT】为Win10 IoT镜像添加默认应用(树莓派)
在Windows 10 IoT应用程序开发好之后,一般通过IoT WebManagement或者直接用vs将应用部署上去。并且执行命令iotstartup.exe add headed/headless AppxID,将应用设置为开机启动。但是,如果想基于一个开发板,量产某种硬件设备,这种方式肯定是不可行的。
我们会想到,是否可以将我们的应用直接打包到镜像中,并设置成为开机自启的默认应用呢?当然可以。
基本原理是这样的:IoT设备第一次启动时,会执行一个OEMCustomization.cmd脚本,安装指定目录下的appx应用并执行iotstartup.exe将其设置为startup应用。
参考:[Add an app to your image][1]
用到的工具:
1. Windows Assessment and Deployment Kit (Windows ADK) [http://go.microsoft.com/fwlink/?LinkId=526803][2]
2. Windows Driver Kit (WDK) [http://go.microsoft.com/fwlink/p/?linkid=261797][3]
3. IoT Core ADK Add-Ons [https://github.com/ms-iot/iot-adk-addonkit/][4]
4. Windows 10 IoT Core Dashboard [http://go.microsoft.com/fwlink/p/?LinkId=708576][5]
上面这些工具,主要修改的是 IoT Core ADK Add-Ons。这个工具包中包含了用于打包一个ffu镜像的一系列脚本和xml模板。Tools文件夹下是一些打包boot相关、驱动、应用以及生成FFU的一些脚本。在Source-arm和Source-x86下的Products文件夹中,包含了一些设备样本。
例:
iot-adk-addonkit\Source-arm\Products\SampleA是树莓派2的sample;
iot-adk-addonkit\Source-arm\Products\SampleB是可以添加自定义驱动的树莓派2的sample;
iot-adk-addonkit\Source-x86\Products\SampleA是intel MBM的sample;
iot-adk-addonkit\Source-x86\Products\SampleB是可以添加自定义驱动的intel MBM的sample。
主要步骤:
1. 先后先当然是先build并打包你的应用程序。注意只生成对应平台的appx就可以了,不要生成appxbundle。
2. 将应用打包成cab包:
- 以管理员身份运行IoT Core ADK Add-Ons中的脚本:IoT-ADK-AddonKit\IoTCoreShell.cmd。选择对应的CPU平台:arm或者x86.
- 执行buildpkg all,这个命令会将IoT-ADK-AddonKit\Common和Source-arm(x86)\Packages目录下已有的Package文件夹打包成cab包。build成功后,我们可以在IoT-ADK-AddonKit\arm(x86)\pkgs目录下看到生成的cab包。
- 使用newappxpkg命令,为你的应用生成一个预打包的工作目录:
newAppxPkg "..\HelloWorld\AppPackages\HelloWorld_1.0.0.0_ARM_Debug_Test\HelloWorld_1.0.0.0_ARM_Debug.appx" Appx.HelloWorld
PS:不要把appx拷贝出来,因为newappxpkg命令会扫面appx所在目录下是否有dependency appx,如果有,会一同拷贝到工作目录与你的appx应用一起打包。一般是Microsoft.NET.Native.Framework、Microsoft.NET.Native.Runtime和Microsoft.VCLibs.ARM这三个依赖。- 执行
buildpkg Appx.HelloWorld命令,就可以将应用打包成cab包了。执行成功后,我们可以在IoT-ADK-AddonKit\arm(x86)\pkgs目录下找到OEM_NAME.Appx.HelloWorld.cab这个包(OEM_NAME是在setoem.cmd脚本中指定的)。
3. 修改feature manifest(以下简称FM): IoT-ADK-AddonKit\Source-\Packages\OEMFM.xml
将appx应用打包成的cab包,添加到feature manifest中声明。格式如下:
<?xml version="1.0"encoding="utf-8"?><FeatureManifestxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns="http://schemas.microsoft.com/embedded/2004/10/ImageUpdate"><BasePackages/><Features><OEM><!-- Feature definitions below --><PackageFilePath="%PKGBLD_DIR%"Name="%OEM_NAME%.Appx.Main.cab"><FeatureIDs><FeatureID>OEM_AppxMain</FeatureID></FeatureIDs></PackageFile><PackageFilePath="%PKGBLD_DIR%"Name="%OEM_NAME%.Appx.HelloWorld.cab"><FeatureIDs><FeatureID>OEM_AppxHelloWorld</FeatureID></FeatureIDs></PackageFile></OEM><OEMFeatureGroups/></Features></FeatureManifest>
4. 更新配置文件 IoT-ADK-AddonKit\Source-\Products\ProductA\TestOEMInput.xml
添加步骤3中修改的feature manifest到AdditionalFM标签下:
<AdditionalFMs><AdditionalFM>%AKROOT%\FMFiles\arm\IoTUAPNonProductionPartnerShareFM.xml</AdditionalFM><AdditionalFM>%AKROOT%\FMFiles\arm\IoTUAPRPi2FM.xml</AdditionalFM><AdditionalFM>%AKROOT%\FMFiles\arm\RPi2FM.xml</AdditionalFM><!--步骤3中修改的feature manifest--><AdditionalFM>%SRC_DIR%\Packages\OEMFM.xml</AdditionalFM><AdditionalFM>%COMMON_DIR%\Packages\OEMCommonFM.xml</AdditionalFM></AdditionalFMs>
然后,将你的应用的featureID添加到标签下:
<OEM><Feature>RPI2_DRIVERS</Feature><Feature>RPI2_DEVICE_TARGETINGINFO</Feature><Feature>PRODUCTION</Feature><Feature>OEM_CustomCmd</Feature><Feature>OEM_AppxHelloWorld</Feature></OEM>
注意:一个镜像中只能有一个默认的前台应用(startup headed app),所以我们需要注释掉之前的默认app:IOT_BERTHA
<Feature>IOT_ENABLE_TESTSIGNING</Feature><Feature>IOT_DISABLE_UMCI</Feature><Feature>IOT_CRT140</Feature><!-- <Feature>IOT_BERTHA</Feature> --><Feature>IOT_APP_TOOLKIT</Feature><Feature>IOT_CP210x_MAKERDRIVER</Feature><Feature>IOT_FTSER2K_MAKERDRIVER</Feature>
步骤3中的FM文件,相当于app cab包的声明,标识某个featureID所对应的cab包;只有在步骤4中的OEMInput文件中添加后相应的featureID后,打包程序才会将对应的cab包打包到镜像中。
5. 设置自动安装脚本
自动安装脚本位于:IoT-ADK-AddonKit\Source-\Products\SampleX\OEMCustomization.cmd,代码如下:
@echooffREM OEMCustomizationScriptfileREMThisscriptifincludedinthe image,iscalled everytime the system boots.REMEnableAdministratorUsernet userAdministratorp@ssw0rd/active:yesifexist C:\AppInstall\AppInstall.cmd(REMEnableApplicationInstallationforonetime only,afterthisthe files are deleted.call C:\Appinstall\AppInstall.cmd>%temp%\AppInstallLog.txtREMCleanupApplicationInstallationFiles.Changedir to root so that the dirs can be deletedcd \rmdir/S/Q C:\AppInstall)
可以看到,脚本执行时,会判断C:\下是否存在AppInstall\AppInstall.cmd脚本(实际上这个AppInstall目录,就是从步骤2.appx打包成的cab包中提取出来的所有文件),如果有,则执行。而AppInstall.cmd脚本,就会将AppInstall目录下的appx应用程序安装并且设置为startup app。
PS: 自动安装脚本OEMCustomization.cmd,同样以cab包的形式打包进ffu镜像的。在步骤4中的OEMInput.xml文件中,OEM_CustomCmd这个feature就是自动安装脚本的cab。OEM_CustomCmd的声明在iot-adk-addonkit\Common\Packages\OEMCommonFM.xml中可以找到。
6. 最后,打包ffu镜像
执行createimage.cmd SampleA Test打包镜像。SampleA就是树莓派的sample。
生成的ffu位于:IoT-ADK-AddonKit\Build\\ProductA\Flash.FFU
大功告成!赶紧烧写到sd卡里测试一下吧!
可能会遇到的小问题
如果系统启动后,一直无法启动默认应用,一定是哪里出了问题(这是废话)。
可以到SD卡的Data\Users\DefaultAccount\AppData\Local\Temp目录下,看是否有你的应用的安装log(名字是****_result.txt)。
如果没有,可能是OEMCustomization.cmd脚本没有成功执行,需要检查下OEM_CustomCmd这个cab包是不是成功生成并打包到ffu中了;
如果有,就打开看一下log,应该会有错误原因。
我遇到的一个蛋疼的问题是:
安装appx时,找不到某个依赖(就是上面说的Microsoft.NET.Native.Framework、Microsoft.NET.Native.Runtime和Microsoft.VCLibs.ARM这三个appx),可是三个依赖包明明都在我的应用cab包中啊!这个时候用文本编辑器打开应用cab包中的AppxConfig.cmd这个脚本看了一下:
setAppxName=IoTCoreMediaPlayer_1.0.4.0_ARMsetcertslist=IoTCoreMediaPlayer_1.0.4.0_ARMsetdependencylist=Microsoft.NET.Native.Framework.1.3Microsoft.NET.Native.Runtime.1.3Microsoft.VCLibs.ARM.14.00
第三行,注意到没有,三个依赖包的名字之间没有空格!!!再继续查找原因,根源在newappxpkg.cmd这个脚本。查看代码:
::Runsetenv before runningthisscript::Thisscript creates the folder structureandcopies thetemplatefilesforanewpackage@echooffgotoSTART:UsageechoUsage:newappxpkg filename.appx[CompName.SubCompName]echo filename.appx...........Required,Inputappxpackage.ExpectsdependenciesinasubfolderechoCompName.SubCompName....Optional,defaultisAppx.filenameecho[/?]............Displaysthisusagestring.echoExample:echo newappxpkg C:\test\MainAppx_1.0.0.0_arm.appxAppx.MainechoExistingpackages aredir/b/AD%SRC_DIR%\Packagesexit/b1:STARTsetlocal ENABLEDELAYEDEXPANSIONif[%1]==[/?]gotoUsageif[%1]==[-?]gotoUsageif[%1]==[]gotoUsagesetFILE_TYPE=%~x1setFILE_NAME=%~n1set"FILE_PATH=%~dp1"if[%FILE_TYPE%]==[.appx](setCOMP_NAME=Appxfor/f"tokens=1 delims=_"%%iin("%FILE_NAME%")do(setSUB_NAME=%%i))else(echo.Unsupportedfiletype.gotoUsage)ifnot[%2]==[](for/f"tokens=1,2 delims=."%%iin("%2")do(setCOMP_NAME=%%isetSUB_NAME=%%j))ifNOT DEFINED SRC_DIR(echoEnvironmentnotdefined.CallsetenvgotoEnd)SET"NEWPKG_DIR=%SRC_DIR%\Packages\%COMP_NAME%.%SUB_NAME%"::ErrorChecksif/i EXIST%NEWPKG_DIR%(echoError:%COMP_NAME%.%SUB_NAME%already existsrmdir/s/q%NEWPKG_DIR%echoDeleteold%NEWPKG_DIR%)::Startprocessing commandechoCreating%COMP_NAME%.%SUB_NAME%packagemkdir"%NEWPKG_DIR%"if[%FILE_TYPE%]==[.appx](::CreateAppxPackageusingtemplatefilesmkdir"%NEWPKG_DIR%\AppInstall"echo.Creatingpackagexml filecall appx2pkg.cmd%1%COMP_NAME%.%SUB_NAME%REMCopythe files to thepackagedirectorymove"%FILE_PATH%\%COMP_NAME%.%SUB_NAME%.pkg.xml""%NEWPKG_DIR%\%COMP_NAME%.%SUB_NAME%.pkg.xml">nulifexist"%FILE_PATH%\Dependencies\%ARCH%"(copy"%FILE_PATH%\Dependencies\%ARCH%\*.appx""%NEWPKG_DIR%\AppInstall\" >nul) else (copy "%FILE_PATH%\Dependencies\*.appx" "%NEWPKG_DIR%\AppInstall\">nul)copy"%FILE_PATH%\*.cer""%NEWPKG_DIR%\AppInstall\" >nulcopy "%FILE_PATH%\%FILE_NAME%.appx" "%NEWPKG_DIR%\AppInstall\%FILE_NAME%.appx" >nulcopy "%IOTADK_ROOT%\Templates\AppInstall\*.cmd" "%NEWPKG_DIR%\AppInstall" >nulREM Update AppxConfig.cmdecho set AppxName=%FILE_NAME%> %NEWPKG_DIR%\AppInstall\AppxConfig.cmdfor /f "useback delims=" %%i in ("%FILE_PATH%\appx_cerlist.txt") do (set certslist=!certslist!%%~ni)echo set certslist=!certslist! >> %NEWPKG_DIR%\AppInstall\AppxConfig.cmdfor /f "useback delims=" %%i in ("%FILE_PATH%\appx_deplist.txt") do (set dependencylist=!dependencylist!%%~ni)echo set dependencylist=!dependencylist! >> %NEWPKG_DIR%\AppInstall\AppxConfig.cmddel "%FILE_PATH%\appx_cerlist.txt"del "%FILE_PATH%\appx_deplist.txt")echo %NEWPKG_DIR% readygoto End:Errorendlocalecho "newappxpkg%1%2" failed with error %ERRORLEVEL%exit /b 1:Endendlocalexit /b 0
注意第87行:set certslist=!certslist!%%~ni,这行代码最后需要有一个空格!!这样生成的AppxConfig.cmd脚本就是这样的(每个依赖包后面有一个空格):
setAppxName=IoTCoreMediaPlayer_1.0.4.0_ARMsetcertslist=IoTCoreMediaPlayer_1.0.4.0_ARMsetdependencylist=Microsoft.NET.Native.Framework.1.3Microsoft.NET.Native.Runtime.1.3Microsoft.VCLibs.ARM.14.00
转载于:https://www.cnblogs.com/LeewayDev/p/5601849.html