Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[C++] [WinApi] "Zamrażanie" okna aplikacji

Ostatnio zmodyfikowano 2021-06-01 11:34
Autor Wiadomość
pekfos
» 2021-05-21 17:43:16
Powód jest taki, że aplikacja może być uruchamiana z linkami do przetworzenia, ale oczywiście może być też uruchamiana bez nich. Pomyślałem, że wątek uśpiony będzie standardowo, a budzony (ResumeThread) gdy trafi się mu jakiś link.
Dziwne znajdujesz rozwiązania. Gdy będziesz mieć więcej niż jeden wątek przetwarzający, chcesz osobiście decydować który będzie kiedy wybudzony? Zapoznaj się może z mechanizmami z biblioteki standardowej, konkretnie std::condition_variable. Wątki czekające na mechanizmach synchronizacji i tak są uśpione, nie musisz ich usypiać ręcznie.
P-178646
idepozapalki
Temat założony przez niniejszego użytkownika
» 2021-05-26 11:20:34
Dla mnie to rozwiązanie było dość oczywiste.
Mam dwa scenariusze które powinny działać. Aplikacja uruchamiana z linkiem i bez linka. Z kilku ćwiczeń z kompilatorem wyciągnąłem wniosek, że nie ma czegoś takiego jak przeładowanie wątku (tak jak funkcji). Więc pierwszy pomysł był taki żeby uruchamiać "śpiący" wątek i jeżeli jest dla niego jakieś zadanie to go budzić. To oczywiście podejście nowicjusza na wątkowym poletku.

Przerobiłem lekcję z condition_variable.
Do testów użyłem przykładu stąd:
https://en.cppreference.com/w/cpp/thread/condition_variable
lekko go zmodyfikowałem do użytku z moim przypadkiem.

Thread uruchamiam w WinMain zgodnie z poprzednimi sugestiami żeby mieć jeden wątek do brudnej roboty i nie zużywać kolejnych zasobów.
Program wygląda tak:
C/C++
//-----------------------------------------------
//
// Program napisany w Microsoft Visual C++ 2019
// Aplikacja napisana w C++ z uzyciem WinApi
//
//-----------------------------------------------
#include <cmath> // do ceil
#include <windows.h>
#include <commctrl.h>
#include <string>
#include <thread>
#include <process.h>  
// wymagane ze względu na _beginthread
#include <vector>
#include <mutex>
#include <condition_variable>

#pragma comment(lib,"comctl32.lib")
// użycie stylu z XP do grafiki do ListView
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version = '6.0.0.0' processorArchitecture = '*' publicKeyToken = '6595b64144ccf1df' language = '*'\"")

void AddNewItemOnListView( int i );

std::mutex m; // mutex dla sekcji krytycznych
std::condition_variable cv;
std::string data;
int ElementDoPrzetworzenia;
bool ready = false;
bool processed = false;


// deklaracje globalnych uchwytow i zmiennych
MSG msg;
HWND hWnd;
HINSTANCE hInst;
static HWND hwndListView;
#define ID_LISTVIEW                     1000
LVITEM col;
LPCWSTR ApplicationTitle = L"Aplikacja";
std::wstring fileName = L"C:\1\Testy.txt"; // testowy plik

struct Task {
   
int ElementNr = 0; // numer elementu
   
std::wstring path; // pełna sciezka w formacie wstring
   
std::wstring Produkt; // nazwa produktu
   
int iProgress = 0; // postep
   
bool iStatus = 0; // 1 - jezeli plik został przetworzony
};
static std::vector < Task > StructureInVector; // w wektorze beda przechowywane struktury z zadaniami

