платформа win32. Пишем на FreeBasic не используя runtime библиотеки, уменьшаем размер бинарника, делаем dll своими руками, функция WinMain.

Предыдущая тема Следующая тема Перейти вниз

платформа win32. Пишем на FreeBasic не используя runtime библиотеки, уменьшаем размер бинарника, делаем dll своими руками, функция WinMain.

Сообщение  electrik в Пт Июн 10, 2011 9:34 pm

платформа win32. Пишем на FreeBasic не используя runtime библиотеки, уменьшаем размер бинарника, делаем dll своими руками, функция WinMain.

введение.
как-то сравнивал я разные hello world'ы, на ассемблере и FreeBasic, и программы откомпилированные на FreeBasic, всегда получались больше.
я думал, что там они суют в бинарник?, что он такой огромный, даже без использования функций rtl.
все оказалось просто. непосредственно перед нашим кодом, идут всякие инициализации критических секций, командная строка и т.д.
зачастую в простых программах, нам это не нужно, да и хочется сделать маленький бинарник.
хорошо, что разработчики FreeBasic, предусмотрели ключик "-v", он показывает все шаги компиляции, какие программы исполняет и какие параметры им передает.
среди этих параметров, есть ссылки на объектные файлы и библиотеки, которыми мы и займемся.
простая программа с MessageBox'ом, занимает около 7 кб, мы же сделаем такую в 2.5 кб, при линковке убрав ненужные нам библиотеки и объектные файлы.
вам вообще придется забыть про стандартные функции FreeBasic,так как библиотеки от него линковаться не будут.

что нельзя делать.
объявлять строки "string"- только "zString, так как там вызывается много rtl функций.
присваивать строкам значения типа такого:
Код:

dim a as zstring * 14
a="hello"
придется написать так:
Код:

dim a as zstring * 14
lstrcpy(a,"hello")
также не допустимы объединения строк:
Код:

dim a as zstring * 14
a="hello" & " world"
правильно будет так:
Код:

dim a as zstring * 14
lstrcpy(a,"hello")
lstrcat(a," world")
заместо функции end, надо использовать ExitProcess.
примечание: ExitProcess, не закрывает все открытые дескрипторы файлов, устройств и т.д, как функция end. вы сами должны позаботится о закрытии открытых устройств, файлов, функцией CloseHandle или вызвать специальные функции предназначеные для ьтой или иной задачи.
хотя, чесно говоря, я этого не знаю, может она и закрывает открытые дескрипторы, но лучше самим освобождать ресурсы, так надежнее.
короче, придется вам писать на чистом WinApi, или самим разрабатывать удобные для вас функции, но тоже на WinApi.
хотим маленький бинарник, придется попотеть.
ну ладно, испугал я вас, насамом деле не так все страшно. давайте перейдем к первой программе, и конечно же это будет Hello world.

hello world.
Код:

#include "windows.bi"

sub main cdecl alias "main"() ' точка входа в программу, потом укажем линкеру
MessageBox(0,"hello world","hello programm",0)
ExitProcess(0) ' используем ExitProcess за место end
'в параметре передается значение, возвращаемое операционке, так же как в fb'шной end
end sub


в процедуре main, мы используем alias имя для того, чтоб линкеру указывать имя точки входа теми символами, которые объявили в исходнике.
если неиспользовать alias имя, нам придется указать линкеру точку входа большими буквами.
примечание: указывать точку входа, обязательно, начиная символом "_".
ну а теперь батник для компиляции
все пути прописаны под мою конфигурацию, сами подправите.

Код:

@echo off
echo compiling
d:\FreeBasic\fbc hello.bas -r -lib
echo assembling
d:\freebasic\bin\win32\as.exe --32 --strip-local-absolute "hello.asm" -o "hello.o"
echo linking
d:\FreeBasic\bin\win32\ld -e _main -subsystem windows -s --stack 1048576,1048576 -L "d:\freebasic\lib\win32" -L "./" "hello.o" -o "hello.exe" -( -lkernel32 -luser32 -)
pause
ну как, думаю размер файла вам понравился.

