Cutting down on executable size

Pages: 12
Hello Everyone!

I am doing a simple program on downloading files to PC. I'm not using cURL or something like that (because of poor lack of documentation), instead, I found some examples that make use of the wininet library and involves writing some class that manages download.

Now, the only libraries I am using are iostream, string, fstream, windows, cstdlib, and wininet. But the output executable has a size of 1.3MB! How could something like that happen!

Any suggestions on how to decrease size?
closed account (1yR4jE8b)
1. Dynamically link to the C++ runtime.
2. Compile the executable without debugging information.
3. Compile with 'optimize for size' flags.
Last edited on
4. Remove #iostream and fstream if you can, and use low level <cstdio> instead. If your other includes don't need those libs and headers, that could save you in the order of 250 - 400K.
Declaring #define WIN32_LEAN_AND_MEAN at the top of your program will cut down on the amount of stuff that is included from the "windows.h" header. Don't use Visual Studio, for some reason the executables from this IDE come out much larger then they do from any others.
Also, if you dynamically link (MD, I think) you might not be saving yourself anything because you'll then likely have to include the Visual Studio (if that's what you are using) redistributables along with your now smaller exe if you want it to work on systems without those components.
Last edited on
You guys (except darkestfright) are all missing the obvious.

do a release build.

1.3 MB for a simple program is absurd, even for VS. The only explanation is he's looking at the debug exe.


What you #include doesn't matter. Unnecessary #includes will slow compiler times, but won't change exe size in an optimized build.

Similar story for WIN32_LEAN_AND_MEAN

Dynamically linking to the runtime would reduce the exe size, but as freddie said that is a double-edged sword because then you need the runtime DLLs.


If the release build is still too large for your tastes, then darkestfright's #3 suggestion of optimizing for size would be the next move.

EDIT: I just realized darkestfright already said this in his #2 option -- but he worded it differently.
Last edited on
closed account (1yR4jE8b)
1.3 MB for a simple program is absurd, even for VS.


Nothing compared to the 5mB 'hello world' program that the TDM-MinGW version of gcc creates because it statically links everything.
I worked on VB a few times when I was a kid and I remember that a [useful] program came out 56K only!

I'll try your tips, and as Disch said, I think, is that if I do a dynamic link I would need the runtime libraries, question: So if I do not compile neither header file (iostream, windows) and use dynamic linking (I still don't know how to do that), I would save size. I mean, every windows OS has a windows.h in dll, roughly put?
Just... press 'release mode' and build again. I don't have a single self-made exe that goes above 100kb. (Though I must say my debug builds generally don't go over 500, so 1.3mb for a simple program is quite shocking.)
Hi Toni! Haven't heard from you in awhile.

I never take interest in anything concerning internet programming, networks, or anything like that, because I
typically work with Win32, user interfaces, databases, technical algorithms concerning the kind of work I do,
so on and so forth, but the reason I replied to your post about wininet and downloading a file is because I do
take interest in program executable size. I'm not certain why I do, its likely just me I guess. I know all
the arguements about why it doesn't matter anymore what with the exploding sizes of RAM and hard disks. But
I suppose its because I'm old and started coding years ago when these things really did matter, and
programmers prided themselves on writing tight fast code that used a minimum of resources. With C or C++ one
can write extremely small fast code; but one can also write bloatware too. Coders who do other languages for
a long time have lambasted C++ about the very issue you raised, i.e., bloated program sizes. But as I
mentioned, it isn't the language that is bloating binaries, its the coder, the coding style, and the libraries
used.

