当前位置:文档之家› 动态连接库

动态连接库

动态连接库
动态连接库

动态链接库(DLL)

一、相关概念

动态链接库(Dynamic Link Library):

动态链接库通常都不能直接运行,也不能接收消息。它们是一些独立的文件,文件后缀一般为.DLL,其中包含供其他可执行程序或其它DLL调用的函数。只有在其它模块调用动态链接库中的函数时,它才发挥作用。

例如,Windows API中的所有函数都包含在DLL中。其中有3个最重要的DLL,Kernel32.dll,它包含用于管理内存、进程和线程的各个函数;User32.dll,它包含用于执行用户界面任务(如窗口的创建和消息的传送)的各个函数;GDI32.dll,它包含用于画图和显示文本的各个函数。他们一般位于C:\windows\System32或类似的目录下。

通俗一点说,动态链接库就是将很多函数放到一起形成一个集合模块,注册后供其他应用程序运行时动态调用。

这许许多多的函数又可分为内部函数和导出函数。内部函数是用来在动态链接库内部调用的函数,主要用来实现动态链接库的实际功能;导出函数,顾名思义就是供外部模块或者应用程序在运行的时候调用的,是应用程序和动态链接库间的接口。导出函数包含在导出表中, 导出表包含动态链接库中所有可以被外部调用的函数名(对外的接口)。

顾名思义,动态链接库,是动态链接,它是相对于静态链接而言的。

静态库:

函数和数据被编译进一个二进制文件(通常扩展名为.LIB)。在其他程序使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据,并把它们和应用程序的其它模块组合起来创建最终的可执行文件(.EXE文件)。发布产品时,只需发布.EXE文件即可,不需要发布.LIB文件。

优点:无须包括函数库所包含的函数代码,应用程序可以利用标准的函数集;

缺点:两个应用程序运行时同时使用同一静态链接库中的函数,需要使用同一函数代码的两份拷贝,降低内存使用率。

动态库:

在其他程序使用动态库的时候,往往要用到DLL提供者提供的两个文件:一个引入库(.LIB)和一个DLL(.DLL)。但这里的.LIB文件和静态库中的.LIB文件有着本质的差别,这里的.LIB只包含导出的函数、变量的符号名,.DLL包含实际的函数和数据。在编译链接时,只需要链接.LIB,DLL中的函数代码和数据并不复制到可执行文件中,在运行的时候,才去加载DLL,并访问DLL中的导出函数。

优点:可以采用多种编程语言来编写;提供二次开发的平台;简化项目管理;可以节省磁盘空间和内存,等。

动态链接库DLL和可执行文件EXE的区别:

可执行文件运行起来后有自己的独立的进程空间,而动态链接库的导出函数被动态链接到应用程序的进程空间,这样多个应用程序可以共享一份代码副本。另外,动态链接库可以包括一个导出表,记录该动态链接库对外提供的函数接口。

二、DLL的开发、声明和调用

1、Win32 DLL和MFC DLL的编写。

见下面的例子。

2、DLL中声明导出函数的三种方式:

使用_declspec(dllexport)、使用extern、使用.def文件

3、导出整个类或仅导出类中的部分成员函数。

导出整个类:在类定义的关键字class后面加_declspec(dllexport)

导出成员函数:在成员函数定义的最前面加_declspec(dllexport)

4、调用动态链接库的两种方式:

隐式链接:需在工程设置中添加对.lib的引用或者使用#pragma comment ( lib, "Dll1.lib " ) 显式加载:需要调用LoadLibrary()或者类似的函数加载动态链接库,再使用GetProcessAddress()获得要调用的每个函数的函数指针,使用完毕后,调用FreeLibrary()卸载DLL。

这两种加载方式都需要预先声明外部函数的类型,然后才能调用成功。只是声明的方式有所不同。

三、实例

1:创建一个Win32 DLL工程Dll1

1.1使用_declspec(dllexport)声明导出函数

在Dll1.cpp中输入以下代码:

#include

#include

int AbsSub(int a, int b) //注意,前面没有加_declspec(dllexport) 导出声明

{

return (a > b ? a - b : b - a);

}

_declspec(dllexport) int Add(int a, int b) //需要导出的函数名前面要加导出声明

{

return a + b;

}

_declspec(dllexport) int Sub(int a, int b)

{

return a - b;

}

_declspec(dllexport) int Sub(int a, int b, bool bAbs)

{

if (bAbs)

return AbsSub(a, b);

else

return Sub(a, b);

}

class /*__declspec(dllexport)*/ CMyPoint

{

public:

void Print(int x, int y)

{

HWND hWnd = ::GetForegroundWindow();

HDC hdc = ::GetDC(hWnd);

char buf[20];

memset(buf, 0, 20);

sprintf(buf, "x = %d, y = %d", x, y);

::TextOut(hdc, 10, 10, buf, strlen(buf));

::ReleaseDC(hWnd, hdc);

};

void Show(int x, int y)

{

HWND hWnd = ::GetForegroundWindow();

HDC hdc = ::GetDC(hWnd);

char buf[20];

memset(buf, 0, 20);

sprintf(buf, "x = %d, y = %d", x, y);

::TextOut(hdc, 10, 30, buf, strlen(buf)); //仅仅y坐标不同。

::ReleaseDC(hWnd, hdc);

};

};

class CPerson

{

public:

/*__declspec(dllexport)*/ void ShowName(const char * name)

{

HWND hWnd = ::GetForegroundWindow();

HDC hdc = ::GetDC(hWnd);

::TextOut(hdc, 10, 50, name, strlen(name));

::ReleaseDC(hWnd, hdc);

};

};

编译链接后在debug目录下可以找到dll1.lib和dll1.dll两个文件。

注意,如果代码中每个函数名前都没有声明_declspec(dllexport),编译时将不会生产dll1.lib文件。这样的DLL将没有使用价值。

这里有一个问题,调用这个.DLL的其他程序如何知道其中的函数名称和用法呢?答案是:在DOS下使用DLL导出函数查看命令dumpbin/exports dll1.dll,就可以看到dll1.dll 文件包含了上述2个Add和Sub的信息。Dumpbin.exe文件一般位于C:\Program Files\Microsoft Visual Studio\VC98\Bin目录下。如果在任意目录不能执行Dumpbin,则需要先运行C:\Program Files\Microsoft Visual Studio\VC98\Bin\VCV ARS32.BA T,使系统将该路径设置为缺省路径。

