Slots and Interfaces介绍(内核开发必看)

Each SfxShell class has a static member called SfxInterface. It is an object containing an array of SfxSlot structs. Each “slot” represents a command supported by the context (shell) it is assigned to. So the complete SfxInterface defines the functionality an instance of the particular SfxShell class using this interface provides.

每个 SfxShell 类都有一个名为 SfxInterface 的静态成员。 它是一个包含 SfxSlot 结构数组的对象。 每个“槽”代表一个命令,它被分配到的上下文(shell)支持。 因此,完整的 SfxInterface 定义了使用此接口的特定 SfxShell 类的实例提供的功能。

The SfxSlot struct
The “slot” struct contains all necessary information that the SfxDispatcher needs to work together with the Shell object containing the slot. The slot contains function pointers that the dispatcher can call to either execute the command represented by the slot or get status information about it. Thus the dispatcher doesn’t need to know the concrete implementation classes of the shells, it can work with them through their base class (SfxShell) interface.

Other important parts of the SfxSlot struct are the “name” of the slot and the numerical ID of it, its so called SlotID. Slot names have been chosen so that they match the part after the colon in “.uno:…” CommandURLs. Mapping between CommandURLs and SlotIDs to then need a simple lookup in all SfxInterfaces of the SfxShells on the shell stack of the SfxDispatcher. More about slots can be found below.

When the SfxDispatcher is asked for support of a particular command it searches for a slot with the given internal command name starting with the top most shell on the stack and then proceeding with the next one until success. This way “higher prioritized” contexts can overrule functionality from “lower prioritized” contexts. As an example, the “document” context has a generic implementation of the “Print” command, but each particular “view” context can overrule the generic behavior and define a specific way of printing.

The SfxInterface class
The slot arrays aka SfxInterfaces are static to each shell class and so each interface is a part of the data segment of the library that contains the shell code assigned to this interface. The data for these arrays needs to be predefined in source files. Writing huge arrays into C++ source files is a very tedious work and so we use a different kind of source file that is easier to edit and have a compiler (“svidl”) that takes these files and compiles them into a generated header file containing the definition of the arrays. These idl files are located in the sdi sub folders of each SFX based project and have the extension “sdi”. The generated header file contains the definitions (not only declarations) of all “interfaces” of the library and some “#define” magic allows to have each interface generated only once. More about this process below.

slot”结构包含 SfxDispatcher 与包含插槽的 Shell 对象一起工作所需的所有必要信息。插槽包含函数指针,调度程序可以调用这些函数指针来执行插槽表示的命令或获取有关它的状态信息。因此,调度程序不需要知道 shell 的具体实现类,它可以通过它们的基类 (SfxShell) 接口与它们一起工作。

SfxSlot 结构的其他重要部分是插槽的“名称”和它的数字 ID,即所谓的 SlotID。已选择插槽名称,以便它们匹配“.uno:…”CommandURL 中冒号后的部分。 CommandURLs 和 SlotIDs 之间的映射需要在 SfxDispatcher 的 shell 堆栈上的 SfxShells 的所有 SfxInterfaces 中进行简单的查找。有关插槽的更多信息,请参见下文。

当 SfxDispatcher 被要求支持特定命令时,它会搜索具有给定内部命令名称的插槽,从堆栈中最顶部的 shell 开始,然后继续下一个直到成功。通过这种方式,“较高优先级”的上下文可以否决“较低优先级”上下文中的功能。例如,“文档”上下文具有“打印”命令的通用实现,但每个特定的“视图”上下文都可以否决通用行为并定义特定的打印方式。

SfxInterface 类

插槽数组又名 SfxInterfaces 对于每个 shell 类都是静态的,因此每个接口都是库的数据段的一部分,其中包含分配给该接口的 shell 代码。这些数组的数据需要在源文件中预定义。将巨大的数组写入 C++ 源文件是一项非常乏味的工作,因此我们使用了一种不同类型的源文件,它更易于编辑,并有一个编译器(“svidl”),它接受这些文件并将它们编译成包含以下内容的生成头文件数组的定义。这些 idl 文件位于每个基于 SFX 的项目的 sdi 子文件夹中,扩展名为“sdi”。生成的头文件包含库的所有“接口”的定义(不仅仅是声明),并且一些“#define”魔法允许每个接口只生成一次。更多关于这个过程如下。

