天天看點

javascript不依賴庫開發windows應用程式

        其實javascript不僅可以做網頁應用,也可以不依賴任何環境開發windows應用程式。windows系統自帶mshta.exe,相當于一個javascript虛拟機,我們把.html檔案擴充名改成.hta檔案,網頁就變成了hta應用程式,輕按兩下就可以執行。當然hta可以操作本地檔案資料庫等,功能比html強大許多,可以開發C/S應用程式。本人之前是個背景程式員,後來我轉行之後,就成了程式設計的業餘愛好者,今天這個demo使用了javascript和少許c++,僅供大家娛樂,參考,謹慎用于生産環境。

        先給大家上幾張圖,看一下javascript開發的windows應用效果,然後再上代碼。這個demo是個音樂播放器,功能隻有掃描磁盤上的音頻檔案随機播放。

javascript不依賴庫開發windows應用程式
javascript不依賴庫開發windows應用程式
javascript不依賴庫開發windows應用程式

        也有一個搜尋功能,搜尋随機播放清單中的歌曲,簡單的正則比對

javascript不依賴庫開發windows應用程式

        測試發現占用記憶體和cpu很低。

javascript不依賴庫開發windows應用程式

        最後樓主用c++給他做了個exe殼子,這樣就能把他設定為預設播放器了,輕按兩下音頻檔案就能用樓主開發的播放器打開,效果如下圖

javascript不依賴庫開發windows應用程式

大家可能也能看出,我用的html中的<audio>标簽,audio标簽支援mp3、m4a、ogg等格式,大家也可以用<embed>标簽,embed标簽支援的格式還多,還支援wma、aac、wav等格式,但是前提是電腦安裝了windows media player,樓主電腦window media player壞了,是以樓主用了<audio>标簽。

javascript不依賴庫開發windows應用程式

        最終軟體包含三個檔案,app.ico是圖示,main.hta就是樓主用javascript開發的windows應用程式,輕按兩下就可以打開,main.exe是樓主給他做的殼子,用于輕按兩下打開音頻檔案通過指令行将檔案路徑傳遞給main.hta。下面先上main,hta的代碼,裡面基本上和普通javascript文法相同,隻是有一些用到了ActiveX控件的對象。

<!doctype html>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=9" >
<title>小小播放器v1.0</title>
<script>
	//窗體初始化
	(function(){
		window.resizeTo(400,80);
	}())
