在BDS 2006的功能文件中著明了dbExpress的MS SQL Server驱动程式支援MS SQL 2005以及Unicode,这非常的有趣。为什么? 因为这牵涉到了许多的事情。首先许多Delphi/C++Builder的使用者早已要求Borland推出支援Unicode的VCL架框,这个支援Unicode的VCL架框早在Delphi 2时我便向Delphi R&D团队要求,因为当时我在台湾负责和新加坡同事为Delphi开发中文套件就我就觉得有这个需要。而当时Anders也认为这很重要,也准备为VCL架框加入Unicode的能力,可惜的是人去楼空,随著Anders的离开这个功能也就一直被悬而未决。
随著多语言环境不断的成长和成熟,Unicode的VCL架框似乎再也不能拖了,因此Borland在2005年Delphi的Roadmap上宣示了在Delphi 64位元的版本中,VCL架框将支援Unicode。那么对于目前需要Unicode的Delphi/C++Builder开发人员要怎么办呢? 就我所知,许多人目前都是使用TNT控制项来解决这个需求:
http://www.tntware.com/delphicontrols/unicode/
即然VCL架框在未来才准备支援Unicode,而BDS 2006的MS SQL Server dbExpress驱动程式已经支援了Unicode,那么这是怎么做到的? 其实Borland虽然在未来才准备让VCL架框支援Unicode,但是在BDS 2006的VCL架框中却已经开始了这个工作,修改了许多VCL的方法,特性,事件和类别,介面等。也加入了一些新的类别,如此一来才能让dbExpress驱动程式和VCL架框配合。例如在BDS 2006的VCL架框中,和dbExpress相关的TCustomSQLDataSet类别已经改成是从TWideDataSet继承下来,而不再是像Delphi 2005和以前的版本,是直接从TDataSet继承下来。
TCustomSQLDataSet = class(TWideDataSet)
Private
…
而TWideDataset是一个新加入的类别,仔细看看TWideDataSet类别,主要是加入了WideString的支援宣告,为Unicode做准备,此外TWideDataset也复载了许多支援WideString的方法,为执行包含Unicode的SQL叙述和命令做准备。
{ TWideDataset }
TWideDataSet = class(TDataSet, IProviderSupport2)
protected
function PSExecuteStatement(const ASQL: string; AParams: TParams;
ResultSet: Pointer = nil): Integer; overload; override; deprecated;
function PSExecuteStatement(const ASQL: WideString; AParams: TParams;
ResultSet: Pointer = nil): Integer; overload; override;
function PSGetCommandText: string; override; deprecated;
function PSGetCommandTextW: WideString; override;
function PSGetKeyFields: string; override; deprecated;
function PSGetKeyFieldsW: WideString; override;
function PSGetQuoteChar: string; override; deprecated;
function PSGetQuoteCharW: WideString; override;
function PSGetTableName: string; override; deprecated;
function PSGetTableNameW: WideString; override;
procedure PSSetCommandText(const CommandText: string); overload; override; deprecated;
procedure PSSetCommandText(const CommandText: WideString); overload; override;
function IProviderSupport2.PSGetCommandText = PSGetCommandTextW;
function IProviderSupport2.PSGetKeyFields = PSGetKeyFieldsW;
function IProviderSupport2.PSGetQuoteChar = PSGetQuoteCharW;
function IProviderSupport2.PSGetTableName = PSGetTableNameW;
end;
除此之外,对于dbExpress来说,BDS 2006也加入了一些新的wrapper类别来封装dbExpress的介面,例如对于dbExpress的ISQLMetaData介面,Delphi 2005加入了TISQLMetaData25封装类别,BDS 2006则加入了TISQLMetaData30封装类别。TISQLMetaData25封装类别复载了抽象虚拟类别 : TISQLMetaData抽象虚拟类别,并且使用做为实作的介面。
TISQLMetaData25 = class(TISQLMetaData)
protected
I : ISQLMetaData25;
public
constructor Create(newMetaData: ISQLMetaData25);
destructor Destroy; override;
function SetOption(eDOption: TSQLMetaDataOption;
PropValue: LongInt): SQLResult; override;
function SetStringOption(eDOption: TSQLMetaDataOption;
const str: WideString): SQLResult; override;
function GetOption(eDOption: TSQLMetaDataOption; PropValue: Pointer;
MaxLength: SmallInt; out Length: SmallInt): SQLResult; override;
function GetStringOption(eDOption: TSQLMetaDataOption;
var str: WideString): SQLResult; override;
function getObjectList(eObjType: TSQLObjectType; var Cursor: TISQLCursor):
SQLResult; override;
function getTables(TableName: PWideChar; TableType: LongWord;
var Cursor: TISQLCursor): SQLResult; override;
function getProcedures(ProcedureName: PWideChar; ProcType: LongWord;
var Cursor: TISQLCursor): SQLResult; override;
function getColumns(TableName: PWideChar; ColumnName: PWideChar;
ColType: LongWord; var Cursor: TISQLCursor): SQLResult; override;
function getProcedureParams(ProcName: PWideChar; ParamName: PWideChar;
var Cursor: TISQLCursor): SQLResult; override;
function getIndices(TableName: PWideChar; IndexType: LongWord;
var Cursor: TISQLCursor): SQLResult; override;
function getErrorMessage(Error: PWideChar): SQLResult; overload; override;
function getErrorMessageLen(out ErrorLen: SmallInt): SQLResult; override;
function getErrorMessage(var Error: WideString): SQLResult; overload; override;
end;
而TISQLMetaData30也是复载了抽象虚拟类别 : TISQLMetaData抽象虚拟类别,但是使用了ISQLMetaData30介面做为实作的依据。
TISQLMetaData30 = class(TISQLMetaData)
protected
I : ISQLMetaData30;
public
constructor Create(newMetaData: ISQLMetaData30);
destructor Destroy; override;
function SetOption(eDOption: TSQLMetaDataOption;
PropValue: LongInt): SQLResult; override;
function SetStringOption(eDOption: TSQLMetaDataOption;
const str: WideString): SQLResult; override;
function GetOption(eDOption: TSQLMetaDataOption; PropValue: Pointer;
MaxLength: SmallInt; out Length: SmallInt): SQLResult; override;
function GetStringOption(eDOption: TSQLMetaDataOption;
var str: WideString): SQLResult; override;
function getObjectList(eObjType: TSQLObjectType; var Cursor: TISQLCursor):
SQLResult; override;
function getTables(TableName: PWideChar; TableType: LongWord;
var Cursor: TISQLCursor): SQLResult; override;
function getProcedures(ProcedureName: PWideChar; ProcType: LongWord;
var Cursor: TISQLCursor): SQLResult; override;
function getColumns(TableName: PWideChar; ColumnName: PWideChar;
ColType: LongWord; var Cursor: TISQLCursor): SQLResult; override;
function getProcedureParams(ProcName: PWideChar; ParamName: PWideChar;
var Cursor: TISQLCursor): SQLResult; override;
function getIndices(TableName: PWideChar; IndexType: LongWord;
var Cursor: TISQLCursor): SQLResult; override;
function getErrorMessage(Error: PWideChar): SQLResult; overload; override;
function getErrorMessageLen(out ErrorLen: SmallInt): SQLResult; override;
function getErrorMessage(var Error: WideString): SQLResult; overload; override;
end;
那么ISQLMetaData25和ISQLMetaData30有什么不同? 细节看看下面这两个介面的差异:
ISQLMetaData25 = interface(ISQLMetaData)
function SetOption(eDOption: TSQLMetaDataOption;
PropValue: LongInt): SQLResult; stdcall;
function GetOption(eDOption: TSQLMetaDataOption; PropValue: Pointer;
MaxLength: SmallInt; out Length: SmallInt): SQLResult; stdcall;
function getObjectList(eObjType: TSQLObjectType; out Cursor: ISQLCursor25):
SQLResult; stdcall;
function getTables(TableName: PChar; TableType: LongWord;
out Cursor: ISQLCursor25): SQLResult; stdcall;
function getProcedures(ProcedureName: PChar; ProcType: LongWord;
out Cursor: ISQLCursor25): SQLResult; stdcall;
function getColumns(TableName: PChar; ColumnName: PChar;
ColType: LongWord; Out Cursor: ISQLCursor25): SQLResult; stdcall;
function getProcedureParams(ProcName: PChar; ParamName: PChar;
out Cursor: ISQLCursor25): SQLResult; stdcall;
function getIndices(TableName: PChar; IndexType: LongWord;
out Cursor: ISQLCursor25): SQLResult; stdcall;
function getErrorMessage(Error: PChar): SQLResult; overload; stdcall;
function getErrorMessageLen(out ErrorLen: SmallInt): SQLResult; stdcall;
end;
我们可以清楚的看到ISQLMetaData25和ISQLMetaData30介面不同的地方是ISQLMetaData30完全使用WideString的格式来处理后理后端的资料来源,而ISQLMetaData25则是使用一般的字串格式,这代表ISQLMetaData30介面能够处理Unicode的资料。当然,BDS 2005其他封装dbExpress介面的wrapper类别也是类似的。
ISQLMetaData30 = interface(ISQLMetaData)
function SetOption(eDOption: TSQLMetaDataOption;
PropValue: LongInt): SQLResult; stdcall;
function GetOption(eDOption: TSQLMetaDataOption; PropValue: Pointer;
MaxLength: SmallInt; out Length: SmallInt): SQLResult; stdcall;
function getObjectList(eObjType: TSQLObjectType; out Cursor: ISQLCursor30):
SQLResult; stdcall;
function getTables(TableName: PWideChar; TableType: LongWord;
out Cursor: ISQLCursor30): SQLResult; stdcall;
function getProcedures(ProcedureName: PWideChar; ProcType: LongWord;
out Cursor: ISQLCursor30): SQLResult; stdcall;
function getColumns(TableName: PWideChar; ColumnName: PWideChar;
ColType: LongWord; Out Cursor: ISQLCursor30): SQLResult; stdcall;
function getProcedureParams(ProcName: PWideChar; ParamName: PWideChar;
out Cursor: ISQLCursor30): SQLResult; stdcall;
function getIndices(TableName: PWideChar; IndexType: LongWord;
out Cursor: ISQLCursor30): SQLResult; stdcall;
function getErrorMessage(Error: PWideChar): SQLResult; overload; stdcall;
function getErrorMessageLen(out ErrorLen: SmallInt): SQLResult; stdcall;
end;
现在封装dbExpress的VCL类别都已经完成了对于Unicode的支援,就等Borland推出支援各种关连资料库的dbExpress驱动程式即可。而VCL架框也在稳定的朝向支援Unicode的方向前进,依照Delphi的Roadmap,我们将可望在Borland推出代号为HighLander的BDS之后,看到全新架构的VCL架框。
在此时和dbExpress 3.0玩玩真的很有意思, dbExpress 3.0不但功能更多了, 效率也比以前的dbExpress快上了许多, 可以说是目前Win32下最好的资料存取技术了.