Slots, interfaces, shells, dispatchers
In OOo1.x the SfxBaseController utilized its SfxDispatcher and its stack to provide a generic Dispatch object for every supported command. It gets the CommandURL in the queryDispatch call, looks for a slot on the dispatcher stack with a suitable internal command name and in case of success creates a dispatch object and returns it. There is a drawback in this approach: if the context changes (means: shells are pushed or popped) slots might appear or disappear from the stack so commands that had been supported aren’t any longer or the other way around. This forces the DispatchProvider to request a complete refetching of all dispatch objects and so can be time consuming.

In OOo2.x we utilize the “slot pool” of the SFX based modules. This is the entirety of all “interfaces” (slot arrays) the module supports. On startup all interfaces register at the module class (SfxModule base class) so it is easy to iterate through its interfaces. Now a Dispatch Object is returned when the slot is found anywhere in the slot pool, even if the interface is not on the stack, but in this case the Dispatch Object reports its slots as disabled everytime it is used. This can be determined dynamically so context changes don’t need external updates, it’s enough to update the internal status of the already created Dispatch Objects. This approach also enables some future optimizations for the representation of slot arrays that will be discussed later.

在 OOo1.x 中,SfxBaseController 利用其 SfxDispatcher 及其堆栈为每个支持的命令提供通用 Dispatch 对象。它在 queryDispatch 调用中获取 CommandURL,使用合适的内部命令名称在调度程序堆栈上查找插槽,如果成功,则创建一个调度对象并返回它。这种方法有一个缺点:如果上下文发生变化(意味着:shell 被推送或弹出),插槽可能会从堆栈中出现或消失,因此已经支持的命令不再存在或相反。这会强制 DispatchProvider 请求完全重新获取所有调度对象,因此可能很耗时。

在 OOo2.x 中,我们利用基于 SFX 的模块的“槽池”。这是模块支持的所有“接口”(插槽阵列)的全部。在启动时,所有接口都在模块类(SfxModule 基类)中注册,因此很容易遍历其接口。现在,当在插槽池中的任何位置找到插槽时,都会返回调度对象,即使接口不在堆栈中,但在这种情况下,调度对象每次使用时都会报告其插槽已禁用。这可以动态确定,因此上下文更改不需要外部更新,更新已创建的调度对象的内部状态就足够了。这种方法还可以对稍后讨论的槽数组表示进行一些未来的优化。
Slot Processing
In former (Pre-UNO) time the slots where not only used for command dispatching but also for the implementation of our Basic API (up to StarOffice 5.2), so the slot arrays where “real” interfaces. There are a lot residues of the in the sdi files (see below), but they aren’t used anymore.

在以前(UNO 之前),槽不仅用于命令调度,还用于实现我们的基本 API(直到 StarSuite 5.2),所以槽数组是“真正的”接口。 sdi 文件中有很多 s 的残留物(见下文),但它们不再使用了。

Slot definitions
The use case explains why there are two different kinds of slots, property slots and method slots, and the difference between them can be spotted easily from the definition of a slot in the sdi file. Here are two examples, the first one describing a method slot and the second one describing a property slot:

用例解释了为什么有两种不同类型的插槽,属性插槽和方法插槽,并且可以从 sdi 文件中插槽的定义很容易地发现它们之间的区别。 这里有两个例子,第一个描述方法槽,第二个描述属性槽:

SfxVoidItem About SID_ABOUT
()
[
/* flags: */
AutoUpdate = FALSE,
Cachable = Cachable,
FastCall = FALSE,
HasCoreId = FALSE,
HasDialog = TRUE,
ReadyOnlyDoc = TRUE,
Toggle = FALSE,
Container = FALSE,
RecordAbsolute = FALSE,
RecordPerSet;
Synchron;

/* config */
AccelConfig = TRUE,
MenuConfig = TRUE,
StatusBarConfig = FALSE,
ToolBoxConfig = TRUE,
GroupId = GID_APPLICATION;

]

SfxBoolItem DesignerDialog SID_STYLE_DESIGNER

