как сделать элементы управления доступными из других потоков в С++ Builder xe2?

Я загрузил пробную версию 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"; но это не сработало. Я пытался поставить static перед элементами управления, но когда я это делаю, 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. Я хочу изменить метку в случае WIM_MSG_PROGRESS: в функции SampleCallback.


person Andrew Butler    schedule 18.06.2012    source источник
comment
Вы можете передать указатель элемента управления, к которому вы хотите получить доступ, в другой поток (например, при его создании), затем вы можете получить доступ к элементу управления через этот указатель. Примечание: обязательно защитите доступ к этому элементу управления с помощью какого-либо мьютекса/семафора, чтобы избежать условий гонки при доступе. Одним из способов сделать это было бы обернуть доступ к элементу управления желанием в функции-члене основной формы и передать (указатель на) основную форму потоку, чтобы он мог вызвать эту функцию.   -  person Attila    schedule 18.06.2012
comment
Я новичок в С++, не могли бы вы опубликовать пример, пожалуйста? Если бы я угадал, должен ли я объявить еще один TLabel в заголовочном файле или что именно?   -  person Andrew Butler    schedule 18.06.2012
comment
Как вы начинаете свою другую тему?   -  person Attila    schedule 18.06.2012
comment
я обновил свой вопрос с файлом cpp. Благодарность!   -  person Andrew Butler    schedule 18.06.2012
comment
Можете ли вы также опубликовать, как вы регистрируете свою функцию SampleCaptureCallback?   -  person Attila    schedule 18.06.2012


Ответы (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");
  //...
}

где SetLabel1Caption является следующей функцией TForm2:

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

где hLabel1 мьютекс является переменной-членом Tform2

HANDLE hLabel1Mutex;

и инициализируется в конструкторе TForm2 как:

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

Обратите внимание, этот пример адаптирован для использования только Label1. Если вы хотите обновить более одного элемента управления одновременно, вы можете использовать один и тот же мьютекс, в противном случае вы должны защитить каждый элемент управления своим собственным мьютексом.

Примечание: узнайте больше о мьютексах в Win32API в этих статьи для начала.

Обновление: Реми отмечает, что традиционные механизмы защиты (мьютекс/семафор) здесь неприменимы для обеспечения полной безопасности потоков. Вместо этого различные потоки должны работать вместе и взаимодействовать с основным потоком, чтобы делегировать доступ к элементам управления пользовательского интерфейса.

person Attila    schedule 18.06.2012
comment
В этом случае мьютекс НЕ является адекватной защитой, потому что основной поток не участвует в нем. Элементы управления пользовательского интерфейса VCL просто не являются потокобезопасными, и к ним нельзя получить доступ вне контекста основного потока. Период. Рабочий поток должен делегировать полномочия основному потоку для безопасного доступа к пользовательскому интерфейсу. Либо используйте статический метод TThread::Synchronize(), либо используйте AllocateHWnd() для создания вспомогательного окна, в которое вы можете отправлять пользовательские сообщения с помощью SendMessage(). - person Remy Lebeau; 19.06.2012
comment
@RemyLebeau - спасибо за исправление, я не знал об этом ограничении - person Attila; 19.06.2012