将事件分配给在运行时动态创建的 VCL 控件 (2)

Posted

技术标签:

【中文标题】将事件分配给在运行时动态创建的 VCL 控件 (2)【英文标题】:Assigning events to a VCL control created dynamically at runtime (2) 【发布时间】:2019-01-23 15:36:07 【问题描述】:

这与Assigning events to a VCL control created dynamically at runtime有关。

我使用上面列出的/参考帖子来解决我在使用某些代码时遇到的问题。感谢您提供该示例。我发现它非常有用,并实现了它提供的“替代”方式,因为我无法使第一种方式工作。

我正在使用 Embarcadero 的 C++Builder 10.x。我刚刚更新到 C++Builder 10.3。此新更新现在发出警告:

[bcc32c 警告] LogitToMemo.cpp(196):函数指针和对象指针之间的隐式转换是 Microsoft 扩展

它抛出的线是:

Method.Code = &LogLevelComboBoxOnChange;

我不确定如何“解决”这个问题。

该代码支持对备忘录字段的日志记录功能,其中日志记录备忘录的页面具有TComboBox 以选择日志记录级别/详细程度。

TComboBox 在日志功能之外,因为它位于用户表单上。我希望TComboBox::OnChange 事件调用我的LogLevelComboBoxOnChange 函数,该函数根据所选的TComboBox 项目/条目调整日志记录级别。

支持这方面的代码包括。

函数声明-TComboBox::OnChange事件函数

void __fastcall LogLevelComboBoxOnChange(void *pThis, TObject *Sender);

函数声明 - 记录在提供 TMemo 记录到的字段和 TComboBox 的位置开始

int LogStartWithComboBox(TMemo *LogIt_MemoField, TComboBox *AppLogLevelComboBox, int iThreshold, AnsiString &asFieldTitles);

这是将OnChange 函数分配给用户日志记录表单上的TComboBox 对象的函数。

int LogStartWithComboBox(TMemo *LogIt_MemoField, TComboBox *AppLogLevelComboBox, int iThreshold, AnsiString &asFieldTitles)

  static TMethod Method;

  //
  //  Set-Up CombBox and OnChange Event
  //  - Save ComboBox pointer
  //  - Assign List of Log Levels
  //  - Assign/Set-Up OnChange Function
  //
  LogLevelComboBox = AppLogLevelComboBox;

  AppLogLevelComboBox->Items->Text =
    "Off\n"
    "All Messages\n"
    "Verbose\n"
    "Trace\n"
    "Informational\n"
    "Warning\n"
    "Error\n"
    "Severe\n"
    "Fatal";

  AppLogLevelComboBox->ItemIndex = iThreshold + 1;

  //
  //  Set-Up - On Change Function for "external" Log Level Combo-Box
  //
  Method.Data = NULL; // passed to the pThis parameter, can be whatever you want

  // 
  //  The Following line generates the warning
  //    [bcc32c Warning] LogitToMemo.cpp(196): implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension
  //
  Method.Code = &LogLevelComboBoxOnChange;

  LogLevelComboBox->OnChange = reinterpret_cast<TNotifyEvent&>(Method);

  return 0;

【问题讨论】:

实现了它提供的“替代”方式,因为我无法使第一种方式工作” - 为什么不呢?您对“第一种”方式到底有什么问题?如果使用得当,这两种方式都可以正常工作。 您无需将TMethod 变量声明为static,因为一旦分配了OnChange 事件,就无需保留它。为什么在 Unicode 环境中使用(并忽略)AnsiString?您应该改用String(或直接使用UnicodeString)。我假设您打算改用AppLogLevelComboBox-&gt;Items-&gt;Text = asFieldTitles; 【参考方案1】:

您正在使用 C++Builder 的基于 CLang 的 C++ 编译器。您看到的“隐式转换”警告是过去几年在多个使用 CLang 的工具链中出现的 CLang 问题,而不仅仅是在 C++Builder 中。此问题不影响 C++Builder 的“经典”(非 CLang)编译器。

&amp;LogLevelComboBoxOnChange 创建一个指向函数的指针,除非将 Clang 置于 Microsoft 兼容模式,否则 CLang 不喜欢将其隐式转换为 void*(这是 TMethod::Code 字段声明的内容)。

可能的解决方案是:

显式类型转换指针:

更新: 显然 CLang doesn't like static_casting 是指向函数的指针,指向 void*,或者!)Method.Code = static_cast(&LogLevelComboBoxOnChange) ;

Method.Code = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(&LogLevelComboBoxOnChange));

在 CLang 编译器设置中启用 -Wmicrosoft-cast 标志。

在对Method.Code 的赋值周围使用#pragma 语句禁用代码中的警告:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmicrosoft-cast"

Method.Code = &LogLevelComboBoxOnChange;

#pragma clang diagnostic pop

使用union(不过,我敢肯定有些人会争辩说这可能违反了严格别名规则,但如果可行的话......):

union 
    void (__fastcall *func)(void*, TObject*);
    void *ptr;
 u;
u.func = &LogLevelComboBoxOnChange;
Method.Code = u.ptr;

【讨论】:

雷米,感谢您的快速回复。按照您的建议,我明确更改为类型转换指针 Method.Code = static_cast(&LogLevelComboBoxOnChange);我仍然收到以下警告 [bcc32c 警告] LogitToMemo.cpp(197):指针到函数和指针到对象之间的隐式转换是 Microsoft 扩展我想理解并做“正确的方法”来解决这个问题无视警告。那就是如果可能的话。回应您的其他 cmets。 - 我现在已经删除了“方法”声明中的静态 回应您的其他 cmets。 - 我现在已经删除了“方法”声明中的静态 - asFieldTitles 是一个返回的字符串,用于为记录 TMemo 字段上方提供列标题 - 我仍在使用我的一些旧/旧代码中的 AnsiStrings。 - 我已经转换了我的一些旧/旧代码,因为我需要它在新代码中,我可以。在某些情况下,我似乎没有找到 UnicodeStrings 中的 AnsiStrings 可用的一些转换和字符串处理功能。 @Mike 在进一步审查后,我预计static_cast 上的a different warning:“函数指针和对象指针之间的静态转换是 Microsoft 扩展我>”。显然 CLang 根本不喜欢将指向函数的指针分配给 void*。因此,您只需要禁用警告(在您的情况下这只是一个警告,而不是错误)。或者尝试reinterpret_cast 而不是static_castMethod.Code = reinterpret_cast&lt;void*&gt;(reinterpret_cast&lt;uintptr_t&gt;(&amp;LogLevelComboBoxOnChange)); @Mike 或者,也许可以尝试使用union 来避免类型转换:union void (__fastcall *func)(void*, TObject*); void *ptr; u; u.func = &amp;LogLevelComboBoxOnChange; Method.Code = u.ptr; 虽然有些人可能会争辩说这违反了严格的别名规则,但如果它有效......跨度>

以上是关于将事件分配给在运行时动态创建的 VCL 控件 (2)的主要内容,如果未能解决你的问题,请参考以下文章

动态地将id分配给在kotlin中单击按钮时创建的edittext [重复]

如何将按键分配给在循环中创建的 QPushButton

动态创建到 WPF 表单的用户控件上的按钮单击事件

VBA for Access 2010:动态创建控件和链接事件处理程序

在运行时动态分配 MFC 命令 ID

如何使用事件将 json 输入传递给在无服务器中部署的 Cron 计划 Lambda?