[
/* flags: */
AutoUpdate = TRUE,
Cachable = Cachable,
FastCall = TRUE,
HasCoreId = FALSE,
HasDialog = FALSE,
ReadyOnlyDoc = FALSE,
Toggle = FALSE,
Container = FALSE,
RecordAbsolute = FALSE,
RecordPerSet;
Synchron;

Readyonly = FALSE,

/* config */
AccelConfig = TRUE,
MenuConfig = TRUE,
StatusBarConfig = FALSE,
ToolBoxConfig = TRUE,
GroupId = GID_FORMAT;

]
Each slot is a block in rectangular bracket filled with attributes that is preceded by two lines that identify the slot and define its nature. The exact meaning of all the attributes is explained below, the most notable difference is seen in the second line of each block: the paranthesis of the “About” slot classify it as a method while the other one (without paranthesis) is a property. As explained above, this classification goes back to the old Basic API but besides that it’s important for the implementation of macro recording that is also based on the Dispatch API (and for SFX component it’s implemented using slots). “Method” and “property” slots are recorded differently.
The name in the middle of each first line of a block was used as the name of the property or method in the Basic API and today this name is the internal command name that is matched to any “.uno:xxx” CommandURLs of the Dispatch API. As an example, the slot named “Undo” is assigned to the “.uno:Undo” command. This matching requires that the internal command names have to be unique in the complete module. This uniqueness is verified by the svidl compiler that breaks in case if name clashes in a module.
The last part of each first line is the so called SlotID that in former times was the real identifier (not as today the internal command name) and it was also used inside the GUI element configuration files where today CommandURLs are used. So this SlotID had to be unique for the module at least, currently it’s even globally unique but at least theoretically it is now obsolete. Inside the slot processing only the textual representation of the SlotID is relevant , not its numerical value.
Property slots have a type specified by the first part of the first line. The same data in a method slot specifies the return value of the associated method. Method slots also can gave parameters (non empty paranthesis) and a return value that could be used in the old Basic API of StarOffice, but also in internal methods of the SfxDispatcher. Strictly speaking the latter is still possible but we try to get rid of this. Here are two examples for slots with arguments, with and without return value:

每个插槽都是矩形括号中的块,其中填充了属性,前面有两行标识插槽并定义其性质。下面解释了所有属性的确切含义,最显着的区别在每个块的第二行:“关于”插槽的括号将其分类为方法,而另一个(没有括号)是属性。如上所述,这种分类可以追溯到旧的 Basic API,但除此之外,它对于同样基于 Dispatch API 的宏录制的实现也很重要(对于 SFX 组件,它是使用插槽实现的)。 “方法”和“属性”槽的记录方式不同。

块的每一行中间的名称被用作基本 API 中的属性或方法的名称,今天这个名称是内部命令名称,与 Dispatch 的任何“.uno:xxx”CommandURLs 匹配应用程序接口。例如,名为“Undo”的槽被分配给“.uno:Undo”命令。这种匹配要求内部命令名称在整个模块中必须是唯一的。这种唯一性由 svidl 编译器验证,如果模块中的名称发生冲突,该编译器就会中断。

每个第一行的最后一部分是所谓的 SlotID,它在以前是真正的标识符(而不是今天的内部命令名称),它也在 GUI 元素配置文件中使用,今天使用 CommandURLs。所以这个 SlotID 至少对于模块来说必须是唯一的,目前它甚至是全球唯一的,但至少理论上它现在已经过时了。在槽处理中,只有 SlotID 的文本表示是相关的,而不是它的数值。

属性槽的类型由第一行的第一部分指定。方法槽中的相同数据指定关联方法的返回值。方法槽还可以提供参数(非空括号)和返回值,这些参数可以在 StarSuite 的旧基本 API 中使用,也可以在 SfxDispatcher 的内部方法中使用。严格来说,后者仍然是可能的,但我们试图摆脱这一点。以下是带参数的插槽的两个示例,带和不带返回值:

SfxVoidItem Undo SID_UNDO
( SfxUInt16Item Undo SID_UNDO )
[
/* flags: */
AutoUpdate = FALSE,
Cachable = Volatile,
FastCall = FALSE,
HasCoreId = FALSE,
HasDialog = FALSE,
ReadyOnlyDoc = FALSE,
Toggle = FALSE,
Container = FALSE,
RecordAbsolute = FALSE,
RecordPerSet;
Synchron;

/* status */
SlotType = SfxStringItem

/* config */
AccelConfig = TRUE,
MenuConfig = TRUE,
StatusBarConfig = FALSE,
ToolBoxConfig = TRUE,
GroupId = GID_EDIT;

]

