如何在 win 32 中使用线程概念实现进度条(显示进度)?

发布于 2024-08-27 13:50:47 字数 2850 浏览 9 评论 0原文

我试图在我的进程正在进行时显示进度条...在我的应用程序中,会出现一种情况,我必须读取文件并操作它们(需要一些时间才能完成)..想要显示进度条在此操作期间..我调用的特定函数是 win 32 ...因此,如果您检查下面的代码,我已经可以在对话框窗口中创建进度条并创建线程了。现在我不知道如何发布消息以及在哪里获取消息和处理...请帮助我..提前致谢

    //my  function
    int Myfunction(....)
    {
     MSG msg;
     HWND dialog = CreateWindowEx(0,WC_DIALOG,L"Proccessing...",WS_OVERLAPPEDWINDOW|WS_VISIBLE,
         600,300,280,120,NULL,NULL,NULL,NULL);
     HWND pBar =  CreateWindowEx(NULL,PROGRESS_CLASS,NULL,WS_CHILD|WS_VISIBLE,40,20,200, 20,
           dialog,(HMENU)IDD_PROGRESS,NULL,NULL);


      while(GetMessage(&msg,NULL,0,0))
{
    TranslateMessage(&msg);
     Dispatch(&message);
}
SendMessage(pBar,PBM_SETRANGE,0,MAKELPARAM(0,noOfFile));

     HANDLE getHandle = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)SetFilesForOperation(...),
        NULL,NULL,0);

    }


    LPARAM SetFilesForOperation(...)       
    {

     for(int index = 0;index < noOfFiles; index++)
     {


      *checkstate = *(checkState + index);
      if(*checkstate == -1)
      {
       *(getFiles+i) = new TCHAR[MAX_PATH];
       wcscpy(*(getFiles+i),*(dataFiles +index));
       i++;

      }
      else
      {
       (*tempDataFiles)->Add(*(dataFiles+index));
       *(checkState + localIndex) = *(checkState + index);
       localIndex++;
      }

      PostMessage(pBar,PBM_SETPOS,(WPARAM)index,0);
     }
    }

编辑2: 使用 AFXTHREAD

//instead of createthread i used AfxBegin thread
    ptrThread = AfxBeginThread((AFX_THREADPROC)SetFilesForOperation(pBar,checkstate,checkState,noOfFiles,i,getFilesforcompression,dataFiles,&tempDataFiles,localIndex),
        NULL,THREAD_PRIORITY_ABOVE_NORMAL,NULL,NULL,NULL);


for(int index = 0;index < noOfFiles; index++)
    {

        MSG msg;
        *checkstate = *(checkState + index);
        if(*checkstate == -1)
        {
            *(getFilesforcompression+i) = new TCHAR[MAX_PATH];
            //*(getFilesforcompression+i) = L"C:\\Documents and Settings\\rakesh\\Desktop\\try2_Extracted";
            wcscpy(*(getFilesforcompression+i),*(dataFiles +index));
            i++;

        }
        else
        {
            (*tempDataFiles)->Add(*(dataFiles+index));
            *(checkState + localIndex) = *(checkState + index);
            localIndex++;
        }


        //PostMessage( pBar, PBM_SETPOS, (WPARAM)index, 0 );
        PostMessage( pBar, PBM_SETRANGE, 0, MAKELPARAM( 0, noOfFiles ) );
        //PostMessage( pBar, PBM_SETPOS, (WPARAM)index, 0 );
        PostMessage( pBar, PBM_STEPIT, (WPARAM)index, 0 );
        PostMessage( pBar, MSG_PROGRESS_VALUE, 0, 0 );


        while(1)
        {
            while(PeekMessage( &msg, NULL, NULL, NULL, PM_NOREMOVE))
            {


                AfxGetThread()->PumpMessage();
                Sleep(10);




        }

I am trying to show a progress bar while my process is going on...in my application there will be a situation where I gotta read files and manipulate them(it will take some time to complete)..want to display a progress bar during this operation..the particular function I am calling is an win 32 ...so if you check my code below i am upto the point of creating the progress bar in a dialog window and creating a thread Now I dont know how to post the message and where to get the message and handle...Please help me..thanks in advance

    //my  function
    int Myfunction(....)
    {
     MSG msg;
     HWND dialog = CreateWindowEx(0,WC_DIALOG,L"Proccessing...",WS_OVERLAPPEDWINDOW|WS_VISIBLE,
         600,300,280,120,NULL,NULL,NULL,NULL);
     HWND pBar =  CreateWindowEx(NULL,PROGRESS_CLASS,NULL,WS_CHILD|WS_VISIBLE,40,20,200, 20,
           dialog,(HMENU)IDD_PROGRESS,NULL,NULL);


      while(GetMessage(&msg,NULL,0,0))
{
    TranslateMessage(&msg);
     Dispatch(&message);
}
SendMessage(pBar,PBM_SETRANGE,0,MAKELPARAM(0,noOfFile));

     HANDLE getHandle = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)SetFilesForOperation(...),
        NULL,NULL,0);

    }


    LPARAM SetFilesForOperation(...)       
    {

     for(int index = 0;index < noOfFiles; index++)
     {


      *checkstate = *(checkState + index);
      if(*checkstate == -1)
      {
       *(getFiles+i) = new TCHAR[MAX_PATH];
       wcscpy(*(getFiles+i),*(dataFiles +index));
       i++;

      }
      else
      {
       (*tempDataFiles)->Add(*(dataFiles+index));
       *(checkState + localIndex) = *(checkState + index);
       localIndex++;
      }

      PostMessage(pBar,PBM_SETPOS,(WPARAM)index,0);
     }
    }

