探索InstallShield——制作一個完整的應(yīng)用程序安裝實例
發(fā)布時間:2008-09-05 09:56:37 作者:佚名
我要評論
大家一定有過這樣的經(jīng)歷,每當(dāng)我們殫精竭慮為用戶開發(fā)完一個系統(tǒng)之后往往都是派一名技術(shù)員到用戶那里進(jìn)行應(yīng)用軟件的安裝,數(shù)據(jù)庫的配置,這些看似簡單的配置對一般用戶來說可不是件容易做的事。這不僅給那些喜歡力求完美的程序設(shè)計師帶來一點遺憾外,而且也使我們
大家一定有過這樣的經(jīng)歷,每當(dāng)我們殫精竭慮為用戶開發(fā)完一個系統(tǒng)之后往往都是派一名技術(shù)員到用戶那里進(jìn)行應(yīng)用軟件的安裝,數(shù)據(jù)庫的配置,這些看似簡單的配置對一般用戶來說可不是件容易做的事。這不僅給那些喜歡力求完美的程序設(shè)計師帶來一點遺憾外,而且也使我們設(shè)計出來的軟件缺少一種安全可靠感。如果我們在交給用戶軟件的時候能給他們一個象一些專業(yè)軟件的安裝程序,這不僅給用戶留下了一個良好的第一印象,同時也節(jié)省了開發(fā)維護(hù)成本。 |
要說起制作安裝程序的軟件有許多,像InstallShield,CreateInstall,Easy Install,EasySetup,SetupBuilder等等,可以舉出一大堆來。但其中最為著名的當(dāng)屬InstallShield。目前世界上絕大多數(shù)的商業(yè)軟件的安裝盤都是用她來完成,比如我們可能天天在使用的Norton AntiVirus 2000。 |
InstallShield之所以很流行也許是因為程序員可以在通過編寫腳本語言方便的進(jìn)行Windows應(yīng)用程序的注冊表修改、ODBC配置等復(fù)雜的操作,以及她本身提供了一些相當(dāng)智能化的功能。下面,就本人在實際開發(fā)中的一些所得和大家一起探討,文中如有任何不妥之處還請同行們給予指教。 |
在講述到數(shù)據(jù)庫配置的一系列問題是以目前最常用的ODBC接口為基礎(chǔ),文中用到的實例是在WIN98系統(tǒng)中,InstallShield用的是Professional 6.21下開發(fā)和調(diào)試的,其中的應(yīng)用程序?qū)嵗玫腜owerBuilder 7.0下開發(fā)的演示程序。 |
首先,我們可以來快速體驗一下用InstallShield制作的安裝程序是什么樣的。 |
當(dāng)然,我不可能詳細(xì)到怎樣建立一個項目之類的問題,有關(guān)這方面的內(nèi)容還請看InstallShield的入門教材。 |
首先,用Project Wizard建立一個新的項目,取名Demo,同時在對話框中選擇所有的選項,并且在語言選擇中選擇Chinese(PRC)和English,即簡體中文和英文支持。當(dāng)然你也可以選擇其它語言,為了便于討論我只選擇了Chinese(PRC)和English。 |
運行一下試一試,一切都正常,只是一個空殼罷了,沒有任何東西。 |
好了,戰(zhàn)斗的號角已經(jīng)吹響了,讓我們集中精力個個殲滅。 |
一、 語言選擇 |

|
由于我們在生成項目的同時在語言選擇中選擇了Chinese(PRC)和English,因此,在圖01中的下拉列表框中會自動出現(xiàn)中文,英文的選項。這一步由模板自動完成,我們不要做任何事情。 |
二、 設(shè)置啟動畫面 |

|
在我們生成項目的同時,InstallShield會為我們?nèi)笔〉姆派弦粡垎赢嬅妗R妶D16。 |
從該圖片上我們可以看出,一般情況下是放置一張能很好的代表你公司的圖片,同時上面很清楚地標(biāo)明你的應(yīng)用程序的名稱,版本,公司名稱,版權(quán)歸屬等基本信息。毫無疑問,我們必須用我們自己的圖片來替換它。替換的方法是,在Setup File面板中,打開Splast Screen,找到Language Independent,這時我們會發(fā)現(xiàn)在它右邊的列表中有一個文件Setup.bmp,它就是圖18的那一張,我們必須首先把它刪除,然后再插入我們想要的那一張,在列表中任何一個位置點擊鼠標(biāo)右鍵,選擇Insert Files,用一個BMP文件替換它就行了。注意,我們替換的圖片的文件名必須是Setup.bmp。 |
另外,順便提一下,在Splash Screen下除了Language Independent分支外,還有Chinese(PRC)和English分支,如果在它們中放置不同的Setup.bmp文件,那么系統(tǒng)會根據(jù)用戶在安裝前選擇了不同的語言而啟動不同的背景,這樣可以有效地避免多國語言的煩惱。同樣,在設(shè)置法律聲明(許可協(xié)議)和自述文件的時候也是一樣的。 |
三、 開始安裝 |