SfxObjectItem Open SID_OPENDOC
(SfxStringItem URL SID_FILE_NAME, SfxStringItem FilterName SID_FILTER_NAME, SfxStringItem OpenFlags SID_OPTIONS,
SfxStringItem Password SID_PASSWORD, SfxStringItem FilterOptions SID_FILE_FILTEROPTIONS, SfxInt16Item Version SID_VERSION,
SfxStringItem Referer SID_REFERER)
[
/* flags: */
AutoUpdate = FALSE,
Cachable = Cachable,
FastCall = FALSE,
HasCoreId = FALSE,
HasDialog = TRUE,
ReadyOnlyDoc = TRUE,
Toggle = FALSE,
Container = TRUE,
RecordAbsolute = FALSE,
RecordPerSet;
Asynchron;

/* config */
AccelConfig = TRUE,
MenuConfig = TRUE,
StatusBarConfig = FALSE,
ToolBoxConfig = TRUE,
GroupId = GID_APPLICATION;

]
As shown in the examples, property types and return values are given as Items, the whole slot “API” is based on SfxPoolItems, each identified by its ID. These IDs must be unique only in the context of the method.
Slots are used to bind their functionality to UI elements. In former times this was done by writing the SlotIDs into the UI element configuration files, today we use CommandURLs. Binding slots to UI elements (today indirectly through the Dispatch Object) means that the binding client wants to get status information from it and/or possibly wants to execute it, in most cases (means: the standard controllers) without any parameters.
The type of the status update information is given by the SlotType attribute. For property slots like “DesignerDialog” this is identical with its property type (and so this attribute is not explictly assigned in the block), for method slots it must be specified like shown in the “Undo” example. If a method slot doesn’t specify a SlotType it has no status (except disabled or enabled). So in the examples “Undo” is of type String, “DesignerDialog” of type Boolean while the other slots don’t have a status.
A menu controller will reflect this by showing status of the “Undo” entry as its title and setting or unsetting a check mark in front of the “DesignerDialof” entry reflecting its boolean status, a toolbar controller reflects the latter one in a pressed/unpressed state, the former in different button titles (or quick help texts that replace the button title if it’s switched off). Other types of status information need speccialized controller that know how to deal with this kind of information.
All the other attributes inside the block of a slot are flags that define the behavior of the slot at runtime. Some of the attributes are contradicting (like synchron or asynchron) and so of course only one of them can be set. The table below sums them up in one line.

如示例中所示,属性类型和返回值作为 Items 给出,整个槽“API”基于 SfxPoolItems,每个都由其 ID 标识。这些 ID 必须仅在方法的上下文中是唯一的。

插槽用于将其功能绑定到 UI 元素。以前这是通过将 SlotID 写入 UI 元素配置文件来完成的,今天我们使用 CommandURLs。将槽绑定到 UI 元素(今天间接通过 Dispatch 对象)意味着绑定客户端想要从中获取状态信息和/或可能想要执行它,在大多数情况下(意味着:标准控制器)没有任何参数。

状态更新信息的类型由 SlotType 属性给出。对于像“DesignerDialog”这样的属性槽,这与其属性类型相同(因此这个属性没有在块中显式分配),对于方法槽,它必须像“撤消”示例中所示那样指定。如果方法槽没有指定 SlotType,则它没有状态(除了禁用或启用)。因此,在示例中,“撤消”是字符串类型,“DesignerDialog”是布尔类型,而其他插槽没有状态。

菜单控制器将通过将“撤消”条目的状态显示为其标题并在“DesignerDialof”条目前设置或取消设置反映其布尔状态的复选标记来反映这一点,工具栏控制器将后者反映为按下/未按下状态,前者在不同的按钮标题中(或在按钮关闭时替换按钮标题的快速帮助文本)。其他类型的状态信息需要专门的控制器,知道如何处理此类信息。

插槽块内的所有其他属性都是定义插槽在运行时的行为的标志。一些属性是矛盾的(如同步或异步),因此当然只能设置其中之一。下表将它们汇总在一行中。

