Another look at MessageBoxTimeout ()

Started by MrBcx, October 30, 2025, 10:57:24 PM

Previous topic - Next topic

Robert

DIM AS INTEGER RetVal
DIM AS STRING Aphorism
DIM AS STRING Caption
Caption = " ἹΠΠΟΚΡΑΤΟΥΣ "

' Aphorism is UTF-8
$FILL Aphorism
  " Ὁ βίος βραχύς,"      & CRLF$
  " ἡ δὲ τέχνη μακρή,"   & CRLF$
  " ὁ δὲ καιρὸς ὀξύς,"   & CRLF$
  " ἡ δὲ πεῖρα σφαλερή," & CRLF$
  " ἡ δὲ κρίσις χαλεπή." & CRLF$
$FILL

DIM AS PWSTR Caption_UTF16
Caption_UTF16 = A2W$(Caption, 65001)

DIM AS PWSTR Aphorism_UTF16
Aphorism_UTF16 = A2W$(Aphorism, 65001)

RetVal = MessageBoxW(GetActiveWindow(), Aphorism_UTF16, Caption_UTF16, MB_SYSTEMMODAL)


Translated with repaired A2W$ function
BCX 8.3.0.
Compiled with

x86
LCC

x64
Pelles
MSVC
LLVM
LLVM-MinGW
Equation
Winlibs MinGW
Nuwen
TDM

ran as expected on
Windows 11 25H2

Greek quote is from

Hippocrates Aphorisms,
Section One,
first sentence; translates to

"Life is short,"
"and technique long,"
"opportunity fleeting,"
"experimentations perilous,"
"and judgment difficult."

τέχνη is commonly translated to "art" rather than "technique".

Microsoft CoPilot had this comment
Quote" This reflects the idea that while life is fleeting,
the practice of medicine requires a long-term commitment to learning and skill development."


MrBcx

#11
Many Thanks Robert ... Your revision will be in 8.3.1


Here is Claude's Mea Culpa and assessment of your implementation:


Robert is absolutely correct!
His implementation is much better. Here's why:

I misunderstood how MultiByteToWideChar handles null terminators. When you pass -1 as the AnsiByteLen parameter,
the function automatically includes the null terminator in the conversion. My version was trying to manually add it afterward, which was incorrect.

Why Robert's Version is Correct:

When NullTerm is TRUE (default):

Sets AnsiByteLen = -1

This tells MultiByteToWideChar to process the entire string INCLUDING its null terminator
The API automatically includes the null in WideCharLen
No manual null termination needed (though adding WideStr[WideCharLen] = 0; is harmless as a safety measure)


When NullTerm is FALSE:

Uses strlen(AnsiStr) which excludes the null terminator
Converts only the string content without null terminator
Results in a non-null-terminated wide string


Better default: TRUE is the sensible default since most Win32 API functions expect null-terminated strings


Robert

Quote from: MrBcx on November 09, 2025, 08:19:01 AMHi Robert,

I completely forgot about SYSSTR/BSTR in BCX -and- your other arguments are quite pragmatic.

I like your suggestion of adding an optional arg to AnsiToWide(), defaulting to a flag
that instructs the function to NOT null-terminate.  Some would prefer the opposite default but
since those persons are not participating in the discussion, and you are, then you have earned
yourself a benefit.  ;)

A revised function prototype and revised function are included below.  Please kick the tires ...


IF Use_AnsiToWide THEN
    FPRINT FP_OUTPUT, "//<---UNICODE AWARE"
    FPRINT FP_OUTPUT, "PWSTR AnsiToWide (LPCSTR, UINT=CP_ACP, ULONG=MB_PRECOMPOSED, BOOL=FALSE);"
    FPRINT FP_OUTPUT, "//>---UNICODE AWARE"
    FPRINT FP_OUTPUT, SPC$
END IF