Out of curiousity, when I saw your post I did a search over in the PowerBASIC forums under their downloads
section and I came up with what looks to me like what you are trying to do. However, maybe it isn't - like I
said, I don't do any kind of network programming, although I am a very experienced Win32 developer. In any
case I downloaded the code and compiled it with several versions of PowerBASIC and I'm seeing executable sizes
in the 22K - 23K range. Here is the code. Please forgive me if this isn't what you are trying to do...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
'------------------------------------------------
'
' Unattended FTP File Download (Sorta)
'
' May 24nd, 2000
'
' Developed under WinNT 4.0 SP5
' WININET.DLL Version 5.00.2314.1003
'
' Target: PB/CC V2.0, PB/DLL V6.0
'
' Public Domain - Your own risk
'
' Notes:
' Will launch the default Internet connection
' dialup as written.
'------------------------------------------------
#Compile Exe
#Dim All
#Include "WIN32API.INC"
'
' Access types
%INTERNET_OPEN_TYPE_PRECONFIG                   = &H0 ' use registry configuration
%INTERNET_OPEN_TYPE_DIRECT                      = &H1 ' direct TO net
%INTERNET_OPEN_TYPE_PROXY                       = &H3 ' via named proxy
%INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY = &H4 ' prevent USING java/script/INS
' Service types
%INTERNET_SERVICE_FTP                           = &H1
%INTERNET_SERVICE_GOPHER                        = &H2
%INTERNET_SERVICE_HTTP                          = &H3
' Port definitions
%INTERNET_INVALID_PORT_NUMBER                   = 0 ' use the protocol-specific DEFAULT
%INTERNET_DEFAULT_FTP_PORT                      = 21 ' DEFAULT FOR FTP servers
%INTERNET_DEFAULT_GOPHER_PORT                   = 70 ' " " gopher "
%INTERNET_DEFAULT_HTTP_PORT                     = 80 ' " " HTTP "
%INTERNET_DEFAULT_HTTPS_PORT                    = 443 ' " " HTTPS "
%INTERNET_DEFAULT_SOCKS_PORT                    = 1080 ' DEFAULT FOR SOCKS firewall servers.
%INTERNET_FLAG_PASSIVE                          = &H8000000 ' used FOR FTP connections
' File transfer methods
%FTP_TRANSFER_TYPE_UNKNOWN                      = &H0
%FTP_TRANSFER_TYPE_ASCII                        = &H1
%FTP_TRANSFER_TYPE_BINARY                       = &H2
'
Declare Function InternetOpen Lib "wininet.dll" Alias "InternetOpenA" _    ' Failure: %NULL, Success: Handle to the connection
( _
  lpszAgent As Long, _ ' Pointer to the calling entity
  dwAccessType As Long, _ ' Type of access required
  lpszProxyName As Long, _ ' Pointer to the proxy's name
  lpszProxyBypass As Long, _ ' Pointer to addresses to bypass proxy
  dwFlags As Long _
) As Long ' Unsigned long integer


Declare Function InternetConnect Lib "wininet.dll" Alias "InternetConnectA" _
( _
  hInternet As Long, _
  lpszServerName As Long, _
  nServerPort As Long, _
  lpszUserName As Long, _
  lpszPassword As Long, _
  dwService As Long, _
  dwFlags As Long, _
  dwContext As Long _
) As Long

Declare Function FtpGetFile Lib "wininet.dll" Alias "FtpGetFileA" _
( _
  hConnect As Long, _
  lpszRemoteFile As Long, _
  lpszNewFile As Long, _
  fFailIfExists As Long, _
  dwFlagsAndAttributes As Long, _
  dwFlags As Long, _
  dwContext As Long _
) As Long