Dumpbin查看结果如下:

ordinal hint RV A name

1 0 00001005 ?Add@@Y AHHH@Z

2 1 0000100A ?Sub@@YAHHH@Z

3 2 0000101

4 ?Sub@@YAHHH_N@Z

其中RV A列包含的数值表示导出函数在DLL模块中的偏移地址,通过该地址可以在模块中找到该函数。Name列是导出函数的名称,但我们发现函数名字被改编了。

但其中没有AbsSub的信息,这是因为我们没有在AbsSub函数名前加入导出声明_declspec(dllexport)。因此,在要导出的函数名前面一定要加上导出声明。

1.2 使用extern “C” _declspec(dll ex port)解决函数名改编问题

加上extern “C”之后,用C++写的DLL就可以在纯C环境使用了,但写DLL时就不能使用类了,因为C语言不支持类。因此,除非特殊需求,一般不使用这种方式。使用dumpbin 命令查看,发现函数名字没有被改编。

1.3 使用.def文件导出函数声明解决函数名改编问题(见后面的MFC DLL实例)

.def是由一个或者多个用于描述DLL属性的文本文件。它包含以下一些模块定义语句:LIBRARY:指出动态链接库的名字,链接器负责将该名字放到动态链接库中;

DESCRIPTION:描述该动态链接库的用途;

EXPORTS:指出被导出函数的名称和序号;

例如:

LIBRARY DllA

DESCRIPTION “实现数据库的操作”

EXPORTS

Add @1

Delete @2

Member @3

对于使用MFC 应用程序向导(AppWizard)生成的动态链接库,应用程序向导(AppWizard)会自动生成一个.def文件;对于非MFC DLL,可手工添加该文件到工程中。使用dumpbin 命令查看,发现函数名字没有被改编。

2、以隐式加载的方式使用DLL

2.1 以_declspec(dll im port)方式声明外部函数

创建一个MFC对话框工程DllTest来调用上述DLL中的3个函数。加入3个按钮,并添加代码。如下:

_declspec(dllimport) int Add(int a, int b); //使用_declspec(dllimport) 声明DLL函数_declspec(dllimport) int Sub(int a, int b); //注意:是dll im port,不是dll ex port

_declspec(dllimport) int Sub(int a, int b, bool bIsAbs);

void CDllTestDlg::OnBtnAdd()

{

CString str;

str.Format("3 + 4 = %d", Add(3, 4));

MessageBox(str);

}

void CDllTestDlg::OnBtnSub()

{

CString str;

str.Format("3 - 4 = %d", Sub(3, 4));

MessageBox(str);

}

void CDllTestDlg::OnBtnAbssub()

{

CString str;

str.Format("abs(3 - 4) = %d", Sub(3, 4, true));

MessageBox(str);

}

编译发现,出现LNK2001链接错误。将dll1.lib复制到当前工程目录下,并做如下工程设置:

再次编译将会通过。但运行却又出现错误,提示找不到dll1.dll文件。将dll1.dll也复制到当前工程目录下,再次运行后,一切就正常了。

从上面的编译链接和运行的过程可以知道:dll1.lib文件是链接器进行链接时才需要的,它只是提供了dll1.dll文件中导出函数的函数名等信息,并没有包含实际的函数和数据。函数的实际功能是在程序运行时动态的调用dll1.dll文件时才加载的。也就是说,DLL的调用者在开发时需要.lib文件,发行时需要.dll文件。

●也可以不用在工程中进行dll1.lib的设置,在需要调用DLL函数的代码的前面,添加如

下语句:

#pragma comment ( lib, "Dll1.lib " )

这和上图中在工程设置里写上Dll1.lib的效果一样。

●使用Viaual Studio提供Depends工具可以查看一个EXE文件或者DLL文件在运行时所

依赖的所有动态链接库。如下图所示,说明dlltest.exe文件需要dll1.dll,mfc42d.dll等的支持。

2.2 以extern方式声明外部函数

上面是使用_declspec(dllimport)对外部函数进行的声明,它明确告诉编译器,函数是来自于DLL的,也是较好的声明方式。还有一种extern声明外部函数的方式,将上述dll1.cpp 文件的声明中的_declspec(dllimport)替换成extern即可,运行效果也是一样的。如下:extern int Add(int a, int b); //使用extern声明DLL中的函数

extern int Sub(int a, int b);

extern int Sub(int a, int b, bool bIsAbs);

2.3 使用头文件完善函数的声明,明确告知使用者函数的调用方式。

上面讲过,可以使用DOS命令dumpbin来查看DLL的导出函数有哪些,但dumpbin 命令看不到函数的具体参数,返回类型等具体的函数原型。因此,作为DLL的开发者,有必要提供完整的导出函数的原型,明确告知调用者DLL的用法。

为Dll1工程添加Dll1.h头文件,代码如下:

#ifndef DLL1_API

#define DLL1_API _declspec(dllimport)

#endif

//int AbsSub(int a, int b) //实际工作中删掉这一行而不是注释掉,隐藏设计创意

DLL1_API int Add(int a, int b);

DLL1_API int Sub(int a, int b);

DLL1_API int Sub(int a, int b, bool bAbs);

在Dll1.cpp文件的最前面加上对DLL1_API的定义,如下:

//在此对DLL1_API进行声明,表示当前是导出模式。

//而本DLL的最终使用者不需进行任何声明,默认使用导入模式。

#define DLL1_API _declspec(dllexport)

将Dll1.h文件复制到DllTest工程的目录下,包含进工程的.cpp文件中去,并注释掉原有的_declspec(dllexport)声明。

这样,同一个头文件就可以被开发者和使用者共同使用了。

3、以显式加载的方式使用DLL

这种方式需要调用LoadLibrary()或者类似的函数,来加载动态链接库,再使用GetProcessAddress()获得要调用的每个函数的函数指针,使用完毕后,调用FreeLibrary()卸载DLL。这种方式不需要*.lib文件,也不需要包含*.h文件。但需要知道被调用函数的原型,以便为GetProcessAddress()的返回值定义相应的函数指针。