краткое описание некоторых параметров.
ключ -e, после него можно указать точку входа в программу.
строки заканчивающиеся расширением ".o", объектные файлы, в нашем случае это "hello.o"- наша программа ассемблированная в объектный файл.
параметры такого типа:
-lkernel32 -luser32- это подключаемые библиотеки.
все просто, если нам нужна библиотека "advapi32, просто к этим параметрам добавим еще -ladvapi32 и получим следующее:
-lkernel32 -luser32 -ladvapi32
подробнее ключи можно посмотреть набрав:
ld --help

функция WinMain.
есть такая всеми излюбленная функция, да и удобно, когда есть командная строка, хэндл программы, да и параметр поведения окна можно передать.
как такое сделать? вот сама программа.

Код:

#include "windows.bi"

declare function        WinMain    ( byval hInstance as HINSTANCE, _
                                      byval hPrevInstance as HINSTANCE, _
                                      byval szCmdLine as zString ptr, _
                                      byval iCmdShow as integer ) as integer
                                 
                                  sub main cdecl()
   ExitProcess(WinMain( GetModuleHandle( null ), null, GetCommandLine(), SW_NORMAL )) ' вызываем функцию WinMain. через exitProcess, возвращаем операционке значение после завершение функции winMain
end sub
'':::::
function WndProc ( byval hWnd as HWND, _
                  byval wMsg as UINT, _
                  byval wParam as WPARAM, _
                  byval lParam as LPARAM ) as LRESULT
   
    function = 0
   
    select case( wMsg )
        case WM_CREATE           
            exit function

        case WM_PAINT
          dim rct as RECT, pnt as PAINTSTRUCT, hDC as HDC
         
            hDC = BeginPaint( hWnd, @pnt )
            GetClientRect( hWnd, @rct )
           
            DrawText( hDC, _
                    "Hello, World!", _
                    -1, _
                      @rct, _
                      DT_SINGLELINE or DT_CENTER or DT_VCENTER )
           
            EndPaint( hWnd, @pnt )
           
            exit function           
       
      case WM_KEYDOWN
         if( lobyte( wParam ) = 27 ) then
            PostMessage( hWnd, WM_CLOSE, 0, 0 )
         end if
case WM_CLOSE
MessageBox(0,"Press 'Ok' to exit program","Hello programm",0)
       case WM_DESTROY
            PostQuitMessage( 0 )
            exit function
    end select
   
    function = DefWindowProc( hWnd, wMsg, wParam, lParam )   
   
end function

'':::::
function WinMain ( byval hInstance as HINSTANCE, _
                  byval hPrevInstance as HINSTANCE, _
                  byval szCmdLine as zString ptr, _
                  byval iCmdShow as integer ) as integer   
   
    dim wMsg as MSG, wcls as WNDCLASS, hWnd as HWND
   
    function = 0

       wcls.style        = CS_HREDRAW or CS_VREDRAW
       wcls.lpfnWndProc  = @WndProc
       wcls.cbClsExtra    = 0
       wcls.cbWndExtra    = 0
       wcls.hInstance    = hInstance
       wcls.hIcon        = LoadIcon( NULL, IDI_APPLICATION )
       wcls.hCursor      = LoadCursor( NULL, IDC_ARROW )
       wcls.hbrBackground = GetStockObject( WHITE_BRUSH )
       wcls.lpszMenuName  = NULL
       wcls.lpszClassName = @"HelloWin"
         
    if( RegisterClass( @wcls ) = FALSE ) then
      MessageBox( null, "Failed to register wcls", "Error", MB_ICONERROR )
      exit function
    end if
   
    hWnd = CreateWindowEx( 0, _
                      @"HelloWin", _
                          "The Hello Program", _
                          WS_OVERLAPPEDWINDOW, _
                          CW_USEDEFAULT, _
                          CW_USEDEFAULT, _
                          CW_USEDEFAULT, _
                          CW_USEDEFAULT, _
                          NULL, _
                          NULL, _
                          hInstance, _
                          NULL )
                         

    ShowWindow( hWnd, iCmdShow )
    UpdateWindow( hWnd )
   
    while( GetMessage( @wMsg, NULL, 0, 0 ) <> FALSE )   
        TranslateMessage( @wMsg )
        DispatchMessage( @wMsg )
    wend
   
    function = wMsg.wParam