|
這一步由系統(tǒng)自動給出,我們不要做任何事情。但是我們可以在這里進(jìn)行軟件安裝前的準(zhǔn)備工作。比如,操作系統(tǒng)判斷,機(jī)器可用內(nèi)存容量的檢測等,這些往往被忽視。 |
當(dāng)我們用項目向?qū)傻男马椖繒r,InstallShield只為我們生成兩個事件,分別是OnFirstUIBefore和OnMoving,它們的意義是, |
OnFirstUIBefore:在應(yīng)用程序第一次安裝時為了搜集用戶信息而彈出的對話框之前,應(yīng)用程序所響應(yīng)的事件。 |
OnMoving:該事件在當(dāng)所選擇的安裝組件正在安裝或反安裝時響應(yīng)。 |
顯然,準(zhǔn)備工作可以在事件OnFirstUIBefore中完成,但I(xiàn)nstallShield同時還為我們提供了事件OnBegin,該事件在是在Setup腳本中第一個被觸發(fā)。還記得圖片02,上面有個進(jìn)度條,顯示了“正準(zhǔn)備InstallShield(R)向?qū)?..”,當(dāng)該進(jìn)度條結(jié)束后就首先調(diào)用OnBegin,然后才是OnFirstUIBefore。因此,我們可以將一些前期的準(zhǔn)備工作放到這里來完成。下面是如何具體實現(xiàn)。 |
InstallShield將事件分成三類:全局事件,組件事件和雜項事件。其中,全局事件的添加最為方便,只要將當(dāng)前行光標(biāo)移到Serup腳本的最后,然后寫上 |
像其它編程工具一樣,InstallShield也為我們提供了一些系統(tǒng)變量,利用這些變量我們可以輕而易舉的得到一些像系統(tǒng)目錄,語言代碼,CUP類型等實用數(shù)據(jù)。 |
/*****************程序代碼*******************/ |
if ( !SYSINFO.bIntel) then |
szMsg = "提示:該軟件只能運行在Intel系列的處理器上!\n\n安裝程序?qū)⒔K止"; |
MessageBox(szMsg, SEVERE); |
if (SYSINFO.WIN9X.bWinMe || |
SYSINFO.WINNT.bWin2000) then |
szMsg = "提示:該軟件只能運行在WIN9X系統(tǒng)上才能確保程序的正常工作!\n\n是否繼續(xù)安裝?"; |
if ( !AskYesNo (szMsg, NO)) then |
/******************結(jié)束*********************/ |
存放本機(jī)的一些系統(tǒng)變量,如操作系統(tǒng),CUP類型等。有關(guān)系統(tǒng)變量的具體使用請參閱編程手冊。 |
該函數(shù)彈出一消息窗口,用戶通過按是或非來回答該窗口顯示的問題。 |
在查閱了InstallShield系統(tǒng)變量后并沒有找到有關(guān)內(nèi)存容量方面的變量,因此只有通過調(diào)用函數(shù)來實現(xiàn)。 |
相關(guān)函數(shù):GetSystemInfo() |
該函數(shù)返回有關(guān)目標(biāo)系統(tǒng)的諸如CUP類型,磁盤容量,當(dāng)前日期,當(dāng)前操作系統(tǒng),內(nèi)存容量等信息,返回的內(nèi)存容量是以千字節(jié)為單位。有關(guān)函數(shù)的具體使用請參閱函數(shù)手冊。 |
/*****************程序代碼*******************/ |
if (GetSystemInfo (EXTENDEDMEMORY, nvFreeMem, svResult) < 0) then |
MessageBox ("內(nèi)存檢測失敗,安裝程序?qū)⒔K止!.", SEVERE); |
if ( nvFreeMem < 16384 ) then |
szMsg = "該軟件只能安裝在16M以上內(nèi)存的機(jī)器中。\n很遺憾,本機(jī)器可用內(nèi)存不足16M!\n\n安裝程序?qū)⒔K止!"; |
MessageBox (szMsg, SEVERE); |
/******************結(jié)束*********************/ |
|
選擇Setup File面板,打開Language Independent,選擇Operating System Independent ,在右邊列表中找到License.txt文件,雙擊它輸入自己的許可協(xié)議就行了。 |
|
同上,找到Infolist.txt文件,雙擊它輸入需要說明的自述文件。 |
|
看一看圖片,不難發(fā)現(xiàn)當(dāng)安裝程序運行這一步時上面已經(jīng)填上了缺省的用戶名和客戶名稱,甚至連序列號都有。這是怎么回事呢?原來在前面我們介紹的事件OnFirstUIBefore的開始有一段代碼,如下: |
TARGETDIR = PROGRAMFILES ^@COMPANY_NAME ^@PRODUCT_NAME; |
SHELL_OBJECT_FOLDER = @FOLDER_NAME; |
如果我們沒有給svName和svCompany賦新值的話缺省的用戶名和客戶名稱就是我們在安裝Windows時注冊的用戶和單位。當(dāng)然,如果沒有給svSerial賦值的話,圖片上的序列號就為空。 |
PROGRAMFILES:存放Windows程序文件夾(Program files)的完整路徑。不能更改。 |
|
用InstallShield安裝模板制作出的安裝程序在運行過程如果沒有序列號是無法再繼續(xù)下去的。但如果我們在序列號上不加任何檢驗的話,InstallShield會默認(rèn)為任何字符都是有效的而不管它是什么,此時序列號也就毫無意義,除非你想讓你的應(yīng)用程序無限制的發(fā)布。因此,還是建議你在安裝過程中對序列號進(jìn)行一下有效性檢驗。方法很簡單,在事件OnFirstUIBefore中找到Dlg_SdRegisterUserEx分支,見下列代碼: |
nResult = SdRegisterUserEx( szTitle, szMsg, svName, svCompany, svSerial ); |
if (nResult = BACK) goto Dlg_SdShowInfoList; |
if (nResult = BACK) goto Dlg_SdShowInfoList; |
if ( svSerial != '199721') then |
MessageBox("警告:輸入序列號錯誤,請確認(rèn)后重輸!",SEVERE); |
goto Dlg_SdRegisterUserEx; |
就這么簡單。聲明一下,這里我只是給出的最簡單的判別模式,較為安全的模式是在判別時對序列號進(jìn)行必要的加解密轉(zhuǎn)換,有興趣的朋友可以自己去實現(xiàn)。 |
|
在一般情況下,InstallShield默認(rèn)的安裝文件夾是存放Windows程序文件夾(Program files)的完整路徑,即C:\ Program files(如果Windows安裝在C盤的話)。但是,在絕大多數(shù)情況下我們都希望客戶將應(yīng)用程序安裝在單獨的數(shù)據(jù)盤上,即不與操作系統(tǒng)安裝在同一個硬盤分區(qū)里。一方面便于管理,另一方面也為了防止我們的程序因為系統(tǒng)盤格式化而受到連累。因此,我們常常會建議用戶在安裝應(yīng)用程序的時候選擇另一個硬盤,如D盤。 |
另外,我們有時希望我們所開發(fā)出來的應(yīng)用程序安裝在一個帶有自己公司名稱和應(yīng)用程序名稱的目錄下。InstallShield默認(rèn)的目錄就做的非常好,她會建立一個諸如“C:\Program Files\ABCD軟件工作室\Demo”的目錄(在這里我虛構(gòu)了一個公司“ABCD軟件工作室”)。但,問題是,一旦讓用戶選擇了其它的路徑,或者硬盤,他們幾乎都不會在目錄上加上開發(fā)商和應(yīng)用程序目錄,“\ABCD軟件工作室\Demo”。如果用戶僅僅選擇了磁盤而忘了選擇任何目錄的話,InstallShield就會將所有的程序和數(shù)據(jù)文件復(fù)制到磁盤根目錄下,用戶和我們當(dāng)然都不希望這樣。因此,為了避免這一切的發(fā)生我們只能寄希望于我們的安裝程序能夠有足夠的智能來應(yīng)付這一切,這樣就不管用戶是否選擇了目錄。實現(xiàn)方法如下。 |
在事件OnFirstUIBefore中找到Dlg_SdAskDestPath分支,如下 |
nResult = SdAskDestPath( szTitle, szMsg, szDir, 0 ); |
if (nResult = BACK) goto Dlg_SdRegisterUserEx; |
nResult = SdAskDestPath( szTitle, szMsg, szDir, 0 ); |
nResult = SdAskDestPath( szTitle, szMsg, szDir, 0 ); |
if ( svTemp != szDir) then |
szDir = szDir^@COMPANY_NAME ^@PRODUCT_NAME; |
該語句的功能是在用戶選擇的路徑后加上單位和應(yīng)用程序名稱作為安裝的目錄(注:這里我并沒有考慮到用戶在選擇了默認(rèn)外的路徑后加上單位和應(yīng)用程序名稱作為安裝目錄的情況)。其中,變量COMPANY_NAME 和PRODUCT_NAME中存放的是單位名和產(chǎn)品名,我們可以隨時在資源面板中修改它的值。 |
變量TARGETDIR是指向在硬盤上目的文件夾的完整路徑。當(dāng)調(diào)用函數(shù)SdAskDestPath時,該函數(shù)會創(chuàng)建一個對話框來讓用戶選擇應(yīng)用程序要存放的目的路徑。 |
不要以為加上了單位和程序的目錄就萬事大吉了,可別忘了既然讓用戶來選擇安裝的目的盤就很有可能出現(xiàn)磁盤空間不夠大,從而導(dǎo)致程序的安裝失敗或安裝后的剩余空間不夠程序?qū)頂?shù)據(jù)的存放等等。因此,必須在用戶選擇之后,程序進(jìn)入下一步之前對目的盤的可用空間進(jìn)行判斷。在這里我假設(shè)我們的程序需要10兆的空間來存放目前的文件和未來幾年可能的數(shù)據(jù)。 |
那么,如何來實現(xiàn)呢?幸好InstallShield為我們提供了足夠多的函數(shù)來應(yīng)付這一切。 |
該函數(shù)從指定的路徑或帶有路徑的文件名當(dāng)中提取該路徑或文件所在的磁盤驅(qū)動器號。調(diào)用成功返回0,否則返回小于0的任何數(shù)。 |
該函數(shù)返回指定磁盤或指定路徑所在磁盤的剩余空間,用字節(jié)表示。 |
//*******加入磁盤剩余空間判斷,目前判斷剩余空間是否少于10M |
//******* 10M * 1024千字節(jié) * 1024字節(jié) = 10485760 字節(jié) |
GetDisk (szDir, svDrive); |
lFreeSpace = GetDiskSpace (svDrive); |
if (lFreeSpace < 10485760) then |
MessageBox("警告:安裝路徑所在的磁盤空間不足10M!請重新選擇!",SEVERE); |
if (nResult = BACK) goto Dlg_SdRegisterUserEx; |
|
該步驟是由InstallShield自動給出的,一般情況下我們不必做任何事。三個安裝選項:典型安裝、壓縮安裝和自定義安裝是根據(jù)我們在安裝類型(Setup Type)的面板中預(yù)先設(shè)置好的值來進(jìn)行的。如果用戶選擇了自定義安裝,系統(tǒng)就會彈出如下界面,見圖。 |
這是由系統(tǒng)自動給出的,我們用不著寫一行代碼,這是不是很棒? |
不知道你有沒有注意到窗口的左面有一個說明框,它是用來提示用戶目前所選擇的組件是干什么的。在缺省的狀態(tài)下它是空的,需要我們來添加。添加的方法是在組件(Components)的面板中,每當(dāng)我們點中一個組件在左邊的列表中就會有一個描述欄(Description),它的內(nèi)容就是上面提到的幫助信息,你只要將相對用戶說的話寫上去就行了。 |
|
這一步由InstallShield自動給出,一般不要做任何修改。 |
|
我們一定還記得每當(dāng)安裝一個像Office等專業(yè)化軟件時,在軟件正式安裝前系統(tǒng)都會給出一個列表框,其中列出了你所做的一切選擇,包括軟件的安裝路徑,需要安裝的組件等,同時還包括了注冊的用戶名和單位等信息。在我們用InstallShield制作安裝盤的時候也會有這一個列表框,但遺憾的是在缺省狀況下InstallShield提供的這個列表框是空的,什么內(nèi)容也沒有,這需要我們給它加入。好在有函數(shù)ListAddString來幫我們的忙。 |
相關(guān)函數(shù):ListAddString |
該函數(shù)在字符串列表框的當(dāng)前對象前后新增加一個字符串。調(diào)用成功返回0,否則返回小于0的任何數(shù)。 |
在腳本Setup.rul的事件OnFirstUIBefore中找到Dlg_SdStartCopy分支,如下: |
listStartCopy = ListCreate( STRINGLIST ); |
//The following is an example of how to add a string(svName) to a list(listStartCopy). |
//eg. ListAddString(listStartCopy,svName,AFTER); |
nResult = SdStartCopy( szTitle, szMsg, listStartCopy ); |
ListDestroy(listStartCopy); |
if (nResult = BACK) goto Dlg_SdSelectFolder; |
nResult = SdStartCopy( szTitle, szMsg, listStartCopy ); |
//*******加入安裝過程中用戶所選擇的主要步驟 |
ListAddString(listStartCopy,"客戶信息:",AFTER); |
ListAddString(listStartCopy,"用戶名:" + svName,AFTER); |
ListAddString(listStartCopy,"用戶單位:" + svCompany,AFTER); |
ListAddString(listStartCopy,"",AFTER); |
ListAddString(listStartCopy,"程序安裝路徑:" + szDir,AFTER); |
ListAddString(listStartCopy,"",AFTER); |
ListAddString(listStartCopy,"程序文件夾:" + szfolder,AFTER); |
ListAddString(listStartCopy,"",AFTER); |
case TYPICAL : ListAddString(listStartCopy,"安裝類型:典型安裝",AFTER); |
case COMPACT: ListAddString(listStartCopy,"安裝類型:壓縮安裝",AFTER); |
case CUSTOM: ListAddString(listStartCopy,"安裝類型:自定義安裝",AFTER); |
其中svName、svCompany、szDir等變量我們可以在事件OnFirstUIBefore開始的變量定義中找到。從變量的名稱我們很清楚的知道該變量存放的是用戶名,單位,安裝目錄等。 |
十二、 第一幅背景,第二幅背景——圖片12,圖片13 |
|
|
細(xì)心的朋友一定會注意到軟件在安裝過程中會出現(xiàn)兩副不同的背景圖片,圖12和圖13,這就是InstallShield的顯示區(qū)界面,我們可以稱它為布告板。這看上去是不是很像在播放幻燈片?呵呵,我們可以和微軟的比一比了(說句笑話)。言歸正傳,還是來說說是如何實現(xiàn)的吧。 |
布告板,它只有在文件被傳輸時才被激活。也就是說,當(dāng)你調(diào)用ComponentTransferData函數(shù)來解壓并拷貝時布告板才被顯示,該函數(shù)是有系統(tǒng)自動調(diào)用,用不著我們來操心。 |
但是,你不能為一個布告板指定顯示時間,InstallShield會根據(jù)整個程序的安裝時間(指文件拷貝時間)自動的為每一個文件平均分配,但至少是2秒。如果你的安裝程序僅持續(xù)20秒,而你卻放置了25副圖片,很顯然系統(tǒng)只會顯示前10副圖片。 |
當(dāng)你需要顯示布告板之前還必須確保以下兩個條件: |
1、 在你的腳本里,首先要確保在文件被傳輸前調(diào)用Enable(BACKGROUND) 和 Enable(FULLWINDOWMODE),我們可以加在事件OnMoving中。形式如下: |
PlaceWindow(STATUSDLG, 400, 10, LOWER_LEFT); |
RegDBSetItem(REGDB_APPPATH, szAppPath); |
RegDBSetItem(REGDB_APPPATH_DEFAULT, szAppPath ^ @PRODUCT_KEY); |
Enable(BACKGROUND) :顯示安裝主背景窗口 |
Enable(FULLWINDOWMODE):設(shè)置主背景窗口為最大化。 |
2、 在設(shè)置文件(Setup Files)面板中,在合適的目標(biāo)語言和平臺下放置具有特殊后綴名的布告板文件。默認(rèn)的命名是:“Bbrd”,然后再跟一個數(shù)字,最后再加上后綴“.bmp”或“.wmf”。例如,我們現(xiàn)在要加三副BMP圖片,則這三副圖片的名稱分別應(yīng)該是Bbrd1.bmp、Bbrd2.bmp、Bbrd3.bmp。 |
好了,運行一下,很不錯。我們可以利用這項技術(shù)在安裝過程中播放類似幻燈片效果,就像安裝Windows一樣。 |
十三、 復(fù)制完成 |