Declare Function InternetCloseHandle Lib "wininet.dll" Alias "InternetCloseHandle" (hInternet As Long) As Long
Declare Function GetUsernameAndPassword(lpszUserName As Asciiz,lpszPassword As Asciiz) As Long
Declare Function GetPassword() As String
'
' Globals
Global hIOpen As Long
Global hIConnect As Long
'
Function PBMain()
  Local lResult       As Long
  Local Agent         As Asciiz * 256
  Local TheFtpServer  As Asciiz * 256
  Local rsvp          As Long
  Local RemoteFile    As Asciiz * 256
  Local LocalFile     As Asciiz * 256
  Local lpszUserName  As Asciiz * 16
  Local lpszPassword  As Asciiz * 12

  Agent = "PB/DLL WinInet" ' <== customize HTTP response with this
  If Instr(Command$,"?") Then GoTo helpme
  '
  ' Open an Internet session as predefined in the registry
  hIOpen = InternetOpen(ByVal VarPtr(Agent), ByVal %INTERNET_OPEN_TYPE_PRECONFIG, ByVal %NULL, ByVal %NULL, ByVal 0)

  ' If it fails exit
  If hIOpen = %NULL Then
     StdOut "Could not open an Internet connection"
     Exit Function
  End If

  TheFtpServer = Parse$(Trim$(Command$)," ",1)' <=== your FTP server here
  lResult = GetUsernameAndPassword(lpszUserName,lpszPassword)
  hIConnect = InternetConnect _
  ( _
    ByVal hIOpen, _
    ByVal VarPtr(TheFtpServer), _
    ByVal %INTERNET_INVALID_PORT_NUMBER, _
    ByVal VarPtr(lpszUserName), _
    ByVal VarPtr(lpszPassword), _
    ByVal %INTERNET_SERVICE_FTP, _
    ByVal 0, _
    ByVal 0 _
  )

  If hIConnect = %NULL Then
     InternetCloseHandle(hIOpen)
     StdOut "FTP Connect Failed " & Str$(hIConnect)
     Exit Function
  End If
  '
  RemoteFile = Parse$(Trim$(Command$)," ",2)' <== the file to GET
  LocalFile = Parse$(Trim$(Command$)," ",3) ' <== where you want to PUT it

  StdOut "Retrieving " + RemoteFile + " from " + TheFtpServer

  rsvp = FtpGetFile _
  ( _
    ByVal hIConnect, _
    ByVal VarPtr(RemoteFile), _
    ByVal VarPtr(LocalFile), _
    ByVal %FALSE, _
    ByVal %FILE_ATTRIBUTE_NORMAL, _
    ByVal %FTP_TRANSFER_TYPE_UNKNOWN, _
    ByVal 0 _
  )

  If rsvp = %FALSE Then StdOut "File Transfer failed."
  '
  ' Close all the handles
  InternetCloseHandle(hIConnect)
  InternetCloseHandle(hIOpen)
  Exit Function
  '
  '

  HELPME:
  StdOut "AppName v1.00"
  StdOut "Useage: PBFTP <host/ipaddress> <remotefile> <localfile>
  StdOut
  StdOut "You will be prompted for username/password"
  StdOut "If using anonymous enter ""anonymous"" for username"
  StdOut "and your email address for a password"
End Function
'
'

Function GetUsernameAndPassword(lpszUserName As Asciiz,lpszPassword As Asciiz) As Long
  StdOut "userName: ";
  StdIn Line lpszUserName
  lpszPassword = GetPassword
End Function
'
'
Function GetPassword() As String
  Local x As Long
  Local y As Long
  Local i As Long
  Local buff As String

  StdOut "Password: ";
  Do
    i = Asc(WaitKey$)
    Select Case i
      Case 8 'BACKSPACE
        x = CursorY
        y = CursorX
        Locate X,Y - 1
        StdOut Chr$(32);
        Locate X,Y - 1
        If Len(buff) > 0 Then buff = Left$(buff,Len(Buff) - 1)
      Case 13,27 '<ENTER><ESC>
        StdOut
        Exit Do
      Case Else
        buff = buff + Chr$(i)
        StdOut "*";
        i = 0
    End Select
    Function = buff
  Loop
End Function 


continued...
Dear [Mr.] freddie1,

Well you might be happy to know that when I finish this whole get-file-from-web thing I'd be working with GUI, old-school win32 style ofcourse (I can't settle for those interfaces!) And perhaps, by then, I'd come crawling back asking for help on my win32.

I'm sorry to tell you that I do not know BASIC, I've done a little VB when I was little, but never serious stuff. I appreciate you helping me out with some code, but what I actually want is help in a language I am relatively comfortable with (C++), so I can get what I am coding. Nevertheless, I am very thankful.

My question now is, having reduced executable size to 264.50 KB (still above 100 KB), I'm still concerned about a further optimization: I'm working on Windows, so there must be a windows.h equivelent runtime library (dll), I don't know if that is technically sound. If so, how can I include all those libraries? I mean, if the DLLs exist, I can cut down on compiling code that is already compiled and residing somewhere in the memory. I'm thinking of a command like this, please tolerate my ignorance:

