Table of Contents
Describe the things you need to be aware of when developing MiniGUI apps for 64-bit platforms.
Table of Contents
Overview
In MiniGUI 3.2.0, we enhanced MiniGUI to support 64-bit platforms.
Although many embedded devices or IoT devices are still using 32-bit processors, we often need to develope MiniGUI apps on a desktop computer (almost all desktop computer are now using 64-bit CPU), and then build the code for the target system through a cross compiler. Therefore, the code of a MiniGUI App needs to be portable, regardless of whether the target system is 32-bit or 64-bit, or even a big-endian or little-endian system.
This document focuses on some of the changes that MiniGUI introduces to support 64-bit systems and what you need to be aware of when writing portable code.
Changes of Data Types
Changes of handle types
All handle types, including GHANDLE, HWND, HDC, etc.,
are now defined as aliases of PVOID (typedef void* PVOID).
You may need to check your code to reflect this change.
Changes of integer types
The type of DWORD now has pointer precision. That is,
the size of DWORD will be 4 bytes on 32-bit platform, and 8 bytes on
64-bit platform.
Similarly, WPARAM and LPARAM now have pointer precision.
WORD and SWORD has a half of pointer precision. The size of these
two types is 2 bytes on 32-bit platform, and 4 bytes on 64-bit platform.
RGBCOLOR now is defined as an alias of DWORD32 (see below).
Note that the type of BYTE always has the size of 8-bit on both
32-bit and 64-bit platforms.
New integer types
We introduce DWORD32 and SDWORD32 types,
which have the size of 4 bytes on both 32-bit and 64-bit platforms.
You should use these types when reading/writing 32-bit integers from
a binary files for the portability. Of course, you can also use
Uint32 or Sint32 types.
Similarly, we introduce WORD16 and SWORD16 types,
which have the size of 2 bytes on both 32-bit and 64-bit platforms.
You should use these types when reading/writing 16-bit integers from
a binary file for the portability. Of course, you can also use
Uint16 or SUint16 types.
LRESULT is defined for window callback procedure, and it has
pointer precision.
LINT is a new integer type with pointer precision.
QDWORD is a new integer type for a quauter of DWORD. This type is
16-bit long on 64-bit architecture, and 8-bit long on 32-bit.
API changes
Integer macros
MAKEWPARAM: this new macro makes a WPARAM value by using four bytes.
On the contrary, FIRSTBYTE, SECONDBYTE, THIRDBYTE, and FOURTH
macros get the four bytes from a WPARAM or a Uint32 value.
MAKEWORD16: this new macro makes a 16-bit word by using two bytes.
Meanwhile, MAKEWORD makes a 16-bit word on 32-bit platform, and a 32-bit
word on 64-bit platform.
Note that MAKELONG macro always makes a DWORD integer, which has pointer
precision. Meanwhile, MAKELONG32 macro makes a Uint32 integer.
Note that MakeRGB and MakeRGBA macros always make DWORD32 integers.
In contract, GetRValue, GetRValue, GetBValue, GetAValue always
get red, green, blue, and alpha components from a DWORD32 integer
respectively.
New macros for QDWORD:
MAKEDWORD: Make a DWROD from four QDWORDs.FIRST_QDWORD: get the first (LSB) QDWORD from a DWORD.SECOND_QDWORD: get the second (LSB) QDWORD from a DWORD.THIRD_QDWORD: get the third (LSB) QDWORD from a DWORD.FOURTH_QDWORD: get the fourth (LSB) QDWORD from a DWORD.
Structures and functions
The main changes in structure and functions:
-
We now use a
UINTinstead of anintinteger for the message identifier. -
We now use a
DWORDinteger for the time tick count. Meanwhile, you can create 64 timers on 64-bit platform. -
We now use a
LRESULTinteger for the return value of a window callback procedure. Now it is safe to return a pointer from the callback procedure on 64-bit platform. This is a very important change, and it will break the source compatibilty of your code. You should check the source code (use gcc option-Wall) carefully. -
We now use a
LINTinteger for the identifier of a timer. So you can pass a pointer as the identifier of the timer on 64-bit platform. mGNCS uses MiniGUI timer in this manner. -
We now use a
LINTinteger for the identifier of a control/widget and a menu item. So you can pass a pointer as the identifier of the timer on 64-bit platform. mGNCS works in this manner.
Message
The strcuture MSG and all message-related functions changed.
For example, the prototype of SendMessage changed from
int SendMessage (HWND hWnd, int nMsg, WPARAM wParam, LPARAM lParam)
to
LRESULT SendMessage (HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
IMPORTANT NOTE
For best portability, you should use FIRSTBYTE to FOURTHBYTE macros
to get the bytes of a character when you extract the bytes from WPARAM
parameter of a MSG_CHAR message:
case MSG_CHAR:
unsigned char ch_buff [4];
unsigned char ch_buff [0] = FIRSTBYTE(wParam);
unsigned char ch_buff [1] = SECONDBYTE(wParam);
unsigned char ch_buff [2] = THIRDBYTE(wParam);
unsigned char ch_buff [3] = FOURTHBYTE(wParam);
Window callback procedure
Furthermore, the structure and functions to register window class,
create main window, and create dialog box changed. For example, the prototype
of WNDPROC changed from
typedef int (* WNDPROC)(HWND, int, WPARAM, LPARAM)
to
typedef LRESULT (* WNDPROC)(HWND, UINT, WPARAM, LPARAM)
Therefore, the prototype of DefaultWindowProc changed from
int DefaultWindowProc (HWND hWnd, int message, WPARAM wParam, LPARAM lParam)
to
LRESULT DefaultWindowProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
All main window procedures, control class procedures, and dialog box procedures defined by your app should change the implementation to reflect the changes above.
IMPORTANT NOTE
Do not cast the result returned by a window procedure to int on 64-bit
platform, unless you know what your are doing.
Notification callback procedure
The type of notification callback changes from:
typedef void (* NOTIFPROC) (HWND hwnd, int id, int nc, DWORD add_data);
to
typedef void (* NOTIFPROC) (HWND hwnd, LINT id, int nc, DWORD add_data);
IMPORTANT NOTE
If you use MSG_COMMAND message to handle the notification sent from children
controls, you should make sure the identifier is small enough on 64-bit
platform. Because MiniGUI packs the identifier and the notification code
in the WPARAM parameter:
MSG_COMMAND
int id = LOWORD(wParam);
int code = HIWORD(wParam);
HWND hwnd = (HWND)lParam;
The code above will not work on 64-bit if you use a pointer as the identifier of the control.
Therefore, we recommend strongly that you use a NOTIFYPOROC callback to
handle the notification sent from controls. To do this, please call
SetNotificationCallback function to set the notification callback function.
Time and timer
The prototype of GetTickCount changed from
unsigned int GetTickCount (void)
to
DWORD GetTickCount (void);
And the prototye of TIMERPROC changed from
typedef BOOL (* TIMERPROC)(HWND, int, DWORD)
to
typedef BOOL (* TIMERPROC)(HWND, LINT, DWORD)
Writing Portable Code
In this section, we give some tips to write portable code.
Use correct data types
When we read/write data from/to a file, we must use the data types suffixed by 16, 32, or 64. For example, the following code writes a Windows BMP file header to a file:
MGUI_RWwrite (fp, &bmfh.bfType, sizeof (WORD16), 1);
MGUI_RWwrite (fp, &bmfh.bfSize, sizeof (DWORD32), 1);
MGUI_RWwrite (fp, &bmfh.bfReserved1, sizeof (WORD16), 1);
MGUI_RWwrite (fp, &bmfh.bfReserved2, sizeof (WORD16), 1);
MGUI_RWwrite (fp, &bmfh.bfOffBits, sizeof (DWORD32), 1);
Invalid pointer values
We often use NULL for an invalid pointer type value. However, we may
also use 0xFFFFFFFF for this purpose. This works on 32-bit architecture,
but not 64-bit.
Instead, we sugguest to use (-1) instead of 0xFFFFFFFF for the invalid
integer or pointer type value for good portability:
#define HDC_INVALID ((HDC)-1)
Quick links