【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,代碼如下:
@echo offREM OEM CustomizationScript fileREM This script if included in the image,is called everytime the system boots.REM EnableAdministratorUsernet user Administrator p@ssw0rd /active:yesif exist C:\AppInstall\AppInstall.cmd (REM EnableApplicationInstallationfor onetime only, after this the files are deleted.call C:\Appinstall\AppInstall.cmd >%temp%\AppInstallLog.txtREM CleanupApplicationInstallationFiles.Change dir 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_ARMset certslist=IoTCoreMediaPlayer_1.0.4.0_ARMset dependencylist=Microsoft.NET.Native.Framework.1.3Microsoft.NET.Native.Runtime.1.3Microsoft.VCLibs.ARM.14.00
第三行,注意到沒有,三個依賴包的名字之間沒有空格!!!再繼續查找原因,根源在newappxpkg.cmd這個腳本。查看代碼:
::Run setenv before running this script::This script creates the folder structure and copies the template files for a newpackage@echo offgoto START:Usageecho Usage: newappxpkg filename.appx [CompName.SubCompName]echo filename.appx...........Required,Input appx package.Expects dependencies in a sub folderecho CompName.SubCompName....Optional,defaultisAppx.filenameecho [/?]............Displaysthis usage string.echo Example:echo newappxpkg C:\test\MainAppx_1.0.0.0_arm.appx Appx.Mainecho Existing packages aredir /b /AD %SRC_DIR%\Packagesexit/b 1:STARTsetlocal ENABLEDELAYEDEXPANSIONif[%1]==[/?]gotoUsageif[%1]==[-?]gotoUsageif[%1]==[]gotoUsageset FILE_TYPE=%~x1set FILE_NAME=%~n1set"FILE_PATH=%~dp1"if[%FILE_TYPE%]==[.appx](set COMP_NAME=Appxfor/f "tokens=1 delims=_"%%i in("%FILE_NAME%")do(set SUB_NAME=%%i))else(echo.Unsupported filetype.gotoUsage)ifnot[%2]==[](for/f "tokens=1,2 delims=."%%i in("%2")do(set COMP_NAME=%%iset SUB_NAME=%%j))if NOT DEFINED SRC_DIR (echo Environmentnotdefined.Call setenvgotoEnd)SET "NEWPKG_DIR=%SRC_DIR%\Packages\%COMP_NAME%.%SUB_NAME%"::ErrorChecksif/i EXIST %NEWPKG_DIR%(echo Error:%COMP_NAME%.%SUB_NAME% already existsrmdir /s /q %NEWPKG_DIR%echo Delete old %NEWPKG_DIR%)::Start processing commandecho Creating%COMP_NAME%.%SUB_NAME%packagemkdir "%NEWPKG_DIR%"if[%FILE_TYPE%]==[.appx](::CreateAppxPackageusingtemplate filesmkdir "%NEWPKG_DIR%\AppInstall"echo.Creatingpackage xml filecall appx2pkg.cmd %1%COMP_NAME%.%SUB_NAME%REM Copy the files to the package directorymove "%FILE_PATH%\%COMP_NAME%.%SUB_NAME%.pkg.xml""%NEWPKG_DIR%\%COMP_NAME%.%SUB_NAME%.pkg.xml">nulif exist "%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_ARMset certslist=IoTCoreMediaPlayer_1.0.4.0_ARMset dependencylist=Microsoft.NET.Native.Framework.1.3Microsoft.NET.Native.Runtime.1.3Microsoft.VCLibs.ARM.14.00