// klasa przetwarzająca plik tekstowy
class ProcesModul {
public:
   
std::wstring pathToFile;
   
   
ProcesModul( std::wstring _pathToFile ) {
       
this->pathToFile = _pathToFile;
   
}
   
~ProcesModul() { }
   
   
// metoda wczytujaca plik
   
int start() {
       
RECT rcClient; // Client area of parent window.
       
HANDLE hFile; // Handle of file.
       
DWORD cb; // Size of file and count of bytes read.
       
LPCH pch; // Address of data read from file.
       
LPCH pchTmp; // Temporary pointer.
       
unsigned int wczytanychbajtow = 0;
       
unsigned int filesize;
       
unsigned int progress; // do przechowywania postępu jako liczby całkowitej
        // Ensure that the common control DLL is loaded
       
GetClientRect( hWnd, & rcClient );
       
hFile = CreateFile( L"C:\\1\\Testy4.txt", GENERIC_READ, FILE_SHARE_READ,
       
( LPSECURITY_ATTRIBUTES ) NULL, OPEN_EXISTING,
       
FILE_ATTRIBUTE_NORMAL,( HANDLE ) NULL );
       
if( hFile ==( HANDLE ) INVALID_HANDLE_VALUE )
           
 return FALSE;
       
       
cb = GetFileSize( hFile,( LPDWORD ) NULL ); // rozmiar pliku w bajtach
       
filesize = cb; // zapamiętuje rozmiar pliku
        // Parse the file.
       
pch =( LPCH ) LocalAlloc( LPTR, sizeof( char ) * 2048 );
       
pchTmp = pch;
       
do {
           
ReadFile( hFile, pchTmp, sizeof( char ) * 2048, & cb,( LPOVERLAPPED ) NULL );
           
wczytanychbajtow += cb; // zapamiętaj wczytaną liczbę bajtów
           
progress = std::ceil((( double ) wczytanychbajtow /( double ) filesize ) * 100 ); // ceil żeby nie było ewentualnych małych skoków paska do tyłu
           
StructureInVector.at( 0 ).iProgress = progress; // wpisz w strukturę
       
} while( cb );
       
       
CloseHandle(( HANDLE ) hFile ); // zamknij plik
       
return TRUE;
       
//        MessageBox(NULL, L"Koniec przetwarzania", L"Info", MB_ICONINFORMATION | MB_OK);
   
}
}
;

void worker_thread() {
   
std::unique_lock < std::mutex > lk( m ); // Poczekaj, az main () wysle ??dane
    //  cv.wait(lk, [] {return ready; }); // przeładwanie wait - to jest to samo co na dole
   
while( !ready ) {
       
cv.wait( lk ); // wait zatrzymuje watek do czasu az cos dostanie
   
}
   
ProcesModul process( StructureInVector.at( ElementDoPrzetworzenia ).path );
   
process.start();
   
data += " po przetworzeniu";
   
processed = true; // Send data back to main()
   
StructureInVector.at( ElementDoPrzetworzenia ).iStatus = true; // element zostal przetworzony
   
lk.unlock();
   
cv.notify_one(); // odblokowuje jeden z wątków znajdujących się w stanie oczekiwania po uprzednim wywołaniu na obiekcie zmiennej warunkowej metody wait()
}

void startproces() {
    {
       
std::lock_guard < std::mutex > lk( m );
       
ready = true; // dane przygotowane do przetworzenia
   
}
   
// przygotowanie danych dla wątku
   
if( StructureInVector.size() > 0 ) { // jezeli w vectorze jest cos to przetwarzaj    
       
for( int i = 0; i < StructureInVector.size(); i++ ) { // przetwarzaj wszystkie elementy z vectora      
           
if( !StructureInVector.at( i ).iStatus ) { // sprawdz czy plik w strukturze został już przetworzony
               
AddNewItemOnListView( i ); // wyswietl pozycje na liście
               
cv.notify_one(); // odblokowuj dodatkowy wątek
               
std::unique_lock < std::mutex > lk( m );
               
//      cv.wait(lk, [] {return processed; }); // przeładowanie wait - to jest to samo co na dole
               
while( !processed ) {
                   
cv.wait( lk );
               
}
            }
        }
    }
   
// przestawiam flagi pod kolejne zadanie
   
{
       
ready = false;
       
//        processed = false;
   
}
}

//---------------------------------------------------------
//
// Wyciaga rozszerzenie ze sciezki
//
//---------------------------------------------------------
std::wstring GetExtension( const std::wstring & fileName ) {
   
auto pos = fileName.rfind( L"." );
   
if( pos == std::wstring::npos )
       
 pos = - 1;
   
   
return std::wstring( fileName.begin() + pos + 1, fileName.end() );
}

