You are on page 1of 75

MicroStation 开发语言(MDL)应用教程

第一章 MDL 介绍
一、MicroStation的开发工具

1. UCMs ………… User Commands

2. MicroCSL …… MicroStation Customer Support Library

3. MDL …………… MicroStation Development Language

二、何为 MDL

MDL是 MicroStation的“应用引擎 (Application Engine)”,MDL 可以直接在 MicroStation 中运行并且表现为 MicroStation 本身


的一部分。许多 MicroStation 的命令都可以在 MDL中应用。

三、MDL 的组成

1. 标准 C (ANSI C) 程序设计语言(当然有某些限制和扩展);

2. 一整套开发工具(包括 C 编译器、连接器、链接库、资源编译器和资源库、Make程序以及调试工具);

3. MicroStation 内部的用于执行 MDL 应用程序的伪码解释器;

4. MicroStation 内部的 MDL 调试器;

5. 大量的运行库函数;

6. 可以改变 MicroStation 各种行为的钩子(Hooks)函数,所谓钩子函数是指那些在某种事件发生时被 MicroStation


自动执行的函数;

7. GUI 开发工具,可以用于开发基于 Motif 的图形用户界面(包括:对话框、下拉菜单等);

8. 资源管理器;

四、运行及卸载 MDL 应用程序

在 MicroStation命令窗口中运行 MDL 应用时,可以指定一个应用名称和任务识别码(Task ID ),其中前者是必须的。具体


使用方法如下:

MDL LOAD|SILENTLOAD [DEBUG|NODEBUG] <应 用 名> [,<任务识别码 >] [<参数>]

MicroStation 将首先在当前目录下查找用户在命令行中指定的应用,如果找不到,再到由 MicroStation 环境变量 MS_EXE 和


MS_MDL 指定的目录下去寻找。

如果用户未指定应用程序的扩展名,MicroStation 将首先寻找无扩展名的文件,其次才是.MA 文件和.RSC 文件。因此运行时


最好键入应用程序的全名。

卸载  MDL应用的命令为: MDL UNLOAD|SILENTUNLOAD [<任务识别码>]

MDL应用还可以在 MDL 对话框中装载运行,该对话框如图 1-1 所示。通过 MDL 对话框也可以卸载 MDL 应用。

图 1-1、MDL 对话框
五、MDL 的高级调用方式

1. 在一个 MDL 应用中调用另一个 MDL 应用:使用 MicroStation 的内部函数 mdlSystem_LoadMDLProgram;

2. 在 MicroStation初始化时(打开 DGN 文件之前或者进入图形模式之前)调入 MDL应用:使用环境变量


MS_INITAPPS和配置变量 MS_DGNAPPS,如果要调用多个 MDL程序,用分号(;)隔开,语法如下:
MS_DGNAPPS=应用程序名[,任务识别码] [/D];

3. 在 DGN 文件打开时运行 M D L应用:使用 mdlSystem_CreateStartupElement 函数在 DGN 文件中创建一个启动元素


(Start-up Element,类型代码为 66),即可在 MicroStation 打开 DGN 文件后立即运行指定的 MDL 程序;

4. KEYIN 应用:MDL KEYIN <应用名> <命令字串>。

六、MDL 的高级卸载方式

1. 在程序中使用标准 C的 “exit”函数卸载程序;

2. MicroSt ation 退出时可自动卸载 MDL程序;

3. 应用程序发生严重错误时会自动退出;

4. 在一个应用中卸载另一个应用使用 MicroStation 内部函数 mdlSyatem_unloadMDLProgram;

在 MDL调试器中使用 QUIT 命令可以卸载一个应用程序。

第二章 MDL 开发环境


1.开发环境变量

MDL开发环境使用一些变量来定义各种环境参数(表 1-1)。

表 1-1、MDL 开发环境变量列表

变量 描述 类型 缺省值

 
MS MicroStation系统目录 环境变量

MDL_COMP mcomp和 rsctype 的命令行参数 环境变量 -I%MS%

RSC_COMP rcomp 的命令行参数 环境变量 -I%MS%

BMAKE_OP bmake 的命令行参数 环境变量 -I%MS%

 
MS_MDL 搜寻 MDL 应用的路径 配置变量

 
MS_MDLAPPS 在 MDL对话框中列出的应用程序 配置变量

在 DOS 和 Windows NT 操作系统中,环境变量可以在 AUTOEXEC.BAT 中设置,同时需要在其中设置的还有 MicroStation


系统路径。用户级别的环境变量设置可以从 MicroStation 命令窗口中选择 User --> Workspace option -- > Modify User
Configuration option 菜单(如图 2-1 所示)激活变量设置对话框(图 2-2),在该对话框中选择需要配置的变量进行设置。

图 2-1、通过菜单激活变量设置对话框
图 2-2、变量设置对话框

二、MDL 应用的结构

一个 MDL 应用由两个主要的部分组成,即程序部分与资源部分。其中程序部分是必须的,而资源部分是可选的。

三、程序代码(Program Code)

程序文件以 .MC 为扩展名,该文件中包含实现各种功能的函数代码。M C文件中的函数可以有如下几种类型:

1. 主函数(Main Function)

与标准 C语言不同,MDL中的主函数可以缺省。主函数通常用于应用程序的初始化,在该应用载入时执行。

2、命令处理函数(Command Process Function)

该函数基于用户事件(User Events)定义应用程序的执行命令。

3、钩子函数(Hook Function)

这种函数通常与对话框或者对话框中的 Item 相联系,用于修改和增加其功能。

四、资源代码(Resource Code)

资源文件以 .R为扩展名,用于定义应用程序的用户界面部分,以下是几种类型的资源组件。资源文件不是一个 MDL 应用


所必须的。

1. 对话框定义(Dialog Box Dinfintions)用于定义对话框以及对话框中的元素(Item)。

2. 命令表(Command Tables) 用于映射键入命令到命令处理函数。

3. 消息列表(Message Lists)用于存储处理期间的消息及提示信息的文本字符串。
五、头文件(Head Files)

头文件以 .H 为扩展名,用于给程序常量设置识别名以及定义将要在多个源码(程序)文件中使用的变量。使用时将头文件
包含(Include)到程序文件中。

六、函数声明文件

函数声明文件以 .FDF 为扩展名,用于声明函数原型(prototype)。这有助于减少诸如函数提前引用(即在定义函数之前引


用)的编译错误,同时方便了调用函数时的参数检查。MDL使用 ANSI C语言的函数原型声明。在 MicroStaion 系统目录下
的 MDL\INCLUDE 目录中存放着 MDL内部函数的原型声明文件(*.FDF)。

七、MDL 工具列表

工具 描述

mcomp MDL源程序编译器(生成目标代码)

mlink 链接器(链接库文件中的有关目标代码到程序中)

mlib 对象库管理器(用于维护库文件中的目标代码)

rcomp 资源编译器

rlib 资源库管理器(比如把多个资源文件链接到一个资源文件或者程序文件)

rsctype 类型(Type)生成器(生成类型定义以便对话框管理器可在运行时模拟 C 表达式)

bmake(CLIX & DOS) MDL应用编译器 (用于编译工程文件中指定的应用程序)


bmakewin (Windows NT)

八、MDL Compiler -- mcomp


mcomp 用于把源程序编译成目标码,即把 .MC 文件编译成 .MO 文件。

.MC ----> .MO

语法 : mcomp [-参数 1 -参数 2 …] 源文件名

参数: -c 只编译,不联结;
-g 生成调试信息;
-idir 使用 dir 目录中的头文件 ;
-pi 显示头文件

编译参数的环境变量设置: MDL_COMP 环境变量可用于设置 mcomp 编译命令行参数,以下是 DOS &


Windows NT 操作系统下 autoexec.bat 文件中的环境变量设置示范。
SET MDL_COMP=-I\WIN32APP\INGR\USTATION\MDL\INCLUDE

九、MDL Linker -- mlink

mlink 用于链接 mcomp 产生的目标码和编译库,生成应用或程序文件。

.MO, .ML ---> .MP, .MA

语法 mlink [-参数 1 -参数 2 ] 输入文件名

参数: afile 指定输出文件(默认为输入文件名.MA);


-g[d|n|o] 产生调试信息;
-gd 和- g:产生所有可用的调试信息 ;
-gn:产生部分调试信息 ;
-go:不产生任何调试信息;
-ssize 指定 MicroStation之外的运行堆栈大小,若未指定,mlink将基于内部算法产
生一个默认堆栈大小。

十、MDL Libarian-- mlib

mlib 用于把 M D L目标码联结到编译库文件中

.MO ---> .ML

语法 mlib 命令[v/c] 库文件 [输入文件]

参数: a 增加 object 到库;


d 从库中删除 object;
t 显示库文件内容;
x 从库中还原 object; 设置参数
v 将显示较多的信息。如果需要更新库中内容,必须先删除要更新的 Object,然后
再加入新的 Object 。

十一、 Resource Compiler-- rcomp

rcomp 工具用于把资源源文件编译成二进制的资源目标文件。默认的输出文件是 .RSC 文件,对命令表文件,也可为其产


生 .H 头文件。

.R ---> .RSC, .H

语法 rcomp [-参数 1 -参数 2 … ] 输入文件

参数: -h 产生头文件;
-idir 使用 dir 目录中的头文件;
-pi 显示头文件

编译参数的环境变量设置: 编译参数的环境变量设置: RSC_COMP 可用于指定编译命令行参数。以下是 DOS


& Windows NT 操作系统 autoexec.bat 文件中的环境变量设置示范。
SET RSC_COMP=-I\WIN32APP\INGR\USTATION\MDL\INCLUDE

十二、 Resource Librarian-- rlib

该工具用于联结资源目标代码文件和应用程序文件到一个单一 MDL 应用或者资源目标代码文件。默认的输出是 .RSC 文


件。.RSC, .MP ---> .MA .RSC
语法: rlib [-参数 1 -参数 2 …] 输入文件

? 参数

-ofile 指定输出文件(默认为输入文件名.MA)

-v 冗余开关(若指定,将产生较多的输出信息)

十三、Type Generator -- rsctype

该工具用于从包含 C类型定义(C type definitions)的文件中产生资源源文件。因为对话框管理器不能“理解”结构(structure)


以及联合(union)中的数据。所以 rsctype 工具将分离结构及联合中的数据,并把它们转换存储到资源源文件(.H)中。

type.mt ---> type.r

? 语法

rsctype [-参数 1 -参数 2…] 输入文件

? 参数

-idir 使用 dir 目录中的头文件

-pi 显示头文件

注意!命名自己的 type 文件时一定要小心,因为如果不按惯例命名,就有覆盖已有 .R 文件的危险。

? 编译参数的环境变量设置

MDL_COMP 可用于指定编译命令行参数。以下是 DOS & Windows NT 操作系统 autoexec.bat 文件中的环境变量设置示范。

SET MDL_COMP=-I \WIN32APP\INGR\USTATION\MDL\INCLUDE

十四、文件类型

在 MDL开发应用过程中的不同阶段,将有不同类型的文件产生或使用到,不同的文件类型通过相应的文件扩展名来区别。

1、创建资源时用到的文件类型

表 2-2、创建资源时用到的文件类型

扩展名 描述

cmd.r 包含命令表的资源源文件

cmd.h 包含应用命令号的头文件,由 rcomp -h 产生

cmd.rsc 编译后的命令表资源文件

扩展名 描述

typ.mt 包含 C结构和联合的类型文件

typ.r rsctype产生的资源源文件

typ.rsc 编译后的类型资源文件

扩展名 描述

.r 资源源文件

.rsc 编译后的资源源文件

扩展名 描述

msg.r 包含消息资源的资源源文件
msg.rsc 编译后的消息资源文件

扩展名 描述

dlg.r 包含对话框及其 item 资源的资源源文件

dlg.rsc 编译后的对话框及其 item 的资源文件

2、创建程序时用到的文件类型

表 4、创建程序时用到的文件类型

扩展名 描述

.h 源程序用到的标准头文件

txt.h 包含文本串的头文件

.fdf 包含函数原型的函数声明文件

.mc MDL 源程序文件

.mo 由.mc 文件编译成的目标文件

.mp 用 mlink 生成的程序文件

.ml 用 mlib生成的包含目标代码的库文件

.mi rlib产生的中间文件

.ma 应用文件(MicroStation 中运行)

十五、MDL 文件类型及相互其关系

图 4 表明了不同的 MDL应用组件是如何编译到一起的。

图 4、MDL 文件类型及其相互关系
十六、间接命令行参数

以上提到的这些开发工具,在实际使用过程中都要键入大量命令行参数,有没有减少这种烦琐的参数输入的方法呢?有,那
就是使用参数文件(option files)把经常使用的参数设置写到一个文本文件中,然后在命令行中加上 @option_file,即:

command @option_file

第三章 bMake 工具
bMake 工具允许用户使用工程文件编译整个 MDL 应用。工程文件定义了组成一个 MDL应用的各种宏并指定了各从属文件
的编译关联。bMake工具通过判断文件创建的时间可以识别工程文件中是否有文件被改变、删除等。

一、工程文件的文件格式

? 工程文件中包含一系列描述应用将如何编译的语句,这些语句有以下几种类型:

1. 宏(Marco)

2. 条件(Conditionals)

3. Build命令

4. 从属(Dependencies)

5. 规则

? 工程文件的几个通用规则:

1. 规则和从属语句之后必须有一空行;

2. 如果一条语句分几行写,用“\”表示后续;

3. 在任何操作系统中,目录的分隔符一律采用“/”(不用“\”);

4. 注释用“#”符号打头。

二、宏(Macros)

宏是一种定义,它给字符串定义一个宏名,在使用时,这个宏名就代表该字符串。您可以引用已有的宏,当然也可以建立自
己的宏。下面是一些预定义的宏:

表 3-1、预定义的宏

宏 定义

$@ 当前目标文件

$? 所有从属文件新于目标文件

$= 最新从属文件

$< 当前从属文件

$* 目标文件的基名(忽略后缀)

$% 第一个从属文件所在目录

msdos.IP32.unix.macintosh.vax.etc. 指定操作系统的各种宏

? 宏定义的几种方式:

1. 在工程文件内定义;

2. 工程文件以外的可被作为宏引用的环境变量;

3. 在命令行中使用-D 开关。
宏定义的语法:macroname = string
例如:BaseDir = $(MS)/mdl/example/basic

三、条件(Conditionals)

条件是工程文件中的流控制语句,条件语句必须以“%”打头。

表 3-2、条件语句的关键字

条件 描述

%include文件名 包含工程头文件

%ifdef 宏 OR %if宏 如果宏已经定义,执行后面的语句

%ifndef 宏 如果宏未定义,执行后面的语句

%else 与%ifdef ,%if,%ifndef 配合使用,表示其条件为假

%elif 在 if条件为假时再判断另一条件

%endif 结束 if 条件

四、 Build 命令

Build命令是被从属和规则用来建立目标文件的一系列语句行。如果未定义 Build命令,预定义的规则将使用 mdl.mki 文件中


的内容。

表 3-3、各种 Build命令标志

标志 描述

@ 命令不回显

