Hello everyone.
The following dynamic libraries are written in collaboration with AI.
I solved some things, but unfortunately only for Windows users. Yes, I was doing research on the laboriousness of Linux... I ran away. Fast. Very fast.
First, one thing that started to crash for some unknown reason - InputBox. The ZIP file contains the source code in C, the BAS program, compiled DLLs for IDE 32bit and IDE64bit.
So for users who want a different inputbox than the one available in QB64PE, there is this option.
32-bit version compiled by QB64PE compiler 4.0.0, 64-bit version compiled by QB64PE compiler 4.1.0
The following dynamic libraries are written in collaboration with AI.
I solved some things, but unfortunately only for Windows users. Yes, I was doing research on the laboriousness of Linux... I ran away. Fast. Very fast.
First, one thing that started to crash for some unknown reason - InputBox. The ZIP file contains the source code in C, the BAS program, compiled DLLs for IDE 32bit and IDE64bit.
So for users who want a different inputbox than the one available in QB64PE, there is this option.
32-bit version compiled by QB64PE compiler 4.0.0, 64-bit version compiled by QB64PE compiler 4.1.0
Code: (Select All)
// qb_inputbox_ex.c (PURE C, build as DLL x86/x64 to match QB64PE)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <commdlg.h>
#include <wchar.h>
#include <wctype.h>
#include <stdlib.h>
#include <strsafe.h>
#define IDC_PROMPT 1001
#define IDC_EDIT 1002
#define IDC_CHECK 1003
#define IDC_BROWSE 1004
// Flags
#define IB_PASSWORD 0x0001
#define IB_MULTILINE 0x0002
#define IB_NUMBERS 0x0004
#define IB_ALLOWEMPTY 0x0008
#define IB_CHECKBOX 0x0010
#define IB_BROWSE 0x0020
// Built-in UI text (English)
#define CAPTION_BOX L"InputBox"
#define BTN_OK_TEXT L"OK"
#define BTN_CANCEL_TEXT L"Cancel"
#define BTN_BROWSE_TEXT L"Browse..."
#define DEFAULT_CHECK_TEXT L"Remember"
#define OFN_TITLE_TEXT L"Select a file"
#define OFN_FILTER_TEXT L"All files (*.*)\0*.*\0\0"
typedef struct INPUTBOX_CTX {
const wchar_t* title;
const wchar_t* prompt;
const wchar_t* defText;
wchar_t* outBuf;
int outBufChars;
int flags;
int minLen;
int maxLen;
double minVal;
double maxVal;
const wchar_t* checkText;
int checkDefault;
int* checkValueOut;
WNDPROC oldEditProc;
} INPUTBOX_CTX;
static BYTE* AlignDword(BYTE* p) {
ULONG_PTR u = (ULONG_PTR)p;
u = (u + 3) & ~((ULONG_PTR)3);
return (BYTE*)u;
}
static void WriteWideString(WORD** pp, const wchar_t* s) {
while (*s) { **pp = (WORD)*s; (*pp)++; s++; }
**pp = 0; (*pp)++;
}
static void ReplaceCommaWithDot(wchar_t* s) {
for (; *s; ++s) if (*s == L',') *s = L'.';
}
static void UpdateOkButton(HWND hDlg) {
INPUTBOX_CTX* ctx = (INPUTBOX_CTX*)GetWindowLongPtrW(hDlg, DWLP_USER);
if (!ctx) return;
wchar_t tmp[4096];
GetDlgItemTextW(hDlg, IDC_EDIT, tmp, (int)(sizeof(tmp)/sizeof(tmp[0])));
int len = (int)wcslen(tmp);
int okEnabled = 1;
if (!(ctx->flags & IB_ALLOWEMPTY)) {
int need = (ctx->minLen > 0) ? ctx->minLen : 1;
if (len < need) okEnabled = 0;
} else {
if (ctx->minLen > 0 && len < ctx->minLen) okEnabled = 0;
}
EnableWindow(GetDlgItem(hDlg, IDOK), okEnabled ? TRUE : FALSE);
}
static int ValidateOnOk(HWND hDlg, INPUTBOX_CTX* ctx) {
wchar_t tmp[4096];
GetDlgItemTextW(hDlg, IDC_EDIT, tmp, (int)(sizeof(tmp)/sizeof(tmp[0])));
int len = (int)wcslen(tmp);
if (!(ctx->flags & IB_ALLOWEMPTY)) {
if (len == 0) {
MessageBoxW(hDlg, L"Please enter a value (cannot be empty).", CAPTION_BOX, MB_ICONWARNING);
SetFocus(GetDlgItem(hDlg, IDC_EDIT));
return 0;
}
}
if (ctx->minLen > 0 && len < ctx->minLen) {
wchar_t msg[256];
wsprintfW(msg, L"Text is too short. Minimum: %d characters.", ctx->minLen);
MessageBoxW(hDlg, msg, CAPTION_BOX, MB_ICONWARNING);
SetFocus(GetDlgItem(hDlg, IDC_EDIT));
return 0;
}
if (ctx->maxLen > 0 && len > ctx->maxLen) {
wchar_t msg[256];
wsprintfW(msg, L"Text is too long. Maximum: %d characters.", ctx->maxLen);
MessageBoxW(hDlg, msg, CAPTION_BOX, MB_ICONWARNING);
SetFocus(GetDlgItem(hDlg, IDC_EDIT));
return 0;
}
// numeric validation only for single-line
if ((ctx->flags & IB_NUMBERS) && !(ctx->flags & IB_MULTILINE)) {
wchar_t numBuf[4096];
wcsncpy(numBuf, tmp, (int)(sizeof(numBuf)/sizeof(numBuf[0])) - 1);
numBuf[(sizeof(numBuf)/sizeof(numBuf[0])) - 1] = 0;
ReplaceCommaWithDot(numBuf);
wchar_t* p = numBuf;
while (*p && iswspace(*p)) p++;
wchar_t* end = NULL;
double v = wcstod(p, &end);
if (end == p) {
MessageBoxW(hDlg, L"Invalid number.", CAPTION_BOX, MB_ICONWARNING);
SetFocus(GetDlgItem(hDlg, IDC_EDIT));
return 0;
}
while (*end && iswspace(*end)) end++;
if (*end != 0) {
MessageBoxW(hDlg, L"Invalid number (contains unsupported characters).", CAPTION_BOX, MB_ICONWARNING);
SetFocus(GetDlgItem(hDlg, IDC_EDIT));
return 0;
}
if (ctx->maxVal > ctx->minVal) {
if (v < ctx->minVal || v > ctx->maxVal) {
wchar_t msg[256];
// wsprintfW(msg, L"Number must be in the range %.12g to %.12g.", ctx->minVal, ctx->maxVal);
StringCchPrintfW(msg, 256, L"Number must be in the range %.12g to %.12g.", ctx->minVal, ctx->maxVal);
MessageBoxW(hDlg, msg, CAPTION_BOX, MB_ICONWARNING);
SetFocus(GetDlgItem(hDlg, IDC_EDIT));
return 0;
}
}
}
return 1;
}
static int DoBrowseFile(HWND hDlg) {
wchar_t fileBuf[4096] = {0};
OPENFILENAMEW ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hDlg;
ofn.lpstrFile = fileBuf;
ofn.nMaxFile = (DWORD)(sizeof(fileBuf)/sizeof(fileBuf[0]));
ofn.lpstrFilter = OFN_FILTER_TEXT;
ofn.nFilterIndex = 1;
ofn.lpstrTitle = OFN_TITLE_TEXT;
ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
if (GetOpenFileNameW(&ofn)) {
SetDlgItemTextW(hDlg, IDC_EDIT, fileBuf);
return 1;
}
return 0;
}
static LRESULT CALLBACK EditSubclassProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
INPUTBOX_CTX* ctx = (INPUTBOX_CTX*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
if (!ctx || !ctx->oldEditProc) return DefWindowProcW(hWnd, msg, wParam, lParam);
if ((ctx->flags & IB_NUMBERS) && !(ctx->flags & IB_MULTILINE)) {
if (msg == WM_CHAR) {
wchar_t ch = (wchar_t)wParam;
if (ch < 32) return CallWindowProcW(ctx->oldEditProc, hWnd, msg, wParam, lParam);
if (ch >= L'0' && ch <= L'9')
return CallWindowProcW(ctx->oldEditProc, hWnd, msg, wParam, lParam);
if (ch == L'.' || ch == L',') {
wchar_t t[4096];
GetWindowTextW(hWnd, t, (int)(sizeof(t)/sizeof(t[0])));
if (wcschr(t, L'.') || wcschr(t, L',')) return 0;
return CallWindowProcW(ctx->oldEditProc, hWnd, msg, wParam, lParam);
}
if (ch == L'-') {
wchar_t t[4096];
GetWindowTextW(hWnd, t, (int)(sizeof(t)/sizeof(t[0])));
if (wcschr(t, L'-')) return 0;
DWORD selStart = 0, selEnd = 0;
SendMessageW(hWnd, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd);
if (selStart != 0) return 0;
return CallWindowProcW(ctx->oldEditProc, hWnd, msg, wParam, lParam);
}
return 0;
}
}
return CallWindowProcW(ctx->oldEditProc, hWnd, msg, wParam, lParam);
}
static void AddItem(BYTE** pp, DWORD style, DWORD exStyle,
short x, short y, short cx, short cy,
WORD id, WORD classAtom, const wchar_t* title)
{
BYTE* p = AlignDword(*pp);
DLGITEMTEMPLATE* it = (DLGITEMTEMPLATE*)p;
it->style = style;
it->dwExtendedStyle = exStyle;
it->x = x; it->y = y; it->cx = cx; it->cy = cy;
it->id = id;
p += sizeof(DLGITEMTEMPLATE);
WORD* pw = (WORD*)p;
*pw++ = 0xFFFF;
*pw++ = classAtom;
if (title && *title) WriteWideString(&pw, title);
else *pw++ = 0;
*pw++ = 0;
*pp = (BYTE*)pw;
}
static INT_PTR CALLBACK InputDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
INPUTBOX_CTX* ctx;
if (uMsg == WM_INITDIALOG) {
ctx = (INPUTBOX_CTX*)lParam;
SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)ctx);
if (ctx->title) SetWindowTextW(hDlg, ctx->title);
if (ctx->prompt) SetDlgItemTextW(hDlg, IDC_PROMPT, ctx->prompt);
if (ctx->defText) SetDlgItemTextW(hDlg, IDC_EDIT, ctx->defText);
if ((ctx->flags & IB_CHECKBOX) && ctx->checkDefault)
CheckDlgButton(hDlg, IDC_CHECK, BST_CHECKED);
{
int limit = 0;
if (ctx->maxLen > 0) limit = ctx->maxLen;
else limit = ctx->outBufChars - 1;
if (limit < 0) limit = 0;
if (limit > ctx->outBufChars - 1) limit = ctx->outBufChars - 1;
SendDlgItemMessageW(hDlg, IDC_EDIT, EM_LIMITTEXT, (WPARAM)limit, 0);
}
{
HWND hEdit = GetDlgItem(hDlg, IDC_EDIT);
SetWindowLongPtrW(hEdit, GWLP_USERDATA, (LONG_PTR)ctx);
ctx->oldEditProc = (WNDPROC)SetWindowLongPtrW(hEdit, GWLP_WNDPROC, (LONG_PTR)EditSubclassProc);
}
SendDlgItemMessageW(hDlg, IDC_EDIT, EM_SETSEL, 0, -1);
UpdateOkButton(hDlg);
SetFocus(GetDlgItem(hDlg, IDC_EDIT));
return FALSE;
}
ctx = (INPUTBOX_CTX*)GetWindowLongPtrW(hDlg, DWLP_USER);
switch (uMsg) {
case WM_COMMAND: {
WORD id = LOWORD(wParam);
WORD code = HIWORD(wParam);
if (id == IDC_EDIT && code == EN_CHANGE) {
UpdateOkButton(hDlg);
return TRUE;
}
if (id == IDC_BROWSE) {
if (DoBrowseFile(hDlg)) UpdateOkButton(hDlg);
return TRUE;
}
if (id == IDOK) {
if (!ctx) { EndDialog(hDlg, IDCANCEL); return TRUE; }
if (!ValidateOnOk(hDlg, ctx)) return TRUE;
if (ctx->outBuf && ctx->outBufChars > 0) {
GetDlgItemTextW(hDlg, IDC_EDIT, ctx->outBuf, ctx->outBufChars);
ctx->outBuf[ctx->outBufChars - 1] = 0;
}
if ((ctx->flags & IB_CHECKBOX) && ctx->checkValueOut) {
*ctx->checkValueOut = (IsDlgButtonChecked(hDlg, IDC_CHECK) == BST_CHECKED) ? 1 : 0;
}
EndDialog(hDlg, IDOK);
return TRUE;
}
if (id == IDCANCEL) {
if (ctx && (ctx->flags & IB_CHECKBOX) && ctx->checkValueOut) {
*ctx->checkValueOut = (IsDlgButtonChecked(hDlg, IDC_CHECK) == BST_CHECKED) ? 1 : 0;
}
EndDialog(hDlg, IDCANCEL);
return TRUE;
}
break;
}
case WM_CLOSE:
EndDialog(hDlg, IDCANCEL);
return TRUE;
}
return FALSE;
}
static int InputBoxExW(INPUTBOX_CTX* ctx, HWND parent) {
if (!ctx || !ctx->outBuf || ctx->outBufChars <= 0) return 0;
ctx->outBuf[0] = 0;
int multiline = (ctx->flags & IB_MULTILINE) ? 1 : 0;
int browse = ((ctx->flags & IB_BROWSE) && !multiline) ? 1 : 0;
int checkbox = (ctx->flags & IB_CHECKBOX) ? 1 : 0;
short dlgCx = 240;
short margin = 7;
short promptY = 7, promptH = 12;
short editY = 22;
short editH = multiline ? 60 : 14;
short belowEditY = (short)(editY + editH + 6);
short checkY = belowEditY;
short buttonsY = checkbox ? (short)(checkY + 16) : belowEditY;
short dlgCy = (short)(buttonsY + 22);
short btnW = 50, btnH = 14, gap = 6;
short btnCancelX = (short)(dlgCx - margin - btnW);
short btnOkX = (short)(btnCancelX - gap - btnW);
short editX = margin;
short browseW = 50;
short browseX = (short)(dlgCx - margin - browseW);
short editW = (short)(dlgCx - margin - editX - (browse ? (browseW + gap) : 0));
int cdit = 4 + (checkbox ? 1 : 0) + (browse ? 1 : 0);
HGLOBAL hMem = GlobalAlloc(GPTR, 8192);
if (!hMem) return 0;
BYTE* mem = (BYTE*)hMem;
BYTE* p = mem;
DLGTEMPLATE* dlg = (DLGTEMPLATE*)p;
dlg->style = WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_SETFONT | DS_CENTER;
dlg->dwExtendedStyle = 0;
dlg->cdit = (WORD)cdit;
dlg->x = 10; dlg->y = 10; dlg->cx = dlgCx; dlg->cy = dlgCy;
p += sizeof(DLGTEMPLATE);
WORD* pw = (WORD*)p;
*pw++ = 0;
*pw++ = 0;
*pw++ = 0;
*pw++ = 9;
WriteWideString(&pw, L"MS Shell Dlg");
p = (BYTE*)pw;
AddItem(&p, WS_CHILD | WS_VISIBLE | SS_LEFT, 0,
margin, promptY, (short)(dlgCx - 2*margin), promptH,
IDC_PROMPT, 0x0082, L"");
DWORD editStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL;
DWORD editEx = WS_EX_CLIENTEDGE;
if (multiline)
editStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL;
if (ctx->flags & IB_PASSWORD)
editStyle |= ES_PASSWORD;
AddItem(&p, editStyle, editEx,
editX, editY, editW, editH,
IDC_EDIT, 0x0081, L"");
if (browse) {
AddItem(&p, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 0,
browseX, editY, browseW, 14,
IDC_BROWSE, 0x0080, BTN_BROWSE_TEXT);
}
if (checkbox) {
AddItem(&p, WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 0,
margin, checkY, (short)(dlgCx - 2*margin), 12,
IDC_CHECK, 0x0080,
(ctx->checkText && *ctx->checkText) ? ctx->checkText : DEFAULT_CHECK_TEXT);
}
AddItem(&p, WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 0,
btnOkX, buttonsY, btnW, btnH,
IDOK, 0x0080, BTN_OK_TEXT);
AddItem(&p, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 0,
btnCancelX, buttonsY, btnW, btnH,
IDCANCEL, 0x0080, BTN_CANCEL_TEXT);
INT_PTR rr = DialogBoxIndirectParamW(GetModuleHandleW(NULL), (LPCDLGTEMPLATEW)mem,
parent, InputDlgProc, (LPARAM)ctx);
GlobalFree(hMem);
return (rr == IDOK) ? 1 : 0;
}
// ACP <-> UTF-16
static int ToWideACP(const char* s, wchar_t* out, int outChars) {
if (!out || outChars <= 0) return 0;
out[0] = 0;
if (!s) return 0;
{
int n = MultiByteToWideChar(CP_ACP, 0, s, -1, out, outChars);
if (n <= 0) out[0] = 0;
return n;
}
}
static int FromWideACP(const wchar_t* s, char* out, int outBytes) {
if (!out || outBytes <= 0) return 0;
out[0] = 0;
if (!s) return 0;
{
int n = WideCharToMultiByte(CP_ACP, 0, s, -1, out, outBytes, NULL, NULL);
if (n <= 0) out[0] = 0;
return n;
}
}
// Export
__declspec(dllexport) int __stdcall QB_InputBoxExA(
const char* title,
const char* prompt,
const char* defText,
char* outBuf, int outBufBytes,
int flags,
int minLen, int maxLen,
double minVal, double maxVal,
const char* checkboxText,
int checkboxDefault,
int* checkboxValueOut
) {
if (!outBuf || outBufBytes <= 0) return 0;
outBuf[0] = 0;
wchar_t wTitle[256], wPrompt[1024], wDef[2048], wCheck[256];
wchar_t wOut[4096] = {0};
ToWideACP(title, wTitle, (int)(sizeof(wTitle)/sizeof(wTitle[0])));
ToWideACP(prompt, wPrompt, (int)(sizeof(wPrompt)/sizeof(wPrompt[0])));
ToWideACP(defText, wDef, (int)(sizeof(wDef)/sizeof(wDef[0])));
ToWideACP(checkboxText, wCheck, (int)(sizeof(wCheck)/sizeof(wCheck[0])));
INPUTBOX_CTX ctx;
ZeroMemory(&ctx, sizeof(ctx));
ctx.title = wTitle;
ctx.prompt = wPrompt;
ctx.defText = wDef;
ctx.outBuf = wOut;
ctx.outBufChars = (int)(sizeof(wOut)/sizeof(wOut[0]));
ctx.flags = flags;
ctx.minLen = minLen;
ctx.maxLen = maxLen;
ctx.minVal = minVal;
ctx.maxVal = maxVal;
ctx.checkText = (wCheck[0] ? wCheck : NULL);
ctx.checkDefault = checkboxDefault ? 1 : 0;
ctx.checkValueOut = checkboxValueOut;
{
HWND parent = GetForegroundWindow();
int ok = InputBoxExW(&ctx, parent);
if (!ok) { outBuf[0] = 0; return 0; }
}
FromWideACP(wOut, outBuf, outBufBytes);
outBuf[outBufBytes - 1] = 0;
return 1;
}Code: (Select All)
' ===== flags =====
Const IB_PASSWORD = &H1
Const IB_MULTILINE = &H2
Const IB_NUMBERS = &H4
Const IB_ALLOWEMPTY = &H8
Const IB_CHECKBOX = &H10
Const IB_BROWSE = &H20
Declare Dynamic Library "qb_inputbox_2en"
FUNCTION QB_InputBoxExA% (BYVAL title AS _OFFSET, BYVAL prompt AS _OFFSET, BYVAL defText AS _OFFSET, _
BYVAL outBuf AS _OFFSET, BYVAL outBufBytes AS LONG, _
BYVAL flags AS LONG, BYVAL minLen AS LONG, BYVAL maxLen AS LONG, _
BYVAL minVal AS DOUBLE, BYVAL maxVal AS DOUBLE, _
BYVAL cbText AS _OFFSET, BYVAL cbDefault AS LONG, _
BYVAL cbValueOut AS _OFFSET)
End Declare
' ===== demo =====
Dim ok As Long, chk As Long
Dim flags As Long
flags = IB_PASSWORD ' Or IB_CHECKBOX Or IB_BROWSE
a$ = WinInputBoxEx$("Password", "Input password", "", flags, 1, 300, 0#, 0#, "", 1, chk, ok)
Print "ok="; ok; " chk="; chk; " text=["; a$; "]"
flags = IB_NUMBERS
b$ = WinInputBoxEx$("Number", "Input number in range -10 to 10:", "0", flags, 1, 20, -10#, 10#, "", 0, chk, ok)
Print "ok="; ok; " text=["; b$; "]"
flags = IB_MULTILINE ' Or IB_CHECKBOX Or IB_BROWSE
a$ = WinInputBoxEx$("Multiline", "Input text", "", flags, 1, 300, 0#, 0#, "", 1, chk, ok)
Print "ok="; ok; " chk="; chk; " text=["; a$; "]"
flags = IB_ALLOWEMPTY ' Or IB_CHECKBOX Or IB_BROWSE
a$ = WinInputBoxEx$("Allow Empty", "Input text", "", flags, 1, 300, 0#, 0#, "", 1, chk, ok)
Print "ok="; ok; " chk="; chk; " text=["; a$; "]"
flags = IB_CHECKBOX ' Or IB_CHECKBOX Or IB_BROWSE
a$ = WinInputBoxEx$("Checkbox", "Input text", "", flags, 1, 300, 0#, 0#, "Memorize", 1, chk, ok)
Print "ok="; ok; " chk="; chk; " text=["; a$; "]"
flags = IB_BROWSE ' Or IB_CHECKBOX Or IB_BROWSE
a$ = WinInputBoxEx$("Browse", "Select file", "", flags, 1, 300, 0#, 0#, "", 1, chk, ok)
Print "ok="; ok; " chk="; chk; " text=["; a$; "]"
Sleep
Function WinInputBoxEx$ (title$, prompt$, def$, flags&, minLen&, maxLen&, minVal#, maxVal#, cbText$, cbDefault&, cbValue&, ok&)
Dim t As String * 256
Dim p As String * 1024
Dim d As String * 2048
Dim c As String * 256
Dim outs As String * 4096
t = Left$(title$, Len(t) - 1) + Chr$(0)
p = Left$(prompt$, Len(p) - 1) + Chr$(0)
d = Left$(def$, Len(d) - 1) + Chr$(0)
c = Left$(cbText$, Len(c) - 1) + Chr$(0)
outs = Chr$(0) + Space$(Len(outs) - 1)
cbValue& = 0
ok& = QB_InputBoxExA(_OFFSET(t), _OFFSET(p), _OFFSET(d), _
_OFFSET(outs), LEN(outs), _
flags&, minLen&, maxLen&, minVal#, maxVal#, _
_OFFSET(c), cbDefault&, _OFFSET(cbValue&))
If ok& Then
WinInputBoxEx$ = Left$(outs, InStr(outs, Chr$(0)) - 1)
Else
WinInputBoxEx$ = ""
End If
End Function

