Microsoft® Visual Basic 之 Variant 變數應用
鄭子璉 | http://feitsui.hyd.ncku.edu.tw/TLCheng/Basic/ | 現任:成大資工 BBS Basic 版版主 |
-、Variant 概述 |
在 Microsoft® Visual Basic (以下簡稱 VB) 中, Variant 為預設變數型態,基於執行速度的考量,在 VB 線上手冊及相關文件均不建議採用 Variant ,因為 Variant 會拖慢程式執行效能。唯 Variant 實為一多變特性之變數型態,透過 Variant 可解決特定目標之問題,筆者建議認為您應深入了解 Variant 特性,並善加利用 Variant 變數。在 VB6 線上手冊中指出, Variant 是一種特殊的資料型態,除了固定長度 String的資料及使用者自訂型態外,也可以包含任何種類的資料, Variant 亦可以包含 Empty、Error、Nothing 及 Null特殊值。您可以用 VarType 函數或 TypeName 函數來決定如何處理 Variant 中的資料。
二、Variant 內容 |
在 VB 中有一函數 VarType 可傳回 Variant 目前之型態,筆者用一簡單之函數來觀察,關於 CopyMemory 的宣告,在後續的程式碼將不再重複。
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (hpvDest As Any, hpvSource As Any, ByVal cbCopy As Long) Public Function VariantMemory(ByVal Expression) Dim tBytes(0 To 15) As Byte End Function |
如表 1 ,表中略去本文不討論之 VbDataObject 、 VbUserDefinedType 之兩種型態,表中以各範例展現各變數型態在記憶體中的內容。
表 1 不同型態變數與記憶體的關係
常數 | 值 | 範例 | 16 進位記憶體內容 | 說明 |
VbEmpty | 0 | Empty | 0000000000000000 0000000000000000 | Empty (未初始化) |
VbNull | 1 | Null | 0100000000000000 0000000000000000 | Null (無有效資料) |
VbInteger | 2 | 1% | 0200000000000000 0100000000000000 | 整數 |
VbLong | 3 | 1& | 0300000000000000 0100000000000000 | 長整數 |
VbSingle | 4 | 1! | 0400000000000000 0000803F00000000 | 單倍精確浮點數 |
VbDouble | 5 | 1# | 0500000000000000 000000000000F03F | 雙倍精確浮點數 |
VbCurrency | 6 | 1@ | 0600000000000000 1027000000000000 | 貨幣 |
VbDate | 7 | #2001/4/1# | 0700000000000000 00000000C00EE240 | 日期 |
VbString | 8 | "培基語言" | 0800000000000000 3CA7060C00000000 | 字串 |
VbObject | 9 | tFont As New StdFont | 0900000000000000 F8A40A0C00000000 | 物件 |
VbError | 10 | CVErr(1) | 0A00000000000000 01000A8000000000 | 錯誤 |
VbBoolean | 11 | True | 0B00000000000000 FFFF000000000000 | 布林值 |
VbVariant | 12 | Array(1, 2) | 0C20000000000000 3C75080C00000000 | Variant(只適用於 Variants 中的陣列) |
VbDecimal | 14 | CDec(1) | 0E00000000000000 0100000000000000 | 十進位值 |
VbByte | 17 | CByte(1) | 1100000000000000 0100000000000000 | 位元值 |
VbArray | 8192 | tBytes() | 0C20000000000000 0000000000000000 | 陣列 |
多數人都知道, Variant 變數為 16 位元組,是 VB 內建變數中記憶體使用量最大者,那麼在 Variant 中又是如何運用這 16 位元組呢? 如圖 1 ,由上表可推知變數型態以前兩位元組存放,變數內容依所需要之位元組數,分別存放於 Variant 變數內。 |
圖 1 Variant 變數位元使用狀態 |
三、取得 Variant 物件變數型態 |
在獲得如上的資訊後,首先先來檢討 VB6 中的函數 VarType ,讀者可測試以下的程式碼: | Dim tFont As New StdFont, tPic As New StdPicture Debug.Print VarType(tFont), VarType(tPic) |
在由 VB6 執行測試前,直覺上程式碼應傳回 9 (vbObject),但經實際測試後得到卻是 8 (vbString) 及 3 (vbLong),筆者推測 VarType 傳回的可能是該物件預設屬性之型態。
例如 StdFont 預設屬性為 Name As String ,StdPicture 預設屬性為 Handle As OLE_HANDLE (Long) ,但對程式設計人員來說,通常需要得到的是 vbObject ,所以筆者設計了一個小函數: | Public Function myVarType(ByVal Expression As Variant) As VbVarType CopyMemory myVarType, Expression, 2 End Function |
這個函數利用圖 1 中第 0 與 1 個位元組代表變數型態,在直接使用上與 VarType 函數相同,並可支援傳回 vbObject 代碼。
四、 Variant 變數型態各位元組的位置 |
其次欲了解 Variant 變數其它各位元組的意義,大部分 Variant 變數均由第 8 個位元值開始,變數型態與所佔之位元組整理,請參考表 2 。
表 2 不同型態變數在 Variant 內所佔之位元組
所需記憶體位元組 | 位置 | 變數型態 |
1 | 8 | 位元值 |
2 | 8 ~ 9 | 整數、錯誤、布林值 |
4 | 8 ~ B | 長整數、單倍精確浮點數、字串指標、物件指標、 Variant 指標、陣列指標 |
8 | 8 ~ F | 雙倍精確浮點數、貨幣、日期 |
14 | 3 ~ F | 十進位值 |
五、 Variant 位元值排列方式 |
在 Win32 (95/98/Me/NT/2k) 下,變數在記憶體中的位置是採用低位元排列 (little endian byte order) ,即位元組由左至右代表低位元值至高位元值,對於 Unix 系統高位元排列來說是相反的,甚至部分軟體在二進位檔內會高位元、低位元排列混用。 例如:某著名地理資訊系統軟體之檔案在前 4 位元組以高位元排列之長整數 9,994 為檢查碼,若要讓 VB 存取必須自行撰寫轉換函數(如右列): 由於位元組排列方向剛好相反,故上述函數可做正逆雙向轉換,目前僅支援整數、長整數、單倍精確浮點數與雙倍精確浮點數,由於筆者目前碰到需要轉換的變數只有這四種,尚無法對其它變數測試。 | Public Function EndianBigToLittle(ByVal hEndian As Variant) Len = 16 Offset = 8 Select Case VarType(hEndian) Case Is = vbInteger nBytes = 2 Case Is = vbLong, vbSingle nBytes = 4 Case Is = vbDouble nBytes = 8 Case Else nBytes = 0 End Select Select Case nBytes Case Is > 1 ReDim tBytes(1 To nLen) As Byte CopyMemory tBytes(1), hEndian, nLen For i = 1 To nBytes \ 2 MySwap tBytes(Offset + i), tBytes(Offset + nBytes - i + 1) Next CopyMemory hEndian, tBytes(1), nLen End Select EndianBigToLittle = hEndian End Function |
六、 Variant 變數與 16 進位值轉換 |
對於經常沉醉在 RPG 的玩家或是面對不同二進位檔的程式設計人員,比較關心的是各變數在的 16 進位值, VB 內建的 Hex 函數會先將引數轉為整數或長整數才求得 16 進位值,但這不符合大部分的需求,您可利用本文一開始提及之 VariantMemory 函數將各位元組轉換為 16 進位值,但一般閱讀習慣上仍喜好以高位元排列為主,以下程式,描述將陣列字串納入考量之函數及反轉換函數:
Public Function VariantToHex(ByVal Expression) tBytes = VariantToBytes(Expression) VariantToHex = ByteToHex(tBytes) End Function Public Function ByteToHex(ByVal hBytes) As String If VarType(hBytes) > vbArray Then lb = LBound(hBytes) ub = UBound(hBytes) For i = lb To ub tHex = tHex & ByteToHex(hBytes(i)) Next I Else tHex = Hex(hBytes) If Len(tHex) = 1 Then tHex = "0" & tHex End If End If ByteToHex = tHex End Function Public Function HexToByte(ByVal HexString As String) As Variant If (Len(HexString) Mod 2) > 0 Then HexString = "0" & HexString End If nByte = Len(HexString) / 2 ReDim tByte(1 To nByte) As Byte For i = 1 To nByte tByte(i) = Val("&H" + Mid(HexString, (i - 1) * 2 + 1, 2)) Next i HexToByte = tByte End Function | Public Function HexToVariant(ByVal HexString As String, Optional ByVal wFlags As VbVarType = vbLong) As Variant nLen = 16 Offset = 8 Select Case wFlags Case vbByte nByte = 1 Case vbInteger, vbBoolean, vbError nByte = 2 Case vbLong, vbSingle nByte = 4 Case vbDouble, vbDate, vbCurrency nByte = 8 Case vbString nByte = Len(HexString) \ 2 Case vbDecimal nByte = 14 Offset = 2 Case Else nByte = 0 End Select If nByte = 0 Then HexToVariant = HexString Else tByte = HexToByte(HexString) If wFlags = vbString Then HexToVariant = CStr(tByte) Else ReDim vByte(1 To nLen) As Byte lb = LBound(tByte) ub = UBound(tByte) nb = ub - lb + 1 If nb < sloc =" nByte" sloc =" 1" i =" sLoc" wflags =" vbError"> |
VariantToHex 函數目前可支援整數、長整數、單倍精確浮點數、雙倍精確浮點數、貨幣、日期、字串、物件、錯誤、布林、十進位值、位元、陣列,若為陣列變數,則利用 ByteToHex 函數遞迴呼叫,可將 16 進位值依序傳回,若為物件變數,則僅傳回物件指標。其中比較特別的是若為字串變數,則利用 VB 內建功能將字串轉換為位元值陣列。
但字串傳回的是 UniCode 的 16 進位值,若需傳回存在檔案中 ASCII 的 16 進位值,則改為: | Debug.Print VariantToHex(StrConv("培基語言",vbFromUnicode)) ' 傳回 B0F6B0F2BB79A8A5 |
HexToVariant 函數目前可支援整數、長整數、單倍精確浮點數、雙倍精確浮點數、貨幣、日期、字串、錯誤、布林、十進位值、位元。
其中若為字串變數,則利用 VB 內建功能將 16 進位值將位元值陣列轉換為字串,但字串引數使用的是 UniCode 的 16 進位值,若需使用存在檔案中 ASCII 的 16 進位值,則改為: | StrConv(HexToVariant("B0F6B0F2BB79A8A5",vbString),vbUnicode) ' 傳回 "培基語言" |
欲轉換為 2 進位值或其它進制,筆者建議您可藉位元值陣列進行各項變換,在此不再贅述,您可參考筆者所建置網頁上的範例。
七、 Variant 變數與記憶體指標 |
在 VB 中有 3 個隱藏保留函數可取得變數在記憶體中的位置,由前面敘述可知,物件及字串在 Variant 變數存放為記憶體指標,然 VB.Net 將不再支援這 3 個保留函數,因此筆者分別以右列兩個自訂函數取得記憶體指標: myObjPtr 為模擬 VB 內建之 ObjPtr 函數,為取得物件記憶體指標, myStrPtr 為模擬 VB 內建之 StrPtr 函數,為取得字串記憶體指標,目前僅支援不定長度字串及 Variant 字串,關於字串在記憶體轉移間的變化及不支援定長度字串的原因,筆者擬另闢專文向您報告,不在本文討論。 | Public Function myObjPtr(ByVal hObject As Variant) As Long Select Case myVarType(hObject) Case Is = vbObject ReDim tBytes(1 To 16) As Byte Dim tLong As Long CopyMemory tBytes(1), hObject, 16 CopyMemory tLong, tBytes(9), 4 myObjPtr = tLong End Select End Function Public Function myStrPtr(lpString As Variant) As Long ReDim tBytes(1 To 16) As Byte Dim tLong As Long CopyMemory tBytes(1), lpString, 16 CopyMemory tLong, tBytes(9), 4 If tBytes(2) > 0 Then CopyMemory myStrPtr, ByVal tLong, 4 Else myStrPtr = tLong End If End Function |
八、支援超長整數 |
在 Win32 中另有一變數型態為超長整數 (Long Long) ,在 VB 中並不直接支援,但可利用十進位值 (Decimal) 配合運用。十進位值在Variant 變數中,第 2 ~ 3 位元值為正負符號及浮點位置,第 4 ~ 7 位元值為超高位元組,第 8 ~ F 恰可當超長整數使用,故十進位值總共使用 14 個位元組,一般讀者多有看到記載為 12 個位元組,筆者以為疑是漏列正負符號及浮點位置的部分。
因此若配合超長整數型態時,應避免使用第 2 ~ 7 位元值,文中為避免贅述,僅介紹讀取及設定兩種應用:
Private Declare Function GetDiskFreeSpaceEx Lib "kernel32" Alias _ "GetDiskFreeSpaceExA" (ByVal lpRootPathName As String, _ lpFreeBytesAvailableToCaller As typLong64, _ lpTotalNumberOfBytes As typLong64, _ lpTotalNumberOfFreeBytes As typLong64) As Long Private Declare Function SetEndOfFile Lib "kernel32" _ (ByVal hFile As Long) As Long Private Type typLong64 Low As Long High As Long End Type Public Enum enuDiskFreeSpace dfs_FreeBytes = 0 dfs_TotalBytes = 1 dfs_TotalFreeBytes = 2 End Enum Private Function CLong64(hLong64 As typLong64) ReDim tBytes(1 To 16) As Byte Dim tVar As Variant CopyMemory tBytes(9), hLong64, Len(hLong64) tBytes(1) = vbDecimal CopyMemory tVar, tBytes(1), 16 CLong64 = tVar End Function Public Function myGetDiskFreeSpace(ByVal lpRootPathName As String, _ Optional ByVal dwFlags As enuDiskFreeSpace = dfs_FreeBytes) Dim lpFreeBytesAvailableToCaller As typLong64 Dim lpTotalNumberOfBytes As typLong64 Dim lpTotalNumberOfFreeBytes As typLong64 summy = GetDiskFreeSpaceEx(lpRootPathName, lpFreeBytesAvailableToCaller,_ lpTotalNumberOfBytes, lpTotalNumberOfFreeBytes) Select Case dwFlags Case Is = dfs_FreeBytes tdfs = CLong64(lpFreeBytesAvailableToCaller) Case Is = dfs_TotalBytes tdfs = CLong64(lpTotalNumberOfBytes) Case Is = dfs_TotalFreeBytes tdfs = CLong64(lpTotalNumberOfFreeBytes) End Select myGetDiskFreeSpace = tdfs End Function | Private Function CTypeLong64(ByVal hDecimal As Variant) As typLong64 ReDim tBytes(1 To 16) As Byte Select Case VarType(hDecimal) Case vbDecimal hDecimal = CDec(CStr(hDecimal)) Case Else hDecimal = CDec(CStr(Int(hDecimal))) End Select CopyMemory tBytes(1), hDecimal, 16 CopyMemory CTypeLong64, tBytes(9), 8 End Function Public Function mySetFilePointer (Byval hFile As Long, ByVal FilePos) Dim tFileLen As typLong64 FilePos = FilePos - 1 tFileLen = CTypeLong64(FilePos) tFileLen.Low = SetFilePointer(hFile, tFileLen.Low, tFileLen.High, FILE_BEGIN) mySetFilePointer = CLong64(tFileLen) + 1 End Function |
函數 myGetDiskFreeSpace 可取得輸入引數磁碟機總空間,由於 VB 內建長整數僅能支援到 2 GB ,而目前市面多可購買到超過 2 GB 之硬碟。
因此以CLong64 函數將超長整數轉換為十進位值變數,如此可在 VB 中支援到 18,446,744,073,709,551,615 Byte (18 EB 或 18,446,744,073 GB) ,可解決 VB 無法顯示或展示超長整數之變數型態。
函數 mySetFilePointer 為設定讀取或寫入檔案動作位置指標,若存取檔案遇上超過 2 GB 以上,原 VB 內建函數或陳述式將不敷使用,透過 Windows API 存取時,需注意到 Windows API 檔案指標是以 0 為基底, VB 是以 1 為基底,所以處理前後均應經過轉換,轉換為十進位值前,為避免十進位值第 3 ~ 7 位元值不為 0 ,故程式碼中透過字串來轉換。
九、 Variant 陣列 |
前面敘述到 Variant 陣列變數在第 8 ~ B 位元值為陣列記憶體指標,因此 Variant 陣列適當靈活運用下,可以達到多重多維度陣列、陣列中可再指定陣列、次維度陣列可不等長等特性,很多在 C 語言中才有的特性及陣列運用, VB 可利用 Variant 陣列來完成,若有機會,筆者將另文探討,本文仍著重於 Variant 的討論。
Variant 的陣列記憶體指標實際上並不是指向實體陣列(陣列第一個元素的位置),實際上是指向陣列描述器的位置。 VB 的 Variant 陣列型態詳細定義於 oleaut32.dll 中,其型態為 SafeArray ,藉由 SafeArray 的描述,可以得到 Variant 陣列的維度、單一元素的記憶體大小、實體陣列記憶體指標、各維度宣告個數及下標,因此可以補充及增進 VB 內建函數所沒有之功能:
Private Declare Function SafeArrayGetDim Lib "oleaut32" _ (ByVal lpSafeArray As Long) As Long Private Declare Function SafeArrayRedim Lib "oleaut32" _ (ByVal lpSafeArray As Long, lpSafeArrayBound As typSafeArrayBound) As Long Public Type typArrayVariant uVarType As Integer unUsed1 As Integer unUsed2 As Long Pointer As Long unUsed3 As Long End Type Public Type typSafeArrayBound cElements As Long LowerBound As Long End Type Public Type typSafeArray nDimension As Integer fFeatures As Integer cbElements As Long cLocks As Long Pointer As Long End Type Private Enum enuSafeArrayMessage S_OK = &H0 End Enum Private Const Lenth_Variant = 16 Private Const Offset_Variant = 8 Public Function GetArrayStructPtr(hVariant As Variant) As Long Dim ArrayStructPtr As typArrayVariant CopyMemory ArrayStructPtr, hVariant, Len(ArrayStructPtr) GetArrayStructPtr = ArrayStructPtr.Pointer End Function Public Function mySafeArrayGetDim(ByVal hVariant As Variant) As Long mySafeArrayGetDim = SafeArrayGetDim(GetArrayStructPtr(hVariant)) End Function Public Function IsArrayInit(ByVal SourceArray As Variant) As Boolean IsArrayInit = False Select Case VarType(SourceArray) Case vbVariant, Is >= vbArray nDim = mySafeArrayGetDim(SourceArray) If nDim > 0 Then IsArrayInit = True End If End Select End Function | Public Function mySafeArrayRedim(ByVal hVariant As Variant, _ Optional ByVal ArrayElements, Optional ByVal ArrayLowerBound) Dim lpArrayBound As typSafeArrayBound nDim = mySafeArrayGetDim(hVariant) If IsMissing(ArrayLowerBound) Then ArrayLowerBound = LBound(hVariant, nDim) End If If IsMissing(ArrayElements) Then ArrayElements = UBound(hVariant, nDim) - LBound(hVariant, nDim) + 1 End If lpArrayBound.cElements = ArrayElements lpArrayBound.LowerBound = ArrayLowerBound summy = SafeArrayRedim(GetArrayStructPtr(hVariant), lpArrayBound) If summy = S_OK Then mySafeArrayRedim = hVariant End If End Function Public Function TransformArrayToOneDimension _ (ByVal SourceArray As Variant) As Variant Dim TargetArray As Variant Dim tPointer As Long Dim tSA As typSafeArray TargetArray = SourceArray tPointer = GetArrayStructPtr(TargetArray) CopyMemory tSA, ByVal tPointer, Len(tSA) With tSA ReDim BoundItem(1 To .nDimension) As typSafeArrayBound tPointer = tPointer + Len(tSA) CopyMemory BoundItem(1), ByVal tPointer, Len(BoundItem(1)) * .nDimension nCount = BoundItem(1).cElements For i = 2 To .nDimension nCount = nCount * BoundItem(i).cElements Next i BoundItem(1).cElements = nCount BoundItem(1).LowerBound = 1 CopyMemory ByVal tPointer, BoundItem(1), Len(BoundItem(1)) .nDimension = 1 End With CopyMemory ByVal tPointer - Len(tSA), tSA, Len(tSA) TransformArrayToOneDimension = TargetArray End Function |
函數 GetArrayStructPtr 為取得 Variant 陣列描述器記憶體指標,函數mySafeArrayGetDim 為取得Variant 陣列宣告的維度,目前在VB 中並沒有任一函數可取得陣列宣告之維度,透過本函數可增進 VB 的功能,原則上亦可透過 SafeArray 之型態取得陣列維度,筆者在這僅介紹透過 Windows API 直接取得的方式,您若欲了解透過 SafeArray 之型態取得陣列維度,可至筆者架設之網頁瀏覽。
函數 IsArrayInit 為判斷陣列是否初始化,陣列在宣告大小前及刪除後,其陣列維度為 0 ,利用此特性可判斷陣列是否已宣告大小,如此可避免程式中在未宣告陣列大小時,使用陣列中元素所產生的錯誤。在 VB 中若要不影響現有資料而調整陣列之個數,可用:
ReDim Preserve varname(lb to ub) |
但運用此陳述式變更陣列個數僅能更改上標,且 Variant 變數陣列不能變更,例如 Array 傳回值即為 Variant 變數陣列,利用函數 mySafeArrayRedim 可同時或部分變更上標及下標,如下例:
項目 | 程式碼 | 下標 | 上標 |
原始宣告 | ReDim vArray(0 To 4) 或 vArray = Array(1, 2, 3, 4, 5) | 0 | 4 |
變更上標 | vArray = mySafeArrayRedim(vArray, 6) | 0 | 5 |
變更下標 | vArray = mySafeArrayRedim(vArray, , 1) | 1 | 5 |
其它變更 | vArray = mySafeArrayRedim(vArray, 6, 1) | 1 | 6 |
函數 TransformArrayToOneDimension 可將任意維度陣列轉換為一維,本函數主要僅修改陣列描述器,可以最短的執行速度將多維陣列變更為一維陣列,算是較高效率的方法,但沒把握時請您勿任意修改程式碼,避免記憶體錯誤導致當機,如下例:
ReDim tArray(1 To 3, 2 To 4, 11 To 13) tArray = TransformArrayToOneDimension(tArray) ' 傳回一維陣列 |
若需從多維陣列中找尋最大值或最小值,轉換為一維陣列後再處理亦是不錯的方法。
十、總結 |
本文完整簡單介紹 VB 內建的 Variant 型態變數,在 VB 、 VBA 及 VBScript 預設的變數型態均為 Variant 變數,在掌握 Variant 變數特性後,您可依個人需求開發出更多的應用,若您目前尚不能理解本文內容,可於筆者經常參與之公開討論區發表討論,但本文並不是使用 VB 系列的必備知識,您不必勉強一定要了解,對於所需參照之程式碼仍可直接引用而不受影響,您會隨著使用 VB 的經驗,慢慢體會 Variant 變數型態的強大,並逐漸了解本文的意義,筆者期望能藉本文之介紹,引出更多 Variant 變數之應用,筆者所架設網站尚有部分 Variant 變數相關應用,讀者若有興趣,可自行上網瀏覽。
備註 |
- 筆者採用 Microsoft Visual Basic 6 SP4 中文專業版,及 Windows 98 SE 為作業系統。
- 筆者自行架設與 VB 相關網頁為「培基語言」網頁,網址為 http://feitsui.hyd.ncku.edu.tw/TLCheng/Basic/ ,筆者現為成大資工 BBS basic 版版主,網址為 telnet://140.116.247.7/ 。
- 筆者經常於「VBQA - 討論中心」網頁參與VB 相關問題討論,若有需要討論,建議請於該網站發表討論,網址為 http://www.vbqa.com/ 。
- 本文所介紹之內容可於 VBA 上應用,唯 Office 97 系列之 VBA 目前不支援列舉 (Enum) 及選擇性參數 (Optional) ,故於 VBA 上應用需將列舉改為常數,選擇性參數改為固定參數方可使用。
- 本文主要參考資料為 VB5 線上手冊、 MSDN for Visual Studio (VB6 線上手冊) 及 MSDN Subscriptions Library April 2001 。
瀏覽原稿及引用 |
本篇經「微軟之友」編輯刪修,原稿可參閱 old_Variant.htm ,我是覺得「微軟之友」選色很漂亮,所以原稿亦沿用相關色系,祇是文字多了一些些而已。不過寫科學論文習慣了,通常均會避免採用你、我、他,說實在的,被編輯將「讀者」改成「您」,我自己是覺得有點肉麻。
本文原文刊載在「微軟之友 季訊」,若要引用,請註明原文出處,不要以網址為參考文獻出處,網頁僅供不易閱讀到「微軟之友 季訊」網友所設置,畢竟拿了稿費,所以要特別強調資料來源及原始出處,才不會感到愧疚。參考文獻列示建議如下所列:
鄭子璉,「Microsoft® Visual Basic 之 Variant 變數應用」,微軟之友季訊,夏季 6 月號,第 42 ~ 49 頁,民國 90 年 6 月。
No comments:
Post a Comment