void AddNewItemOnListView( int i ) {
   
TCHAR pszBuffer[ 16 ]; // bufor na cyferke na progresbarze
    // sprawdzam czy w wektorze ze strukturami cos jest
   
if( !StructureInVector.empty() ) { // sprawdzam czy w wektorze ze strukturami cos jest
        // pierwsza kolumna (oznaczona jako 0)
       
col.mask = LVIF_PARAM | LVIF_TEXT;
       
col.iItem = i;
       
col.iSubItem = 0;
       
// tu robię wskaźnik na fileName
       
col.pszText = LPTSTR(( LPCTSTR )( StructureInVector.at( i ).path ).c_str() );
       
col.lParam = StructureInVector.at( i ).iProgress; // tutaj ustawia sie dlugosc progresbaru wizualnie
       
SendMessageW( hwndListView, LVM_INSERTITEM, 0,( LPARAM ) & col );
       
       
// druga kolumna (oznaczona jako 1)
       
col.mask = LVIF_TEXT;
       
col.iSubItem = 1;
       
col.pszText = LPTSTR(( LPCTSTR )( StructureInVector.at( i ).Produkt ).c_str() );
       
SendMessageW( hwndListView, LVM_SETITEM, 0,( LPARAM ) & col );
       
       
// pierwsza kolumna (oznaczona jako 2)
        // pasek progresbaru, ale tutaj wpisuje sie tylko jego procentowa wartosc jako cyfre
       
col.iSubItem = 2;
       
col.pszText = pszBuffer;
       
swprintf_s( pszBuffer, L"%d %%", StructureInVector.at( i ).iProgress );
       
SendMessageW( hwndListView, LVM_SETITEM, 0,( LPARAM ) & col );
   
}
}

static LRESULT HandleCustomDraw( NMLVCUSTOMDRAW * pcd ) {
   
TCHAR buffer[ 40 ];
   
switch( pcd->nmcd.dwDrawStage ) {
   
case CDDS_PREPAINT:
       
/* Tell the control we are interested in per-item notifications.
   * (We need it just to tell the control we want per-subitem
   * notifications.) */
       
return CDRF_DODEFAULT | CDRF_NOTIFYITEMDRAW;
       
       
case( CDDS_ITEM | CDDS_PREPAINT )
            :
/* Tell the control we are interested in per-subitem notifications. */ return CDRF_DODEFAULT | CDRF_NOTIFYSUBITEMDRAW;
       
       
case( CDDS_ITEM | CDDS_SUBITEM | CDDS_PREPAINT )
            :
switch( pcd->iSubItem )
       
{
       
case 1:
           
// obsluga drugiej kolumny danych
            /* Customize "priority" column by marking some priority levels
        * with appropriate color. */
           
col.iSubItem = pcd->iSubItem;
           
col.pszText = buffer;
           
col.cchTextMax = sizeof( buffer ) / sizeof( buffer[ 0 ] );
           
SendMessage( hwndListView, LVM_GETITEMTEXT, pcd->nmcd.dwItemSpec,( LPARAM ) & col ); // LVM_GETITEMTEXT Retrieves the text of a list-view item or subitem.
            /* Let the control do the painting itself with the new color. */
           
return CDRF_DODEFAULT;
           
       
case 2:
           
// Customize "progress" column. We paint simple progress indicator.
           
int iProgress = pcd->nmcd.lItemlParam;
           
int cx;
           
HDC hdc = pcd->nmcd.hdc;
           
COLORREF clrBack;
           
HBRUSH hBackBrush;
           
HBRUSH hProgressBrush;
           
HBRUSH hOldBrush;
           
HPEN hPen;
           
HPEN hOldPen;
           
RECT rc;
           
           
clrBack = pcd->clrTextBk;
           
if( clrBack == CLR_NONE || clrBack == CLR_DEFAULT )
               
 clrBack = RGB( 255, 255, 255 ); // tło pod progresbarami
           
           
hBackBrush = CreateSolidBrush( clrBack );
           
hProgressBrush = CreateSolidBrush( RGB( 190, 190, 255 ) ); // kolory pasków
           
hPen = CreatePen( PS_SOLID, 0, RGB( 190, 190, 255 ) ); // obwódka progresbaru
           
           
hOldBrush =( HBRUSH ) SelectObject( hdc, hBackBrush );
           
FillRect( hdc, & pcd->nmcd.rc, hBackBrush );
           
           
cx = pcd->nmcd.rc.right - pcd->nmcd.rc.left - 6;
           
if( cx < 0 )
               
 cx = 0;
           
           
rc.left = pcd->nmcd.rc.left + 3; // ustawinie lewego brzegu progressbaru
           
rc.top = pcd->nmcd.rc.top + 2;
           
rc.right = rc.left + cx * iProgress / 100;
           
rc.bottom = pcd->nmcd.rc.bottom - 2;
           
SelectObject( hdc, hProgressBrush );
           
FillRect( hdc, & rc, hProgressBrush ); // wypełnienie progressbaru kolorem
           
           
rc.right = pcd->nmcd.rc.right - 3;
           
SelectObject( hdc, GetStockObject( HOLLOW_BRUSH ) );
           
hOldPen =( HPEN ) SelectObject( hdc, hPen );
           
Rectangle( hdc, rc.left, rc.top, rc.right, rc.bottom ); // rysuje obwódkę na około progressbaru
           
           
col.iSubItem = pcd->iSubItem;
           
col.pszText = buffer;
           
col.cchTextMax = sizeof( buffer ) / sizeof( buffer[ 0 ] );
           
SendMessage( hwndListView, LVM_GETITEMTEXT, pcd->nmcd.dwItemSpec,( LPARAM ) & col ); // wrzucenie tekstu
           
DrawText( hdc, buffer, - 1, & rc, DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS );
           
           
SelectObject( hdc, hOldBrush );
           
DeleteObject( hProgressBrush );
           
DeleteObject( hBackBrush );
           
SelectObject( hdc, hOldPen );
           
DeleteObject( hPen );
           
           
// Tell the control to not paint as we did so.
           
return CDRF_SKIPDEFAULT; // brak tego nie narysuje nic
       
}
       
break;
   
}
   
// For all unhandled cases, we let the control do the default.
   
return CDRF_DODEFAULT;
}