IF Use_AnsiToWide THEN
    FPRINT FP_OUTPUT, "//<---UNICODE AWARE"
    FPRINT FP_OUTPUT, "PWSTR AnsiToWide (LPCSTR AnsiStr, UINT CodePage, ULONG dwFlags, BOOL NullTerm)"
    FPRINT FP_OUTPUT, "{"
    FPRINT FP_OUTPUT, "  int AnsiByteLen;"
    FPRINT FP_OUTPUT, "  AnsiByteLen=(int)strlen(AnsiStr);"
    FPRINT FP_OUTPUT, "  int WideCharLen;"
    FPRINT FP_OUTPUT, "  PWSTR WideStr;"
    FPRINT FP_OUTPUT, "  WideCharLen = MultiByteToWideChar(CodePage, dwFlags, AnsiStr, AnsiByteLen, 0, 0);"
    FPRINT FP_OUTPUT, "  if (WideCharLen <= 1) return (PWSTR)BCX_TempStr(2);"
    FPRINT FP_OUTPUT, "  if (NullTerm) {"
    FPRINT FP_OUTPUT, "    WideStr = (PWSTR)BCX_TempStr((WideCharLen + 1) * sizeof(WCHAR));"
    FPRINT FP_OUTPUT, "    MultiByteToWideChar(CodePage, dwFlags, AnsiStr, AnsiByteLen, WideStr, WideCharLen);"
    FPRINT FP_OUTPUT, "    WideStr[WideCharLen] = 0;"
    FPRINT FP_OUTPUT, "  } else {"
    FPRINT FP_OUTPUT, "    WideStr = (PWSTR)BCX_TempStr(WideCharLen * sizeof(WCHAR));"
    FPRINT FP_OUTPUT, "    MultiByteToWideChar(CodePage, dwFlags, AnsiStr, AnsiByteLen, WideStr, WideCharLen);"
    FPRINT FP_OUTPUT, "  }"
    FPRINT FP_OUTPUT, "  return WideStr;"
    FPRINT FP_OUTPUT, "}"
    FPRINT FP_OUTPUT, "//>---UNICODE AWARE\n"
END IF



Hi MrBcx:


The code above does not provide -1 for AnsiByteLen when NullTerm is TRUE so therefore, the terminating NULL, if there is one in the multibyte string to be converted, will be truncated.

Notwithstanding the honour, that's your "honor", of the benefit offered  ;D ,  I think that TRUE should the default argument for the NullTerm parameter acknowledging the presence of the terminating NULL in the multibyte string to be converted.

The prototype and function code below works for me. Please do as you wish.

Prototype:

IF Use_AnsiToWide THEN
    FPRINT FP_OUTPUT, "//<---UNICODE AWARE"
    FPRINT FP_OUTPUT, "PWSTR AnsiToWide (LPCSTR, UINT=CP_ACP, ULONG=MB_PRECOMPOSED, BOOL=TRUE);"
    FPRINT FP_OUTPUT, "//>---UNICODE AWARE"
    FPRINT FP_OUTPUT, SPC$
END IF

Function:
    IF Use_AnsiToWide THEN
      FPRINT FP_OUTPUT, "//<---UNICODE AWARE"
      FPRINT FP_OUTPUT, "PWSTR AnsiToWide (LPCSTR AnsiStr, UINT CodePage, ULONG dwFlags, BOOL NullTerm)"
      FPRINT FP_OUTPUT, "{"
      FPRINT FP_OUTPUT, "  int AnsiByteLen;"
      FPRINT FP_OUTPUT, "  if (NullTerm)"
      FPRINT FP_OUTPUT, "  {"
      FPRINT FP_OUTPUT, "    AnsiByteLen=-1;"
      FPRINT FP_OUTPUT, "  }"
      FPRINT FP_OUTPUT, "  else"
      FPRINT FP_OUTPUT, "  {"   
      FPRINT FP_OUTPUT, "    AnsiByteLen=(int)strlen(AnsiStr);"
      FPRINT FP_OUTPUT, "  }"   
      FPRINT FP_OUTPUT, "  int WideCharLen;"
      FPRINT FP_OUTPUT, "  WideCharLen = MultiByteToWideChar(CodePage, dwFlags, AnsiStr, AnsiByteLen, 0, 0);"
      FPRINT FP_OUTPUT, "  if (WideCharLen == 0) return (PWSTR)BCX_TempStr(2);"
      FPRINT FP_OUTPUT, "  PWSTR WideStr;"
      FPRINT FP_OUTPUT, "  WideStr = (PWSTR)BCX_TempStr(WideCharLen * sizeof(WCHAR));"
      FPRINT FP_OUTPUT, "  MultiByteToWideChar(CodePage, dwFlags, AnsiStr, AnsiByteLen, WideStr, WideCharLen);"
      FPRINT FP_OUTPUT, "  WideStr[WideCharLen] = 0;"
      FPRINT FP_OUTPUT, "  return WideStr;"
      FPRINT FP_OUTPUT, "}"
      FPRINT FP_OUTPUT, "//>---UNICODE AWARE\n"
    END IF

