在Delphi程序中动态创建ODBC数据源
林 琪
在开发Delphi应用程序中,经常会遇到在程序中动态设置数据源的问题。如果使用到一些分布式数据库或Access数据库,还可能需要建立相应的ODBC关联,因此数据源设置问题不仅体现为BDE数据源的设置,还需要完成ODBC数据源的设置。
以Access数据库为例,我们在实际编程中发现,如果不建立ODBC数据源,即只建立MSAccess类型的BDE数据源,虽然可以避开ODBC数据源设置的麻烦,但在Delphi中利用此数据源生成报表时会出现死机现象。如果确实需要使用Access数据库,而且应用程序需要报表功能时,利用程序完成ODBC数据源的设置就必不可少了。
一、实现方法
我们在用VC编程中曾经通过调用函数SQLCreateDataSource动态创建过ODBC数据源,其作用是设置好数据源名后,自动提示用户选择所需连接的数据库名,使用相当灵活。由于在Delphi的接口函数库中找不到它,笔者就自己动手做一个函数库,旨在提供一个标准接口。下面将介绍具体实现方法:
1.动态链接库的设计
在VC++ 6.0环境下设计了一个动态链接库DLL(ODBCDLL),核心就是设计输出函数creatodbc,其作用是创建ODBC数据源。
首先利用AppWizard创建一个动态链接库工程ODBCDLL.dsw,在主程序ODBCDLL.cpp中实现函数定义。
函数声明为void creatodbc(HWND hld,char strDSN),其中hld参数为窗口句柄,strDSN为ODBC数据源名。
void creatodbc(HWND hld,char strDSN)
{
SQLCreateDataSource(hld, strDSN);
}
接着建立头文件odbcapi.h,用来建立公共函数的输出接口:
#ifndef _ODBCAPI_H_
#define _ODBCAPI_H_
//__declspec(dllexport)
extern "C" {
void creatodbc(HWND hld,char strDSN);
}
#endif
编译生成ODBCDLL.dll后,就可着手下一步设计。
2.Delphi接口单元的设计
有了动态链接库,下一步需要使用其中的函数creatodbc,根据ODBCAPI.h的内容设计接口单元ODBCAPI.pas,这一步非常重要,否则不能链接成功。
接口单元ODBCAPI.pas内容为:
unit ODBCAPI;
interface
uses
{$IFDEF WIN32}
Windows;
{$ELSE}
Wintypes, WinProcs;
{$ENDIF}
{=> ODBCAPI.H <=}
{$IFNDEF _ODBCAPI_H_}
{$DEFINE _ODBCAPI_H_}
{///__declspec(dllexport) }
procedure creatodbc(hld: HWND;
strDSN: PChar) {$IFDEF WIN32} cdecl {$ENDIF};
{$ENDIF}
implementation
procedure creatodbc; external 'ODBCDLL.DLL';
end.
函数声明中需要注意到,由于VC和Delphi的类型有所差异,需要将strDSN的类型调整为PChar。
下面,可以开始实现动态创建ODBC数据源了。
3. ODBC数据源的创建
要使用creatodbc函数创建ODBC数据源,首先将接口单元加入到工程中,并在相应文件中进行声明:
uses ODBCAPI;
在事件响应函数中设置变量hd用来标识主应用的主窗口句柄,它将在调用creatodbc时用到。若创建的ODBC数据源名为my_new_data,设置如下:
procedure TSETUP.SpeedButton1Click(Sender: TObject);
var
hd:HWND;
begin
hd:=application.Handle;
creatodbc(hd,'my_new_data');
end;
二、实例应用
一般来讲,创建数据源的工作总是在首次进入安装时完成,我们可以模拟一个安装过程,包括设置安装进程条、创建ODBC数据源,特别是需要在创建ODBC数据源后相应调整BDE数据源的设置。
首先来看调整BDE数据源的方法,由于Delphi应用中存在一个非常好用的默认变量Session,所以这个过程就相对简单多了。
设计一个方法creatAlias,其作用是建立与ODBC数据源相关的BDE数据源别名,参数AliasString表示建立的别名,database则为ODBC数据源名,这里以Access数据库为例,因此将ODBC数据源的驱动设置为“Microsoft Access Driver (.mdb)”,如果必要,可以将驱动程序本身也作为参数在程序中设置,将进一步增加其灵活程度。如果别名已经存在,系统将会自动删除,并以新创建的别名为准。定义如下:
procedure TSETUP.creatAlias (AliasString,database : String);
var
MyList : TStringList;
const
Driver = 'Microsoft Access Driver (*.mdb)';
begin
MyList:=TStringList.Create;
try
With MyList do
begin
Add('DATABASE NAME= ');
Add('ODBC DSN='+database);
Add('USER NAME= ');
end;
if Session.IsAlias(AliasString) then
begin
Session.DeleteAlias
(AliasString);
Session.SaveConfigFile;
end;
Session.AddAlias(AliasString,Driver,MyList);
Session.SaveConfigFile;
finally
myList.Free;
end;
end;
编写安装按钮的响应事件代码如下:
procedure TSETUP.SpeedButton1Click(Sender: TObject);
var
hd:HWND;
F: TextFile;
i,count:integer;
staPanleWidth:integer;
begin
progress:=TProgressbar.create(setup);
count:=3000; //进程条的最大值
staPanleWidth:=status.Panels.Items[2].width;
status.Panels.Items[2].width:=150; // 改变宽度
status.repaint;
with progress do
begin
top:=StatusDrawRect.top;
left:=StatusDrawRect.left;
width:=StatusDrawRect.right-StatusDrawRect.left;
height:=StatusDrawRect.bottom-StatusDrawRect.top;
//设定进程条的宽度和高度
visible:=true;
Parent := status; //该进程条的拥有者为状态条status
Min := 0; Max := Count; //进程条的最大和最小值
Step := 1; //进程条的步长
for i := 1 to round(count/5) do
Stepit; // 累加进程条
//这里可以分发证书
for i := 1 to round(count/5) do
Stepit; // 累加进程条
//这里可以检查证书的合法性
for i := 1 to round(count/5) do
Stepit; // 累加进程条
//这里可以检查时间是否合法
for i := 1 to round(count/5) do
Stepit; // 累加进程条
//创建ODBC数据源
hd:=application.Handle;
creatodbc(hd,'my_new_data');
for i := 1 to round(count/5) do
Stepit; // 累加进程条
creatAlias('my_new_data','my_new_data');
//建立BDE别名
Free; //释放进程条
end; //with
status.Panels.Items[2].width:=staPanleWidth;
//恢复状态条嵌板的宽度
end;
以上程序已在Delphi 4.0、Windows 98平台上运行通过。