3.1 建立一个MFC DLL工程MyDll

3.2 在MyDll.cpp文件中添加函数声明

#include "MyDll.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = __FILE__;

#endif

void MyFun1(CWnd *h);

void MyFun2(CWnd *h);

添加函数实现:

CMyDllApp theApp;

void MyFun1(CWnd *h)

{

CBrush newBrush;

newBrush.CreateSolidBrush(RGB(0,0,255));

CBrush *pOldBrush;

CClientDC dc(h);

pOldBrush = dc.SelectObject(&newBrush);

dc.Ellipse(50,30,300,200);

dc.SelectObject(pOldBrush);

}

void MyFun2(CWnd *h)

{

CPen newPen;

CPen *pOldPen;

newPen.CreatePen(PS_SOLID,1,RGB(0,0,255));

CClientDC dc(h);

CRect rect(40,20,310,210);

InvalidateRect((HWND__*)h,&rect,TRUE);

pOldPen = dc.SelectObject(&newPen);

dc.Ellipse(50,30,300,200);

dc.SelectObject(pOldPen);

}

3.3 .def文件的编写:

; MyDll.def : Declares the module parameters for the DLL. LIBRARY "MyDll"

DESCRIPTION 'MyDll Windows Dynamic Link Library' EXPORTS

; Explicit exports can go here

MyFun1

MyFun2

注意:.def文件注释是使用分号;而不是//

3.4 编译生成MyDll.Dll文件

将生成的MyDll.dll文件拷贝到D:\WINDOWS\system32目录下.这一部叫做动态链接库文件的注册.注意:Debug和Release编译方式生成的该文件位置不一样。怎么找?

3.5 使用动态链接库

1建立使用动态链接库的工程(顺便介绍MFC DLL的使用)

其他步骤默认既可。

涉及如下对话框界面

添加代码:

1.声明全局变量

在TesDllDlg.cpp中添加如下代码:

HINSTANCE hDLL = NULL; // 声明全局变量hDLL用于存放DLL的句柄并初始化为空typedef void (* MYFUN1)(CWnd *h);// 声明函数指针类型,用它来声明变量MyFun1;MYFUN1 MyFun1;

typedef void (* MYFUN2)( CWnd *h);

MYFUN2 MyFun2;

如图:

2 添加按钮点击消息响应函数

void CTestDllDlg::OnLoadDll()

{

// TODO: Add your control notification handler code here

if(hDLL != NULL)

{

MessageBox("你已经装载了MyDll.DLL文件!");

return;

}

hDLL=LoadLibrary("MyDll.DLL");

if(hDLL == NULL)

{

MessageBox("无法装载MyDll.DLL文件!");

return;

}

MyFun1 = (MYFUN1)GetProcAddress(hDLL,"MyFun1");

MyFun2 = (MYFUN2)GetProcAddress(hDLL,"MyFun2"); }

void CTestDllDlg::OnRunDll()

{

// TODO: Add your control notification handler code here

if(hDLL == NULL)

{

MessageBox("你还没有装载MyDll.DLL文件!");

return;

}

for(int i=0;i<=10;i++)

{

MyFun1(this);

Sleep(1000);

MyFun2(this);

Sleep(1000);

}

}

程序运行结果:反复绘制实心的椭圆和空心的椭圆,如下图:

需要特别注意的是,如果函数名字被改编了,在使用GetProcessAddress()时,其第二个参数需要使用改编后的函数名。

另外,使用DEF导出函数时,不允许进行函数重载,但使用_declspec(dllexport)导出时就可以,其原因是_declspec(dllexport)对函数进行了名称改编。

另外也可根据导出函数的序号来获取函数指针。但不推荐使用这种方法。

如:

MYFUN2 Draw2 = (MYFUN2)GetProcAddress(hDLL, MAKEINTRESOURCE(2));

MYFUN2 Draw2 = (MYFUN2)GetProcAddress(hDLL, “MyFun2”);

是一样的效果。

四、DllMain

可有可无。如有,其中应尽量少的写代码。可以在此做一些初始化的工作。

BOOL WINAPI DllMain(

HINSTANCE hinstDLL, // handle to DLL module

DWORD fdwReason, // reason for calling function

LPVOID lpvReserved // reserved

);

作业:

1、提交:

编写一个Win32 DLL工程MaxMin,对外提供带2个参数和3个参数的求最大值和最小值的4个导出函数:int Max(int a, int b, int c); int Max(int a, int b); int Min(int a, int b, int c); int Min(int a, int b);

并另外编写一个MFC对话框工程TestMaxMin,使用隐式链接方式对MaxMin.dll进行测试。

2、思考:

MFC DLL中能导出某个类中的所有函数吗?如果能,有哪些限制?为什么?如何导出?使用上述Dll1工程中的例子进行测试。(考点: DEF和

declspec(dllexport)两种导出方式的比较及混合使用)

VB调用动态链接库(DLL)

VB调用动态链接库(DLL) 作为一种简单易用的Windows开发环境,Visual Basic 从一推出就受到了广大编程人员的欢迎。它使程序员不必再直接面对纷繁复杂的Windows消息,而可以将精力主要集中在程序功能的实现上,大大提高了编程效率。但凡事有利必有弊。 VB中高度的封装和模块化减轻了编程者的负担,同时也使开发人员失去了许多访问低层API函数和直接与Windows 交互的机会。因此,相比而言,VB应用程序的执行效率和功能比C/C++或Delphi生成的程序要差。为了解决这个问题,在一个大型的VB开发应用中,直接调用Windows API函数几乎是不可避免的;同时,还有可能需要程序员自己用 C/C++等开发一些动态连接库,用于在VB中调用。本文主要讨论在32位开发环境Visual Basic 5.0中直接调用Windows 95 API函数或用户生成的32位动态连接库的方法与规则。 Windows动态连接库是包含数据和函数的模块,可以被其它可执行文件(EXE、DLL、OCX 等)调用。动态连接库包含两种函数:输出(exported)函数和内部(internal)函数。输出函数可以被其它模块调用,而内部函数则只能在动态连接库内部使用。尽管动态连接库也能输出数据,但