Attribute
Meaning
Cachable (Volatile) Slot status is cached, sending a new status to controllers need an Invalidate() call that forces SFX to fetch a new status. Volatile slots are not cached and SFX permanently asks for status information based on a timer.
AutoUpdate TRUE: After execution of the slot SFX automatically fetches the new status, no Invalidate() call necessary
FastCall TRUE: SFX doesn’t check “enable” status before executing a slot
Toggle Execute without parameters automatically toggles the current status. Works for property slots with boolean or enum type.
HasCoreId Obsolete
HasDialog Obsolete
ReadOnlyDoc FALSE: SFX automatically disables this slot if the document is read only
Container TRUE: in case of OLE inplace editing this slot is automatically disabled by SFX if the component is the object.
FALSE: in case of OLE inplace editing this slot is automatically disabled by SFX if the component is the container.
RecordAbsolute Obsolete
RecordPerSet (RecordPerItem, NoRecord) See explanation for Recording.
Synchron (Asynchron) Asynchron: SFX execute the slot by posting a user event instead of directly calling the execute function of the slot
Readonly Obsolete
AccelConfig Slot may be offered in the configuration dialog for keyboard shortcuts
MenuConfig Slot may be offered in the configuration dialog for menus
StatusBarConfig Slot may be offered in the configuration dialog for status bar
ToolboxConfig Slot may be offered in the configuration dialog for toolbars
GroupId Assign the slot to a function group in the configuration dialogs (module is free to define categories)
ImageRotation Toolbar images are rotated in case of writing direction from right to left
ImageReflection Toolbar images are rotated in case of vertical text orientation
Interface definitions
As already mentioned, slots are bundled to interfaces and interfaces are used by shells. SDI files know both terms. Here’s an example how this looks. For clarity only a few slots are shown, the read file contains much more of them.

如前所述,插槽绑定到接口,而接口由外壳使用。 SDI 文件知道这两个术语。 这是一个示例。 为清楚起见,仅显示了几个插槽,读取的文件包含更多插槽。

interface Document : Object
[
Automation = FALSE ;
]
{
SID_CLOSEDOC
[
ExecMethod = ExecFile_Impl ;
StateMethod = GetState_Impl ;
]
}

interface OfficeDocument : Document
[
Automation = FALSE
]
{
SID_DOC_MODIFIED
[
StateMethod = GetState_Impl ;
]

SID_PRINTDOC
[
    ExecMethod = PrintExec_Impl ;
    StateMethod = NoState ;
]

}

shell SfxObjectShell

{
import OfficeDocument [AUTOMATION];

SID_DOCINFO
[
    ExecMethod = ExecFile_Impl ;
    StateMethod = GetState_Impl ;
]

};
The interface term is used slightly different in the sdi files that it is used by SFX, so now we use the name “slot arrays” for what the SFX itself calls “interfaces” to avoid confusion.
As a residue of the old Basic API sdi files define interfaces and shells separately, thus differentiating between slots for both Basic and UI usage and slots only used for UI purposes. This is obsolete today, but still used. Additionally sdi files define base and derived interfaces and shells in a granularity we don’t need anymore, but this is also a residue of the StarBasic support. This might change in the near future. Some interfaces even contained real basic properties that are not represented by slots at all (like the “object” interface referenced in the example) but they are not used anymore.
The “shell” defined in the file references a C++ implementation class SfxObjectShell. This part of the sdi file defines the complete slot interface of this class. The “import” statement tells the svidl compiler that it should add all slots of the interface “OfficeDocument” here (and of course all slots of its “base class” interfaces).
The “Automation” attribute that is assigned to both interfaces and shells tells the svidl compiler whether it should generate a slot array definition for this interface/shell or not. We only generate arrays for shell, not for interfaces (because we don’t need them anymore). In former times we also generated arrays for interfaces, and the “Automation” attribute allowed us to prevent svidl from generating them for base class interfaces also. The “Automation” attribute will become obsolete pretty soon.
Inside the interface/shell definition block slots are referenced by their SlotID. Svidl will get all attributes of the slot it needs to generate the array definition from the slot definition (that of course needs to be included and processed before svidl gets to the shell definition). In the block below this identifier (again with rectangular brackets) two additional attributes are specified: the names of one or two methods can be defined that SFX calls to get status information or execute the slot. These methods have a prescribed signature:

接口术语在 SFX 使用的 sdi 文件中使用略有不同,因此现在我们使用名称“插槽阵列”来表示 SFX 本身称为“接口”的内容,以避免混淆。

作为旧基本 API 的残余,sdi 文件分别定义了接口和外壳,从而区分了用于基本和 UI 用途的插槽和仅用于 UI 目的的插槽。这在今天已经过时了,但仍在使用。此外,sdi 文件以我们不再需要的粒度定义基础和派生接口和外壳,但这也是 StarBasic 支持的残余。这可能会在不久的将来改变。一些接口甚至包含根本不由插槽表示的真实基本属性(如示例中引用的“对象”接口),但它们不再使用。

文件中定义的“shell”引用了一个 C++ 实现类 SfxObjectShell。 sdi 文件的这部分定义了该类的完整插槽接口。 “import”语句告诉 svidl 编译器它应该在这里添加接口“OfficeDocument”的所有插槽(当然还有它的“基类”接口的所有插槽)。

分配给接口和外壳的“自动化”属性告诉 svidl 编译器它是否应该为此接口/外壳生成插槽数组定义。我们只为 shell 生成数组,而不是为接口生成数组(因为我们不再需要它们了)。以前我们也为接口生成数组,“自动化”属性允许我们阻止 svidl 也为基类接口生成它们。 “自动化”属性很快就会过时。

在接口/外壳定义块中,插槽由它们的 SlotID 引用。 Svidl 将获取它需要的插槽的所有属性,以从插槽定义生成数组定义(当然需要在 svidl 到达 shell 定义之前包含和处理)。在此标识符下方的块中(再次使用方括号)指定了两个附加属性:可以定义一个或两个方法的名称,SFX 调用这些方法来获取状态信息或执行插槽。这些方法有一个规定的签名:

void Class::StateMethod( SfxItemSet& );
void Class::ExecMethod( SfxRequest& );
The ItemSet is used to collect the status information, the SfxRequest contains everything necessary for the execution of a slot.
The svidl compiler takes the shell class name and the method name and generates an inline stub function that calls the C++ member. A pointer to each stub is generated into the slot definitions. The stubs are generated into the same header file that contains the generated arrays. Here is an example for the first slot of the interface “Document” shown above:

SFX_EXEC_STUB( SfxObjectShell, ExecFile_Impl )
SFX_STATE_STUB( SfxObjectShell, GetState_Impl )
We use function stubs here because pointer to member functions don’t have a fixed size and so we can’t store them inside a struct like SfxSlot. A pointer to a C function always has the same size as normal pointer on the machine.
The slots in our example show that both pointers can be set or only one of them. A slot without an “Exec” function is a slot that only has a status, execution is done by another slot. A slot without a “GetState” function is always enabled and has no status. SFX automatically generates empty stub in these cases.
Inside the block defining the functions theoretically all slot attributes of the referenced slot can be overwritten and sometimes this can be found. This should be seen as exceptional cases and it might happen in the near future that this overwriting will become forbidden for more and more attributes. The benefit of this is very limited but the risk of dangerous inconsistencies is high.
Here’s an exerpt of the generated header file that belongs to the SID_CLOSEDOC slot shown above:

ItemSet 用于收集状态信息,SfxRequest 包含执行插槽所需的一切。

svidl 编译器采用 shell 类名和方法名,并生成一个调用 C++ 成员的内联存根函数。指向每个存根的指针被生成到槽定义中。存根生成到包含生成的数组的同一头文件中。这是上面显示的界面“文档”的第一个插槽的示例:

SFX_EXEC_STUB( SfxObjectShell, ExecFile_Impl )

SFX_STATE_STUB( SfxObjectShell, GetState_Impl )

我们在这里使用函数存根,因为指向成员函数的指针没有固定大小,因此我们不能将它们存储在像 SfxSlot 这样的结构中。指向 C 函数的指针始终与机器上的普通指针具有相同的大小。

