ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
楼主: c001q

XLL文档翻译

[复制链接]

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-6-19 11:01 | 显示全部楼层
本帖已被收录到知识树中,索引项:插件开发
===================
开发 DLL
===================

库是一个编译后的代码文件,它为可执行应用程序提供一些功能和数据。库分为静态链接和动态链接,根据惯例库文件通常拥有 .lib 和 .dll 扩展名。静态库(如 C 运行时库)在编译时链接到可执行应用程序,并成为应用程序的一个组成部分。而DLL 只会在需要的时侯由应用程序读取。一个DLL 可以动态链接到另一个 DLL。

------------------
使用 DLL 的好处
------------------

使用 DLL 包含以下几种好处:

* 所有应用程序可以都使用硬盘上的同一个副本文件。
* 可执行应用程序,可以拥有体积更小。
* 让可以大型的开发项目分解成一些小的项目。 应用程序和DLL开发商,只需为应用和DLL约定一个接口。接口由DLL暴露出来。
*为了让程序拥有更好的性能以及修复BUG。 DLL 开发商只需要更新DLL而不是整个实用程序。以上操作的前提条件是,DLL的接口没有改变。

你可以使用DLL添加 工作表函数和命令到 Excel。

------------------
创建DLL的资源
------------------

为创建一个DLL,你需要以下内容:

* 一个源代码编辑器
* 一个编译器,将源代码转换成与你硬件相匹配的机器语言。
* 一个链接器来连接静态库或程序运行过程中连接是可执行DLL文件。

现代可集成开发环境,如 Microsoft Visual Studio,支持以上的所有内容。同时它们还提供其它更多的有用特性:智能编辑器,代码调试工具,多项目管理工具,新项目向导,以及别的一些十分重要的工具。

你可以使用几种语言创建 DLL,如:c、c++、Pascal、Visual Basic。考虑到 Excel提供的 API 源码是 C 和 C++,在本文档中就只考虑这两种语言。

------------------
输出函数和命令
------------------

当编译 一个DLL项目时,编译器和链接器需要知道那些函数需要输出以便它们可以应用到应用中。本部分将描述怎样实现这种操作。

当编译器编译源代码时,通常会改变源码中的原有的函数名。通常会将改变添加到名称的开始或结束处,这一过程称为名称修饰。你需要保证函数的输出名称,在读取DLL时是可以辨认的。这可能意味通过链接器,为修饰过的函数设置一个简单的函数名。输出函数名可以命名为原代码中的函数名,也可以命名为其它名字。

修饰函数名的方法取决于所使用的编程语言,以及通知编译器需要进行什么操作,这称为调用约定。WinAPI约定被认为是用于DLL的Window的标准进程间调用约定,WINAPI定义在Windows 头文件中 。它们使用 __stdcall 依次定义。

用于Excel使用的DLL输出函数(无论是工作表函数,宏表等价函数或用户定义命令),总是使用 WINAPI/__stdcall 调用约定。它必需在函数定义中明确包含 WINAPI ,Win32 编译器默认使用 __cdecl 调用约定,如果没有指定的话,它也可以使用 WINAPIV 定义。


你可以通知链接器,那些函数需要输出,你可以下面几种方法导出函数名:

* 把函数放置在 DEF 文件中的 EXPORTS 关键字后面,设置你的 DLL 项目选项在链接时引用这个文件。
* 在函数定义中使用 __declspec(dllexport) 说明符
* 使用 #pragma 预处理指令通知链接器进行相关处理。

尽管在你的项目中,这三种方法都可以使用,并且你的编译器和链接器都支持它们,但你不应该在导出一个函数时同时使用这三种方法。

----------------------------------
使用 DEF 文件
----------------------------------

double WINAPI my_C_export(double x)
{
/* Modify x and return it. */
    return x * 2.0;
}

double WINAPI my_Cpp_export(double x)
{
// Modify x and return it.
    return x * 2.0;
}

***********
DLL文件
***********
DEF 文件中需要包含这些行

EXPORTS
    my_C_export = _my_C_export@8
    my_Cpp_export

跟在 EXPORTS 声明后的 单行的标准语法如下所示:

entryname[=internalname] [@ordinal[NONAME]] [DATA] [PRIVATE]

注意 C 函数是修饰过的,但DEF文件强制输出函数使用源代码中的原始的函数名(见实例)。链接器会隐式的使用 C++  函数的原始函数名进行输出,因此不需要在DEF文件中包含修饰名。

32位 Windows API 函数调用,C 编译器装饰约定如下: function_name 变为 _function_name@n 这儿的 n 是多字节数字,表示为接收所有参数的数量,此数量为4的倍数。

注意:

在 Win32中所有指针都是4位长度。返回的类型不会对名称修饰造成影响


可以强制 C++ 编译器输出未修饰的 C++ 函数,任何函数原型,包含在 "C" {…} 块中,如同本实例所呈现的那样。

extern "C"
double WINAPI my_undecorated_Cpp_export(double x)
{
// Modify x and return it.
    return x * 2.0;
}

当你放置 C 函数原型在头文件中,那么它只能包含在 C 或 C++ 源代码中。可应该包含以下预处理指令。

#ifdef __cplusplus
extern "C" {
#endif

double WINAPI my_C_export(double x);
double WINAPI my_Cdecorated_Cpp_export(double x);

#ifdef __cplusplus
}
#endif

----------------------------------
使用 __declspec(dllexport) 说明符
----------------------------------

__declspec(dllexport) 关键字可用于函数声明,就像下面所示的那样。

__declspec(dllexport) double WINAPI my_C_export(double x)
{
/* Modify x and return it. */
    return x * 2.0;
}

__declspec(dllexport) 关键字必需添加在声明的最左边,此方法的优点是,函数不需要列举在 DEF 文件中。它是否能输出是取决于你是否正确的进行声明。

