CreateObject(“ADOX.Column”)

ADOX.Column对象用于访问数据库的字段
可以从Columns对象集合中提取或枚举Column对象
ADOX.Column对象有10个属性0个方法0个事件

(一)属性:
Attributes 与ADOX数据类型相关的字段特征。类型:Long
DefinedSize 字段宽度。类型:Long
Name 字段名称。类型:String
NumericScale adNumeric或adDecimal数据类型的小数位数。类型:Byte
ParentCatalog 所属的Catalog对象。类型:Catalog对象
Precision 数值型字段的有效数字的位数。类型:Byte
Properties 字段属性(只读)。类型:Properties对象
RelatedColumn 关联字段,仅用于Key.Columns中的Column对象。类型:String
SortOrder 排序方式,仅用于Index.Columns中的Column对象。类型:Long
Type 字段的数据类型。类型:Long

(二)属性说明:
以下均以Access数据库为例:字段宽度是指数据在数据库中占用的存储空间的大小
比如adInteger类型总是占用4字节的存储空间,所以该类型的DefinedSize属性设置无效

DefinedSize在Type为adBinary、adVarBinary、adWChar、adVarWChar时设置有效
字符串类型的数据,DefinedSize的单位是字符
其它类型的数据,DefinedSize的单位是字节
Precision和NumericScale在Type为adNumeric时设置有效

有效数字的位数不包括正负号、小数点和科学计数法的符号
比如32767是5位有效数字,比如-3.402823E+38是7位有效数字

Precision和NumericScale的初始值均为0,在Type为adNumeric时必须设置Precision值
新建的Column对象,其Properties为空集合
指定了ParentCatalog,或把该Column添加到Columns对象集合后,方可访问Properties字段属性

自动增量类型的字段,要先设置字段属性中的Autoincrement,再添加到Table.Columns
所以必须先指定ParentCatalog以访问字段属性
已在Columns对象集合中的Column对象,其Name属性可读可写,其它属性均为只读

(三)示例:
‘示例:Column对象,Access数据库
Option Explicit
Dim oFSO, oCatalog, oConnection, oColumn, oProperty, s

Const FILE = “D:\1\1.mdb” ‘数据库文件,要求目标文件夹已存在

‘DataTypeEnum
Const adArray = &H2000 ‘数组标志。ADOX中不适用
Const adBigInt = 20 ‘八字节有符号整数
Const adBinary = 128 ‘定长二进制数据 Access
Const adBoolean = 11 ‘布尔值 Access
Const adBSTR = 8 ‘BSTR字符串
Const adChapter = 136
Const adChar = 129 ‘ANSI定长字符串
Const adCurrency = 6 ‘货币类型 Access
Const adDate = 7 ‘日期时间类型 Access
Const adDBDate = 133
Const adDBTime = 134
Const adDBTimeStamp = 135
Const adDecimal = 14
Const adDouble = 5 ‘八字节浮点数 Access
Const adEmpty = 0
Const adError = 10
Const adFileTime = 64
Const adGUID = 72 ‘GUID全球唯一标识符 Access
Const adIDispatch = 9
Const adInteger = 3 ‘四字节有符号整数 Access
Const adIUnknown = 13
Const adLongVarBinary = 205 ‘长二进制数据 Access
Const adLongVarChar = 201 ‘ANSI长字符串
Const adLongVarWChar = 203 ‘Unicode长字符串 Access
Const adNumeric = 131 ‘十进制数 Access
Const adPropVariant = 138
Const adSingle = 4 ‘四字节浮点数 Access
Const adSmallInt = 2 ‘双字节有符号整数 Access
Const adTinyInt = 16 ‘单字节有符号整数
Const adUnsignedBigInt = 21 ‘八字节无符号整数
Const adUnsignedInt = 19 ‘四字节无符号整数
Const adUnsignedSmallInt = 18 ‘双字节无符号整数
Const adUnsignedTinyInt = 17 ‘单字节无符号整数 Access
Const adUserDefined = 132
Const adVarBinary = 204 ‘变长二进制数据 Access
Const adVarChar = 200 ‘ANSI变长字符串
Const adVariant = 12 ‘变体类型
Const adVarNumeric = 139
Const adVarWChar = 202 ‘Unicode变长字符串 Access
Const adWChar = 130 ‘Unicode定长字符串 Access

Call RunAs32() ‘使vbs运行在32位应用程序模式
s = “20240611” ‘数据库密码

‘连接字符串
s = “Provider=Microsoft.Jet.OLEDB.4.0;Data Source=” & FILE & “;Jet OLEDB:Database Password=” & s

‘删除数据库文件并新建,防止已有数据库文件导致示例出错
Set oFSO = CreateObject(“Scripting.FileSystemObject”)
If oFSO.FileExists(FILE) Then oFSO.DeleteFile FILE, True
CreateObject(“ADOX.Catalog”).Create s

Set oConnection = CreateObject(“ADODB.Connection”)
oConnection.Open s
oConnection.Execute “CREATE TABLE 表1”
Set oCatalog = CreateObject(“ADOX.Catalog”)
Set oCatalog.ActiveConnection = oConnection