实际上它的数据通常是只在内部使用的。使用动态连接库的优点是显而易见的。将应用程序的一部分功能提取出来做成动态连接库,不但减小了主应用程序的大小,提高了程序运行效率,还使它更加易于升级。多个应用程序共享一个动态连接库还能有效地节省系统资源。正因为如此,在Windows系统中,动态连接库得到了大量的使用。 一般来说,动态连接库都是以DLL为扩展名的文件,如Kernel32.dll、commdlg.dll等。但也有例外,如16位Windows 的核心部件之一GDI.exe其实也是一个动态库。编写动态连接库的工具很多,如VisualC++、BorlandC++、Delphi等,具体方法可以参见相关文档。下面只以Visual C++5.0为例,介绍一下开发应用于VisualBasic5.0的动态连接库时应注意的问题(本文中所有涉及C/C++语言或编译环境的地方,都以VC5为例;所有涉及VisualBasic的地方都以VB5 为例)。 作为一种32位Windows应用程序的开发工具,VB5生成的exe文件自然也都是32位的,通常情况下也只能调用32位的动态连接库。但是,并不是所有的32位动态库都能被VB生成的exe 文件正确地识别。一般来说,自己编写用于VB应用程序调用的动态连接库时,应注意以下几个方面的问题: 1、生成动态库时要使用__stdcall调用约定,而不能使用缺省的__cdecl调用约定;__stdcall 约定通常用于32位API

Android创建和使用数据库详细指南

Android创建和使用数据库详细指南(1) 摘要:每个应用程序都要使用数据,Android应用程序也不例外,Android使用开源的、与操作系统无关的SQL数据库--SQLite,本文介绍的就是如何为你的Android应用程序创建和操作SQLite数据库。 数据库支持每个应用程序无论大小的生命线,除非你的应用程序只处理简单的数据,那么就需要一个数据库系统存储你的结构化数据,Android使用SQLite数据库,它是一个开源的、支持多操作系统的SQL数据库,在许多领域广泛使用,如Mozilla FireFox就是使用SQLite 来存储配置数据的,iPhone也是使用SQLite来存储数据的。 在Android中,你为某个应用程序创建的数据库,只有它可以访问,其它应用程序是不能访问的,数据库位于Android设备/data/data//databases文件夹中,在这篇文章中,你将会学习到如何在Android中创建和使用数据库。 1SQLite数据库 使用Eclipse创建一个Android项目,取名为Database,如图1所示: 图1 数据库-使用Eclipse创建你的Android新项目

2创建DBAdapter辅助类 接下来创建一个数据库,取名为bookstitles,字段如图2所示。 图2 数据库字段在DBAdapter.java文件中,定义清单1中的常量。 清单1 定义DBAdapter.java文件中的常量 package net.learn2develop.Database; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper;

各种数据库的连接连接Connection方法