//---------------------------------------------------------
//
// Funkcja robi opisy na belce nad ListView
//
//---------------------------------------------------------
static void CreateListViewLabels() {
   
LVCOLUMN col;
   
// tutaj są etykietki na belce nad wrzuconymi stringami
   
col.mask = LVCF_WIDTH | LVCF_TEXT;
   
// piersza pozycja na pasku
   
col.pszText =( LPWSTR ) L"Przetwarzany plik";
   
col.cx = 265;
   
SendMessageW( hwndListView, LVM_INSERTCOLUMN, 0,( LPARAM ) & col );
   
// druga pozycja na pasku
   
col.pszText =( LPWSTR ) L"Produkt";
   
col.cx = 60;
   
SendMessageW( hwndListView, LVM_INSERTCOLUMN, 1,( LPARAM ) & col );
   
// trzecia pozycja na pasku
   
col.pszText =( LPWSTR ) L"Postęp";
   
col.cx = 80;
   
SendMessageW( hwndListView, LVM_INSERTCOLUMN, 2,( LPARAM ) & col );
}

void AddControls( HWND hWnd ) {
   
hwndListView = CreateWindowEx( 0, WC_LISTVIEW, NULL, LVS_REPORT | WS_TABSTOP | WS_BORDER | WS_CHILD | WS_VISIBLE,
   
10, 10, 600, 250, hWnd,( HMENU ) ID_LISTVIEW, hInst, 0 );
}

//---------------------------------------------------------
//
// Obsluga rzuconych plikow
//
//---------------------------------------------------------
void DropFile( WPARAM wParam ) {
   
TCHAR lpszFile[ MAX_PATH ] = { 0 };
   
UINT uFile = 0;
   
HDROP hDrop =( HDROP ) wParam;
   
int index, count, length;
   
count = DragQueryFile( hDrop, 0xFFFFFFFF, NULL, 0 ); // ilosc rzuconych plikow
    // petla bedzie czytac rzucone pliki
   
for( index = 0; index < count; ++index ) {
       
length = DragQueryFile( hDrop, index, NULL, 0 );
       
if( length > 0 ) {
           
TCHAR * fname = new TCHAR[ static_cast < size_t >( length ) + 1 ]; // rzutuje
           
DragQueryFile( hDrop, index, fname, length + 1 );
           
// konwertuje tchar do wstringa
           
std::wstring sciezka( & fname[ 0 ] );
           
std::wstring rozszerzenie;
           
rozszerzenie = GetExtension( sciezka );
           
// filtruje pliki i do przetwarzania puszczam tylko aptsource
           
if( rozszerzenie == L"txt" ) {
               
               
Task t { 0, sciezka, L"Produkt3", 2 }; // testowe ustawienie 8% na progressbarze i opisy
               
StructureInVector.push_back( t ); // dodaje strukture do vectora
               
startproces();
           
}
           
delete[ ] fname;
       
}
    }
   
DragFinish( hDrop );
}