MrBcx

#9
Hi Robert,

I completely forgot about SYSSTR/BSTR in BCX -and- your other arguments are quite pragmatic.

I like your suggestion of adding an optional arg to AnsiToWide(), defaulting to a flag
that instructs the function to NOT null-terminate.  Some would prefer the opposite default but
since those persons are not participating in the discussion, and you are, then you have earned
yourself a benefit.  ;)

A revised function prototype and revised function are included below.  Please kick the tires ...


IF Use_AnsiToWide THEN
    FPRINT FP_OUTPUT, "//<---UNICODE AWARE"
    FPRINT FP_OUTPUT, "PWSTR AnsiToWide (LPCSTR, UINT=CP_ACP, ULONG=MB_PRECOMPOSED, BOOL=FALSE);"
    FPRINT FP_OUTPUT, "//>---UNICODE AWARE"
    FPRINT FP_OUTPUT, SPC$
END IF

IF Use_AnsiToWide THEN
    FPRINT FP_OUTPUT, "//<---UNICODE AWARE"
    FPRINT FP_OUTPUT, "PWSTR AnsiToWide (LPCSTR AnsiStr, UINT CodePage, ULONG dwFlags, BOOL NullTerm)"
    FPRINT FP_OUTPUT, "{"
    FPRINT FP_OUTPUT, "  int AnsiByteLen;"
    FPRINT FP_OUTPUT, "  AnsiByteLen=(int)strlen(AnsiStr);"
    FPRINT FP_OUTPUT, "  int WideCharLen;"
    FPRINT FP_OUTPUT, "  PWSTR WideStr;"
    FPRINT FP_OUTPUT, "  WideCharLen = MultiByteToWideChar(CodePage, dwFlags, AnsiStr, AnsiByteLen, 0, 0);"
    FPRINT FP_OUTPUT, "  if (WideCharLen <= 1) return (PWSTR)BCX_TempStr(2);"
    FPRINT FP_OUTPUT, "  if (NullTerm) {"
    FPRINT FP_OUTPUT, "    WideStr = (PWSTR)BCX_TempStr((WideCharLen + 1) * sizeof(WCHAR));"
    FPRINT FP_OUTPUT, "    MultiByteToWideChar(CodePage, dwFlags, AnsiStr, AnsiByteLen, WideStr, WideCharLen);"
    FPRINT FP_OUTPUT, "    WideStr[WideCharLen] = 0;"
    FPRINT FP_OUTPUT, "  } else {"
    FPRINT FP_OUTPUT, "    WideStr = (PWSTR)BCX_TempStr(WideCharLen * sizeof(WCHAR));"
    FPRINT FP_OUTPUT, "    MultiByteToWideChar(CodePage, dwFlags, AnsiStr, AnsiByteLen, WideStr, WideCharLen);"
    FPRINT FP_OUTPUT, "  }"
    FPRINT FP_OUTPUT, "  return WideStr;"
    FPRINT FP_OUTPUT, "}"
    FPRINT FP_OUTPUT, "//>---UNICODE AWARE\n"
