Προς το περιεχόμενο

Win32 API + C + UpDown controls


Προτεινόμενες αναρτήσεις

Δημοσ.

Ευχαριστώ παπι!

 

Να το κάνω όμως λίγο (έως πολύ :lol:) πιο συγκεκριμένο το ερώτημα με την ελπίδα πως θα προσελκύσει περισσότερες απαντήσεις (ενδεχομένως και βελτιώσεις).

 

Λοιπόν, για τα hot-areas των ζώων στο κεντρικό παράθυρο (που τα κάνει κλικ ο χρήστης και "μιλάνε") έχω τον εξής πίνακα...

 

Πίνακας HotAnimals:

 

>
typedef struct HotAnimal {
RECT	rect1, rect2, rect3;
TCHAR	name[ MAXLEN_szTEXT ];
TCHAR	ressound[ MAXLEN_szTEXT ];
} HotAnimal;

...
HotAnimal hotanimals[ MAX_HOTANIMALS ] = {
	{	{246,91, 283,139}, {-1,-1,-1,-1}, {-1,-1,-1,-1},
		TEXT("\0"),	// Bear
		TEXT("WAV_BEAR")
	},
	{	{286,103, 310,138}, {281,77, 313,102}, {277,78, 282,91},
		TEXT("\0"),	// Buffalo
		TEXT("WAV_BUFFALO")
	},
	{	{203,99, 226,138},  {-1,-1,-1,-1}, {-1,-1,-1,-1},
		TEXT("\0"),	// Cougar
		TEXT("WAV_COUGAR")
	},
	{	{227,82, 245,138}, {224,56, 257,81}, {-1,-1,-1,-1},
		TEXT("\0"),	// Deer
		TEXT("WAV_DEER")
	},
	{	{110,18, 120,46}, {120,11, 224,81}, {155,82, 203,139},
		TEXT("\0"),	// Elephant
		TEXT("WAV_ELEPHANT")
	},
	{ 	{4,6, 109,75}, {99,54, 119,87}, {-1,-1,-1,-1},
		TEXT("\0"),	// Giraffe
		TEXT("WAV_GIRAFFE")
	},
	{	{335,91, 383,138},  {-1,-1,-1,-1},  {-1,-1,-1,-1},
		TEXT("\0"),	// Hippo
		TEXT("WAV_HIPPO")
	},
	{	{122,80, 138,135}, {-1,-1,-1,-1}, {-1,-1,-1,-1},
		TEXT("\0"),	// Kangaroo
		TEXT("WAV_KANGAROO")
	},
	{	{91,87, 119,138}, {-1,-1,-1,-1}, {-1,-1,-1,-1},
		TEXT("\0"),	// Lion
		TEXT("WAV_LION")
	},
	{	{309,107, 335,139}, {313,100, 331,107}, {-1,-1,-1,-1},
		TEXT("\0"),	// Panda
		TEXT("WAV_PANDA")
	},
	{	{10,74, 91,139}, {-1,-1,-1,-1}, {-1,-1,-1,-1},
		TEXT("\0"),	// Rhino
		TEXT("WAV_RHINO")
	},
	{	{138,104, 156,139}, {-1,-1,-1,-1}, {-1,-1,-1,-1},
		TEXT("\0"),	// Wolf
		TEXT("WAV_WOLF")
	}
};

 

 

Αυτός ο πίνακας χρειάζεται initialization, τόσο για τα πεδία .name (τα οποία τα φορτώνω από το STRINGTABLE του resource) όσο και για τη σύνδεση tooltips στα hot-areas τους (τα 3 .rect πεδία). Αν και δεν νομίζω πως για το ερώτημα μου χρειάζεται να παραθέσω τη ρουτίνα αρχικοποίησης, το κάνω σε περίπτωση που φανεί χρήσιμη σε κάποιον (ή σε περίπτωση που επιδέχεται προτεινόμενων βελτιώσεων)...

 

wmain_init_hotanimals():

 