$ 强制回显,即使使用了 silent 模式

~ 使用内嵌的 bmake命令,例如: ~CURRENT 设置目标文件的日期为当前日期; ~TIME


设置目标文件的时间为当前时间

> 文件 把所有的命令行写入指定的文件中,直到出现“<”标志

< 关闭“>”标志指定的文件

五、从属(Dependencies)

从属语句指定了用那些源文件生成目标文件。当任一个源文件被改变时会执行从属语句。

从属语句的语法:目标文件: 从属 1 从属 2 … 从属 n [build 命令]


例如:$(o)basic.mo :$(BaseDir)basic.mc $(BaseDir)basic.h

六、规则(Rules )

“规则”用于定义如何使用给定扩展名的从属文件来建立指定扩展名的目标文件。许多规则都已经在 mdl.mki 中定义过了。如


果一个目标文件有多个从属文件,其中第一个文件的扩展名将用来寻找规则。如果对一个目标文件使用了多个规则,只有最
后一个有效。规则的语法:.从属文件扩展名 .目标文件扩展名 :build命令

以下的 mdl.mki 文件显示了规则的范例


七、运行 bMake

bMake 命令把 MDL 源码文件编译成应用。在 Windows NT 中使用 bMakeWin 命令

bMake 的语法: bMake [options] 工程文件(*.mke)


bMakeWin [options] 工程文件(*.mke) # -- Windows NT 平台

bMake / bMakeWin 的参数: -a 重编译所有文件


-dmacro=value 定义宏
-n 仅仅显示 build命令
-Idir 设置 .mki文件路径

有两个常用的宏值得一提,它们是 -ddebug 和- dproduction。设置宏- ddebug 将打开编译器和链接器的调试信息生成开关,使


得编译后的 MDL应用中包含调试信息。相反,设置宏-dproduction将此开关关闭,运行时将不能进入 MDL的调试器。

环境变量:
BMAKE_OPT 可用于指定编译命令行参数。以下是 DOS & Windows NT 操作系统 autoexec.bat 文件中的环境变量设置示范。
SET BMAKE_OPT=-I\WIN32APP\INGR\USTATION\MDL\INCLUDE

八、bMake涉及到的文件类型

在使用 bMake进行编译时,除了源程序文件外,还涉及到两种类型的文件:工程文件和工程包含文件(或工程头文件)。
工程文件以“.MKE”为扩展名;工程包含文件以“.MKI”为扩展名。一般而言,规则在工程文件中定义;从属在工程包含文件
中定义。

在 $(MS)/mdl/inclde/目录中有一个标准的工程头文件:mdl.mki,其中包含了一些可以用于编译  MDL 应用的默认设置。以


下是 MicroStation提供的示例程序 Basic 的工程文件和工程包含文件。

basic.mke 文件

#----------------------------------------------------------------------
#
# MDL Make File
#
# $Workfile: basic.mke $
# $Revision: 5.3 $
# $Date: 01 Apr 1993 07:39:06 $
#
# Copyright (1993) Bentley Systems, Inc.
#
# Limited permission is hereby granted to reproduce and modify this
# copyrighted material provided that the resulting code is used only in
# conjunction with Bentley Systems products under the terms of the
# license agreement provided therein, and that this notice is retained
# in its entirety in any such reproduction or modification.
#
#----------------------------------------------------------------------
#---------------------------------------------
# Define macros specific to this example
#---------------------------------------------
BaseDir = $(MS)/mdl/examples/basic/
privateInc = $(BaseDir)
#---------------------------------------------
# mdl.mki contains the default rules for creating .rsc, .mo, etc files
# mdlexmpl.mki contains the output directory overrides used by examples
#---------------------------------------------
%include mdl.mki
%include mdlexmpl.mki
#----------------------------------------------------------------------
# Define macros for files included in our link and resource merge
#----------------------------------------------------------------------
basicObjs = \
$(o)basic.mo \
$(mdlLibs)ditemlib.ml
??
basicRscs = \
$(o)basic.mp \
$(o)basiccmd.rsc \
$(o)basictyp.rsc
??
#---------------------------------------------
# Generate command table include & resource file using rcomp
#---------------------------------------------
$(genSrc)basiccmd.h : $(BaseDir)basiccmd.r
$(o)basiccmd.rsc : $(BaseDir)basiccmd.r
#---------------------------------------------
# Create & compile the application's type resource file using rsctype
# and rcomp
#---------------------------------------------
$(o)basictyp.r : $(BaseDir)basictyp.mt $(BaseDir)basic.h
$(o)basictyp.rsc : $(o)basictyp.r $(BaseDir)basic.h
#---------------------------------------------
# Compile the MDL source file using mcomp
#---------------------------------------------
$(o)basic.mo : $(BaseDir)basic.mc $(BaseDir)basic.h
#---------------------------------------------
# Link MDL program file from basic.mo & ditemlib.ml using r link
#---------------------------------------------
$(o)basic.mp : $(basicObjs)
$(msg)
> $(o)make.opt
$(linkOpts)
-a$@
$(basicObjs)
<
$(linkCmd) @$(o)make.opt
~time
#---------------------------------------------
# Merge the dialog resources & MDL program file using rlib
#---------------------------------------------
$(reqdObjs)basic.mi : $(basicRscs)
$(msg)
> $(o)make.opt
-o$@
$(basicRscs)
<
$(rscLibCmd) @$(o)make.opt
~time
# complete construction of the .ma by getting the last resource.
%include $(BaseDir)

basicrsc.mki 文件

#----------------------------------------------------------------------
#
# MDL Make File
#
# $Workfile: basicrsc.mki $
# $Revision: 5.0 $
# $Date: 01 Apr 1993 07:33:12 $
#
# Copyright (1993) Bentley Systems, Inc., All rights reserved
#
# Limited permission is hereby granted to reproduce and modify this
# copyrighted material provided that the resulting code is used only in
# conjunction with Bentley Systems products under the terms of the
# license agreement provided therein, and that this notice is retained
# in its entirety in any such reproduction or modification.
#
#----------------------------------------------------------------------
basicRscs = \
$(reqdObjs)basic.mi \
$(rscObjects)basic.rsc \
$(rscObjects)basicmsg.rsc
$(rscObjects)basic.rsc : $(BaseDir)basic.r $(langSpec)basictxt.h \
$(privateInc)basic.h
$(rscObjects)basicmsg.rsc : $(langSpec)basicmsg.r $(privateInc)basic.h
$(mdlapps)basic.ma : $(basicRscs)
$(msg)
> $(rscObjects)make.opt
-o$@
$(basicRscs)
<
$(rscLibCmd) @$(rscObjects)make.opt
~time

九、工程文件涉及到的 MDL 文件类型

图 3-1 工程文件涉及到的 MDL文件类型

十、工程文件涉及到的 MDL 文件类型细节


图 3-2 工程文件涉及到的 MDL文件类型细节

第四章 创建对话框
许多 MDL 应用都使用对话框。对话框实际上就是作为用户和 MDL应用之间交互界面的一种窗口。对话框元素是嵌入对话
框中的一种资源,用户通过它可以方便直观地修改特定的数据以及执行特定的动作。

一、资源

最简单的定义就是:资源是在源文件中声明并初始化以备后用的变量,但与作为一块内存区域的在程序中用来存储数据的变
量不同。

对话框以及对话框中的元素通常在源程序中定义成资源,常用的有:对话框(dialog boxs)、对话框元素(dialog items)、消息列


表(message lists)、命令表(command tables)和同义列表(synonym lists) 。

二、为什么 MDL 要使用资源?

1. 资源管理器可以使用户快速存取资源文件而不必考虑其文件格式的细节;

2. 把数据同程序分离开来有助于方便的修改诸如消息和提示信息之类的元素而不必重新编译源程序;

3. 资源只是在需要的时候才调入内存,节省了内存空间;

4. 在 MDL应用中可以使用 MicroStation 预定义的资源类型,加快了定义资源要素的速度,同时也使得 MDL 应用有


统一的风格。

三、资源的结构

资源结构采用了类似 C 语言的结构定义。

表 4-1 资源结构的组成部分
组件 描述

C 类型定义或资源类型 C类型定义(Typedef)用于定义特定类型的资源实例(比如对话框或按钮);资源类型
是与一个特定资源类型相关联的唯一识别码。 C类型定义用于声明一个资源实例而不是
资源类型。

资源识别码 资源识别码是一个 32 位无符号整数,用于唯一标志特定的资源。这个整数一般在程序文


件的头部定义。

属性 指定资源属性的参数

数组 通常情况下资源有一系列的成员。在初始化时,资源编译器会为数组中的每一个成员数
产生一个内部长整型值。

每个资源类型都有自己的定义,因此,不同资源类型中各组件的类型和值都不相同。下例是资源文件的一部分:

四、对话框的坐标单位

在对话框上放置对话框元素时自然会涉及到该元素在对话框中的位置和大小,那么度量这种位置和大小的单位是什么呢?
MDL中的默认规定是把当前字体高度的 1/12作为对话框的纵坐标单位,当前字体的 1/6 作为对话框的横坐标单位。在
$(MS)/MDL/INCLUDE/DLOGBOX.H 文件中存放了有关的声明。

五、对话框

所有的对话框都由对话框管理器控制,对话框管理器控制调入对话框资源、创建对话框、显示对话框以及维护对话框。

六、对话框的类型
对话框有模态和非模态两种。模态对话框要求用户必须作出响应后才能执行其它的任务,而非模态对话框则不然。一般警告
信息提示框使用模态对话框。

七、对话框的结构

对话框是在资源文件中定义的,其定义的基础是 dlogbox.h 中定义的对话框结构。

八、定义对话框

定义对话框实际上就是定义对话框结构的各项成员变量。下面的例子仅仅定义一个没有任何元素的空对话框,其标题(Title)
为 “示范对话框”

十、打开对话框

定义了对话框,也包含了头文件,现在只差最后一步 — 打开(显示)对话框了。打开对话框有两个函数可以使用:

mdlResource_openFile 或

mdlDialog_open

以下是一个在 main()函数中显示对话框的示例:
第五章 对话框元素(Items)
上一章讨论了如何创建对话框,本章将集中讨论如何在对话框上放置对话框元素(Items)。

一、对话框元素

MicroStation 中有几种类型的对话框元素,每一种实现特定的功能。

二、对话框元素的类型

1、标签(Label)

标签是一种静态文本,只显示一个文本字符串,并不接收用户输入。使用

标签不需引用资源,只需将对话框元素的 type 设置为 Label 即可。

2、组合框(Group Box)

组合框是一个带标题的矩形图形框,用于把相 关联的一组元素框起来。
组合框不接收用户输入,使用时不需要引用资源,只需将对话框元素的 type 设置
为 GroupBox即可。

3、分隔线(Separator)

分隔线是一个垂直或水平的线段,用于分隔对话框上的元素。分隔线不接收用户输
入,使用时不必引用资源,元素列表中的 type 字段等于 Separator。

4、检查按钮(Check Button 或 Toggle Button)

检查按钮是一个有两种状态(开/关,或者 是/否)的按钮。元素列表中的 type 字段等于 ToggleButton


或 CheckButton。

5、命令按钮(Push Button)

命令按钮用于激活一个操作,用户选择此按钮后,与之相关联的代码就会被执行。元素列表中的 type 字
段等于 PushButton。

6、选项按钮(Option Button)

选项按钮允许用户在给出的多个选项中作出选择,选项菜单只显示当前的选择,占用较少的屏幕空
间。元素列表中的 type 字段等于 OptionButton。。

7、滚动条(Scroll Bar)

滚动条用于显示有 连续取值范围的值,如果滚动条代表某一值的话,必须有钩子
函数与之相连。元素列表中的 type 字段等于 ScrollBar。

8、文本框(Text)
文本框允许用户键入并编辑文本,不过只允许一行,文本类型可以是字符串、整型值或者浮点数。元素列表中的 type 字段
等于 Text

9、多行文本框( Multi -line Text)

功能与文本框相似,正如其名称所暗示的那样,多行文本
框允许编辑多行
文本。元素列表
中的 type 字段
等于 MLText 或 MultiLineText。

10、颜色拾取框(Clor Picker)

颜色拾取框用于在预定义的 256 种颜色中选取一种颜色。元素 列表中的 type


字段等于 ColorPicker。

11、图层控制(Level Map)

图层控制用于设置各个图层的打开或者关闭,以及设置当前激活图层,结果存于一个变量
中。元素列表中的 type 字段等于 LevelMap。

12、菜单条(menu Bar)

菜单条在对话框上创建一个基本的菜单。菜单
上有 3 个下拉菜单:Text、Option 和 Color。元素列表中的 type 字段等于 MenuBar, dlogbox.h中文件还新定义了另外一个菜
单资源类型:Ditem_MenuItemXRsc。

13、Text 下拉菜单

Text 下拉菜单包含数个文本字符串,选择一个字符串将激活一个操作,或者弹出下一级菜单。所以下拉菜单既可以放在
Menu Bar 上,也可以放在另一个下拉菜单里。元素列表中的 type 字段等于 PulldownMenu或 PDTextMenu,dlogbox.h 中文件
还新定义了另外一个下拉菜单资源类型:PulldownMenuX 或 PDTextMenuX 。

14、Option 下拉菜单

Option 下拉菜单包含数个选项,用户可以从这些选项列表中选择一个。Option 下拉菜单可以隶属于一个菜单条。也可以是


Text 下拉菜单的子菜单。Option 下拉菜单不可以有子菜单。元素列表中的 type 字段等于 PulldownOptionMenu或
PDOptionMenu。

15、颜色拾取下拉菜单(Color Picker Pull -down)

颜色拾取下拉菜单的功能同于颜色拾取按钮。元素列表中的 type 字段等于 PulldownCPickerMenu或 PDColorPickerMenu。

16、列表框(List Box)
列表框列出几个文本字符串以供用户选择,其功能同于选项按钮,不同点在于列表框可以同时显示多行字符串。元素列表中
的 type 字段等于 ListBox。

17、通用元素(Generic)

通用元素是一个外观和行为都可以自定义的一种对话框元素,该元素需要钩子函数。元素列表中的 type 字段等于 Generic。

18、按钮组(Button Group)

按钮组中可以放置一组图标在对话框中。元素列表中的 type 字段等于 ButtonGroup。

19、活动分隔线(Sash)

活动分隔线用于分隔对话框,与分隔线不同的是,活动分隔线可以允
许用户改变被活动分隔线分隔的对话框两块区域的大小比例。该元素需要钩子函数。元素列表中的 type 字段等于 Sash。

20、Scale 滚动条( Scale)

Scale 滚动条类似于滚动条,不同的是 Scale滚动条可以通过输入数值来改变滚动块的位置。元


素列表中的 type 字段等于 Scale。

21、弹出式菜单(Popup Menu)

弹出式菜单是一种活动菜单,弹出式菜单通常用一个热键激活。元素列表中的 type 字段等于 PopupMenu。

22、无线按钮(Radio Button)

用于在一组选项中设置一个选项的对话框元素。元素列表中的 type 字段等于 RadioButton。