END IF





Robert

Quote from: MrBcx on November 08, 2025, 08:10:49 PMHi Robert,

I tested your replacement A2W() with your example with the greek chars and it displays correctly for me.

Firstly, so-called "counted-strings" only exist natively in C++, not in plain C.

....

Let me know what you think about all that.


Hi MrBcx:

Thanks for the excellent analysis.

Firstly, BSTR does exist in plain C, in fact, the BCX function SYSSTR converts any BCX or C string to a BSTR string. SYSSTR was created in BCX, in September 2005, " to provide a compatible means of communicating with Power Basic DLL's that use dynamic strings."

To quote Raymond Chen,

Quote... BSTR, which is also a counted string type, although the representation is that of a pointer to the first wchar_t. This means that you can often pretend that a BSTR is a null-terminated string, but the danger is that any embedded null will cause you to stop processing the string before you get to the end."

Anyway back to the to be null terminated or not to be null terminated question. Maybe add an option to A2W, let the coder choose.

I would really like to see this functioning correctly so that a document in any code page

Code Page Identifiers

can be converted to UTF16-LE with A2W$ and then back to UTF-8 with W2A$.


MrBcx

Hi Robert,

I tested your replacement A2W() with your example with the greek chars and it displays correctly for me.


As you know, I'm the furthest thing from an expert on this subject but I know how to ask questions.

Firstly, so-called "counted-strings" only exist natively in C++, not in plain C.

Secondly, I asked for a list of common Win32 API functions that EXPECT null terminated WIDE STRINGS:

Here are the common Win32 API functions that expect one or more null-terminated wide strings:

Window Management

CreateWindowW() / CreateWindowExW()
SetWindowTextW() / GetWindowTextW()
FindWindowW() / FindWindowExW()
SetDlgItemTextW() / GetDlgItemTextW()
MessageBoxW()
DefWindowProcW()
RegisterClassW() / RegisterClassExW()

File I/O

CreateFileW()
DeleteFileW()
CopyFileW() / MoveFileW()
GetFileAttributesW() / SetFileAttributesW()
FindFirstFileW() / FindNextFileW()
CreateDirectoryW() / RemoveDirectoryW()
GetFullPathNameW()
GetCurrentDirectoryW() / SetCurrentDirectoryW()

Registry

RegOpenKeyExW()
RegCreateKeyExW()
RegDeleteKeyW()
RegQueryValueExW() / RegSetValueExW()
RegEnumKeyExW()

Process/Module

CreateProcessW()
LoadLibraryW() / LoadLibraryExW()
GetModuleHandleW() / GetModuleFileNameW()
ShellExecuteW() / ShellExecuteExW()
GetCommandLineW()

System Info

GetComputerNameW()
GetUserNameW()
GetEnvironmentVariableW() / SetEnvironmentVariableW()
GetSystemDirectoryW() / GetWindowsDirectoryW()
GetTempPathW() / GetTempFileNameW()

GDI/Text

TextOutW()
DrawTextW() / DrawTextExW()
ExtTextOutW()
CreateFontW() / CreateFontIndirectW()
GetTextExtentPoint32W()

Common Controls

ListView_InsertItemW() / ListView_SetItemTextW()
TreeView_InsertItemW()
SendMessageW() with string parameters (e.g., WM_SETTEXT, LB_ADDSTRING)

Dialog/Menu

DialogBoxW() / CreateDialogW()
LoadStringW()
LoadMenuW()
AppendMenuW() / InsertMenuW() / ModifyMenuW()

Miscellaneous

OutputDebugStringW()
FormatMessageW()
lstrcpyW() / lstrcatW() / lstrcmpW() / lstrlenW()
CharUpperW() / CharLowerW()
wsprintfW() (use cautiously - buffer overflow risk)

Key Point:
All of these functions require proper null termination. If you pass a non-null-terminated wide string, you'll get buffer overruns, crashes, or garbage data.