end function

батник для компиляции:
Код:

@echo off
echo compiling
d:\FreeBasic\fbc test.bas -r -lib
echo assembling
D:\FreeBASIC\bin\win32\as.exe --32 --strip-local-absolute "test.asm" -o "test.o"
echo linking
d:\FreeBasic\bin\win32\ld.exe -e _MAIN -subsystem windows -s --stack 1048576,1048576  --gc-sections -L "D:\FreeBASIC\lib\win32" -L "./" "test.o" -o "test.exe" -( -lgdi32 -lkernel32 -luser32 -)
pause

компиляция с ресурсами.
тут все просто. для компиляции .rc файлов, будем использовать утилиту gorc.exe.

файл dialog.rc
Код:

#include "dialog.bi"

IDD_DLG1 DIALOGEX 6,6,194,102
CAPTION "HELLO WORLD"
FONT 8,"MS Sans Serif"
STYLE 0x10CC0000
EXSTYLE 0x00000080
BEGIN
   CONTROL "Click me",IDC_BTN1,"Button",0x50010000,60,57,74,26,0x00000000
END

файл dialog.bi

Код:


#define IDD_DLG1 1000
#define IDC_BTN1 1001

файл dialog.bas

Код:

#include "windows.bi"
#include "dialog.bi"

declare function DlgProc (byval hwnd as HWND, byval umsg as UINT, byval wparam as WPARAM, byval lparam as LPARAM) as BOOL
                               
sub main cdecl alias "main"()

    ''
    '' Create the Dialog
    ''
    DialogBoxParam( GetModuleHandle( NULL ), cast( LPCSTR, IDD_DLG1 ), NULL, @DlgProc, NULL )
ExitProcess(0)
end sub


function DlgProc (byval hwnd as HWND, byval umsg as UINT, byval wparam as WPARAM, byval lparam as LPARAM) as BOOL
    dim as long id, event
   
    ''
    '' Process message
    ''
    select case uMsg
    case WM_INITDIALOG
       
    ''
    '' Window was closed
    ''
    case WM_CLOSE
       EndDialog( hwnd, 0 )

    case WM_COMMAND
      id    = loword( wParam )
      event = hiword( wParam )

        select case id
        case IDC_BTN1
MessageBox(0,"Press 'ok' to exit programm","message",0)
           EndDialog( hwnd, 0 )
       end select
       
    case else
       return FALSE
   
    end select
   
  return TRUE
end function

батник для компиляции

Код:

@echo off
echo compiling
d:\FreeBasic\fbc dialog.bas -r -lib
echo assembling
d:\freebasic\bin\win32\as.exe --32 --strip-local-absolute "dialog.asm" -o "dialog.o"
echo compiling resource
d:\FreeBasic\bin\win32\gorc /ni /nw /o /fo "dialog.obj" "dialog.rc" ' компилим ресурсы
echo linking
d:\FreeBasic\bin\win32\ld -e _main -subsystem windows -s --stack 1048576,1048576 -L "d:\freebasic\lib\win32" -L "./" "dialog.o" "dialog.obj" -o "dialog.exe" -( -lkernel32 -luser32 -)
pause

все просто, а самое главное, размер бинарника маленький.

создаем dll своими руками.
во FreeBasic, есть возможность создавать динамические файлы dll, но нет возможности обрабатывать внутренние события, которые операционная система посылает главной функции dll "DllMain.
точнее говоря, когда происходит то или иное событие, загрузка, выгрузка и т.д, операционная система вызывает функцию DllMain.
вот программа, в которой реализована функция DllMain, и она же является точкой входа в dll.
так же реализована процедура, которая выводит MessageBox с текстовой строкой на экран.
полное описание функции DllMain, можно посмотреть:
http://vsokovikov.narod.ru/New_MSDN_API/DLL/fn_dllmain.htm

файл mydll.bas

Код:

#include "windows.bi"

function DllMain  alias "DllMain" _
(byval hinstDLL as HINSTANCE, _ ' дескриптор модуля DLL
byval fdwReason as DWORD, _    ' причина вызова функции
lpvReserved as LPVOID) as BOOL 'зарезервированный
select case fdwReason
case DLL_PROCESS_ATTACH
MessageBox(0,"the dll is loaded","dll test",0)
case DLL_PROCESS_DETACH
MessageBox(0,"the dll is unloaded","dll test",0)
case DLL_THREAD_ATTACH
MessageBox(0,"A thread is created in this process","dll test",0)
case DLL_THREAD_DETACH
MessageBox(0,"A thread is destroyed","test dll",0)
end select
return TRUE
end function

sub TestMess alias "TestMess"() export
MessageBox(0,"this is test message from function dll","dll function",0)
end sub

батник для компиляции

Код:

@echo off
echo compiling
d:\FreeBasic\fbc -dll -r -lib mydll.bas
echo assembling
d:\freebasic\bin\win32\as.exe --32 --strip-local-absolute "mydll.asm" -o "mydll.o"
echo linking
d:\FreeBasic\bin\win32\ld -subsystem windows --dll --enable-stdcall-fixup --export-dynamic -e _DllMain@12 -s --stack 1048576,1048576 -L "d:\freebasic\lib\win32" -L "./" "mydll.o" -o "mydll.dll" -( -lkernel32 -luser32 -) --output-def "mydll.def"
echo creating import library
d:\FreeBasic\bin\win32\dlltool --def "mydll.def" --dllname "mydll.dll" --output-lib "libmydll.dll.a"
pause

программа, для вызова процедуры из mydll.dll.

файл testmydll.bas

Код:

#include "windows.bi"
#inclib "mydll"
declare sub TestMess alias "TestMess"()

sub main cdecl alias "main" ()
TestMess()
ExitProcess(0)
end sub

батник для компиляции

Код:

@echo off
echo compiling
d:\FreeBasic\fbc testmydll.bas -r -lib
echo assembling
d:\freebasic\bin\win32\as.exe --32 --strip-local-absolute "testmydll.asm" -o "testmydll.o"
echo linking
d:\FreeBasic\bin\win32\ld -e _main -subsystem windows -s --stack 1048576,1048576 -L "d:\freebasic\lib\win32" -L "./" "testmydll.o" -o "testmydll.exe" -( -lmydll -lkernel32 -)
pause

при линковке, мы указываем точку входа в dll таким образом
-e DllMain@12
функция с соглашением stdcall, помимо названия, имеет префикс, число байт отводимых под параметры.
если такую функцию написать в блоке extern ... end extern с префиксом "windows-ms", функция будет тоже stdcall, но без префиксов. соответственно, при линковке в точке входа префиксы писать ненадо.

после запуска программы testmydll.exe, при загрузке dll, появится соответствующее сообщение.
потом выполница наша процедура, которая выдаст нам сообщение.
после завершения программы и выгрузки dll, будет еще одно сообщение.
надеюсь, что данная писанина вам пригодиться.

electrik

Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 37
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург

Посмотреть профиль

Вернуться к началу Перейти вниз

Молодец!

Сообщение  VerhoLom в Пт Июн 08, 2012 3:00 pm

Если существует просветление в бейсике - это оно! lol!
avatar
VerhoLom

Сообщения : 67
Дата регистрации : 2010-07-06
Возраст : 36

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: платформа win32. Пишем на FreeBasic не используя runtime библиотеки, уменьшаем размер бинарника, делаем dll своими руками, функция WinMain.

Сообщение  DiG. GeRR в Сб Июн 16, 2012 1:42 pm

ExitProcess, не закрывает все открытые дескрипторы файлов, устройств и т.д, как функция end. вы сами должны позаботится о закрытии открытых устройств, файлов, функцией CloseHandle или вызвать специальные функции предназначеные для ьтой или иной задачи.