package com.jdbc.myConnection; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class ConnectionFactory { //获取Oracle连接 public static Connection getOracleConnection() { Connection con = null; try { Class.forName("oracle.jdbc.OracleDriver"); String url = "jdbc:oracle:thin:@localhost:1521:orcl"; con = DriverManager.getConnection(url, "scott", "tiger"); } catch (Exception e) { e.printStackTrace(); } return con; } //获取SQLServer的test数据库连接 public static Connection getSQLServerConnection() { return getMySQLConnection("test"); } //获取SQLServer 连接 public static Connection getSQLServerConnection(String databaseName) { Connection con = null; try { Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver"); String url = "jdbc:microsoft:sqlServer://localhost:1433;databaseName=" + databaseName; con = DriverManager.getConnection(url, "sa", "sa"); } catch (Exception e) { e.printStackTrace(); } return con; } //获取MySQL连接 public static Connection getMySQLConnection() { return getMySQLConnection("test");

VC++动态链接库创建和调用全过程详解

1.概论 先来阐述一下DLL(Dynamic Linkable Library)的概念,你可以简单的把DLL看成一种仓库,它提供给你一些可以直接拿来用的变量、函数或类。在仓库的发展史上经历了“无库-静态链接库-动态链接库”的时代。 静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib中的指令都被直接包含在最终生成的EXE文件中了。但是若使用DLL,该DLL不必被包含在最终EXE文件中,EXE文件执行时可以“动态”地引用和卸载这个与EXE独立的DLL文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。 对动态链接库,我们还需建立如下概念: (1)DLL 的编制与具体的编程语言及编译器无关 只要遵循约定的DLL接口规范和调用方式,用各种语言编写的DLL都可以相互调用。譬如Windows提供的系统DLL(其中包括了Windows的API),在任何开发环境中都能被调用,不在乎其是Visual Basic、Visual C++还是Delphi。 (2)动态链接库随处可见 我们在Windows目录下的system32文件夹中会看到kernel32.dll、user32.dll和gdi32.dll,windows的大多数API都包含在这些DLL中。kernel32.dll中的函数主要处理内存管理和进程调度;user32.dll中的函数主要控制用户界面;gdi32.dll中的函数则负责图形方面的操作。 一般的程序员都用过类似MessageBox的函数,其实它就包含在user32.dll这个动态链接库中。由此可见DLL对我们来说其实并不陌生。 (3)VC动态链接库的分类 Visual C++支持三种DLL,它们分别是Non-MFC DLL(非MFC动态库)、MFC Regular DLL(MFC规则DLL)、MFC Extension DLL(MFC扩展DLL)。 非MFC动态库不采用MFC类库结构,其导出函数为标准的C接口,能被非MFC或MFC编写的应用程序所调用;MFC规则DLL 包含一个继承自CWinApp的类,但其无消息循环;MFC扩展DLL采用MFC的动态链接版本创建,它只能被用MFC类库所编写的应用程序所调用。 由于本文篇幅较长,内容较多,势必需要先对阅读本文的有关事项进行说明,下面以问答形式给出。 问:本文主要讲解什么内容? 答:本文详细介绍了DLL编程的方方面面,努力学完本文应可以对DLL有较全面的掌握,并能编写大多数DLL程序。 问:如何看本文? 答:本文每一个主题的讲解都附带了源代码例程,可以随文下载(每个工程都经WINRAR压缩)。所有这些例程都由笔者编写并在VC++6.0中调试通过。

GCC编译动态和静态链接库

我们通常把一些公用函数制作成函数库,供其它程序使用。函数库分为静态库和动态库两种。静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。本文主要通过举例来说明在Linux中如何创建静态库和动态库,以及使用它们。 在创建函数库前,我们先来准备举例用的源程序,并将函数库的源程序编译成.o文件。 第1步:编辑得到举例的程序--hello.h、hello.c和main.c; hello.c(见程序2)是函数库的源程序,其中包含公用函数hello,该函数将在屏幕上输出"Hello XXX!"。hello.h(见程序1)为该函数库的头文件。main.c(见程序3)为测试库文件的主程序,在主程序中调用了公用函数hello。 1.#ifndef HELLO_H 2.#define HELLO_H 3. 4.void hello(const char *name); 5. 6.#endif //HELLO_H 复制代码 程序1: hello.h 1.#include 2. 3.void hello(const char *name) 4.{ 5.printf("Hello %s!\n", name); 6.} 复制代码 程序2: hello.c 1.#include "hello.h" 2. 3.int main() 4.{ 5.hello("everyone"); 6.return 0; 7.} 复制代码 程序3: main.c

第2步:将hello.c编译成.o文件; 无论静态库,还是动态库,都是由.o文件创建的。因此,我们必须将源程序hello.c通过g cc先编译成.o文件。 在系统提示符下键入以下命令得到hello.o文件。 # gcc -c hello.c # 我们运行ls命令看看是否生存了hello.o文件。 # ls hello.c hello.h hello.o main.c # 在ls命令结果中,我们看到了hello.o文件,本步操作完成。 下面我们先来看看如何创建静态库,以及使用它。 第3步:由.o文件创建静态库; 静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为myhello,则静态库文件名就是libmyhello.a。在创建和使用静态库时,需要注意这点。创建静态库用ar命令。 在系统提示符下键入以下命令将创建静态库文件libmyhello.a。 # ar crv libmyhello.a hello.o # 我们同样运行ls命令查看结果: # ls hello.c hello.h hello.o libmyhello.a main.c # ls命令结果中有libmyhello.a。 第4步:在程序中使用静态库; 静态库制作完了,如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明静态库名,gcc将会从静态库中将公用函数连接到目标文件中。注意,gcc会在静态库名前加上前缀lib,然后追加

ASP NET 6种常用数据库的连接方法

1.C#连接连接Access 程序代码: using System.Data; using System.Data.OleDb; .. string strConnection="Provider=Microsoft.Jet.OleDb.4.0;"; strConnection+=@"Data Source=C:BegASPNETNorthwind.mdb"; OleDbConnection objConnection=new OleDbConnection(strConnection); .. objConnection.Open(); objConnection.Close(); 解释: 连接Access数据库需要导入额外的命名空间,所以有了最前面的两条using命令,这是必不可少的! strConnection这个变量里存放的是连接数据库所需要的连接字符串,他指定了要使用的数据提供者和要使用的数据源. "Provider=Microsoft.Jet.OleDb.4.0;"是指数据提供者,这里使用的是Microsoft Jet引擎,也就是Access中的数据引擎,https://www.doczj.com/doc/e013767207.html,就是靠这个和Access的数据库连接的. "Data Source=C:\BegASPNET\Northwind.mdb"是指明数据源的位置,他的标准形式是"Data

Source=MyDrive:MyPath\MyFile.MDB". PS: 1."+="后面的"@"符号是防止将后面字符串中的"\"解析为转义字符. 2.如果要连接的数据库文件和当前文件在同一个目录下,还可以使用如下的方法连接: strConnection+="Data Source="; strConnection+=MapPath("Northwind.mdb"); 这样就可以省得你写一大堆东西了! 3.要注意连接字符串中的参数之间要用分号来分隔. "OleDbConnection objConnection=new OleDbConnection(strConnection);"这一句是利用定义好的连接字符串来建立了一个链接对象,以后对数据库的操作我们都要和这个对象打交道. "objConnection.Open();"这用来打开连接.至此,与Access数据库的连接完成. 2.C#连接SQL Server 程序代码: using System.Data; using System.Data.SqlClient; .. string strConnection="user id=sa;password=;"; strConnection+="initial catalog=Northwind;Server=YourSQLServer;";

创建数据库和表

数学与信息技术学院实验报告 实验课程名称:SQL语言基础实验 实验项目名称:实验二创建数据库和表 专业班级: 学号: 姓名: 指导教师:

实验一创建数据库和表 【实验目的】 (1)了解MySQL数据库的存储引擎的分类; (2)了解表的结构特点; (3)了解MySQL的基本数据类型; (4)了解空值概念; (5)学会使用SQL界面工具中创建数据库和表; (6)学会使用SQL语句创建数据库和表。 【实验容及步骤】 一,实验容 实验题目 创建用于企业管理的员工管理数据库,数据库名为YGGL,包含员工的信息、部门信息及员工的薪水信息。数据库YGGL包含下列3个表: (1)Employees:员工信息表: (2)Departments:部门信息表: (3)Salary:员工薪水情况表。 二,实验步骤 1,使用命令行为方式创建数据库YGGL

打开MySQL Command Line Client,输入管理员密码登录,使用CREATE语句创建YGGL数据库: Creat database YGGL; 2,使用SQL语句在YGGL数据库中创建表Employees 执行创建表employees的SQL语句: 用同样的方法在数据库YGGL中创建表Salary。 创建一个结构与employees表结构相同的空表Employees(): Creat table Employees0LIKE Employees; 3,使用SQL语句删除表和数据库 删除表employees: Drop table employees; 删除数据库YGGL Drop database YGGL;

在VB 中调用动态连接库

在VB 中调用动态连接库 作为一种简单易用的Windows开发环境,Visual Basic从一推出就受到了广大编程人员的欢迎。它使程序员不必再直接面对纷繁复杂的Windows消息,而可以将精力主要集中在程序功能的实现上,大大提高了编程效率。但凡事有利必有弊。 VB中高度的封装和模块化减轻了编程者的负担,同时也使开发人员失去了许多访问低层API函数和直接与Windows交互的机会。因此,相比而言,VB应用程序的执行效率和功能比C/C++或Delphi生成的程序要差。为了解决这个问题,在一个大型的VB开发应用中,直接调用Windows API函数几乎是不可避免的;同时,还有可能需要程序员自己用C/C++等开发一些动态连接库,用于在VB中调用。本文主要讨论在32位开发环境Visual Basic 5.0中直接调用Windows 95 API函数或用户生成的32位动态连接库的方法与规则。 Windows动态连接库是包含数据和函数的模块,可以被其它可执行文件(EXE、 DLL、OCX 等)调用。动态连接库包含两种函数:输出(exported)函数和内部(internal)函数。输出函数可以被其它模块调用,而内部函数则只能在动态连接库内部使用。尽管动态连接库也能输出数据,但实际上它的数据通常是只在内部使用的。使用动态连接库的优点是显而易见的。将应用程序的一部分功能提取出来做成动态连接库,不但减小了主应用程序的大小,提高了程序运行效率,还使它更加易于升级。多个应用程序共享一个动态连接库还能有效地节省系统资源。正因为如此,在Windows系统中,动态连接库得到了大量的使用。 一般来说,动态连接库都是以DLL为扩展名的文件,如 Kernel32.dll、commdlg.dll等。但也有例外,如16位Windows的核心部件之一GDI.exe其实也是一个动态库。编写动态连接库的工具很多,如VisualC++、BorlandC++、Delphi等,具体方法可以参见相关文档。下面只以Visual C++5.0为例,介绍一下开发应用于VisualBasic5.0的动态连接库时应注意的问题(本文中所有涉及C/C++语言或编译环境的地方,都以 VC5为例;所有涉及VisualBasic的地方都以VB5 为例)。 作为一种32位Windows应用程序的开发工具,VB5生成的exe文件自然也都是32位的,通常情况下也只能调用32位的动态连接库。但是,并不是所有的32位动态库都能被VB生成的exe 文件正确地识别。一般来说,自己编写用于VB应用程序调用的动态连接库时,应注意以下几个方面的问题: 1、生成动态库时要使用__stdcall调用约定,而不能使用缺省的__cdecl 调用约定;__stdcall 约定通常用于32位API函数的调用。 2、在VC5中的定义文件(.def)中,必须列出输出函数的函数名,以强制VC5系统将输出函数的装饰名(decoratedname)改成普通函数名;所谓装饰名是VC的编译器在编译过程中生成的输出函数名,它包含了用户定义的函数名、

FORTRAN静态库动态库的生成

FORTRAN静态库、动态库的生成、维护与调用 闫昊明2006-9-10 一、FORTRAN静态库的生成与维护 FORTRAN 静态库是经过编译的代码块,它与主程序相对独立,可以被主程序调用,是FORTRAN工程类型之一. 静态库包含一系列子程序,但不包括主程序. 静态库一般具有LIB扩展名并包含目标代码,且静态库存放在它们特定的目录中. FORTRAN静态库在组织大型程序和在不同程序之间共享子程序等方面具有较大的优点,其重要性不言而喻. 当将静态库与主程序联系起来时,在主程序中调用静态库中的任何子程序将编译到相应的可执行程序. 应用静态库的时候,只有所需要的子程序才在编译过程中插入到可执行文件(.EXE),这意味着这种可执行文件将比包含所有的子程序所生成的可执行文件小. 而且,不必担心哪些子程序是需要的,哪些是不需要的,编译器将替你做出选择. 同时,当更改静态库中的子程序时,相应的应用程序可以不做任何改变,而只需要对其进行重新的编译链接,即可获得新的结果,这无疑也是方便的. 目前,常用的FORTRAN静态库有很多种,WINDOWS操作系统下的Compaq Visual FORTRAN version 6.5(简称CVF65)自带的数学统计库IMSL就是一个非常全面的静态库,可以用来解决线性代数和统计学上的很多经典问题. 此外,在NCAR互联网站有很多有用的FORTRAN子程序(网址:https://www.doczj.com/doc/e013767207.html,/softlib/mathlib.html),其中包括地球物理科学问题、离散和快速Fourier变换、可分离的椭圆微分方程、插值、Legendre多项式、普通数学问题、本征值问题求解、线性方程求解、非线性方程求解、常微分方程求解、特殊函数、统计学等常用子程序集等. 这些FORTRAN子程序可以解决很多基础性的问题,因此有很高的利用价值. 在WINDOWS操作系统下,可以用两个命令分别生成静态库. 一个是用‘nmake’命令,它一般用来编译原来应用在UNIX环境下的FORTRAN子程序集,在编译过程中要读取makefile文件中的编译命令,类似于在UNIX下安装软件. 另一个是用‘lib’命令,它可以在WINDOWS环境下编译任何需要集成为静态库的子程序集. 编译静态库在DOS命令行环境下比较方便,以后的命令行都指在此环境下运行. 在编译静态库前,首先要安装CVF65,其次要完成要编译的FORTRAN子程序(*.f90). 对于FORTRAN子程序,最好用FORTRAN90的标准来完成,应该放弃FORTRAN77标准。FORTRAN90是FORTRAN语言从结构化走向面向对象化的重要一步,使FORTRAN语言更加接近C++。在FORTRAN90标准中,对数组的操作既增强了功能又简化了使用,此外自由格式、MODULE、动态数组、指针等的应用大大丰富了FORTRAN语言,使得编程更加轻松。目前,FORTRAN95和FORTRAN2000标准也在应用,它们与FORTRAN90标准比较类似,主要的改进在并行运算方面,因此目前在单机上应用的主要还是FORTRAN90. 在DOS命令行环境下,进入到FORTRAN子程序所在的子目录,然后按下面两个步骤生成FORTRAN静态库. (1)键入“df *.f90 /c”,回车,可以看到CVF65编译器对所有的FORTRAN子程序(*.f90)进行编译,生成*.obj文件(注意,编译时,/c中的“c”必须小写). (2)键入“lib *.obj /out:libname.lib”,回车,可以看到链接生成libname.lib静态库. 需要注意的是,每次加入新的子程序或对静态库中的子程序修改以后,都要按上述两个步骤重新进行编译链接. 生成静态库以后,可用“dumpbin /linkermember libname.lib”来查看静态库中可用的子程序名称. 也可执行“lib /list libname.lib”来查看静态库中的*.obj文件. 当然,也可以在CVF65集成环境下,生成静态库. 步骤如下:

Java连接各种数据库写法

随笔- 6 文章- 0 评论- 1 Java连接各种数据库写法 # 示例配置参考,涵盖几乎所有的主流数据库 ############# Oracle数据库######################## # 数据库驱动名 driver=oracle.jdbc.driver.OracleDriver # 数据库URL(包括端口) dburl=jdbc:oracle:thin:@127.0.0.1:1521:zvfdb # 数据库用户名 user=root # 用户密码

password=zvfims ############# DB2数据库######################## # 数据库驱动名 driver=com.ibm.db2.jcc.DB2Driver # 数据库URL(包括端口) dburl=jdbc:db2://127.0.0.1:50000/zvfdb # 数据库用户名 user=root # 用户密码 password=zvfims ############# MySQL数据库######################## # 数据库驱动名

driver=com.mysql.jdbc.Driver # 数据库URL(包括端口) dburl=jdbc:mysql://127.0.0.1:3306/zvfdb # 数据库用户名 user=root # 用户密码 password=zvfims ############# PostgreSQL数据库数据库######################## # 数据库驱动名 driver=org.postgresql.Driver # 数据库URL(包括端口) dburl=jdbcostgresql://127.0.0.1/zvfdb

编译生成动态库时,被关联的静态库会被编译到动态库里面

动态库调用静态库. 生成动态库: 需要的目标文件得用-fPIC选项生成. 而静态库所需的目标文件可以不用-fPIC选项. 一个应用程序调用动态库, 而这个动态库其中的函数调用某静态库时,如何生成应用程序呢? 例: /////// static.h void static_print(); ///////static.cpp #include #include "static.h" void static_print() { std::cout<<"This is static_print function"< #include "shared.h" #include "static.h" void shared_print() { std::cout<<"This is shared_print function"; static_print(); } ////////test.cpp #include "share.h" int main() { shared_print(); return 0; } 方法一: 静态库的.o文件也用-fPIC生成. 生成动态库时把静态库加入. 生成应用程序时只加载动态库 g++ -c -fPIC static.cpp // 生成static.o ar -r libstatic.a static.o // 生成静态库libstatic.a g++ -c -fPIC shared.cpp // 生成shared.o g++ -shared shared.o -lstatic -o libshared.so // 生成动态库libshared.so 注: -shared是g++的选项,与shared.o无关. -lstatic选项把libstatic.a的函数加入动态库中. g++ test.cpp -lshared -o test.exe // link libshared.so 到test.exe中. 方法二: 静态库的.o文件不用-fPIC生成. 生成动态库时不加静态库. 生成应用程序时加载动态库和静态库. g++ -c static.cpp // 生成static.o ar -r libstatic.a static.o // 生成静态库libstatic.a g++ -c -fPIC shared.cpp // 生成shared.o g++ -shared shared.o -o libshared.so // 生成动态库libshared.so 注: -shared是g++的选项,与shared.o无关. 这时如果加-lstatic. error:relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC

java平时最常用的7种数据库连接方式

今天总结了java平时最常用的7种数据库连接方式,现在分享给大家 MySQL: String Driver="com.mysql.jdbc.Driver"; //驱动程序 String URL="jdbc:mysql://localhost:3306/db_name"; //连接的URL,db_name 为数据库名 String Username="username"; //用户名 String Password="password"; //密码 Class.forName(Driver).new Instance(); Connection con=DriverManager.getConnection(URL,Username,Password); Microsoft SQL Server: 1) String Driver="com.microsoft.jdbc.sqlserver.SQLServerDriver"; //连接SQL数据库的方法 String URL="jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=db_name"; //db_name为数据库名 String Username="username"; //用户名 String Password="password"; //密码 Class.forName(Driver).new Instance(); //加载数据可驱动 Connection con=DriverManager.getConnection(URL,UserName,Password); // 2) String Driver="com.microsoft.sqlserver.jdbc.SQLServerDriver"; //连接SQL数据库的方法 String URL="jdbc:sqlserver://localhost:1433;DatabaseName=db_name"; //db_name为数据库名 String Username="username"; //用户名 String Password="password"; //密码 Class.forName(Driver).new Instance(); //加载数据可驱动 Connection con=DriverManager.getConnection(URL,UserName,Password); Sysbase: String Driver="com.sybase.jdbc.SybDriver"; //驱动程序 String URL="jdbc:Sysbase://localhost:5007/db_name"; //db_name为数据可名 String Username="username"; //用户名 String Password="password"; //密码 Class.forName(Driver).newInstance(); Connection con=DriverManager.getConnection(URL,Username,Password); Oracle(用thin模式): String Driver="oracle.jdbc.driver.OracleDriver"; //连接数据库的方法String URL="jdbc:oracle:thin:@loaclhost:1521:orcl"; //orcl为数据库的SID String Username="username"; //用户名 String Password="password"; //密码 Class.forName(Driver).newInstance(); //加载数据库驱动

实验2 数据库的创建和管理

实验2 数据库的创建和管理 学号: 2011193158 姓名:韩江玲 一、实验目的: 1、掌握使用企业管理器创建SQL Server数据库的方法; 2、掌握使用T-SQL语言创建SQL Server数据库的方法; 3、掌握附加和分离数据库的方法; 4、掌握使用企业管理器或存储过程查看SQL数据库属性的方法; 5、熟悉数据库的收缩、更名和删除; 6、掌握使用企业管理器或sp_dboption存储过程修改数据库选项的方法。 二、实验内容和步骤: 本次实验所创建数据库(包括数据库文件和事务日志)存放位置都为“D:\TestDB”。因此首先在D盘下新建文件夹TestDB。 1. 数据库的创建 创建数据库的过程实际上就是为数据库设计名称、设计所占用的存储空间和文件存放位置的过程。 实验内容1:使用SQL Server企业管理器创建一个数据库,具体要求如下: 1)数据库名称为Test1。 2)主要数据文件:逻辑文件名为Test1_Data1,物理文件名为Test1_Data1.mdf,初始容量为1MB,最大容量为10MB,递增量为1MB。 3)次要数据文件:逻辑文件名为Test1_Data2,物理文件名为Test1_Data2.ndf,初始容量为1MB,最大容量为10MB,递增量为1MB。 4)事务日志文件:逻辑文件名为Test1_Log,物理文件名为Test1_Log.ldf,初始容量为1MB,大容量为5MB,递增量为1MB。其他选项为默认值。