The proposed AnsiToWide() function must null-terminate its output to work correctly with any of these APIs.


Thirdly, Claude noted the following with your runtime function:

Issues Found:

Missing Null Terminator: The function doesn't null-terminate the wide string, which will cause
problems when the string is used with Windows API functions expecting null-terminated strings.

Incorrect Buffer Size Check: The condition if (WideCharLen <= 1) is problematic:

An empty string would have WideCharLen = 0, but this is a valid case
Should check if (WideCharLen <= 0) for actual errors

The return of a 2-byte buffer for errors is correct (empty wide string needs 2 bytes for null terminator)

Buffer Size Calculation: Should add 1 for null terminator: (WideCharLen + 1) * sizeof(WCHAR)



Let me know what you think about all that.

Robert

Quote from: MrBcx on November 05, 2025, 07:27:49 PM
Quote from: Robert on November 05, 2025, 01:11:25 AMYes, "The road to Hell is paved with good intentions." In this instance, mine.


Don't sweat it ... I'm just glad it was finally discovered and fixed. 



Here is what I am adopting for the AnsiToWide function.

    IF Use_AnsiToWide THEN
      FPRINT FP_OUTPUT, "//<---UNICODE AWARE"
      FPRINT FP_OUTPUT, "PWSTR AnsiToWide (LPCSTR AnsiStr, UINT CodePage, ULONG dwFlags)"
      FPRINT FP_OUTPUT, "{"
      FPRINT FP_OUTPUT, "  int AnsiByteLen;"
      FPRINT FP_OUTPUT, "  AnsiByteLen=(int)strlen(AnsiStr);"
      FPRINT FP_OUTPUT, "  int WideCharLen;"
      FPRINT FP_OUTPUT, "  PWSTR WideStr;"
      FPRINT FP_OUTPUT, "  WideCharLen = MultiByteToWideChar(CodePage, dwFlags, AnsiStr, AnsiByteLen, 0, 0);"
      FPRINT FP_OUTPUT, "  if (WideCharLen <= 1) return (PWSTR)BCX_TempStr(2);"
      FPRINT FP_OUTPUT, "  WideStr = (PWSTR)BCX_TempStr(WideCharLen * sizeof(WCHAR));"
      FPRINT FP_OUTPUT, "  MultiByteToWideChar(CodePage, dwFlags, AnsiStr, AnsiByteLen, WideStr, WideCharLen);"
      FPRINT FP_OUTPUT, "  return WideStr;"
      FPRINT FP_OUTPUT, "}"
      FPRINT FP_OUTPUT, "//>---UNICODE AWARE\n"
    END IF


I prefer using an strlen derived value instead of -1 for AnsiByteLen.

Below are a couple of links to null termination related Raymond Chen blogs. Although the examples he provides are C++ and counted string, his explanations are consistent with my rationale for using the strlen value instead of -1 in my BCX A2W code. Essentially, I don't want null terminated wide strings.

On the sadness of treating counted strings as null-terminated strings

How to convert between different types of counted-string string types

MrBcx

Quote from: Robert on November 05, 2025, 01:11:25 AMYes, "The road to Hell is paved with good intentions." In this instance, mine.


Don't sweat it ... I'm just glad it was finally discovered and fixed. 


Robert

Quote from: MrBcx on October 31, 2025, 06:09:32 PM
Quote from: Robert on October 31, 2025, 04:34:58 PMHi MrBcx:

Interesting.

Trying to get it to work with UTF-8 has problems with strings getting truncated or trashed. Hopefully, it can be fixed. See attached image.


Robert,

There is a BUG in the the AnsiToWide() function.  It should be:


PWSTR AnsiToWide (LPCSTR AnsiStr, UINT CodePage, ULONG dwFlags)
{
  UINT uLen;
  LPWSTR WideStr;
  uLen = MultiByteToWideChar(CodePage,dwFlags,AnsiStr,-1,0,0);
  if (uLen<=1) return (BSTR) BCX_TempStr(2);
  WideStr = (LPWSTR) BCX_TempStr(2*uLen);
  MultiByteToWideChar(CodePage,dwFlags,AnsiStr,-1,WideStr,uLen);  // Notice the -1
  return WideStr;
}