//---------------------------------------------------------
//
// Procedura obsługująca wiadomości dla okna, wywoływana przez
// MS-Windows przy okazji różnych zdarzeń, które należy obsłużyć
//
//--------------------------------------------------------------
static LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
   
switch( msg )
   
{
   
case WM_CREATE:
       
AddControls( hWnd );
       
CreateListViewLabels();
       
break;
       
       
// Obsluga drag & drop
   
case WM_DROPFILES: {
           
DropFile( wParam );
           
break;
       
}
       
   
case WM_NOTIFY:
       
{
           
NMHDR * pHdr =( NMHDR * ) lParam;
           
if( pHdr->idFrom == ID_LISTVIEW && pHdr->code == NM_CUSTOMDRAW )
               
 return HandleCustomDraw(( NMLVCUSTOMDRAW * ) pHdr );
           
           
break;
       
}
       
       
// reakcja na zamkniecie okienka
   
case WM_CLOSE:
       
PostQuitMessage( 0 );
       
return 0;
       
       
// komunikat od systemu wyslany po zamknieciu okienka
   
case WM_DESTROY:
       
PostQuitMessage( 0 );
       
return 0;
       
       
default:
       
return DefWindowProc( hWnd, msg, wParam, lParam );
       
break;
   
}
   
return DefWindowProc( hWnd, msg, wParam, lParam );
}

void InitMainWnd() {
   
HINSTANCE hInstance = GetModuleHandle( 0 );
   
WNDCLASSEX wClass = { 0 };
   
ZeroMemory( & wClass, sizeof( WNDCLASSEX ) );
   
wClass.style = CS_HREDRAW | CS_VREDRAW;
   
wClass.cbClsExtra = 0;
   
wClass.cbWndExtra = 0;
   
wClass.cbSize = sizeof( WNDCLASSEX );
   
wClass.hbrBackground =( HBRUSH )( COLOR_BTNFACE + 1 );
   
wClass.hInstance = hInstance;
   
wClass.lpfnWndProc =( WNDPROC ) WndProc;
   
wClass.lpszClassName = L"My Window Class";
   
wClass.hCursor = LoadCursor( 0, IDC_ARROW );
   
wClass.hIcon = LoadIcon( NULL, IDI_APPLICATION );
   
wClass.hIconSm = LoadCursor( NULL, IDC_ARROW );
   
wClass.lpszMenuName = 0;
   
// Rejestrujemy klasę okna w Windows.
   
if( !RegisterClassEx( & wClass ) ) {
       
int nResult = GetLastError();
       
MessageBox( NULL, L"Nie utworzono żadnej klasy!", L"Błąd", MB_ICONERROR );
   
}
}

//---------------------------------------------------------
//
// Funkcja tworząca główne okno
//
//---------------------------------------------------------
HWND CreateMainWnd() {
   
return
   
CreateWindowEx(
   
WS_EX_CLIENTEDGE,
   
L"My Window Class",
   
ApplicationTitle,
   
WS_OVERLAPPED | WS_CAPTION | WS_BORDER | WS_MINIMIZEBOX | WS_SYSMENU,
   
0,
   
0,
   
650,
   
400,
   
NULL,
   
NULL,
   
hInst,
   
NULL );
}

//---------------------------------------------------------
//
// Do obsługi LisView
//
//---------------------------------------------------------
void CreateCommonControls() {
   
// inicjacja klasy pasków chyba sa niepotrzebne narazie
   
INITCOMMONCONTROLSEX cc;
   
cc.dwSize = sizeof( INITCOMMONCONTROLSEX );
   
cc.dwICC = ICC_PROGRESS_CLASS;
   
cc.dwICC = ICC_BAR_CLASSES; // toolbary, statusbary, tooltipy i oczywiście progressbary
   
InitCommonControlsEx( & cc );
}

