← All posts tagged freebasic

mabu
программирование freebasic Строку BSTR можно создать как литеральную константу.

Пример:

```
' Первые два символа — количество байт в строке без учёта завершающего нуля
' Порядок байт Little Endian, от младшего к старшему
Const HttpMethod = WStr(!"\06\00GET")
Const HttpUrl = WStr(!"\52\00https://www.lindachan.net/")
```

1. Создаём константу. Декорируем литерал в WStr, потому что строка BSTR юникодная по своей природе.
2. Литерал должен предваряться знаком «!», чтобы в нём можно было делать экранированные последовательности.
3. Первые два символа литерала следует устанавливать в число байт, которые занимает в памяти строка BSTR (без учёта завершающего нулевого символа). Для этого ставим через обратный слэш ASCII‐коды количества символов в литерале умноженных на два, потому что UTF-16. Порядок байт Little Endian: младшие байты указывают слева, старшие — справа. В этом примере длина строки 3 символа, количество байт строки 6, поэтому указываем сначала \06 потом \00.
4. Такую строку нельзя уничтожать и модифицировать, то есть вызов SysFreeString для неё запрещён. Данные располагаются в секции с атрибутом ReadOnly, создаются и уничтожаются загрузчиком системы.
mabu
программирование freebasic Внимание, спойлер! программа получилась размером 2048 байт.

Сделал не тут программу, которая скачивает с сайта страницу и отображает заголовки ответа и тело в коробке сообщений.

Для уменьшения размера пришлось прибегать к хитростям:
* самому определять GUID для XmlHttpRequest;
* объединить секцию кода с секцией данных.

Хотя у меня не получилось объединить секции .text и .idata, но это бы всё равно не повлияло на размер конечного файла из‐за выравнивания секций. Ещё система отказывается запускать файл, если компоновщику указать файловое выравнивание не кратное 512 и выравнивание секций не по 4096.

Я уверен, что вы сделаете ещё меньше и жду вашего результата.

disk.yandex.ru
mabu
программирование freebasic Оказывается у окон с классом EDIT есть встроенная всплывающая подсказка. Её можно назначить через Edit_ShowBalloonTip.
Но это работает только в шестой версии Comclt32.dll.
Заодно добавил UpDown к полям.

mabu
программирование freebasic Обновил шрифт и размеры дочерних окон под стиль Windows XP.
Ещё добавил проверку пользовательского ввода на корректность: выскакивают сообщательные коробки, попозже сделаю

mabu
программирование freebasic Сегодня 12.04.2021 выпустили финальную версию 1.07.3.
Изменения:
— C backend: switch to .text section after writing the exports to the C file in the explicit asm block. gcc can move sections around with optimizations and there is a change between 7.x and 8.x that causes issue with where the directive section is located

Это последняя версия из ветки 1.07, следующие релизы будут из ветки 1.08.
mabu
программирование freebasic А ты знала, что можно не париться насчёт пакетных файлов компиляции, выбрать мышкой список исходников и перетащить его на значок компилятора? Подходит для несложных консольных программ.
mabu
программирование freebasic Парадокс: функциям можно присваивать значения.

Представим простую функцию:
Function Foo(ByVal param As Integer)As Integer
Return 2
End Function

Теперь присвоим ей значение:
Foo(3) = 5

Жду твоего ответа как такое возможно.
mabu
freebasic Как‐то один %USERNAME% показал код и спросил, какое сравнение быстрее: > или >=.
DIM i AS LONG
FOR i = 1 TO 100000000
IF i > 1 THEN k = 777 '(вторым было сравнение i >= 1)
NEXT

Однако фрибесик не поддался на эту хитрую уловку и оптимизировал код в одну инструкцию:
mov eax, 777
mabu
программирование freebasic Переменные должны объявляться в начале логического блока, в котором они используются, а НЕ в начале функции или программы.

И вот почему.

Все программные системы иерархичны. Программы делются на пакеты, пакеты — на классы, классы разбиваются на отдельные функции.

Данные, относящиеся к тому или иному модулю программы, принято объявлять в начале этого модуля. Локальные переменные объявляются в начале функции; поля, относящиеся ко всему классу, объявляются в начале определения класса и так далее.

