714dd74636
git-svn-id: svn://10.65.10.50/trunk@5350 c028cbd2-c16b-5b4b-a496-9718f37d4682
538 lines
18 KiB
C++
Executable File
538 lines
18 KiB
C++
Executable File
//
|
|
// EX23WIN.CPP
|
|
//
|
|
// C++/Windows Example program for ArchiveLib 2.0
|
|
//
|
|
// Copyright (c) Greenleaf Software, Inc. 1994 - 1996
|
|
// All Rights Reserved
|
|
//
|
|
// MEMBERS/FUNCTIONS DEMONSTRATED
|
|
//
|
|
// N/A
|
|
//
|
|
// DESCRIPTION
|
|
//
|
|
// This program is used to benchmark the performance of our various
|
|
// compression engines. The program looks at the Input Files text
|
|
// box for a wildcard specification for a group of files. The program
|
|
// expands the wild card specification and individually compresses and
|
|
// decompresses each of the files. The temporary files created by this
|
|
// process are deleted.
|
|
//
|
|
// After the whole batch of files is done, a summary of the compression
|
|
// time and statistics is displayed.
|
|
//
|
|
// If the optional Compress Only check box is checked, the
|
|
// program runs in "compress-only" mode. In this case, the decompression
|
|
// state is skipped. You can use this to determine the ratio between
|
|
// compression and decompresion times.
|
|
//
|
|
// If the Rude Mode check box is checked, the program doesn't service
|
|
// the Windows message loop while it is compression.
|
|
//
|
|
// This example program does one funky thing to try to give us real
|
|
// fast performance. Instead of using the default buffer size of 4096
|
|
// bytes, I jack it up to a hefty 28672 bytes. This gives us a few
|
|
// percentage points. Just change the definition of BIG_BUFFER to
|
|
// benchmark at the default size.
|
|
//
|
|
//
|
|
// REVISION HISTORY
|
|
//
|
|
// February 1, 1996 2.0A : Second release
|
|
//
|
|
|
|
#define STRICT
|
|
#include <windows.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include "al.h"
|
|
#include "ex23win.h"
|
|
|
|
/*
|
|
* I don't have a 32 bit version of CTL3D.DLL for Symantec or Microsoft.
|
|
*/
|
|
|
|
#if defined( AL_BORLAND ) || !defined( AL_FLAT_MODEL )
|
|
#define AL_3D
|
|
#include "ctl3d.h"
|
|
#else
|
|
#define Ctl3dColorChange()
|
|
#define Ctl3dRegister( a )
|
|
#define Ctl3dAutoSubclass( a )
|
|
#define Ctl3dUnregister( a )
|
|
#endif
|
|
|
|
#if !defined( BIG_BUFFER )
|
|
#define BIG_BUFFER 28672
|
|
#endif
|
|
|
|
HCURSOR hHourGlassCursor;
|
|
HINSTANCE hInstance;
|
|
|
|
//
|
|
// This storage object is used when compressing or decompressing. If
|
|
// the user wants to abort the compression, it just has to set the
|
|
// error flag for this object. If the object pointer is set to 0, it means
|
|
// no compression is currently in progress, so the Abort button does
|
|
// nothing.
|
|
//
|
|
ALStorage *compressed = 0;
|
|
|
|
extern "C" BOOL AL_EXPORT CALLBACK AboutDialogProc( HWND, UINT, WPARAM, LPARAM );
|
|
|
|
//
|
|
// The time class is a very modest convenience.
|
|
//
|
|
class TIME {
|
|
public :
|
|
long milliseconds;
|
|
TIME(){ milliseconds = GetTickCount(); }
|
|
TIME( long t ){ milliseconds = t; }
|
|
TIME operator-(TIME &);
|
|
};
|
|
|
|
TIME TIME::operator-( TIME &rhs )
|
|
{
|
|
return TIME( milliseconds - rhs.milliseconds );
|
|
}
|
|
|
|
//
|
|
// This function takes two file sizes and returns the compression ratio
|
|
// in the standard format we expect.
|
|
//
|
|
inline int ratio( long plain, long compressed )
|
|
{
|
|
if ( plain == 0 )
|
|
return 0;
|
|
compressed *= 100;
|
|
return (int)( 100 - ( compressed / plain ) );
|
|
}
|
|
|
|
//
|
|
// This function allocates a character buffer, then prints the
|
|
// time into it. It is up to the caller to free the returned
|
|
// string.
|
|
//
|
|
char *format_time( const TIME &t )
|
|
{
|
|
long m = t.milliseconds;
|
|
int hours;
|
|
int minutes;
|
|
int seconds;
|
|
int hsecs;
|
|
|
|
char *s = (char *) malloc( 25 );
|
|
if ( s ) {
|
|
hours = (int) ( m / ( 1000L * 60L * 60L ));
|
|
m -= hours * ( 1000L * 60L * 60L );
|
|
minutes = (int) ( m / ( 1000L * 60L ) );
|
|
m -= minutes * (1000L * 60L );
|
|
seconds = (int)( m / ( 1000L ) );
|
|
m -= seconds * 1000L;
|
|
hsecs = (int)( m / 10L );
|
|
sprintf( s, "%02d:%02d:%02d.%02d", hours, minutes, seconds, hsecs );
|
|
}
|
|
return s;
|
|
}
|
|
|
|
//
|
|
// This is the first of two possible compression functions. It is very
|
|
// much like the main loop in EX23CON.CPP. It just sets up a wild card
|
|
// expander object, then starts chunking out file names. Each time it
|
|
// gets a new file name, it compresses it, and optionally expands it.
|
|
// When the whole thing is over, the resulting stats are stuffed into
|
|
// the dialog, somewhere.
|
|
//
|
|
|
|
void NiceCompress( HWND hDlg )
|
|
{
|
|
char input_name[ 128 ];
|
|
char buf[ 128 ];
|
|
long total_input = 0;
|
|
long total_compressed = 0;
|
|
int compress_only = IsDlgButtonChecked( hDlg, AL_COMPRESS_ONLY );
|
|
|
|
ALWindowsMessage monitor( AL_MONITOR_OBJECTS,
|
|
GetDlgItem( hDlg, AL_PROGRESS_TEXT ),
|
|
AL_SEND_BYTE_COUNT,
|
|
GetDlgItem( hDlg, AL_PROGRESS_NUMBER ) );
|
|
monitor.mlObjectStart = 0;
|
|
|
|
GetDlgItemText( hDlg, AL_INPUT_FILES, input_name, 128 );
|
|
ALWildCardExpander expander( input_name );
|
|
#if defined( ZIP )
|
|
ALPkCompressor compressor;
|
|
ALPkDecompressor decompressor;
|
|
#else
|
|
ALGlCompressor compressor;
|
|
ALGlDecompressor decompressor;
|
|
#endif
|
|
//
|
|
// This code clears out all the boxes on the dialog that might be left
|
|
// over from the last run of this program.
|
|
//
|
|
SendDlgItemMessage( hDlg, AL_DATA, LB_RESETCONTENT, 0, 0 );
|
|
SetDlgItemText( hDlg, AL_TOTAL_INPUT, "" );
|
|
SetDlgItemText( hDlg, AL_TOTAL_COMPRESSED, "" );
|
|
SetDlgItemText( hDlg, AL_RATIO, "" );
|
|
SetDlgItemText( hDlg, AL_TIME, "" );
|
|
|
|
char AL_DLL_FAR *name;
|
|
TIME global_start;
|
|
//
|
|
// This is the loop where all of the work happens. All we do here is
|
|
// get a new file name and use it as the input file. We then compress
|
|
// to a second file, and then optionally expand to a third file.
|
|
//
|
|
while ( ( name = expander.GetNextFile() ) != 0 ) {
|
|
TIME local_start;
|
|
ALFile input( name, BIG_BUFFER );
|
|
compressed = new ALFile( "", BIG_BUFFER );
|
|
ALFile output( "", BIG_BUFFER );
|
|
ALName filename( name );
|
|
filename.StripPath();
|
|
SetDlgItemText( hDlg, AL_PROGRESS_TEXT, (LPSTR) filename );
|
|
input.mpMonitor = &monitor;
|
|
compressor.Compress( input, *compressed );
|
|
if ( compressed->mStatus < AL_SUCCESS )
|
|
break;
|
|
if ( !compress_only ) {
|
|
input.mpMonitor = 0;
|
|
output.mpMonitor = &monitor;
|
|
decompressor.Decompress( *compressed, output );
|
|
output.Delete();
|
|
if ( compressed->mStatus < AL_SUCCESS )
|
|
break;
|
|
}
|
|
total_input += input.GetSize();
|
|
total_compressed += compressed->GetSize();
|
|
TIME local_stop;
|
|
char *s;
|
|
wsprintf( buf,
|
|
"%s\t%ld\t%ld\t%d%%\t%s",
|
|
(LPSTR) filename,
|
|
input.GetSize(),
|
|
compressed->GetSize(),
|
|
ratio( input.GetSize(), compressed->GetSize() ),
|
|
(LPSTR)( s = format_time( local_stop - local_start ) )
|
|
);
|
|
if ( s )
|
|
free( s );
|
|
//
|
|
// I stuff the file stats in the list box, then make sure that the currently
|
|
// selected position in the list box is on the screen.
|
|
//
|
|
int pos = (int) SendDlgItemMessage( hDlg,
|
|
AL_DATA,
|
|
LB_ADDSTRING,
|
|
0,
|
|
(LPARAM)(LPSTR) buf );
|
|
SendDlgItemMessage( hDlg,
|
|
AL_DATA,
|
|
LB_SETCURSEL,
|
|
pos,
|
|
0 );
|
|
SendDlgItemMessage( hDlg,
|
|
AL_DATA,
|
|
LB_SETCURSEL,
|
|
(WPARAM) -1,
|
|
0 );
|
|
compressed->Delete();
|
|
delete compressed;
|
|
compressed = 0;
|
|
}
|
|
if ( compressed ) {
|
|
delete compressed;
|
|
compressed = 0;
|
|
}
|
|
TIME global_stop;
|
|
sprintf( buf, "%ld", total_input );
|
|
SetDlgItemText( hDlg, AL_TOTAL_INPUT, buf );
|
|
sprintf( buf, "%ld", total_compressed );
|
|
SetDlgItemText( hDlg, AL_TOTAL_COMPRESSED, buf );
|
|
sprintf( buf, "%d%%", ratio( total_input, total_compressed ) );
|
|
SetDlgItemText( hDlg, AL_RATIO, buf );
|
|
char * s = format_time( global_stop - global_start );
|
|
if ( s ) {
|
|
sprintf( buf,
|
|
"%s",
|
|
s = format_time( global_stop - global_start ) );
|
|
SetDlgItemText( hDlg, AL_TIME, buf );
|
|
free( s );
|
|
} else
|
|
SetDlgItemText( hDlg, AL_TIME, "" );
|
|
}
|
|
|
|
//
|
|
// In order to blow off the Windows message loop, we have to derive a new
|
|
// file class, which is what we are doing here with ALRudeFile. This
|
|
// class only needs a constructor and a new Yield function (the destructor
|
|
// is here because of DLL problems when we take the default dtor.)
|
|
// The Yield function does nothing. This overrides the base class
|
|
// Yield function, which services the message loop.
|
|
//
|
|
class AL_CLASS_TYPE ALRudeFile : public ALFile {
|
|
public :
|
|
virtual AL_PROTO ~ALRudeFile(){;}
|
|
void AL_DLL_FAR * AL_PROTO operator new( size_t size ){ return ::new char[ size ]; }
|
|
AL_PROTO ALRudeFile( const char AL_DLL_FAR *name = "" );
|
|
virtual void AL_PROTO YieldTime(){;}
|
|
};
|
|
|
|
AL_PROTO ALRudeFile::ALRudeFile( const char AL_DLL_FAR *name )
|
|
: ALFile( name, BIG_BUFFER, AL_MIXED )
|
|
{
|
|
}
|
|
|
|
//
|
|
// RudeCompress is just like NiceCompress(), except that it uses rude
|
|
// files instead of ALFile. It doesn't bother updating most of
|
|
// the elements on the screen, because we want to be as rude as
|
|
// possible. One kind of not rude thing here is the hourglass cursor
|
|
// we put up to let you know that you aren't going to be able to do
|
|
// anything else for a while.
|
|
//
|
|
void RudeCompress( HWND hDlg )
|
|
{
|
|
char input_name[ 128 ];
|
|
char buf[ 128 ];
|
|
long total_input = 0;
|
|
long total_compressed = 0;
|
|
int compress_only = IsDlgButtonChecked( hDlg, AL_COMPRESS_ONLY );
|
|
|
|
|
|
HCURSOR old_cursor = SetCursor( hHourGlassCursor );
|
|
GetDlgItemText( hDlg, AL_INPUT_FILES, input_name, 128 );
|
|
ALWildCardExpander expander( input_name );
|
|
#if defined( ZIP )
|
|
ALPkCompressor compressor;
|
|
ALPkDecompressor decompressor;
|
|
#else
|
|
ALGlCompressor compressor;
|
|
ALGlDecompressor decompressor;
|
|
#endif
|
|
SendDlgItemMessage( hDlg, AL_DATA, LB_RESETCONTENT, 0, 0 );
|
|
SetDlgItemText( hDlg, AL_TOTAL_INPUT, "" );
|
|
SetDlgItemText( hDlg, AL_TOTAL_COMPRESSED, "" );
|
|
SetDlgItemText( hDlg, AL_RATIO, "" );
|
|
SetDlgItemText( hDlg, AL_TIME, "" );
|
|
SetDlgItemText( hDlg, AL_PROGRESS_NUMBER, "" );
|
|
|
|
char AL_DLL_FAR *name;
|
|
TIME global_start;
|
|
while ( ( name = expander.GetNextFile() ) != 0 ) {
|
|
TIME local_start;
|
|
ALRudeFile input( name );
|
|
compressed = new ALRudeFile;
|
|
ALRudeFile output;
|
|
ALName filename( name );
|
|
filename.StripPath();
|
|
SetDlgItemText( hDlg, AL_PROGRESS_TEXT, (LPSTR) filename );
|
|
compressor.Compress( input, *compressed );
|
|
if ( compressed->mStatus < AL_SUCCESS )
|
|
break;
|
|
if ( !compress_only ) {
|
|
decompressor.Decompress( *compressed, output );
|
|
output.Delete();
|
|
if ( compressed->mStatus < AL_SUCCESS )
|
|
break;
|
|
}
|
|
total_input += input.GetSize();
|
|
total_compressed += compressed->GetSize();
|
|
compressed->Delete();
|
|
delete compressed;
|
|
compressed = 0;
|
|
}
|
|
if ( compressed ) {
|
|
delete compressed;
|
|
compressed = 0;
|
|
}
|
|
TIME global_stop;
|
|
sprintf( buf, "%ld", total_input );
|
|
SetDlgItemText( hDlg, AL_TOTAL_INPUT, buf );
|
|
sprintf( buf, "%ld", total_compressed );
|
|
SetDlgItemText( hDlg, AL_TOTAL_COMPRESSED, buf );
|
|
sprintf( buf, "%d%%", ratio( total_input, total_compressed ) );
|
|
SetDlgItemText( hDlg, AL_RATIO, buf );
|
|
char * s = format_time( global_stop - global_start );
|
|
if ( s ) {
|
|
sprintf( buf,
|
|
"%s",
|
|
s = format_time( global_stop - global_start ) );
|
|
SetDlgItemText( hDlg, AL_TIME, buf );
|
|
free( s );
|
|
} else
|
|
SetDlgItemText( hDlg, AL_TIME, "" );
|
|
SetCursor( old_cursor );
|
|
}
|
|
|
|
//
|
|
// This text message used to be in the RC file, but for some reason
|
|
// Microsoft's RC.EXE decided it was too long to compile.
|
|
//
|
|
char *help = "This program is used to run benchmarks on ArchiveLib's "
|
|
"compression performance. Simply enter a wild-card file "
|
|
"specification in the Input Files text box, then press the "
|
|
"Compress button. When the program completes, it will "
|
|
"print statistics regarding compression speed and ratio. The "
|
|
"Compress Only check box can be used to disable decompression. "
|
|
"When the Rude Mode check box is selected the compression "
|
|
"routines will not service the Windows message loop.";
|
|
//
|
|
// This is the dialog procedure for our main dialog. In this program, all
|
|
// the important work gets done here. Most of it gets down by the
|
|
// handler for WM_COMMAND, which processes the button presses from the
|
|
// dialog.
|
|
//
|
|
|
|
BOOL AL_EXPORT CALLBACK MainDialogProc( HWND hDlg,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM )
|
|
{
|
|
short int tab_stops[] = { 65, 120, 175, 195 };
|
|
switch ( message ) {
|
|
//
|
|
// We respond to the init message by positioning the dialog on the screen,
|
|
// initializing the windows title, the setting up the initial values of
|
|
// all the text boxes.
|
|
//
|
|
case WM_INITDIALOG :
|
|
RECT rc;
|
|
SetDlgItemText( hDlg, AL_HELP, help );
|
|
SendDlgItemMessage( hDlg, AL_DATA, LB_SETTABSTOPS, 4, (LPARAM) (LPINT) tab_stops );
|
|
/* I use this line of code to help me line up the columns...
|
|
SendDlgItemMessage( hDlg,
|
|
AL_DATA,
|
|
LB_ADDSTRING,
|
|
0,
|
|
(LPARAM)(LPSTR) "A\tB\tC\tD\tE" );
|
|
*/
|
|
GetWindowRect( hDlg, &rc );
|
|
SetWindowPos( hDlg,
|
|
NULL,
|
|
((GetSystemMetrics(SM_CXSCREEN) - (rc.right - rc.left)) / 2),
|
|
((GetSystemMetrics(SM_CYSCREEN) - (rc.bottom - rc.top)) / 2),
|
|
0,
|
|
0,
|
|
SWP_NOSIZE | SWP_NOACTIVATE );
|
|
SetWindowText( hDlg, "Windows example 23" );
|
|
SetDlgItemText( hDlg, AL_INPUT_FILES, "C:\\DOS\\COMMAND.COM" );
|
|
SetDlgItemText( hDlg, AL_PROGRESS_TEXT, "" );
|
|
SetDlgItemText( hDlg, AL_PROGRESS_NUMBER, "" );
|
|
return( TRUE );
|
|
//
|
|
// Have to support this message in order to make the CTL3D stuff work.
|
|
//
|
|
case WM_SYSCOLORCHANGE :
|
|
Ctl3dColorChange();
|
|
break;
|
|
//
|
|
// WM_COMMAND is for all the button presses.
|
|
//
|
|
case WM_COMMAND :
|
|
switch ( wParam ) {
|
|
//
|
|
// If the user wants to compress, we do so, but only if no compression
|
|
// is already in progress.
|
|
//
|
|
case AL_COMPRESS :
|
|
if ( compressed == 0 )
|
|
if ( IsDlgButtonChecked( hDlg, AL_RUDE ) )
|
|
RudeCompress( hDlg );
|
|
else
|
|
NiceCompress( hDlg );
|
|
return TRUE;
|
|
//
|
|
// If the user wants to quit, I have to set one of the file objects to an
|
|
// error status first. Otherwise, the compression code will keep right on
|
|
// going even after we have killed our dialog window. Code like this is used
|
|
// all over the place in all of the example programs.
|
|
//
|
|
case AL_EXIT :
|
|
case WM_QUIT :
|
|
case WM_DESTROY :
|
|
if ( compressed != 0 )
|
|
compressed->mStatus.SetError(
|
|
AL_USER_ABORT,
|
|
"User pressed abort key" );
|
|
else
|
|
EndDialog( hDlg, TRUE );
|
|
return TRUE;
|
|
//
|
|
// We abort a compression in progress the same way, by setting the
|
|
// error code for the storage object. Likewise, code that looks
|
|
// just like this is used all throughout our example programs.
|
|
//
|
|
case AL_ABORT :
|
|
if ( compressed != 0 )
|
|
compressed->mStatus.SetError(
|
|
AL_USER_ABORT,
|
|
"User pressed abort key" );
|
|
return TRUE;
|
|
|
|
case AL_ABOUT :
|
|
DialogBox( hInstance, "ALAboutDialog", hDlg, AboutDialogProc );
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// The about procedure just displays the about box.
|
|
//
|
|
BOOL AL_EXPORT CALLBACK AboutDialogProc( HWND hDlg,
|
|
UINT message,
|
|
WPARAM wParam,
|
|
LPARAM )
|
|
{
|
|
switch ( message ) {
|
|
case WM_INITDIALOG :
|
|
RECT rc;
|
|
GetWindowRect( hDlg, &rc );
|
|
SetWindowPos( hDlg,
|
|
NULL,
|
|
((GetSystemMetrics(SM_CXSCREEN) - (rc.right - rc.left)) / 2),
|
|
((GetSystemMetrics(SM_CYSCREEN) - (rc.bottom - rc.top)) / 2),
|
|
0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
|
|
break;
|
|
case WM_QUIT :
|
|
case WM_DESTROY :
|
|
EndDialog( hDlg, TRUE );
|
|
return TRUE;
|
|
case WM_COMMAND :
|
|
switch ( wParam ) {
|
|
case IDOK :
|
|
case AL_EXIT :
|
|
EndDialog( hDlg, TRUE );
|
|
return TRUE;
|
|
}
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// WinMain just has to dispatch the dialog box. It also sets up the CTL3d
|
|
// stuff.
|
|
//
|
|
int PASCAL WinMain( HINSTANCE instance,
|
|
HINSTANCE,
|
|
LPSTR,
|
|
int )
|
|
{
|
|
hInstance = instance;
|
|
hHourGlassCursor = LoadCursor( NULL, IDC_WAIT );
|
|
Ctl3dRegister( instance );
|
|
Ctl3dAutoSubclass( instance );
|
|
DialogBox( instance, "ALMainDialog", 0, MainDialogProc );
|
|
Ctl3dUnregister( instance );
|
|
DestroyCursor( hHourGlassCursor );
|
|
return 0;
|
|
}
|