Set oColumn = CreateObject(“ADOX.Column”)
oColumn.Name = “字段1”
oColumn.Type = adBinary
oColumn.DefinedSize = 50
oCatalog.Tables.Item(“表1”).Columns.Append oColumn

Set oColumn = CreateObject(“ADOX.Column”)
oColumn.ParentCatalog = oCatalog ‘设置ParentCatalog后才能访问Column.Properties
oColumn.Name = “字段2”
oColumn.Type = adNumeric
oColumn.Precision = 19
oColumn.NumericScale = 2
oColumn.Properties.Item(“Default”).Value = 0
oCatalog.Tables.Item(“表1”).Columns.Append oColumn

oCatalog.Tables.Item(“表1”).Columns.Refresh() ‘刷新

s = “”
For Each oColumn In oCatalog.Tables.Item(“表1”).Columns
s = “Name ” & vbTab & oColumn.Name & vbLf & _
“Attributes ” & vbTab & oColumn.Attributes & vbLf & _
“DefinedSize ” & vbTab & oColumn.DefinedSize & vbLf & _
“NumericScale” & vbTab & oColumn.NumericScale & vbLf & _
“Precision ” & vbTab & oColumn.Precision & vbLf & _
“Type ” & vbTab & DataTypeEnum(oColumn.Type) & vbLf & vbLf
For Each oProperty In oColumn.Properties
s = s & oProperty.Name & vbTab & ” = ” & oProperty.Value & _
” (” & DataTypeEnum(oProperty.Type) & “)” & vbLf
Next
MsgBox s
Next

oConnection.Close()
MsgBox “ok”, vbSystemModal

Sub RunAs32()
Dim oWshShell, oFSO, s, i
Set oWshShell = CreateObject(“WScript.Shell”)
Set oFSO = CreateObject(“Scripting.FileSystemObject”)
s = oWshShell.ExpandEnvironmentStrings(“%windir%\SysWOW64\WScript.exe”)
If oFSO.FileExists(s) And LCase(WScript.FullName) <> LCase(s) Then
s = s & ” “”” & WScript.ScriptFullName & “”” “
For Each i In WScript.Arguments
If InStr(i, ” “) > 0 Then i = “””” & i & “”””
s = s & i & ” “
Next
oWshShell.Run Left(s, Len(s) – 1)
WScript.Quit()
End If
End Sub

Function DataTypeEnum(ByVal DataType)
Dim arr1, arr2, i
arr1 = Array(20, _
128, _
11, _
8, _
136, _
129, _
6, _
7, _
133, _
134, _
135, _
14, _
5, _
0, _
10, _
64, _
72, _
9, _
3, _
13, _
205, _
201, _
203, _
131, _
138, _
4, _
2, _
16, _
21, _
19, _
18, _
17, _
132, _
204, _
200, _
12, _
139, _
202, _
130)
arr2 = Array(“adBigInt”, _
“adBinary”, _
“adBoolean”, _
“adBSTR”, _
“adChapter”, _
“adChar”, _
“adCurrency”, _
“adDate”, _
“adDBDate”, _
“adDBTime”, _
“adDBTimeStamp”, _
“adDecimal”, _
“adDouble”, _
“adEmpty”, _
“adError”, _
“adFileTime”, _
“adGUID”, _
“adIDispatch”, _
“adInteger”, _
“adIUnknown”, _
“adLongVarBinary”, _
“adLongVarChar”, _
“adLongVarWChar”, _
“adNumeric”, _
“adPropVariant”, _
“adSingle”, _
“adSmallInt”, _
“adTinyInt”, _
“adUnsignedBigInt”, _
“adUnsignedInt”, _
“adUnsignedSmallInt”, _
“adUnsignedTinyInt”, _
“adUserDefined”, _
“adVarBinary”, _
“adVarChar”, _
“adVariant”, _
“adVarNumeric”, _
“adVarWChar”, _
“adWChar”)
For i = 0 To UBound(arr1)
If DataType = arr1(i) Then DataTypeEnum = arr2(i) : Exit Function
Next
End Function

(四)示例说明:
示例中创建的2个字段,相当于JQL语句:
CREATE TABLE 表1(字段1 BINARY(50) NOT NULL, 字段2 DECIMAL(19, 2) DEFAULT 0)

ANSI字符串,一个英文字母是1字节,一个汉字是2字节
Unicode字符串,一个英文字母或一个汉字都是2字节

BSTR字符串,是在一个Unicode字符串的前后加上标志
字符串前加4字节的字符串长度,字符串后加2字节的字符串结尾标志Chr(0)字符

单字节无符号整数,取值范围是     0  ~  2^8  - 1
单字节有符号整数,取值范围是  -2^7  ~  2^7  - 1
双字节无符号整数,取值范围是     0  ~  2^16 - 1
双字节有符号整数,取值范围是 -2^15  ~  2^15 - 1
四字节无符号整数,取值范围是     0  ~  2^32 - 1
四字节有符号整数,取值范围是 -2^31  ~  2^31 - 1
八字节无符号整数,取值范围是     0  ~  2^64 - 1
八字节有符号整数,取值范围是 -2^63  ~  2^63 - 1