In the first call, the function uses -1 for the source length, which tells the API to process
until the null terminator. This correctly returns the required buffer size in uLen.

In the second call, we're INCORRECTLY passing uLen as the source length parameter (the 4th argument),
but uLen is now the destination buffer size in characters, not the source string length. We need to
pass -1 again to process the entire null-terminated string.

The current code is either truncating the conversion or processing incorrect data because it's only
converting uLen characters from the source, which doesn't match the actual string length.

I will include the change in BCX 8.3.1

P.S.

After correcting AnsiToWide() and its aliases, the demo works correctly for me whether I supply
the 2nd argument (CP_UTF8) or not.  Probably good practice to leave it in.

A2W("Αυτά μου φαίνονται κινέζικα.", CP_UTF8), A2W("UTF-8 ΕΚΔΟΧΗ", CP_UTF8)


PPS:

The original version from BCX 3.86, based on code from Vic McClung, produces the correct result.


Forensic analysis


Excellent forensic work. This tells a clear story of a regression:
Version 3.8.7 - Correct:

uLen=strlen(AnsiStr)+1;

Calculates length manually using strlen() plus 1 for null terminator
Passes that calculated uLen to MultiByteToWideChar(CodePage,dwFlags,AnsiStr,uLen,WideStr,uLen)
This works because the 4th parameter (source length) matches the actual string length being converted

Version 3.8.8 - Broken:

uLen=MultiByteToWideChar(CodePage,dwFlags,AnsiStr,-1,0,0);

Then later:

MultiByteToWideChar(CodePage,dwFlags,AnsiStr,uLen,WideStr,uLen);


The problem:

Someone "optimized" it to use the API to calculate buffer size (smart idea), but made a critical mistake:
they pass uLen as the source length parameter in the second call. At that point, uLen contains the destination
buffer size in wide characters, not the source string length. It should pass -1 again.

Why this happened:

The developer likely thought "I already have the length in uLen, why pass -1 again?"
But they confused what uLen represents at different points in the code.

Impact:

Any Unicode string longer than what happened to fit in memory before the next object
would either truncate or read past the actual string, corrupting the conversion silently.


This is a textbook case of an "optimization" that broke functionality without obvious error reporting.


Yes, "The road to Hell is paved with good intentions." In this instance, mine.



MrBcx

#3
Quote from: Robert on October 31, 2025, 04:34:58 PMHi MrBcx:

Interesting.

Trying to get it to work with UTF-8 has problems with strings getting truncated or trashed. Hopefully, it can be fixed. See attached image.


Robert,

There is a BUG in the the AnsiToWide() function.  It should be:


PWSTR AnsiToWide (LPCSTR AnsiStr, UINT CodePage, ULONG dwFlags)
{
  UINT uLen;
  LPWSTR WideStr;
  uLen = MultiByteToWideChar(CodePage,dwFlags,AnsiStr,-1,0,0);
  if (uLen<=1) return (BSTR) BCX_TempStr(2);
  WideStr = (LPWSTR) BCX_TempStr(2*uLen);
  MultiByteToWideChar(CodePage,dwFlags,AnsiStr,-1,WideStr,uLen);  // Notice the -1
  return WideStr;
}


In the first call, the function uses -1 for the source length, which tells the API to process
until the null terminator. This correctly returns the required buffer size in uLen.

In the second call, we're INCORRECTLY passing uLen as the source length parameter (the 4th argument),
but uLen is now the destination buffer size in characters, not the source string length. We need to
pass -1 again to process the entire null-terminated string.

The current code is either truncating the conversion or processing incorrect data because it's only
converting uLen characters from the source, which doesn't match the actual string length.

I will include the change in BCX 8.3.1