三、定义对话框元素
定义对话框元素需要对话框元素的资源规格说明。这种资源可以在一个应用的资源文件中定义,也可以从其他任何已经打开
的资源文件中引用。在对话框上放置一个元素需要在对话框的 itemList 数组中指定一个资源描述列表。

四、对话框元素的资源规格

要执行的命令、欲改变的变量以及标签等是对话框元素的资源规格中的典型信息。对大多数元素来说,资源规格代码存放于
资源文件中。资源规格描述涉及到很多参数,详情请参考《MDL 程序员手册》。以下是各种参数的列表和说明(表 5-
1)。
表 5-1、对话框元素的资源规格参数

参数 描述

commandNumber 与操作相关联的命令识别码(NOCMD 表示没有命令)

commandSource 表明是 MicroStation 命令或是程序指定的命令(MCMD-MicroStation 命令, LCMD-程序指定的命


令)

unparsed 命令参数(””表示无参数)

helpInfo 帮助的 ID 码(NOHELP-无有效帮助)

helpSource 定位帮助的资源(MHELP-用 MicroStation的系统帮助或者没有帮助 ,LHELP — 程序指定帮助)

itemHookID 元素的钩子函数(NOHOOK-无钩子函数)

itemHookArg 元素钩子函数的参数(NOARG-无参数)

label 元素的标签(标题)

accessStr 指定与元素相关联的变量

synonymsID 指定同义元素的同义列表(NOSYNONYM-无同义列表)

每一类元素在 dlogbox.h中都有自己的定义。不同的对话框元素类型有区别于其他元素类型的数据结构,比如文本框需要有
一个变量来描述允许的文本长度,而无线按钮就不需要。下表(表 5-2)中左边是文本框的类型定义,右边是一个文本框的
资源规格说明范例。
表 5-2 文本框的结构定义及资源规格说明范例

dlogbox.h 中定义的结构 范例代码

typedef struct ditem_textrsc{ {


ULong commandNumber; NOCMD,
ULong commandSource;
long synonymsID;
MCMD,
ULong helpInfo; NOSYNONYM,
ULong helpSOurce; NOHELP,
long itemHookID; MHELP,
long itemHookArg;
byte maxSize; NOHOOK,
char formatToDisplay[16]; NOARG,
char formatToInternal[16]; 1,
char mininum[16];
char maxinum[16]; “%-1d”,
ULong mask; “%1d”,
Ushort attributes; “1”,
#if defined (resource)
char label[]; “3”,
char accessStr[]; NOMASK,
#else
NOCONCAT,
long labelLength;
char label[]; TXT_Parametertxt,
#endif “samplesGlobals.number”
}Ditem_TextRsc;
}
上面出现的 TXT_Parametertxt 是一个在包含文件中定义的字符串宏。

文本框有一些其他元素都有的公共属性参数,但也有一些特有的参数。比如
maxSize,formatToDisplay,formatToInternal,mininum,maxinum,mask,attributes 等。
五、对话框元素列表规格

对话框元素列表的规格描述了该元素的大小和位置等信息。它可以没有标签、访问字符串,也可以使元素有效或者无效。对
话框元素列表规格的格式如表 5-3 所示。

dlogbox.h 中定义的结构 范例代码

typedef struct dialogitemrsc{ {


Sextent extent; {XC*12,GENY(1),XC*10,0},Text, TEXTID_Samples, ON, 0, “”,
long type; /* item type */ “”, }
long id;
byte attributes;
long itemArg;
#if defined (resource)
char label[];
char auxInfo[];
#else
long labelLength;
char label[1];
#endif
} DialogItemRsc;

上例中的 type 字段的取值可以是下面列出各项中的任何一个:

Label, GroupBox, Seperator, ToggleButton or CheckButton, PushButton, OptionButton, ScrolBar, Text, MLText or MultiLineText,
ColorPicker, LevelMap, MenuBar or MenuBarX, ListBox, Generic, ButtonGroup, Sash, Scale, PopupMenu, or RadioButton.

下面的例子显示了一个加入对话框定义中的元素列表规则。示例中创建的对话框有 5 个元素:一个文本框、选项按钮、颜色
拾取按钮、检查按钮以及一个命令按钮。

#define DIALOG_WIDTH XC *28

#define DIALOG_HEIGHT GENY(7)

DialogBoxRsc DIALOGID_Samples=

DIALOGATTR_DEFAULT | DIALOGATTR_SINKABLE,

DIALOG_WIDTH,DIALOG_HEIGHT,

NOHLEP,MHELP,NOHOOK,NOPARENTID,

“示范对话框”,

{{XC*12,GENY(1),XC*10,0},

Text,TEXTID_Samples,ON,0,””,””},

{{XC*12,GENY(2),XC(10),0},

OptionButton,OPTIONBUTTONID_Samples,ON,0,””,””},

{{XC*12,GENY(3),0,0},

ColorPicker,COLORPICKERID_Samples,ON,0,””,””},

{{X C*12,GENY(4),0,0},

ToggleButton,TOGGLEID_Samples,ON,0,””,””},

{{XC*12,GENY(5),PUSHBUTTONID_Samples,ON,0,””,””},

}
#undef DIALOG_WIDTH

#undef DIALOG_HEIGHT

六、对话框风格建议

如果希望开发出功能强大而且界面美观的 MDL 应用的话,请接受以下建议:

1. 对话框不要太大;

2. 尽可能使用非模态对话框;

3. 尽量少用字体类型;

4. 限制使用的颜色数量;

5. 尽可能使垂直对齐的元素有相同的宽度;

6. 使用相同宽度的命令按钮;

7. 使用 GENY宏来保持垂直距离的一直性;

8. 不要把文本框、选项按钮和命令按钮的宽度设置为 0;

参考 MicroStation本身的设计。

第六章 工具条/工具模板(Tool Palettes)


对话框的另外一种使用方法就是创建工具条。工具条的元素有三种:

一、工具条的组成

工具条由以下几个部分组成:

1. 图标框架(Icon Command Frame),用以放置图标或者图标模板;

2. 图标模板(Icon Command Palette),拥有几个下一级图标的图标;

3. 图标(Icon Command),工具条中的最小组成单位,是一种图形按钮;

4. 下拉区(Pop-Down)。

图 6-1 工具条的组成

图标框架内既可以放置图标,也可以放置图标模板。我们建议最好垂直排放图标框架,也就是说行多于列比较好。如果要与
MicroStation 保持一致的话,图标框架最好排列成两列。

图标模板通常包含几组相关的图标,与图标框架一样,最好竖直排列。图标模板中只能放置图标,而不能放置另外的图标模
板。图标模板可以从图标框架中拉出来,以使其包含的所有图标都显示出来。
图标实际上是一种图形按钮,用户可以通过选择图标来激活某种操作。如果一个图标有一组对话框元素相关联,那么这些元
素都放在下拉区,当图标选中时就可以显示出来。

二、创建工具条

创建工具条时需要几种资源,别忘了在头文件中定义资源识别码。

1、创建对话框

为工具条创建对话框类似于为其他的对话框元素创建对话框,主要的区别在于在对话框元素列表规格描述中只有一个元素,
即:图标框架或者扩展的图标框架,分别用 IconCmdFrame 或者 IconCmdFrameX 来识别。尺寸字段没有意义,系统会根据
图标的数量来计算对话框的大小。

2、创建框架

创建框架需要创建一个图标框架的资源规格描述。框架资源主要说明框架中有那些图标或者图标模板。在图标框架中可以有
3 种类型的元素。首先,可以将图标(以 IconCmd识别)直接放置在框架上,不过如果图标带有下拉区的话,最好不要把它
直接放在框架上,

因为直接放在框架上的图标不能自动显示下拉区。其次,可以将预定义的图标模板(以 IconCmdPaletteX )放在创建的框架


上。第三,可以放置用户自定义的图标模板(以 IconCmdPalette或者 IconCmdPaletteX)。如果您创建的图标模板中包含了
由应用定义的来自一个共享资源号目录(shareids)的图标时,就需要使用 IconCmdPaletteX 类型来识别。

如果使用 IconCmdFrameX 来创建框架,请使用下例中的调用格式。其格式类似于上例,包含了框架的属性描述和一个


iconPieces列表。不过可以看出,两例中的调用格式有两个区别:其一,下例中框架在 helpSource之后多出一个 attributes 参
数;其二,除了要指定资源识别码外,还需要指定一个任务识别码(taskID)。对于每一个图标或者图标模板,必须给本应
用指定其资源来源于何处。若图标及图标模板在 MicroStation(dlogbox.h)定义的,可以使用 MTASKID 作为任务识别码;
如果是在本应用中定义的图标或图标模板,则使用 OTASKID;如果引用了另一个应用或一个“共享资源号”目录(shareids)
中文件里定义的图标或图标模板,则应把任务识别码设置为相应的 ID 值。

3、创建图标模板
程序员也可以创建自己的模板而不用从其他应用中引用。若使用 IconCmdPalette 定义模板,应当使用下例中的格式。其中的
图标可以来源于 dlogbox.h或者其他的应用。

如果使用 IconCmdPaletteX 定义图标模板,请使用下例中的格式。其中任务码用法与创建框架相同。

4、创建图标

如果要创建自己的图标,有三种资源必须创建:图标命令、小图标(icon)、大图标(icon)。图标命令定义了图标的属性,比如
定义了选择图标后要执行的代码。

创建大、小图标可以使用栅格图形编辑器,比如 RASTICON或者 MDE 的图符编辑器。

三、工具条使用建议

1. 当要引用已有图标或图标模板时,首先从“共享资源号”目录(shareids)中去查找;

2. 如果 shareids 目录中没有,引用 dlogbox.h中的定义;

3. 如果使用 shareids目录或其他应用中的定义,必须做到:

1. 在资源中包含 .ids头文件;

2. 如果图标或者图标模板定义于 shareids 或者其他应用中,创建一个扩展的框架资源


(Ditem_IconCmdFrameXRsc);

3. 如果模板中的图标定义于 shareids 或者其他应用中,创建一个扩展的模板资源


(Ditem_IconCmdPaletteXRsc)。
第七章 对话框元素同步(Item Synchronization)
当您需要改变或者更新对话框中元素的值时,还有一些程序细节需要处理。同义列表是维护元素同步的一个方法,用于指定
当列表中的一个元素的值改变时需要通知哪些对话框元素。一旦某元素被通知到,元素外观显示将被强制与相连的变量值一
致。

一、对话框元素的同步

对话框元素的内部值用于决定元素的外观显示,而元素的外部值是元素所控制的应用程序中使用的变量。“访问字符串”
(access string)是一个 C 表达式,用于指定应用程序变量名称,并用于获取元素的外部值。保持元素的内部值(或外观显
示内容)跟外部值一致的操作就是对话框元素的同步,也就是说,一旦某元素被同步,其内值和外值将是一致的。

如果同一个资源在屏幕上有多个显示实例,这些实例将自动保持一致。

二、有关同步的函数

有关同步操作的的函数有 3 个:

1. mdlDialog_itemSynch强制一个元素的内值等于外值;

2. mdlDialog_itemsSynch 强制对话框中的所有元素的内值等于外值;

3. mdlDialog_synonymsSynch强制同义列表中的所有元素的内值等于外值。

三、同义资源

在某种情况下,不仅需要在元素的外值改变时更新其内值及外观显示,还需要同步更新其它一系列元素的内部状态。这种同
步改变可以通过同义资源来实现。

同义资源是一个简单的元素列表,列表给每一个元素指定了元素的类型和资源识别码。有些类型元素(比如检查按钮、文本
框、选项按钮)可以在其元素资源规格描述中包含同义资源识别码。无论何时这些类型元素的外值发生变化,将会发送一个
同步消息到所有与之相连的同义资源中,这就会强制使每一个同义资源的外观显示与相应的程序变量相匹配。

四、使用同义资源

使用同义资源有几个步骤。这个来自第五章《对话框元素》的例子将被用于示范同义资源如何合并到应用中的。

1、定义同义识别码

和其它资源一样,同义资源也必须有一个识别码。完成定义的方法如下:

#define SYNONYMID_Sample 1

2、创建同义列表

同义列表是在资源文件中定义的。列表中指定了当一个元素改变时要被同步的元素。下例示范如何同步文本框和选项按钮:

3、更新元素资源

还有一步必须完成,那就是增加同义识别码到相应的元素资源规格描述中。
需要注意的是必须设置访问字符串字段,访问字符串包含了与元素相关的数据。如果同义列表中的一个元素的值被改变,其
它的元素会收到一个消息,并且其外观显示将被更新,使其与访问字符串中的值一致。

第八章 消息列表与命令表
消息列表和命令列表是另外两个用于增强 MDL 应用的资源类型。消息列表集中了应用中要显示的消息字符串。命令表是一
系列层状定义 MDL 应用中键入命令的结构。

一、消息列表

消息列表用于定义命令、提示信息和错误信息的文本字符串,消息列表中的每个字符串都有一个唯一的消息码。

使用消息列表有两个原因:首先,可以改变消息内容(比如翻译成其它国家的语言)而不用重新编译程序,只需要改变资源
文件就可以了;第二,消息只在需要时才调入内存,节省了 MDL 应用的使用内存。

1、创建消息列表

消息列表的结构定义于 $(MS)/mdl/include/rscdef.h 文件中。

2、定义资源识别码

和其它资源一样,消息列表资源的识别码必须在头文件中定义,而且号码要以升序排列。
一个应用可以使用 MDL函数直接显示消息列表中的字符串,这些函数有:mdlOutput_rscPrintf、mdlState_startPrimitive、
mdlState_startModeifyCommand 等。下例是使用 mdlOutput_rscPrintf 函数的示范

有些函数,(比如 mdlOutput_rscPrintf)不仅需要消息识别码,同时还需要消息列表识别码作为参数。另一些函数(比如
mdlState_startPrimitive)只需要消息识别码作为参数。如果没有指定消息列表,系统会自动使用当前注册的消息列表。至于
如何注册消息列表,将在后面讨论。

3、注册消息列表

注册消息列表使用 mdlState_registerStringIdss函数。一般这种注册都在主函数中进行,以便以后可以使用消息字符串。

二、命令表

一个应用可以在命令表中定义自己的命令语言。命令表用于定义键入命令的语法,使得 MDL 应用的命令如同 MicroStation


的命令一样。键入命令通过一个名为 Table 的结构在资源文件中定义,该结构在 $(MS)/mdl/include/rscdefs.h 中定义。

三、命令表实例

#include <rscdefs.h>

#include <cmdclass.h>

/* Version 100 */

/*----------------------------------------------------------------------+

| Local Defines |

+----------------------------------------------------------------------*/

#define CT_NONE 0

#define CT_CALCULATOR_HEADER 1

#define CT_CALCULATOR 2
#define CT_TYPE 3

#define CT_CLASSES 4

#define CT_PREPROCESSOR 5

/*--------------------------------------------------*/

/* Main Calculator Commands */

/*--------------------------------------------------*/

Table CT_CALCULATOR_HEADER =

{ 1, CT_CALCULATOR, IMM, TRY, "CALCULATOR"},

{ 2, CT_PREPROCESSOR, IMM, TRY, "PREPROCESSO R"},

