【问题标题】:how to make controls accessible from other threads in c++ builder xe2?c++ - 如何使控件可以从c ++ builder xe2中的其他线程访问?
【发布时间】:2012-06-18 14:53:16
【问题描述】:

我下载了 c++ builder xe2 的试用版,并试图了解如何访问和更改控件属性(示例:从不同的线程更改 TLabel 的文本)。我知道您可以使用以下方法在同一个线程上更改它:

Label1->Caption = " Text ";

但我需要做的是从另一个函数中更改它。到目前为止,在我拥有的表单的头文件中:

//---------------------------------------------------------------------------

#ifndef Hello_VCLH
#define Hello_VCLH

#define IN
#define INOUT
#define OUT

//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ComCtrls.hpp>
//---------------------------------------------------------------------------
class TForm2 : public TForm
{
__published:    // IDE-managed Components
    TLabel *Label1;
    TButton *Button1;
    TProgressBar *ProgressBar1;
private:    // User declarations
public:     // User declarations
    __fastcall TForm2(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm2 *Form2;
//---------------------------------------------------------------------------
#endif

在表单的 .cpp 文件中,我尝试将 TForm2::Label1->Caption = "test";但这没有用。 我尝试将静态放在控件前面,但是当我这样做时,xe2 声称表单代码是错误的。任何人都知道如何使它可以从除主函数或线程之外的另一个函数或线程访问控件的位置吗?谢谢!

编辑**:

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Hello_VCL.h"
#include <tchar.h>
#include <windows.h>
#include "wimgapi.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm2 *Form2;

//---------------------------------------------------------------------------
__fastcall TForm2::TForm2(TComponent* Owner)
    : TForm(Owner)
{
}


DWORD
WINAPI
SampleCaptureCallback(
    IN      DWORD msgId,    //message ID
    IN      WPARAM param1,   //usually file name
    INOUT   LPARAM param2,   //usually error code
    IN      void  *unused
    )
{
    //First parameter: full file path for if WIM_MSG_PROCESS, message string for others
    TCHAR *message  = (TCHAR *) param1;
    TCHAR *filePath = (TCHAR *) param1;
    DWORD percent   = (DWORD)   param1;

    //Second parameter: message back to caller if WIM_MSG_PROCESS, error code for others
    DWORD errorCode = param2;
    DWORD *msg_back = (DWORD *) param2;
    DWORD seconds = (DWORD) param2;


    switch ( msgId )
    {
        case WIM_MSG_PROGRESS:

            // Prints out the current progress percentage.
            //

            //lbl->Caption="Test";

            Label1->Caption = (String)param1 + " % completed";
            //Label1->Caption = (DWORD)param1;
            //wprintf(L"__________________\n\n| Capture process|\t\t(c) 2012 Andrew Butler\n__________________\n\n%d %% captured. About %i seconds(s) remaining - %i minute(s)", (DWORD)param1, ((INT)seconds / 1000), ((INT)seconds / 60000));

            break;
        case WIM_MSG_PROCESS:

            //This message is sent for each file, capturing to see if callee intends to
            //capture the file or not.
            //
            //If you do not intend to capture this file, then assign FALSE in msg_back
            //and still return WIM_MSG_SUCCESS.
            //Default is TRUE.
            //

            //In this example, print out the file name being applied
            //
            //_tprintf(TEXT("FilePath: %s\n"), filePath);

            break;

        case WIM_MSG_ERROR:

            //This message is sent upon failure error case
            //
            //printf("ERROR: %s [err = %d]\n", message, errorCode);
            break;

        case WIM_MSG_RETRY:

            //This message is sent when the file is being reapplied because of
            //network timeout. Retry is done up to five times.
            //
            //printf("RETRY: %s [err = %d]\n", message, errorCode);
            break;

        case WIM_MSG_INFO:

            //This message is sent when informational message is available
            //
            //printf("INFO: %s [err = %d]\n", message, errorCode);
            break;

        case WIM_MSG_WARNING:

            //This message is sent when warning message is available
            //
            //printf("WARNING: %s [err = %d]\n", message, errorCode);
            break;
    }

    return WIM_MSG_SUCCESS;
}

void
SampleCaptureCleanup ( HANDLE hWim, HANDLE hImg, FARPROC callback )
{
    if (hImg) {
        WIMCloseHandle (hImg);
    }

    if (hWim) {
        WIMCloseHandle (hWim);
    }

    if (callback) {
        WIMUnregisterMessageCallback( NULL, callback );
    }
}

//---------------------------------------------------------------------------


void __fastcall TForm2::Button1Click(TObject *Sender)
{
    //Label1->Caption = "Test";
}

编辑 2 * :

FARPROC callback = (FARPROC) SampleCaptureCallback;

if (WIMRegisterMessageCallback( NULL,
                                callback,
                                NULL ) == INVALID_CALLBACK_VALUE) {
    printf ("Cannot set callback\n");
    return 3;
}

我已对其进行了编辑以包含 cpp 文件。我想在 SampleCallback 函数中更改 case WIM_MSG_PROGRESS: 下的标签。

【问题讨论】:

  • 您可以将要访问的控件的指针传递给其他线程(例如在创建时),然后您可以通过该指针访问控件。 注意:确保使用某种互斥体/信号量来保护对该控件的访问,以避免访问中的竞争条件。一种方法是将对desiredc控件的访问包装在主窗体的成员函数中,并将(指向主窗体的指针)传递给线程,以便它可以调用该函数
  • 我是 C++ 新手,您介意发布一个示例吗?如果我猜我会在头文件中声明另一个 TLabel 还是什么?
  • 你如何开始你的另一个话题?
  • 我用 cpp 文件更新了我的问题。谢谢!
  • 您能否也发布一下您是如何注册SampleCaptureCallback 函数的?

标签: c++ c++builder c++builder-xe2


【解决方案1】:

WIMRegisterMessageCallback 调用的最后一个参数指定了一个自定义用户数据,您可以使用该数据将信息传递给您的回调函数(在其最后一个参数中,当前名为 unused)。

您可以通过修改注册调用将指向 TForm2 对象的指针传递给回调

WIMRegisterMessageCallback( NULL, callback, form)

form 是上面提到的指针。

然后您可以在回调中使用该用户数据,如下所示:

DWORD  WINAPI  SampleCaptureCallback(
  IN      DWORD msgId,
  IN      WPARAM param1,
  INOUT   LPARAM param2,
  IN      PVOID  udata)
{
  TForm2* form = reinterpret_cast<TForm2*>(udata);
  udata->SetLabel1Caption("my text");
  //...
}

其中SetLabel1CaptionTForm2的以下函数:

void SetLabel1Cation(String str)
{
  WaitForSingleObject(hLabel1Mutex, INFINITE);
  Label1->Caption = str;
  ReleaseMutex(hLabel1Mutex);
}

其中hLabel1 mutex 是Tform2 的成员变量

HANDLE hLabel1Mutex;

并在TForm2的构造函数中初始化为:

hLabel1Mutex = CreateMutex (NULL, FALSE, NULL);
if (hLabel1Mutex == NULL)
{
  // failed to create mutex, throw exception
}

注意此示例仅适用于使用 Label1。如果您想同时更新多个控件,可以使用相同的互斥锁,否则您应该使用自己的互斥锁来保护每个控件。

注意:阅读更多关于 Win32API inthesearticles 中的互斥锁的信息。

更新:Remy 指出传统的保护机制(互斥量/信号量)不适用于全线程安全。相反,各个线程需要协同工作并与主线程通信以委托对 UI 控件的访问。

【讨论】:

  • 在这种情况下,互斥锁不能提供足够的保护,因为主线程不参与其中。 VCL UI 控件根本不是线程安全的,不能从主线程的上下文之外访问。时期。工作线程必须委托给主线程才能安全地访问 UI。使用静态TThread::Synchronize() 方法,或使用AllocateHWnd() 创建一个帮助窗口,您可以使用SendMessage() 向该窗口发送自定义消息。
  • @RemyLebeau - 谢谢纠正,我不知道这个限制
猜你喜欢
  • 2017-04-27
  • 2017-06-09
  • 1970-01-01
  • 1970-01-01
  • 2015-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-11
相关资源
最近更新 更多