在我們目前的業(yè)務(wù)開發(fā)中禀晓,遇到了PPT轉(zhuǎn)PDF的業(yè)務(wù)需求莉擒。如何能夠無(wú)損并且快速的進(jìn)行轉(zhuǎn)換棒动,是我們遇到的一個(gè)不大不小的問(wèn)題茉帅。
經(jīng)過(guò)各種調(diào)查研究叨叙,使用C#的COM接口直接操作微軟家的Office PowerPoint,讓其將PPT文件另存為PDF堪澎,是一個(gè)比較穩(wěn)妥擂错,資源消耗也比較小的好辦法。
基本的代碼如下圖所示樱蛤,很簡(jiǎn)單钮呀。
但是這是個(gè)單進(jìn)程模式,轉(zhuǎn)換效率跟不上昨凡,服務(wù)器利用率也不高爽醋。
怎么辦?
多開幾個(gè)進(jìn)程不就好了便脊!
于是蚂四,我就一口氣打開了三個(gè)進(jìn)程,然后……
根本跑不起來(lái)……
我在Google上找了找原因,在一個(gè)很不起眼的網(wǎng)頁(yè)寫著(原文記不清了遂赠,而且我再也沒找到那個(gè)網(wǎng)頁(yè)):
PowerPoint不像Word和Excel久妆,PowerPoint只能單進(jìn)程運(yùn)行,同時(shí)只能執(zhí)行一項(xiàng)導(dǎo)出指令
大概意思就是說(shuō)跷睦,PowerPoint在進(jìn)程內(nèi)部維護(hù)了一個(gè)導(dǎo)出隊(duì)列筷弦,同時(shí)只能有一個(gè)文件進(jìn)行導(dǎo)出,其他的要排隊(duì)送讲,但是Word和Excel就能多進(jìn)程運(yùn)行奸笤。
是這樣么?我開始了一次不太漫長(zhǎng)的追尋之路哼鬓。
太長(zhǎng)不看版結(jié)論:是這樣的监右,這項(xiàng)技術(shù)稱為"MDI多文檔窗口程序",但是Word和Excel可以通過(guò)命令行參數(shù) "/w" 強(qiáng)行啟動(dòng)多個(gè)進(jìn)程同時(shí)執(zhí)行導(dǎo)出异希,而PowerPoint只能借助不同用戶身份開啟多個(gè)進(jìn)程同時(shí)導(dǎo)出健盒,詳情請(qǐng)搜索“runas”命令。
首先称簿,我去確定下這個(gè)“MDI”是如何工作的扣癣。
下面文字摘自網(wǎng)絡(luò):
多文檔界面 (MDI) 允許創(chuàng)建在單個(gè)容器窗體中包含多個(gè)窗體的應(yīng)用程序。象 Microsoft Excel 與 Microsoft Word 這樣的應(yīng)用程序就具有多文檔界面憨降。
MDI 應(yīng)用程序允許用戶同時(shí)顯示多個(gè)文檔父虑,每個(gè)文檔顯示在它自己的窗口中。文檔或子窗口被包含在父窗口中授药,父窗口為應(yīng)用程序中所有的子窗口提供工作空間士嚎。例如:Microsoft Excel 允許創(chuàng)建并顯示不同樣式的多文檔窗口。每個(gè)子窗口都被限制在 Excel 父窗口的區(qū)域之內(nèi)悔叽。當(dāng)最小化 Excel 時(shí)莱衩,所有的文檔窗口也被最小化,只有父窗口的圖標(biāo)顯示在任務(wù)欄中娇澎。
比較直觀的展示就是:
以上兩張圖就是開啟MDI和關(guān)閉MDI的進(jìn)程展示笨蚁。可以看到趟庄,當(dāng)開始了MDI時(shí)括细,雖然打開了兩個(gè)文檔,但是實(shí)際上只有一個(gè)Word進(jìn)程在工作戚啥。當(dāng)關(guān)閉了MDI模式勒极,每個(gè)文檔都會(huì)新建一個(gè)Word進(jìn)程。
微軟官方的文檔中關(guān)于新建Office程序的命令行參數(shù)虑鼎,是這么記錄的:
但是很遺憾辱匿,PowerPoint并不支持這個(gè)命令行參數(shù)键痛,所有的PPT文檔都是在一個(gè)PowerPoint進(jìn)程中打開的。
官方并沒有給我們一個(gè)方案匾七,那么我們有沒有其他的方案去實(shí)現(xiàn)單機(jī)多個(gè)PowerPoint進(jìn)程呢絮短?
我大概思考了下,有一下幾個(gè)思路:
虛擬機(jī)
Docker
沙盒軟件(多開軟件)
虛擬機(jī)
虛擬機(jī)采用了虛擬化技術(shù)昨忆,能夠很完美的模擬出一個(gè)操作環(huán)境丁频,那么就可以完美運(yùn)行office程序,但是額外的性能消耗很大邑贴,單個(gè)PowerPoint程序的內(nèi)存消耗在150M-300M之間席里,一個(gè)虛擬機(jī)的消耗能夠輕松達(dá)到這個(gè)數(shù)值的五倍之多。本來(lái)單機(jī)能夠支持二十個(gè)進(jìn)程拢驾,結(jié)果現(xiàn)在只能支持四個(gè)奖磁,差距太大,不考慮繁疤。
Docker
Docker也是虛擬化技術(shù)咖为,能夠模擬出一套完全隔離的內(nèi)存空間和系統(tǒng)資源。經(jīng)過(guò)微軟官方和Docker開發(fā)組長(zhǎng)達(dá)兩年的合作稠腊,Docker推出了完美支持windows系統(tǒng)的的docker for windows.
與之前推出的windows版不同躁染,Docker for windows是完全針對(duì)于windows的內(nèi)核開發(fā)〖芗桑可以支持Windows Server和一個(gè)比較小的Nano windows鏡像吞彤。
但是很遺憾,這次我們沒法用叹放,因?yàn)镈ocker不支持有UI界面的程序運(yùn)行饰恕。
沙盒/多開軟件
這個(gè)應(yīng)該也是Windows使用過(guò)程中經(jīng)常會(huì)遇到的軟件。尤其是玩游戲的網(wǎng)友许昨,應(yīng)該都用過(guò)各種游戲的多開助手懂盐。網(wǎng)游為了限制游戲內(nèi)資源分布褥赊,把握游戲發(fā)展進(jìn)程糕档,往往會(huì)限制單機(jī)運(yùn)行客戶端的數(shù)量,而各種助手又能破解掉這種限制拌喉。那么我們能不能使用這種方式去開啟多個(gè)PowerPoint進(jìn)程呢速那?
事實(shí)上是可以的,我這里測(cè)試使用的是windows最著名的沙盒軟件 Sandboxie
將PowerPoint添加到Sandboxie中尿背,然后點(diǎn)擊運(yùn)行端仰,我們就可以驚喜的發(fā)現(xiàn),任務(wù)管理器中出現(xiàn)了兩個(gè)PowerPoint的進(jìn)程田藐。
這時(shí)候荔烧,我思考的問(wèn)題是:Why吱七?
為什么沒有通過(guò)虛擬化技術(shù),也能夠騙過(guò)PowerPoint的識(shí)別鹤竭,開啟出兩個(gè)進(jìn)程呢踊餐?
先讓我們來(lái)看看這兩個(gè)程有什么區(qū)別。我這里使用了 **ProcessExplorer **軟件來(lái)查看進(jìn)程的詳細(xì)信息臀稚。
左邊是沙盒啟動(dòng)的進(jìn)程吝岭,右邊是正常啟動(dòng)的進(jìn)程。
可以看到吧寺,兩個(gè)進(jìn)程啟動(dòng)的文件窜管,運(yùn)行目錄,命令行參數(shù)之類的基本信息都完全一樣稚机,但是有兩個(gè)地方不同:
1. 父進(jìn)程ID
沙盒啟動(dòng)的進(jìn)程幕帆,父進(jìn)程id是一個(gè)不存在的進(jìn)程,而正常啟動(dòng)的進(jìn)程抒钱,父進(jìn)程是window桌面進(jìn)程 explorer.exe蜓肆。
2. 啟動(dòng)用戶名
沙盒啟動(dòng)的進(jìn)程,用戶名是匿名用戶Anonymous,而正常啟動(dòng)的進(jìn)程谋币,用戶名就是我們當(dāng)前登陸的用戶名仗扬。
事出有異必有妖,那么導(dǎo)致兩個(gè)進(jìn)程獨(dú)立運(yùn)行的原因蕾额,肯定就因?yàn)橐陨蟽蓚€(gè)原因中的一個(gè)早芭。
我使用了HideTools(一款能夠修改進(jìn)程信息的軟件),修改了正常打開的PowerPoint程序的父進(jìn)程id诅蝶,如下圖退个。
那么當(dāng)我又正常打開了一個(gè)PowerPoint程序,會(huì)不會(huì)產(chǎn)生三個(gè)獨(dú)立運(yùn)行的進(jìn)程呢调炬?
事實(shí)證明语盈,并沒有。
那么缰泡,我們幾乎能夠確定刀荒,讓PowerPoint能夠多進(jìn)程同時(shí)執(zhí)行的關(guān)鍵點(diǎn),就在于“打開程序的用戶不同”上棘钞。
其實(shí)到了這里缠借,我們大概能明白這是怎么一回事了。作為經(jīng)常在Linux下開發(fā)程序的服務(wù)器端人員宜猜,這時(shí)候才想起來(lái)這么一回事泼返,其實(shí)是有點(diǎn)羞愧的。
Linux下的權(quán)限管理和進(jìn)程分配使用非常廣泛姨拥,服務(wù)器程序的用戶一般都是www-data,php-fpm的用戶是php,管理員使用root賬戶,開發(fā)人員使用對(duì)文件擁有只讀權(quán)限的dev賬戶等等绅喉。不同的用戶渠鸽,可訪問(wèn)和可分配的資源都是相互獨(dú)立〔窆蓿互不干擾的拱绑。
而同樣的,windows下也有著一套用戶管理和訪問(wèn)限制體系丽蝎。
在Linux下猎拨,我們可以使用sudo切換進(jìn)程的執(zhí)行用戶,而在windows下屠阻,我們可以使用runas命令去達(dá)到同樣的效果红省。
RUNAS 用法:
RUNAS [/noprofile | /profile] [/env] [/savecred | /netonly] /user:<UserName> programRUNAS使用示例:
runas /noprofile /user:mymachine\administrator cmd
說(shuō)明:使用本機(jī)上的Administrator管理員身份執(zhí)行CMD,/noprofile為不加載該用戶的配置信息国觉。runas /profile /env /user:mydomain\admin “mmc %windir%\system32\dsa.msc”
說(shuō)明:使用本機(jī)上的admin身份掃行msc控制臺(tái)吧恃。 /profile為指定加載用戶配置文件。 /env 表示使用當(dāng)前環(huán)境麻诀。runas /env /user:user@domain.microsoft.com “notepad \”my file.txt\””
說(shuō)明:使用域用戶身份運(yùn)行痕寓,并指定使用notepad打開my file.txt文檔。實(shí)際應(yīng)用實(shí)例:
@echo off
runas /user:Administrator /sa “C:\Program Files\Internet Explorer\iexplore.exe”
說(shuō)明:以管理員身份運(yùn)行IE瀏覽器蝇闭。像這樣呻率,我們將命令保存為批處理后,只要在用戶電腦上運(yùn)行這個(gè)批處理(第一次輸入管理員密碼)呻引,以后用戶只要雙擊該文件就可會(huì)以管理員身份執(zhí)行命令中所指定的程序了礼仗。
那么我們只需要在服務(wù)器上預(yù)先建立幾個(gè)賬戶,甚至可以在程序中動(dòng)態(tài)創(chuàng)建逻悠,然后分配到一個(gè)合適權(quán)限的組(比如一個(gè)ppt用戶組元践,只開放一個(gè)轉(zhuǎn)換文件夾的讀寫權(quán)限),然后使用runas 命令童谒,就能達(dá)到多進(jìn)程同時(shí)執(zhí)行单旁,同時(shí)導(dǎo)出的效果。
還是最開始我們使用的四行代碼饥伊,打包成exe文件象浑,
然后在shell中使用runas,如下圖撵渡,換幾個(gè)用戶多執(zhí)行幾次融柬。
這里要指定命令行參數(shù) /profile 加載用戶配置文件死嗦,不然默認(rèn)分配的內(nèi)存是不夠啟動(dòng)PowerPoint程序的趋距。
然后~ BOOM!
我們成功啟動(dòng)了多個(gè)發(fā)布進(jìn)程,并且都在正常發(fā)布越除。
由于runas必須手動(dòng)輸入密碼节腐,我們可以使用sanur等windows下的管道工具外盯,或者指定rnas 的/savecred選項(xiàng),做到免密啟動(dòng)翼雀。
如果有c#的編程基礎(chǔ)饱苟,可以使用c#的Process類,指定進(jìn)程啟動(dòng)信息StartInfo的username和password狼渊,就可以不使用這種命令行方式箱熬。