如果你想避免 C++ 函数被重命名,你必需想以下这样声明 函数。

extern "C"
__declspec(dllexport) double WINAPI my_undecorated_Cpp_export(double x)
{
// Modify x and return it.
    return x * 2.0;
}

链接器将认为 my_undcorated_Cpp_export 是可用的。就是说会直接使用源码中的原始函数名。

-------------------------------
使用 #pragma 预处理链接指令
-------------------------------

最新的 Microosoft Visual Studio 支持2个预处理宏,结合 #pragma 指令可以让你从源码中输出函数。这些宏是 __FUNCTION__ 和 __FUNCDNAME__(注意这里有两个下划线),它们分别对应 未修饰的和修饰的函数名。

例如,当你使用 Microsoft Visual Studio ,下面这些代码行可以放在共同的头文件中,

#if _MSC_VER > 1200 // Later than Visual Studio 6.0
    #define EXPORT comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
#else // Cannot use this way of exporting functions.
    #define EXPORT
#endif // else need to use DEF file or __declspec(dllexport)


在包含上面头文件的示例代码中可以输出二个函数。

double WINAPI my_C_export(double x)
{
#pragma EXPORT
/* Modify x and return it. */
    return x * 2.0;
}


double WINAPI my_Cpp_export(double x)
{
#pragma EXPORT
// Modify x and return it.
    return x * 2.0;
}

注意指令必需放置在函数的内部,并且没有设置编译器的 /EP 和 /P 选项。只有这样,宏才能正确的展开。此技巧可以让你不在需要使用 DEF 文件,或 __declspec(dllexport) 声明,可以保持函数代码输出状态的规范化。


////////////////////////////////////////////////
译者注
    本节内容,简单来说,就是从DLL中输出函数有三种方法,分别是 DEF文件,__declspec(dllexport) 声明,和使用 __FUNCTION__ 和 __FUNCDNAME__
////////////////////////////////////////////////







TA的精华主题

TA的得分主题

 楼主| 发表于 2016-6-23 11:11 | 显示全部楼层
本帖最后由 c001q 于 2016-6-25 11:45 编辑

============================
如何在Excel中使用DLL
============================
你可以使用以下几种方法在Excel中使用DLL函数或命令:
* 在 VBA 代码模块,使用 Declare 语句调用DLL函数或命令。
* 在 XLM 宏表 通过使用CALL 或 REGISTER 函数。
* 来自工作簿的指令 或用户界面(UI)的自定义项目指令。
此文档不包括 XLM 函数。我们推荐你使用另外两种办法。
来自工作簿的指令 或用户界面(UI)的自定义项目指令,其函数和命令必需首先注册到Excel中。关于注册命令和函数,请查看之后的 《在Excel中使用XLL代码》 一节。
-------------------------------------
从VBA中调用DLL函数和命令
-------------------------------------
你可以在VBA中使用 Declare 语句来调用 DLL 中的函数和命令,此语句在调用 命令和函数时有分别有两种不同的语法:
[Public | Private] Declare Sub name Lib "libname" [Alias "aliasname"] [([arglist])]
[Public | Private] Declare Function name Lib "libname" [Alias "aliasname"] [([arglist])] [As type]
Public 和 Private 关键字,指定了函数的输入范围:用于整个VB项目,或只是各自的VB模块中。name 是你想在 VBA 代码中使用的函数名。如果此函数名和DLL中的不同。你必需使用 Alias "aliasname" 说明符。并给出 DLL 中输出的函数名。如果你想通过DLL序号来使用 DLL函数,你可以提供 alias name。它使用#顺序前缀。
命令 会返回void, 函数返回是 ByVal(值传递)类型。包含了 strings、arrays、user-defined、object。
注意:
VBA 不检查VB模块中,引用的DLL函数中的参数列表和返回数据类型,你要小心检查这些内容,因为一个错误就可能导致Excel的崩溃。
当函数或命令参数不是指针或引用传递,就必需在参数声明时使用 ByVal 关键字。当 C或C++ 函数取得引用参数,它必需通过 ByRef。关键字 ByRef 可以从参数列表中省略,因为它们VBA的默认选项。
-----------------------------------------
C/C++ 和 VBA 参数类型
-----------------------------------------
你应该注意比较  C/C++ 和 VBA 中的参数类型声明
* VBA String 使用 ByVal声明时,是通过字节字符 的BSTR 结构指针来实现,使用 ByRef 就会使用用一个指针的指针。
* VBA Variant 包含的字符串,是由 ByVal 声明的一个 Unicode 宽字符 BSTR 结构指针,或 ByRef 声明的指针的指针。
* VBA Integer 是16位 类型相当于 C/C++ 中的 signed short 类型。
* VBA Long 是32位类型,等效于 C/C++ 中的 signed int 类型。
* VBA 和 C/C++ 都允许用户定义用户定义类型,使用 Type 或 struct 分别声明。
* VBA Currency 数据类型,当使用ByVal 声明时通过 CY结构,定义在Windows头文件 wtypes.h中,使用 ByRef 声明是指针的指针。
在VBA,用户定义数据类型的数据元素,采用4个字节处理。然而在 Visual Studio中,默认情况下,它们使用 8个字节处理。因此,你需要使用 #pargma pack(4) ... #pragma pack() 块包住 C/C++ 结构定义,避免数据长度不一至。
下面是等价的用户类型定义。
---- VB ----
Type VB_User_Type
    i As Integer
    d As Double
    s As String
End Type
---- C++ ----
#pragma pack(4)
struct C_user_type
{
    short iVal;
    double dVal;
    BSTR bstr; // VBA String type is a byte string
}
#pragma pack() // restore default