#include library.dll

Recap:

(1) Linking DLLs (haven't tried yet)
(2) LEAN_AND_MEAN, nothing
(3) Optimize for size (with optimize for speed), nothing
(4) Optimize for size (with optimize for speed turned off), 1 KB less
(5) Release build, 1.1 MB less

Using MinGW on Code::Blocks
Last edited on
I'm working on Windows, so there must be a windows.h equivelent runtime library (dll), I don't know if that is technically sound. If so, how can I include all those libraries?

Windows has a lot of libraries which represent the WinAPI. You just need to link against them. Some are linked automatically by MinGW (e.g. kernel32), others you have to add to the link libraries when you use any functions from them (e.g. gdi32, wsock32).

What's inflating your executable so much is the statically linked C++ runtime, which is not part of the WinAPI. Particularly the C++ stream classes and the locale library that is pulled in as a result is very large. If the size bothers you, you can look for alternatives to C++ streams. As a replacement for stringstream, boost's lexical_cast in combination with the string algos usually is a better alternative.
As for iostream and fstream, I don't know of any lightweight alternatives (but that is not to say there aren't any). If you have the necessary experience, I would recommend writing your own.
Edit: you might want to take a look at http://www.fastformat.org/
Last edited on

Well you might be happy to know that when I finish this whole get-file-from-web thing I'd be working with GUI, old-school win32 style ofcourse (I can't settle for those interfaces!) And perhaps, by then, I'd come crawling back asking for help on my win32.


Wonderful! You're my kind of man!


I'm sorry to tell you that I do not know BASIC, I've done a little VB when I was little, but never serious stuff. I appreciate you helping me out with some code, but what I actually want is help in a language I am relatively comfortable with (C++), so I can get what I am coding. Nevertheless, I am very thankful.


My point in providing that code was just to show you that a target of somewhere in the rough neighborhood of 15 K to 30 K was a realistic and achievable target. However, that program did show the Win32 functions likely involved in what you are trying to do such as InternetConnect(), InternetOpen(), FtpGetFile(), etc, and those functions have nothing inherently to do with basic, C, C++, or any other language (although they are documented in C). Of course, maybe I'm way off mark; like I said, I don't deal with internet connectivity code in the work I do. But at least no one so far has told me I'm that far off the mark.

Athar wrote

If the size bothers you, you can look for alternatives to C++ streams. As a replacement for stringstream, boost's lexical_cast in combination with the string algos usually is a better alternative.
As for iostream and fstream, I don't know of any lightweight alternatives (but that is not to say there aren't any). If you have the necessary experience, I would recommend writing your own.


Well, I can think of alternatives. But I'll let you figure it out. And here's the data from which you can accomplish that....

1
2
3
4
5
6
7
#include <iostream>  // 457 Kb Code::Blocks 10.05; Symbols stripped; Optimized for code size 
                     // 85 byte text file
int main()
{
 std::cout << "Hello, World!\n";
 return 0;
}


Uses wee bit earlier version of Code::Blocks, i.e., 10.02; Exe is nearly 200 K Smaller!?! Go Figure!

1
2
3
4
5
6
7
#include <iostream>  // 269.5  Kb Code::Blocks 10.02; Symbols stripped; Optimized for code size 

int main()
{
 std::cout << "Hello, World!\n";
 return 0;
}


1
2
3
4
5
6
7
#include <cstdio>  // 5.5 k  Code::Blocks 10.02  ; 6.0 Kb Code::Blocks 10.05
                   // 79 bytes
int main()
{
 printf("Hello, World\n");
 return 0;
}


So you go from an almost 500 K exe to a 5 K exe by just eliminating iostream. In TonyAz's case, he mentions he wants to end up with a Win32 GUI at some point along the line, and in that scenario iostreams buys you little to nothing.
C IO can hardly be called a comparable alternative.
Those functions are neither typesafe nor do they support C++ strings directly.

However, one can use them in place of the native functions the OS provides to implement a lightweight set of stream classes.
closed account (DSLq5Di1)
@freddie1
Just for the sake of comparison,
> Code::Blocks SVN 7452 (latest nightly build) + GCC 4.6.1
  - C++ I/O: 523 kb
  - C I/O: 14.5 kb