adVariant变体类型,表示该数据类型不确定,可能是数字,可能是字符串,或者其它类型
比如 Column.Properties.Item("Default").Value 就是一个adVariant变体类型

(五)增加扩展说明之VBS字符串的内部实现:
VBS 是基于微软的 ActiveX/COM 技术实现的,而 COM 对象为了做到支持任何语言,定义了一系列通用的数据类型,微软称之为自动化对象类型(Automation data types),其中之一就是 BSTR。VBS 在内部是以 BSTR 来表示字符串的,BSTR 在 WTypes.h 中定义:
typedef wchar_t WCHAR;
typedef WCHAR OLECHAR;
typedef OLECHAR *BSTR;

从定义可以看出,BSTR 是指向 wchar_t 类型(也就是 C 语言中的 Unicode)的指针,但是 BSTR 并不是普通的 wchar_t 指针。标准 BSTR 指向一个有长度前缀和 NUL 结束符的 wchar_t 数组。BSTR 的前4字节是一个表示字符串长度的前缀。BSTR 长度域的值是字符串的字节数,并且不包括 NUL 结束符。

(1)理论说的有点抽象,下面用代码来说明:
str = “Hello” & Chr(0) & “world”
这是一句很简单的 VBS 代码,但是 VBScript 解释器在内部做了什么呢?其实就是初始化了一个 BSTR 变量(不考虑字符串连接过程):

BSTR str = SysAllocStringLen(L”Hello\0world”, 11);
为了更清楚地了解 BSTR 的结构,我们换一种写法:

/* BSTR 包含长度前缀,但是却实际指向第一个字符 */
wchar_t arr[] = {22,0,’H’,’e’,’l’,’l’,’\0′,’w’,’o’,’r’,’l’,’d’,’\0′};
BSTR str = &arr[2];
这个 BSTR 在内存中的结构为:
00000000 16 00 00 00 48 00 65 00 6C 00 6C 00 6F 00 00 00
00000010 77 00 6F 00 72 00 6C 00 64 00 00 00
橙色表示四个字节的长度前缀。红色高亮表示 BSTR 指针的当前指向,蓝色高亮表示字符串中的 Chr(0) 字符,绿色高亮表示 BSTR 的结束字符 NUL(该字符是 SysAllocStringLen 函数加上去的,因为是 Unicode,所以要占两个字节)。也就是说,如果不考虑前面四个字节,BSTR 就是 C 语言中的 null-terminated string。

(2)再看一段 VBS 代码:
MsgBox Len(str)
用 MsgBox 来显示刚才定义的字符串长度,VBScript 解释器内部又做了什么呢?是不是像 C 语言标准库函数 strlen 一样,遍历整个字符串,以 NUL 作为字符串结束的标识呢?

/* C语言 strlen 函数的简单实现 */
size_t strlen (const char * str)
{
const char *eos = str;
while( *eos++ ) ;
return( (int)(eos – str – 1) );
}
答案显然是否定的,因为字符串中含有 Chr(0),如果像 strlen 这样实现,那么就会被 Chr(0) 截断,Len 函数应该返回5才对,然而实际上返回的是11这个正确的数字。

VBS 的 Len 函数内部应该是这么实现的:
size_t Len(const BSTR str)
{
return SysStringLen(str);
}
或者不调用 Windows API,由于 BSTR 前4个字节前缀表示字符串的字节数(不包括结尾的 BUL 字符),所以只要移动一下指针就行了:

/* 强制转换成int指针减一后读取,然后除以2(一个Unicode字符两字节) */
size_t Len(const BSTR str)
{
return *((int *)str – 1) / 2;
}
可以看出,由于 BSTR 的长度可以通过前缀取得,并不需要以 NUL 来作为字符串结束符,也就是说,VBS 字符串是 binary safe (二进制安全)的。

那么为什么下面的代码只能显示 Hello 呢?
MsgBox str
这看起来好像和上面说的矛盾,其实不然。VBS 字符串的确是兼容 Chr(0) 字符的,MsgBox 之所以会被 Chr(0) 截断,是因为 MsgBox 在内部调用了 MessageBox 函数,而该函数是以 NUL 作为字符串结束符的。

/* 简单起见只实现一个参数

* MessageBox 的第二个参数是以 NUL 作为结束符的

* Pointer to a null-terminated string that contains the message to be displayed.

* 所以 VBS 字符串中包含的 Chr(0) 会把字符串截断
*/
int MsgBox(const BSTR str)
{
return MessageBoxW(NULL, str, L””, 0);
}
也就是说,如果 VBS 内置的函数或者 COM 组件的某些方法在其内部实现中调的 Windows API 的字符串参数是以 NUL 作为结束符的话,就会被 Chr(0) 字符截断,上面示例的Chr(0) 和 NUL 交替使用,表示同一个意思。

Views: 54

Hi, I’m vbsgirl

办公职员