{ 3, CT_NONE, IMM, HID, "UCCALC"}

}; /* Endtable exampleChangeCommands */

/*--------------------------------------------------*/

/* Caclulator options */

/*--------------------------------------------------*/

Table CT_CALCULATOR =

{ 1, CT_TYPE, INHERIT, REQ, "FORMAT" },

{ 2, CT_TYPE, INHERIT, REQ, "DECLARE"},

{ 3, CT_CLASSES, INHERIT, HID, "SHOW"},

}; /* Endtable changeType */

/*--------------------------------------------------*/

/* Data types */

/*--------------------------------------------------*/

Table CT_TYPE =

{ 1, CT_NONE, INHERIT, 0, "INT" },

{ 2, CT_NONE, INHERIT, 0, "DOUBLE" },

}; /* Endtable declare */

/*--------------------------------------------------*/

/* Symbol classes */

/*--------------------------------------------------*/

Table CT_CLASSES =

{
{ 1, CT_NONE, INHERIT, 0, "VARIABLES" },

{ 2, CT_NONE, INHERIT, DEF, "FUNCTIONS" },

}; /* Endtable declare */

/*--------------------------------------------------*/

/* Preprocessor options */

/*--------------------------------------------------*/

Table CT_PREPROCESSOR =

{ 1, CT_TYPE, INHERIT, REQ, "FORMAT" },

{ 2, CT_NONE, INHERIT, 0, "TOGGLE" },

{ 3, CT_NONE, INHERIT, 0, "ON"},

{ 4, CT_NONE, INHERIT, 0, "OFF"},

{ 5, CT_NONE, INHERIT, 0, "START"},

{ 6, CT_NONE, INHERIT, 0, "END"},

{ 7, CT_NONE, INHERIT, 0, "STATUS"},

}; /* Endtable changeType */

四、命令表语法

命令列表的语法通过建立一系列层状表来定义的(如下所示)。

Table <识别码>

/*初始化语句*/

识别码是一个 32 位的无符号整型数。主表必须给识别码赋值,这个表包含了可以在应用命令中用作词头的每一个关键字。
另外还有一些表资源包含命令中的其它后续字。命令中的每个关键字都有一个初始化语句。初始化语句的格式如下:

{<num>,<subtable>,<classname>,<options>,”<name>”},

1、num 字段

{<num>,<subtable>,<classname>,<options>,”<name>”},

num 字段为命令构造一个唯一的命令识别码,命令码的前 3 位的取值范围是从 1 到 255,后两位的取值范围从 1 到 15。一条


命令不能超过 5 个词。

2、subtable字段

{<num>, <subtable>,<classname>,<options>,”<name>”},

subtable字段指定命令中一个表的下一个词,并建立命令的后续表层次。如果命令中没有下一个词,则设置该字段位零。

3、classname 字段

{<num>,<subtable>,<classname>,<options>,”<name>”},

classname 字段决定了键入的词代表何种命令。取值范围是从 0 到 63,前 4 9 个类(classes)为 MicroStation 所保留,后 15 个可


以在应用程序中自定义。命令的类决定于类定义中最后一个匹配的词。命令类可以通过 mdlInput_disableCommandClass和
mdlInput_enableCommandClass 来使之无效或有效。下表是$(MS)/mdl/include/cmdclass.h中的类定义。
 

4、options 字段

{<num>,<subtable>,<classname>, <options>,”<name>”},

options字段是一个一位的数,几个二进制状态符可以用逻辑或(|)操作符联结起来,也可以将该字段指定为 NONE。

HID –Hidden;

DEF –Default,如果没有给出下一词,指定将从子表(subtable)中选出的命令词;

REQ – Required,指定需要子表中的命令词;

TRY –Try to parse,指定试图在子表中查找匹配词的语法分析器,如果没有找到,将把这个不能匹配的词作为参数发送给命


令。

CMDSTR(n) –Command String,把注册的命令名称消息列表框和命令表中的命令联系起来,消息列表中的消息将显示在命


令框中。

IMM –Immediate(已过时)

NOI –No Immediate(已过时)

5、name 字段

{<num>,<subtable>,<classname>,<options>,”<name>”},

name<name>”},
name字段是可以键入的关键词。在有的平台上,关键词区分大小写。关键词要用引号括起来,其中不允许有空格、斜杠
(/)和等号(=)。如果语法分析器找到两个匹配的命令表,MicroStation 将使用匹配得最好的那一个。如果几个应用中包
含同一个命令名称,MicroStation将忽略后调入的应用中的命令。

五、编译命令表

编译命令行语法为:

rcomp -h filecmd.r

编译后将产生两个输出文件:一个是 filecmd.rsc,用于对键入命令进行检查语法检查;另一个是 filecmd.h 包含了一系列 32


位无符号整数作为应用所支持命令的识别码。

六、联结命令到函数

MDL源文件中的命令初始化函数使用关键词 cmdNumber来联结命令号。命令号给 MicroStation 用户提供了一个熟悉的执行


命令的方式。例如,用户可以键入 uSTN > place myline 来执行一个命令。

如果一个函数名通过 cmdNumber被指定为一个命令名,这个函数可以用以下格式在 MicroStation命令窗口中键入执行。切


记,命令名区分大小写!

uSTN> MDL COMMAND [<taskID>] <functionName> <parameters>

该命令名不能用来建立另外的命令表。命令名被设计来用于帮助程序员测试函数。

七、调入命令表

同消息列表一样,命令表也需要调入才能使用。一般都在主函数或者被主函数调用的初始化函数中调入。下例示范了如何使
用 mdlParse_loadCommandTable 函数调入命令表。

  第九章 事件驱动程序设计
本章将介绍创建功能强大的应用程序的其他一些程序设计工具和技术,这些功能强大的新的特征均为 MicroStation 所提供。

目标:

1、明了事件驱动程序设计的基本概念

2、创建自己的简单应用

一、程序设计工具

MicroStation 定义了很多变量、常量以及函数可供编程者调用,使用系统提供的工具可以大大加快程序开发的进程。

二、内置变量

内置变量是可以被 MDL程序存取的全局变量。MDL 编译器知道变量名和变量类型名称,但并不知道类型定义。因此,编


译器无法确定诸如结构和联合的成员变量的特征,如变量名和变量类型等。任何内置的非基础数据类型(如 int,char,double
等等)都必须包含类型定义(内置变量的类型定义包含在 MDL开发软件包的头文件中)。下面的表中列出了内置变量的变
量名、类型及其描述。

类型 变量名 描述
short dgnBuf[] 在很多操作中的当前元素。DOS 和 Windows NT 下数组大小为 768,CLIX 下
为 780。

MSStateData statedata 与 MicroStation 的当前状态有关的信息,在 global.h 中定义。

Tcb *tcb MicroStation执行的当前状态(如:激活的参数),在 tcb.h中定义。

Mgds_modes mgds_modes 有关当前 MicroStation执行的所有模式信息(只读),在 global.h中定义。

char mgdsPromt[35] 命令区中的提示信息,缺省为“uSTN>”。

short element_draw[8] 元素显示位掩码,每种元素类型占一位。

int mdlError 不同的 MDL函数的附加错误状态。其值在 mdlerrs.h中定义。

int errno 操作系统函数的附加错误状态。

MSGraphicConfig graphConfig 图形配置描述,在 global.h 中定义。

long mdlCommandNumber 最近刚使用过的 MicroStation或 MDl 应用的命令的数量。

MicroStation 还包含内置的可称为浮点常量的变量,可作为只读的变量使用。

常量 值 常量 值

fc_onehalf 0.5 fc_30 30.0

fc_zero 0.0 fc_60 60.0

fc_1 1.0 fc_90 90.0

fc_ml -1.0 fc_180 180.0

fc_2 2.0 fc_270 270.0

fc_3 3.0 fc_360 360.0

fc_4 4.0 fc_750 750.0

fc_5 5.0 fc_1000 1000.0

fc_10 10.0 fc_10000 10000.0

fc_100 100.0 fc_100000 100000.0

fc_p1 0.1 fc_2pi 2.0*pi

fc_p01 0.01 fc_epsilon 0.00001

fc_p001 0.001 fc_mm_per_in 25.40

fc_p0001 0.0001 fc_360000 360000.0

fc_pi pi fc_iang_to_rad (pi/180.0)360000.0

fc_180overpi 180.0/pi fc_rad_to_iang (180.0*360000.0)pi

fc_piover180 pi/180.0 fc_miang_to_rad -1.0*(pi/180.0)/360000.0

fc_piover2 pi/2.0 fc_rmaxi4 RMAXI4

fc_piover3 pi/3.0 fc_rmini4 RMINI4

fc_piover4 pi/4.0 fc_rmaxui4 RMAXUI4

fc_piover66 pi/6.0 fc_tan30 .5773502692

三、函数
MDL的强大功能在于它的很多内置的函数。MDL 应用提供了通过这些函数直接存取 MicroStation的“CAD引擎”的能力。其
中有几千个函数可供使用,以提供直接创建和操纵元素以及界面的功能。这些函数与 MicroStation 内部使用的函数是完全一
致的。

命名规则

MicroStation 的函数使用它自己的特别的命名风格,而且建议使用者创建新的函数时也采用类似的规则,以形成良好的程序
设计风格。

• 内置函数、MicroStation函数使用下列格式:mdlFunctionType_operationDescription

• 用户自定义函数使用下列格式:userFuntionType_operationDescription

• 来自于外部程序与 MDL通讯的函数使用下列格式:extprg_description

• 许多标准的 C 函数已经包含在 M D L库中,保留它们原来的名称。

四、程序设计方法

有两种基本的方法开发 MDL 程序。一种方法是使用输入处理函数、激活 MicroStation命令、模拟命令输入来完成;另一种


方法是调用 MDL的内置函数去完成相应的任务,而不用激活 MicroStation 命令。

• 顺序化程序设计

第一种方法可以称为顺序化程序设计方法。这种方法不善于完成复杂的任务。MicroStation倾向于采用事件驱动的程序设计
方法。如果您采用顺序化程序设计方法,那么您的应用看起来将不象一个 MicroStation 的应用程序。

优点

• 容易完成简单的任务;

• 对 MicroStation的内部处理过程了解的程度要求较低。

缺点

• 要求了解命令的输入;

• 难于使命令透明地使用;

• 多个应用的交互相当复杂;

• 很难与 MicroStation 保持一致。

• 事件驱动程序设计

与第一种方法相比有较大的优势。MicroStation 主要有以下几种事件:

• 数据点

• 键盘输入

• 复位

• 光标移动

在事件驱动程序中,事件驱动程序的运行。MicroStation 是完全的事件驱动程序,MDL 应用也应该采用这种技术。可以把


MDL想象为处理一系列事件的函数集合。MDL 程序员只需创建处理应用产生的特定的事件的响应函数。

在 MicroStation中,所有的事件都由输入处理器(input handler)进行收集。每一种输入处理器收集特定的输入到“输入包”
中,“输入包”验证事件并以标准格式将之放入输入队列中,事件就被发送到到输入队列中。当事件到达队列头部,任务调度
器开始处理该输入消息。调度器将该事件发送到相应的任务之中。整个事件处理的过程称为 MicroStation 输入主循环。

MicroStation主输入循环

输入处理器 输入处理器 输入处理器


输入处理器

数据 键入 复位 其它

五、MicroStation 的命令类型

MicroStation的命令主要有五种命令类型,即 Utility 命令、Primitive命令、Modification 命令、Immediate命令、View命令 。

Utility 命令

Utility 命令通常执行非交互式的函数。当 Utility 命令执行结束时,应调用 mdlState_StartDefaultCommand 或 mdlState_clear。


Utility 命令通常没有命令行参数。

Primitive 命令

Primitive命令是 MicroStation 中最为基础的命令。通常 Primitive命令用于处理数据点或复位。使用 Primitive 命令必须调用函


数 mdlState_startPrimitive. mdlState_startModifyCommand或 mdlState_startFenceCommand。启动一个 Primitive 命令将终止其
它的任何命令。Primitive 命令不会自动终止,除非启动另外一个 Primitive 命令,或 Utility 命令。MicroStation保持一指针集
以处理 Primitive 命令。

Modification 命令

Modification命令是 Primitive 命令的子集。这种命令要求已存在图形元素的选择集。选择元素不一定被修改,但可用作建立


新元素或提供信息。

您可使用两种模式为 Modification命令选择元素:动名词模式首先激活 Modification 命令,然后提示操作者选择元素;名动


词模式(Mac/Motif)使用选择集。必须在激活命令之前进行选择。但是有些 Modification 命令支持选择集,而有的不支持。对
MDL开发者来说,两种模式都是可行的,可以允许用户在不同的模式下完成相同的命令。

Immediate 命令

Immediate命令主要用于修改设置,因此常常带有一个或更多的参数。它不调用函数,也不对事件进行响应。Immediate命令
的运行不影响其他命令的执行的状态.

View 命令

View命令用于修改或更新视图窗口。当收到 reset 命令时,应调用 mdlState_exitViewCommond函数,启动 View 命令将暂时


挂起 Primitive命令的执行或置换当前的 View命令。MicroStation 为 View 命令保留了一系列指针集,从而允许在不中断
primitive的情况下执行 View 命令。

 
六、创建命令

创建一个命令就应该创建相应的处理函数。例如:对于 PLACE LINE 命令来说,有这样几个函数:1.载入应用;2.初始化;


3.放置第一个点;4.放置第二个点;5.显示线的动态变化。

**************************************************************************

/*This is a portion of a ???.mc file.*/

main