Однако функции не являются последним уровнем в иерархии программы. Любая нетривиальная функция состоит из блоков, реализующих отдельные шаги выполнения алгоритма. Тех самых блоков, которые никак не обособляются в коде, разве что отделяются друг от друга парочкой пустых строк.

Однако эти блоки — полноценные элементы в иерархии программы. И они тоже имеют право на собственные локальные переменные! Которые объявляются в начале этого блока и используются только в его пределах.

1. Это приводит к смешению переменных, относящихся ко всей функции, с переменными, относящимися только к её отдельному блоку.
2. Это разрывает блок на две части: объявления данных (в начале функции) и использования этих данных (в самом блоке).
3. Это усложняет комментирование блока: в одном месте мы комментируем переменные, но не знаем, как их использовать; в другом месте мы комментируем алгоритм, но не знаем, с какими данными он работает.

Пример.

Вы только представьте: У нас есть функция в 300 строк кода. Где-нибудь на 200-й строке нам надо поменять две переменные местами. Для этого мы лезем на 200 сток выше в начало функции, объявляем переменную Temp, которая не имеет никакого отношения ко всей функции, а используется только один раз в одном месте, потом опять возвращаемся к 200-й строке и меняем переменные местами… Это просто кошмар.

Инструкции Scope End Scope, Do Loop, For Next, If Else End If, While WEnd While, Using End Using и подобные создают собственный локальный блок жизни переменных, если какая‐то переменная нужна только в этом блоке, то она должа быть объявлена только там, а не во всей функции.
mabu
программирование freebasic В Visual Basic все объявленные в модуле переменные видны на уровне модуля. Почему же во FreeBASIC к переменным уровня модуля необходимо дописывать модификатор Shared? Всё дело в том, что во фрибесике нет явной функции Main. Любой код вне процедур и функций считается стартовой точкой программы и заворачивается в неявную Main. Например:

```FreeBASIC

Dim X As Integer = Rnd() * 100
Dim Y As Integer = Rnd() * 100

Function AddNumbers(a As Integer, b As Integer)As Integer
Return a + b
End Function

Print AddNumbers(X, Y)

```

Объявление переменных X и Y и вызов `Print AddNumbers(X, Y)` кладётся внутрь неявной Main, поэтому переменные X и Y считаются локальными для неявной Main и не видны внутри функции AddNumbers. Чтобы они стали видны внутри AddNumbers, к X и Y необходимо дописывать модификатор Shared.

(Небольшое уточнение: во фрибесике модулей нет, есть только единицы трансляции)
mabu
freebasic Оказывается, в 32‐битной версии для деления 64‐битных чисел нужны какие‐то встраиваемые функции типа __idivi3. А они тащат за собой версию gcc в исполняемый файл. И не убираются через --gc-sections
Написал свою функцию деления 64‐битных как обёртку над VarIdiv из библиотеки OLEAUT32.dll.
gist.github.com
mabu
winapi freebasic Чтобы рисовать «в памяти», нужен контекст устройства в памяти. Создать его можно через CreateCompatibleDC. Однако проблема в таком устройстве в том, что по умолчанию в нём выбран чёрно‐белый BITMAP размером 1 на 1 пиксель. Поэтому рисунок в контексте устройства в памяти будет чёрно‐белым. Чтобы рисунок был цветным, необходимо создать BITMAP на основе оконного или дисплейного контекста устройства и выбрать его в контекст устройства в памяти:

```FreeBASIC
Const MOVE_DX = 10
Const MOVE_DY = 10

' Переменные уровня модуля:
Dim Shared MemoryDC As HDC
Dim Shared MemoryBM As HBITMAP
Dim Shared OldMemoryBM As HGDIOBJ
Dim Shared ShapeRectangle As RECT

Function MainFormWndProc(ByVal hWin As HWND, ByVal wMsg As UINT, ByVal wParam As WPARAM, ByVal lParam As LPARAM) As LRESULT

Select Case wMsg

Case WM_CREATE
' Контекст устройства окна
Dim hDC As HDC = GetDC(hWin)

' Контекст устройства в памяти
MemoryDC = CreateCompatibleDC(hDC)
' Цветной рисунок на основе окна

Dim ClientRect As RECT = Any
GetClientRect(hWin, @ClientRect)
MemoryBM = CreateCompatibleBitmap(hDC, ClientRect.right, ClientRect.bottom)

' Освобождаем контекст устройства окна, он больше не нужен
ReleaseDC(hWin, hDC)

' Выбираем цветной рисунок, сохраняя старый
OldMemoryBM = SelectObject(MemoryDC, MemoryBM)

' Положение круглешка
SetRect( _
@ShapeRectangle, _
ClientRect.right \ 2 — 20, _
ClientRect.bottom \ 2 — 20, _
ClientRect.right \ 2 + 20, _
ClientRect.bottom \ 2 + 20 _
)
' Визуализация
Render(hWin)
```

Теперь можно рисовать при изменениях сцены прямо в MemoryDC:

```FreeBASIC
Sub Render(ByVal hWin As HWND)
' Прямоугольник обновления
Dim updRect As RECT = Any
SetRect( _
@updRect, _
ShapeRectangle.left — MOVE_DX — 1, _
ShapeRectangle.top — MOVE_DY — 1, _
ShapeRectangle.right + MOVE_DX + 1, _
ShapeRectangle.bottom + MOVE_DY + 1 _
)
' Стираем старое изображение
FillRect(MemoryDC, @updRect, Cast(HBRUSH, GetStockObject(BLACK_BRUSH)))
' Рисуем
Ellipse(MemoryDC, ShapeRectangle.left, ShapeRectangle.top, ShapeRectangle.right, ShapeRectangle.bottom)
' Выводим в окно
InvalidateRect(hWin, @updRect, FALSE)
End Sub
```

В обработчике WM_ERASEBKGND просто возвращаем TRUE, давая понять системе, что фон мы уже стёрли:

```FreeBASIC
Case WM_ERASEBKGND
Return TRUE
```

В обработчике WM_PAINT копируем изменённые участки из контекста памяти в окно:

```FreeBASIC
Case WM_PAINT
Dim ps As PAINTSTRUCT = Any
Dim hDC As HDC = BeginPaint(hWin, @ps)

BitBlt(hDC, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom, _
MemoryDC, ps.rcPaint.left, ps.rcPaint.top, _
SRCCOPY _
)

EndPaint(hWin, @ps)
```

Очистка:

```FreeBASIC
Case WM_DESTROY
SelectObject(MemoryDC, OldMemoryBM)
DeleteObject(MemoryBM)
DeleteDC(MemoryDC)
PostQuitMessage(0)
```

Примечания.
1. Сохранять старый BITMAP и выбирать его обратно в процедуре очистки обязательно. Всё потому, что пока наш BITMAP выбран в контексте стройства, он не может быть удалён. Чтобы удалить наш BITMAP, надо «развыбрать» его обратно и выбрать предыдущий BITMAP. Это же относится к перьям, кистям и прочим GDI объектам, которые можно выбрать в контекст устройства.
2. Контекст устройства полученный через GetDC необходимо уничтожать через ReleaseDC. Контекст устройства полученный через CreateCompatibleDC необходимо удалять через DeleteDC. Контекст устройства, полученный через BeginPaint неявно удаляет система через EndPaint.
3. Некоторую сложность представляет изменение размера BITMAP при изменении размера окна. Когда окно меняет свой размер, то придётся уничтожать старый BITMAP и создавать новый по новому размеру клиентской области.
mabu
Windows winapi freebasic Сделал программу. Она получает список доступных разрешений монитора, переключает разрешение и создаёт выскакивающее полноэкранное окно.
Для полноэкранного окна ведь достаточно стиля WS_POPUP? Или нужно что‐то ещё?
github.com
Добавить нескучную анимацию, что ли.
mabu
freebasic Версия 1.07.2 при использовании излучателя кода gas64 екзешник полчается меньше примерно на 500 байт, так как отсутствует секция .rdata. Все константы укладываются в секцию .data.
Если компилировать через излучатель кода gcc, то константы попадают в секцию .rdata, при этом есть секция .data, в которой непонятно что лежит.