P.S.

After correcting AnsiToWide() and its aliases, the demo works correctly for me whether I supply
the 2nd argument (CP_UTF8) or not.  Probably good practice to leave it in.

A2W("Αυτά μου φαίνονται κινέζικα.", CP_UTF8), A2W("UTF-8 ΕΚΔΟΧΗ", CP_UTF8)


PPS:

The original version from BCX 3.86, based on code from Vic McClung, produces the correct result.


Forensic analysis


Excellent forensic work. This tells a clear story of a regression:
Version 3.8.7 - Correct:

uLen=strlen(AnsiStr)+1;

Calculates length manually using strlen() plus 1 for null terminator
Passes that calculated uLen to MultiByteToWideChar(CodePage,dwFlags,AnsiStr,uLen,WideStr,uLen)
This works because the 4th parameter (source length) matches the actual string length being converted

Version 3.8.8 - Broken:

uLen=MultiByteToWideChar(CodePage,dwFlags,AnsiStr,-1,0,0);

Then later:

MultiByteToWideChar(CodePage,dwFlags,AnsiStr,uLen,WideStr,uLen);


The problem:

Someone "optimized" it to use the API to calculate buffer size (smart idea), but made a critical mistake:
they pass uLen as the source length parameter in the second call. At that point, uLen contains the destination
buffer size in wide characters, not the source string length. It should pass -1 again.

Why this happened:

The developer likely thought "I already have the length in uLen, why pass -1 again?"
But they confused what uLen represents at different points in the code.

Impact:

Any Unicode string longer than what happened to fit in memory before the next object
would either truncate or read past the actual string, corrupting the conversion silently.


This is a textbook case of an "optimization" that broke functionality without obvious error reporting.


Robert

Hi MrBcx:

Interesting.

Trying to get it to work with UTF-8 has problems with strings getting truncated or trashed. Hopefully, it can be fixed. See attached image.


MACRO TIMEOUT = 4000 ' Milliseconds
MACRO MBTYPE = MB_YESNOCANCEL|MB_SETFOREGROUND|MB_SYSTEMMODAL|MB_ICONINFORMATION

DIM Result

' Returns 32000 if the messagebox timed out.

Result = MessageBoxTimeoutA(LIB "user32.dll", NULL, "This works with all the compilers...", "ANSI VERSION", MBTYPE, 0, TIMEOUT)
PRINT "MessageBoxTimeoutA Returned:", Result

' Returns 32000 if the messagebox timed out.

Result = MessageBoxTimeoutW(LIB "user32.dll", NULL, A2W ("Αυτά μου φαίνονται κινέζικα.", CP_UTF8), A2W ("UTF-8 ΕΚΔΟΧΗ", CP_UTF8), MBTYPE, 0, TIMEOUT)

PRINT "MessageBoxTimeoutW Returned:", Result

PAUSE
END


MrBcx

Since I replaced the inline assembly language with plain "C" (7 months ago), the BCX_DYNACALL
handlers makes calling these undocumented MessageBoxTimeout functions easy (ANSI and UNICODE versions)

And you can compile them in 32-bit and 64-bit!


MACRO TIMEOUT = 4000 ' Milliseconds
MACRO MBTYPE = MB_YESNOCANCEL|MB_SETFOREGROUND|MB_SYSTEMMODAL|MB_ICONINFORMATION

DIM Result

' Returns 32000 if the messagebox timed out.

Result = MessageBoxTimeoutA(LIB "user32.dll", NULL, "This works with all the compilers...", "ANSI VERSION", MBTYPE, 0, TIMEOUT)
PRINT "MessageBoxTimeoutA Returned:", Result

' Returns 32000 if the messagebox timed out.

Result = MessageBoxTimeoutW(LIB "user32.dll", NULL, A2W ("This works with all the compilers..."), A2W ("UNICODE VERSION"), MBTYPE, 0, TIMEOUT)

PRINT "MessageBoxTimeoutW Returned:", Result

PAUSE
END