(int argc, /*=>命令参数个数* /

char *argv[] /*=>命令参数数组*/

DialogBox *dialogP;

RscFileHandle rscFileH;

/*打开资源文件*/

mdlResource_openFile(&rscFileH,NULL,0);

/*装载命令表*/

if (mdlParse_LoadCommandTable(NULL)==NULL)

mdlOutput_error("Command Table not loaded");

/*登记字串表*/

mdlState_registerStringIds(MSGLISTEDID_Commands,MSGLISTID_Prompts);

/*打开对话框*/

if ((dialogP=mdlDialog_open(NULL,DIALOGID_Samples))==NULL)

mdlOutput_error("Dialog Box not loaded");

**************************************************************************

初始化

命令的初始化函数对所有的命令都是相同的,它是所有命令的入口点,当用户或应用激活命令时被调用.任何执行命令的初始化
均应在该函数中完成.

**************************************************************************

Public cmdlName VOID userCommandInitialization_PlaceLine

char *unparsedP /*用户键入参数*/

cmdNumber CMD_PLACE_LINE

mdlState_startPrimitive(userState_PlaceLineFirstPoint,

userCommandInitia lization_placeLine,

MSGID_PlaceLine,
MSGID_FirstPoint);

**************************************************************************

在命令之后键入的任何参数将被传递给 unparsedP 。

关键词 cmdNumber 将命令表中的命令与其相应的函数关联起来。当键入命令时,将自动调用相关的函数。

如果想在建立命令表之前测试命令的执行,可以用 cmdName来修饰命令。在提示符下键入:

mdl command <命令函数(userCommandInitialization)>

mdlState_startPrimitive 函数被调用来设置数据点、复位等状态函数,显示命令名和提示信息等。第一个参数指定当用户输入
第一个数据点时被调用的函数。第二个参数指定当用户复位时被调用的函数和使用 mdlState_restartCurrentCommand函数被
调用的函数。

MSGID_PlaceLine被定义为 Integer,与命令消息列表中的命令名相关。MSGID_FirstPoint 也为 Integer,它与提示符字串列表


中字串相关。当命令激活时,它显示在命令窗口中。

**************************************************************************

First Data Point

Private void userState_placeLineFirstpoint

DPoint3d *pointP /*用户键入的数据点,DOUBLE 类型*/

int view /*数据点所在视区*/

/*保存当前数据点到栈中*/

statedata.dPointStack[0]=*PointP;

/*设定数据点函数*/

mdlState_setFunction(STATE_DATAPOINT,userState_placeLineSecondPoint);

/*设置动态显示线的函数*/

mdlState_dynamicUpdate(userState_placeLineDynamics,FALSE);

/*显示第二点的提示信息*/

mdlOutput_rscPrintf(MSG_PROMPT,0,MSGLISTEDID_Prompts,MSGID_SecondPoint);

**************************************************************************

数据点被传递到*PointP 并被存储在 statedata.dPointStack 中。视图窗口号被传递到变量 View中。由于 MicroStation 使用


statedata.dPointSatck,会覆盖掉当前的数据。因此,您应该创建您自己的数据点数组存储数据。

mdlState_setFunction设置 userState_placeLineSecondPoint 作为新的数据点处理函数。

mdlState_dynamicUpdate设置 userState_placeLineDynamics作为新的动态显示函数,第二个参数确定有效的元素数据,是否已
在 dgnBuf 之中。

**************************************************************************

Second Data Point

_________________
Private void userState_placeLineSecondpoint

DPoint3d *pointP /*用户键入的数据点,DOUBLE 类型*/

int view /*数据点所在视区*/

MSElementUnion myElement;

/*保存当前数据点到栈中*/

statedata.dPointStack[1]=*PointP;

/*在元素结构中创建线*/

mdlLine_create(&myElement,NULL,statedata.dPointStack);

/*显示线*/

mdlElement_Display(&myElement,NORMALDRAW);

/*加线到设计文件*/

mdlElement_add(&myElement);

/*保存当前最后点,为以后作土使用*/

statedata.dPointStack[0]=statedata.dPointStack[1];

**************************************************************************

数据点被传递到*PointP 并被存储在 statedata.dPointStack 中。视图窗口号被传递到变量 View中。由于 MicroStation 使用


statedata.dPointSatck,会覆盖掉当前的数据。因此,您应该创建您自己的数据点数组存储数据。

userState_placeLineSecondPoint 不仅在第二点中调用,而且在其它点中被调用。

mdlLine_Create 创建一个线元素,第一个参数指定输出缓冲区,第二个参数为显示参数指定输入缓冲区,如为 NULL,则从当


前活动的设置中取得参数。第三个参数指定线的终止点。如果元素创建成功,该函数返回 SUCCESS。

MSElement 与 M SElementUnion 是同一种类型。但它们混合使用时,编译会出现警告信息。

动态显示

动态显示靠指定被光标显示循环调用的函数来实现。光标位置被转换为设计文件坐标,被加以当前捕俘锁定处理,然后转交
给动态显示函数例程。

简单动态显示例程基于当前光标位置创建一个简单元素,将其放入 dgnBuf 中。大部分处理由 MicroStation完成。如果多个


简单元素需要显示,必须使用复合动态技术。在这种情况下,当前光标位置、视图号、显示模式都被传给动态显示例程。该
例程自行负责动态元素的创建和显示。指定一个复合动态显示函数,使用 mdlState_setFunction 指定处理例程。当前光标位置
(以 UORs )被传递给*PointP 并放入到点坐标栈中。新线被创建并被放入到 dgnBuf中。

**************************************************************************

动态显示例程

_________________

Private void userState_placeLineDynamics


(

DPoint3d *pointP /* 用户键入的数据点,DOUBLE 类型*/

int view /*数据点所在视区*/

/*保存当前数据点到栈中*/

statedata.dPointStack[1]=*PointP;

/*在 dgnBuf中创建线*/

mdlLine_create(dgnBuf,NULL,statedata.dPointStack);

**************************************************************************

  第十章 对话框与应用的连接
目标

学会将对话框与应用连接起来的几种方法。要在应用中使用对话框,必须将它们连接起来。有三种方法将应用与对话框连接
起来。

一、命令号

使用对话框元素的命令号发送命令到输入队列中。

**************************************************************************

Ditem_ToggleButton TOGGLE_LockGrid=

CMD_LOCK_GRID_TOGGLE,MCMD

NOSYNONYM,NOHELP,MHELP,

NOHOOK,NOARG,NOMASK,NOINVERT,

"GRID LOCK",""

**************************************************************************

二、存取串

存取串允许对话框直接操纵应用中的单个变量。

**************************************************************************

Ditem_ToggleButton TOGGLE_LockGrid=

CMD_LOCK,MCMD

NOSYNONYM,NOHELP,MHELP,

NOHOOK,NOARG,NOMASK,NOINVERT,
"GRID LOCK","tcb- >control.grid_lock"

**************************************************************************

三、对话框钩子函数

对话框钩子函数包括对话框钩子函数和对话框元素钩子函数两种。

**************************************************************************

Ditem_ToggleButton TOGGLE_LockGrid=

CMD_LOCK,MCMD

NOSYNONYM,NOHELP,MHELP,

HOOKITEMID_GridLock,NOARG,NOMASK,NOINVERT,

"GRID LOCK",""

**************************************************************************

第十一章 命令号与存取串
目标

• 使用命令号连接一个对话框和应用。

• 输出变量与结构

• 整合存取串到应用

• 废弃存取串

作为对话框所用的存取串必须是为之可用的变量。

一、命令号

命令号在文件 cmdlist.h中定义。如果使用 MicroStation 命令,在 commandSource 中使用 MCMD,如果命令定义在您的应用


之中,则使用 LCMD。见第十一章的例程。

二、存取串

连接存取串到应用有几个步骤:

1、定义结构或联合变量

*****************************************

typedef struct samplesglobals

int parameter1;

int parameter2;

} SamplesGlobals;

*****************************************

2、输出结构到类型定义文件
输出结构到类型定义文件,然后使用 rsctype 创建资源。

*****************************************

/*结构定义文件(???typ.mt)部分/

#include "???typ.h"

publishStructures(samplesglobals);

*****************************************

定义联合的头文件不能直接包含到资源文件(*.r)中,因为资源编译器 rcomp.exe 不支持联合。

3、声明并输出变量

为了在运行时刻对 c表达式求值,所有引用变量必须使用 mdlDialog_publish...函数输出。

低级的 mdlCExpression_symbolPublish 也可使用,但 mdlDialog_publish 更容易一些。

除此之外,其相应的资源文件必须已经打开。

**************************************************************************

SamplesGlobals myGlobals;

Public main

int argc;

char *argv[]

char *setP /*C 表达式符号设置指针*/

.......

/*打开资源文件*/

.......

/*设置将被求值的 c 表达式变量*/

setP=mdlCExpression_initializeSet(VISIBILITY_DIALOG_BOX,0,FALSE);

mdlDialog_publishComplexVariable(setP,"samplesglobals","myGlobals",&myGlobals);

.......

/*打开对话框*/

......

**************************************************************************

4、更新资源

如果对话框元素使用了应用的变量,对话框的存取串必须被修改以引用该变量。

**************************************************************************

/*资源文件 .r 部分*/
DItem_TextRsc TEXTID_Samples=

NOCMD,MCMD,NOSYNONYM,NOHELP,MHELP,NOHOOK,NOARG,

1,"%-ld","%ld","1","3",NOMASK,NOCONCAT,

TXT_Parameter1,

"myGlobals.parameter1"

};

**************************************************************************

5、废弃存取串

在对话框的 auxInfo 域可被用于废弃存取串。格式为

"access=\"newAccessString\""

当拥有多个仅仅存取串不同的实例时,可以使用该方式。可以引用同样的对话框元素资源任意次,只需将 auxInfo 指向每个


实例的不同存取串。这种技术模拟对话框资源的覆盖标签。但应注意所有新的存取串都已输出到对话框管理器。

**************************************************************************

/*.r 文件部分*/

DialogBoxRsc DIALOGID_Samples=

......

{{XC*12,GENY(1),XC*10,0},Text,TEXTID_Samples,

ON,0,"","access=\"otherstruct.value\""},

......

};

......

DItem_TextRsc TEXTID_Samples=

NOCMD,MCMD,NOSYNONYM,NOHELP,MHELP,NOHOOK,NOARG,

1,"%-ld","%ld","1","3",NOMASK,NOCONCAT,

TXT_Parameter1,

"myGlobals.parameter1"

};

**************************************************************************

  第十二章 对话框钩子函数
对话框钩子函数是对话框及其元素设计功能最强大的工具之一。它提供了更为强大的功能和更大的灵活性。

目标
• 创建并连接对话框钩子函数。

• 创建并连接对话框元素钩子函数。

一、实现步骤

1、定义对话框标志号

对话框标志号为长整型,MicroStation 的对话框标志号为负值。自定义的对话框标志号应该使用正值,以避免与系统冲突。
对话框和对话框元素的钩子函数不能使用相同的标志号。对话框标志号通常在头文件中定义。

**************************************************************************

#define HOOKITEMID_ToggleButton_Samples 1

#define HOOKDIALOGID_Samples 2

**************************************************************************

2、将标志号与函数相关

通过创建数组 DialogHookInfo来将标志号与函数相关。

**************************************************************************

/* .mc 文件的部分*/

Private DialogHookInfo uHooks[]=

{HOOKITEMID_ToggleButton_Samples,ssamples_toggleButtonHook},

{HOOKDIALOGID_Samples,samples_dialogHook},

**************************************************************************

3、输出对话框到对话框管理器

调用 mdlDialog_hookPublish 来输出 uHooks 数组.通常在 main 函数中实现且必须在相关对话框打开之前.

**************************************************************************

Public main

int argc,

char *argv[]

......

mdlDialog_hookPublish(sizeof(uHooks)/sizeof(DialogHooInfo),uHooks);

.....

/*打开对话框*/

**************************************************************************

4、编写函数
对话框钩子函数示例:

**************************************************************************

Private void basic_dialogHook

DialogMessage *dmP /* => 对话框消息结构指针*/

/* 忽略所有送到模式对话框的消息*/

if (dmP->dialogId != DIALOGID_Basic)

return;

dmP->msgUnderstood = TRUE;

switch (dmP->messageType)

case DIALOG_MESSAGE_DESTROY:

/* 当对话框关闭时卸载该 MDL 应用*/

mdlDialog_cmdNumberQueue (FALSE, CMD_MDL_UNLOAD,

mdlSystem_getCurrTaskID(), TRUE);

/* mdlSystem_getCurrTaskID 在 MDL文档中忽略错误处理。返回指向任务 ID 值的字符指针。*/

break;

};

default:

dmP->msgUnderstood = FALSE;

break;

**************************************************************************

对话框元素钩子函数示例:

**************************************************************************

Private void basic_toggleButtonHook

DialogItemMessage *dimP /* =>指向消息结构的指针*/

dimP->msgUnderstood = TRUE;
switch (dimP->messageType)

case DITEM_MESSAGE_CREATE:

basicGlobals.parameter2 = 0;

break;

default:

dimP->msgUnderstood = FALSE;

break;

**************************************************************************

5、添加对话框到资源

创建了对话框钩子函数之后,必须将它与对话框资源相连,才能产生交互式的应用。

**************************************************************************

DialogBoxRsc DIALOGID_Basic =

DIALOGATTR_DEFAULT | DIALOGATTR_SINKABLE,

25*XC, 7*YC,

NOHELP, MHELP, HOOKDIALOGID_Basic, NOPARENTID,

TXT_BasicDialogBox,

{{X1,GENY(1),XW,0}, Text, TEXTID_Basic, ON, 0, "", ""},

{{X1,GENY(2),XW,0}, OptionButton, OPTIONBUTTONID_Basic, ON, 0, "", ""},

{{X2,GENY(4),BTN_WIDTH,0}, PushButton, PUSHBUTTONID_OModal, ON, 0,"",""},

};

DItem_ToggleButtonRsc TOGGLEID_Basic =

NOCMD, LCMD, NOSYNONYM, NOHELP, MHELP,

HOOKITEMID_ToggleButton_Basic, NOARG, NOMASK, NOINVERT,

TXT_IncrementParameter1,

"basicGlobals.parameter2"

};

**************************************************************************
2. 对话框钩子的消息结构

**************************************************************************

typedef struct dialogmessage

boolean msgUnderstood;

int messageType; /* 消息类型*/

DialogBox *db; /*对话框句柄*/

long dialogId; /* 对话框的资源 ID*/

Void *userDataP; /* 在 CREATE 时被用户设置*/

Union

…… /* 消息结构联合(随消息类型的不同而不同) */

} u;

} DialogMessage;

**************************************************************************

3. 对话框钩子消息类型

对话框钩子消息类型定义在 dlogitem.h中。

**************************************************************************

#define DIALOG_MESSAGE_CREATE 0 /* 对话框元素创建之前传送*/

#define DIALOG_MESSAGE_INIT 1 /* 对话框元素创建之后传送 */

#define DIALOG_MESSAGE_DESTROY 2 /* 窗口关闭消息 */

#define DIALOG_MESSAGE_UPDATE 3 /* 窗口更新消息 */

#define DIALOG_MESSAGE_RESIZE 4 /* 窗口尺寸变化消息 */

#define DIALOG_MESSAGE_CALCSIZE 5 /* 以新字体索引计算尺寸*/

#define DIALOG_MESSAGE_FONTCHANGED 6 /* 对话框字体索引被改变*/

#define DIALOG_MESSAGE_SYNCH 7

#define DIALOG_MESSAGE_BUTTON 8 /* 得到按钮事件 */

#define DIALOG_MESSAGE_KEYSTROKE 9

#define DIALOG_MESSAGE_FOCUSIN 10

#define DIALOG_MESSAGE_FOCUSOUT 11

#define DIALOG_MESSAGE_ITEMFOCUSIN 12

#define DIALOG_MESSAGE_ITEMFOCUSOUT 13

#define DIALOG_MESSAGE_CHILDDESTROYED 14 /* 子窗口从屏摹移去 */

#define DIALOG_MESSAGE_ACTIONBUTTON 15

#define DIALOG_MESSAGE_USER 16
#define DIALOG_MESSAGE_ANOTHEROPENED 17 /* 在 INIT 消息之后发送 */

#define DIALOG_MESSAGE_ANOTHERCLOSED 18 /* 在 DESTROY 消息之前发送 */

#define DIALOG_MESSAGE_STATECHANGED 19 /* 在消息发送到对话框元素之后发送*/

#define DIALOG_MESSAGE_HIDE 20 /* 在对话框隐藏之前发送*/

#define DIALOG_MESSA GE_MINIMIZE 21 /* 在 X windows 下不支持*/