>
/*******************************************************//**
* @brief		Init the hot-animal names (the animals consisting the bitmap shown
*			in the main window) and assign balloon tooltips to their rect areas.
* @param hWnd		The handle of the main window.
* @param hotanimals[]	The array of the hot-animals.
* @return		FALSE on error, TRUE otherwise.
***********************************************************
*/
static BOOL wmain_init_hotanimals( HWND hWnd, HotAnimal hotanimals[] )
{
int 	i = 0;
TCHAR 	*cp = NULL;
HINSTANCE hInst = GetModuleHandle(NULL);

if ( NULL == hWnd || ! hotanimals ) {
	myRESERR_BOX( IDSTR_ERR_GUI_INTERNAL );
	return FALSE;
}

/* Init hot-animal names from resource */

ZeroMemory( g_szBuffer, MAXLEN_szTEXT );
LoadString( hInst, IDSTR_HOTANIMAL_NAMES, g_szBuffer, MAXLEN_szTEXT );

for (
i=0, cp = _tcstok( g_szBuffer, TEXT(" ") );
i < MAX_HOTANIMALS && cp;
i++, cp = _tcstok(NULL, TEXT(" "))
)
	_tcsncpy( hotanimals[i].name, cp, MAXLEN_szTEXT - sizeof(TCHAR) );

/* Add balloon tooltips on the hotanimals rectangles */

ZeroMemory( g_szBuffer, MAXLEN_szTEXT );
for (i=0; i < MAX_HOTANIMALS; i++)
{
	LoadString( hInst, IDSTR_CLICK_FOR_ANIMAL_SOUND, g_szBuffer, MAXLEN_szTEXT );
	if ( -1 != hotanimals[i].rect1.left )
	{
		if ( !Rect_AddBalloon(
				&hotanimals[i].rect1,
				hWnd, hotanimals[i].name,
				g_szBuffer,
				1 )
		)
			myRESERR_BOX( IDSTR_ERR_WND_NOCREATE );
	}
	if ( -1 != hotanimals[i].rect2.left )
	{
		if ( !Rect_AddBalloon(
				&hotanimals[i].rect2,
				hWnd, hotanimals[i].name,
				g_szBuffer,
				1 )
		)
			myRESERR_BOX( IDSTR_ERR_WND_NOCREATE );
	}
	if ( -1 != hotanimals[i].rect3.left )
	{
		if ( !Rect_AddBalloon(
				&hotanimals[i].rect3,
				hWnd, hotanimals[i].name,
				g_szBuffer,
				1 )
		)
			myRESERR_BOX( IDSTR_ERR_WND_NOCREATE );
	}
}

return TRUE;
}

 

Σημείωση: τα ονόματα των ζώων στο resource είναι σε ένα μονοκόμματο string, διαχωρισμένα με spaces, για αυτό κάνω _tcstok ( ή strtok είναι αυτή).

 

Αφού αρχικοποιηθεί ο πίνακας, χρησιμοποιείται στην callback ρουτίνα του παραθύρου, και συγκεκριμένα όταν ο χρήστης πατήσει το ποντίκι (άρα στο WM_LBUTTONDOWN message)...

 

WndMain_Callback():

 

