About a year ago I downloaded, installed, and started using and experimenting with the new GNU CodeBlocks C++ application suite. I was really surprised to find that by setting various compiler optimazition switches I could create smaller executables than I could with PowerBASIC Windows compilers. This is something I always noted with various compilers I used. The Dev-Cpp compier suite is another popular C / C++ development environment written mostly by Colin Laplace. It uses the GNU compiler too. Its executables were always way larger than PowerBASIC's. Microsoft Visual C++ 6 also produced files considerably larger than PowerBASIC's.
Here about a month ago I finally broke down and bought myself Visual Studio 2008 Pro; mostly because I need it to do the Windows CE programming which occupies about half my time. I'll tell 'ya, the exes are really small. I have a neat little ODBC demo I put together the past couple weeks and I coded a C++ version and a PowerBASIC version and the VC9 (Visual Studio 2008 Pro) comes in around 36K and I think the PB one is in the mid to high 40s. CodeBlocks is in around there too. Dev-C++ is way up around a 100K.
I'm just surprised about it, thats all. I'm wondering if anyone knows why? What I think the issue is, but I'm not sure, is that these newer C++ compilers dropped support for non NT operating systems, that is, their exes don't support Win 95-98 anymore. And I also seem to remember reading something about the msvcrt (Microsoft Visual C++ Runtime Library) being considered a part of the operating system now, whereas it wasn't with Win 95-98, etc. So I'm guessing the PowerBASIC compilers are adding content to the exes that VC9 and CodeBlocks aren't? Any thoughts?
Frederick,
C and C++ have always been able to produce smaller EXE files than PB.
However did you use DDT or plain SDK?
Older version of the compiler will also produce smaller EXE files.
...
Makes me look dumb! I think you're right Patrice. I just looked at it real close, checked all the compiler optimazations on my various C++ compilers, and they are all (I didn't check Dev-Cpp) coming in a good bit smaller than the PowerBASIC 9 compiler.
The program is around 1500 lines of PB code that I wrote, and about 2500 for the C++ app. The reason the C++ app has so many more lines (its virtually an exact line by line translation) is that I did my best to follow C++ file organization standards with a header file containing prototypes, etc., for every source code file, and so on. Also, since they are both Sdk style programs (I'd have a real hard time writing a Hello, World! in DDT) I included my home made String class in the C++ program so I could have easier string handling. I think that added four or five hundred lines. Of course, with PB the string handling is part of the compiler and we get it for free, so to speak.
Anyway, VC6 is now giving me around 32K, VC9 about 33K, CodeBlocks 46k, and PowerBASIC around 63K. Now I'm wondering how I ever came up with the conclusions I voiced in my first post. Probably I wasn't checking that I had optimazitions turned on for smaller size, and of course there are several gradations there.
In any case, its a neat little program. I wanted to do something to get myself acclamated to the new Visual Studio 2008 I just bought, and so I picked doing an ODBC demo. I'll probably post it soon, both the C++ and PowerBASIC version. It has a main form with four buttons. The top button scours the registry for ODBC drivers and dumps them and their attributes to a little scrollable output screen.
The second button uses the MS Jet ODBC Driver to dump an Excel spreadsheet to an instance of the output screen.
The 3rd button creates an Access Database, a table within the database, inserts a few records, then dumps the records to another instance of the output screen.
The fourth button does the same with Sql Server Express or MSDE.
The output screens all handle their own memory allocations/deallocations and so you can keep clicking buttons to your hearts content filling up your screen with output windows. When you click a button the 2nd, 3rd time, etc, records are just added to the database you already created on your first click. Also, the program uses threads in the database processing routines.
Actually, even though I've played around quite a bit with PowerBASIC's new class capabilities, this is the first time I've really created something with it that I'll actually be able to use in my 'production' programs. I've been kind of slow to adapt. Guess I'm mostly a procedural coder. My wrapper class for the ODBC functionality turned out really neat though. I enjoyed doing it and seeing it work.
I just wrote me an empty app and it was 6kb.
I know that when you add certain functionality like CURDIR$ it will add 2kb.
It comes in 'run-time' blocks.
Dialog new makes the exe 22kb
I don't really mind but i usually start of with about 70kb for tiny apps, would be nice to have less then 40kb though
How did you get the 6K Edwin? Using %NOMDI (or something like that) and %USEMACROS ?
Be curious to see what a Hello, World Sdk GUI app using the Console Compiler would be???
Easy:
#COMPILE EXE
#DIM ALL
FUNCTION PBMAIN () AS LONG
? "Hello Word"
END FUNCTION
6 Kb.
With PBCC, 7Kb (PRINT uses more code that MSGBOX).
Ha! No, I meant just using Sdk code for a CreateWindow() and WndProc() in a CC50 program to see what effect that had. Its certainly no big deal. The idea just occurred to me though to see if leaving out a lot of the GUI machinery in PB Win90 would make the exe smaller. I didn't know it could be trimmed down to the size Edwin got. Mine bare bones tests were a little bigger.
This compiles at a size of 9 KB, both in PBWIN 9.x and PBCC 5.x.
#COMPILE EXE
#DIM ALL
%NULL = 0
%WHITE_BRUSH = 0
%ANSI_VAR_FONT = 12
%CS_VREDRAW = &H0001???
%CS_HREDRAW = &H0002???
%IDC_ARROW = 32512???
%COLOR_BTNFACE = 15&
%COLOR_3DFACE = %COLOR_BTNFACE
%IDI_APPLICATION = 32512???
%SPI_GETWORKAREA = &H0030???
%WM_SETFONT = &H0030???
%DT_CENTER = &H00000001???
%DT_VCENTER = &H00000004???
%DT_SINGLELINE = &H00000020???
TYPE WNDCLASSEXA
cbSize AS DWORD ' UINT cbSize
' /* Win 3.x */
style AS DWORD ' UINT style
lpfnWndProc AS DWORD ' WNDPROC lpfnWndProc
cbClsExtra AS LONG ' int cbClsExtra
cbWndExtra AS LONG ' int cbWndExtra
hInstance AS DWORD ' HINSTANCE hInstance
hIcon AS DWORD ' HICON hIcon
hCursor AS DWORD ' HCURSOR hCursor
hbrBackground AS DWORD ' HBRUSH hbrBackground
lpszMenuName AS ASCIIZ PTR ' LPCSTR lpszMenuName
lpszClassName AS ASCIIZ PTR ' LPCSTR lpszClassName
' /* Win 4.0 */
hIconSm AS DWORD ' HICON hIconSm
END TYPE
MACRO WNDCLASSEX = WNDCLASSEXA
TYPE tagMSG
hwnd AS DWORD ' HWND hwnd
message AS DWORD ' UINT message
wParam AS DWORD ' WPARAM wParam
lParam AS LONG ' LPARAM lParam
time AS DWORD ' DWORD time
pt AS POINT ' POINT pt
END TYPE
TYPE RECT
nLeft AS LONG ' LONG
nTop AS LONG ' LONG
nRight AS LONG ' LONG
nBottom AS LONG ' LONG
END TYPE
TYPE PAINTSTRUCT
hDC AS DWORD ' HDC hdc
fErase AS LONG ' BOOL fErase
rcPaint AS RECT ' RECT rcPaint
fRestore AS LONG ' BOOL fRestore
fIncUpdate AS LONG ' BOOL fIncUpdate
rgbReserved (31) AS BYTE ' BYTE rgbReserved[32]
END TYPE
DECLARE SUB PostQuitMessage LIB "USER32.DLL" ALIAS "PostQuitMessage" ( _
BYVAL nExitCode AS LONG _ ' __in int nExitCode
) ' VOID
DECLARE FUNCTION DefWindowProc LIB "USER32.DLL" ALIAS "DefWindowProcA" ( _
BYVAL hWnd AS DWORD _ ' __in HWND hWnd
, BYVAL Msg AS DWORD _ ' __in UINT Msg
, BYVAL wParam AS DWORD _ ' __in WPARAM wParam
, BYVAL lParam AS LONG _ ' __in LPARAM lParam
) AS LONG ' LRESULT
DECLARE FUNCTION DrawText LIB "USER32.DLL" ALIAS "DrawTextA" ( _
BYVAL hdc AS DWORD _ ' __in HDC hdc
, BYREF lpchText AS ASCIIZ _ ' __in LPCSTR lpchText
, BYVAL cchText AS LONG _ ' __in int cchText
, BYREF lprc AS RECT _ ' __in_out LPRECT lprc
, BYVAL format AS DWORD _ ' __in UINT format
) AS LONG ' int
DECLARE FUNCTION EndPaint LIB "USER32.DLL" ALIAS "EndPaint" ( _
BYVAL hWnd AS DWORD _ ' __in HWND hWnd
, BYREF lpPaint AS PAINTSTRUCT _ ' __in CONST PAINTSTRUCT *lpPaint
) AS LONG ' BOOL
DECLARE FUNCTION GetStockObject LIB "GDI32.DLL" ALIAS "GetStockObject" ( _
BYVAL i AS LONG _ ' __in int i
) AS DWORD ' HGDIOBJ
DECLARE FUNCTION RegisterClassEx LIB "USER32.DLL" ALIAS "RegisterClassExA" ( _
BYREF lpwcx AS WNDCLASSEXA _ ' __in CONST WNDCLASSEXA *lpwcx
) AS WORD ' ATOM
DECLARE FUNCTION LoadCursor LIB "USER32.DLL" ALIAS "LoadCursorA" ( _
BYVAL hInstance AS DWORD _ ' __in_opt HINSTANCE hInstance
, BYREF lpCursorName AS ASCIIZ _ ' __in LPCSTR lpCursorName
) AS DWORD ' HCURSOR
DECLARE FUNCTION LoadIcon LIB "USER32.DLL" ALIAS "LoadIconA" ( _
BYVAL hInstance AS DWORD _ ' __in_opt HINSTANCE hInstance
, BYREF lpIconName AS ASCIIZ _ ' __in LPCSTR lpIconName
) AS DWORD ' HICON
DECLARE FUNCTION SystemParametersInfo LIB "USER32.DLL" ALIAS "SystemParametersInfoA" ( _
BYVAL uiAction AS DWORD _ ' __in UINT uiAction
, BYVAL uiParam AS DWORD _ ' __in UINT uiParam
, BYREF pvParam AS ANY _ ' __in_out_opt PVOID pvParam
, BYVAL fWinIni AS DWORD _ ' __in UINT fWinIni
) AS LONG ' BOOL
DECLARE FUNCTION CreateWindowEx LIB "USER32.DLL" ALIAS "CreateWindowExA" ( _
BYVAL dwExStyle AS DWORD _ ' __in DWORD dwExStyle
, BYREF lpClassName AS ASCIIZ _ ' __in_opt LPCSTR lpClassName
, BYREF lpWindowName AS ASCIIZ _ ' __in_opt LPCSTR lpWindowName
, BYVAL dwStyle AS DWORD _ ' __in DWORD dwStyle
, BYVAL X AS LONG _ ' __in int X
, BYVAL Y AS LONG _ ' __in int Y
, BYVAL nWidth AS LONG _ ' __in int nWidth
, BYVAL nHeight AS LONG _ ' __in int nHeight
, BYVAL hWndParent AS DWORD _ ' __in_opt HWND hWndParent
, BYVAL hMenu AS DWORD _ ' __in_opt HMENU hMenu
, BYVAL hInstance AS DWORD _ ' __in_opt HINSTANCE hInstance
, BYREF lpParam AS ANY _ ' __in_opt LPVOID lpParam
) AS DWORD ' HWND
DECLARE FUNCTION SendMessage LIB "USER32.DLL" ALIAS "SendMessageA" ( _
BYVAL hWnd AS DWORD _ ' __in HWND hWnd
, BYVAL Msg AS DWORD _ ' __in UINT Msg
, BYVAL wParam AS DWORD _ ' __in WPARAM wParam
, BYVAL lParam AS LONG _ ' __in LPARAM lParam
) AS LONG ' LRESULT
DECLARE FUNCTION ShowWindow LIB "USER32.DLL" ALIAS "ShowWindow" ( _
BYVAL hWnd AS DWORD _ ' __in HWND hWnd
, BYVAL nCmdShow AS LONG _ ' __in int nCmdShow
) AS LONG ' BOOL
DECLARE FUNCTION UpdateWindow LIB "USER32.DLL" ALIAS "UpdateWindow" ( _
BYVAL hWnd AS DWORD _ ' __in HWND hWnd
) AS LONG ' BOOL
DECLARE FUNCTION GetMessage LIB "USER32.DLL" ALIAS "GetMessageA" ( _
BYREF lpMsg AS tagMSG _ ' __out LPMSG lpMsg
, BYVAL hWnd AS DWORD _ ' __in_opt HWND hWnd
, BYVAL wMsgFilterMin AS DWORD _ ' __in UINT wMsgFilterMin
, BYVAL wMsgFilterMax AS DWORD _ ' __in UINT wMsgFilterMax
) AS LONG ' BOOL
DECLARE FUNCTION IsDialogMessage LIB "USER32.DLL" ALIAS "IsDialogMessageA" ( _
BYVAL hDlg AS DWORD _ ' __in HWND hDlg
, BYREF lpMsg AS tagMSG _ ' __in LPMSG lpMsg
) AS LONG ' BOOL
DECLARE FUNCTION TranslateMessage LIB "USER32.DLL" ALIAS "TranslateMessage" ( _
BYREF lpMsg AS tagMSG _ ' __in CONST MSG *lpMsg
) AS LONG ' BOOL
DECLARE FUNCTION DispatchMessage LIB "USER32.DLL" ALIAS "DispatchMessageA" ( _
BYREF lpMsg AS tagMSG _ ' __in CONST MSG *lpMsg
) AS LONG ' LRESULT
DECLARE FUNCTION GetClientRect LIB "USER32.DLL" ALIAS "GetClientRect" ( _
BYVAL hWnd AS DWORD _ ' __in HWND hWnd
, BYREF lpRect AS RECT _ ' __out LPRECT lpRect
) AS LONG ' BOOL
DECLARE FUNCTION BeginPaint LIB "USER32.DLL" ALIAS "BeginPaint" ( _
BYVAL hWnd AS DWORD _ ' __in HWND hWnd
, BYREF lpPaint AS PAINTSTRUCT _ ' __out LPPAINTSTRUCT lpPaint
) AS DWORD ' HDC
' ========================================================================================
' Main
' ========================================================================================
FUNCTION WinMain (BYVAL hInstance AS DWORD, BYVAL hPrevInstance AS DWORD, BYVAL lpszCmdLine AS ASCIIZ PTR, BYVAL nCmdShow AS LONG) AS LONG
LOCAL hWndMain AS DWORD
LOCAL hCtl AS DWORD
LOCAL hFont AS DWORD
LOCAL wcex AS WNDCLASSEX
LOCAL szClassName AS ASCIIZ * 80
LOCAL rc AS RECT
LOCAL szCaption AS ASCIIZ * 255
LOCAL nLeft AS LONG
LOCAL nTop AS LONG
LOCAL nWidth AS LONG
LOCAL nHeight AS LONG
hFont = GetStockObject(%ANSI_VAR_FONT)
' Register the window class
szClassName = "MyClassName"
wcex.cbSize = SIZEOF(wcex)
wcex.style = %CS_HREDRAW OR %CS_VREDRAW
wcex.lpfnWndProc = CODEPTR(WndProc)
wcex.cbClsExtra = 0
wcex.cbWndExtra = 0
wcex.hInstance = hInstance
wcex.hCursor = LoadCursor (%NULL, BYVAL %IDC_ARROW)
wcex.hbrBackground = GetStockObject(%WHITE_BRUSH)
wcex.lpszMenuName = %NULL
wcex.lpszClassName = VARPTR(szClassName)
wcex.hIcon = LoadIcon (%NULL, BYVAL %IDI_APPLICATION) ' Sample, if resource icon: LoadIcon(hInst, "APPICON")
wcex.hIconSm = LoadIcon (%NULL, BYVAL %IDI_APPLICATION) ' Remember to set small icon too..
RegisterClassEx wcex
' Window caption
szCaption = "SDK Main Window"
' Retrieve the size of the working area
SystemParametersInfo %SPI_GETWORKAREA, 0, BYVAL VARPTR(rc), 0
' Calculate the position and size of the window
nWidth = (((rc.nRight - rc.nLeft)) + 2) * 0.75 ' 75% of the client screen width
nHeight = (((rc.nBottom - rc.nTop)) + 2) * 0.70 ' 70% of the client screen height
nLeft = ((rc.nRight - rc.nLeft) \ 2) - nWidth \ 2
nTop = ((rc.nBottom - rc.nTop) \ 2) - (nHeight \ 2)
' Create a window using the registered class
hWndMain = CreateWindowEx(%WS_EX_CONTROLPARENT, _ ' extended style
szClassName, _ ' window class name
szCaption, _ ' window caption
%WS_OVERLAPPEDWINDOW OR _
%WS_CLIPCHILDREN, _ ' window styles
nLeft, _ ' initial x position
nTop, _ ' initial y position
nWidth, _ ' initial x size
nHeight, _ ' initial y size
%NULL, _ ' parent window handle
0, _ ' window menu handle
hInstance, _ ' program instance handle
BYVAL %NULL) ' creation parameters
hCtl = CreateWindowEx(0, "BUTTON", "&Ok", %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %BS_FLAT, _
0, 0, 0, 0, hWndMain, %IDOK, hInstance, BYVAL %NULL)
IF hFont THEN SendMessage hCtl, %WM_SETFONT, hFont, 0
hCtl = CreateWindowEx(0, "BUTTON", "&Close", %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %BS_FLAT, _
0, 0, 0, 0, hWndMain, %IDCANCEL, hInstance, BYVAL %NULL)
IF hFont THEN SendMessage hCtl, %WM_SETFONT, hFont, 0
' Show the window
ShowWindow hWndMain, nCmdShow
UpdateWindow hWndMain
' Message handler loop
LOCAL uMsg AS tagMsg
WHILE GetMessage(uMsg, %NULL, 0, 0)
IF ISFALSE IsDialogMessage(hWndMain, uMsg) THEN
TranslateMessage uMsg
DispatchMessage uMsg
END IF
WEND
FUNCTION = uMsg.wParam
END FUNCTION
' ========================================================================================
' ========================================================================================
' Main dialog callback.
' ========================================================================================
FUNCTION WndProc (BYVAL hWnd AS DWORD, BYVAL message AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
LOCAL hdc AS DWORD
LOCAL ps AS PAINTSTRUCT
LOCAL rc AS RECT
SELECT CASE message
CASE %WM_PAINT
hdc = BeginPaint(hWnd, ps)
GetClientRect hWnd, rc
DrawText hdc, "Hello, Windows!", -1, rc, %DT_SINGLELINE OR %DT_CENTER OR %DT_VCENTER
EndPaint(hWnd, ps)
FUNCTION = 0
EXIT FUNCTION
CASE %WM_DESTROY
PostQuitMessage 0
FUNCTION = 0
EXIT FUNCTION
END SELECT
FUNCTION = DefWindowProc(hWnd, message, wParam, lParam)
END FUNCTION
' ========================================================================================
Could be reduced to 7680 bytes, the smallest size possible with PB9.
and 7168 bytes with PB8 :)
i am unable to check with PBWIN7 and PBDLL6 because they won't run on VISTA 64 (16-bit compiler), but they will probably produce something around 6500 bytes.
...
The PE format imposes a lower limit (practically speaking) of aoround 2.5 k, Even if it is mostly empty space. Almost all the remainder is run-time library. The zip below contains 3 test progs created with the thinBasic/Oxygen assembler. The Hello_box is 2.5k, the HelloWin with message loop is 3.5k and the 4 Port Opengl viewer is 8.5K. These programs have no run-time extras to carry. You can see these 3 progs together zip down to 5.7K :)
PB by default is small enough but 'grows rapidly' when you use its BASIC features.
This is no real complaint, sizes < 100KB suits me fine.
Though i made the request for optimized static linking and should have impact on BASIC features imo.
Quotei am unable to check with PBWIN7 and PBDLL6 because they won't run on VISTA 64 (16-bit compiler), but they will probably produce something around 6500 bytes.
Hey Patrice, have you tried XPMode and PB7?
Carlo,
Even when using the compatibility mode, you can't run 16-bit code on VISTA/W7 64-bit
...
he did not say "compatibility mode"
Added:
I may be confused as well, i assumed XPMode for Windows 7 but then.. there is no retail of w7 yet :)
(XP on virtual pc)
Here is the smallest one ;D
6656 bytes
#COMPILE EXE
DECLARE FUNCTION MessageBox LIB "USER32.DLL" ALIAS "MessageBoxA" (BYVAL hWnd AS DWORD, lpText AS ASCIIZ, lpCaption AS ASCIIZ, BYVAL dwType AS DWORD) AS LONG
FUNCTION WinMain (BYVAL hInstance AS DWORD, BYVAL hPrevInstance AS DWORD, BYVAL lpszCmdLine AS ASCIIZ PTR, BYVAL nCmdShow AS LONG) AS LONG
MessageBox (0, "Hello world!", "SDK MessageBox", 0)
END FUNCTION
...
The minimum section size in a PE file is 512 bytes but the memory image size goes in steps of 4k. So with the PE header, the code section, the import and export sections and the data section this will be 20k, - possibly double this under Vista.
Using Patrice's code I get a size of 6656 not 6556.
#OPTIMIZE SIZE doesn't have an effect on this program but could have an effect on larger programs and no one has mentioned using this option when testing PB programs.
The absolute smallest program you can get is:
#COMPILE EXE
FUNCTION WINMAIN (BYVAL hInstance AS DWORD, BYVAL hPrevInstance AS DWORD, BYVAL lpszCmdLine AS ASCIIZ PTR, BYVAL nCmdShow AS LONG) AS LONG
END FUNCTION
or
#COMPILE EXE
FUNCTION PBMAIN
END FUNCTION
Both compile to 5120 bytes and all they do is start up and immediately shut down.
Just the other day I drug out my old PB 6 disks (floppies!!!!) and installed it on one of my XPs. My basic template app which includes my usual function pointer message cracker scheme & whatnot came in at 11K without doing anything special do eliminate stuff. That's about 6K less than with PB9. I've honestly been considering using that some because most of the stuff I do doesn't involve anything especially modern or high powered.
Yes, 6656 is the correct size (6556 was a typo).
#OPTIMIZE SIZE over #OPTIMIZE SPEED, i never see a big difference when used with SDK code.
...
QuoteI've honestly been considering using that some because most of the stuff I do doesn't involve anything especially modern or high powered.
Is it a Question of honour, Fred?
Because I don't know what device you are coding for, but if its todays PC's, even a programm with 6656 will fit into a todays keyhole. If Bob would call me and ask me "Shall I use a day to crimple the runtime into pieces and only link the needed parts", I'd tell him to do something useful instead. Maybe make PB 64...
Emotionally I understand you idea, maybe thats why we use PowerBasic.
But seen under modern light, 6 kb is just the shit of a fly.
Yes, you are right Theo. I havn't had PB 6 on any of my computers for years, but I had to reinstall it just recently to deal with a vary large old project that had never been upgraded. Over the years though, I have occasionally wondered if there were any compelling reasons to use older versions of software when money wasn't really an issue and when one even had the newer versions. I recall Chris Boss mentioning several times that he still used PB 6.
There certainly aren't very many bugs with the PowerBASIC compilers, so its usually not an issue of justifying the newer compiler in terms of hopes that some particular bug might have been fixed. Its predominently an issue of newer features - such as COM support.
I have always greatly appreciated the small sizes of both my PowerBASIC and my C++ programs. Some of them are used in network settings, so a 600K program loads over a network much faster than a 6MB one.
The standalone sprite engine I am currently work on is written using PB 6.01.
It compiles to about 47 KB.
When compiled using PB 9.01 it compiles to about 58 KB.
So when trying to compile very small DLL's, later versions of the PB compiler will add some extra runtime code so they will be larger.
Since I don't need the extra features of PB 9.01 (ie. DDT command set) since I use the Windows API exclusively, the older compiler saves a bit on the final size of the app.
Older compiler like 6.01, do not work on 64-bit computers.
I, for myself, would like to have a bare bone compiler without extra features (or with a switch to turn them off) ;D
...