Wednesday, July 12, 2006

Microsoft Visual Basic 之 Variant 變數應用

Microsoft® Visual BasicVariant 變數應用


鄭子璉 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
CopyMemory tBytes(0), Expression, 16
VariantMemory = tBytes

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 變數相關應用,讀者若有興趣,可自行上網瀏覽。



備註
  1. 筆者採用 Microsoft Visual Basic 6 SP4 中文專業版,及 Windows 98 SE 為作業系統。
  2. 筆者自行架設與 VB 相關網頁為「培基語言」網頁,網址為 http://feitsui.hyd.ncku.edu.tw/TLCheng/Basic/ ,筆者現為成大資工 BBS basic 版版主,網址為 telnet://140.116.247.7/
  3. 筆者經常於「VBQA - 討論中心」網頁參與VB 相關問題討論,若有需要討論,建議請於該網站發表討論,網址為 http://www.vbqa.com/
  4. 本文所介紹之內容可於 VBA 上應用,唯 Office 97 系列之 VBA 目前不支援列舉 (Enum) 及選擇性參數 (Optional) ,故於 VBA 上應用需將列舉改為常數,選擇性參數改為固定參數方可使用。
  5. 本文主要參考資料為 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