//---------------------------------------------------------
//
// Główna funkcja w Windows , od której uruchamiany jest program
// kodowanie zależne od ustawień projektu
//
//---------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int showCmd )
{
   
InitMainWnd(); // startuje głowne okno
   
hWnd = CreateMainWnd(); // create the main window!
   
if( !hWnd ) {
       
int nResult = GetLastError();
       
MessageBox( NULL, L"Nie utworzono żadnego okna!", L"Błąd", MB_ICONERROR );
   
}
   
// wstawiam pierwszy element do listView
   
std::thread worker( worker_thread );
   
data = "Przykladowe dane";
   
{
       
std::lock_guard < std::mutex > lk( m );
   
}
   
CreateCommonControls();
   
DragAcceptFiles( hWnd, true ); // Drag & Drop w starym stylu
    //    Task t{ 0, fileName, L"Produkt2", 8 }; // testowe ustawienie 8% na progressbarze i opisy
    //    StructureInVector.push_back(t); // dodaje strukture do vectora
    //    AddNewItemOnListView(0);
    //    startproces()
   
ShowWindow( hWnd, showCmd ); // Utworzone okno wyświetlamy na ekranie  
   
UpdateWindow( hWnd ); // Uaktualniamy treść okna
   
ZeroMemory( & msg, sizeof( MSG ) );
   
while( msg.message != WM_QUIT ) { // Rozpoczynamy pętlę obsługi wiadomości napływających do naszego okna.
       
if( PeekMessage( & msg, 0, 0, 0, PM_REMOVE ) ) {
           
TranslateMessage( & msg ); // Przekształcamy wiadomość
           
DispatchMessage( & msg ); // Wysyłamy ją do procedury okna
       
}
    }
   
worker.join();
   
return 0;
}
Zrobiłem trochę testów (z drag&drop) i na pierwszy rzut oka to działa, ale okazało się, że przetwarzany jest tylko pierwszy rzucony link. Thread przetwarza pierwszy rzucony link, a później gdzieś "znika". Kolejne linki dodawane są do ListView, ale nie pojawiają się w void worker_thread().

Zrobiłem też test w WinMain (jest zakomentowany). Aplikacja uruchomiona z linkiem przetwarza plik (robi to thread), później główny wątek wpada w pętlę i po tym dodatkowy "thread" jest w którymś momencie "gubiony". Po głównej pętli z wiadomościami mam już tylko jeden wątek.
C/C++
while( msg.message != WM_QUIT ) { // Rozpoczynamy pętlę obsługi wiadomości napływających do naszego okna.
   
if( PeekMessage( & msg, 0, 0, 0, PM_REMOVE ) ) {
       
TranslateMessage( & msg ); // Przekształcamy wiadomość
       
DispatchMessage( & msg ); // Wysyłamy ją do procedury okna
   
}
}

Ustawiłem pułapkę na linii
worker.join();
i przy wyłączaniu aplikacji dodatkowego thread już nie ma.

Pomysł był taki żeby ten thread usypiać po pracy, ale coś mi go zabija.
P-178668
pekfos
» 2021-05-26 22:13:02
Kod wątku się kończy po przetworzeniu jednego zadania. Powinna tam być pętla.
P-178669
idepozapalki
Temat założony przez niniejszego użytkownika
» 2021-05-26 23:32:40
Dodałem pętlę.

C/C++
void worker_thread() {
   
for(;; ) {
       
std::unique_lock < std::mutex > lk( m ); // Poczekaj, az main () wysle ??dane
        //  cv.wait(lk, [] {return ready; }); // przeładwanie wait - to jest to samo co na dole
       
while( !ready ) {
           
cv.wait( lk ); // wait zatrzymuje watek do czasu az cos dostanie
       
}
       
ProcesModul process( StructureInVector.at( ElementDoPrzetworzenia ).path );
       
process.start();
       
data += " po przetworzeniu";
       
processed = true; // Send data back to main()
       
StructureInVector.at( ElementDoPrzetworzenia ).iStatus = true; // element zostal przetworzony
       
lk.unlock();
       
cv.notify_one(); // odblokowuje jeden z wątków znajdujących się w stanie oczekiwania po uprzednim wywołaniu na obiekcie zmiennej warunkowej metody wait()
       
   
}
}