#define DIALOG_MESSAGE_MAXIMIZE 22 /* 在 X windows 下不支持* /

#define DIALOG_MESSAGE_ACTIVATE 23 /* 5.0 */

#define DIALOG_MESSAGE_BEFOREDESTROY 24 /* 5.0 */

#define DIALOG_MESSAGE_PREBUTTON 25 /* 5.0主要用于弹出式菜单 */

**************************************************************************

缺省情况下,只有六种消息类型被发送到对话框钩子函数:DIALOG_MESSAGE_CREATE,DIALOG_MESSAGE_INIT ,
DIALOG_MESSAGE_DESTROY,DIALOG_MESSAGE_BEFOREDESTROY,DIALOG_MESSAGE_HIDE,
DIALOG_MESSAGE_USER。

**************************************************************************

CREATE 在对话框元素钩子函数被发送 create消息时发送。

INIT 在所有对话框元素钩子函数被发送 create 消息时发送。

DESTROY 在所有对话框元素钩子函数被发送 destroy 消息时发送。

UPDATE 在所有 update消息被发送到对话框元素元素之后被发送。

RESIZE 在窗口尺寸变化(或移动)之后,但在 Update之前。(don't draw here simply move & rescale).

CALCSIZE window mgr needs to know new size since dialog

may be moved to screen w/ different screen font.

FONTCHANGED dialog has been moved to a screen w/ different screen

font.

BUTTON 按钮消息发送到对话框元素之后发送。

FOCUSIN 当对话框在对话框元素之前收到焦点时发送。(sent when dialog receives focus before item focus in.)

FOCUSOUT sent when dialog loses focus after item focus out.

ITEMFOCUSIN sent after item in dialog gets focusIn message.

ITEMFOCUSOUT sent after item in dialog gets focusOut message.

**************************************************************************

如果需要处理其他的消息,需要响应 DIALOG_MESSAGE_CREATE消息,并将 interests中的其他消息属性设为 TRUE.对话框


元素将接收所有的消息,不用进行例外的处理.

**************************************************************************

Private void dialogNameHook

dialogMessage *dmp

{
dmp->msgUnderStood=TRUE;

SWITCH (dmp->messageType)

case DIALOG_MESSAGE_CREATE:

dmp->u.create.interests.updates=TRUE;

dmp->u.create.interests.mouses=TRUE;

...

break;

case DIALOG_MESSAGE_DESTROY:

...

break;

default:

dmp_msgUnderstood=FALSE;

...

**************************************************************************

4. 对话框元素消息结构

typedef struct dialogitemmessage

boolean msgUnderstood;

int messageType; /* 消息类型*/

DialogBox *db; /*对话框句柄*/

long dialogId; /* 对话框的资源 ID*/

int itemIndex; /* 消息被发送的子对话框元素的索引*/

DialogItem *dialogItemP; /*元素的对话框信息*/

int auxMessageType; /*对话框元素指定 msg 类型*/

void *auxInfoP; /*对话框元素指定 msg 信息*/

Union

{
…… /* 消息结构联合(随消息类型的不同而不同) */

} u;

} DialogItemMessage;

**************************************************************************

五、对话框元素消息类型

**************************************************************************

#define DITEM_MESSAGE_CREATE 50

#define DITEM_MESSAGE_DESTROY 51

#define DITEM_MESSAGE_DRAW 52

#define DITEM_MESSAGE_FONTCHANGED 53

#define DITEM_MESSAGE_BUTTON 54

#define DITEM_MESSAGE_SETSTATE 55

#define DITEM_MESSAGE_SYNCHRONIZE 56 /* match app state */

#define DITEM_MESSAGE_HIGHLIGHT 57

/* Messages only sent to keystrokable items */

#define DITEM_MESSAGE_KEYSTROKE 58

#define DITEM_MESSAGE_POSTKEYSTROKE 59 /* after default processing */

#define DITEM_MESSAGE_FOCUSIN 60

#define DITEM_MESSAGE_FOCUSOUT 61

/* Messages for application support (not sent by dialog manager) */

#define DITEM_MESSAGE_SETENABLEDSTATE 62

#define DITEM_MESSAGE_MOVE 63

#define DITEM_MESSAGE_SETEXTENT 64

#define DITEM_MESSAGE_SETLABEL 65

#define DITEM_MESSAGE_GETSTATE 66 /* gets appl state */

#define DITEM_MESSAGE_GETVALUE 67 /* gets item value not app state */

#define DITEM_MESSAGE_SETVALUE 68 /*does NOT set application state*/

/* item hook messages (never sent TO item handlers) */

#define DITEM_MESSAGE_INIT 69 /* after initial getState */

#define DITEM_MESSAGE_QUEUECOMMAND 70

#define DITEM_MESSAGE_STATECHANGED 71 /* notification only */

#define DITEM_MESSAGE_USER 72

#define DITEM_MESSAGE_ALLCREATED 73 /* 4.2 */

#define DITEM_MESSAGE_DOWNMOTION 74 /* 5.0 */

/* handler initialization & cleanup messages */

#define DITEM_MESSAGE_HANDLERLOADED 100


#define DITEM_MESSAGE_HANDLERUNLOADED 101

#define DITEM_MESSAGE_ACTIVATE 110 /* 5.0 */

#define DITEM_MESSAGE_ATTACH 111 /* 5.0 */

#define DITEM_MESSAGE_GENRESOURCE 112 /* 5.0 */

#define DITEM_MESSAGE_GUIMODECHANGED 113/*5.0;userPrefsP->guiMode changed*/

#define DITEM_MESSAGE_GETMNEMONICS 114 /* 5.0; */

#define DITEM_MESSAGE_SHOWHELP 115 /* 5.0 */

#define DITEM_MESSAGE_CLEANUP 116 /* 5.0 */

**************************************************************************

六、跟踪钩子函数

对话框管理器提供的 Messages 对话框可以跟踪显示消息列表。最多可以显示 100 行,当超过时,将删除最前面 50 行。对话框


管理器可以响应请求,打印出发送到钩子函数的消息的信息。具体命令如下:

DMSG ITEMDEBUG [ON|OFF] 打开或关闭所有的对话框元素的消息显示,如果对话框元素没有钩子函数,将没有消息被显


示。

DMSG DIALOGDEBUG [ON|OFF] 打开或关闭所有的对话框的消息显示,如果对话框没有钩子函数,将没有消息被显示。

DMSG CLEARDEBUG 是否显示跟踪的细节信息。

DMSG OPENMSGFILE 文件名 将消息跟踪写入文件,如果文件已经存在,将被覆盖。

DMSG APPENDMSGFILE 文件名 将消息跟踪写入文件,如果文件已经存在,将追加到末尾。

DMSG CLOSEMSGFILE 将消息跟踪文件关掉。

DMSG ACTION SHOWMNEMONIC 按照字母顺序显示活动对话框键盘存取串,如果想在命令窗口之外的其他地方显示,可


以使用 CTRL -ALT -SHIFT -M 或定义自己的功能键菜单。

关闭 Messages窗口将关闭所有的消息跟踪,要继续跟踪必须重新键入使用的命令。

第十三章 启动应用的几种高级方法
通过设置环境变量 MS_DGNAPPS和 MS_INITAPPS可以指定 MicroStation 自动启动的应用程序。

目标

• 配置自动装入的应用程序

• 了解 OFF_Line方法的程序设计

• 学会怎样存取应用程序参数

一、MS_DGNAPPS

MS_DGNAPPS变量可以通过用户配置菜单来设置,该菜单允许用户选择自动启动的应用程序。

MS_DGNAPPS指定的应用在 MicroStation启动到设计文件打开的时间内运行。当用户打开设计文件或从文件菜单中选择了
关闭并打开另一个设计文件是运行该程序。

如果用户键入 rd=command或从文件菜单中选择了打开时,该指定应用程序将不被运行。

典型处理

1. 打开资源文件
2. 装入命令表

3. 打开工具条或其他非模式窗口,MS_DGNAPPS不支持模式窗口.

4. 控制返回到 MicroStation.

二、MS_INITAPPS

如果希望永久设置该变量,将下列配置行加入批文件或 script 文件,在 CLIX 系统上,加入 SHOME简介文件中。在 PC 上加


入 autoexec.bat 文件中。

MS_INITAPPS=<APPICATION>,[TASKID],[/d];...

多个应用名以分号隔开,如要跟踪执行,加入 /D 选项。

被 MS_INITAPPS指定的应用的 MicroStation启动到进入图形模式之前执行。

图形和设计文件在 MS_INITAPPS应用初始化时并不被激活。应用程序自身决定是否初始化图形模式并激活设计文件或装入
cell 库。

MS_INIT APPS与 Front end 程序设计

所谓 front end 程序是指在进入图形模式之前运行的程序。一般为工程管理器、安全设置、帐户管理等应用程序。

MicroStation Maneger为 MicroStation 提供的 front end程序。它提供了一系列文件管理的操作。可以定义环境变量


MS_INITAPPS来替换掉该应用程序。

MS_INITAPPS与 Off_line程序设计

MS_INITAPPS可用于 Off_line 程序设计,所谓 Off_line 程序设计,即是不需要用户交互的程序。它以批处理的方式处理数


据。

Off_line程序处理过程

1. 命令行参数被传递

2. 设计文件被使用 mdlSystem_newDesignFile函数打开.

3. 被指定的设计文件或一系列设计文件被打开并处理.

4. 最后一个设计文件被使用 mdlSystem_closeDesignFile 函数关闭.

5. 当应用关闭的同时,也将关闭 MicroStation .使用标准的 C 函数 EXIT 或 MDL 的退出函数均可用于退出应用.

Off_line程序设计要点

如果要使用对话框,使用 mdlSystem_enterGraphicsExtended函数。

当没有设计文件打开时,应用将退出到操作系统下。

跟踪 MS_INITAPPS指定应用的执行,使用 /d 启动选项。但必须在编译、连接时使用了 - g 选项。

三、MicroStation 命令行参数

-sfile 指定启动命令文件(MicroStation Script 文件)。

-usize 指定 UNDO 操作所用的内存量(以 kb 计算)。

-csize 指定元素的缓冲区大小(以 k b 计算)。

-r 以只读方式打开文件。

-o 不连接引用文件。

-iargument 设置为 MS_INITAPPS所用的参数.将被 MicroStation 所忽略。


- wufile 指定用户配置文件。

- widir 指定用户界面文件所在目录。

- wpfile 定义工程配置文件。

- wdfile 指定数据库配置文件。

- waappname 启动指定的应用作为 MS_INITAPPS应用。

debug=n 设置调试级别到 n。输出在当前目录的 msdebug.txt 文件中。如果使用此选项,MicroStation处理之后将退出。

命令行处理过程

1、标准 C 方式

main(argc,argv[])

argc 命令行参数个数

argv[] 命令行参数串数组指针。

2、MS_INITAPPS 方式

例如: ustation -r -ix=2 file.dgn

================================================================

启动类型 argv[0] argv[1] argv[2] argv[3] argv[4] argv[5]

MS_INITAPPS app_name MS_INITAPPS -r -ix=2 file.dgn

3、MS_DGNAPPS

参数不能传递给 MS_DGNAPPS启动的应用.其存取参数如下:

=================================================================

启动类型 argv[0] argv[1] argv[2] argv[3] argv[4] argv[5]

MS_DGNAPPS app_name MS_DGNAPPS

=================================================================

4、从启动元素装入的应用

当应用从启动元素装入时,将忽略从命令行传入的参数。启动元素可用函数 mdlSystem_createStartupElement函数,然后使用
mdlElement_add 函数加到设计文件。它将存取如下所示的参数:

mdlSystem_createStartupElement(&element,"app_name 100 blue");

=================================================================

启动类型 argv[0] argv[1] argv[2] argv[3] argv [4] argv[5]

Start-up app_name STARTUP 100 blue

=================================================================

5、从命令窗口提示符装入的应用

例如: mdl load app_name 100 red

=================================================================

启动类型 argv[0] argv[1] argv[2] ar gv[3] argv[4] argv[5]

Loaded from uSTN> Prompt app_name USER 100 red

=================================================================
6、从 MDL 对话窗口装入的应用

参数将不被传递。

=================================================================

启动类型 argv[0] argv[1] argv[2] argv[3] argv[4] argv[5]

Loaded from MDL dialog box app_name USER

第十四章 元素描述符
元素描述符(Element Descriptors)为程序员提供了另一种访问文件中元素的方法。它们实际上是一系列简单或复杂元素的
多连接列表,元素描述符中的指针允许程序员访问列表中的元素。

一、什么是元素描述符

元素描述符是一个多连接的元素列表。这种多连接给程序员提供了从多个方向纵横浏览列表的能力。

MicroStation 会为元素描述符分配内存空间,这些空间是分配在 MicroStation 的内存堆里的。在应用程序中不能使用


malloc(sizeof(***))的方式为元素描述符分配内存空间,因为元素描述符的真正空间是根据其中所包含的元素的数量多少而动
态确定的,并非一个常量。尽管元素的内存空间是 MicroStation 分配的,MDL 应用仍然有使用 mdlElmdscr_freeAll函数释元素
描述符所占内存的责任。

复杂元素不能使用简单的结构来描述,它们拥有大小不等的各种元素。如果一个复杂元素的数据存放在 MSElementUnion结构
或者 dgnBuf 中,只能访问元素的头部信息,要访问整个元素的所有信息,则需使用元素描述符。

元素描述符确认复杂元素的头部信息,包括:范围、类别位图(Class Bitmap)、级别指示器(Level Indicator)、描述字、元素个


数、极数(Number of Poles)、最大文本长度等等。当元素描述符写入设计文件时,元素的有效性将自动检查。

二、元素描述符的结构

元素描述符的结构定义在$(MS)/mdl/include/mselems.h文件中。对于复杂元素头(比如要素头和文本节点)h.isHeader 应设置
为 TRUE。

MSElementUnion和 MSElement 的联合是相同的类型。请注意,当你使用其中一个代替另一个并包含标准头文件(.fdf)时,编


译器都会发出警告,因为编译器要执行严格的原型检查。这时您可以不去理会它。

三、简单的元素描述符
图 14-1、Simple Cell 图 14-2、Nested Cell

图 14-3、Selection Set

四、递归

元素描述符因为其多连接特性,导致了它们自身被递归浏览。递归和元素描述符非常适合嵌套状的复杂元素,因为对于嵌套的
每一层,应用会简单地调用递归函数进行任何处理。因为难于确定嵌套级数,所以调试递归程序比较麻烦。

当连接一个递归程序时,可能不得不增加堆栈的大小,至于增加多少,这得看自动变量的数量。

1、创建元素描述符的两种基本方法

2、需要记住的几件事

? 使用完元素描述符后,一定要释放其内存;

? 元素描述符可能会使用大量内存;

? 千万别使用 sizeof(MSElement)来复制元素描述符。

五、 AppCelement应用例程

/*----------------------------------------------------------------------+

||

| Copyright (1993) Bentley Systems, Inc., All rights reserved. |

||

| "MicroStation", "MDL", and "MicroCSL" are trademarks of Bentley |

| Systems, Inc. and/or Intergraph Corporation. |

||

| Limited permission is hereby granted to reproduce and modify this |

| copyrighted material provided that the resulting code is used only in |

| conjunction with Bentley Systems products under the terms of the |

| license agreement provided therein, and that this notice is retained |

| in its entirety in any such reproduction or modification. |

||

+----------------------------------------------------------------------*/

/*----------------------------------------------------------------------+

||

| $Logfile: J:/mdl/examples/applcelm/applcelm.mcv $

| $Workfile: applcelm.mc $

| $Revision: 1.6 $

| $Date: 16 Dec 1994 16:14:06 $

||

+----------------------------------------------------------------------*/
/*----------------------------------------------------------------------+

||

| Function - |

||

| applcelm.mc -- Example of application element (type 66, level 20) |

| element manipuation. |

||

| This example application shows how to use the IGDS type 66 level 20 |

| elements to store application specific data. The application |

| elements are application defined, and have no real design data |

| except for the element headers. This application also shows the |

| use of the Binary Compatibility extensions to MDL, using both |

| the mdlCnv_bufferToFileFormat and mdlCnv_bufferFromFileFormat |

| routines. |

||

| To use this application, set MS_INITAPPS to applcelm. To run this |

| program, run MicroStation and specify the design file to use. |

| Applcelm will create and write an application element to |

| the design file specified, then find and read the element back |

| and display the contents on the screen. The application does not |

| go into graphics mode at all. |

||

| The topics illustrated in this example apply both to application |

| elements and user- defined attribute data. For more information |

| on the use of user attribute data, see the irrigate example. |

||

| Our application data element will have this format : |

||

| word 0 = type, level (type MICROSTATION_ELM, APPLICATION_LEVEL) |

| word 1 = words to follow (APPL_DATA_WORDS + 18 - 2 = 38) |

| word 2 through word 13 = range cube (entire design plane) |

| word 14 = graphic group (0) |

| word 15 = index to attributes (24) |

| word 16 = properties (class) (all 0) |

| word 17 = symbology (0) |

| word 18 = signature word (EXAMPLE_SIGNATURE) |

| word 19,20 = application lval1 |


| word 21-24 = application dval1 |

| word 25-28 = application dval2 |

| word 29 = application sval1 |

| word 30 = application sval2 |

| word 31-40 = application string |

||

|-- -- - -- -- -- -- - -- -- -- - -- -- -- -- |

||

| Public Routine Summary - |

||

| applcelm_createApplicationHeader - Create application element |

| header |

| applcelm_findApplicationElement - Find application element in |

| design file |

| applcelm_retrieveApplicationData - Get copy of application |

| element from design file |

| applcelm_storeApplicationData - Write application element to |

| design file |

| main - main entry point |

||

+----------------------------------------------------------------------*/

/*----------------------------------------------------------------------+

||

| Include Files |

||

+----------------------------------------------------------------------*/

#include <mdl.h>

#include <mselems.h>

#include <scanner.h>

#include <cexpr.h>

#include <rscdefs.h>

#include <msbnrypo.fdf>

#include <stdarg.h>

#include "applcelm.h"

/*----------------------------------------------------------------------+
||

| Local defines |

||

+----------------------------------------------------------------------*/

/* The following macros evaluate to function calls for the BIG ENDIAN */

/* machines (680x0, Sun, etc) and to nothing for other systems. They */

/* are used to swap the byte order of two - byte integers to accommodate */

/* the byte ordering of these machines. */

#if defined (BIG_ENDIAN)

# define SWAP_BYTE(arg1) mdlCnv_swapByteArray(arg1, 1)

# define SWAP_BYTE_ARRAY(arg1,arg2) mdlCnv_swapByteArray(arg1,arg2)

#else

# define SWAP_BYTE(arg1)

# define SWAP_BYTE_ARRAY(arg1,arg2)

#endif

/*----------------------------------------------------------------------+

||

| Local type definitions |

||

+----------------------------------------------------------------------*/

typedef struct scanbuffer

short offset;

short sector;

ApplElement appEl;

} ScanBuffer;