> Visual C++ 2008 Express
  - C++ I/O: 8.5 kb
  - C I/O: 7 kb
-edit-
> Visual C++ 2008 Express (static linking to the visual c runtime library)
  - C++ I/O: 101 kb
  - C I/O: 52 kb


Last edited on
What's the technique of finding the GCC MinGW (I guess) version sloppy9? Do you know offhand? Seems to me its a compiler switch from the command line or something.

In terms of the VC, I have most versions of it going all the way back to VC6. I never bothered getting VC10 though; my latest version is VStudio 2008 Pro. I discovered by accident that I even had to copy several redistributable Dlls onto Win 2000 machines even to get exes to run when statically linked with it. That kind of bummed me out. I'm just not really a big fan of Microsoft's stuff. I got it mostly to do Mobile app dev, but that fell through for me, so I don't use it for anything but testing and kicks and giggles at this point.
Thank you all for the help! Okay now I'm gonna eliminate iostream because I won't be heavily using that, what I'm at at this point is interacting with internet and windows environment (with no GUI just yet).

I said this once, maybe some of you missed it:
I am linking with wininet.a so maybe that adds to the executable size, I think that would be static linking if I'm not mistaken.

Now I will try dynamic linking (for the first time). I may your help on this one too, so this is my question:


All windows applications use windows.h header, so there must be a DLL for this library, if I can link it to my program, I can save considerable size, right? So goes for wininet.a (the static library)
All windows applications use windows.h header, so there must be a DLL for this library, if I can link it to my program, I can save considerable size, right? So goes for wininet.a (the static library)

No, the WinAPI consists of many different DLLs. See one of my previous posts.
WinAPI libraries are always linked dynamically, static linking isn't possible here.
Maybe you could elaborate on that issue of dynamic linking with the base windows dlls Athar. That's actually a topic that I don't think too many fully understand, including myself. My understanding of it is that one can't link directly with the windows dlls because an error would take windows down (crash the OS). I believe the actual mechanism is a LPC - local procedure call.

Anyway, I've actually become interested in your project Tonyiz. Like I said, I don't know squat about this, and that makes me feel pretty dumb. So maybe I'll try to follow along and learn something from you if you don't mind. I just tried this and lo and behold! It worked!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <Windows.h>
#include <tchar.h>
#include <wininet.h>
#include <cstdio>

int main()
{
 TCHAR szAgent[]=_T("DumbMe");
 HINTERNET hInternet=NULL;

 printf("Goodby Mr. Ritchie!  We Appreciate All You've Done!\n");
 //' Open an Internet session as predefined in the registry
 //' hIOpen = InternetOpen(ByVal VarPtr(Agent), ByVal %INTERNET_OPEN_TYPE_PRECONFIG, ByVal %NULL, ByVal %NULL, ByVal 0)

 hInternet=InternetOpen(szAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
 if(hInternet)
 {
    printf("InternetOpen() Succeeded! (You'll Never See This!)\n");
    InternetCloseHandle(hInternet);
 }
 else
    printf("InternetOpen() Failed! (This Is Likely What You'll See!\n");
 getchar();

 return 0;
}


1
2
Goodby Mr. Ritchie!  We Appreciate All You've Done!
InternetOpen() Succeeded! (You'll Never See This!)


It was a Code::Blocks 10.05 - MinGW Console project. I was just using that PowerBASIC code I previously posted. Are those the right wininet functions? I thought I read somewhere in that code that it pertained to making a dial up connection. What about broadband???

And so far I'm only at about 6.5 K. Oh! I went into linker options and set libwininet.a.
Last edited on
closed account (DSLq5Di1)
@freddie1
I think it's just "gcc --version" in command prompt. I haven't had that problem when linking statically to the CRT, though installing the redistributable if needed isn't an issue for me.

ToniAz wrote:
All windows applications use windows.h header, so there must be a DLL for this library,
http://en.wikipedia.org/wiki/Microsoft_Windows_library_files
Pages: 12