Przetwarzanie działa, tak jak powinno.
Czy ta pętla może mieć jakiś wpływ na zamykanie całej aplikacji?
Bo po kliknięciu krzyżyka kręci klepsydra i na coś czeka.
P-178670
pekfos
» 2021-05-27 21:06:59
Czy ta pętla może mieć jakiś wpływ na zamykanie całej aplikacji?
Bo po kliknięciu krzyżyka kręci klepsydra i na coś czeka.
No pewnie. Na koniec programu czekasz na wątek, który w nieskończonej pętli czeka na zadania. Musisz dorobić jakiś mechanizm na wyłączenie go, np przez ustawienie jakiejś zmiennej bool i wywołanie cv.notify_all() przed join().

Poza tym, ten kod w ogóle działa poprawnie? Tzn nie zawiesza głównego wątku?
P-178674
idepozapalki
Temat założony przez niniejszego użytkownika
» 2021-05-28 11:43:28
Tak, kod jako taki działa poprawnie, chociaż są w nim drobne potknięcia - ale to oczywiście do wyeliminowania.


Kończenie dodatkowego wątku nie zadziałało. Zrobiłem wg zaleceń:
Dodałem zmienną globalną

bool przerwij = false;

Przełączanie jej umieściłem w zdarzeniu WM_CLOSE
C/C++
case WM_CLOSE:
przerwij = true;
PostQuitMessage( 0 );
return 0;

W wątku dodałem breaka(w dwóch miejscach, najpierw na początku).

C/C++
void worker_thread() {
   
for(;; ) {
       
       
//        if (przerwij)
        //            break;
       
       
std::unique_lock < std::mutex > lk( m ); // Poczekaj, az main () wysle ??dane
        //  cv.wait(lk, [] {return ready; }); // przeładwanie wait - to jest to samo co na dole
       
while( !ready ) {
           
cv.wait( lk ); // wait zatrzymuje watek do czasu az cos dostanie
       
}
       
       
if( przerwij )
           
 break;
       
       
ProcesModul process( StructureInVector.at( ElementDoPrzetworzenia ).path );
       
process.start();
       
data += " po przetworzeniu";
       
processed = true; // Send data back to main()
       
StructureInVector.at( ElementDoPrzetworzenia ).iStatus = true; // element zostal przetworzony
       
lk.unlock();
       
cv.notify_one(); // odblokowuje jeden z wątków znajdujących się w stanie oczekiwania po uprzednim wywołaniu na obiekcie zmiennej warunkowej metody wait()
       
   
}
}

przed worker.join(); dodałem jeszcze cv.notify_all();

Sprawdziłem debugerem działanie. Pułapki ustawiłem na zdarzenie WM_CLOSE i na warunek przerwij w worker_thread()
Przy zamykaniu WM_CLOSE działa i zmienia przerwij na true(oba wątki pracują), ale później debuger już nie wskakuje do worker_thread() - chociaż powinien.

P-178675
idepozapalki
Temat założony przez niniejszego użytkownika
» 2021-05-28 20:23:14
Moje niedopatrzenie.
Brakowało odblokowania przetwarzania ready.

C/C++
case WM_CLOSE:
ready = true;
przerwij = true;
PostQuitMessage( 0 );
return 0;

Teraz dodatkowy wątek zamyka się poprawnie.
P-178677
idepozapalki
Temat założony przez niniejszego użytkownika
» 2021-06-01 09:56:07
Chciałbym jeszcze zapytać o odświeżanie ListView.
W funkcji startproces() dodawana jest nowa pozycja do ListView.
Teoretycznie powinna wyświetlić się zanim wystartuje wątek wczytujący plik.
Praktycznie jednak jest odwrotnie. Podejrzewam, że chodzi o odświeżanie.

Pomysł z progresbar w ListView jest zapożyczony z tego miejsca:

https://www.codeproject.com/Articles/646482/Custom-Controls-in-Win-API-Control-Customization

Mam problem z uruchomieniem funkcji CustomPaint()
Nie wiem jak ją uzupełnić.