我们示例中的插槽显示可以设置两个指针或仅设置其中之一。没有“Exec”功能的插槽是只有状态的插槽,执行由另一个插槽完成。没有“GetState”功能的插槽始终处于启用状态并且没有状态。在这些情况下,SFX 会自动生成空存根。

在理论上定义函数的块内,引用插槽的所有插槽属性都可以被覆盖,有时可以找到。这应该被视为例外情况,并且在不久的将来可能会发生这种覆盖越来越多的属性将被禁止的情况。这样做的好处非常有限,但出现危险不一致的风险很高。

下面是生成的属于 SID_CLOSEDOC 槽的头文件的摘录:

SFX_SLOTMAP_ARG(SfxObjectShell)
{
// Slot Nr. 0 : 5502
SFX_NEW_SLOT_ARG( SfxObjectShell,SID_SAVEASDOC,SID_SAVEASDOC,GID_DOCUMENT,
0,&aSfxObjectShellSlots_Impl[1] /* Offset Next*/,
SFX_STUB_PTR(SfxObjectShell,ExecFile_Impl),SFX_STUB_PTR(SfxObjectShell,GetState_Impl),
SFX_SLOT_CACHABLE|SFX_SLOT_SYNCHRON|SFX_SLOT_RECORDPERSET|SFX_SLOT_HASDIALOG|SFX_SLOT_MENUCONFIG|
SFX_SLOT_TOOLBOXCONFIG|SFX_SLOT_ACCELCONFIG|SFX_SLOT_CONTAINER|SFX_SLOT_READONLYDOC|0,
0,
SfxStringItem,
0/Offset/, 9/Count/,".SaveAs",SFX_SLOT_METHOD|0,“SaveAs” ),
// Slot Nr.1 : 5503
SFX_NEW_SLOT_ARG( SfxObjectShell,SID_CLOSEDOC,SID_CLOSEDOC,GID_DOCUMENT,
0,&aSfxObjectShellSlots_Impl[3] /* Offset Next*/,
SFX_STUB_PTR(SfxObjectShell,ExecFile_Impl),SFX_STUB_PTR(SfxObjectShell,GetState_Impl),
SFX_SLOT_CACHABLE|SFX_SLOT_ASYNCHRON|SFX_SLOT_RECORDPERSET|SFX_SLOT_MENUCONFIG|
SFX_SLOT_TOOLBOXCONFIG|SFX_SLOT_ACCELCONFIG|SFX_SLOT_CONTAINER|SFX_SLOT_READONLYDOC|0,
0,
SfxStringItem,
9/Offset/, 2/Count/,".CloseDoc",SFX_SLOT_METHOD|0,“CloseDoc” ),
// Slot Nr.2 : 5504
SFX_NEW_SLOT_ARG( SfxObjectShell,SID_PRINTDOC,SID_PRINTDOC,GID_DOCUMENT,
0,&aSfxObjectShellSlots_Impl[2] /* Offset Next*/,
SFX_STUB_PTR(SfxObjectShell,ExecFile_Impl),SFX_STUB_PTR_NONE,
SFX_SLOT_CACHABLE|SFX_SLOT_ASYNCHRON|SFX_SLOT_RECORDPERSET|SFX_SLOT_HASDIALOG|SFX_SLOT_MENUCONFIG|
SFX_SLOT_TOOLBOXCONFIG|SFX_SLOT_ACCELCONFIG|SFX_SLOT_CONTAINER|SFX_SLOT_READONLYDOC|0,
0,
SfxStringItem,
11/Offset/, 10/Count/,".Print",SFX_SLOT_METHOD|0,“Print” ),

     // ... to be continued ...

The SFX_NEW_SLOT_ARG macro initializes a complete slot. Most of it should be pretty obvious from what was described already, please note the macros for the generated stubs (and especially SFX_STUB_PTR_STATE_NONE for the empty stub).
An interesting addition is the second line of each block, it contains an “Offset Next”. It is a pointer that points to the next slot in this shell that uses the same “GetState” method as the current slot. This enables SFX to collects status information for several slots at once when a complete update is requested. In general the shell code allows to group slots together in Exec/GetState methods and have more than one per shell for clarity reasons.
The last line in each block also contains an offset and a count. These attributes point to the arguments of the slot (of course only for methods). All possible arguments of all slots inside a shell are grouped into a single array, arguments that belong to the same slot of course beneath each other. The offset points to the first argument of a slot:

SFX_NEW_SLOT_ARG 宏初始化一个完整的插槽。从已经描述的内容来看,大部分应该是很明显的,请注意生成的存根的宏(尤其是空存根的 SFX_STUB_PTR_STATE_NONE)。

一个有趣的补充是每个块的第二行,它包含一个“Offset Next”。它是一个指向此 shell 中下一个插槽的指针,该插槽使用与当前插槽相同的“GetState”方法。这使 SFX 能够在请求完整更新时一次收集多个插槽的状态信息。通常,shell 代码允许将 Exec/GetState 方法中的插槽组合在一起,并且为了清晰起见,每个 shell 有多个插槽。

每个块中的最后一行还包含一个偏移量和一个计数。这些属性指向插槽的参数(当然仅用于方法)。外壳内所有插槽的所有可能参数都被分组到一个数组中,属于同一插槽的参数当然彼此放在一起。偏移量指向插槽的第一个参数:

SFX_ARGUMENTMAP(SfxObjectShell)
{
SFX_ARGUMENT(SID_FILE_NAME,“URL”,SfxStringItem),
SFX_ARGUMENT(SID_FILTER_NAME,“FilterName”,SfxStringItem),
SFX_ARGUMENT(SID_PASSWORD,“Password”,SfxStringItem),
SFX_ARGUMENT(SID_FILE_FILTEROPTIONS,“FilterOptions”,SfxStringItem),
SFX_ARGUMENT(SID_DOCINFO_COMMENTS,“VersionComment”,SfxStringItem),
SFX_ARGUMENT(SID_DOCINFO_AUTHOR,“VersionAuthor”,SfxStringItem),
SFX_ARGUMENT(SID_OVERWRITE,“Overwrite”,SfxBoolItem),
SFX_ARGUMENT(SID_UNPACK,“Unpacked”,SfxBoolItem),
SFX_ARGUMENT(SID_SAVETO,“SaveTo”,SfxBoolItem),
SFX_ARGUMENT(SID_CLOSEDOC_SAVE,“SaveChanges”,SfxBoolItem), // <---- here args for SID_CLOSEDOC start
SFX_ARGUMENT(SID_CLOSEDOC_FILENAME,“FileName”,SfxStringItem), // “count” is 2, so this is the second one

// ... to be continued ...

};
In each module one header file containing all shells is created. It must be included in the source file of each shell class that contains the SFX_IMPL_INTERFACE macro. To avoid multiple definitions of the slot arrays by this multiple inclusion each code block assigned to a particular shell is framed by an “#ifdef ClassName” block, so in the source file the typical code sequence is

在每个模块中创建一个包含所有 shell 的头文件。 它必须包含在每个包含 SFX_IMPL_INTERFACE 宏的 shell 类的源文件中。 为了避免通过这种多重包含对槽数组进行多重定义,分配给特定 shell 的每个代码块都由“#ifdef ClassName”块构成,因此在源文件中,典型的代码序列是

#define SfxObjectShell
#include “sfxslots.hxx”
Other parts of the header file are framed by “#ifdef SFX_TYPEMAP” and this part mustbe activated only once also so each module contains one file that includes the generated header file and defined SFX_TYPEMAP before. This can be on e of the shell files that also defines its shell interface or it can be a different one. The part of the generated header file that is activated here is not necessary for the SFX core (the slot dispatching), but was necessary in former times for the type information of the old Basic API and for macro recording. Today it is used for the wrapper that mediates between SFX dispatching and the Dispatch API and thus also for macro recording because the current macro recorder also uses the Dispatch API.

头文件的其他部分以“#ifdef SFX_TYPEMAP”为框架,这部分也必须只激活一次,因此每个模块都包含一个文件,其中包含生成的头文件和之前定义的 SFX_TYPEMAP。 这可以在也定义其 shell 接口的 shell 文件中,也可以是不同的。 此处激活的生成头文件部分对于 SFX 核心(插槽调度)不是必需的,但在以前对于旧的 Basic API 的类型信息和宏录制是必需的。 今天,它用于在 SFX 调度和调度 API 之间进行调解的包装器,因此也用于宏录制,因为当前的宏录制器也使用调度 API。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值