注:我在创建数据库的时候,系统要求主文件(Test1_data1和Test1_data2)的大小不能小于3MB,所以在本例中我设置的主文件的初始大小均为3MB 实验内容2:用Transact-SQL(T-SQL)语句创建数据库,实验步骤:启动“查询分析器”,在编辑窗口输入SQL语句。 用T-SQL语句创建一个名为teach的数据库,它由5MB的主数据文件、2MB 的次数据文件和1MB的日志文件组成。并且主数据文件以2MB的增长速度增长,其最大容量为15MB;次数据文件以10%的增长速度增长,其最大容量为10MB;事务日志文件以1MB增长速度增长,其最大日志文件大小为10MB。运行完语句后,仔细查看结果框中的消息。 提示:在查询分析器中输入如下SQL语句。 CREATE DATABASE teach On (name= teach_data1, filename= 'd:\TestDB\teach_data1.mdf ', size=5,

动态连接库和符号(symbol)

动态连接库和符号(symbol) shared library (.so) "Program Library Howto-Shared Libraries"是很好的材料, 下面的内容多是据此整理的. 定义: Shared libraries are libraries that are loaded by programs when they start. 使用shared library(共享库)会有很多好处, 比如软件升级, 不难想象. 命名约定: 1. soname: 每个共享库都有一个soname, 形式为"libNAME.so.x", 其中x是版本号. 如"libc.so.6". 2. real name: 真正的库文件, 一般形式为"soname.y[.z]", 即"libName.so.x.y[.z]", 其中y是minor number, z是release number, 是可选的. 如"libattr.so.1.1.0". 3. linker name: compiler用来请求库时使用的名字, 一般是没

有版本号的soname. 放置位置& load/preload: 共享库一般放在一些约定的目录下, 如/usr/lib/, /usr/local/lib, /lib/等. 这其实是遵循FHS的, 比如/usr/local/lib下放置的一般是用户开发的库. 在启动程序时, program loader(ld-linux.so.x)会找到并加载程序需要的共享库, loader查找的路径一般就是上述的几个目录, 这些目录在/etc/ld.so.conf文件中配置. 如果只想覆盖共享库的某几个函数, 保持其余函数不变, 则可以将共享库名字和函数名字输入到/etc/ld.so.preload中, 这里面定义的规则会覆盖标准规则. cache arrangement & ldconfig 实际上, 在启动程序时再去搜寻所需的共享库不是高效做法, 所以loader使用了cache. ldconfig的作用就是读取文件 /etc/ld.so.conf, 在各个库目录中, 对共享库设置合适的symbolic link(使得遵守命名约定), 然后写入某种数据到 /etc/ld.so.cache, 这个文件再今后就被其他程序使用, 从而大幅提升了共享库的查找速度.

C语言程序静态库和动态库的创建及其应用

C语言程序静态库和动态库的创建及其应用 在用c写程序时,很多时候需要存储一些简单的数据,如果为此而用mysql数据库就有些大才小用了,可以把这些数据以结构的形写入文件,然后再需要时读取文件,取出数据。 如下是定义函数的源文件和头文件: 源文件struct.c: #include "struct.h" //第一个参数是要写入的文件名,第二个参数是缓冲区,第三个参数是缓冲区大小,第四个参数是打开文件流的形态,返回TRUE表示写入成功,返回FALSE表示写入失败int writeStruct(const char *fileName,char *buffer,int bufferLen,char *mode){ int ret; FILE *fileID = NULL; fileID = fopen(fileName,mode); if (fileID == NULL){ perror("fopen"); goto writeEnd; } rewind(fileID); ret = fwrite(buffer,bufferLen,1,fileID); if (ret <= 0){ perror("fwrite"); goto writeEnd; } if (fileID != NULL){ fclose(fileID); fileID = NULL; } return TRUE;

writeEnd: if (fileID != NULL){ fclose(fileID); fileID = NULL; } return FALSE; } //第一个参数是要读取的文件名,第二个参数是缓冲区,第三个参数是缓冲区大小,第四个参数是打开文件流的形态,返回TRUE表示读取成功,返回FALSE表示读取失败int readStruct(const char *fileName,char *buffer,int bufferLen,char *mode){ int ret; FILE *fileID = NULL; fileID = fopen(fileName,mode); if (fileID == NULL){ perror("fopen"); goto readEnd; } rewind(fileID); memset(buffer,0,sizeof(buffer)); ret = fread(buffer,bufferLen,1,fileID); if (ret >= 0){ strcat(buffer,"\0"); }else{ perror("fread") ; goto readEnd; } if (fileID != NULL){ fclose(fileID); fileID = NULL; }