在某些情况 VBA 支持值范围比 EXCEL 要大。VBA 支持的双精度符合 IEEE 标准。支持小于正常范围的数字,一般会舍入到零。 VBA日期类型表示的最早日期可以到 100年 9月 1日,Excel 只允许大于或等为零的 序列化日期。VBA Currency 类型,为64位长的 integer 类型,其精度超过 8字节的 双精度类型,所以和工作表是不匹配的。
Excel 只能通过以下的类型变体来定义函数

VBA 数据类型C/C++ 位状态变体类型描述
DoubleVT_R8
BooleanVT_BOOL
DateVT_DATE
StringVT_BSTR OLE Bstr byte string
RangeVT_DISPATCH Range 和 cell 引用
Variant containing an array VT_ARRAY | VT_VARIANTLiteral arrays
CcyVT_CY 64-bit integer scaled to permit 4 decimal places of accuracy.
Variant containing an errorVT_ERROR
VT_EMPTY 空单元格,或有限参数


你可以在VBA中使用 VarType 检查传入的变体类型,函数调用引用时将返回 range 类型。确定 Variant 是 Range 引用对象,你可以使用 IsObject 函数。
你可以通过给 Range 的 Value 属性 分配为 Variants 类型来创建 Variants 数组。任何区域中的单元格,设置为区域中表示的货币格式,都会转换成 货币数组元素,任何格式化为日期的单元格都被转换成 日期数组元素。单元格中包含的字符转,会转换成 宽字符 BSTR 可变类型。单元格包含错误,转换为 VT_ERROR 变体。 单元格包含 Boolean 类型的 True 或 False 会转换成 类型 VT_BOOL 变体

注意:
Variant_bool (感觉liucqa意见,此处已改) 会将 True 当作 -1 将 False 当作0. 数字不格式化为 日期或货币类型,会转换成 VT_RB 变体类型。

点评

哦,文档中说的是VT_BOOL类型,只在这个类型中true才用-1来表示。 小写的bool 的是C++ 标准定义的布尔类型,大写的BOOL 是Windows 里面定义的布尔类型。VARIANT_BOOL 是COM 使用的布尔类型, 其实质上是 short 。  发表于 2016-6-25 07:28
一般来说 true=1  发表于 2016-6-23 12:31

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-6-24 16:15 | 显示全部楼层
======================================================
创建 XLL 和 DLL ,并在EXCEL中调用 DLL中的函数和指令
======================================================

如果你的DLL是独立的或依赖别的库的库文件。你必需知道如何才能使用 Excel 访问这些函数和命令。更多信息,查看 如何在Excel中访问DLL。

如果你的 DLL 需要访问 Excel 功能。例如,获取单元格中的内容、访问一个工作表函数,或分析Excel获取工作空间信息,就要求你的代码必需可以回调 Excel 功能。

Excel C-API 提供了数个函数,可以让 DLL 回调Excel中的功能。访问这些函数之前,必需静态链接到 Excel 32位库,xlcall32.lib。此静态库可以从 Microsoft 下载中心下载。

------------------------------------
让 DLL 回调 Excel中的功能
------------------------------------

为了让一个 DLL 可以访问Excel中的功能,获取或设置工作空间信息,必需首先获取 Excel 回调函数 Excel4、Excel4V、Excel12 和 Excel12v 的地址。第二步,在将DLL引进到 Excel 2007 中。访问所有这些功能,DLL 项目必需包含以下文件引用。如果你只想访问头二个回调,你的只需要包含二个文件。

----------
xlcall.h
----------

xlcall.h 包含以下内容

* 所有回调函数的函数原型
* 定义数据结构,回调函数用它来交换 DLL/XLL 和 Excel 的数据。
* 工作表的 C-API 函数和命令定义,宏表函数,和被支持的 Excel 命令。
* 定义加回调函数的返回值

你需要在需要使用C-API或用于C-API的数据类型的源文件中,使用 #include 直接包含这个文件,或简接通过其它头文件包含这个文件。

--------------
xlcall32.lib
--------------

xlcall32.lib 库输出头二个回调,Excel4 和 Excel4v,以及 XlcallVer 函数。在你的项目中没有引这个库,如果你在代码中使用任意的回调函数,链接器无法成功创建 XLL。

---------------
xlcall.cpp
---------------

Excel 2007 回调 Excel12 和 Excel 12v 不会在 xlcall32.lib 中输出。Excel 2007 XLL 项目项目创建后可用于 Excel 早期版本。 xlcall.cpp 模块包含 Excel21 和 Excel12v 函数代码,如果你运行 Excel 2007 它就会调用 Excel 入口指针,或者在你运行早期版本的Excel时,返回安全的错误值。如果你想创建的XLL运行于Excle2007,并可以处理大的网格数据类型和长的 Unicode 字符串,你需要在你的项目中包含这个模块。

------------------------------------
将 DLL 转换为 XLL:添加管理接口函数
------------------------------------

XLL是一个DLL文件,它输出几个程序可以被 Excel 或 Excel外接程序管理器 调用。在这里,会对这些程序进行一个简单的描述,更详细的内容,请参考 Add-in Manager and XLL Interface Functions。所有这些 DLL 回调函数,以 xlAuto 作为前缀。有一个命令 xlAutoOpen 是必需的。它会在 add-in 被激活时调用,它常常用于注册 XLL 函数 和 Excel 命令,以及进行一些其它的初始化任务。xlAuto 函数签名以及实现实例,会在后面的内容提供。

你的 add-in 中所有输出函数都依赖于 xlAutoOpen 函数,这是一个必需的回调函数。

Excel 2007 针对大网络 和 Unicode字符串,引入了一个新的数据类型,XLOPER12,它会在后面的主题中进行介绍。鉴于 xlAuto 函数获取或返回 老的数据类型 XLOPER,在 Excel 2007中引入了新版的 XLOPER12s, 除了 xlAutoFree12 外,有时我们必须避免 XLOPER12 发生内存泄漏。在 Version 12 中 xlAuto 函数可以安全的省略掉。

