idepozapalki Temat założony przez niniejszego użytkownika |
[C++] [WinApi] Problem z dodaniem Drag&drop » 2021-03-18 12:48:01 W zamierzeniu aplikacja ma pobierać linki z rzuconych w nią plików. Chciałbym żeby oprogramowane były 3 scenariusze. 1 - aplikacja nie uruchomiona, pliki rzucane są na exe'ka To już oprogramowałem. Użyłem funkcji CommandLineToArgvW(GetCommandLine(), &argCount); 2 - aplikacja uruchomiona, pliki rzucane są na otwarte okno To też już działa z użyciem zdarzenia case WM_DROPFILES: 3 - aplikacja uruchomiona, pliki są rzucane w exe'ka Tutaj mam problem, rzucanie nie działa. Po rzuceniu pliku słyszę jedynie dzwięk okienka informacyjnego, ale nic poza tym. Kilka razy widziałem "mrugnięcie" okienka, ale nie wiem jak to sprawdzić. Nie wiem też jak użyć debugera w VisualStudio wg 3 scenariusza. Co już zrobiłem: Sprawdziłem forum, ale tu nie znalazłem rozwiązania. Przejrzałem stackoverflow, tam nie zauważyłem rozwiązania 3 scenariusza. Tam znalazłem inspiracje do pierwszego scenariusza. Odwiedziłem tą stronę: https://bugrazoid.ru/doku.php?id=develop:winapi:draganddrop Przetestowałem wszystkie aplikacje, ale żadna nie działa wg 3 scenariusza. Zacząłem czytać te informacje, ale nie wiem czy tam jest rozwiązanie dla mnie. To jest w trakcie https://devblogs.microsoft.com/oldnewthing/20100503-00/?p=14183 Znalazłem kilka rozwiązań ale z użyciem MFC A tu to co sam poskładałem: #include <windows.h> #include <tchar.h> #include <commctrl.h> #include <vector> #include <string>
#pragma comment(lib,"comctl32.lib")
MSG msg; HWND hWnd; HINSTANCE hInst;
const TCHAR * ApplicationTitle = _T( "Aplikacja" );
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 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" ) { MessageBox( NULL, fname, L"Rzucone pliki", MB_OK ); } delete[ ] fname; } } DragFinish( hDrop ); }
static LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { switch( msg ) { case WM_DROPFILES: { DropFile( wParam ); 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 = _T( "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, _T( "Nie utworzono żadnej klasy!" ), _T( "Błąd" ), MB_ICONERROR ); } HWND hOther = FindWindow( wClass.lpszClassName, NULL ); if( hOther ) { PostQuitMessage( 0 ); } }
void LaunchCommandLineElements() { LPWSTR * szArgList; int argCount; szArgList = CommandLineToArgvW( GetCommandLine(), & argCount ); for( int i = 1; i < argCount; i++ ) { TCHAR * plik = szArgList[ i ]; std::wstring wplik( plik ); std::wstring rozszerzenie; rozszerzenie = GetExtension( wplik ); if( rozszerzenie == L"txt" ) { MessageBox( NULL, szArgList[ i ], L"Zawartość listy argumentów", MB_OK ); } } LocalFree( szArgList ); }
HWND CreateMainWnd() { return CreateWindowEx( WS_EX_CLIENTEDGE, _T( "My Window Class" ), ApplicationTitle, WS_OVERLAPPED | WS_CAPTION | WS_BORDER | WS_MINIMIZEBOX | WS_SYSMENU, 0, 0, 400, 400, NULL, NULL, hInst, NULL ); }
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int showCmd ) { OleInitialize( NULL ); InitMainWnd(); hWnd = CreateMainWnd(); if( !hWnd ) { int nResult = GetLastError(); MessageBox( NULL, _T( "Nie utworzono żadnego okna!" ), _T( "Błąd" ), MB_ICONERROR ); } DragAcceptFiles( hWnd, true ); LaunchCommandLineElements(); 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 ); } else { } } OleUninitialize(); return 0; }
|
|
pekfos |
» 2021-03-18 17:08:45 Mignięcie aplikacji w scenariuszu 3 wynika zapewne z tego: HWND hOther = FindWindow( wClass.lpszClassName, NULL ); if( hOther ) { PostQuitMessage( 0 ); } To sugeruje że chcesz by w scenariuszu nr 3 plik rzucony na exe był obsłużony przez wcześniej działającą instancję. W tym celu musisz użyć jakiegoś mechanizmu komunikacji między procesami i przesłać odpowiednią informację do pierwszego procesu. https://docs.microsoft.com/en-us/windows/win32/ipc/interprocess-communicationsMożesz użyć na przykład named pipe w trybie message ( PIPE_TYPE_MESSAGE). Pierwsza instancja by tworzyła pipe i nasłuchiwała przychodzących wiadomości w osobnym wątku. https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipesNie wiem też jak użyć debugera w VisualStudio wg 3 scenariusza. Uruchom program w tle, potem drugą instancję pod debuggerem z podaniem ścieżki do pliku w opcjach uruchomieniowych. |
|
idepozapalki Temat założony przez niniejszego użytkownika |
» 2021-03-18 23:24:45 Masz rację, to miganie wynika właśnie z tego fragmentu kodu. Przy rzucie linka tworzona była kolejna instancja której okno przykrywało okno tej pierwszej, a ten fragment go wygaszał. Planowałem, żeby w aplikacji była jedna instancja. Przy częstym używaniu programu każde uruchomienie, byłoby kolejnym okienkiem i kolejnym elemencikiem na pasku. Poza tym gdy jest jedna instancja to w łatwy sposób można w jakimś ListView zgromadzić wyniki pracy programu, a tak trzeba przeglądać te pojedyncze instancje. Dlatego użyłem kawałka kodu z odcinka kursu pt "Jedna instancja", ale okazało się, że ma on też minusy w połączeniu z uruchamianiem przez rzut. Sprawdziłem też mutex'a, ale oczywiście efekt ten sam. Możliwa jest też opcja, że aplikacja będzie tray'u koło zegarka, ale nie wiem jak w tym przypadku wyglądałaby historia z kolejnymi instancjami. Starałem się w prosty sposób jakoś rozwiązać mój kłopot. Pierwsze podejście jest takie: Na początku dodaje zmienną globalną: bool jednainstancja = TRUE ; i zmieniam kod głównej części: int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int showCmd ) { OleInitialize( NULL ); LaunchCommandLineElements(); InitMainWnd(); if( jednainstancja ) { hWnd = CreateMainWnd(); if( !hWnd ) { int nResult = GetLastError(); MessageBox( NULL, _T( "Nie utworzono żadnego okna!" ), _T( "Błąd" ), MB_ICONERROR ); } 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 ); } else { } } OleUninitialize(); return 0; }
Na pierwszy rzut oka spełnia to swoje zadanie, ale to tylko pierwsze wrażenie. Uruchomienie na początku LaunchCommandLineElements() spowoduje że w każdym przypadku dostanę linki, ALE linki te będę miał dużo wcześniej zanim pojawi się okno gdzie mogę pokazać na bieżąco efekty pracy programu. Trochę bez sensu będzie obróbka pliku zanim jeszcze pojawi się okno programu. Przy dużych plikach takie zachowanie programu byłby frustrujący, bo rzucasz link do dużej porcji tekstu a tu nic się nie dzieje. Ten kod wyżej nie rozwiązał też problemu z miganiem aplikacją. Sprawdzałem i niby nie powinna się pojawiać, bo dołożony warunek omija kawałek wyświetlający i uaktualniający okno ale mimo to coś tam migało. Chciałbym uniknąć takiego "druciarstwa". Po cichu miałem nadzieję że drag&drop ma gdzieś metodę/funkcję która przy uruchomionej aplikacji mogłaby pobrać linka rzuconego w exe'ka - ale do tej pory się nie doszukałem. Na ten moment nie mam praktyki z named pipe, ale postaram się odrobić tą lekcję. |
|
pekfos |
» 2021-03-19 00:06:59 Mignięcie bierze się z tego, że PostQuitMessage() nie zamyka programu, a tylko zleca jego zamknięcie. Zdążysz w tym czasie utworzyć okno. W tej sytuacji powinieneś raczej wyjść z WinMain(), albo użyć czegoś w stylu exit(). Możliwa jest też opcja, że aplikacja będzie tray'u koło zegarka, ale nie wiem jak w tym przypadku wyglądałaby historia z kolejnymi instancjami.
Każda instancja by generowała osobną ikonę w tray'u, co raczej odpada. |
|
idepozapalki Temat założony przez niniejszego użytkownika |
» 2021-03-19 00:49:08 Poprawiłem to do takiej postaci. int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int showCmd ) { LaunchCommandLineElements(); if( jednainstancja ) { InitMainWnd(); hWnd = CreateMainWnd(); if( !hWnd ) { int nResult = GetLastError(); MessageBox( NULL, _T( "Nie utworzono żadnego okna!" ), _T( "Błąd" ), MB_ICONERROR ); } 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 ); } else { } } } return 0; }
Teoretycznie przy uruchomionej aplikacji po rzucie linków powinna uruchomić się tylko funkcja LaunchCommandLineElements(), a później program powinien skoczyć do return 0 i omijam całość okienkową. Testy pokazują jednak coś innego. Szczególnie to widać gdy przesunie się okno z "z pozycji startowej" w lewym górnym rogu ekranu gdzieś z bok. Po kliknięciu w okienko komunikatu z linkiem w "pozycji startowej" pojawia się "duch" okna. Nie rozumiem tej sytuacji, bo właściwie to omijam wszystko "okienkowe" i tam nic nie powinno się pojawiać. Rzeczywiście więcej ikonek w tray'u to tylko przeniesienie tam problemu. ---------------------------------------------------------------------------- Jednak popełniłem błąd. Funkcja InitMainWnd() MUSI być przed warunkiem sprawdzającym instancję. Teraz zaczęło wszystko działać poprawnie. Potestuję jednak całość trochę dla pewności. |
|
pekfos |
» 2021-03-19 15:40:34 Czemu dokładnie służy zmienna jednainstancja? Jak dotąd nie widać w kodzie by robiła cokolwiek. |
|
DejaVu |
» 2021-03-19 16:53:24 Ode mnie mały hint (nie związany z Twoim problemem): const TCHAR * ApplicationTitle = _T( "Aplikacja" );
Na Twoim miejscu olałbym całkowicie TCHAR/char i implementował wszystko już na wide stringach tj. const wchar_t * ApplicationTitle = L"Aplikacja";
Powody:
|
|
idepozapalki Temat założony przez niniejszego użytkownika |
» 2021-03-19 19:32:57 @pekfos Zmienna jednainstancja służy do wykrywania pojawiania się kolejnych instancji i tak jak sugerowałeś do wyjścia z WinMain(). Za pierwszym razem aplikacja startuje normalnie. Po rzuceniu linkami w uruchomionego exe'ka w funkcji InitMainWnd() wykrywam próbę uruchomienia kolejnej instancji i przy jej wykryciu ustawiam: jednainstancja = FALSE ; teraz pętla w części głównej omija całą "windosową" część i kończy WinMain na return 0; Dzięki temu unikam wszelkiego migania. Testowałem i jest ok. Teraz muszę przemyśleć jak przekazywać linki z pierwszego uruchomienia i z rzuconych linków do części okienkowej. Załączam całość kodu na dole. @DejaVu W moim przypadku najlepiej sprawdzają się TCHAR w takiej postaci. Ten kod to uproszczona część większej całości i wrzuciłem tutaj działające minimum, żeby pokazać z czym mam problem i nie zaciemniać perspektywy windosową częścią i innymi zbędnymi dodatkami. W pierwszej fazie miałem to wszystko mieszane, trochę TCHAR trochę wchar_t. Przy "sprzątaniu" kodu konwersja do wchar_t okazała się bardziej upierdliwa (kompilator bulwersował się o różne rzutowania) i okazało się że makro _T( ) zadziało wszędzie, dlatego jest jak jest. Przy okazji poznałem różne mikrosoftowe wynalazki typu: LPSTR, LPCSTR LPWSTR, LPCWSTR, LPTSTR, LPCTSTR używane to tu to tam. #include <windows.h> #include <tchar.h> #include <commctrl.h> #include <string>
#pragma comment(lib,"comctl32.lib")
MSG msg; HWND hWnd; HINSTANCE hInst;
bool jednainstancja = TRUE;
const TCHAR * ApplicationTitle = _T( "Aplikacja" );
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 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" ) { MessageBox( NULL, fname, L"Rzucone pliki", MB_OK ); } delete[ ] fname; } } DragFinish( hDrop ); }
void LaunchCommandLineElements() { LPWSTR * szArgList; int argCount; szArgList = CommandLineToArgvW( GetCommandLine(), & argCount ); for( int i = 1; i < argCount; i++ ) { TCHAR * plik = szArgList[ i ]; std::wstring wplik( plik ); std::wstring rozszerzenie; rozszerzenie = GetExtension( wplik ); if( rozszerzenie == L"txt" ) { MessageBox( NULL, szArgList[ i ], L"Zawartość listy argumentów", MB_OK ); } } LocalFree( szArgList ); }
static LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) { switch( msg ) { case WM_DROPFILES: { DropFile( wParam ); 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 = _T( "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, _T( "Nie utworzono żadnej klasy!" ), _T( "Błąd" ), MB_ICONERROR ); } HWND hOther = FindWindow( wClass.lpszClassName, NULL ); if( hOther ) { jednainstancja = FALSE; PostQuitMessage( 0 ); } }
HWND CreateMainWnd() { return CreateWindowEx( WS_EX_CLIENTEDGE, _T( "My Window Class" ), ApplicationTitle, WS_OVERLAPPED | WS_CAPTION | WS_BORDER | WS_MINIMIZEBOX | WS_SYSMENU, 0, 0, 400, 400, NULL, NULL, hInst, NULL ); }
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int showCmd ) { LaunchCommandLineElements(); InitMainWnd(); if( jednainstancja ) { hWnd = CreateMainWnd(); if( !hWnd ) { int nResult = GetLastError(); MessageBox( NULL, _T( "Nie utworzono żadnego okna!" ), _T( "Błąd" ), MB_ICONERROR ); } 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 ); } else { } } } return 0; }
|
|
« 1 » |