|
|
在很多情況下,當(dāng)我們修改了注冊表或是在系統(tǒng)中安裝了其它驅(qū)動程序的時候,為了確保應(yīng)用程序能夠順利運行,我們還是希望用戶在安裝結(jié)束之后能夠重新啟動一下系統(tǒng)。這時函數(shù)SdFinishReboot就顯得十分有用。 |
相關(guān)函數(shù):SdFinishReboot |
該函數(shù)顯示一個重新啟動的對話框,它提供給用戶兩種選擇:系統(tǒng)自動啟動和用戶自己啟動。當(dāng)函數(shù)返回WILL_REBOOT表明用戶選擇了重新啟動;當(dāng)返回NEXT(或1)表明用戶選擇了自己啟動;當(dāng)返回小于0表明當(dāng)用戶選擇了重新啟動,但重新啟動失敗。在缺省的情況下是用戶自己啟動。 |
一般情況下,我們是在事件OnEnd中調(diào)用SdFinishReboot函數(shù)。OnEnd顧名思義是在Setup腳本里最后被執(zhí)行的事件。程序代碼如下: |
/*****************重啟動代碼*******************/ |
STRING szTitle, szMsg1, szMsg2; |
NUMBER nOption, nReserved; |
szTitle = "已經(jīng)完成全部的拷貝工作,"; |
szMsg1 = "在使用本軟件前必須重新啟動。"; |
szMsg2 = '請選擇一種啟動方式,然后單擊"確定"完成安裝。'; |
if (SdFinishReboot (szTitle, szMsg1, nOption, szMsg2, nReserved) < 0) then |
MessageBox ("重啟動失敗,請重新啟動系統(tǒng)后再使用本軟件!", SEVERE); |
/**************** 結(jié)束 *******************/ |
其中,szTitle、szMsg1、szMsg2不設(shè)置也罷,只是系統(tǒng)會以缺省的方式出現(xiàn)。 |
如果說我們完成了圖1到圖15全部功能后就可以馬上動手制作安裝盤的話那我們所做的也只是給應(yīng)用程序加了一個花邊,并沒有實際功效。它只是把原先由人工的復(fù)制工作交給系統(tǒng)自動去執(zhí)行,更說不上文章開頭所說的實現(xiàn)系統(tǒng)的自動配置。我們還是得在應(yīng)用程序發(fā)行時去用戶現(xiàn)場安裝、配置一些接口程序。 |
大家都知道,我們開發(fā)的程序有百分之九十以上是和數(shù)據(jù)庫打交道。目前,數(shù)據(jù)庫種類的繁多,各軟件廠商又各自推出自己的數(shù)據(jù)庫接口程序,這就使得我們要確保開發(fā)出來的應(yīng)用程序最終能夠安全順利的運行就必須攜帶上自己的數(shù)據(jù)庫接口程序,雖然有一些接口標(biāo)準(zhǔn),但這遠(yuǎn)遠(yuǎn)不夠。因此,如何將這些工作交給安裝程序去完成就成為能否制作出具有專業(yè)水準(zhǔn)的安裝程序的關(guān)鍵,也是為什么我會把它作為一個標(biāo)題單獨分開。 |
那么如何在制作安裝程序的時候?qū)崿F(xiàn)數(shù)據(jù)庫驅(qū)動程序的安裝和配置呢?要實現(xiàn)它我們還是先從注冊表和ODBC兩個基本概念入手,下面進(jìn)行分別討論。 |
在早期的Windows系列應(yīng)用程序中用后綴為.ini的文件保存Windows系統(tǒng)和其他該應(yīng)用程序的配置信息。后來,Windows95的出現(xiàn),系統(tǒng)就用一個稱做注冊表的來代替保存應(yīng)用程序配置信息的.ini文件,盡管Windows95仍然支持使用.ini文件。用注冊表來保留這些信息可以為我們提供一個配置信息的單一源,這不同于那些分布在磁盤里的各種.ini文件的雜燴。同時,注冊表也提供了一個簡單機(jī)制來保存當(dāng)前用戶或當(dāng)前配置的信息。注冊表甚至允許你通過網(wǎng)絡(luò)管理其他機(jī)器的注冊表。另外,在NT下,注冊表能提供安全級別,這在先前的配置存儲機(jī)制中是沒有的。因此,使得能否合理的使用注冊表成為WIN32應(yīng)用程序的特征之一。 |
注冊表使用的是樹型體系結(jié)構(gòu),樹中的每個結(jié)點稱鍵。每個鍵也可以包含其他的鍵或子鍵。它允許進(jìn)一步的分支,也即為值,它用來存儲有效的數(shù)據(jù)。在注冊表中,注冊表用鍵來組織數(shù)據(jù),一個鍵中的值用它們的名來識別,鍵名由可以打印的ASCII字符組成的簡單字符串。另外,以英文句號(.)開始的鍵名被系統(tǒng)所保留。鍵中的值可以用不同的數(shù)據(jù)來表示,可以是從一個簡單的整數(shù)到用戶定義的二進(jìn)制對象。在注冊表中只有四個基本鍵會受到應(yīng)用程序安裝的影響,這四個鍵分別是HKEY_CLASSES_ROOT、HKEY_CURRENT_USER、HKEY_LOCAL_MACHINE和HKEY_USERS。而其中對我們配置ODBC有用的只有HKEY_LOCAL_MACHINE和HKEY_CURRENT_USER。 |
在HKEY_LOCAL_MACHINE中保存的是關(guān)于本地機(jī)器的配置信息,獨立于任何特殊的用戶都存儲在其中。此配置信息又進(jìn)一步分成幾個不同的子鍵,它們是Config,Enum,Hardware,Network,Security,Software和System。 |
其中,Software子鍵用于包含用戶應(yīng)用程序的配置信息,這是最有可能被我們的應(yīng)用程序使用的地方。由于我們的應(yīng)用程序所要的信息一般來說對其他程序并不需要,同時也為了更好的管理這些信息,因此我們應(yīng)該在HKEY_LOCAL_MACHINE\SOFTWARE下增加一個子鍵,鍵名當(dāng)然是能代表我們的應(yīng)用程序。 |
鍵HKEY_CURRENT_USER通常為我們的應(yīng)用程序存儲和恢復(fù)特殊用戶相應(yīng)的特殊配置提供了方便。同樣,在HKEY_CURRENT_USER中也存在一些子鍵,最常用的鍵是Software,打開Software鍵我們會發(fā)現(xiàn)有一個子鍵ODBC,顧名思義里面存放的當(dāng)前機(jī)器上的ODBC.INI文件的內(nèi)容, |
好了,有關(guān)注冊表的一些知識就簡單的說到這里,在開始開始配置我們的ODBC之前,為了便于讀者能夠順利的讀懂本文所附帶的程序代碼,現(xiàn)將有關(guān)InstallShell注冊表函數(shù)作一個簡單的介紹。 |
該函數(shù)用來查看某個指定的鍵是否在注冊表中。 |
該函數(shù)為其他的注冊表函數(shù)設(shè)置一個不同的根鍵。大多數(shù)InstallShield注冊表函數(shù)工作在缺省的以鍵HKEY_CLASSES_ROOT為根的注冊鍵樹上,用這個函數(shù)你就可以指定一個特殊的鍵為根,像鍵HKEY_LOCAL_MACHINE或 HKEY_CURRENT_USER或HKEY_USERS。 |
該函數(shù)為某個在注冊表中的鍵設(shè)置鍵值。如果該鍵不存在,該函數(shù)將為你創(chuàng)建該鍵。但是,新創(chuàng)建的鍵不會被安裝軟件卸載程序所登記,除非它是一個已登記過的鍵的子鍵。要想讓安裝軟件卸載程序所登記可以先用RegDBCreateKeyEx創(chuàng)建然后再設(shè)置該鍵值。 |
該函數(shù)用來在注冊表中創(chuàng)建一個鍵。當(dāng)使用該函數(shù)來創(chuàng)建一個鍵或子鍵時,為了保證能被正確地安裝軟件卸載程序登記要首先確保它的父鍵首先已經(jīng)被成功創(chuàng)建。 |
ODBC,即開放式數(shù)據(jù)庫連接,它是由Microsoft公司提供的應(yīng)用程序接口(API),一個單獨的應(yīng)用程序通過它可以訪問許多個不同類型的數(shù)據(jù)庫及不同格式的文件。 |
雖然繼ODBC之后業(yè)界已推出了一些像OLE DB,ADO等之類的新技術(shù),但目前,開放式數(shù)據(jù)庫連接API也許是在Windows應(yīng)用程序中用得最廣泛的數(shù)據(jù)庫接口。在每種數(shù)據(jù)庫所用到的專用接口中,除了加入特殊代碼外,還需要為ODBC API譯碼。在ODBC API和用來與數(shù)據(jù)庫交換信息的專用接口之間,特殊的ODBC驅(qū)動程序提供了任何一種必需的譯碼。 |
以下的表列出了 ODBC 桌面數(shù)據(jù)庫驅(qū)動程序 4.0 的每一個組件所要求的文件。這些文件安裝在 Windows的系統(tǒng)目錄\Windows\System或 Windows NT的系統(tǒng)目錄\Windows\System32下。如果 ODBC 文件以前被安裝在一個不同的目錄中,請確定你使用的是在 \Windows\System (或 System32) 目錄下的新文件。 |
某些文件是多個部件共同需要的。如果你想要和你的應(yīng)用程序一起重新分配任一個 ODBC 桌面數(shù)據(jù)庫驅(qū)動程序,這些文件也必須被重新分配。建議在分發(fā)軟件時在你的應(yīng)用程序目錄下建立一個 “\ODBC\”目錄,將這些重新拷貝到該目錄底下,但一定要確保該路徑是個PATH搜索路徑。 |
以下的文件對于每一個 ODBC 桌面數(shù)據(jù)庫驅(qū)動程序 4.0 是共同的: |
下列文件是安裝某一個驅(qū)動程序類型的軟件所必須的: |
Msrd2x40.dll,Msrd3x40.dll |
Msxbse40.dll,Oddbse32.dll |
Mstext40.dll,Odtext32.dll |
1、 ODBC 桌面數(shù)據(jù)庫驅(qū)動程序4.0版本至少需要 16 MB 的隨機(jī)訪問內(nèi)存 (RAM) |
2、 有關(guān)與這些驅(qū)動程序一起使用的ODBC版本的信息,參考ODBC程序員參考手冊。 |
3、Adaptive Server Anywehre ODBC驅(qū)動程序 |
下表列出的是工作在Adaptive Server Anywehre環(huán)境下的驅(qū)動程序,必須將這些文件拷貝到一個獨立的目錄下,并且使系統(tǒng)能夠搜索到。 |
1、 ODBC翻譯程序僅僅在你的應(yīng)用程序是依賴于ANSI標(biāo)準(zhǔn)的字符串轉(zhuǎn)換。 |
2、 網(wǎng)絡(luò)接口程序庫是專門用于網(wǎng)絡(luò)通信用,它僅僅是在客戶端的程序訪問網(wǎng)絡(luò)服務(wù)器上才必須存在。 |
3、 連接對話框程序文件在以下的幾種情況下才需要加入: |
a. 你的最終用戶需要創(chuàng)建自己的數(shù)據(jù)源 |
b. 當(dāng)用戶連接數(shù)據(jù)庫時需要輸入用戶標(biāo)識和口令 |
為了使用ODBC驅(qū)動程序安裝程序不僅僅只是將這些驅(qū)動程序文件拷貝到硬盤上,它還必須在注冊表中設(shè)置一組ODBC驅(qū)動程序的屬性。 |
Adaptive Server Anywhere安裝程序會自動在NT和WIN9X的系統(tǒng)注冊表中標(biāo)識和配置ODBC驅(qū)動程序。因此,如果你為你的最終用戶制作應(yīng)用程序的安裝程序時也必須進(jìn)行相同的設(shè)置。 |
通過查看Windows注冊表工具,我們發(fā)現(xiàn)Adaptive Server Anywhere的ODBC驅(qū)動程序是在下列的鍵中被系統(tǒng)所標(biāo)識的。 |
HKEY_LOCAL_MACHINE\Software\ODBC\ODBCINST.INI\Adaptive Server Anywhere 6.0,其中需要設(shè)置的下鍵有 |
除此之外,你還必須在鍵HKEY_LOCAL_MACHINE\Software\ODBC\ODBCINST.INI\ODBC Drivers中為Adaptive Server Anywhere注冊 |
Adaptive Server Anywhere 6.0 |
除了進(jìn)行ODBC驅(qū)動程序注冊外,還必須為用戶的數(shù)據(jù)源進(jìn)行注冊。這是因為每一個用戶數(shù)據(jù)源必須被注冊表登記才能被系統(tǒng)所識別,這樣用戶才能使用它。因此,必須在鍵HKEY_CURRENT_USER\Software\ODBC\ODBC.INI\中進(jìn)行如下注冊。 |
Adaptive Server Anywhere Sample Database |
1、 上面的path除了DatabaseFile指的是客戶數(shù)據(jù)庫所在的路徑,其他的都是指Adaptive Server Anywhere的安裝路徑。我們在給客戶分發(fā)應(yīng)用軟件的時候一般不再另外安裝Adaptive Server Anywhere,所以可以將Adaptive Server Anywhere路徑下的文件Dbeng6.exe,Dbodbc6.dll,Dblgon6.dll,Dbport6.dll,Dbcon6.dll,Dbodtr6.dll一起分發(fā)給客戶,并且拷貝至一個單獨目錄下,然后將路徑指向它就可以了。 |
2、 在dbeng6.exe后加上“-q”是為了隱藏任務(wù)欄上的SQLAnywhere窗口。 |
另外,必須將數(shù)據(jù)源加到注冊表中的數(shù)據(jù)源列表中,加入到如下鍵中 |
HKEY_CURRENT_USER\Software\ODBC\ODBC.INI\ODBC Data Sources\ |
該鍵中存放的是每一個數(shù)據(jù)源和ODBC驅(qū)動程序的關(guān)聯(lián)。鍵名是指數(shù)據(jù)源名字;鍵值是指ODBC驅(qū)動程序的名字。由于本文所敘述的是基于PowerBuilder7.0開發(fā)環(huán)境下的應(yīng)用程序的發(fā)布,所以鍵值都是"Adaptive Server Anywhere 6.0"(因為,當(dāng)我們安裝PowerBuilder 7.0時,系統(tǒng)默認(rèn)的ODBC驅(qū)動程序的名字為Adaptive Server Anywhere 6.0),當(dāng)然,如果你愿意話也可以改變它。 |
這里有必要提示一下,由于用戶數(shù)據(jù)源的配置可能包含一些敏感的數(shù)據(jù)庫設(shè)置,比如說連接數(shù)據(jù)庫的用戶ID和口令。這些設(shè)置都會以無格式的文本形式存儲在注冊表中,可以很容易的被Windows注冊表編輯程序regedit.exe或regedt32.exe查看,只要有點編程經(jīng)驗的人都能通過獲這些實用工具取該ID和口令來修改數(shù)據(jù)庫中數(shù)據(jù)。因此,你在處理這些問題的時候還要三思一下,你可以選擇加密口令或讓用戶在連接數(shù)據(jù)庫時錄入。 |
既然我們知道了如何在注冊表中為應(yīng)用程序進(jìn)行ODBC配置,那么剩下的問題是,把他們放在什么地方?不用說,大家也猜到了,最好的地方就是當(dāng)所有文件都已經(jīng)復(fù)制完畢,在我們要求用戶重新啟動系統(tǒng)之前。事件OnMoved可以做到這一切。在做這些事的同時我們最好能夠顯示給用戶一個消息框,告訴用戶我們在干什么,做完這一切之后再關(guān)閉它。這就要用到函數(shù)SdShowMsg和Delay。見圖。 |
該事件是在當(dāng)所有在目標(biāo)機(jī)器上的組件都被安裝或反安裝時響應(yīng),在該事件中的代碼總是會被執(zhí)行。 |
SdShowMsg:該函數(shù)打開或關(guān)閉一個非模態(tài)的小窗口,該窗口顯示指定的消息。 |
Delay:該函數(shù)可以用指定的時間(秒)來使安裝程序的執(zhí)行時間延遲。 |
/*********************程序代碼********************/ |
//*******當(dāng)所有數(shù)據(jù)拷貝完畢后在這里配置ODBC |
STRING szNumName, szNumValue, svNumValue, szTitle; |
NUMBER nType, nSize, nvType, nvSize; |
szMsg = "正在進(jìn)行系統(tǒng)配置,請等待..."; |
svDB = TARGETDIR + "\\db\\Demo.db"; |
svASAOdbcDll = TARGETDIR + "\\ASA\\dbodbc6.dll"; |
svASA = TARGETDIR + "\\ASA\\dbeng6.exe"; |
RegDBSetDefaultRoot (HKEY_LOCAL_MACHINE); |
szKey = "Software\\ODBC\\ODBCINST.INI\\MyDemo ASA"; |
if (RegDBCreateKeyEx(szKey, szClass) < 0) then |
MessageBox ("注冊表操作失敗,安裝程序?qū)⒔K止!", SEVERE); |
szNumValue = svASAOdbcDll; |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注冊表操作失敗,安裝程序?qū)⒔K止!", SEVERE); |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注冊表操作失敗,安裝程序?qū)⒔K止!", SEVERE); |
szKey = "Software\\ODBC\\ODBCINST.INI\\ODBC Drivers"; |
szNumName = "MyDemo ASA"; |
szNumValue = "Installed"; |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注冊表操作失敗,安裝程序?qū)⒔K止!", SEVERE); |
RegDBSetDefaultRoot (HKEY_CURRENT_USER); |
szKey = "Software\\ODBC\\ODBC.INI\\MyDemo"; |
if (RegDBCreateKeyEx(szKey, szClass) < 0) then |
MessageBox ("注冊表操作失敗,安裝程序?qū)⒔K止!", SEVERE); |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注冊表操作失敗,安裝程序?qū)⒔K止!", SEVERE); |
szNumName = "DatabaseFile"; |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注冊表操作失敗,安裝程序?qū)⒔K止!", SEVERE); |
szNumName = "Description"; |
szNumValue = "My Paper's Sample"; |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注冊表操作失敗,安裝程序?qū)⒔K止!", SEVERE); |
szNumValue = svASAOdbcDll; |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注冊表操作失敗,安裝程序?qū)⒔K止!", SEVERE); |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注冊表操作失敗,安裝程序?qū)⒔K止!", SEVERE); |
szNumValue = svASA + " -d -c8m"; |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注冊表操作失敗,安裝程序?qū)⒔K止!", SEVERE); |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注冊表操作失敗,安裝程序?qū)⒔K止!", SEVERE); |
//***************注冊數(shù)據(jù)源 |
szKey = "Software\\ODBC\\ODBC.INI\\ODBC Data Sources"; |
szNumValue = "MyDemo ASA"; |
if (RegDBSetKeyValueEx (szKey, szNumName, nType, szNumValue, |
MessageBox ("注冊表操作失敗,安裝程序?qū)⒔K止!", SEVERE); |
// UInstalled -- 反安裝后刪除新建的鍵 |
if ( FindFile(TARGETDIR + "\\db\\", "Demo.db", svResult) < 0 ) then |
RegDBSetDefaultRoot (HKEY_LOCAL_MACHINE); |
szKey = "Software\\ODBC\\ODBCINST.INI\\MyDemo ASA"; |
if (RegDBDeleteKey (szKey) < 0) then |
MessageBox ("刪除注冊表數(shù)據(jù)失敗!", SEVERE); |
szKey = "\\Software\\ODBC\\ODBCINST.INI\\ODBC Drivers"; |
if (RegDBDeleteValue (szKey, "MyDemo ASA") < 0) then |
MessageBox ("刪除注冊表數(shù)據(jù)失敗!", SEVERE); |
RegDBSetDefaultRoot (HKEY_CURRENT_USER); |
szKey = "Software\\ODBC\\ODBC.INI\\MyDemo"; |
if (RegDBDeleteKey (szKey) < 0) then |
MessageBox ("刪除注冊表數(shù)據(jù)失敗!", SEVERE); |
szKey = "Software\\ODBC\\ODBC.INI\\ODBC Data Sources"; |
if (RegDBDeleteValue (szKey, "MyDemo") < 0) then |
MessageBox ("刪除注冊表數(shù)據(jù)失敗!", SEVERE); |
SdShowMsg (szMsg, FALSE); |
/********************* 結(jié)束 ********************/ |
在進(jìn)行完成注冊表的添加之后還必須做的最后一件事就是為我們的應(yīng)用程序指定一個搜索路徑,因為我們在安裝的時候拷貝了一些DLL庫文件。顯兒易見這就要求我們在Autoexec.bat中文件添加一個搜索路徑。 |
* FindFile:在指定的路徑下查找指定的文件,當(dāng)函數(shù)返回0時表示文件找到,當(dāng)返回小于0的任何數(shù)時表示沒有找到。 |
* CreateFile:創(chuàng)建一個新的文件,如果該文件已經(jīng)存在,那么CreateFile將覆蓋掉原先的。同樣,在創(chuàng)建之前要先用OpenFileMode設(shè)置文件模式。 |
* OpenFileMode:在你想要打開一個已存在的文件或建立一個新文件設(shè)置文件的模式。文件的模式根據(jù)文件類型的不同會有如下幾種: |
* OpenFile:打開一個已經(jīng)存在的文本文件或二進(jìn)制文件。但打開之前必須先要用OpenFileMode設(shè)置文件打開的模式。 |
* WriteLine:該函數(shù)在一個以添加模式(append mode)打開或建立的文本文件中寫上一行文本。 |
* CloseFile:當(dāng)你完成用GetLine讀文件或用WriteLine寫文件的操作后必須用CloseFile函數(shù)將使用的文件關(guān)閉。 |
* LongPathToQuote:在長文件名上放置或去掉雙引號。因為如果是沒有加引號的長文件名PAHT命令是不會認(rèn)的。 |
/**********************程序代碼******************/ |
OpenFileMode(FILE_MODE_APPEND); |
if (FindFile("c:\\", "autoexec.bat", svResult) < 0) then //沒有,需要create一個 |
CreateFile(nvFileHandle, "c:\\","Autoexec.bat" ); |
OpenFile ( nvFileHandle , "C:\\" , "Autoexec.bat" ); |
svDir = TARGETDIR + "\\DLL"; // 將路徑指向DLL目錄 |
LongPathToQuote (svDir , TRUE ); |
WriteLine( nvFileHandle," "); |
WriteLine( nvFileHandle ,"Set PATH=" +svDir +";%PATH%"); |
WriteLine( nvFileHandle," "); |
/********************** 結(jié)束 ******************/ |
到目前為止,我們已經(jīng)完成了制作一個應(yīng)用程序的安裝實例所需要的全部工作。剩下的也只是一些錦上添花的事了。讓我們最后再看看還有什么要做的?對了,還應(yīng)該像其他專業(yè)軟件那樣在開始菜單中或者是在桌面上或者是在程序文件夾中添加快捷方式,這樣用戶在安裝完畢之后可以很方便的找到它。在這里我們將會用到函數(shù)AddFolderIcon。 |
相關(guān)函數(shù):AddFolderIcon |
該函數(shù)在指定的程序文件中插入或替換圖標(biāo)。 |
/******************程序代碼******************/ |
STRING szProgramFolder, szItemName, szCommandLine, szWorkingDir, szIconPath; |
STRING szShortCutKey, szProgram, szParam; |
//SzProgramFolder = FOLDER_STARTMENU; |
// Set up parameters for call to AddFolderIcon. |
szProgram = FOLDER_STARTMENU; |
szParam = TARGETDIR + "\\Demo.exe"; |
LongPathToQuote (szProgram, TRUE); |
LongPathToShortPath (szParam); |
szWorkingDir = TARGETDIR + "\\DLL"; // 加上應(yīng)用程序的工作路徑 |
szProgramFolder = FOLDER_STARTMENU; |
szItemName = "My Demo in StartMenu"; |
if (AddFolderIcon (szProgramFolder, szItemName, szCommandLine, szWorkingDir, |
szIconPath, nIcon, szShortCutKey, REPLACE) < 0) then |
MessageBox ("安裝程序在進(jìn)行開始菜單中添加快捷方式失敗,稍侯請自己添加!.", SEVERE); |
szProgramFolder = FOLDER_PROGRAMS; |
szItemName = "My Demo in Programs"; |
if (AddFolderIcon (szProgramFolder, szItemName, szCommandLine, szWorkingDir, |
szIconPath, nIcon, szShortCutKey, REPLACE) < 0) then |
MessageBox ("安裝程序在進(jìn)行程序文件中添加快捷方式失敗,稍侯請自己添加!.", SEVERE); |
szProgramFolder = FOLDER_DESKTOP; |
szItemName = "My Demo in Desktop"; |
if (AddFolderIcon (szProgramFolder, szItemName, szCommandLine, szWorkingDir, |
szIconPath, nIcon, szShortCutKey, REPLACE) < 0) then |
MessageBox ("安裝程序在進(jìn)行桌面上添加快捷方式失敗,稍侯請自己添加!.", SEVERE); |
/****************** 結(jié)束 ******************/ |
當(dāng)然了,我們還得在卸載程序中加上刪除這些快捷方式的代碼,否則的話當(dāng)用戶刪除了我們的應(yīng)用程序后會對我們的程序怒發(fā)沖冠的,我當(dāng)然不希望這樣。 |
/***************程序代碼***************/ |
if (DeleteFolderIcon (FOLDER_DESKTOP, "My Demo in Desktop") < 0) then |
MessageBox ("安裝程序在刪除桌面上的快捷方式的時候出錯,稍侯請自己刪除!.", SEVERE); |
if (DeleteFolderIcon (FOLDER_STARTMENU, "My Demo in StartMenu") < 0) then |
MessageBox ("安裝程序在刪除開始菜單中的快捷方式的時候出錯,稍侯請自己刪除!.", SEVERE); |
if (DeleteFolderIcon (FOLDER_PROGRAMS, "My Demo in Programs") < 0) then |
MessageBox ("安裝程序在刪除程序文件夾中的快捷方式的時候出錯,稍侯請自己刪除!.", SEVERE); |
/*************** 結(jié)束 ***************/ |
好了,我們可以拿著這個安裝程序向老板領(lǐng)賞了。還不趕快制作一個具有專業(yè)級水平的應(yīng)用程序安裝盤來分發(fā)給我們的客戶。相信他們一定會瞪大眼睛驚嘆我們的軟件開發(fā)水平是一流的。廢話不說了,現(xiàn)在就開始行動! |
至此,所有圖例的有關(guān)技術(shù)已經(jīng)全部講述完畢。總之,InstallShield博大精深,我所敘述的只是一些皮毛而已,如果大家對她想更深入的了解還是請看她自帶的幫助文檔。 |
不過,最后我還是要多說一句,千萬不要忘了編寫一個詳細(xì),完整的用戶手冊。這可是用戶最需要的,但同時也是我們最懶得去做的一件事。 |
相關(guān)文章
有道云筆記網(wǎng)頁剪報功能使用方法介紹
有道云筆記這款網(wǎng)易開發(fā)的筆記應(yīng)用相信選擇的人還是很多的,作為一款同步記錄編輯軟件,它還有一個很多人喜歡的功能---網(wǎng)頁剪報
2013-08-21 共享文件設(shè)置及密碼清除具體方法
Windows XP操作系統(tǒng)中內(nèi)置了“簡單文件共享”這一功能,這項功能默認(rèn)情況下是打開的,這一功能是專門為初級電腦用戶而設(shè)計的,接下來將為你詳細(xì)介紹下共享文件設(shè)置及密碼清
2013-03-06