>
LRESULT CALLBACK WndMain_Callback( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
static HBITMAP hBmpWmain;				// must be static
/* previous way: define hotanimals[] here, init it inside WndMain_OnCreate() and use it inside WndMain_OnMouseDown */

switch ( uMsg )
{
	case WM_CREATE:					// hBmpWmain gets loaded
		WndMain_OnCreate( hWnd, /*hotanimals,*/ &hBmpWmain );
		break;

	case WM_PAINT:					// hBmpWmain gets painted
		WndMain_OnPaint( hWnd, hBmpWmain );
		break;

	case WM_LBUTTONDOWN:				// mouse down
//			WndMain_OnMouseDown( hWnd, lParam, hotanimals );
		WndMain_OnMouseDown( hWnd, lParam );
		break;

	case WM_COMMAND:				// menu command
		WndMain_OnCommand( hWnd, wParam );
		break;

	case WM_CLOSE:
		DestroyWindow( hWnd );
		break;

	case WM_DESTROY:
#if 1
		EnumPropsEx(
			hWnd, (PROPENUMPROCEX)WndMain_DelProp_Callback, (LPARAM) 0);
#else
		RemoveProp( hWnd, TEXT("PROP_HOTANIMALS") );
		RemoveProp( hWnd, TEXT("PROP_TTADATADLG") );
#endif
		DeleteObject( hBmpWmain );		// don't forget!
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

return 0;
}

 

Σημείωση: έχω αφήσει επίτηδες σε σχόλια ότι αφορά τον πίνακα "hotanimals" ως ένδειξη εναλλακτικού τρόπου περάσματός του στις διάφορες ρουτίνες.

 

Αυτά σε ότι αφορά το υπόβαθρο για το ερώτημα. Πάμε τώρα στην ουσία.

 

Ο πίνακας hotanimals μπορεί ...

 

  1. να οριστεί ως global, να αρχικοποιηθεί π.χ. στην WinMain() (ή στην WndMain_Callback() ) και να χρησιμοποιείται απευθείας μέσα σε όποια ρουτίνα τον χρειάζεται
  2. να οριστεί local στην ρουτίνα WndMain_Callback(), κατόπιν να αρχικοποιείται στο WM_CREATE message και να περνιέται ως όρισμα της WndMain_OnMouseDown() ... είναι αυτό που έχω ως σχόλιο στον κώδικα της WndMain_Callback() στον παραπανω κώδικα.
  3. να οριστεί είτε global είτε local στην WinMain() και κατόπιν να περαστεί ως δείκτης στα extra-bytes της WNDCLASS του κεντρικού παραθύρου, με την αρχικοποίησή του να μπορεί να γίνει είτε στην WinMain() είτε στο WM_CREATE message της WndMain_Callback()
  4. να οριστεί είτε global είτε local στην WinMain() και κατόπιν να περαστεί ως δείκτης ως property του κεντρικού παραθύρου, με την αρχικοποίησή του να μπορεί να γίνει και πάλι είτε στην WinMain() είτε στο WM_CREATE message της WndMain_Callback()

Το 1 και το 2 είναι τα προφανή (χρησιμοποιούσα περισσότερο το 2 μέχρι τώρα), το 3 δεν το έχω "εξερευνήσει" ακόμα, είναι αυτό που είπε ο παπι στο προηγούμενο ποστ.

 

Το 4 είναι αυτό που αντιστοιχεί στον παραπάνω κώδικα, με χρήση των SetProp() και GetProp(). Συγκεκριμένα ο κώδικας της WinMain() είναι ο παρακάτω...

WinMain():

 

>
int WINAPI WinMain(
HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
HWND	hWnd = NULL;
MSG 	uMsg;
TTADataDlg tadataDlg;
HotAnimal hotanimals[ MAX_HOTANIMALS ] = {
	{	{246,91, 283,139}, {-1,-1,-1,-1}, {-1,-1,-1,-1},
		TEXT("\0"),	// Bear
		TEXT("WAV_BEAR")
	},
	{	{286,103, 310,138}, {281,77, 313,102}, {277,78, 282,91},
		TEXT("\0"),	// Buffalo
		TEXT("WAV_BUFFALO")
	},
	{	{203,99, 226,138},  {-1,-1,-1,-1}, {-1,-1,-1,-1},
		TEXT("\0"),	// Cougar
		TEXT("WAV_COUGAR")
	},
	{	{227,82, 245,138}, {224,56, 257,81}, {-1,-1,-1,-1},
		TEXT("\0"),	// Deer
		TEXT("WAV_DEER")
	},
	{	{110,18, 120,46}, {120,11, 224,81}, {155,82, 203,139},
		TEXT("\0"),	// Elephant
		TEXT("WAV_ELEPHANT")
	},
	{ 	{4,6, 109,75}, {99,54, 119,87}, {-1,-1,-1,-1},
		TEXT("\0"),	// Giraffe
		TEXT("WAV_GIRAFFE")
	},
	{	{335,91, 383,138},  {-1,-1,-1,-1},  {-1,-1,-1,-1},
		TEXT("\0"),	// Hippo
		TEXT("WAV_HIPPO")
	},
	{	{122,80, 138,135}, {-1,-1,-1,-1}, {-1,-1,-1,-1},
		TEXT("\0"),	// Kangaroo
		TEXT("WAV_KANGAROO")
	},
	{	{91,87, 119,138}, {-1,-1,-1,-1}, {-1,-1,-1,-1},
		TEXT("\0"),	// Lion
		TEXT("WAV_LION")
	},
	{	{309,107, 335,139}, {313,100, 331,107}, {-1,-1,-1,-1},
		TEXT("\0"),	// Panda
		TEXT("WAV_PANDA")
	},
	{	{10,74, 91,139}, {-1,-1,-1,-1}, {-1,-1,-1,-1},
		TEXT("\0"),	// Rhino
		TEXT("WAV_RHINO")
	},
	{	{138,104, 156,139}, {-1,-1,-1,-1}, {-1,-1,-1,-1},
		TEXT("\0"),	// Wolf
		TEXT("WAV_WOLF")
	}
};


/* Register the main window class (only if this is the 1st instance)*/
if ( !hPrevInstance )
	if ( !InitApplication(hInstance) )
		return FALSE;

/* Create & display the main window */
hWnd = InitInstance(hInstance, nCmdShow);
if ( NULL == hWnd ) 
	return FALSE;

/* Init user-defined application data */
if ( !init_app_data(hWnd, hotanimals, &tadataDlg) )
	return FALSE;

/* The message loop */
while ( GetMessage(&uMsg, NULL, 0, 0) > 0 )
{
	TranslateAccelerator(
		hWnd,
		LoadAccelerators( hInstance, MAKEINTRESOURCE(ACC_MNMAIN) ),
		&uMsg );
	TranslateMessage( &uMsg );
	DispatchMessage( &uMsg );
}

return uMsg.wParam;
//		UNREFERENCED_PARAMETER(lpCmdLine); 
}

 

Σημείωση: εναλλακτικά ο πίνακας μπορεί να οριστεί ως global αν ξέρουμε πως υπάρχει πολύ περιορισμένο stack-space.

 

Η σημαντική ρουτίνα για το παρόν ποστ είναι η init_app_data() όπου ο πίνακας hotanimals εκτός της αρχικοποίησής του ορίζεται και ως Property του κεντρικού παραθύρου, με χρήση της SetProp() και ονομασία TEXT("PROP_HOTANIMALS") (τα properties τα φαντάζομαι σαν entries ενός hash-table, με keys την ονομασία τους)...

 

init_app_data():

 

>
BOOL init_app_data( HWND hWndMain, HotAnimal hotanimals[], TTADataDlg *tadataDlg )
{
if ( NULL == hWndMain || !hotanimals || !tadataDlg )
	return FALSE;

srand( time(NULL) );

if ( !dlgTAData_Init( hWndMain, tadataDlg ) )
	return FALSE;
if ( !SetProp( hWndMain, TEXT("PROP_TADATADLG"), (HANDLE)(TTADataDlg *)tadataDlg ) )
	return FALSE;

if ( !wmain_init_hotanimals(hWndMain, hotanimals) )
	return FALSE;
if ( !SetProp( hWndMain, TEXT("PROP_HOTANIMALS"), (HANDLE)(HotAnimal *)hotanimals ) )
	return FALSE;

return TRUE;
}

 

Σημείωση: το tadataDlg είναι κάποια άλλα user-defined data, ένα struct είναι που αφορά εκείνο το τεράστιο το Dialog για το datafile του κάθε ζώου. Το περνάω κι αυτό ως ξεχωριστό property του κεντρικού παραθύρου, και κατόπιν όταν ανοίγω το Dialog το κάνω με την DialogBoxParam() η οποία επιτρέπει να της περάσουμε ως τελευταίο όρισμα έναν δείκτη, τον οποίο μας τον επιστρέφει στο lParam του WM_INITDIALOG message, μέσα στην callback συνάρτηση του Dialog.

 

Οπότε, επιστρέφοντας στον hotanimals, όποτε τον χρειαζόμαστε καλούμε...

 

> (HotAnimal *) GetProp(hWnd, TEXT("PROP_HOTANIMALS") );

με hWnd να είναι το handle του κεντρικού μας παραθύρου, όπως κάνω στο παραπάνω παράδειγμα στην WndMain_OnMouseDown() ... δεν έχω δώσει κώδικα για αυτήν, δεν νομίζω πως χρειάζεται (και προφανώς ομοίως καλούμε (TTADataDld *) GetProp(hWnd, TEXT("PROP_TADATADLG") ); όποτε χρειαζόμαστε το struct tadataDlg).

 

Μια "επιπλοκή" με τα Window properties είναι πως πρέπει να τα καταστρέψουμε χειροκίνητα πριν καταστρέψουμε το ίδιο το παράθυρο. Στον παραπάνω κώδικα το κάνω στο WM_DESTROY message, με κλήση της...

 

> EnumPropsEx( hWnd, (PROPENUMPROCEX)WndMain_DelProp_Callback, (LPARAM) 0 );

η οποία ως 2ο όρισμα παίρνει μια callback συνάρτηση την οποία την εφαρμόζει διαδοχικά σε όλα τα properties του παραθύρου (με χρήση της RemoveProp() )... έχω αφήσει σε σχόλιο την εναλλακτική χειροκίνητων κλήσεων της RemoveProp()...

 

WndMain_DelProp_Callback():

 

>
BOOL CALLBACK WndMain_DelProp_Callback( 
HWND	hwndSubclass,			// handle of window with property
TCHAR	*szString,			// property string or atom
HANDLE	hData				// data handle
)
{ 
hData = RemoveProp(hwndSubclass, szString); 
//
// if appropriate, free the handle hData
//

return TRUE; 
}

 

Σημείωση: για κάποιον λόγο που δεν καταλαβαίνω αν αφαιρέσω το PROPENUMPROCEX casting της WndMain_DelProp_Callback() όταν την περνάω 2ο όρισμα στην κλήση της EnumPropsEx() τρώω data-type conflict warning (που στην Pelles-C είναι error... δεν κάνει compile δλδ).

 

Δεν ξέρω πόσοι είχατε το ... κουράγιο να διαβάσατε όλο το ποστ (και κυρίως τον κώδικα). Επίσης δεν ξέρω πόσοι έχετε την απαραίτητη εμπειρία (επαγγελματική ή μη) να μου απαντήσετε, αλλά με τόσους διαφορετικούς τρόπους (ενδεχομένως να υπάρχουν κι άλλοι που δεν τους ξέρω) είναι νομίζω λογικό να αναρωτιέμαι ποιος θεωρείται good και ποιος bad practice και κυρίως γιατί.

 

Παρουσίασα αναλυτικά τον τρόπο με τα Window properties όχι μόνο επειδή τον ανακάλυψα πολύ πρόσφατα, αλλά κι επειδή μου φαντάζει ιδιαίτερα δομημένος & εύχρηστος συγκριτικά με τον παραπλήσιο τρόπο των extra-bytes της WNDCLASS (αυτ που είπε ο παπι). Επίσης δεν είμαι σίγουρος αν τον έχω υλοποιήσει σωστά. Για παράδειγμα, υπάρχει κάποιο πλεονέκτημα/μειονέκτημα αν το initialization του hotanimals το κάνω στο WM_CREATE message της WndMain_Callback() αντί για την WinMain() που το κάνω τώρα;

 

Παρόλο που δεν τον έχω εξερευνήσει ακόμα τον τρόπο του παπι, με τα πρώτα που διάβασα κατάλαβα ότι αφενός είναι ταχύτερος (αφού στα extra-bytes δουλεύεις απευθείας με memory-offset αντί για key-string hashing) αλλά αν το 'πιασα σωστά προϋποθέτει να φτιάξεις και 2η callback ρουτίνα για το παράθυρο, η οποία θα πιάνει το WM_NCREATE message (που προηγείται του WM_CREATE) θα κάνεις ότι είναι να κάνεις με τα user-data και μετά θα καλείς την κανονική callback του παραθύρου (αν και μου φαντάζει πολύ κουφό κάτι τέτοιο).

  • Απαντ. 32
  • Δημ.
  • Τελ. απάντηση

Συχνή συμμετοχή στο θέμα

Συχνή συμμετοχή στο θέμα

Δημοσιευμένες Εικόνες

Δημοσ.

Για τα SetProp εχε το νου σου οτι παιρνουν και ATOM. Δηλαδη αν περασεις το 0x0000ffff & myValue τοτε θα γινει συγκριση με βαση το myValue που μπορει να ειναι ενα int define

πχ

>SetProp(hWnd,MAKEINTATOM(1),(HANDLE)10);
int i = (int)GetProp(hWnd,MAKEINTATOM(1));

 

Δημοσ.

Για τα SetProp εχε το νου σου οτι παιρνουν και ATOM. Δηλαδη αν περασεις το 0x0000ffff & myValue τοτε θα γινει συγκριση με βαση το myValue που μπορει να ειναι ενα int define

πχ

>SetProp(hWnd,MAKEINTATOM(1),(HANDLE)10);
int i = (int)GetProp(hWnd,MAKEINTATOM(1));

 

Καλή μαγκιά, οπότε γίνεται απευθείας int-indexing των properties αντί για το χρονοβόρο string-indexing. Γιατί παίρνεις μόνο τα 16-μπιτς από το myValue?

 

ΥΓ. Σχετικά με τα extra-bytes της WNDCLASS, όντως χρειάζεται να φτιάξεις 2η callback συνάρτηση για το παράθυρο προκειμένου να τα χρησιμοποιήσεις ή έχω καταλάβει λάθος;

 

Κάπου πήρε το μάτι μου επίσης πως και η CreateWindow() σε αφήνει να ορίσεις ένα user-defined value στο lParam το οποίο κατόπιν στο επιστρέφει στο WM_CREATE μέσα στο CREATESTRUCT... δηλαδή 5ος τρόπος :lol:

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα

  • Δημιουργία νέου...