----------------
xlAutoOpen
----------------

Excel 调用 xlAutoOpen 函数,不管什么时候,XLL 都是激活的。 add-in 会在开始一个Excel会话时被激活,如果在上一次会话中被激活,并正常退出的话。add-in 会在Excel 会话期间被读取。add-in 可以在Excel 会话期间,去激活和重激活,函数会在 重激活时被调用。

xlAutoOpen 函数建议放在 注册 XLL 函数或命令,初始化数据结构,定制用户接口等等。

如果你的 add-in 工具输出 xlAutoRegister 函数或 xlAutoRegister12 函数,Excel 会不会首先尝试先调用 xlAutoOpen 函数激活和注册函数和命令。 在这种情况下,你要确保你的函数和命令可以正常工作,你必需完成初始化。如果没有,你就要完成初始化,否则注册函数和命令就会失败

----------------
xlAutoClose
----------------

Excel 调用 xlAutoClose 函数,不管怎样,XLL都会被去激活。 add-in 会在Excel 会话正常结束时,去激活。如果在 Excel 会话期间去激活,此函数就会被调用。

xlAutoClose函数 建议放在 去激活函数函数和命令中,释放资源时、撤销定制等等。把它分配在 去激活函数命令,可以看看 Known Issues in Excel XLL Development.一节中的描述。

-----------------
xkAutoAdd
-----------------

Excel 调用  xlAutoAdd 函数,不论何时, 用户都会在Excel会话期间,使用 Add-In 管理器激活。这个函数不会参Excel开始时装载预安装组件时被调用调用。

此函数常常显示定制对话框,对话框中显示了那些 add-in 已经被激活。

-----------------
xlAutoRemove
-----------------

Excel 调用 xlAutoRemove 函数,无论何时,在Excel会话期间用户使用 Add-In 管理器 去激活 XLL。这个函数不会在 Excel 会话正常关闭或异常关闭时被调用安装 add-in。

此函数常常用于在显示定制对话框时,通知用户那些 add-in 去激活。


-------------------------------------------
xlAddInManagerInfo/xlAddInManagerInfo12
-------------------------------------------

无论什么时候,Excel 调用 xlAutoRegister 函数,都会注册 XLM 函数,或 C-API 等价函数 xlRegister 返回和参数类型都会缺失。它允许 XLL 查找内部的输出函数命令列表,用指定的返回和参数类型注册函数。

注意:
        如果 xlAddInRegister/xlAddInRegister12 尝试注册函数而不提供参数和返回类型,递归调用就会出现,最终导致 内存溢出和Excel崩溃。

-------------------------------------------
xlAutoFree/xlAutoFree12
-------------------------------------------

Excel 调用 xlAutoRegister 函数仅在 XLL 工作表函数返回  XLOPER/XLOPER12 数据类型和状态,告诉存在内存XLL仍然需要释放,这可以让 XLL 返回动态分配数组,字符串,外部引用到 worksheet 并且没有内存泄漏。只在 Excel 2007 和之后版本,支持 XLOPER12 数据类型。

注意:

当Excel 2007 设置为使用多线程重计算, xlAutoFree/xlAutoFree12 会在同样的线种上调用,这只是用来调用一个函数并返回它。线程上调用 xlAutoFree/xlAutoFree12  总是在后续的 worksheet cells 评估之前。这在你的XLL中是线程安全的。

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-6-24 16:19 | 显示全部楼层
c001q 发表于 2016-6-23 11:11
============================
如何在Excel中使用DLL
============================

原文是这样的,我也没有验证过,不知道对不对。The Variant stores True as –1 and False as 0. Numbers not formatted as dates or currency amounts are converted to Variants of type VT_R8.

点评

http://blog.csdn.net/yuanjilai/article/details/7598566 具体这里有个说明。文档里指的是VARIANT_BOOL  发表于 2016-6-25 07:30

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-6-24 16:26 | 显示全部楼层
本帖最后由 c001q 于 2016-6-24 16:45 编辑

How to: Access DLLs in Excel ,这一节,我的翻译遇到很多问题,翻译挺困难的,有高手的,希望能重翻译一下。

特别是各种字符编码内容的翻译。

点评

翻译有什么技术上的困难可以联系我的qq 564955427  发表于 2016-6-25 07:13

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-6-24 16:46 | 显示全部楼层
======================================================
在 Excel 中访问 XLL 代码
======================================================

如果你的DLL是独立的或依赖别的库的库文件。你必需知道如何才能使用 Excel 访问这些函数和命令。更多信息,查看 如何在Excel中访问DLL。

如果你的 DLL 需要访问 Excel 功能。例如,获取单元格中的内容、访问一个工作表函数,或分析Excel获取工作空间信息,就要求你的代码必需可以回调 Excel 功能。

Excel C-API 提供了数个函数,可以让 DLL 回调Excel中的功能。访问这些函数之前,必需静态链接到 Excel 32位库,xlcall32.lib。此静态库可以从 Microsoft 下载中心下载。

------------------------------------
让 DLL 回调 Excel中的功能
------------------------------------

为了让一个 DLL 可以访问Excel中的功能,获取或设置工作空间信息,必需首先获取 Excel 回调函数 Excel4、Excel4V、Excel12 和 Excel12v 的地址。第二步,在将DLL引进到 Excel 2007 中。访问所有这些功能,DLL 项目必需包含以下文件引用。如果你只想访问头二个回调,你的只需要包含二个文件。

----------
xlcall.h
----------

xlcall.h 包含以下内容

* 所有回调函数的函数原型
* 定义数据结构,回调函数用它来交换 DLL/XLL 和 Excel 的数据。
* 工作表的 C-API 函数和命令定义,宏表函数,和被支持的 Excel 命令。
* 定义加回调函数的返回值

你需要在需要使用C-API或用于C-API的数据类型的源文件中,使用 #include 直接包含这个文件,或简接通过其它头文件包含这个文件。