</script>
<!-- hta應用程式特有标簽 -->
<hta:application 
id=musicPlayer 
scroll=no 
innerborder=no 
icon=app.ico
contextMenu=no
selection=no
>
<style>
body{background: #ccc;text-align: center;}
button{
	font-size: 22px;
	color:#38f;
	background: #fff;
	border:1px solid #fff;
	border-radius: 10px;
	cursor: pointer;
}
input[type='text']{
	position: relative;
	bottom:4px;
	font-size: 12px;
	padding:5px;
	color:#38f;
	background: #fff;
	border:1px solid #fff;
	border-radius: 10px;
	width: 100px;
}
</style>
<body>
	<!-- 視圖子產品 -->
	<audio 
	style="width:100%;" 
	id="mediaPlayer" 
	onended="sys.next()" 
	autoplay="autoplay" 
	onerror="sys.error()" 
	src="">
	</audio>
	<div id="normalPanel">
		<button title="上一曲"
		onclick="sys.last()"
		>◄◄</button>
		<button title="暫停" style="border-radius: 24px;padding-bottom: 5px;"
		onclick="if(sys.playerHandel.paused){sys.playerHandel.play();this.innerText='■';this.title='暫停'}else{sys.playerHandel.pause();this.innerText='►';this.title='播放'}" 
		>■</button>
		<button title="下一曲"
		onclick="sys.next()"
		>►►</button>
		<input type="text" id="findInp" value="關鍵字搜尋..." 
		onclick="if(this.value=='關鍵字搜尋...'){this.value=''}" 
		/>
		<button title="搜尋"
		onclick="sys.find()"
		>F</button>
		<button title="關于"
		onclick="sys.about()"
		>A</button>
	</div>
	<div id="abnormalPanel"
		style="display:none;"
	>
		<button title="關于"
		onclick="sys.about()"
		>A</button>
		<span style="padding: 5px;" id="abnormalPanelShowName"></span>
	</div>
</body>
<script>
/**
 * 小小播放器
 * @type {Object}
 */
 	this.windowsForm={
 		Text:function(str){
 			document.title=str;
 		}
 	}
 	this.commandLine=musicPlayer.commandLine.split(" ");//接收指令行參數
 	commandLine[0]="";
 	commandLine=commandLine.join(" ");
	this.sys={
		appName:"小小播放器v1.0",
		playList:[],actNum:1,totalNum:0,playerHandel:document.getElementById("mediaPlayer"),
		getPlaylist:function(){
			/*枚舉音頻檔案取得播放清單*/
			var data=[];
			var fso=new ActiveXObject("Scripting.FileSystemObject");
			var f=fso.GetFolder("F:\\音樂");
			var fc=new Enumerator(f.files);
			var fileType=/(.mp3|.m4a|.wav)/i;
			for (; !fc.atEnd(); fc.moveNext()){
				if(fileType.test(fc.item())){
					data.push(fc.item());
				}
			}
			data.sort(function() {
			     return (0.5-Math.random());
			})
			this.playList=data;
			this.totalNum=data.length;
		},
		main:function(){//應用程式入口
			this.getPlaylist();
			this.play(1);
		},
		play:function(num){
			this.playerHandel.setAttribute("src","null");
			this.playerHandel.setAttribute("src",""+this.playList[num-1]);
			this.actNum=num;
			windowsForm.Text(this.appName+"[♫"+this.playList[num-1].name+"]");
		},
		next:function(){
			this.actNum=this.actNum==this.totalNum?1:this.actNum+1;
			this.play(this.actNum);
		},
		last:function(){
			this.actNum=this.actNum==1?this.totalNum:this.actNum-1;
			this.play(this.actNum);
		},
		find:function(){
			/*搜尋歌曲*/
			var keyWd=document.getElementById("findInp").value;
			var str="<style>.musicItem{padding: 5px;margin:5px;border:1px solid #38f;background: #38f;color:#fff;font-size: 20px;border-radius:10px;cursor:pointer;}.musicItem:hover{padding: 5px;margin:5px;border:1px solid #38f;background: #fff;color:#38f;font-size: 20px;border-radius:10px;cursor:pointer;}</style><div style='position:absolute;left:0px;top:0px;width:100%;height:100%;overflow-y:auto;'>";
			for(var i=0;i<this.playList.length;i++){
				if(RegExp(keyWd,"i").test(this.playList[i].name)){
					str+="<div class='musicItem' onclick=parent.sys.play("+(i+1)+")>";
					str+=this.playList[i].name;
					str+="</div>";
				}
			}
			str+="</div>";
			var aPopup=window.createPopup();
			aPopup.document.body.innerHTML=str;
			aPopup.show(window.screenX, window.screenY+82,400,300);
		},
		about:function(){
			var str="about:";
			str+="<title>關于-小小播放器v1.0</title>";
			str+="<body style='text-align:justify;font-size:24px;background:#ccc;padding:10px;text-align:center;'><img src='app.ico'/><h1 style='font-size:36px;'>小小播放器v1.0</h1><hr><p>版權所有:© 2019 sdxjwkq01</p><p style='text-align:left;'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;小小播放器是一款小巧的音樂播放器,使用<span style='color:#ffa533;background:#fff;'>javascript</span>和<span style='color:#2d78f4;background:#fff;'>c++</span>開發,占用極低的記憶體及cpu,可以快速枚舉出音樂庫所有音樂并随機播放。支援音頻格式為:mp3,m4a,wav。</p></body>";
			showModalDialog(str);
		},
		error:function(){
			alert("很抱歉!應用程式發生錯誤:\n\r音頻格式錯誤!");
		}
	}
	/*正常模式和試聽模式【作為預設播放器輕按兩下打開某個音頻檔案】*/
	if(commandLine!=" null"&&commandLine!=" "){
		window.resizeTo(500,140);
		document.getElementById("normalPanel").style.display="none";
		document.getElementById("abnormalPanel").style.display="block";
		var musicTitle=commandLine.split("\\");
		document.getElementById("abnormalPanelShowName").innerHTML=musicTitle[musicTitle.length-1];
		sys.playerHandel.setAttribute("controls","controls");
 		sys.playerHandel.onended=function(){sys.playerHandel.play()};
 		sys.playerHandel.setAttribute("src",commandLine);
 		sys.playerHandel.play();
 	}else{
 		sys.main();
 		this.onkeydown=function(e){
	 		switch(e.keyCode){
	 			case 39:{
	 				sys.next();//->鍵
	 				break;
	 			}
	 			case 37:{
	 				sys.last();//<-鍵
	 				break;
	 			}
	 			default:;
	 		}
	 	}
 	}
</script>
           

        裡面有注釋,代碼很簡單,就是掃描“F:\\音樂"檔案夾下所有檔案,枚舉出音頻檔案存到數組,打亂順序,開始播放,監聽audio标簽。大家也可以改成掃描全盤檔案,這個需要用遞歸實作,也要用到ActiveX控件的FileSystem對象。

        到這裡,音樂播放器主程式就實作了,下面講一下他的殼子,main.exe

        本來想用c#做exe檔案的,不過,考慮到有一些電腦沒有.net環境,可能會運作失敗,是以後來改成了c++實作,下面先上代碼。

#include <iostream>
#include <windows.h>
#include <string>
using namespace std;
/**
 * [main 應用程式入口]
 * @param  argc [參數個數]
 * @param  argv [參數指針]
 * @return      [description]
 */
int KillProcessByTitle(const char* sWindowName);
int main(int argc,char **argv) {
    char **temp = argv;//接收參數指針
    string exePath=*temp;
    /*exe檔案尋址*/
    exePath=exePath.replace(exePath.find("main.exe"),8,"")+"main.hta";
    /*string轉char* */
    const char* htaPath=exePath.c_str();
    KillProcessByTitle("小小播放器v1.0");
    if(argc==2){
    	++temp;
    	ShellExecute(NULL, NULL, htaPath, *temp, NULL, SW_HIDE);
    }else{
    	ShellExecute(NULL, NULL, htaPath, "null", NULL, SW_HIDE);
    }
    return 0;
}
/**
 * [KillProcessByTitle 根據視窗名稱關閉視窗]
 * @param  sWindowName [description]
 * @return             [description]
 */
int KillProcessByTitle(const char* sWindowName)
{
    HWND hWindow=FindWindow(NULL,sWindowName);
    if(hWindow==NULL)
    {
        return 1;
    }
    PostMessage(hWindow,WM_CLOSE,0,0);//向視窗發送WM_CLOSE消息關閉對話框視窗,如果該對話框是程序的子對話框則要進行後面的關閉程序操作
    DWORD dwProcessID;
    HANDLE hProcess;
    if(GetWindowThreadProcessId(hWindow, &dwProcessID)==0)
    {   
        return 2;   
    }   
 
    hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessID);   
    if(hProcess==NULL)   
    {   
        return 3;   
    }   
 
    if(!TerminateProcess(hProcess, 0))   
    {   
        return 4;
    }  
 
    return 0;
}
           

        我的電腦沒有vc++和vs,是以我用的gcc編譯的,gcc比較小巧。

        樓主做的這個windows應用因為是在虛拟機中執行,是以不像正常的windows程式設計那樣友善操作窗體句柄和處理windows的消息機制。我們可以看到c++的實作很簡單,一個main函數,用于打開音頻檔案給main.hta傳參,main.hta裡面這一句this.commandLine=musicPlayer.commandLine.split(" ");//接收指令行參數  就是接收參數的。另一個KillProcessByTitle()函數就是根據窗體名稱關閉窗體,這樣就防止了main.hta同時打開多個視窗,當程式運作時,輕按兩下音頻檔案,會先結束程式再重新建立窗體。

        此案例理論上支援win7及以上系統,不需要攜帶webkit庫等亂七八糟的東西,軟體體積不到1MB。這樣看來,hta應用程式還是很實用的。

        用javascript開發windows應用的方法就簡單給大家講到這裡,此案例僅供參考,請勿用于生産環境。

繼續閱讀