Exiting a process causes the following:
...
6. All of the object handles opened by the process are closed.
...
(http://msdn.microsoft.com/en-us/library/windows/desktop/ms682658(v=vs.85).aspx)

Так что хендлы можно вручную и не закрывать.

Только вот я одного не пойму - зачем в наше время так издеваться над кодом (ломая кроссплатформенность, в том числе) и собой, чтоб получить выигрыш в пару килобайт?

DiG. GeRR

Сообщения : 101
Дата регистрации : 2009-01-30
Возраст : 25
Откуда : Рудный, Казахстан

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: платформа win32. Пишем на FreeBasic не используя runtime библиотеки, уменьшаем размер бинарника, делаем dll своими руками, функция WinMain.

Сообщение  VerhoLom в Пн Июн 18, 2012 1:31 pm

Не скажите. С ростом объема кода растет и разница в весе. Простой пример: я на офисе делал утилиту для отслеживания вставки съемных носителей (флешек, сидюшек, телефонов и т. д.) в компы и ихнего извлечения. Так разница между использованием ФБшного кода и вот такого метода составила 2 порядка: 8 кб против 16. То что написал электрик - это хороший метод afro
avatar
VerhoLom

Сообщения : 67
Дата регистрации : 2010-07-06
Возраст : 36

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: платформа win32. Пишем на FreeBasic не используя runtime библиотеки, уменьшаем размер бинарника, делаем dll своими руками, функция WinMain.

Сообщение  electrik в Вт Июн 19, 2012 5:30 pm

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

electrik

Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 37
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: платформа win32. Пишем на FreeBasic не используя runtime библиотеки, уменьшаем размер бинарника, делаем dll своими руками, функция WinMain.

Сообщение  Замабувараев в Ср Июн 04, 2014 9:45 pm

А для чего у всех функций main стоит модификатор cdecl? По-моему, там должен быть stdcall (что принято по умолчанию на Windows-платформах).
avatar
Замабувараев

Сообщения : 99
Дата регистрации : 2008-08-20
Возраст : 34
Откуда : Красноярск

Посмотреть профиль http://www.freebasic.su

Вернуться к началу Перейти вниз

Re: платформа win32. Пишем на FreeBasic не используя runtime библиотеки, уменьшаем размер бинарника, делаем dll своими руками, функция WinMain.

Сообщение  electrik в Пт Авг 29, 2014 2:00 pm

cdecl - это так, для того, чтоб небыло префиксов с собаками. можно и через windows-ms. насамом деле, эта функция main только во FreeBasic выглядит как функция, а так, вобщем это точка старта в программу, поэтому, пофиг как тут написано.

electrik

Сообщения : 391
Дата регистрации : 2008-09-02
Возраст : 37
Откуда : галактика Млечный путь, система Солнечная, планета Земля, страна россия, город Санкт Петербург

Посмотреть профиль

Вернуться к началу Перейти вниз

Re: платформа win32. Пишем на FreeBasic не используя runtime библиотеки, уменьшаем размер бинарника, делаем dll своими руками, функция WinMain.

Сообщение  Замабувараев в Вт Окт 07, 2014 3:15 pm

Решил продолжить издевательства над васиком.
Для компиляции без RTL сделал пару функций, преобразующих число в строку. Размер программы 2048 байт.
Фактически, Я пишу на продвинутом бейсико-ассемблере, поэтому программа такая маленькая.
В программе также используются виндовые функции выделенеия памяти из кучи и вывода на консоль.
Исходный код лежит на пастебине
avatar
Замабувараев

Сообщения : 99
Дата регистрации : 2008-08-20
Возраст : 34
Откуда : Красноярск

Посмотреть профиль http://www.freebasic.su

Вернуться к началу Перейти вниз

Re: платформа win32. Пишем на FreeBasic не используя runtime библиотеки, уменьшаем размер бинарника, делаем dll своими руками, функция WinMain.

Сообщение  Замабувараев в Пт Апр 10, 2015 11:17 am

В версии компилятора 1.02.0 в заголовочных файлах содержится код, увеличивающий бинарники на семь килобайт. Этот код нужен только для функций C-runtime.
Это недопустимо много.
Есть два решения.
1. Вручную удалять лишний код из ассемблерных листингов.
2. Автоматизировать процесс удаления.
Вот скрипт RemoveLines.vbs, принимающий в качестве параметра имя файла *.asm удаляющий из этого файла лишний код. Затем такой листинг уже можно подавать на вход ассемблеру.

Код:

' Прочитать файл построчно. Если встретим строку балигн 16, то будем настороже
' Читаем следующую строку, если она попадает в список запрещённых слов
' то ставим флаг, чтобы строку в файл не писать
' далее не пишем строки в файл, пока не встретится ret что?нибудь
Dim objFSO
Dim strParam
Dim objArgs
Set objFSO = CreateObject("Scripting.FileSystemObject")
' Получить параметр программы
Set objArgs = WScript.Arguments
For Each strParam In objArgs
   REM WScript.Echo strParam
   ' Открыть файл на чтение, прочитать до конца, закрыть
   Dim objTS
   Set objTS = objFSO.OpenTextFile(strParam)
   Dim strLines
   strLines = objTS.ReadAll
   REM WScript.Echo strLines
   objTS.Close
   Set objTS = Nothing
   ' Открыть снова на запись
   Set objTS = objFSO.CreateTextFile(strParam)
   ' Разбить строку на массив
   ' Пройтись по массиву
   Dim blnSkipLines
   blnSkipLines = False
   Dim astrLines
   astrLines = Split(strLines, vbCrLf)
   Dim i
   For i = 0 To UBound(astrLines)
      Select Case astrLines(i)
         Case ".balign 16"
            ' Начало, нужно быть готовым
            Select Case astrLines(i + 1)
               Case "_GetCurrentFiber:"
                  ' Ставим флаг, что строку нужно пропустить
                  blnSkipLines = True
               Case "_InterlockedCompareExchange64@20:"
                  ' Ставим флаг, что строку нужно пропустить
                  blnSkipLines = True
               Case "_IN6_IS_ADDR_UNSPECIFIED:"
                  ' Ставим флаг, что строку нужно пропустить
                  blnSkipLines = True
               Case "_IN6_IS_ADDR_LOOPBACK:"
                  ' Ставим флаг, что строку нужно пропустить
                  blnSkipLines = True
               Case "_IN6_IS_ADDR_MULTICAST:"
                  ' Ставим флаг, что строку нужно пропустить
                  blnSkipLines = True
               Case "_IN6_SET_ADDR_UNSPECIFIED:"
                  ' Ставим флаг, что строку нужно пропустить
                  blnSkipLines = True
               Case "_IN6_SET_ADDR_LOOPBACK:"
                  ' Ставим флаг, что строку нужно пропустить
                  blnSkipLines = True
               Case Else
                  ' Записываем
                  objTS.WriteLine(astrLines(i))
            End Select
         Case "ret", "ret 20"
            If blnSkipLines Then
               '  Снимаем флаг и пропускаем эту строку
               blnSkipLines = False
            Else
               objTS.WriteLine(astrLines(i))
            End If
         Case Else
            ' Пишем строку, только если разрешено
            If Not blnSkipLines Then
               objTS.WriteLine(astrLines(i))
            End If
      End Select
   Next
   ' Закрыть файл
   objTS.Close
   Set objTS = Nothing
Next
Set objArgs = Nothing
Set objFSO = Nothing

Использование скрипта:

Код:

cscript //Nologo RemoveLines.vbs "имя файла с ассемблерным листингом.asm" "второй файл.asm"
avatar
Замабувараев

Сообщения : 99
Дата регистрации : 2008-08-20
Возраст : 34
Откуда : Красноярск

Посмотреть профиль http://www.freebasic.su

Вернуться к началу Перейти вниз

Re: платформа win32. Пишем на FreeBasic не используя runtime библиотеки, уменьшаем размер бинарника, делаем dll своими руками, функция WinMain.

Сообщение  Спонсируемый контент


Спонсируемый контент


Вернуться к началу Перейти вниз

Предыдущая тема Следующая тема Вернуться к началу

- Похожие темы

 
Права доступа к этому форуму:
Вы не можете отвечать на сообщения