通过ADO连接各种数据库的字符串

通过ADO连接各种数据库的字符串 在网络编程过程中,ADO(Activex Data Object)编程往往是不可少的工作,特别是开发电子商务网站。既然讲到ADO数据对象,那么就顺便简单地介绍一下ADO数据对象及其功能。ADO数据对象共有七种独立的对象,他们分别是连接对象(Connection)、记录集对象(RecordSet)、域对象(Field)、命令对象(Command)、参数对象(Parameter)、属性对象(Property)和错误对象(Error)。功能请参见附录1。 ADO数据对象用来连接数据库的方式有两种,分别是ODBC和OLE DB方式,下面就分别介绍用这两种方式连接数据库的例子。 第一种:ODBC DSN-Less Connections ODBC Driver for Access ODBC Driver for dBASE ODBC Driver for Excel ODBC Driver for MySQL ODBC Driver for Oracle ODBC Driver for Paradox ODBC Driver for SQL Server ODBC Driver for Sybase ODBC Driver for Sybase SQL Anywhere ODBC Driver for Text ODBC Driver for Teradata ODBC Driver for Visual FoxPro 第二种:OLE DB Data Provider Connections OLE DB Provider for Active Directory Service OLE DB Provider for DB2 OLD DB Provider for Internet Publishing OLE DB Provider for Index Server OLE DB Provider for Microsoft Jet OLE DB Provider for ODBC Databases OLE DB Provider for Oracle (From Microsoft) OLE DB Provider for Oracle (From Oracle)

相关主题
文本预览
相关文档 最新文档