--------------
xlcall32.lib
--------------

xlcall32.lib 库输出头二个回调,Excel4 和 Excel4v,以及 XlcallVer 函数。在你的项目中没有引这个库,如果你在代码中使用任意的回调函数,链接器无法成功创建 XLL。

---------------
xlcall.cpp
---------------

Excel 2007 回调 Excel12 和 Excel 12v 不会在 xlcall32.lib 中输出。Excel 2007 XLL 项目项目创建后可用于 Excel 早期版本。 xlcall.cpp 模块包含 Excel21 和 Excel12v 函数代码,如果你运行 Excel 2007 它就会调用 Excel 入口指针,或者在你运行早期版本的Excel时,返回安全的错误值。如果你想创建的XLL运行于Excle2007,并可以处理大的网格数据类型和长的 Unicode 字符串,你需要在你的项目中包含这个模块。

------------------------------------
将 DLL 转换为 XLL:添加管理接口函数
------------------------------------

XLL是一个DLL文件,它输出几个程序可以被 Excel 或 Excel外接程序管理器 调用。在这里,会对这些程序进行一个简单的描述,更详细的内容,请参考 Add-in Manager and XLL Interface Functions。所有这些 DLL 回调函数,以 xlAuto 作为前缀。有一个命令 xlAutoOpen 是必需的。它会在 add-in 被激活时调用,它常常用于注册 XLL 函数 和 Excel 命令,以及进行一些其它的初始化任务。xlAuto 函数签名以及实现实例,会在后面的内容提供。

你的 add-in 中所有输出函数都依赖于 xlAutoOpen 函数,这是一个必需的回调函数。

Excel 2007 针对大网络 和 Unicode字符串,引入了一个新的数据类型,XLOPER12,它会在后面的主题中进行介绍。鉴于 xlAuto 函数获取或返回 老的数据类型 XLOPER,在 Excel 2007中引入了新版的 XLOPER12s, 除了 xlAutoFree12 外,有时我们必须避免 XLOPER12 发生内存泄漏。在 Version 12 中 xlAuto 函数可以安全的省略掉。

----------------
xlAutoOpen
----------------

Excel 调用 xlAutoOpen 函数,不管什么时候,XLL 都是激活的。 add-in 会在开始一个Excel会话时被激活,如果在上一次会话中被激活,并正常退出的话。add-in 会在Excel 会话期间被读取。add-in 可以在Excel 会话期间,去激活和重激活,函数会在 重激活时被调用。

xlAutoOpen 函数建议放在 注册 XLL 函数或命令,初始化数据结构,定制用户接口等等。

如果你的 add-in 工具输出 xlAutoRegister 函数或 xlAutoRegister12 函数,Excel 会不会首先尝试先调用 xlAutoOpen 函数激活和注册函数和命令。 在这种情况下,你要确保你的函数和命令可以正常工作,你必需完成初始化。如果没有,你就要完成初始化,否则注册函数和命令就会失败

----------------
xlAutoClose
----------------

Excel 调用 xlAutoClose 函数,不管怎样,XLL都会被去激活。 add-in 会在Excel 会话正常结束时,去激活。如果在 Excel 会话期间去激活,此函数就会被调用。

xlAutoClose函数 建议放在 去激活函数函数和命令中,释放资源时、撤销定制等等。把它分配在 去激活函数命令,可以看看 Known Issues in Excel XLL Development.一节中的描述。

-----------------
xkAutoAdd
-----------------

Excel 调用  xlAutoAdd 函数,不论何时, 用户都会在Excel会话期间,使用 Add-In 管理器激活。这个函数不会参Excel开始时装载预安装组件时被调用调用。

此函数常常显示定制对话框,对话框中显示了那些 add-in 已经被激活。

-----------------
xlAutoRemove
-----------------

Excel 调用 xlAutoRemove 函数,无论何时,在Excel会话期间用户使用 Add-In 管理器 去激活 XLL。这个函数不会在 Excel 会话正常关闭或异常关闭时被调用安装 add-in。

此函数常常用于在显示定制对话框时,通知用户那些 add-in 去激活。


-------------------------------------------
xlAddInManagerInfo/xlAddInManagerInfo12
-------------------------------------------

无论什么时候,Excel 调用 xlAutoRegister 函数,都会注册 XLM 函数,或 C-API 等价函数 xlRegister 返回和参数类型都会缺失。它允许 XLL 查找内部的输出函数命令列表,用指定的返回和参数类型注册函数。

注意:
        如果 xlAddInRegister/xlAddInRegister12 尝试注册函数而不提供参数和返回类型,递归调用就会出现,最终导致 内存溢出和Excel崩溃。

-------------------------------------------
xlAutoFree/xlAutoFree12
-------------------------------------------

Excel 调用 xlAutoRegister 函数仅在 XLL 工作表函数返回  XLOPER/XLOPER12 数据类型和状态,告诉存在内存XLL仍然需要释放,这可以让 XLL 返回动态分配数组,字符串,外部引用到 worksheet 并且没有内存泄漏。只在 Excel 2007 和之后版本,支持 XLOPER12 数据类型。

注意:

当Excel 2007 设置为使用多线程重计算, xlAutoFree/xlAutoFree12 会在同样的线种上调用,这只是用来调用一个函数并返回它。线程上调用 xlAutoFree/xlAutoFree12  总是在后续的 worksheet cells 评估之前。这在你的XLL中是线程安全的。

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-6-25 11:38 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
======================================================
在 Excel 中访问 XLL 代码
======================================================

在 Excel 中访问一个 XLL 包含的功能和命令:

* 函数必需由 XLL 输出
* 必需注册到 Excel

--------------------------
注册函数和命令到Excel
--------------------------

关于 DLL入口点的登记会告诉 Excel 以下内容:

* 无论函数是否隐藏或在函数向导中是否可见。
* 无论它只能从 XML 宏表调用,或 worksheet 调用。
* 无论函数是 worksheet 函数,或等价的宏表函数。
* XLL/DLL 输出函数名是什么,你想EXCEL使用什么函数
* 如果它是一个函数:
    * 返回什么数据类型,拥有什么参数
    * 在这里更改参数后,是否返回结果
    * 是否是 易失性的
    * 是否是线程安全的
    * 文本粘贴函数向导,自动完成编辑器,将显示出来帮助用户调用函数。
    * 那个函数种类将显示列举在下面。

这些都是使用 C-API 函数 xlfRegister 实现的,相当于 XLM 函数 REGISTER。

--------------------------
从 Excel 立即调用 XLL 函数
--------------------------

一旦注册 XLL worksheet 函数 和 macro sheet 函数,就可以从任意位置调用,如:

* worksheet 中的单个单元络,或数组公式。
* macro sheet 中的单个单元格,或数组公式。
* 定义名称的定义中调用。
* 条件格式对话框中的条件和限制字段中。
* 在别的 add-in 中通过 C-API 函数 xlUDF 调用。
* 在VBA中通过 Application.Run 方法调用。

使用 C-API xlfCaller函数,你可以获取你的函数中的单元格引用。如果 函数是从一个条件表达式调用,你仍可以返回一个关联的单元格引用,因此你不能假设 单元格公式包含 XLL函数。如果你的函数从 VBA 用户定义函数 (UDF)中调用,xlfCaller 再次返回调用VBA函数的单元格地址。

注册:
    Excel 也可以从 粘贴函数向导 和 替换 对话框中调用 XLL 函数代码。在粘贴函数对话框中你可能需要限制你的代码正常运行,尤其是当你的函数需要很长的时间来执行的时候。如果你的函数是从这两个对话框中一个调用,你必需在你的项目中实现一些代码,可以循环访问所有打开的窗体去确定,如果前面的窗口是这些对话框中的一个,而且要知道是哪一个。

--------------------------
从 Excel 立即调用 XLL 命令
--------------------------

一旦注册 XLL命令,就可以用下面所有这些方法调用,其它的用户定义宏也可以调用。

* 嵌入到 worksheet 中的控件对象
* 动行宏对话框
* 在VBA中使用 Application.Run 方法
* 从用户定制菜单项目或工具栏
* 使用设置的命令快捷键
* 当一个事件被捕获时运行。

注意:
    XLL 命令,不会显示在宏对话框中。但它们可以手动输入到宏名称文本框中。Excel 需要的是在运行宏对话框中的名字,而不是 DLL 的输出名称。

所有 XLL 命令假定使用以下方式构成:

short WINAPI xll_cmd_name(void)
{
// Function code...
    return 1;
}

Excel 忽略返回值,除非由 XML 宏表调用。在这种情况下,返回值会转换为 TRUE 或 FALSE。如果命令成功,就应该返回1,否则就应该返回0,当用户取消命令,也应该返回0.

关于 使用 C-API 函数 xlfCaller 调用命令的相关信息,可以参数 xlfCaller 函数一节。

Excel 2007 用户界面早期版本有很大的不同。C-API 函数在大多数情况下允许 添加和删除 定制菜单按钮,菜单,子菜单,菜单项目,定制工具栏,工具栏按钮。然而,有时添加的项目可能会是无效的。你需要小心检查,添加的这些功能是否可用。或针对不同的版本进行制定。Excel 2007 用户界面,最好使用托管代码定制,然后可以精确耦合到你的 XLL 命令中。

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-6-25 18:03 | 显示全部楼层
======================================================
从函数向导,或替换对话框中怎样调用 XLL 函数
======================================================

Excel 通常在 workbook 的重计算期间调用 XLL 函数。牢记,函数可能不存在单元格公式中,但可能会存在于命名的单元格区域中或条件格式表达式。

在两种情况下,函数可以从 Excel 对话框中调用。一种情况是 粘贴函数参数 对话框,在这里每次用户都可以在创建函数时,调用一个参数。另一种情况是, 通过替换对话框重新编辑公式时。由于粘贴函数 对话框,你可能不想你的函数正常执行。这也许是因为需要很长的时间来执行,你不想很久才能关闭对话框。

粘贴函数对话框,和 替换对话框 拥有窗口类名 bosa_sdm_XLn,n 是数值。Windows 提供 API 函数,GetClassName ,可以从窗口句柄中获取窗口名称,此名称是一个 HWND 变量类型。Windows 还支持 函数 EnumWindows , 该函数枚举所有屏幕上的顶层窗口,并将窗口句柄传送给应用程序定义的回调函数

回调函数需要执行,只需要通过以下几步:

* 检查当前窗口的父窗口是否为当前Excel实例(指的是同时运行多个Excel实例的情况)
* 从窗口句柄获取类型名。
* 检查类型名是否为为 bosa_sdm_XLn 的形式
* 如果你需要区分两个窗口对话框,可以通过检查对话框标题文本,进行识别。标题栏可以使用 Windows API GetWindowText 函数来获取。

下面的 C++ 代码将展示 类和回调传送到 Windows 的步骤。

注意:
    未来的 Excel 版本,Windows 标题可能会发生改变,导致此代码无效。需要注意的还有,设置 windows_title_text 为 NULL,在回调查询中忽略标题的大小写效果。

#define CLASS_NAME_BUFFSIZE  50
#define WINDOW_TEXT_BUFFSIZE  50

// Data structure used as input to xldlg_enum_proc(), called by
// called_from_paste_fn_dlg(), called_from_replace_dlg(), and
// called_from_Excel_dlg(). These functions tell the caller whether
// the current worksheet function was called from one or either of
// these dialog boxes.