/*===============================================================

||

| Minor Code Section - These routines are specific to an application, |

| but can be used with very little modification |

| for many cases |

||

+===============================================================*/

/*----------------------------------------------------------------------+
||

| name applcelm_createApplicationHeader - create application |

| header common to all application elements |

||

| author BSI 9/91 |

||

+----------------------------------------------------------------------*/

Public void applcelm_createApplicationHeader

ApplElement *elP, /* <= element to set up header info */

int words, /* => words in application data */

int signature /* => a ssigned signature value */

short *signatureP;

/* ------------------------------------------------------------------

Set up the required portions of the header :

Set type to the MicroStation application element.

Set level to the assigned level.

Set wo rds to the words to follow in our element.

Set range to the entire design cube.

------------------------------------------------------------------ */

elP->ehdr.type = MICROSTATION_ELM;

elP->ehdr.level = APPLICATION_LEVEL;

elP->ehdr.words = HDR_WORDS_AFTER_WTF + words;

elP->ehdr.xlow = elP->ehdr.ylow = elP- >ehdr.zlow =

mdlCnv_toScanFormat (MINI4);

elP->ehdr.xhigh = elP- >ehdr.yhigh = elP->ehdr.zhigh =

mdlCnv_toScanFormat (MAXI4);

/* ------------------------------------------------------------------

We set the index to attributes in the display header portion

of the element. In this case we have no attribute data on the

element, so there is a simple relationship between words to follow

and the attribute index.

------------------------------------------------------------------ */
elP->dhdr.attindx = elP ->ehdr.words - WORDS_BTWN_WTF_ATTRINDX;

/* the rest of the header information is already correctly set to zero */

/* Set the signature word in the element. We need to swap the signature */

/* word because the scanner operates on the first 20 words of the */

/* element. When the scanner returns this to us, it will be correctly */

/* aligned and swapped. */

signatureP = ((short *)elP) + 18;

*signatureP = EXAMPLE_SIGNATURE;

SWAP_BYTE (signatureP);

/*----------------------------------------------------------------------+

||

| name applcelm_findApplicationElement - finds application |

| element in design file |

||

| author BSI 9/91 |

||

+----------------------------------------------------------------------*/

Public int applcelm_findApplicationElement

ApplElement *elP, /* <= file format element */

int signature, /* => our assigned signature */

ULong ddbId /* => Data def. resource used for conversion. */

int status, scanSize;

Scanlist scanList;

ScanBuffer *sbP;

short *signatureP, *fileDataP;

ULong filePos;

short possibleSignature;

ULong scanBuf[1024];

memset (elP, 0, sizeof (ApplElement));


 

/* zero everything we won't need */

memset (scanBuf, 0, sizeof (scanBuf));

/* initialize the scanList */

mdlScan_initScanlist (&scanList);

mdlScan_noRangeCheck (&scanList);

scanList.scantype = ELEMTYPE | LEVELS | ONEELEM | BOTH;

scanList.typmask[4] = TMSK4_MICROSTATION_ELM; /* scan for type 66 only */

scanList.levmask[1] = LMSK1_MSAPPINFO_LEVEL; /* scan for level 20 only */

/* use mdlScan_initialize, mdlScan_file to find element */

mdlScan_initialize (0, &scanList);

do

status = mdlScan_file (scanBuf, &scanSize,

sizeof (scanBuf), &filePos);

if (scanSize)

sbP = (ScanBuffer *) &scanBuf[0];

/* Copy element header and signature word. */

memcpy (elP, &sbP ->appEl, sizeof (Header) + sizeof(short));

/*-----------------------------------------------------------

Hard code offset in element to signature to avoid

platform-specific padding that comes from using member name.

-----------------------------------------------------------*/

signatureP = ((short *)elP) + 18;

SWAP_BYTE (signatureP);

if (*signatureP == signature)

fileDataP = ((short *) &sbP ->appEl) + 19;

/* Convert the rest of the element to internal format. */

status = mdlCnv_bufferFromFileFormat (NULL, NULL,


(byte *)&elP->appData, NULL,

(byte *)fileDataP, ddbId);

return SUCCESS;

} while (scanSize && (status == BUFF_FULL));

/* if we did not find element, return error */

memset (elP, 0, sizeof (ApplElement));

return ERROR;

/*================================================================

||

| Private Utility Routines - These routines are very specific to |

| our particular example |

||

+===============================================================*/

/*----------------------------------------------------------------------+

||

| name applcelm_retrieveApplicationData - get application data |

from design file |

||

| author BSI 9/91 |

||

+----------------------------------------------------------------------*/

Public int applcelm_retrieveApplicationData

ApplicationData *appData, /* <= pointer to data for storage */

ULong ddbId

int status;

ApplElement el;

if ((status = applcelm_findApplicationElement (&el, EXAMPLE_SIGNATURE,

ddbId))
== SUCCESS)

memcpy (appData, &el.appData, sizeof (ApplicationData));

return status;

/*----------------------------------------------------------------------+

||

| name applcelm_storeApplicationData - store application data |

| in file |

| author BSI 9/91 |

||

+----------------------------------------------------------------------*/

Public int applcelm_storeApplicationData

ApplicationData *appDataP, /* => Data to be converted and saved. */

ULong ddbId /* => RscId for data conv. rules. */

ApplElement el;

ULong filePos;

int fileFormatDataSize, status;

short *fileDataP;

/* Initialize element. */

memset (&el, 0, sizeof (ApplElement));

applcelm_createApplicationHeader (&el, APPL_DATA_WORDS,

EXAMPLE_SIGNATURE);

/* Convert the data portion of the element. */

fileDataP = ((short *) &el) + 19;

status = mdlCnv_bufferToFileFormat (&fileFormatDataSize,

(byte *)fileDataP,

NULL, (byte *)appDataP, ddbId);

if (status != SUCCESS)

return status;
 

/* store the element at the end of file */

filePos = mdlElement_add (&el);

/* check return from mdlElement_add. If file position is 0, a */

/* problem occured with the write, and mdlErrno has the error */

/* number in it. Otherwise return SUCCESS. */

if (filePos == 0L)

return mdlErrno;

else

return SUCCESS;

/*----------------------------------------------------------------------+

||

| Name applcelm_rscSprintf |

||

| Author BSI 3/93 |

||

+----------------------------------------------------------------------*/

Private void applcelm_rscSprintf

char *stringP, /* <= Result of sprintf from resource */

int messageNumber, /* => Index into msg list for format str */

... /* => Any other optional arguments */

va_list ap;

char tempStr[1024];

va_start (ap, messageNumber);

*stringP = tempStr[0] = '\0';

mdlResource_loadFrom StringList (tempStr, NULL, STRINGID_Messages,

messageNumber);

vsprintf (stringP, tempStr, ap);

 
va_end (ap);

/*================================================================

| Major Public Code Section

+===============================================================*/

Public main

int argc,

char *argv[]

void *setP;

RscFileHandle rfHandle;

ApplicationData appData, savedAppData;

short status;

char fileName[MAXFILELENGTH],

fileToOpen[MAXFILELENGTH],

msgBuffer[128];

memset (&appData, 0, sizeof (ApplicationData));

memset (&savedAppData, 0, sizeof (ApplicationData));

mdlSystem_enterGraphics ();

if (argc < 4)

mdlOutput_rscPrintf (MSG_ERROR, NULL, STRINGID_Messages,

MSGID_Usage);

exit (0);

else

strcpy (fileName, argv[3]);

/* Open our resource file for access to Data Definition resources. */

if (mdlResource_openFile (&rfHandle, NULL, RSC_READONLY) != SUCCESS)


{

mdlOutput_rscPrintf (MSG_ERROR, NULL, STRINGID_Messages,

MSGID_CantOpenResource);

exit (1);

mdlFile_find (fileToOpen, fileName, "MS_DEF", ".dgn");

mdlOutput_rscPrintf (MSG_ERROR, NULL, STRINGID_Messages, MSGID_FileName,

fileToOpen);

status = mdlSystem_newDesignFile (fileToOpen);

if (status)

mdlOutput_rscPrintf (MSG_ERROR, NULL, STRINGID_Messages,

MSGID_ErrorOpeningDGN);

exit (1);

/* Initialize example application data. */

savedAppData.longValue1 = 44;

savedAppData.doubleValue1 = 120.45;

savedAppData.doubleValue2 = 145.72;

savedAppData.shortValue1 = 60;

savedAppData.shortValue2 = 30;

strcpy (savedAppData.string, "the 20 char string");

/* Retrieve application data from file */

if (status = applcelm_retrieveApplicationData (&appData,

RSCID_AppData_DataDefBlock))

/* Write application data to design file. */

if (status = applcelm_storeApplicationData (&savedAppData,

RSCID_AppD ata_DataDefBlock))

mdlOutput_rscPrintf (MSG_ERROR, NULL, STRINGID_Messages,

MSGID_ErrorWritingApplElem);

exit (1);

}
 

/* Retrieve application data from file */

if (status = applcelm_retrieveApplicationData (&appData,

RSCID_AppData_DataDefBlock))

mdlOutput_rscPrintf (MSG_ERROR, NULL, STRINGID_Messages,

MSGID_ErrorReadingApplElem);

exit (1);

/* compare data we set and data we retrieved to make sure they are */

/* the same. */

mdlResource_loadFromStringList (msgBuffer, NULL, STRINGID_Messages,

MSGID_FormatStr01);

mdlResource_loadFromStringList (msgBuffer, NULL, STRINGID_Messages,

MSGID_FormatStr02);

applcelm_rscSprintf (msgBuffer, MSGID_FormatStr03, savedAppData.longValue1,

appData.longValue1);

mdlDialog_dmsgsPrint (msgBuffer);

applcelm_rscSprintf (msgBuffer, MSGID_FormatStr04, savedAppData.doubleValue1,

appData.doubleValue1);

mdlDialog_dmsgsPrint (msgBuffer);

applcelm_rscSprintf (msgBuffer, MSGID_FormatStr05, savedAppData.doubleValue2,

appData.doubleValue2);

mdlDialog_dmsgsPrint (msgBuffer);

applcelm_rscSprintf (msgBuffer, MSGID_FormatStr06, savedAppData.shortValue1,

appData.shortValue1);

mdlDialog_dmsgsPrint (msgBuffer);

applcelm_rscSprintf (msgBuffer, MSGID_FormatStr07, savedAppData.shortValue2,

appData.shortValue2);
mdlDialog_dmsgsPrint (msgBuffer);

applcelm_rscSprintf (msgBuffer, MSGID_FormatStr08, savedAppData.string,

appData.string);

mdlDialog_dmsgsPrint (msgBuffer);

exit (0);

第十五章 交互式元素操纵
MDL应用最常用的一个操作就是定位和修改已有的元素或者元素组。

一、交互式元素操纵

元素操纵(manipulation)或者修改(modification)命令是 primitive 命令的一个子集。操纵命令和 primitive 命令之间的区别在


于操纵命令要求设计文件中有已经存在的元素。

元素的定位操作要求使用 MicroStation 的设备调用扫描器(Scanner)。MDL在扫描器顶层提供一个界面(mdlLocate 等函数)