EDIT2:
using AFXTHREAD

//instead of createthread i used AfxBegin thread
    ptrThread = AfxBeginThread((AFX_THREADPROC)SetFilesForOperation(pBar,checkstate,checkState,noOfFiles,i,getFilesforcompression,dataFiles,&tempDataFiles,localIndex),
        NULL,THREAD_PRIORITY_ABOVE_NORMAL,NULL,NULL,NULL);


for(int index = 0;index < noOfFiles; index++)
    {

        MSG msg;
        *checkstate = *(checkState + index);
        if(*checkstate == -1)
        {
            *(getFilesforcompression+i) = new TCHAR[MAX_PATH];
            //*(getFilesforcompression+i) = L"C:\\Documents and Settings\\rakesh\\Desktop\\try2_Extracted";
            wcscpy(*(getFilesforcompression+i),*(dataFiles +index));
            i++;

        }
        else
        {
            (*tempDataFiles)->Add(*(dataFiles+index));
            *(checkState + localIndex) = *(checkState + index);
            localIndex++;
        }


        //PostMessage( pBar, PBM_SETPOS, (WPARAM)index, 0 );
        PostMessage( pBar, PBM_SETRANGE, 0, MAKELPARAM( 0, noOfFiles ) );
        //PostMessage( pBar, PBM_SETPOS, (WPARAM)index, 0 );
        PostMessage( pBar, PBM_STEPIT, (WPARAM)index, 0 );
        PostMessage( pBar, MSG_PROGRESS_VALUE, 0, 0 );


        while(1)
        {
            while(PeekMessage( &msg, NULL, NULL, NULL, PM_NOREMOVE))
            {


                AfxGetThread()->PumpMessage();
                Sleep(10);




        }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

你列表最软的妹 2024-09-03 13:50:47

跨线程发送消息时存在一些问题。如果您从线程 A 向线程 B 发送消息,那么内部发生的事情是线程 A 将消息发布到线程 B 的消息队列。然后,它会等待消息被处理,然后再将结果发送回线程 A。这意味着您需要在线程 B 中泵送消息,否则线程 A 将死锁。

至于您的具体问题,您可以在创建进度条后简单地添加以下代码:

SendMessage( pBar, PBM_SETRANGE, 0, MAKELPARAM( 0, noOfFiles ) );

然后您需要将 pBar 传递到您的线程中。这很容易。您会注意到,CreateThread 允许您将参数作为 void* 传递给线程函数。因此,将您的 CreateThread 重写为:

HANDLE getHandle = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)SetFilesForOperation(...),
                                (void*)pBar,NULL,0);

然后将您的 SetFilesForOperation 原型更改为:

LPARAM SetFilesForOperation( HWND pBar );

请注意,您将线程函数转换为正确的格式。因此,Windows 在内部只会传递 void*。然后发生隐式转换,您在另一端看到的是 HWND 而不是 void*。您可以通过将指向结构的指针传递给线程函数来传递更多数据。请记住,在 SetFilesForOperation 从中获取所需信息之前,不要释放该结构(无论是让它超出范围还是显式释放)。您可以通过一个简单的事件来解决这个问题,一旦线程函数获得了它所在的数据,该事件就会被触发,然后在创建事件的线程中等待被触发。

然后只需在循环末尾添加以下消息:

PostMessage( pBar, PBM_SETPOS, (WPARAM)index, 0 );

这样,每次循环都会将条形前进一。

编辑:正如评论中所指出的,值得使用 PostMessage,因为它不会等待返回,这意味着消息只是发布到 UI 线程的消息队列。但请注意,如果您不泵送该消息队列,您将发送消息,并且您不会看到进度条更新,因为消息只会备份在队列中。