C/C++
static void
CustomPaint( HWND hWnd, HDC hDC, RECT * rcDirty, BOOL bErase )
{
   
NMCUSTOMDRAW nmcd; // The custom draw structure
   
LRESULT cdControlMode; // Return value of NM_CUSTOMDRAW for CDDS_PREPAINT
   
LRESULT cdItemMode; // Return value of NM_CUSTOMDRAW for CDDS_ITEM | CDDS_PREPAINT
   
    // Initialize members of the custom draw structure used for all the stages below:
   
nmcd.hdr.hwndFrom = hWnd;
   
nmcd.hdr.idFrom = GetWindowLong( hWnd, GWL_ID );
   
nmcd.hdr.code = NM_CUSTOMDRAW;
   
nmcd.hdc = hDC;
   
   
if( bErase ) {
       
LRESULT cdEraseMode;
       
       
// Send control pre-erase notification:
       
nmcd.dwDrawStage = CDDS_PREERASE;
       
cdEraseMode = SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd );
       
       
if( !( cdEraseMode & CDRF_SKIPDEFAULT ) ) {
           
// Do the erasing:
            // Send control post-erase notification:
           
if( cdEraseMode & CDRF_NOTIFYPOSTERASE ) {
               
nmcd.dwDrawStage = CDDS_POSTERASE;
               
SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd );
           
}
        }
    }
   
   
// Send control pre-paint notification:
   
nmcd.dwDrawStage = CDDS_PREPAINT;
   
GetClientRect( hWnd, & nmcd.rc );
   
cdControlMode = SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd );
   
   
if( !( cdControlMode &( CDRF_SKIPDEFAULT | CDRF_DOERASE ) ) ) {
       
// Do the control (as a whole) painting (e.g. some kind of background or frame)
        // Iterate through all control items.
        // (If the control does not support any items, just omit all this for-loop.)
       
for( int i = 0; i < lvi.iSubItem; i++ )
       
//for (int i = 0; i < lv.Items.Count; i++)
       
{
           
// Send item pre-paint notification (if desired by the app):
           
if( cdControlMode & CDRF_NOTIFYITEMDRAW ) {
               
nmcd.dwDrawStage = CDDS_ITEM | CDDS_PREPAINT;
               
// Set some attributes describing the items. The app. can change
                // them to augment painting of the item:
               
nmcd.rc =;
               
               
// Identify the item (in per control specific way) so app. knows what item is augmenting.
               
nmcd.dwItemSpec =;
               
               
// Tell app. if selection box, focus highlight etc. should be painted for the item.
               
nmcd.uItemState =;
               
               
// Fill in also any data the app. may have associated with the
                // item so it can use them for augmenting of the control.
               
nmcd.lItemlParam =;
               
               
// Send the notification.
               
cdItemMode = SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd );
           
}
           
else {
               
cdItemMode = CDRF_DODEFAULT;
           
}
           
           
// Do the item painting (unlesse suppressed by the app.)
           
if( !( cdItemMode & CDRF_SKIPDEFAULT ) ) {
               
// Note you should be ready for a case hdc may have different font selected
                // if (cdItemMode & CDRF_NEWFONT)). In such case you should reset it to
                // the default control font after this particular item is painted:
               
               
                // Do the item (as a whole) painting
                // (e.g. some kind of item background or frame)
               
               
                // If the item is composed from a set of subitems, another similar
                // nested loop would be here to handle them. You would also need
                // yet another variable (cdSubitemMode) and handle the subitems
                // in similar way. nmcd.dwDrawStage would just set CDDS_SUBITEM
                // instead of CDDS_ITEM.
               
               
                // Do item "post-painting"
                // (e.g. paint a focus rectangle if the item is selected and
                // control has a focus).
               
               
                // Send item post-paint notification:
               
if( cdItemMode & CDRF_NOTIFYPOSTPAINT ) {
                   
nmcd.dwDrawStage = CDDS_ITEM | CDDS_POSTERASE;
                   
SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd );
               
}
            }
        }
       
       
// Do control "post-painting":
       
if( !( cdControlMode & CDRF_SKIPPOSTPAINT ) ) {
           
        }
       
       
// Send control post-paint notification:
       
if( cdControlMode & CDRF_NOTIFYPOSTPAINT ) {
           
nmcd.dwDrawStage = CDDS_POSTERASE;
           
SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd );
       
}
    }
}

Czy w lekcjach znajdę coś o tej tematyce?
Nie rozumiem mechanizmu jaki tym kieruje.
P-178685
1 2 « 3 » 4
Poprzednia strona Strona 3 z 4 Następna strona