进行交互式的元素定位操作。通过指定搜索判别式,MDL 应用可以定位设计文件中的已有元素。应用中可以使用多种搜索判
别式,比如:元素类型、位置、属性、级别等。

要知道,在几乎所有的 MicroStation 的操纵命令中,均支持两种标定元素的方式。第一种方式是提示并等待用户标定一个或者


一组元素,另一种方式是在激活操纵命令之前先标定元素或元素组。大部分的 MDL命令都支持这两种方式。

二、执行操纵命令

编写操纵命令有几个步骤,以下是几个基本的步骤:

1、设置查询判别式

在 tcb中有一组元素定位变量,它们定义了当前的查询模板(Mask )。MDL 提供了设置这些 tcb变量的函数。最基本的函数


有两种:对于通用的查询,使用 mdlLocate_normal 函数,函数对于查询指定类型的元素,使用 mdlLocate_noElemNoLocked以及紧
跟其后的 mdlLocat e_setElemSearchMask函数。

2、初始化定位处理

使用 mdlState_startModifyCommand函数紧跟 mdlLocate_init 函数来初始化定位操作并开始从设计文件顶部定位元素。对于栅


栏操作,使用 mdlState_startFenceCommand。

3、传送元素到处理函数

mdlModify_elementSignle, mdlModify_elementMuti, mdlModify_elementDescr函数可以用于传送元素到用户定义的处理函数。

4、元素处理

一个用户定义的元素函数将接受每一个元素进行处理,在这个函数中,可以使用 mdlElement_…或 mdlElemdscr…函数进行实际


的元素修改处理。

5、重新开始定位操作
mdlLocate_restart 函数可以用于重新开始定位处理。它将重新初始化定位逻辑并提示用户标定下一个元素。栅栏操作不需要
这一步。

三、交互式操纵命令的源码

下例中示范了如何使用操纵命令查找线和线串并把它们的颜色和风格修改成当前的激活值。

1、初始化定位操作

典型的交互式元素修改命令使用 mdlState_startModifyCommand函数定位元素。该函数给 mdlLocate_identifyElement设置了


数据点函数。您也可以直接使用 mdlLocate_identifyElement 或 mdlLocate_findElement函数来查找元素。

mdlState_startModifyCommand的参数:

参数 1 –重置/重新开始函数(Reset/restart Function):当用户输入 reset 或者用 mdlState_restartCurrentCommand 函数重新开始时


调用的函数;

参数 2— 数据点函数(Datapoint Function):当用户接受一个元素时调用的函数;

参数 3— 动态函数(Dynamics Function):在一个简单的动态元素显示时调用;

参数 4— 显示函数(Show Function):在元素被确定之后、被接受之前调用,典型的情况是,该函数用于显示一些附加信息,
凭借它用户可以决定是否接受一个元素,它们还可以用于设置复杂动态元素;

参数 5— 清除函数(Clean Function):在选择元素之后调用来做清除工作的函数;

参数 6— 用于从注册的命令名称消息列表中指定一个命令名称字符串的消息识别码;

参数 7— 用于从注册的提示信息列表中指定提示信息的消息识别码;

参数 8— 指定处理选择的元素集;

参数 9— 指定选择操作命令所需的附加点的数量。

2、建立查询判别式 (Search Criteria)


上面的函数可以用作编写查询指定类型元素的函数的样板。当你需要一个通用的查询操作时(比如 MicroStation 的 DELETE
ELEMENT 命令),可以调用 mdlLocate_normal。有许多函数可以用于调整查询模板,其差异描述如下(表 15-1)。请注
意,函数 mdlLocate_noElementNoLocked和 mdlLocate_noElemAllowLocked需要在其后调用函数
mdlLocate_setElemSearchMask 来指定元素类型。

表 15-1 设置查询判别式的有关函数

函数 元素特性 查询文件 元素类型

mdlLocate_normal 未锁定 主文件 所有可显示的元素

mdlLocate_allowLocked 所有元素 所有文件 所有可显示的元素

mdlLocate_noElemNoLocked 未锁定 主文件 无

mdlLocate_noElemAllowLocked 所有元素 所有文件 无

mdlLocate_setElemSearchMask (未定) (未定) 函数参数指定的类型

另外,为扫描器(静态判别式)使用查询模板后,应用可以在定位处理的不同时间执行附加的处理,如元素类型过滤处理。
在使用 mdlLocate_setFunction函数定位元素时,可以建立用户自定义函数来进行事务处理。

例如,您可以要求只定位 cell元素。为达到这一点,用 mdlLocate_normal 建立您自己的查询判别式,以便让用户识别所有的


cell 元素。然后用 mdlLocate_setFunction 函数调用一个过滤函数来滤输出需要的元素。例如:

mdlLocate_setFunction(LOCATE_POSTLOCATE,userFilterCells);

userFilterCells函数是写来检查当前元素的类型并排除那些不是 cell类型的元素。

3、传送元素到处理函数/重新开始定位

   

一般而言,操纵命令一次只修改一个元素,但是 mdlModify_elementMuti函数可以通过对选择集中的每一个元素调用一次
mdlModify_elementSingle,从而实现在一次操作中修改选择集或图形组(graphic group )中所有元素的功能(不过 doGroup
参数必须设置为 TRUE)。同时,为了让图形组作为一个组来处理,还需要把图形组的锁“打开”(turn on)。值得注意的
是,选择集(selection set)和图形组(graphic group)是相互排斥的,并且选择集优先。比如,有一个激活的选择集,但选择集中
的一个成员正好在一个图形组中(而且图形组的锁有效),那么,只有选择集中的元素才会被处理。另一方面,如果没有选
择集,并且标定用于修改的元素属于一个图形组(同时,图形组锁为 on),那么属于这个组的其他元素将连同这个元素将
一起被修改。

mdlModify_elementSingle 和 mdlModify_elementMuti 函数的参数:

参数 1— 处理的文件号;

参数 2— 元素在文件中的位置;

参数 3— 为复杂元素调用元素函数的标志;

参数 4— 表明修改元素所用步骤的修改标志;

参数 5— 元素函数:在修改元素时为每一个成员调用的函数;

参数 6— 指向包含元素参数的结构的指针;

参数 7— 对于 mdlModify_elementSingle函数,本参数与成员标志变量一起使用(参数 3);对于 mdlModify_elementMuti函


数,本参数用于决定是否处理图形组(如果为 TRUE,图形组以及选择集将被处理)。

尽管 mdlModify_elementSingle和 mdlModify_elementMuti经常使用,有时也可以使用稍微低级一点的
mdlModify_elementDescr 函数来编写操纵函数。所有这 3 个函数最终都调用一个在应用中定义的元素函数(element
Func(tion))来执行实际的元素修改操作(图 15-1 显示了这种层次结构)。

图 15-1、各修改函数之间的层次示意图

4. 元素处理
元素函数的具体说明参见《MDL参考手册》中有关 mdlModify_elementSignle 函数的说明,元素函数必须有返回值,返回值
可能是:

MODIFY_STATUS_NOCHANGE— 元素没有被修改;

MODIFY_STATUS_REPLACE— 用新元素替换了已有元素;

MODIFY_STATUS_DELETE— 元素被删除;

MODIFY_STATUS_ABORT 、MODIFY_STATUS_FAIL、

MODIFY_STATUS_ERROR— 修改失败;

MODIFY_STATUS_REPLACEDSCR— 元素描述符被替换。

5、栅栏操作命令的源码

栅栏处理与单元素/选择集处理之间的差别是很微妙的。单元素 /选择集处理中必须在初始化修改函数之后设置查询判别式,
下例中使用栅栏操作改变线的颜色和风格,其用法与前面使用 CHANG_LINE_SINGLE 的例子类似,只不过这里使用
CHANGE_LINE_FENCE。

 
 
 

上例中调用的 mdlState_startFenceCommand 类似于单元素/选择集处理中使用的 mdlState_startModifyCommand函数。

mdlState_startFenceCommand 的参数:

参数 1— 内容函数(contents function):用于通过 mdlFence_process 进行栅栏处理(类似于单元素/选择集处理中的“元素函


数”);

参数 2— 轮廓函数(outline function):处理完毕后调用。通常用于显示一个新的栅栏轮廓;

参数 3— 数据点函数(datapoint function):当下一个数据点输入时调用(如果等于 NULL,内部函数 mdlFence_process 将直


接被使用);

参数 4— 重置/重新开始函数(reset/restart function);

参数 5— 用于从注册的命令名称消息列表中指定一个命令名称字符串的消息识别码;

参数 6— 用于从注册的提示信息列表中指定提示信息的消息识别码;

参数 7— 栅栏裁减模式(fence clipping mode ):决定 MicroStation 如何传递与栅栏重叠的元素给内容函数。

1. 传递元素给栅栏处理函数

 
因为本例中调用 mdlState_startFenceCommand函数时,数据点函数的指针参数设置为空(NULL),MDL 会自动设置
mdlFence_process函数为数据点处理函数。在进行栅栏锁和

裁减模式检查之后,元素被一个一个地传递给内容函数 acceptFenceContents。

虽然本例中数据点函数的指针参数设置为空,但在有的应用中需要自己的栅栏函数来记录用于预先处理栅栏的附加信息,比
如栅栏移动和栅栏复制所需的其他数据点。因此,数据点函数将在数据点处理结束后直接调用 mdlFence_process。必须注
意,调用 mdlFence_process函数时,栅栏必须已经定义,否则将有错误发生。

7、轮廓( outline)函数的附加信息

在上面的例子中,轮廓函数都被设置为空(NULL),但是在通常情况下,设置为空并不是最好的选择。

轮廓函数是在所有栅栏处理完成后执行的唯一函数。如果栅栏命令在处理完成之后继续进行,倘若该处理针对大量元素的
话,会有一个时钟错误。只有当轮廓函数执行之后才能确信栅栏处理已经完成。尽管您的命令可能并不需要绘出栅栏的轮
廓,栅栏函数也可以用来执行一个需要在内容函数执行完毕后进行的进一步处理。确信您已经对大的设计文件进行了栅栏命
令测试,以保证您的命令操作适合大量元素。

第十六章 M D L 调试器使用指南
 

MDL 调试器提供类似于 C表达式求值器的标准命令。

一、MDL 调试器环境变量

=================================================================

变量 描述 设置

-------------------------------------

MS_DBGSOURCE 指定 MDL 调试器使用源程序的查找目录


MS_DBGOUT MDL 调试器输出的目录位置

MS_MDLTRACE MDL 调试器将多个调试输出到标准设备 0 或 1

MS_DEBUGFAULT 当错误发生时是否自动重新启动 MDL 调试器 0 或 1

MS_DEBUG 如果 BIT 1 为 ON,阻止超时 整数

MS_DEBUGMDLHEAP 打开内存跟踪 应用名或 ALL

MS_TRAP 打开意外错误处理 NONE,MDL或 ALL

MS_SYSTEM 是否可以返回操作系统 0 或 1

MS_DOSWINDOW 打开 DOS窗口 0 或 1

MS_DBGINIT 从文件中读取调试命令 文件名

================================================================

二、激活 MDL 调试器

可以采用下述方法之一来激活 MDL调试器:

1. 从 MicroStation装载一个应用并激活调试器。输入:

MDL LOAD DEBUG <应用程序名>

2. 从已经装载的 MDL 应用中激活调试器。输入:

MDL LOAD <应用程序名>

3.从 MDL 应用中激活调试器。调用函数 mdlSystem_enterDebug。

4. 加开关 d,被环境变量 MS_INITAPPS指定的应用可被调试。例如:

MS_INITAPPS=<应用程序名>

在同一时刻,只能有一个应用的调试信息被载入.

三、源码位置

调试器首先在当前目录下查找被调试的源程序,如果未找到,然后查找环境变量 MS_DBGSOURCE 指定的目录。

四、调试器使用方法

MDL调试器只能在 MicroStation 运行时使用(不一定是图形模式),装载的应用必须以 - g选项进行编译和连接。如果使用


BMAKE工具,需指定 - ddebug 选项。

五、调试器输入与输出

在 InterGraph 工作站上,调试信息输出到 VT220 窗口(MS启动窗口)。


在 PC上,输出到标准输出设备:

1. 使用双屏但仅为 microStation 配置单屏。从不使用的屏上启动 MicriStation,调试信息将输出到 MicriStation启动的


监视器上。

2. 从 MicriStation命令行重定向标准输出到其他外部设备上,或使用环境变量 MS_DBGOUT 指定输出目标设备,例


如:MS_DBGOUT=com1。

3. 缺省为输出到 MicriStation的 DOS 窗口。

4. 如果调试器使用单独的调试器或使用 MicriStation 的 D O S窗口,ansi.sys必须加载到配置文件中。

当 MDB> 提示符出现在调试器输出设备上时,就可以在 MicriStation命令窗口输入调试命令。

可以在一行输入多个命令。命令之间用分号隔开。单独按 Enter 键将重复前一命令。调试器可以支持简单的命令行编辑(不同


平台有不同的高级命令),但仅使用插入模式。

六、 C表达式求值器

调试器可以接受标准操作符、变量和结构定义,以及内置的 MDL 标准 C函数,但不接受用户声明和用户函数调用。

MDL 调试器命令

• 设置断点: Break 断点位置可按函数名。当前地址或行号指定。

选项: /delete;/<count>

• 函数调用栈跟踪:CALLS

选项: /<count>

• 求值:DISPLAY以不同格式显示数据.

选项: /CHAR;/SHORT;/LONG;/DOUBLE;/STRING,可与下列符号组合使用:HEX,OCTAL,DECIMAL。

• 继续:GO <函数名>继续执行到下一断点,或执行到一指定函数。

• 帮助:HELP /帮助命令主题

例如:help /calls

• 内存监视:MEMORY 显示当前被 malloc/calloc/realloc 函数分配的内存块的起始地址。元素描述子的内存不被显示。

• 退出:QUIT 返回 MicroStation 并退出此应用。如果调试器被环境变量 MS_INITAPPS 所激活,则返回操作系统。

• 单步执行:STEP 选项:/INFO;/OVER;/FROM 和 <COUNT>。

• 列出符号表:SYMBOLS选项:/FUNCTIONS;/VARIABLES;/STRUCTURES。

• 显示源码:TYPE 选项:/<number> 显示以下 number 行;/. 显示当前执行的源码。

• 监视:WATCH 仅在 PC 上使用。

• 记录:RECORD[选项][文件名]启动 MicroStation 对调试器输入输出的记录。

选项:/input;/output;/all;/append;/replace;/stop;default。如未指定文件名,则记录到缺省的 mdldebug.log文件。
• 别名:ALIAS <name> <substitution string>

• 条件:IF

• 范围:SCOPE <new_frame_number>;scope . ;scope <source_file_name>设置栈跟踪时的范围。

• 上限:UP 设置 SCOPE上限。

• 下限:DOWN 设置 SCOPE 下限。

• 调试级别变量:debugLevel Variable

该变量为 MDl的内置变量,可用下述命令修改: SET DEBUG <number>。

• 显示输出:printf("字串 格式串 ",变量名) 与 C 语言中的格式相同。另一命令可以输出


一单行到命令窗口:mdlOutput_printf。

You might also like