编辑2:您正在消息循环之后设置进度条的范围。所以范围永远不会被设定。您需要事先这样做。还值得注意的是,只有当您发送 WM_QUIT 消息时,您的消息泵才会退出。这并不理想。您可以在线程循环的末尾发布您自己的消息。您需要的更改如下。首先,您需要声明自定义(用户)消息。

#define WM_EXITTHREAD WM_USER + 1

然后将消息循环更改为以下内容:

SendMessage(pBar,PBM_SETRANGE,0,MAKELPARAM(0,noOfFile));

MSG msg;
while(GetMessage(&msg,NULL,0,0))
{ 
    if ( msg.message == WM_EXITTHREAD )
    {
         break;
    }
    TranslateMessage(&msg); 
    Dispatch(&msg); 
}

最后在线程末尾添加以下内容:

PostMessage( pBar, WM_EXITTHREAD, 0, 0 );
EndThread( 0 );  // This is the preferred way of exiting a thread in C
return 0; // This is the preferred way of exiting a thread in C++ so that destructors get called.

Edit3:如果您使用这样的 peek 消息循环会发生什么?

while( 1 )
{
    MSG msg;
    if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
    {
        if ( msg.message == WM_EXITTHREAD )
        {
            break;
        }
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    else
    {
        Sleep( 0 );
    }
}

There are a few gotchas when sending messages across threads. If you SendMessage from Thread A to Thread B then what happens internally is that Thread A posts the message to Thread B's message queue. It then sits and waits for the message to be processed before sending the result back to Thread A. This means that you need to be pumping messages in Thread B or Thread A will deadlock.

As to your specific problem you can simply put the following code after you create the progress bar:

SendMessage( pBar, PBM_SETRANGE, 0, MAKELPARAM( 0, noOfFiles ) );

Then you need to pass pBar into your thread. This is pretty easy. You'll note that CreateThread allows you to pass a parameter to the thread function as a void*. So re-write your CreateThread as:

HANDLE getHandle = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)SetFilesForOperation(...),
                                (void*)pBar,NULL,0);

Then change your SetFilesForOperation prototype to this:

LPARAM SetFilesForOperation( HWND pBar );

Note thatyou cast the thread function to the right format. So Windows, internally, will just pass the void*. An implicit cast then happens and what you see at the other end is an HWND rather an a void*. You can pass much more data through by passing a pointer to a struct to the thread function. Just remember not to free the struct (either by letting it drop out of scope or explicitly) before SetFilesForOperation has the info it needs out of it. You can solve this with a simple event that gets fired once the thread function has the data it is after and then waiting in the thread that does the creation for the event to be fired.

Then simply add the following message at the end of the loop:

PostMessage( pBar, PBM_SETPOS, (WPARAM)index, 0 );

This will then advance the bar by one each loop through.

Edit: As pointed out in the comments its worth just using PostMessage as it won't wait for the return meaning that the message is simply posted to the UI thread's message queue. Do note, though, that if you don't pump that message queue you'll be sending messages across and you won't see the progress bar updating as the messages will just back up in the queue.

Edit 2: You are setting the range of your progress bar AFTER the message loop. So the range never gets set. You need to do that before hand. Its also worth noting that your message pump will only exit when you send a WM_QUIT message. This is not ideal. You could though post a message of your own at the end of your thread loop. The changes you would need are as follows. Firstly you need to declare the custom (User) message.

#define WM_EXITTHREAD WM_USER + 1

Then change your message loop to the following:

SendMessage(pBar,PBM_SETRANGE,0,MAKELPARAM(0,noOfFile));

MSG msg;
while(GetMessage(&msg,NULL,0,0))
{ 
    if ( msg.message == WM_EXITTHREAD )
    {
         break;
    }
    TranslateMessage(&msg); 
    Dispatch(&msg); 
}

And finally have the following at the end of your thread:

PostMessage( pBar, WM_EXITTHREAD, 0, 0 );
EndThread( 0 );  // This is the preferred way of exiting a thread in C
return 0; // This is the preferred way of exiting a thread in C++ so that destructors get called.

Edit3: What happens if you use a peek message loop like this?

while( 1 )
{
    MSG msg;
    if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
    {
        if ( msg.message == WM_EXITTHREAD )
        {
            break;
        }
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    else
    {
        Sleep( 0 );
    }
}
深海不蓝 2024-09-03 13:50:47

我更改了线程调用中的 Postmessage,它起作用了......

while(PeekMessage( &msg, NULL, NULL, NULL, PM_NOREMOVE))
                {


                    AfxGetThread()->PumpMessage();
                    Sleep(10);
}

I changed the Postmessage in the thread call and it worked...

while(PeekMessage( &msg, NULL, NULL, NULL, PM_NOREMOVE))
                {


                    AfxGetThread()->PumpMessage();
                    Sleep(10);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文