typedef struct
{
  bool is_dlg;
  short low_hwnd;
  char *window_title_text; // set to NULL if don't care
}
  xldlg_enum_struct;

// The callback function called by Windows for every top-level window.
BOOL CALLBACK xldlg_enum_proc(HWND hwnd, xldlg_enum_struct *p_enum)
{
// Check if the parent window is Excel.
  if(LOWORD((DWORD)GetParent(hwnd)) != p_enum->low_hwnd)
    return TRUE; // keep iterating

  char class_name[CLASS_NAME_BUFFSIZE + 1];
//  Ensure that class_name is always null terminated for safety.
  class_name[CLASS_NAME_BUFFSIZE] = 0;

  GetClassName(hwnd, class_name, CLASS_NAME_BUFFSIZE);

//  Do a case-insensitve comparison for the Excel dialog window
//  class name with the Excel version number truncated.
  size_t len; // The length of the window's title text
  if(_strnicmp(class_name, "bosa_sdm_xl", 11) == 0)
  {
// Check if a searching for a specific title string
    if(p_enum->window_title_text)
    {
// Get the window's title and see if it matches the given text.
      char buffer[WINDOW_TEXT_BUFFSIZE + 1];
      buffer[WINDOW_TEXT_BUFFSIZE] = 0;
      len = GetWindowText(hwnd, buffer, WINDOW_TEXT_BUFFSIZE);
      if(len == 0) // No title
      {
        if(p_enum->window_title_text[0] != 0)
          return TRUE; // No match, so keep iterating
      }
// Window has a title so do a case-insensitive comparison of the
// title and the search text, if provided.
      else if(p_enum->window_title_text[0] != 0
      && _stricmp(buffer, p_enum->window_title_text) != 0)
        return TRUE; // Keep iterating
    }
    p_enum->is_dlg = true;
    return FALSE; // Tells Windows to stop iterating.
  }
  return TRUE; // Tells Windows to continue iterating.
}

粘贴函数对话框,没有标题,因此接下来的函数传递的标题字符为 “”,代表这是一个空字符,通知回调函数,没有可以匹配的 标题。

bool called_from_paste_fn_dlg(void)
{
    XLOPER xHwnd;
// Calls Excel4, which only returns the low part of the Excel
// main window handle. This is OK for the search however.
    if(Excel4(xlGetHwnd, &xHwnd, 0))
        return false; // Couldn't get it, so assume not

// Search for bosa_sdm_xl* dialog box with no title string.
    xldlg_enum_struct es = {FALSE, xHwnd.val.w, ""};
    EnumWindows((WNDENUMPROC)xldlg_enum_proc, (LPARAM)&es);
    return es.is_dlg;
}

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-6-26 16:44 | 显示全部楼层
======================================================
从 DLL/XLL 调用 Excel 中的命令
======================================================
Excel 可以让你的DLL 访问 Excel 的内部命令,工作表函数,宏表函数。VBA 可以都 DLL 中的命令或函数调用,或注册的 XLL 命令和函数。

--------------------------------------------
Excel4, Excel4v, Excel12, and Excel12v 函数
--------------------------------------------
Excek 通过 Excel4 ,Excel4v ,Excel12 和 Excel12v  的回调函数来调用命令。

Excel4 和 Excel4v 函数由 Excel4 引进。它们和 XLOPER 数据结构一起使用。Excel 2007 (Excel12) 引入了两个数据的函数 Excel12 和 Excel12v,它们和 XLOPER12 数据结构一起使用。 Excel4 和 Excel4v 函数由库 xlcall32.lib 输出,它必需包含在你的 DLL/XLL 项目中。而后两者包含在 SDK C++ 源文件 xlcall.cpp 中。如果你想使用 XLOPER12s 访问 Excel 功能,你必需包含这个文件。

这个个函数的函数原型,在下面。前三个参数相同,除了,第二个参数,第一对指向 XLOPER,第二对指向 XLOPER12。在 Excel4 和 Excel12 中参数调用方式为 __cdecl。

所有版本的 Excel
int _cdecl Excel4(int xlfn, LPXLOPER operRes, int count,... );
int pascal Excel4v(int xlfn, LPXLOPER operRes, int count, LPXLOPER opers[]);

只用于 Excel2007版本及后续版本
int _cdecl Excel12(int xlfn, LPXLOPER12 operRes, int count,... );
int pascal Excel12v(int xlfn, LPXLOPER12 operRes, int count, LPXLOPER12 opers[]);

由于 DLL 可以调用 Excel4, Excel4v, Excel12, and Excel12v ,DLL 因此可直接控制 Excel。也就是说 C-API 回调函数可以被以下情况调用:

*  通过 XLL 命令,Excel 功能可以被直接调用,或经由VBA调用。
*  通过 XLL 工作表或宏表函数,Excel 功能可以被直接调用,或经由VBA调用。

你不可以从以下方式调用 Excel C-API:

* 从操作系统事件调用,如 DllMain
* 用你的 DLL 后台线程调用。


返回值

这 四个函数的返回值是 interger 数据类型,无论 调用者是函数还是命令,成功或是失败。返回值可以是下面中的任意一个:

返回值 xlcall.h 头文件中的定义 描述
0 xlretSuccess 函数或命令执行成功。所谓的成功,并不意味着,任何错误都不会产生。例如 Excel4 当调用函数 FIND 时,即使由于查找的字符不存在返回了 #VALUE!信息,函数还是会返回 xlretSuccess。你应该检查 返回的 XLOPER/XLOPER12 类型和值。
1 xlretAbort 当用户按下 ESC 停止命令宏的运行,就会返回这个值
2 xlretInvXlfn
提供的函数或命令是无效的。当调用的函数无权限调用函数命令时,此错误就会产生。例如,一个工作表函数不能调用宏表信息函数或命令函数。
4 xlretInvCount 提供的实参不正确
8 xlretInvXloper
一个或多个实参 XLOPER/XLOPER12s 不能正确的形成或填充
16 xlretStackOvfl Excel 检测到堆栈内存溢出风险,不能调用函数。会返回此错误。
32 xlretFailed
命令或函数返回值无法识别函数或命令的返回值。例如一个操作需要大量内存,就会发生错误。当尝试使用 xlCoerce 将一个大引用转换为 xltypeMulti 数组 也会发生此错误
64 xlretUncalced 当一个操作尝试恢复未计算的单元格。 为了保持重计算的完整性,工作表函数不被允许执行此操作。然而 被注册为宏表函数的命令和函数,被允许访问未计算单元格值。
124 xlretNotThreadSafe (只在 Excel 2007 和后续版本有效。)一个被注册为线程安全的 XLL 工作表函数,如果调用 C-API 函数,它将变不再是纯种安全。例如,一个线程安全的函数,不能调用 XLM 函数 xlfGetCell。


TA的精华主题

TA的得分主题

 楼主| 发表于 2016-6-26 18:52 | 显示全部楼层
本帖最后由 c001q 于 2016-6-26 18:54 编辑

如果在表格中函数 返回了一个错误的代码,它就无法返回 xlretSuccess, 其返回值 XLOPER/XLOPER12 将被设置为 #VALUE!。在某种情况下,需要进行足够的测试,以保证结果的正确性。但是你要注意,调用会返回 xlretSuccess 也可以返回  #VALUE!

如果一个C-API的调用结果是 xlretUncalced 或 xlretAbor 中的一个,你的 DLL/XLL 代码将返回控制Excel,

your DLL/XLL code should return control to Excel before making any other C API calls other than calls to xlFree to release Excel-allocated memory resources in XLOPERs and XLOPER12s.

在进行某它的任何 C-API 调用之前,你的 DLL/XLL 代码将返回控制到EXCEL,除了调用 xlFree 释放 Excel 分配的在XLOPER 和 XLOPER12s 中的内存资源。

-------------------------------------------------
命令和函数列举参数:xlfn
-------------------------------------------------

xlfn 参数是回调函数的第一个参数,它是32位整数型(integer),它的值是定在 SDK 头文件 xlcall.h 中的 函数和命令常量。


/* Excel function numbers */
#define xlfCount 0
#define xlfIsna 2
#define xlfIserror 3
#define xlfSum 4
#define xlfAverage 5
#define xlfMin 6
#define xlfMax 7
#define xlfRow 8
#define xlfColumn 9
#define xlfNa 10
...

/* Excel command numbers */
#define xlcBeep (0 | xlCommand)
#define xlcOpen (1 | xlCommand)
#define xlcOpenLinks (2 | xlCommand)
#define xlcCloseAll (3 | xlCommand)
#define xlcSave (4 | xlCommand)
#define xlcSaveAs (5 | xlCommand)
#define xlcFileDelete (6 | xlCommand)
#define xlcPageSetup (7 | xlCommand)
#define xlcPrint (8 | xlCommand)
#define xlcPrinterSetup (9 | xlCommand)
...


虽然工作表和函数表函数的值范围在 0x0 到 0x0fff 之间,但是在Excel 2007中能用的最大值只有484(0x01e4 十六进制),

所有命令函数的值范围是 0x8000 到 0x8ffff,但在 Excel 2007 中使用的最大值为 0x8328 (xlcHideallInkannots)。

它们在头文件中定义为 (n | xlCommand),n 表示十六进制的大于或等于0的数字, xlCommand 定义为 十六进制的 0x8000。

---------------------------
调用 Excel命令对话框
---------------------------

一些命令相当于 Excel中,一些对话框支持的操作。例如,xlcFileDelete 带有以下参数,一个文件名称或掩码。可以被对话框调用,用户有机会退出或编辑删除操作。它也可以不通过对话调用,在这个情况下,文件的删除,不会有一更多的交互步骤,要假设文件存在,并且调用是许可的。在对话框中调用这些命令,命令枚举必需是位模式。

此实例,将删除自身目录下的所有 my_data*.bak 文件。只当参数设置为 true 时,才显示对话框。

bool delete_my_backup_files(bool show_dialog)
{
    XLOPER12 xResult, xFilter;
    xFilter.xltype = xltypeStr;
    xFilter.val.str = L"\014my_data*.bak"; // String length: 14 octal
    int cmd;

    if(show_dialog)
        cmd = xlcFileDelete | xlPrompt;
    else
        cmd = xlcFileDelete;

// xResult should be Boolean TRUE if successful in which
// case return true, otherwise false.
    return (Excel12(cmd, &xResult, 1, &xFilter) == xlretSuccess
        && xResult.xltype == xltypeBool
        && xResult.val.xbool == 1);
}

---------------------------
在国际版本中调用函数和命令
---------------------------

你可以配置让 Excel 使用各种语言显示函数或 XLM 命令名称。一些 C-API 命令和函数操作的字符串,会被当作函数和命令的名称。例如,xlcFormula 带有一个字符串参数,标定它放置在指定的单元格。为了让你的 add-in 工作于所有的语言中,你可以提供英文字符串名称,并设置 0x2000 到 函数和命令枚举中。

下面的实例放置在A2 单元格同中,与 =SUM(X1:X100) 是等效的。注意这里使用框架函数TempActiveRef 创建临时的外部引用 XLOPER。公式会以当前语言显示在 A2单元格中,例如 以法语显示 =SOMME(X1:X100)
int WINAPI InternationlExample(void)
{
    XLOPER12 xSum, xResult;
    xSum.xltype = xltypeStr;
    xSum.val.str = L"\015=SUM(X1:X100)";
    Excel12(xlcFormula | xlIntl, &xResult, 2,
        &xSum, TempActiveRef(2,2,1,1));
    return 1;
}

您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-24 18:25 , Processed in 0.051795 second(s), 8 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表