The usage of #pragma

本文详细介绍了C和C++编译器中的#pragma指令的使用方法和应用场景,包括了各种编译指示的语法、功能及注意事项。适用于需要了解或使用编译器特有功能的开发者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C和C++的每个实现对它的主机或操作系统都支持一些独有的特征。例如, 某些程序须对存放数据的存储器区域进行精确的控制,或必须控制特定函数接受参量的方式。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。
语法
#pragma 语言符号字符串
语言符号字符串是给出特有编译器指令和参量的字符序列。数字符号(#)必须是包含编译指示行中的第一个非空白字符。空白字符可分开数字符号(#)和单词pragma 。在#pragma之后,可以编写翻译器作为预处理器语言符号分析的任何文本。#pragma的参量从属于宏扩展。
如果编译器找到一个不能识别的编译指示,将发出一个警告,但编译将继续。编译指示可用在条件说明中,以提供新的预处理器功能,或提供定义的实现信息给编译器。C和C++编译器可识别下面的编译指示:
alloc_text comment init_seg* optimize
auto_inline component inline_depth pack
bss_seg data_seg inline_recursion pointers_to_members*
check_stack function intrinsic setlocale
code_seg hdrstop message vtordisp*
const_seg include_alias once warning*
仅被C++编译器支持
--------------------------------------------------------------------------------
C++编译器专有编译指示
以下是C++编译器专有的编译指示指令:
* init_segl
pointers_to_members
* vtordisp
init_seg
C++特殊处
#pragma init-seg({complier/lib/user/ "section-name" [,"func-name"]})该指令指定一个影响启动代码执行顺序的关键字或代码段。由于全局静态对象的初始化可能涉及执行代码,因此必须指定创建对象时定义的一个关键字。在动态连接库(DLL)或需初始化的库中使用init_seg编译指示尤其重要。
init_seg编译指示的选项如下:
complier
该选项保留给Microsoft C运行库初始化。这个组中的对象最先被创建。lib 用于第三方类库供应商的初始化。该组中的对象在complier标志之后,其他标记之前创建。
user
用于任何用户。此组对象最后创建。
section_name
允许初始化段的显示规格。在一个用户指定section-name(段名称)中的对象不能被隐含地创建,但它们的地址可放在以section_name命名的段中。
func_name
指定在程序退出时在exit()地方调用的函数。指定的函数必须与exit函数具有相同的特征:int funcname(void(__cdecl*)(void));
如果你需要延迟初始化过程(例如,在一个DLL中),你可以选择显式地指定该段名称。然后必须为每个静态对象调用构造函数。
C++特殊处结束
pointers_to_members
C++特殊处
#progma pointers_to_members(指针说明,[最一般表示])
该指令指定一个类成员的指针能否在其相关定义之前被说明,且用于控制该指针尺寸和解释该指针需要的代码。你可以把一个pointers_to_members编译指示放入你的源文件中替换/vmx编译器选项。
指针说明参量指定你在一个关联函数定义之前还是之后说明了一个成员的指针。指针说明参量是以下两个符号之一:
参量 说明
full_generality 生成安全、但常常并非最优的代码。如果在关联类定义之前说明任何成员的指针,可使用full_generality。该参量通常使用最一般表示参量指定的指针表示形式。等同于/vmg选项
best_case 为所有成员指针使用最佳情况(best__case)表示生成安全的最优代码。使用该参量是需在定义一个类的成员指针说明之前定义此类。其缺省值为best_case
best_case 为所有成员指针使用最佳情况(best__case)表示生成安全的最优代码。使用该参量是需在定义一个类的成员指针说明之前定义此类。其缺省值为best_case
最一般表示参量说明了在转换单元中,编译器能够安全地引用任何指向类成员的指针的最小指针表示。该参量取如下值之一:
参量 说明
single_inheritance 最一般表示是单继承的,即一个成员函数的指针。对于其中说明了一个指向成员指针的一个类定义,若其继承模式说明为多重的或虚拟的,将导致错误
multiple_inheritance 最一般表示是多重继承的,即一个成员函数的指针。对于其中说明了一个指向成员指针的一个类定义,若其继承模式是虚拟的,将导致错误
vitual_inheritance 最一般表示是虚拟继承,即一个成员函数的指针。该函数不会导致错误。当使用#pragma pointers_to_members(full_generality) 时这是个缺省参量
C++特殊处结束
vtordisp
C++特殊处
#pragma vtordisp({on|off})
该指令允许增加隐含的vtordisp构造函数/析构函数替换成员。vtordisp编译指示只使用虚基类的代码。若一个派生类重迭一个从虚拟基类继承的虚拟函数,且如果派生类的一个构造函数或析构函数调用那个使用该虚拟基类指针的函数,则编译器可能将增加的隐含“vtordisp”域到有虚拟基的类中。
vtordisp编译指示会影响其后类的分布。/Vd0或/Vd1选项指定了对于完全模式的相同动作。指定off将抑制隐含的vtordisp成员。指定缺省值on, 将在需要的位置打开它们。Vtordisp指令仅在类的构造/析构函数在用this 指针指向的对象处不可能调用虚拟函数时关闭。
#pragma vtordisp(off)
class GetReal:virtual public{...};
#pragma vtordisp(on)
C++特殊处结束
--------------------------------------------------------------------------------
C和C++编译器编译指示
以下是为C和C++编译器定义的编译指示:
alloc_text component init_seg* optimize
auto_inline const_seg inline_depth pack
bss_seg data_seg inline_recursi onpointers_to_members*
check_stack function intrinsic setlocale
code_seg hdrstop message vtordisp*
comment include_alias once warning
* 仅被C++编译器支持
alloc_text#pragma alloc_text(“文本段”,函数1,...)
该指令用于命名指定的函数定义将要驻留的代码段。该编译指示对已命名的函数必须出现在一个函数说明符和该函数定义之间。
alloct_text编译指示并不处理C++成员函数或重载函数。它仅用于以C连接方式说明的函数,这指的是用extern“C”连接规格说明的函数。如果你试图将此编译指示用于非C++连接的函数,将产生一个编译错误。
由于不支持使用__based的函数地址,指定段位址需要使用alloc_text编译指示,以文本段指定的名称应包括在双引号间。
alloc_text编译指示必须出现在指定的函数说明之后,这些函数的定义之前。一个alloc_text编译指示中的函数引用必须在此编译指示的同一模块中定义。如果未这样做,且一个未定义的函数随后被编译到一个不同的文本段,则这个错误可能找得到,也可能找不到。虽然该程序一般会正常运行,但该函数不会分配到预期的段中。
alloc_text的其他限制如下:
* 它不能用在一个函数的内部。
* 它必须在已说明的函数之后和已定义的函数之前使用。
auto_inline
#pragma auto_inline([{on|off}])
排除自动内联扩展的候选者中指定为off的区域中定义的函数。为了使用auto_inline编译指示,把它放在一个函数定义之前或立即之后(不在该函数定义之内)。在看到该编译指示之后的第一个函数定义处,该编译指示发生作用。编译指示auto_inline不能应用于显式内联函数。
bss_
seg#pragma data-seg(["section-name"[,"section-class"]])
指定未初始化数据的缺省段。data_seg编译指示处理初始化或未初始化数据有相同的作用。在某些情况下,你可以使用bss_seg通过把所有未初始化数据放在一个段中来加速加载的时间。
#pragma bss_seg("MY_DATA")
导致#pragma语句后未初始化的数据分配到一个名称为MY_DATA的段中。
用bss_seg编译指示分配的数据不会保留关于它的位置的任何信息。
第二个参量section_class用于与Visual C++之前的版本兼容,现在已被忽略。
check_stack
#pragma check_stack([{on|off}])
#pragma check_stack{+|-}
该指令在off(或-)选项时指示编译器关闭栈搜索。在on(或+)选项指定时,打开搜索。
若无参量,栈搜索就按缺省情况处理。在看到该编译指示之后第一个定义的函数处发生作用。栈搜索既不是宏的一部分,也不是产生的内联函数的一部分。
如果未赋予一个参量给check_stack编译指示,栈检查将还原成在命令行中说明的行为,有关更多的信息参见“编译器参考”。#pragma check_stack和/Gs选项的交互关系参见表2.1。
表2.1 使用check_stack编译指示
语法 是否用/Gs选项编译 行为
#pragma check_stack()或#pragma check_stack 是 关闭其后函数的栈检查
#pragma check_stack()或#pragma check_stack 否 打开其后函数的栈检查
#pragma check_stack(on)或#pragma check_stack + 是或否 打开其后函数的栈检查
#pragma check_stack(off)或#pragma check_stack - 是或否 关闭其后函数的栈检查
code_seg
#pragma code_seg(["section-name"[,"section-class"]])
该指令用于指定一个分配函数的代码段。code_seg编译指示指定了函数的缺省段。
你可以有选择性地指定类和段名。使用没有section-name字符串的#pragmacode_seg可在编译开始时将其复位。
const_seg
#pragma const_seg(["section-name"[,"section-class"]])
该指令用于指定对于常量数据的缺省段。data_seg编译指示对所有数据具有相同作用。你可以用此指令将你的所有常量数据放入一个只读段中。
#pragma const_seg("MY_DATA")
导致该指令将#pragma语句后的常量数据放入一个名称为MY_DATA的段里。
使用const_seg编译指示分配的数据不会保留有关它的位置的任何信息。
第二个参数section-class用于与Visual C++ 2.0版之前的版本兼容,现在已可忽略。
comment
#pragma comment(comment-type,[commentstring])
该指令将一个注释记录放入一个对象文件或可执行文件中。comment-type是下面五种说明的预定义标识符之一,它们指出了注释记录的类型。任选的commentstring是给一些注释类型提供额外信息的字符串文字。由于commentstring是一个字符串文字, 因此它必须遵循对于字符串文字的诸如转义字符、嵌lib
该选项将一个库搜索记录放入对象文件。该注释类型必须带有一个commentstring参数。这个参数包含你想要的链接器搜索的库的名称(有可能包含路径)。由于在对象文件中该库名称在缺省的库搜索记录之前,所以链接器搜索该库就象你在命令行中命名了它一样。你可以把多个库搜索记录放在同一个源文件中,每个记录在对象文件中都以其在源文件中出现的同样顺序出现。
linker
该选项将一个链接器选项放入对象文件中。可以用该注释类型指定一个链接器选项,用于取代在Project Setting对话框中Link选项卡上放入该选项。例如,你可以指定/include选项来强行包括一个符号:
#pragma comment(linker,"/include:__symbol")
user
该选项将一个一般的注释放入对象文件中。commentstring参量包含了该注释的文本。这个注释记录被链接器忽略。
以下编译指示导致链接器在连接时搜索EMAPI.LIB库。 该连接器首先在当前工作目录中搜索,随后在LIB环境变量说明的路径中搜索。
#pragma comment(lib,"emapi")
以下编译指示导致编译器把编译器的名称和版本号放入对象文件:
#pragma comment(complier)
注意:对于一个带commentstring参量的注释, 你可以在任何要使用一个字符串文字的地方使用宏,让这个宏扩展为一个字符串文字。你也可以把任何一个字符串文字的任何组合与扩展为字符串文字的宏合并起来,例如:下面的语句是可以接受的:
#pragma comment(user,"Compiled on" __DATA_ _ "at" _ _TIME_ _)
component#pragma
component(browser,{on|off}[,references[,name]])#pragma component(minrebuild,on|off)
该指令用于控制源文件内的浏览信息或依赖信息的集合。
browser
你可以打开或关闭集合,并可以在收集信息时指定忽略的特定名称。
用on或off选项控制前面编译指示的浏览信息的集合。例如:
#pragma component(browser,off)
该指令让编译器停止收集浏览信息。
注意:用此指令打开浏览信息的集合,浏览信息必须先用Project Settings对话框或命令行打开。
references选项可带也可不带name参量。不带name参量的references选项用于打开或关闭引用的集合(但此时继续收集其它浏览信息)。例如:
#pragma component(browser,off,references)
该指令使编译器停止收集引用信息。
带name和off参量的references选项,用于防止浏览信息窗口中出现对name的引用。使用这种语法可忽略你不感兴趣的名称和类型, 并且可缩短浏览信息的尺寸。例如:
#pragma component(browser,off,references,DWORD)
忽略该点之前对于DWORD的引用。但你可用on选项把对于DWORD引用的集合重新打开。
#pragma component(browser,on,references,DWORD)
这是恢复对name引用集合唯一的方式;用此方式你可以显式地打开任何你已经关闭的name。
为了防止预处理器把name展开(如把NULL扩展为0),将它加上引号:
#pragma component(browser,off,references,"NULL")
Minimal Rebuild
Visual C++的minimal rebuild(最小重建)特性需要编译器创建和存储依赖信息的C++类,这将占用磁盘空间。为了节省磁盘空间,你可以在任何你不需收集依赖信息的时候仅用#pragma component(minirebuild,off),例如,在不变的头文件中。在不变的类后插入#pragma component(minrebuild,on)可重新打开依赖信息。
有关更多的信息参见Enable Minimal Rebuild(/Gm)编译器选项。
data_seg
#pragma data_seg(["section-name"[,"section-class"]])
该指令指定数据的缺省段。例如:
#pragma data_seg("MY_DATA")
将#pragma语句后分配的数据放在以名称为MY_DATA的段里。
使用data_seg编译指示分配的数据不会保留关于它的位置的任何信息。
第二个参量section-class用于与Visual C++ 2.0之前的版本兼容,现在已可忽略。
function
#pragma function(function1[,function2,...])
该指令指定对在生成的编译器编译指示参量表中指定调用的函数。如果你使用intrinsic编译指示(或/Oi)告诉编译器生成内在函数(被生成为内联代码,而非函数调用的内在函数),就能用function编译指示来显式地强制调用一个函数。当一个函数编译指示出现时,它在第一个包含一个指定的内在函数的函数定义处发生作用,这个作用持续到源文件的结尾或直到一个说明这种相同的内在函数的编译指示出现为止。在全局层时,该function编译指示只能用在一个函数外。对于具有内部形式的函数表,参见#pragma intrinsic。
hdrstop
#pragma hdrstop[("filename")]
该指令用于控制预编译头文件的工作方式。filename是预编译头文件使用或创建的名称(根据指定选项/Yu或/Yc决定)。如果filename不包含路径说明,预编译头文件将被假定在与源文件相同的目录中。当指定/YX自动预编译头文件选项时,所有filename都被忽略。
当采用/YX或/Yc编译时,一个C或C++文件包含一个hdrstop编译指示,该编译器将把编译状态存入到编译指示的位置。该编译指示之后任何代码的编译状态都不存储。
hdrstop编译指示不可能出现在一个头文件中。它必须出现在源文件中。这指的是,它不能出现在任何数据、函数说明或函数定义中。
注意:除非/YX选项或无文件名的/Yu或/Yc选项被指定,否则hdrstop编译指示将被忽略。
该指令使用filename命名编译状态存储的预编译头文件。hdrstop和filename之间的一个空白是任选的。在hdrstop编译指示中说明的文件名称是一个字符串, 而且必须服从C或C++字符串的约束。尤其重要的是必须将其置于括号中
如下例所示:
#pragma hdrstop("c:\projects\include\myinc.pch")
预编译头文件的名称由下列规则决定,顺序如下:
1. /Fp编译器选项的参量。
2. #pragma hdrstop的filename参量。
3. 以.PCH为扩展名的源文件的基名称。
include_alias
#pragma include_alias("long_filename","short_filename")
#pragma include_alias(,)
该指令指定short_filename作为long_filename的别名。某些文件系统允许比8.3 FAT文件系统限定更长的文件名。编译器不能够简单地把更长的头文件名截短为8.3格式,因为这种更长的头文件名的开始八个字符可能不是唯一的。只要编译器遇到long_filename字符串,就用short_filename替换,并且代替查找short_filename头文件。这个编译指示必须出现在相应的#include指令之前,
例如:/
/这两个文件的开头八个字符不是唯一的
#pragma include_alias("AppleSystemHeaderQuickdraw.h","quickdra.h")
#pragma include_alias("AppleSystemHeaderFruit.h","fruit.h")
#pragma include_alias( "GraphicsMenu.h", "gramenu.h")
#include "AppleSystemHeaderQuickdraw.h"
#include "AppleSystemHeaderFruit.h"
#include "GraphicsMenu.h"
无论是拼写,还是双引号或尖括号的用法,被搜索的别名都必须符合规格。include_alias编译指示在该文件名中处理简单的字符串匹配,而在其它的文件名中是无效的。例如,给出如下指令:
#pragma include_alias("mymath.h","math.h")
#include "./mymath.h"
#include "sys/mymath.h"
没有别名使用(替换)被执行,因为头文件字符串并不匹配。用作/Yu、/Yc和/YX编译器选项参量的头文件名,或hdrstop编译指示的头文件名也不能被替换。例如,若你的源文件包含下列指令:
#include
相应的编译器选项应该是:
/YcAppleSystemHeaderStop.h
你可以用include_alias编译指示把任何头文件名映射为另一个。例如:
#pragma include_alias("api.h", "c:\version1.0\api.h")
#pragma include_alias(, )
#include "api.h"
#include
不要把双引号内的文件名和尖括号内的文件名相混淆,例如,对于上面给出的两个#pragma include_alias指令,编译器在下面的#include指令中不执行任何替换:
#include
Include "stdio.h"
此外,下面的指令将导致错误:
#pragma include_alias(,"header.h") //错误
注意在错误信息中给出的文件名,或作为预先定义的__FILE__宏的值,是替换执行之后的文件的名称,例如,下面指令后:
#pragma include_alias("VeryLongFileName.H","myfile.h" )
#include "VeryLongFileName.H"
在VERYLONGFILENAME.H中的一个错误将导致如下错误消息:
myfile.h(15) : error c2059 : syntax error
同时注意传递性是不支持的。如下指令中:
#pragma include_alias( "one.h", "two.h" )
#pragma include_alias( "two.h", "three.h")
#include "one.h"编译器搜索的是文件TWO.H而不是THREE.H。
inline_depth
#pragma inline_depth([0...255])
该指令通过控制一系列函数调用能被扩展的次数(从0到255次),来控制内联扩展可发生的次数。这个编译指示控制标记为inline和_ _inline的函数的联编或在/Ob2选项下已经自动联编的函数。
inline_depth编译指示控制一序列函数调用能被扩展的次数,例如,若联编深度为4,且若A调用B,B调用C,三个调用都将内联扩展。但如果最近的内联扩展为2,只有A和B被扩展,C保留为一个函数调用。
为使用这个编译指示,你必须设置/Ob编译器选项为1或2。使用这个编译指示的深度设置在该编译指示指令后的第一个函数处生效。如果你没有在圆括号中指定一个值,inline_depth将设置联编深度为缺省值8。
联编深度在扩展时只减不增。若联编深度为6,且在扩展时,预处理器遇到一个联编深度值为8的inline_depth编译指示,该深度仍保持为6。
联编深度0禁止联编扩展;联编深度255对联编扩展无限制,若使用一个未指定其值的编译指示,则将使用其缺省值。
inline_recursion
#pragma inline_recursion([{on|off}])
该指令控制直接或相互递归函数调用的联编扩展。该指令用于控制标记为inline和_ _inline的函数,或编译器在Ob2选项下自动扩展的函数。这个编译指示的用法需要一个设置为1或2的/Ob编译器选项。inline_recursion的缺省状态是off。这个编译指示只是在其出现之后的第一个函数处起作用,并且不会影响该函数的定义。
inline_recursion编译指示控制递归函数如何被扩展。如果inline_recursion关闭,且若一个联编函数调用其自身(直接或间接),该函数只被扩展一次。若inline_recursion为打开状态,则该函数将被扩展多以下所列的浮点函数没有真正的内在格式。但它们有这样的版本:将参量直接送入浮点芯片,而不是将它们压入程序栈中:
acos cosh pow tanh
asin fmod sinh
当你指定/Oi和/Og编译器选项(或任何包括/Og、/Ox、/O1和/O2的选项)时,以下浮点函数有真正的内在格式:
atan exp log10 sqrt
atan2 log sin tan
cos
你可以使用/Op或/Za编译器选项重迭真正的内在浮点选项的生成。这种情况下,该函数被生成为库例程,该例程直接把参量送入浮点芯片,而不是把它们压入程序栈。
message
#pragma message(messagestring)
该指令不终止编译,直接把一个字符串文字送到标准输出。message编译指示的典型用法是在编译时显示信息消号。以下代码段使用message编译指示在编译时显示一个消息:
#if _M_IX86==500
#pragma
message("Pentium processor build")#endifmessagestring参数可以是可扩展为一个字符串文字的宏,并且你可以以任何组合方式用字符串文字将这些宏合并起来。例如,下列语句显示了被编译的文件名以及该文件最后改动的日期和时间:
#pragma message("Compiling "__FILE__ )
#pragma message("Last modified on " __TIMESTAMP_ _)
once
#pragma once
该指令指定该编译指示驻留的文件将只在一次建立中被编译器包括(打开)一次。该编译指示的一种普通用法如下:
//header.h
#pragma once
//接着是你的C或C++代码
optimize
#pragma optimize( "[optimization-list]",{on|off} )
仅用于专业和企业版本的特征:代码优化只被Visual C++专业版和企业版支持。更多的信息参见Microsoft Visual C++联机编辑。
该指令指定在函数基中执行的优化。optimize编译指示必须出现在一个函数之外,并且在编译指示出现后定义的第一个函数处产生作用。on和off参量可以打开或关闭在Optimization-list中指定的选项。
optimization-list可以是0或在表2.2中给出参数。
表2.2 "优化"编译指示的参数
参量 优化类型
a 假定无别名
g 允许全局优化
p 提高浮点相容性
s或t 指定机器码的短或快序列
w 假定无交叉函数调用的别名
y 生成程序堆栈中的框架指针
这些是采用/O编译器选项的相同字母,例如:
#pragma optimize("atp", on)
用空字符串("")使用optimize编译指示是该指令的一种特殊形式,它可关闭所有的优化或恢复它们的原有(缺省的)设置。
#pragma optimize("",off)
...
#pragma optimize("", on)
pack
#pragma pack([n])
该指令指定结构和联合成员的紧凑对齐。而一个完整的转换单元的结构和联合
的紧凑对齐由/Zp选项设置。紧凑对齐用pace编译指示在数据说明层设置。该编译指示在其出现后的第一个结构或联合说明处生效。该编译指示对定义无效。当你使用#pragma pack(n)时,这里n为1、2、4、8或16。第一个结构成员之后的每个结构成员都被存储在更小的成员类型或n字节界限内。如果你使用无参量的#pragma pack,结构成员被紧凑为以/Zp指定的值。该缺省/Zp紧凑值为/Zp8。
编译器也支持以下增强型语法:
#pragma pack([[{push|pop},][标识符,]][n])
若不同的组件使用pack编译指示指定不同的紧凑对齐,这个语法允许你把程序组件组合为一个单独的转换单元。
带push参量的pack编译指示的每次出现将当前的紧凑对齐存储到一个内部编译器堆栈中。编译指示的参量表从左到右读取。如果你使用push,则当前紧凑值被存储起来;如果你给出一个n的值,该值将成为新的紧凑值。若你指定一个标识符,即你选定一个名称,则该标识符将和这个新的的紧凑值联系起来。
带一个pop参量的pack编译指示的每次出现都会检索内部编译器堆栈顶的值,并且使该值为新的紧凑对齐值。如果你使用pop参量且内部编译器堆栈是空的,则紧凑值为命令行给定的值,并且将产生一个警告信息。若你使用pop且指定一个n的值,该值将成为新的紧凑值。
若你使用pop且指定一个标识符, 所有存储在堆栈中的值将从栈中删除,直到找到一个匹配的标识符,这个与标识符相关的紧凑值也从栈中移出,并且这个仅在标识符入栈之前存在的紧凑值成为新的紧凑值。如果未找到匹配的标识符,将使用命令行设置的紧凑值,并且将产生一个一级警告。缺省紧凑对齐为8。
pack编译指示的新的增强功能让你编写头文件,确保在遇到该头文件的前后的紧凑值是一样的。
/* File name: include1.h*/
#pragma pack(push,enter_include1)
/* 你的包括文件代码... */
#pragma pack(pop, enter_include1)
/* include1.h结束 */
在上面的例子中,当前紧凑值与标识符enter_include1联系起来,并被压入头文件的项中。头文件末尾的pack编译指示删除所有可能出现在头文件中的干预紧凑值,并且删除与enter_include1相关的紧凑值。因此确保该头文件的前后的紧凑值是相同的。
这种新功能也允许你使用代码,例如头文件,它可以使用pack编译指示设置不同于在你的代码中设置的紧凑值的紧凑对齐:
#pragma pack(push,before_include1)
#include "include1.h"
#pragma pack( pop,before_include1)
在上面的例子中,对于出现在include.h中的紧凑值的任何变化,你的代码是受到保护的。
setlocale
#pragma setlocale("locale_string")
该指令在翻译宽字符常量和字符串文字时定义其场所(国家和语言)。由于用于转换多字节字符为宽位字符的算法可能由于场所或编译而不同,该执行文件在不同的场所运行也可能不同。这个编译指示提供了在编译时给出目标场所的方法。这保证了宽字符串以正确的格式进行存储。缺省的locale_ _string(场所字符串)是 “C”。“C”场所将每个该串中的字符映射为一个wchar_t(unsigned short)型的值。
warning
#pragma warning( warning-specifier:warning-number-list
[,warning-specifier:warning-number-list...])
#pragma warning(push[,n])#pragma warning(pop)
该指令允许选择性地改变编译器的警告消息。
warning-specifier可以是如下值之一:
警告指示符 含义
once 只显示一次指定的消息default将缺省的编译器行为应用于指定的消息
1,2,3,4 把给定的警告级应用于指定的警告消息
disable 不发出指定警告消息
error 作为错误报告指定的警告
warning-number-list(警告编号表)可以包含任何警告编号。在相同的编译指示指令中可指定多个选项如下:
#pragma warning( disable : 4507 34; once : 4385; error : 164 )其功能想当于:
#pragma warning( disable : 4507 34 )
// 禁止警告消息4507和34.#pragma warning( once : 4385 )// 仅发出警告4385一次
#pragma warning( error : 164 )// 作为一个错误报告警告164
对于那些与代码生成有关,且大于4699的警告编号来说,这个warning编译指示仅当放在函数定义外时有效。如果警告编号大于4699或用在函数体内,则忽略该编译指示。下面的例子指出了禁止warning编译指示的正确位置,且随后恢复一个代码生成警告消息的产生。
int a;
#pragma warning( disable : 4705 )
void func()
{
a;
}
#pragma warning( default : 4705 )
warning编译指示也支持以下语法:
#pragma warning( push[,n])
#pragma warning( pop)
这里n代表警告级(1到4)。
编译指示warning(push)存储所有警告的当前警告状态。编译指示warning(push,n)存储所有警告的当前警告状态并设置全局警告级为n。
编译指示warning(pop)将上次压入栈的警告状态弹出,push和pop之间警告状态的任何变化都将被取消。考虑这个例子:
#pragma warning(push)
#pragma warning(disable : 4705)
#pragma warning(disable : 4706)
#pragma warning(disable : 4707)
// 某些代码
#pragma warning(pop)
在这段代码的未尾,pop恢复所有警告状态(包括4705、4706和4707)为它在代码起始处的警告状态。
当你编写头文件时,可以用push和pop以确保对于用户造成的警告状态的变化,不会影响头部的正确编译。通常在头部的起始处使用push,在末尾处使用pop。例如, 有一个在警告级4未彻底编译的头部。以下代码将警告级改为3,然后在头部的未尾恢复原来的警告级:
#pragma warning(push,3)
//说明/定义
#pragma warning(pop)  
我做了一个宠物智能投喂系统,现在我有一个stm32f103c6单片机外接8MHz的晶振,其PA1连作为RXD,PA2作为TXD,PB0、PB1连接了发光二极管的负极,发光二极管的正极接的电源,发光二极管用来模拟电机转动状态,在收到喂食的指令后PB0先亮两秒,PB1先不亮,然后在PB1亮两秒PB0熄灭,最后一起闪烁两秒表示喂食已完成,我创建了一个mfc程序(PCMFCDLG),添加了5个Combo Box控件分别用来选择端口号(控件类,m_cbPort)、波特率(控件类,m_cbBaud)、校验位(控件类,m_cbParity)、数据位(控件类,m_cbDate)以及停止位(控件类,m_cbStop),4个按钮控件分别用来打开串口连接stm32单片机、关闭串口、立即投喂、定时投喂,3个Edit control控件(value类,m_strTime1/2/3)用来定时3个时间点实现定时投喂,最后还有一个List Box(控件类,m_listStatus)用来显示执行状态,在PC机与单片机没有建立连接的时候除了连接按钮,其他按钮都不能点击,在定时按钮没有按下的时候edit control控件不能输入,在单片机执行完相应操作后,会返还给PC机信息,然后显示在list box里面如mfc界面点击立即投喂,单片机执行完操作后返回给pc端mfc界面一个标志,告诉pc端操作已完成,然后在mfc界面的list里面显示对应操作已完成。 /* Module : SerialPort.h Purpose: Interface for an C++ wrapper class for serial ports Copyright (c) 1999 - 2015 by PJ Naughter. All rights reserved. Copyright / Usage Details: You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise) when your product is released in binary form. You are allowed to modify the source code in any way you want except you cannot modify the copyright details at the top of each module. If you want to distribute source code with your application, then you are only allowed to distribute versions released by the author. This is to maintain a single distribution point for the source code. */ // Macros / Structs etc #pragma once #ifndef __SERIALPORT_H__ #define __SERIALPORT_H__ #ifndef CSERIALPORT_EXT_CLASS #define CSERIALPORT_EXT_CLASS #endif //#ifndef CSERIALPORT_EXT_CLASS #ifndef _Out_writes_bytes_ #define _Out_writes_bytes_(X) #endif //#ifndef _Out_writes_bytes_ #ifndef __out_data_source #define __out_data_source(X) #endif //#ifndef __out_data_source #ifndef _Out_writes_bytes_to_opt_ #define _Out_writes_bytes_to_opt_(X,Y) #endif //#ifndef _Out_writes_bytes_to_opt_ #ifndef _Out_writes_bytes_opt_ #define _Out_writes_bytes_opt_(X) #endif //#ifndef _Out_writes_bytes_opt_ #ifndef _In_reads_bytes_opt_ #define _In_reads_bytes_opt_(X) #endif //#ifndef _In_reads_bytes_opt_ #ifndef _In_ #define _In_ #endif //#ifndef _In_ #ifndef _In_z_ #define _In_z_ #endif //#ifndef _In_z_ #ifndef _Inout_opt_ #define _Inout_opt_ #endif //#ifndef _Inout_opt_ #ifndef _Out_opt_ #define _Out_opt_ #endif //#ifndef _Out_opt_ #ifndef _Out_ #define _Out_ #endif //#ifndef _Out_ #ifndef _Inout_ #define _Inout_ #endif //#ifndef _Inout_ #ifndef _In_opt_ #define _In_opt_ #endif //#ifndef _In_opt_ // Includes /// #include <sal.h> #ifndef CSERIALPORT_MFC_EXTENSTIONS #include <exception> #include <string> #endif //#ifndef CSERIALPORT_MFC_EXTENSTIONS /// Classes /// #ifdef CSERIALPORT_MFC_EXTENSIONS class CSERIALPORT_EXT_CLASS CSerialException : public CException #else class CSERIALPORT_EXT_CLASS CSerialException : public std::exception #endif //#ifdef CSERIALPORT_MFC_EXTENSIONS { public: //Constructors / Destructors CSerialException(DWORD dwError); //Methods #ifdef CSERIALPORT_MFC_EXTENSIONS #ifdef _DEBUG virtual void Dump(CDumpContext& dc) const; #endif //#ifdef _DEBUG #endif //#ifdef CSERIALPORT_MFC_EXTENSIONS #if _MSC_VER >= 1700 virtual BOOL GetErrorMessage(_Out_z_cap_(nMaxError) LPTSTR lpszError, _In_ UINT nMaxError, _Out_opt_ PUINT pnHelpContext = NULL); #else virtual BOOL GetErrorMessage(__out_ecount_z(nMaxError) LPTSTR lpszError, __in UINT nMaxError, __out_opt PUINT pnHelpContext = NULL); #endif #ifdef CSERIALPORT_MFC_EXTENSIONS CString GetErrorMessage(); #endif //#ifdef CSERIALPORT_MFC_EXTENSIONS //Data members DWORD m_dwError; }; class CSERIALPORT_EXT_CLASS CSerialPort { public: //Enums enum FlowControl { NoFlowControl, CtsRtsFlowControl, CtsDtrFlowControl, DsrRtsFlowControl, DsrDtrFlowControl, XonXoffFlowControl }; enum Parity { NoParity = 0, OddParity = 1, EvenParity = 2, MarkParity = 3, SpaceParity = 4 }; enum StopBits { OneStopBit, OnePointFiveStopBits, TwoStopBits }; //Constructors / Destructors CSerialPort(); virtual ~CSerialPort(); //General Methods void Open(_In_ int nPort, _In_ DWORD dwBaud = 9600, _In_ Parity parity = NoParity, _In_ BYTE DataBits = 8, _In_ StopBits stopBits = OneStopBit, _In_ FlowControl fc = NoFlowControl, _In_ BOOL bOverlapped = FALSE); void Open(_In_z_ LPCTSTR pszPort, _In_ DWORD dwBaud = 9600, _In_ Parity parity = NoParity, _In_ BYTE DataBits = 8, _In_ StopBits stopBits = OneStopBit, _In_ FlowControl fc = NoFlowControl, _In_ BOOL bOverlapped = FALSE); void Close(); void Attach(_In_ HANDLE hComm); HANDLE Detach(); operator HANDLE() const { return m_hComm; }; BOOL IsOpen() const { return m_hComm != INVALID_HANDLE_VALUE; }; #ifdef CSERIALPORT_MFC_EXTENSIONS #ifdef _DEBUG void Dump(_In_ CDumpContext& dc) const; #endif //#ifdef _DEBUG #endif //#ifdef CSERIALPORT_MFC_EXTENSIONS //Reading / Writing Methods void Read(_Out_writes_bytes_(dwNumberOfBytesToRead) __out_data_source(FILE) void* lpBuffer, _In_ DWORD dwNumberOfBytesToRead); void Read(_Out_writes_bytes_to_opt_(dwNumberOfBytesToRead, *lpNumberOfBytesRead) __out_data_source(FILE) void* lpBuffer, _In_ DWORD dwNumberOfBytesToRead, _In_ OVERLAPPED& overlapped, _Inout_opt_ DWORD* lpNumberOfBytesRead = NULL); void ReadEx(_Out_writes_bytes_opt_(dwNumberOfBytesToRead) __out_data_source(FILE) LPVOID lpBuffer, _In_ DWORD dwNumberOfBytesToRead, _Inout_ LPOVERLAPPED lpOverlapped, _In_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); DWORD Write(_In_reads_bytes_opt_(dwNumberOfBytesToWrite) const void* lpBuffer, _In_ DWORD dwNumberOfBytesToWrite); void Write(_In_reads_bytes_opt_(dwNumberOfBytesToWrite) const void* lpBuffer, _In_ DWORD dwNumberOfBytesToWrite, _In_ OVERLAPPED& overlapped, _Out_opt_ DWORD* lpNumberOfBytesWritten = NULL); void WriteEx(_In_reads_bytes_opt_(dwNumberOfBytesToWrite) LPCVOID lpBuffer, _In_ DWORD dwNumberOfBytesToWrite, _Inout_ LPOVERLAPPED lpOverlapped, _In_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); void TransmitChar(_In_ char cChar); DWORD GetOverlappedResult(_In_ OVERLAPPED& overlapped, _Out_ DWORD& dwBytesTransferred, _In_ BOOL bWait); void CancelIo(); DWORD BytesWaiting(); DWORD BytesPending(); //Configuration Methods void GetConfig(_In_ COMMCONFIG& config); static void GetDefaultConfig(_In_ int nPort, _Out_ COMMCONFIG& config); static void GetDefaultConfig(_In_z_ LPCTSTR pszPort, _Out_ COMMCONFIG& config); void SetConfig(_In_ COMMCONFIG& Config); static void SetDefaultConfig(_In_ int nPort, _In_ COMMCONFIG& config); static void SetDefaultConfig(_In_z_ LPCTSTR pszPort, _In_ COMMCONFIG& config); //Misc RS232 Methods void ClearBreak(); void SetBreak(); void ClearError(_Out_ DWORD& dwErrors); void GetStatus(_Out_ COMSTAT& stat); void GetState(_Out_ DCB& dcb); void SetState(_In_ DCB& dcb); void Escape(_In_ DWORD dwFunc); void ClearDTR(); void ClearRTS(); void SetDTR(); void SetRTS(); void SetXOFF(); void SetXON(); void GetProperties(_Inout_ COMMPROP& properties); void GetModemStatus(_Out_ DWORD& dwModemStatus); //Timeouts void SetTimeouts(_In_ COMMTIMEOUTS& timeouts); void GetTimeouts(_Out_ COMMTIMEOUTS& timeouts); void Set0Timeout(); void Set0WriteTimeout(); void Set0ReadTimeout(); //Event Methods void SetMask(_In_ DWORD dwMask); void GetMask(_Out_ DWORD& dwMask); void WaitEvent(_Inout_ DWORD& dwMask); BOOL WaitEvent(_Inout_ DWORD& dwMask, _Inout_ OVERLAPPED& overlapped); //Queue Methods void Flush(); void Purge(_In_ DWORD dwFlags); void TerminateOutstandingWrites(); void TerminateOutstandingReads(); void ClearWriteBuffer(); void ClearReadBuffer(); void Setup(_In_ DWORD dwInQueue, _In_ DWORD dwOutQueue); //Static methods static void ThrowSerialException(_In_ DWORD dwError = 0); protected: //Member variables HANDLE m_hComm; //Handle to the comms port }; #endif //#ifndef __SERIALPORT_H__ /* Module : SerialPort.cpp Purpose: Implementation for an C++ wrapper class for serial ports Created: PJN / 31-05-1999 History: PJN / 03-06-1999 1. Fixed problem with code using CancelIo which does not exist on 95. 2. Fixed leaks which can occur in sample app when an exception is thrown PJN / 16-06-1999 1. Fixed a bug whereby CString::ReleaseBuffer was not being called in CSerialException::GetErrorMessage PJN / 29-09-1999 1. Fixed a simple copy and paste bug in CSerialPort::SetDTR PJN / 08-05-2000 1. Fixed an unreferrenced variable in CSerialPort::GetOverlappedResult in VC 6 PJN / 10-12-2000 1. Made class destructor virtual PJN / 15-01-2001 1. Attach method now also allows you to specify whether the serial port is being attached to in overlapped mode 2. Removed some ASSERTs which were unnecessary in some of the functions 3. Updated the Read method which uses OVERLAPPED IO to also return the bytes read. This allows calls to WriteFile with a zeroed overlapped structure (This is required when dealing with TAPI and serial communications) 4. Now includes copyright message in the source code and documentation. PJN / 24-03-2001 1. Added a BytesWaiting method PJN / 04-04-2001 1. Provided an overriden version of BytesWaiting which specifies a timeout PJN / 23-04-2001 1. Fixed a memory leak in DataWaiting method PJN / 01-05-2002 1. Fixed a problem in Open method which was failing to initialize the DCB structure incorrectly, when calling GetState. Thanks to Ben Newson for this fix. PJN / 29-05-2002 1. Fixed an problem where the GetProcAddress for CancelIO was using the wrong calling convention PJN / 07-08-2002 1. Changed the declaration of CSerialPort::WaitEvent to be consistent with the rest of the methods in CSerialPort which can operate in "OVERLAPPED" mode. A note about the usage of this: If the method succeeds then the overlapped operation has completed synchronously and there is no need to do a WaitForSingle/MultipleObjects. If any other unexpected error occurs then a CSerialException will be thrown. See the implementation of the CSerialPort::DataWaiting which has been rewritten to use this new design pattern. Thanks to Serhiy Pavlov for spotting this inconsistency. PJN / 20-09-2002 1. Addition of an additional ASSERT in the internal _OnCompletion function. 2. Addition of an optional out parameter to the Write method which operates in overlapped mode. Thanks to Kevin Pinkerton for this addition. PJN / 10-04-2006 1. Updated copyright details. 2. Addition of a CSERIALPORT_EXT_CLASS and CSERIALPORT_EXT_API macros which makes the class easier to use in an extension dll. 3. Removed derivation of CSerialPort from CObject as it was not really needed. 4. Fixed a number of level 4 warnings in the sample app. 5. Reworked the overlapped IO methods to expose the LPOVERLAPPED structure to client code. 6. Updated the documentation to use the same style as the web site. 7. Did a spell check of the HTML documentation. 8. Updated the documentation on possible blocking in Read/Ex function. Thanks to D Kerrison for reporting this issue. 9. Fixed a minor issue in the sample app when the code is compiled using /Wp64 PJN / 02-06-2006 1. Removed the bOverlapped as a member variable from the class. There was no real need for this setting, since the SDK functions will perform their own checking of how overlapped operations should 2. Fixed a bug in GetOverlappedResult where the code incorrectly checking against the error ERROR_IO_PENDING instead of ERROR_IO_INCOMPLETE. Thanks to Sasho Darmonski for reporting this bug. 3. Reviewed all TRACE statements for correctness. PJN / 05-06-2006 1. Fixed an issue with the creation of the internal event object. It was incorrectly being created as an auto-reset event object instead of a manual reset event object. Thanks to Sasho Darmonski for reporting this issue. PJN / 24-06-2006 1. Fixed some typos in the history list. Thanks to Simon Wong for reporting this. 2. Made the class which handles the construction of function pointers at runtime a member variable of CSerialPort 3. Made AfxThrowSerialPortException part of the CSerialPort class. Thanks to Simon Wong for reporting this. 4. Removed the unnecessary CSerialException destructor. Thanks to Simon Wong for reporting this. 5. Fixed a minor error in the TRACE text in CSerialPort::SetDefaultConfig. Again thanks to Simon Wong for reporting this. 6. Code now uses new C++ style casts rather than old style C casts where necessary. Again thanks to Simon Wong for reporting this. 7. CSerialException::GetErrorMessage now uses the strsafe functions. This does mean that the code now requires the Platform SDK if compiled using VC 6. PJN / 25-06-2006 1. Combined the functionality of the CSerialPortData class into the main CSerialPort class. 2. Renamed AfxThrowSerialPortException to ThrowSerialPortException and made the method public. PJN / 05-11-2006 1. Minor update to stdafx.h of sample app to avoid compiler warnings in VC 2005. 2. Reverted the use of the strsafe.h header file. Instead now the code uses the VC 2005 Safe CRT and if this is not available, then we fail back to the standard CRT. PJN / 25-01-2007 1. Minor update to remove strsafe.h from stdafx.h of the sample app. 2. Updated copyright details. PJN / 24-12-2007 1. CSerialException::GetErrorMessage now uses the FORMAT_MESSAGE_IGNORE_INSERTS flag. For more information please see Raymond Chen's blog at http://blogs.msdn.com/oldnewthing/archive/2007/11/28/6564257.aspx. Thanks to Alexey Kuznetsov for reporting this issue. 2. Simplified the code in CSerialException::GetErrorMessage somewhat. 3. Optimized the CSerialException constructor code. 4. Code now uses newer C++ style casts instead of C style casts. 5. Reviewed and updated all the TRACE logging in the module 6. Replaced all calls to ZeroMemory with memset PJN / 30-12-2007 1. Updated the sample app to clean compile on VC 2008 2. CSerialException::GetErrorMessage now uses Checked::tcsncpy_s if compiled using VC 2005 or later. PJN / 18-05-2008 1. Updated copyright details. 2. Changed the actual values for Parity enum so that they are consistent with the Parity define values in the Windows SDK header file WinBase.h. This avoids the potential issue where you use the CSerialPort enum parity values in a call to the raw Win32 API calls. Thanks to Robert Krueger for reporting this issue. PJN / 21-06-2008 1. Code now compiles cleanly using Code Analysis (/analyze) 2. Updated code to compile correctly using _ATL_CSTRING_EXPLICIT_CONSTRUCTORS define 3. The code now only supports VC 2005 or later. 4. CSerialPort::Read, Write, GetOverlappedResult & WaitEvent now throw an exception irrespective of whether the last error is ERROR_IO_PENDING or not 5. Replaced all calls to ZeroMemory with memset PJN / 04-07-2008 1. Provided a version of the Open method which takes a string instead of a numeric port number value. This allows the code to support some virtual serial port packages which do not use device names of the form "COM%d". Thanks to David Balazic for suggesting this addition. PJN / 25-01-2012 1. Updated copyright details. 2. Updated sample app and class to compile cleanly on VC 2010 and later. PJN / 28-02-2015 1. Updated sample project settings to more modern default values. 2. Updated copyright details. 3. Reworked the CSerialPort and CSerialPortException classes to optionally compile without MFC. By default the classes now use STL classes and idioms but if you define CSERIALPORT_MFC_EXTENSTIONS the classes will revert back to the MFC behaviour. 4. Remove logic to use GetProcAddress to access CancelIO functionality. 5. Updated the code to clean compile on VC 2013 6. Added SAL annotations to all the code 7. Addition of a GetDefaultConfig method which takes a string 8. Addition of a SetDefaultConfig method which takes a string PJN / 26-04-2015 1. Removed unnecessary inclusion of WinError.h 2. Removed the CSerialPort::DataWaiting method as it depends on the port being open in overlapped mode. Instead client code can simply call CSerialPort::WaitEvent directly themselves. Removing this method also means that the CSerialPort::m_hEvent handle has not also been removed. 3. The CSerialPort::WriteEx method has been reworked to expose all the parameters of the underlying WriteFileEx API. This rework also fixes a memory leak in WriteEx which can sometimes occur. This reworks also means that the CSerialPort::_OnCompletion and CSerialPort::_OnCompletion methods have been removed. Thanks to Yufeng Huang for reporting this issue. 4. The CSerialPort::ReadEx method has been reworked to expose all the parameters of the underlying ReadFileEx API. This rework also fixes a memory leak in ReadEx which can sometimes occur. This reworks also means that the CSerialPort::_OnCompletion and CSerialPort::_OnCompletion methods have been removed. Thanks to Yufeng Huang for reporting this issue. Copyright (c) 1996 - 2015 by PJ Naughter (Web: www.naughter.com, Email: pjna@naughter.com) All rights reserved. Copyright / Usage Details: You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise) when your product is released in binary form. You are allowed to modify the source code in any way you want except you cannot modify the copyright details at the top of each module. If you want to distribute source code with your application, then you are only allowed to distribute versions released by the author. This is to maintain a single distribution point for the source code. */ // Includes #include "pch.h" #include "SerialPort.h" #ifndef __ATLBASE_H__ #pragma message("To avoid this message, please put atlbase.h in your pre compiled header (normally stdafx.h)") #include <atlbase.h> #endif //#ifndef __ATLBASE_H__ // Defines / #ifdef CSERIALPORT_MFC_EXTENSIONS #ifdef _DEBUG #define new DEBUG_NEW #endif //#ifdef _DEBUG #endif //#ifdef CSERIALPORT_MFC_EXTENSIONS //Implementation /// #if _MSC_VER >= 1700 BOOL CSerialException::GetErrorMessage(_Out_z_cap_(nMaxError) LPTSTR lpszError, _In_ UINT nMaxError, _Out_opt_ PUINT pnHelpContext) #else BOOL CSerialException::GetErrorMessage(__out_ecount_z(nMaxError) LPTSTR lpszError, __in UINT nMaxError, __out_opt PUINT pnHelpContext) #endif { //Validate our parameters ATLASSERT(lpszError != NULL); if (pnHelpContext != NULL) *pnHelpContext = 0; //What will be the return value from this function (assume the worst) BOOL bSuccess = FALSE; LPTSTR lpBuffer; DWORD dwReturn = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, m_dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT), reinterpret_cast<LPTSTR>(&lpBuffer), 0, NULL); if (dwReturn == 0) *lpszError = _T('\0'); else { bSuccess = TRUE; Checked::tcsncpy_s(lpszError, nMaxError, lpBuffer, _TRUNCATE); LocalFree(lpBuffer); } return bSuccess; } #ifdef CSERIALPORT_MFC_EXTENSIONS CString CSerialException::GetErrorMessage() { CString rVal; LPTSTR pstrError = rVal.GetBuffer(4096); GetErrorMessage(pstrError, 4096, NULL); rVal.ReleaseBuffer(); return rVal; } #endif //#ifdef CSERIALPORT_MFC_EXTENSIONS CSerialException::CSerialException(DWORD dwError) : m_dwError(dwError) { } #ifdef CSERIALPORT_MFC_EXTENSIONS #ifdef _DEBUG void CSerialException::Dump(_In_ CDumpContext& dc) const { CObject::Dump(dc); dc << _T("m_dwError = ") << m_dwError << _T("\n"); } #endif //#ifdef _DEBUG #endif //#ifdef CSERIALPORT_MFC_EXTENSIONS CSerialPort::CSerialPort() : m_hComm(INVALID_HANDLE_VALUE) { } CSerialPort::~CSerialPort() { Close(); } void CSerialPort::ThrowSerialException(_In_ DWORD dwError) { if (dwError == 0) dwError = ::GetLastError(); ATLTRACE(_T("Warning: throwing CSerialException for error %d\n"), dwError); #ifdef CSERIALPORT_MFC_EXTENSIONS CSerialException* pException = new CSerialException(dwError); THROW(pException); #else CSerialException e(dwError); throw e; #endif //#ifdef CSERIALPORT_MFC_EXTENSIONS } #ifdef CSERIALPORT_MFC_EXTENSIONS #ifdef _DEBUG void CSerialPort::Dump(CDumpContext& dc) const { dc << _T("m_hComm = ") << m_hComm << _T("\n"); } #endif //#ifdef _DEBUG #endif //#ifdef CSERIALPORT_MFC_EXTENSIONS void CSerialPort::Open(_In_z_ LPCTSTR pszPort, _In_ DWORD dwBaud, _In_ Parity parity, _In_ BYTE DataBits, _In_ StopBits stopBits, _In_ FlowControl fc, _In_ BOOL bOverlapped) { Close(); //In case we are already open //Call CreateFile to open the comms port m_hComm = CreateFile(pszPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, bOverlapped ? (FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED) : 0, NULL); if (m_hComm == INVALID_HANDLE_VALUE) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::Open, Failed to open the comms port, Error:%u\n"), dwLastError); //ThrowSerialException(dwLastError); return; } //Get the current state prior to changing it DCB dcb; dcb.DCBlength = sizeof(DCB); GetState(dcb); //Setup the baud rate dcb.BaudRate = dwBaud; //Setup the Parity switch (parity) { case EvenParity: { dcb.Parity = EVENPARITY; break; } case MarkParity: { dcb.Parity = MARKPARITY; break; } case NoParity: { dcb.Parity = NOPARITY; break; } case OddParity: { dcb.Parity = ODDPARITY; break; } case SpaceParity: { dcb.Parity = SPACEPARITY; break; } default: { ATLASSERT(FALSE); break; } } //Setup the data bits dcb.ByteSize = DataBits; //Setup the stop bits switch (stopBits) { case OneStopBit: { dcb.StopBits = ONESTOPBIT; break; } case OnePointFiveStopBits: { dcb.StopBits = ONE5STOPBITS; break; } case TwoStopBits: { dcb.StopBits = TWOSTOPBITS; break; } default: { ATLASSERT(FALSE); break; } } //Setup the flow control dcb.fDsrSensitivity = FALSE; switch (fc) { case NoFlowControl: { dcb.fOutxCtsFlow = FALSE; dcb.fOutxDsrFlow = FALSE; dcb.fOutX = FALSE; dcb.fInX = FALSE; break; } case CtsRtsFlowControl: { dcb.fOutxCtsFlow = TRUE; dcb.fOutxDsrFlow = FALSE; dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; dcb.fOutX = FALSE; dcb.fInX = FALSE; break; } case CtsDtrFlowControl: { dcb.fOutxCtsFlow = TRUE; dcb.fOutxDsrFlow = FALSE; dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; dcb.fOutX = FALSE; dcb.fInX = FALSE; break; } case DsrRtsFlowControl: { dcb.fOutxCtsFlow = FALSE; dcb.fOutxDsrFlow = TRUE; dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; dcb.fOutX = FALSE; dcb.fInX = FALSE; break; } case DsrDtrFlowControl: { dcb.fOutxCtsFlow = FALSE; dcb.fOutxDsrFlow = TRUE; dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; dcb.fOutX = FALSE; dcb.fInX = FALSE; break; } case XonXoffFlowControl: { dcb.fOutxCtsFlow = FALSE; dcb.fOutxDsrFlow = FALSE; dcb.fOutX = TRUE; dcb.fInX = TRUE; dcb.XonChar = 0x11; dcb.XoffChar = 0x13; dcb.XoffLim = 100; dcb.XonLim = 100; break; } default: { ATLASSERT(FALSE); break; } } //Now that we have all the settings in place, make the changes SetState(dcb); } void CSerialPort::Open(_In_ int nPort, _In_ DWORD dwBaud, _In_ Parity parity, _In_ BYTE DataBits, _In_ StopBits stopBits, _In_ FlowControl fc, _In_ BOOL bOverlapped) { //Form the string version of the port number TCHAR szPort[12]; _stprintf_s(szPort, sizeof(szPort) / sizeof(TCHAR), _T("\\\\.\\COM%d"), nPort); //Delegate the work to the other version of Open Open(szPort, dwBaud, parity, DataBits, stopBits, fc, bOverlapped); } void CSerialPort::Close() { if (IsOpen()) { //Close down the comms port CloseHandle(m_hComm); m_hComm = INVALID_HANDLE_VALUE; } } void CSerialPort::Attach(_In_ HANDLE hComm) { Close(); //Validate our parameters, now that the port has been closed ATLASSERT(m_hComm == INVALID_HANDLE_VALUE); m_hComm = hComm; } HANDLE CSerialPort::Detach() { //What will be the return value from this function HANDLE hComm = m_hComm; m_hComm = INVALID_HANDLE_VALUE; return hComm; } void CSerialPort::Read(_Out_writes_bytes_(dwNumberOfBytesToRead) __out_data_source(FILE) void* lpBuffer, _In_ DWORD dwNumberOfBytesToRead) { //Validate our parameters ATLASSERT(IsOpen()); DWORD dwBytesRead = 0; if (!ReadFile(m_hComm, lpBuffer, dwNumberOfBytesToRead, &dwBytesRead, NULL)) { DWORD dwLastError = GetLastError(); if (ERROR_IO_PENDING != dwLastError) ATLTRACE(_T("CSerialPort::Read, Failed in call to ReadFile, Error:%u\n"), dwLastError); // ThrowSerialException(dwLastError); } } void CSerialPort::Read(_Out_writes_bytes_to_opt_(dwNumberOfBytesToRead, *lpNumberOfBytesRead) __out_data_source(FILE) void* lpBuffer, _In_ DWORD dwNumberOfBytesToRead, _In_ OVERLAPPED& overlapped, _Inout_opt_ DWORD* lpNumberOfBytesRead) { //Validate our parameters ATLASSERT(IsOpen()); *lpNumberOfBytesRead = 0; if (overlapped.hEvent) ResetEvent(overlapped.hEvent); if (!ReadFile(m_hComm, lpBuffer, dwNumberOfBytesToRead, lpNumberOfBytesRead, &overlapped)) { DWORD dwLastError = GetLastError(); if (ERROR_IO_PENDING != dwLastError) { ATLTRACE(_T("CSerialPort::Read, Failed in call to ReadFile, Error:%u\n"), dwLastError); } // ThrowSerialException(dwLastError); } } void CSerialPort::ReadEx(_Out_writes_bytes_opt_(dwNumberOfBytesToRead) __out_data_source(FILE) LPVOID lpBuffer, _In_ DWORD dwNumberOfBytesToRead, _Inout_ LPOVERLAPPED lpOverlapped, _In_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) { //Validate our parameters ATLASSERT(IsOpen()); if (!ReadFileEx(m_hComm, lpBuffer, dwNumberOfBytesToRead, lpOverlapped, lpCompletionRoutine)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::ReadEx, Failed in call to ReadFileEx, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } DWORD CSerialPort::Write(_In_reads_bytes_opt_(dwNumberOfBytesToWrite) const void* lpBuffer, _In_ DWORD dwNumberOfBytesToWrite) { //Validate our parameters ATLASSERT(IsOpen()); DWORD dwBytesWritten = 0; if (!WriteFile(m_hComm, lpBuffer, dwNumberOfBytesToWrite, &dwBytesWritten, NULL)) { DWORD dwLastError = GetLastError(); if (ERROR_IO_PENDING != dwLastError) ATLTRACE(_T("CSerialPort::Write, Failed in call to WriteFile, Error:%u\n"), dwLastError); //ThrowSerialException(dwLastError); } return dwBytesWritten; } void CSerialPort::Write(_In_reads_bytes_opt_(dwNumberOfBytesToWrite) const void* lpBuffer, _In_ DWORD dwNumberOfBytesToWrite, _In_ OVERLAPPED& overlapped, _Out_opt_ DWORD* lpNumberOfBytesWritten) { //Validate our parameters ATLASSERT(IsOpen()); if (!WriteFile(m_hComm, lpBuffer, dwNumberOfBytesToWrite, lpNumberOfBytesWritten, &overlapped)) { DWORD dwLastError = GetLastError(); if (ERROR_IO_PENDING != dwLastError) { ATLTRACE(_T("CSerialPort::Write, Failed in call to WriteFile, Error:%u\n"), dwLastError); } // ThrowSerialException(dwLastError); } } void CSerialPort::WriteEx(_In_reads_bytes_opt_(dwNumberOfBytesToWrite) LPCVOID lpBuffer, _In_ DWORD dwNumberOfBytesToWrite, _Inout_ LPOVERLAPPED lpOverlapped, _In_ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) { //Validate our parameters ATLASSERT(IsOpen()); if (!WriteFileEx(m_hComm, lpBuffer, dwNumberOfBytesToWrite, lpOverlapped, lpCompletionRoutine)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::WriteEx, Failed in call to WriteFileEx, Error:%u\n"), dwLastError); //ThrowSerialException(dwLastError); } } DWORD CSerialPort::GetOverlappedResult(_In_ OVERLAPPED& overlapped, _Out_ DWORD& dwBytesTransferred, _In_ BOOL bWait) { //Validate our parameters DWORD dwLastError = 0; ATLASSERT(IsOpen()); dwBytesTransferred = 0; if (!::GetOverlappedResult(m_hComm, &overlapped, &dwBytesTransferred, bWait)) { dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::GetOverlappedResult, Failed in call to GetOverlappedResult, Error:%u\n"), dwLastError); //ThrowSerialException(dwLastError); } return dwLastError; } void CSerialPort::CancelIo() { //Validate our parameters ATLASSERT(IsOpen()); if (!::CancelIo(m_hComm)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("Failed in call to CancelIO, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } DWORD CSerialPort::BytesWaiting() { //Validate our parameters //ATLASSERT(IsOpen()); if (!IsOpen()) return 0; //Check to see how many characters are unread COMSTAT stat; GetStatus(stat); return stat.cbInQue; } DWORD CSerialPort::BytesPending() { if (!IsOpen()) return 0; //Check to see how many characters are unwrite COMSTAT stat; GetStatus(stat); return stat.cbOutQue; } void CSerialPort::TransmitChar(_In_ char cChar) { //Validate our parameters ATLASSERT(IsOpen()); if (!TransmitCommChar(m_hComm, cChar)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::TransmitChar, Failed in call to TransmitCommChar, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::GetConfig(_In_ COMMCONFIG& config) { //Validate our parameters ATLASSERT(IsOpen()); DWORD dwSize = sizeof(COMMCONFIG); if (!GetCommConfig(m_hComm, &config, &dwSize)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::GetConfig, Failed in call to GetCommConfig, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::SetConfig(_In_ COMMCONFIG& config) { //Validate our parameters ATLASSERT(IsOpen()); DWORD dwSize = sizeof(COMMCONFIG); if (!SetCommConfig(m_hComm, &config, dwSize)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::SetConfig, Failed in call to SetCommConfig, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::SetBreak() { //Validate our parameters ATLASSERT(IsOpen()); if (!SetCommBreak(m_hComm)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::SetBreak, Failed in call to SetCommBreak, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::ClearBreak() { //Validate our parameters ATLASSERT(IsOpen()); if (!ClearCommBreak(m_hComm)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::ClearBreak, Failed in call to SetCommBreak, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::ClearError(_Out_ DWORD& dwErrors) { //Validate our parameters ATLASSERT(IsOpen()); if (!ClearCommError(m_hComm, &dwErrors, NULL)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::ClearError, Failed in call to ClearCommError, Error:%u\n"), dwLastError); //ThrowSerialException(dwLastError); } } void CSerialPort::GetDefaultConfig(_In_ int nPort, _Out_ COMMCONFIG& config) { //Create the device name as a string TCHAR szPort[12]; _stprintf_s(szPort, sizeof(szPort) / sizeof(TCHAR), _T("COM%d"), nPort); return GetDefaultConfig(szPort, config); } void CSerialPort::GetDefaultConfig(_In_z_ LPCTSTR pszPort, _Out_ COMMCONFIG& config) { DWORD dwSize = sizeof(COMMCONFIG); if (!GetDefaultCommConfig(pszPort, &config, &dwSize)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::GetDefaultConfig, Failed in call to GetDefaultCommConfig, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::SetDefaultConfig(_In_ int nPort, _In_ COMMCONFIG& config) { //Create the device name as a string TCHAR szPort[12]; _stprintf_s(szPort, sizeof(szPort) / sizeof(TCHAR), _T("COM%d"), nPort); return SetDefaultConfig(szPort, config); } void CSerialPort::SetDefaultConfig(_In_z_ LPCTSTR pszPort, _In_ COMMCONFIG& config) { DWORD dwSize = sizeof(COMMCONFIG); if (!SetDefaultCommConfig(pszPort, &config, dwSize)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::SetDefaultConfig, Failed in call to SetDefaultCommConfig, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::GetStatus(_Out_ COMSTAT& stat) { //Validate our parameters ATLASSERT(IsOpen()); DWORD dwErrors; if (!ClearCommError(m_hComm, &dwErrors, &stat)) { DWORD dwLastError = GetLastError(); stat.cbInQue = 0; ATLTRACE(_T("CSerialPort::GetStatus, Failed in call to ClearCommError, Error:%u\n"), dwLastError); // ThrowSerialException(dwLastError); } } void CSerialPort::GetState(_Out_ DCB& dcb) { //Validate our parameters ATLASSERT(IsOpen()); if (!GetCommState(m_hComm, &dcb)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::GetState, Failed in call to GetCommState, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::SetState(_In_ DCB& dcb) { //Validate our parameters ATLASSERT(IsOpen()); if (!SetCommState(m_hComm, &dcb)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::SetState, Failed in call to SetCommState, Error:%u\n"), dwLastError); // ThrowSerialException(dwLastError); } } void CSerialPort::Escape(_In_ DWORD dwFunc) { //Validate our parameters ATLASSERT(IsOpen()); if (!EscapeCommFunction(m_hComm, dwFunc)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::Escape, Failed in call to EscapeCommFunction, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::ClearDTR() { Escape(CLRDTR); } void CSerialPort::ClearRTS() { Escape(CLRRTS); } void CSerialPort::SetDTR() { Escape(SETDTR); } void CSerialPort::SetRTS() { Escape(SETRTS); } void CSerialPort::SetXOFF() { Escape(SETXOFF); } void CSerialPort::SetXON() { Escape(SETXON); } void CSerialPort::GetProperties(_Inout_ COMMPROP& properties) { //Validate our parameters ATLASSERT(IsOpen()); if (!GetCommProperties(m_hComm, &properties)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::GetProperties, Failed in call to GetCommProperties, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::GetModemStatus(_Out_ DWORD& dwModemStatus) { //Validate our parameters ATLASSERT(IsOpen()); if (!GetCommModemStatus(m_hComm, &dwModemStatus)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::GetModemStatus, Failed in call to GetCommModemStatus, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::SetMask(_In_ DWORD dwMask) { //Validate our parameters ATLASSERT(IsOpen()); if (!SetCommMask(m_hComm, dwMask)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::SetMask, Failed in call to SetCommMask, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::GetMask(_Out_ DWORD& dwMask) { //Validate our parameters ATLASSERT(IsOpen()); if (!GetCommMask(m_hComm, &dwMask)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::GetMask, Failed in call to GetCommMask, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::Flush() { //Validate our parameters ATLASSERT(IsOpen()); if (!FlushFileBuffers(m_hComm)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::Flush, Failed in call to FlushFileBuffers, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::Purge(_In_ DWORD dwFlags) { //Validate our parameters ATLASSERT(IsOpen()); if (!PurgeComm(m_hComm, dwFlags)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::Purge, Failed in call to PurgeComm, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::TerminateOutstandingWrites() { Purge(PURGE_TXABORT); } void CSerialPort::TerminateOutstandingReads() { Purge(PURGE_RXABORT); } void CSerialPort::ClearWriteBuffer() { Purge(PURGE_TXCLEAR); } void CSerialPort::ClearReadBuffer() { Purge(PURGE_RXCLEAR); } void CSerialPort::Setup(_In_ DWORD dwInQueue, _In_ DWORD dwOutQueue) { //Validate our parameters ATLASSERT(IsOpen()); if (!SetupComm(m_hComm, dwInQueue, dwOutQueue)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::Setup, Failed in call to SetupComm, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::SetTimeouts(_In_ COMMTIMEOUTS& timeouts) { //Validate our parameters ATLASSERT(IsOpen()); if (!SetCommTimeouts(m_hComm, &timeouts)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::SetTimeouts, Failed in call to SetCommTimeouts, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::GetTimeouts(_Out_ COMMTIMEOUTS& timeouts) { //Validate our parameters ATLASSERT(IsOpen()); if (!GetCommTimeouts(m_hComm, &timeouts)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::GetTimeouts, Failed in call to GetCommTimeouts, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } void CSerialPort::Set0Timeout() { COMMTIMEOUTS Timeouts; memset(&Timeouts, 0, sizeof(Timeouts)); Timeouts.ReadIntervalTimeout = MAXDWORD; SetTimeouts(Timeouts); } void CSerialPort::Set0WriteTimeout() { COMMTIMEOUTS Timeouts; GetTimeouts(Timeouts); Timeouts.WriteTotalTimeoutMultiplier = 0; Timeouts.WriteTotalTimeoutConstant = 0; SetTimeouts(Timeouts); } void CSerialPort::Set0ReadTimeout() { COMMTIMEOUTS Timeouts; GetTimeouts(Timeouts); Timeouts.ReadIntervalTimeout = MAXDWORD; Timeouts.ReadTotalTimeoutMultiplier = 0; Timeouts.ReadTotalTimeoutConstant = 0; SetTimeouts(Timeouts); } void CSerialPort::WaitEvent(_Inout_ DWORD& dwMask) { //Validate our parameters ATLASSERT(IsOpen()); if (!WaitCommEvent(m_hComm, &dwMask, NULL)) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::WaitEvent, Failed in call to WaitCommEvent, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } } BOOL CSerialPort::WaitEvent(_Inout_ DWORD& dwMask, _Inout_ OVERLAPPED& overlapped) { //Validate our parameters ATLASSERT(IsOpen()); ATLASSERT(overlapped.hEvent != NULL); BOOL bSuccess = WaitCommEvent(m_hComm, &dwMask, &overlapped); if (!bSuccess) { DWORD dwLastError = GetLastError(); ATLTRACE(_T("CSerialPort::WaitEvent, Failed in call to WaitCommEvent, Error:%u\n"), dwLastError); ThrowSerialException(dwLastError); } return bSuccess; } #pragma once #include "afxwin.h" // 自定义串口异常类 class CSerialException : public CException { public: DWORD m_dwError; CSerialException(DWORD dwError); }; // 串口类声明 class CSerialPort { public: enum Parity { None, Odd, Even }; enum StopBits { One = 1, OnePointFive = 2, Two = 3 }; CSerialPort(); virtual ~CSerialPort(); // 添加 virtual 关键字 BOOL Open(LPCTSTR lpszPort, DWORD dwBaudRate, Parity parity, BYTE byDataBits, StopBits stopBits); void Close(); BOOL IsOpen() const { return m_hComm != INVALID_HANDLE_VALUE; } DWORD Write(const void* lpBuf, DWORD dwCount); DWORD Read(void* lpBuf, DWORD dwCount); DWORD BytesWaiting(); void Set0Timeout(); private: HANDLE m_hComm; }; // CPCMFCDlg 对话框 class CPCMFCDlg : public CDialogEx { public: CPCMFCDlg(CWnd* pParent = nullptr); virtual ~CPCMFCDlg(); #ifdef AFX_DESIGN_TIME enum { IDD = IDD_PCMFC_DIALOG }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); virtual BOOL OnInitDialog(); protected: HICON m_hIcon; // 控件变量 CComboBox m_cbPort; CComboBox m_cbBaud; CComboBox m_cbParity; CComboBox m_cbDate; CComboBox m_cbStop; CListBox m_listStatus; // 数据变量 CString m_strTime1; CString m_strTime2; CString m_strTime3; // 状态变量 BOOL m_bConnected; BOOL m_bTimerActive; BOOL m_bThreadRunning; // 串口对象 CSerialPort m_SerialPort; HANDLE m_hThread; // 辅助函数 void UpdateControls(); UINT ReadThreadFunc(); static UINT ReadThread(LPVOID pParam); BOOL ValidateTimeFormat(const CString& strTime); // 消息处理 DECLARE_MESSAGE_MAP() afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); afx_msg void OnBnClickedBtnOpen(); afx_msg void OnBnClickedBtnClose(); afx_msg void OnBnClickedBtnFeedNow(); afx_msg void OnBnClickedBtnFeedTimer(); afx_msg LRESULT OnUpdateStatus(WPARAM wParam, LPARAM lParam); afx_msg void OnTimer(UINT_PTR nIDEvent); afx_msg void OnDestroy(); }; #include "pch.h" #include "PCMFC.h" #include "PCMFCDlg.h" #include "afxdialogex.h" #include <winbase.h> #include <tchar.h> #include <atlstr.h> #ifdef _DEBUG #define new DEBUG_NEW #endif #define FEED_COMMAND 0x01 #define COMPLETE_SIGNAL 0xFF #define WM_UPDATE_STATUS (WM_USER + 100) // CSerialException 实现 CSerialException::CSerialException(DWORD dwError) : m_dwError(dwError) {} // CSerialPort 成员函数实现 CSerialPort::CSerialPort() : m_hComm(INVALID_HANDLE_VALUE) {} CSerialPort::~CSerialPort() { if (IsOpen()) Close(); } BOOL CSerialPort::Open(LPCTSTR lpszPort, DWORD dwBaudRate, Parity parity, BYTE byDataBits, StopBits stopBits) { m_hComm = CreateFile(lpszPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (m_hComm == INVALID_HANDLE_VALUE) throw new CSerialException(GetLastError()); DCB dcb = { 0 }; dcb.DCBlength = sizeof(DCB); if (!GetCommState(m_hComm, &dcb)) throw new CSerialException(GetLastError()); dcb.BaudRate = dwBaudRate; dcb.ByteSize = byDataBits; switch (parity) { case None: dcb.Parity = NOPARITY; break; case Odd: dcb.Parity = ODDPARITY; break; case Even: dcb.Parity = EVENPARITY; break; } switch (stopBits) { case One: dcb.StopBits = ONESTOPBIT; break; case OnePointFive: dcb.StopBits = ONE5STOPBITS; break; case Two: dcb.StopBits = TWOSTOPBITS; break; } dcb.fBinary = TRUE; dcb.fDtrControl = DTR_CONTROL_ENABLE; dcb.fRtsControl = RTS_CONTROL_ENABLE; if (!SetCommState(m_hComm, &dcb)) throw new CSerialException(GetLastError()); // 设置超时 COMMTIMEOUTS timeouts = { 0 }; timeouts.ReadIntervalTimeout = 50; timeouts.ReadTotalTimeoutConstant = 50; timeouts.ReadTotalTimeoutMultiplier = 10; timeouts.WriteTotalTimeoutConstant = 50; timeouts.WriteTotalTimeoutMultiplier = 10; if (!SetCommTimeouts(m_hComm, &timeouts)) throw new CSerialException(GetLastError()); return TRUE; } void CSerialPort::Close() { if (m_hComm != INVALID_HANDLE_VALUE) { CloseHandle(m_hComm); m_hComm = INVALID_HANDLE_VALUE; } } DWORD CSerialPort::Write(const void* lpBuf, DWORD dwCount) { DWORD dwBytesWritten = 0; OVERLAPPED ov = { 0 }; ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!WriteFile(m_hComm, lpBuf, dwCount, &dwBytesWritten, &ov)) { if (GetLastError() == ERROR_IO_PENDING) { WaitForSingleObject(ov.hEvent, INFINITE); GetOverlappedResult(m_hComm, &ov, &dwBytesWritten, FALSE); } else { throw new CSerialException(GetLastError()); } } CloseHandle(ov.hEvent); return dwBytesWritten; } DWORD CSerialPort::Read(void* lpBuf, DWORD dwCount) { DWORD dwBytesRead = 0; OVERLAPPED ov = { 0 }; ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!ReadFile(m_hComm, lpBuf, dwCount, &dwBytesRead, &ov)) { if (GetLastError() == ERROR_IO_PENDING) { WaitForSingleObject(ov.hEvent, INFINITE); GetOverlappedResult(m_hComm, &ov, &dwBytesRead, FALSE); } else { throw new CSerialException(GetLastError()); } } CloseHandle(ov.hEvent); return dwBytesRead; } DWORD CSerialPort::BytesWaiting() { COMSTAT comStat; DWORD dwErrors; if (!ClearCommError(m_hComm, &dwErrors, &comStat)) throw new CSerialException(GetLastError()); return comStat.cbInQue; } void CSerialPort::Set0Timeout() { COMMTIMEOUTS timeouts = { 0 }; timeouts.ReadIntervalTimeout = MAXDWORD; timeouts.ReadTotalTimeoutConstant = 0; timeouts.ReadTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutConstant = 0; if (!SetCommTimeouts(m_hComm, &timeouts)) throw new CSerialException(GetLastError()); } // CPCMFCDlg 实现 CPCMFCDlg::CPCMFCDlg(CWnd* pParent) : CDialogEx(IDD_PCMFC_DIALOG, pParent) , m_strTime1(_T("08:00")) , m_strTime2(_T("12:00")) , m_strTime3(_T("18:00")) , m_bConnected(FALSE) , m_bTimerActive(FALSE) , m_bThreadRunning(FALSE) , m_hThread(NULL) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } CPCMFCDlg::~CPCMFCDlg() { if (m_hThread) { CloseHandle(m_hThread); } } void CPCMFCDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_COMBO_PORT, m_cbPort); DDX_Control(pDX, IDC_COMBO_BAUD, m_cbBaud); DDX_Control(pDX, IDC_COMBO_PARITY, m_cbParity); DDX_Control(pDX, IDC_COMBO_DATA, m_cbDate); DDX_Control(pDX, IDC_COMBO_STOP, m_cbStop); DDX_Control(pDX, IDC_LIST, m_listStatus); DDX_Text(pDX, IDC_EDIT_TIME1, m_strTime1); DDX_Text(pDX, IDC_EDIT_TIME2, m_strTime2); DDX_Text(pDX, IDC_EDIT_TIME3, m_strTime3); } BEGIN_MESSAGE_MAP(CPCMFCDlg, CDialogEx) ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BUTTON_OPEN, &CPCMFCDlg::OnBnClickedBtnOpen) ON_BN_CLICKED(IDC_BUTTON_CLOSE, &CPCMFCDlg::OnBnClickedBtnClose) ON_BN_CLICKED(IDC_BUTTON_NOWFEED, &CPCMFCDlg::OnBnClickedBtnFeedNow) ON_BN_CLICKED(IDC_BUTTON_TIMEFEED, &CPCMFCDlg::OnBnClickedBtnFeedTimer) ON_MESSAGE(WM_UPDATE_STATUS, &CPCMFCDlg::OnUpdateStatus) ON_WM_TIMER() ON_WM_DESTROY() END_MESSAGE_MAP() BOOL CPCMFCDlg::OnInitDialog() { CDialogEx::OnInitDialog(); SetIcon(m_hIcon, TRUE); SetIcon(m_hIcon, FALSE); // 初始化串口选择下拉框 for (int i = 1; i <= 16; i++) { CString port; port.Format(_T("COM%d"), i); m_cbPort.AddString(port); } m_cbPort.SetCurSel(0); // 初始化波特率下拉框 CString baudRates[] = { _T("9600"), _T("19200"), _T("38400"), _T("57600"), _T("115200") }; for (auto& baud : baudRates) { m_cbBaud.AddString(baud); } m_cbBaud.SetCurSel(0); // 初始化校验位下拉框 CString parityOptions[] = { _T("None"), _T("Odd"), _T("Even") }; for (auto& parity : parityOptions) { m_cbParity.AddString(parity); } m_cbParity.SetCurSel(0); // 初始化数据位下拉框 CString dataBits[] = { _T("5"), _T("6"), _T("7"), _T("8") }; for (auto& bits : dataBits) { m_cbDate.AddString(bits); } m_cbDate.SetCurSel(3); // 初始化停止位下拉框 CString stopBits[] = { _T("1"), _T("1.5"), _T("2") }; for (auto& stop : stopBits) { m_cbStop.AddString(stop); } m_cbStop.SetCurSel(0); // 初始禁用控件 UpdateControls(); return TRUE; } void CPCMFCDlg::UpdateControls() { BOOL bConnected = m_SerialPort.IsOpen(); GetDlgItem(IDC_BUTTON_OPEN)->EnableWindow(!bConnected); GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(bConnected); GetDlgItem(IDC_BUTTON_NOWFEED)->EnableWindow(bConnected); GetDlgItem(IDC_BUTTON_TIMEFEED)->EnableWindow(bConnected); BOOL bTimerActive = m_bTimerActive; GetDlgItem(IDC_EDIT_TIME1)->EnableWindow(bConnected && !bTimerActive); GetDlgItem(IDC_EDIT_TIME2)->EnableWindow(bConnected && !bTimerActive); GetDlgItem(IDC_EDIT_TIME3)->EnableWindow(bConnected && !bTimerActive); } BOOL CPCMFCDlg::ValidateTimeFormat(const CString& strTime) { if (strTime.GetLength() != 5) return FALSE; if (strTime[2] != ':') return FALSE; int hour = _ttoi(strTime.Left(2)); int minute = _ttoi(strTime.Mid(3)); return (hour >= 0 && hour <= 23) && (minute >= 0 && minute <= 59); } UINT CPCMFCDlg::ReadThread(LPVOID pParam) { CPCMFCDlg* pDlg = (CPCMFCDlg*)pParam; return pDlg->ReadThreadFunc(); } UINT CPCMFCDlg::ReadThreadFunc() { BYTE buffer[1]; while (m_bThreadRunning) { try { DWORD dwBytes = m_SerialPort.BytesWaiting(); if (dwBytes > 0) { if (m_SerialPort.Read(buffer, 1) > 0) { if (buffer[0] == COMPLETE_SIGNAL) { CString* pMsg = new CString(_T("喂食操作完成")); PostMessage(WM_UPDATE_STATUS, (WPARAM)pMsg, 0); } } } Sleep(50); } catch (CSerialException* e) { CString* pMsg = new CString(); pMsg->Format(_T("串口读取错误: %d"), e->m_dwError); PostMessage(WM_UPDATE_STATUS, (WPARAM)pMsg, 0); delete e; break; } } return 0; } void CPCMFCDlg::OnBnClickedBtnOpen() { CString strPort; m_cbPort.GetWindowText(strPort); strPort = _T("\\\\.\\") + strPort; CString strBaud; m_cbBaud.GetLBText(m_cbBaud.GetCurSel(), strBaud); DWORD dwBaud = _ttoi(strBaud); int nParity = m_cbParity.GetCurSel(); CString strDataBits; m_cbDate.GetLBText(m_cbDate.GetCurSel(), strDataBits); BYTE byDataBits = (BYTE)_ttoi(strDataBits); int nStopBits = m_cbStop.GetCurSel(); try { CSerialPort::Parity parity = static_cast<CSerialPort::Parity>(nParity); CSerialPort::StopBits stopBits = static_cast<CSerialPort::StopBits>(nStopBits); m_SerialPort.Open(strPort, dwBaud, parity, byDataBits, stopBits); m_SerialPort.Set0Timeout(); m_bConnected = TRUE; m_bThreadRunning = TRUE; m_hThread = (HANDLE)_beginthreadex(NULL, 0, ReadThread, this, 0, NULL); m_listStatus.AddString(_T("串口打开成功")); } catch (CSerialException* e) { CString strError; strError.Format(_T("打开串口失败: 错误代码 %d"), e->m_dwError); m_listStatus.AddString(strError); delete e; m_bConnected = FALSE; } UpdateControls(); } void CPCMFCDlg::OnBnClickedBtnClose() { m_bThreadRunning = FALSE; if (m_hThread) { WaitForSingleObject(m_hThread, 1000); CloseHandle(m_hThread); m_hThread = NULL; } try { if (m_SerialPort.IsOpen()) { m_SerialPort.Close(); m_listStatus.AddString(_T("串口已关闭")); } } catch (CSerialException* e) { delete e; } m_bConnected = FALSE; UpdateControls(); } void CPCMFCDlg::OnBnClickedBtnFeedNow() { BYTE cmd = FEED_COMMAND; try { if (m_SerialPort.IsOpen()) { m_SerialPort.Write(&cmd, 1); m_listStatus.AddString(_T("喂食命令已发送")); } } catch (CSerialException* e) { CString strError; strError.Format(_T("发送失败: 错误代码 %d"), e->m_dwError); m_listStatus.AddString(strError); delete e; } } void CPCMFCDlg::OnBnClickedBtnFeedTimer() { if (m_bTimerActive) { KillTimer(1); m_bTimerActive = FALSE; GetDlgItem(IDC_BUTTON_TIMEFEED)->SetWindowText(_T("开始定时")); m_listStatus.AddString(_T("定时器已停止")); } else { UpdateData(TRUE); // 验证时间格式 if (!ValidateTimeFormat(m_strTime1) || !ValidateTimeFormat(m_strTime2) || !ValidateTimeFormat(m_strTime3)) { MessageBox(_T("时间格式无效! 请使用HH:MM格式"), _T("错误"), MB_ICONERROR); return; } m_bTimerActive = TRUE; SetTimer(1, 1000, NULL); // 每秒检查一次 GetDlgItem(IDC_BUTTON_TIMEFEED)->SetWindowText(_T("停止定时")); m_listStatus.AddString(_T("定时器已启动")); } UpdateControls(); } void CPCMFCDlg::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == 1) { CTime now = CTime::GetCurrentTime(); CString strNow = now.Format(_T("%H:%M")); if (strNow == m_strTime1 || strNow == m_strTime2 || strNow == m_strTime3) { OnBnClickedBtnFeedNow(); } } CDialogEx::OnTimer(nIDEvent); } LRESULT CPCMFCDlg::OnUpdateStatus(WPARAM wParam, LPARAM lParam) { CString* pStr = (CString*)wParam; if (pStr) { m_listStatus.AddString(*pStr); delete pStr; } return 0; } void CPCMFCDlg::OnDestroy() { CDialogEx::OnDestroy(); if (m_bTimerActive) { KillTimer(1); } m_bThreadRunning = FALSE; if (m_hThread) { WaitForSingleObject(m_hThread, 1000); CloseHandle(m_hThread); m_hThread = NULL; } if (m_SerialPort.IsOpen()) { m_SerialPort.Close(); } } void CPCMFCDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } HCURSOR CPCMFCDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } "public: void __cdecl CSerialPort::Set0Timeout(void)" (?Set0Timeout@CSerialPort@@QEAAXXZ) 已经在 PCMFCDlg.obj 中定义 "public: unsigned long __cdecl CSerialPort::BytesWaiting(void)" (?BytesWaiting@CSerialPort@@QEAAKXZ) 已经在 PCMFCDlg.obj 中定义 "public: unsigned long __cdecl CSerialPort::Write(void const *,unsigned long)" (?Write@CSerialPort@@QEAAKPEBXK@Z) 已经在 PCMFCDlg.obj 中定义 "public: void __cdecl CSerialPort::Close(void)" (?Close@CSerialPort@@QEAAXXZ) 已经在 PCMFCDlg.obj 中定义 "public: virtual __cdecl CSerialPort::~CSerialPort(void)" (??1CSerialPort@@UEAA@XZ) 已经在 PCMFCDlg.obj 中定义 "public: __cdecl CSerialPort::CSerialPort(void)" (??0CSerialPort@@QEAA@XZ) 已经在 PCMFCDlg.obj 中定义 "public: __cdecl CSerialException::CSerialException(unsigned long)" (??0CSerialException@@QEAA@K@Z) 已经在 PCMFCDlg.obj 中定义 找到一个或多个多重定义的符号 请你解决这些问题
06-04
""" title: SQL Server Access author: MENG author_urls: - https://github.com/mengvision description: A tool for reading database information and executing SQL queries, supporting multiple databases such as MySQL, PostgreSQL, SQLite, and Oracle. It provides functionalities for listing all tables, describing table schemas, and returning query results in CSV format. A versatile DB Agent for seamless database interactions. required_open_webui_version: 0.5.4 requirements: pymysql, sqlalchemy, cx_Oracle version: 0.1.6 licence: MIT # Changelog ## [0.1.6] - 2025-03-11 ### Added - Added `get_table_indexes` method to retrieve index information for a specific table, supporting MySQL, PostgreSQL, SQLite, and Oracle. - Enhanced metadata capabilities by providing detailed index descriptions (e.g., index name, columns, and type). - Improved documentation to include the new `get_table_indexes` method and its usage examples. - Updated error handling in `get_table_indexes` to provide more detailed feedback for unsupported database types. ## [0.1.5] - 2025-01-20 ### Changed - Updated `list_all_tables` and `table_data_schema` methods to accept `db_name` as a function parameter instead of using `self.valves.db_name`. - Improved flexibility by decoupling database name from class variables, allowing dynamic database selection at runtime. ## [0.1.4] - 2025-01-17 ### Added - Added support for Oracle database using `cx_Oracle` driver. - Added dynamic engine creation in each method to ensure fresh database connections for every operation. - Added support for Oracle-specific queries in `list_all_tables` and `table_data_schema` methods. ### Changed - Moved `self._get_engine()` from `__init__` to individual methods for better flexibility and tool compatibility. - Updated `_get_engine` method to support Oracle database connection URL. - Improved `table_data_schema` method to handle Oracle-specific column metadata. ### Fixed - Fixed potential connection issues by ensuring each method creates its own database engine. - Improved error handling for Oracle-specific queries and edge cases. ## [0.1.3] - 2025-01-17 ### Added - Added support for multiple database types (e.g., MySQL, PostgreSQL, SQLite) using SQLAlchemy. - Added configuration flexibility through environment variables or external configuration files. - Enhanced query security with stricter validation and SQL injection prevention. - Improved error handling with detailed exception messages for better debugging. ### Changed - Replaced `pymysql` with SQLAlchemy for broader database compatibility. - Abstracted database connection logic into a reusable `_get_engine` method. - Updated `table_data_schema` method to support multiple database types. ### Fixed - Fixed potential SQL injection vulnerabilities in query execution. - Improved handling of edge cases in query validation and execution. ## [0.1.2] - 2025-01-16 ### Added - Added support for specifying the database port with a default value of `3306`. - Abstracted database connection logic into a reusable `_get_connection` method. ## [0.1.1] - 2025-01-16 ### Added - Support for additional read-only query types: `SHOW`, `DESCRIBE`, `EXPLAIN`, and `USE`. - Enhanced query validation to block sensitive keywords (e.g., `INSERT`, `UPDATE`, `DELETE`, `CREATE`, `DROP`, `ALTER`). ### Fixed - Improved handling of queries starting with `WITH` (CTE queries). - Fixed case sensitivity issues in query validation. ## [0.1.0] - 2025-01-09 ### Initial Release - Basic functionality for listing tables, describing table schemas, and executing `SELECT` queries. - Query results returned in CSV format. """ import os from typing import List, Dict, Any from pydantic import BaseModel, Field import re from sqlalchemy import create_engine, text from sqlalchemy.engine.base import Engine from sqlalchemy.exc import SQLAlchemyError class Tools: class Valves(BaseModel): db_host: str = Field( default="localhost", description="The host of the database. Replace with your own host.", ) db_user: str = Field( default="admin", description="The username for the database. Replace with your own username.", ) db_password: str = Field( default="admin", description="The password for the database. Replace with your own password.", ) db_name: str = Field( default="db", description="The name of the database. Replace with your own database name.", ) db_port: int = Field( default=3306, # Oracle 默认端口 description="The port of the database. Replace with your own port.", ) db_type: str = Field( default="mysql", description="The type of the database (e.g., mysql, postgresql, sqlite, oracle).", ) def __init__(self): """ Initialize the Tools class with the credentials for the database. """ print("Initializing database tool class") self.citation = True self.valves = Tools.Valves() def _get_engine(self) -> Engine: """ Create and return a database engine using the current configuration. """ if self.valves.db_type == "mysql": db_url = f"mysql+pymysql://{self.valves.db_user}:{self.valves.db_password}@{self.valves.db_host}:{self.valves.db_port}/{self.valves.db_name}" elif self.valves.db_type == "postgresql": db_url = f"postgresql://{self.valves.db_user}:{self.valves.db_password}@{self.valves.db_host}:{self.valves.db_port}/{self.valves.db_name}" elif self.valves.db_type == "sqlite": db_url = f"sqlite:///{self.valves.db_name}" elif self.valves.db_type == "oracle": db_url = f"oracle+cx_oracle://{self.valves.db_user}:{self.valves.db_password}@{self.valves.db_host}:{self.valves.db_port}/?service_name={self.valves.db_name}" else: raise ValueError(f"Unsupported database type: {self.valves.db_type}") return create_engine(db_url) def list_all_tables(self, db_name: str) -> str: """ List all tables in the database. :param db_name: The name of the database. :return: A string containing the names of all tables. """ print("Listing all tables in the database") engine = self._get_engine() # 动态创建引擎 try: with engine.connect() as conn: if self.valves.db_type == "mysql": result = conn.execute(text("SHOW TABLES;")) elif self.valves.db_type == "postgresql": result = conn.execute( text( "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';" ) ) elif self.valves.db_type == "sqlite": result = conn.execute( text("SELECT name FROM sqlite_master WHERE type='table';") ) elif self.valves.db_type == "oracle": result = conn.execute(text("SELECT table_name FROM user_tables;")) else: return "Unsupported database type." tables = [row[0] for row in result.fetchall()] if tables: return ( "Here is a list of all the tables in the database:\n\n" + "\n".join(tables) ) else: return "No tables found." except SQLAlchemyError as e: return f"Error listing tables: {str(e)}" def get_table_indexes(self, db_name: str, table_name: str) -> str: """ Get the indexes of a specific table in the database. :param db_name: The name of the database. :param table_name: The name of the table. :return: A string describing the indexes of the table. """ print(f"Getting indexes for table: {table_name}") engine = self._get_engine() try: key, cloumn = 0, 1 with engine.connect() as conn: if self.valves.db_type == "mysql": query = text(f"SHOW INDEX FROM {table_name}") key, cloumn = 2, 4 elif self.valves.db_type == "postgresql": query = text( """ SELECT indexname, indexdef FROM pg_indexes WHERE tablename = :table_name; """ ) elif self.valves.db_type == "sqlite": query = text( """ PRAGMA index_list(:table_name); """ ) elif self.valves.db_type == "oracle": query = text( """ SELECT index_name, column_name FROM user_ind_columns WHERE table_name = :table_name; """ ) else: return "Unsupported database type." result = conn.execute(query) indexes = result.fetchall() if not indexes: return f"No indexes found for table: {table_name}" description = f"Indexes for table '{table_name}':\n" for index in indexes: description += f"- {index[key]}: {index[cloumn]}\n" return description # result = conn.execute(query) # description = result.fetchall() # if not description: # return f"No indexes found for table: {table_name}" # column_names = result.keys() # description = f"Query executed successfully. Below is the actual result of the query {query} running against the database in CSV format:\n\n" # description += ",".join(column_names) + "\n" # for row in description: # description += ",".join(map(str, row)) + "\n" # return description except SQLAlchemyError as e: return f"Error getting indexes: {str(e)}" def table_data_schema(self, db_name: str, table_name: str) -> str: """ Describe the schema of a specific table in the database, including column comments. :param db_name: The name of the database. :param table_name: The name of the table to describe. :return: A string describing the data schema of the table. """ print(f"Database: {self.valves.db_name}") print(f"Describing table: {table_name}") engine = self._get_engine() # 动态创建引擎 try: with engine.connect() as conn: if self.valves.db_type == "mysql": query = text( " SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_KEY, COLUMN_COMMENT " " FROM INFORMATION_SCHEMA.COLUMNS " f" WHERE TABLE_SCHEMA = '{self.valves.db_name}' AND TABLE_NAME = '{table_name}';" ) elif self.valves.db_type == "postgresql": query = text( """ SELECT column_name, data_type, is_nullable, column_default, '' FROM information_schema.columns WHERE table_name = :table_name; """ ) elif self.valves.db_type == "sqlite": query = text("PRAGMA table_info(:table_name);") elif self.valves.db_type == "oracle": query = text( """ SELECT column_name, data_type, nullable, data_default, comments FROM user_tab_columns LEFT JOIN user_col_comments ON user_tab_columns.table_name = user_col_comments.table_name AND user_tab_columns.column_name = user_col_comments.column_name WHERE user_tab_columns.table_name = :table_name; """ ) else: return "Unsupported database type." # result = conn.execute( # query, {"db_name": db_name, "table_name": table_name} # ) result = conn.execute(query) columns = result.fetchall() if not columns: return f"No such table: {table_name}" description = ( f"Table '{table_name}' in the database has the following columns:\n" ) for column in columns: if self.valves.db_type == "sqlite": column_name, data_type, is_nullable, _, _, _ = column column_comment = "" elif self.valves.db_type == "oracle": ( column_name, data_type, is_nullable, data_default, column_comment, ) = column else: ( column_name, data_type, is_nullable, column_key, column_comment, ) = column description += f"- {column_name} ({data_type})" if is_nullable == "YES" or is_nullable == "Y": description += " [Nullable]" if column_key == "PRI": description += " [Primary Key]" if column_comment: description += f" [Comment: {column_comment}]" description += "\n" return description except SQLAlchemyError as e: return f"Error describing table: {str(e)}" def execute_read_query(self, query: str) -> str: """ Execute a read query and return the result in CSV format. :param query: The SQL query to execute. :return: A string containing the result of the query in CSV format. """ print(f"Executing query: {query}") normalized_query = query.strip().lower() if not re.match( r"^\s*(select|with|show|describe|desc|explain|use)\s", normalized_query ): return "Error: Only read-only queries (SELECT, WITH, SHOW, DESCRIBE, EXPLAIN, USE) are allowed. CREATE, DELETE, INSERT, UPDATE, DROP, and ALTER operations are not permitted." sensitive_keywords = [ "insert", "update", "delete", "create", "drop", "alter", "truncate", "grant", "revoke", "replace", ] for keyword in sensitive_keywords: if re.search(rf"\b{keyword}\b", normalized_query): return f"Error: Query contains a sensitive keyword '{keyword}'. Only read operations are allowed." engine = self._get_engine() # 动态创建引擎 try: with engine.connect() as conn: result = conn.execute(text(query)) rows = result.fetchall() if not rows: return "No data returned from query." column_names = result.keys() csv_data = f"Query executed successfully. Below is the actual result of the query {query} running against the database in CSV format:\n\n" csv_data += ",".join(column_names) + "\n" for row in rows: csv_data += ",".join(map(str, row)) + "\n" return csv_data except SQLAlchemyError as e: return f"Error executing query: {str(e)}" 将上面的工具连接到Microsoft sql server
最新发布
07-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值