其實javascript不僅可以做網頁應用,也可以不依賴任何環境開發windows應用程式。windows系統自帶mshta.exe,相當于一個javascript虛拟機,我們把.html檔案擴充名改成.hta檔案,網頁就變成了hta應用程式,輕按兩下就可以執行。當然hta可以操作本地檔案資料庫等,功能比html強大許多,可以開發C/S應用程式。本人之前是個背景程式員,後來我轉行之後,就成了程式設計的業餘愛好者,今天這個demo使用了javascript和少許c++,僅供大家娛樂,參考,謹慎用于生産環境。
先給大家上幾張圖,看一下javascript開發的windows應用效果,然後再上代碼。這個demo是個音樂播放器,功能隻有掃描磁盤上的音頻檔案随機播放。
也有一個搜尋功能,搜尋随機播放清單中的歌曲,簡單的正則比對
測試發現占用記憶體和cpu很低。
最後樓主用c++給他做了個exe殼子,這樣就能把他設定為預設播放器了,輕按兩下音頻檔案就能用樓主開發的播放器打開,效果如下圖
大家可能也能看出,我用的html中的<audio>标簽,audio标簽支援mp3、m4a、ogg等格式,大家也可以用<embed>标簽,embed标簽支援的格式還多,還支援wma、aac、wav等格式,但是前提是電腦安裝了windows media player,樓主電腦window media player壞了,是以樓主用了<audio>标簽。
最終軟體包含三個檔案,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;'> 小小播放器是一款小巧的音樂播放器,使用<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應用的方法就簡單給大家講到這裡,此案例僅供參考,請勿用于生産環境。