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: #include <cmath> #include <windows.h> #include <commctrl.h> #include <string> #include <thread> #include <process.h> #include <vector> #include <mutex> #include <condition_variable>
#pragma comment(lib,"comctl32.lib") #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; std::condition_variable cv; std::string data; int ElementDoPrzetworzenia; bool ready = false; bool processed = false;
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"; struct Task { int ElementNr = 0; std::wstring path; std::wstring Produkt; int iProgress = 0; bool iStatus = 0; }; static std::vector < Task > StructureInVector; class ProcesModul { public: std::wstring pathToFile; ProcesModul( std::wstring _pathToFile ) { this->pathToFile = _pathToFile; } ~ProcesModul() { } int start() { RECT rcClient; HANDLE hFile; DWORD cb; LPCH pch; LPCH pchTmp; unsigned int wczytanychbajtow = 0; unsigned int filesize; unsigned int progress; 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 ); filesize = cb; pch =( LPCH ) LocalAlloc( LPTR, sizeof( char ) * 2048 ); pchTmp = pch; do { ReadFile( hFile, pchTmp, sizeof( char ) * 2048, & cb,( LPOVERLAPPED ) NULL ); wczytanychbajtow += cb; progress = std::ceil((( double ) wczytanychbajtow /( double ) filesize ) * 100 ); StructureInVector.at( 0 ).iProgress = progress; } while( cb ); CloseHandle(( HANDLE ) hFile ); return TRUE; } };
void worker_thread() { std::unique_lock < std::mutex > lk( m ); while( !ready ) { cv.wait( lk ); } ProcesModul process( StructureInVector.at( ElementDoPrzetworzenia ).path ); process.start(); data += " po przetworzeniu"; processed = true; StructureInVector.at( ElementDoPrzetworzenia ).iStatus = true; lk.unlock(); cv.notify_one(); }
void startproces() { { std::lock_guard < std::mutex > lk( m ); ready = true; } if( StructureInVector.size() > 0 ) { for( int i = 0; i < StructureInVector.size(); i++ ) { if( !StructureInVector.at( i ).iStatus ) { AddNewItemOnListView( i ); cv.notify_one(); std::unique_lock < std::mutex > lk( m ); while( !processed ) { cv.wait( lk ); } } } } { ready = false; } }
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 ]; if( !StructureInVector.empty() ) { col.mask = LVIF_PARAM | LVIF_TEXT; col.iItem = i; col.iSubItem = 0; col.pszText = LPTSTR(( LPCTSTR )( StructureInVector.at( i ).path ).c_str() ); col.lParam = StructureInVector.at( i ).iProgress; SendMessageW( hwndListView, LVM_INSERTITEM, 0,( LPARAM ) & col ); col.mask = LVIF_TEXT; col.iSubItem = 1; col.pszText = LPTSTR(( LPCTSTR )( StructureInVector.at( i ).Produkt ).c_str() ); SendMessageW( hwndListView, LVM_SETITEM, 0,( LPARAM ) & col ); 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: return CDRF_DODEFAULT | CDRF_NOTIFYITEMDRAW; case( CDDS_ITEM | CDDS_PREPAINT ) : return CDRF_DODEFAULT | CDRF_NOTIFYSUBITEMDRAW; case( CDDS_ITEM | CDDS_SUBITEM | CDDS_PREPAINT ) : switch( pcd->iSubItem ) { case 1: col.iSubItem = pcd->iSubItem; col.pszText = buffer; col.cchTextMax = sizeof( buffer ) / sizeof( buffer[ 0 ] ); SendMessage( hwndListView, LVM_GETITEMTEXT, pcd->nmcd.dwItemSpec,( LPARAM ) & col ); return CDRF_DODEFAULT; case 2: 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 ); hBackBrush = CreateSolidBrush( clrBack ); hProgressBrush = CreateSolidBrush( RGB( 190, 190, 255 ) ); hPen = CreatePen( PS_SOLID, 0, RGB( 190, 190, 255 ) ); 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; 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 ); 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 ); col.iSubItem = pcd->iSubItem; col.pszText = buffer; col.cchTextMax = sizeof( buffer ) / sizeof( buffer[ 0 ] ); SendMessage( hwndListView, LVM_GETITEMTEXT, pcd->nmcd.dwItemSpec,( LPARAM ) & col ); 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 ); return CDRF_SKIPDEFAULT; } break; } return CDRF_DODEFAULT; }
static void CreateListViewLabels() { LVCOLUMN col; col.mask = LVCF_WIDTH | LVCF_TEXT; col.pszText =( LPWSTR ) L"Przetwarzany plik"; col.cx = 265; SendMessageW( hwndListView, LVM_INSERTCOLUMN, 0,( LPARAM ) & col ); col.pszText =( LPWSTR ) L"Produkt"; col.cx = 60; SendMessageW( hwndListView, LVM_INSERTCOLUMN, 1,( LPARAM ) & col ); 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 ); }
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 ); 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 ]; DragQueryFile( hDrop, index, fname, length + 1 ); std::wstring sciezka( & fname[ 0 ] ); std::wstring rozszerzenie; rozszerzenie = GetExtension( sciezka ); if( rozszerzenie == L"txt" ) { Task t { 0, sciezka, L"Produkt3", 2 }; StructureInVector.push_back( t ); startproces(); } delete[ ] fname; } } DragFinish( hDrop ); }
static LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { switch( msg ) { case WM_CREATE: AddControls( hWnd ); CreateListViewLabels(); break; 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; } case WM_CLOSE: PostQuitMessage( 0 ); return 0; 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; if( !RegisterClassEx( & wClass ) ) { int nResult = GetLastError(); MessageBox( NULL, L"Nie utworzono żadnej klasy!", L"Błąd", MB_ICONERROR ); } }
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 ); }
void CreateCommonControls() { INITCOMMONCONTROLSEX cc; cc.dwSize = sizeof( INITCOMMONCONTROLSEX ); cc.dwICC = ICC_PROGRESS_CLASS; cc.dwICC = ICC_BAR_CLASSES; InitCommonControlsEx( & cc ); }
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int showCmd ) { InitMainWnd(); hWnd = CreateMainWnd(); if( !hWnd ) { int nResult = GetLastError(); MessageBox( NULL, L"Nie utworzono żadnego okna!", L"Błąd", MB_ICONERROR ); } std::thread worker( worker_thread ); data = "Przykladowe dane"; { std::lock_guard < std::mutex > lk( m ); } CreateCommonControls(); DragAcceptFiles( hWnd, true ); ShowWindow( hWnd, showCmd ); UpdateWindow( hWnd ); ZeroMemory( & msg, sizeof( MSG ) ); while( msg.message != WM_QUIT ) { if( PeekMessage( & msg, 0, 0, 0, PM_REMOVE ) ) { TranslateMessage( & msg ); DispatchMessage( & msg ); } } 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. while( msg.message != WM_QUIT ) { if( PeekMessage( & msg, 0, 0, 0, PM_REMOVE ) ) { TranslateMessage( & msg ); DispatchMessage( & msg ); } }
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. |
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ć. static void CustomPaint( HWND hWnd, HDC hDC, RECT * rcDirty, BOOL bErase ) { NMCUSTOMDRAW nmcd; LRESULT cdControlMode; LRESULT cdItemMode; nmcd.hdr.hwndFrom = hWnd; nmcd.hdr.idFrom = GetWindowLong( hWnd, GWL_ID ); nmcd.hdr.code = NM_CUSTOMDRAW; nmcd.hdc = hDC; if( bErase ) { LRESULT cdEraseMode; nmcd.dwDrawStage = CDDS_PREERASE; cdEraseMode = SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd ); if( !( cdEraseMode & CDRF_SKIPDEFAULT ) ) { if( cdEraseMode & CDRF_NOTIFYPOSTERASE ) { nmcd.dwDrawStage = CDDS_POSTERASE; SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd ); } } } 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 ) ) ) { for( int i = 0; i < lvi.iSubItem; i++ ) { if( cdControlMode & CDRF_NOTIFYITEMDRAW ) { nmcd.dwDrawStage = CDDS_ITEM | CDDS_PREPAINT; nmcd.rc =; nmcd.dwItemSpec =; nmcd.uItemState =; nmcd.lItemlParam =; cdItemMode = SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd ); } else { cdItemMode = CDRF_DODEFAULT; } if( !( cdItemMode & CDRF_SKIPDEFAULT ) ) { if( cdItemMode & CDRF_NOTIFYPOSTPAINT ) { nmcd.dwDrawStage = CDDS_ITEM | CDDS_POSTERASE; SendMessage( GetParent( hWnd ), WM_NOTIFY, nmcd.hdr.code,( LPARAM ) & nmcd ); } } } if( !( cdControlMode & CDRF_SKIPPOSTPAINT ) ) { } 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. |