Click here to Skip to main content
Click here to Skip to main content

Modified Easy Installer

By , 10 Aug 2005
Rate this:
Please Sign up or sign in to vote.

Introduction

I modified Abu Mami's Easy Installer, to create a setup.exe style installer for installations from CD. Following are my notes, and the code for the replacement module for "Unwrap.cpp".

Using the code

The data for a particular installer is simply compiled in; to create setup, just adjust it and compile. No other modules of the Installer need changing, although I made some other changes, for example reading a single machine license key in AgreementPage; and changing the MessageBox in CInstallPage::OnWizardFinish() as follows:

  str = "Installation of " + unwrap.GetPrjName() +
        " has been successfully completed." +
        "\nClick Yes to view readme file, No to exit.";
  if (AfxMessageBox(str,MB_ICONEXCLAMATION|MB_YESNO)==IDYES)
        WinExec("notepad readme", SW_SHOW);

unzip is no longer needed; it is replaced by a recursive copy subroutine in unwrap. I also added some code for setting environment variables in the registry, which works for NT or XP.

I used VC 6.0, without any problem. Like Abu I've used the MFC static library. I had to make one change in CreateInstaller\wrap.cpp when I was testing it. CStringArray::GetCount() does not exist, so use GetSize() instead.

We only had the free version of InstallShield, so we couldn't add on our own C programs, which we needed to do. I was going to write a setup from scratch, but thought to look on the Web first, and found Abu's program. It took three days to finish the job. The .NET professional edition of InstallShield costs over $1000. I like this method a lot better.

I wonder whether eventually it wouldn't be better to get rid of NewWizDialog, the main reason being that the pages all have to be the same size.

----- replacement module for Unwrap
// Based on ExeExtractor written
// by Kaushal Malhotra (malhotrakaushal@mantraonline.com)
// Copyright (c) 2001

// Revised by Martin Dowd for "CD version" (straight copy), 2005
// Various data is "hard-coded"; simply revise it and compile to
//  produce the Installer executable (e.g. "setup.exe").
// Hard-coded data is preceded by a comment containing "DATA".

#include "stdafx.h"
#include ".\unwrap.h"
#include "ShellUtils.h"
#include "unzip.h"
#include "resource.h"
#include "kill.h"


CUnwrap unwrap;

CUnwrap::CUnwrap(void)
{
}

CUnwrap::~CUnwrap(void)
{
}

BOOL CUnwrap::Init()
{
    // DATA, project name
    m_prjName = "MCAD_AFEMS";

    // DATA, executable name
    m_prjExeName = "";

    // DATA, web site caption
    m_webCaption = "";

    // DATA, web site URL
    m_webURL = "";

    // DATA, agreement; don't use newlines
    m_agreement = "This product needs a machine-dependent key. "
                  "You must obtain this from FEM Engineering, "
                  "based on your machine ID. Your machine ID "
                  "is shown below. Obtain your key, type it "
                  "below, accept the license agreement, and continue.";

    // DATA, info
    m_info = "";

    // DATA, allowed Windows versions
    // b0-7: 95, 98, ME, NT35, NT4, 2K, XP
    m_winVer = 0xa0;

    return TRUE;
}

void CUnwrap::Close()
{
}

void CUnwrap::SetPath(CString path)
{
    m_path = path;
}

void CUnwrap::SetShortcut(BOOL shortcut)
{
    m_shortcut = shortcut;
}

void CUnwrap::SetStartup(BOOL startup)
{
    m_startup = startup;
}

void CUnwrap::SetStartMenu(BOOL startmenu)
{
    m_startmenu = startmenu;
}

DWORD CUnwrap::GetWinVer()
{
    return m_winVer;
}

CString    CUnwrap::GetPrjName()
{
    return m_prjName;
}

CString    CUnwrap::GetPrjExeName()
{
    return m_prjExeName;
}

CString    CUnwrap::GetWebCaption()
{
    return m_webCaption;
}

CString    CUnwrap::GetWebURL()
{
    return m_webURL;
}

CString CUnwrap::GetAgreement()
{
    return m_agreement;
}

CString CUnwrap::GetInfo()
{
    return m_info;
}

// recursive copy subroutine
// returns FALSE if doesn't complete.
// pathname length required to  be < MAX_PATH
// paths have terminating \'s; input dstpth assumed to exist
// uses TCHAR, etc for UNICODE (the SDK functions all do)

// there's no Windows generic memcpy, so define one.
#ifdef _UNICODE
#define _tcmcpy wmemcpy
#else
#define _tcmcpy memcpy
#endif

const char *errm[]={
"MAX_PATH exceeded; aborting file copy",
"Missing input file; aborting file copy",
"CopyFile failed; aborting file copy",
"CreateDirectory failed; aborting file copy",
"FindFirstFile failed; aborting file copy",
"Couldn't open registry; values not set",
};
#define ERRX(N) {AfxMessageBox(errm[N], 
        MB_ICONEXCLAMATION|MB_OK); return FALSE;}

static TCHAR srcpth[MAX_PATH+1]; /*extra for wildcard*/
static TCHAR dstpth[MAX_PATH];
static unsigned short srcpl,dstpl;
static WIN32_FIND_DATA fnd_d;

static int cpy_rec(const TCHAR *nm)
{unsigned short psl,pdl;
 HANDLE fnd_h;
 psl=srcpl; pdl=dstpl;

 // update src, dst
 {
   unsigned short l;
   l=_tcslen(nm);
   if (srcpl+l>=MAX_PATH || dstpl+l>=MAX_PATH) ERRX(0)
   _tcmcpy(srcpth+srcpl,nm,l); srcpl+=l; srcpth[srcpl]=0;
   _tcmcpy(dstpth+dstpl,nm,l); dstpl+=l; dstpth[dstpl]=0;
 }

 // check if file; copy if so
 {unsigned attr;
 if ((attr=GetFileAttributes(srcpth))==0xffffffff) ERRX(1)
 else if (!(attr&FILE_ATTRIBUTE_DIRECTORY))
 {if (CopyFile(srcpth,dstpth,FALSE)) goto xit;
  else ERRX(2)
 }}

 // create dst path
 if (CreateDirectory(dstpth,NULL)==0)
 {if (GetLastError()!=ERROR_ALREADY_EXISTS) ERRX(3)}

 //call recursively for contents
 srcpth[srcpl++]=_T('\\');
 dstpth[dstpl++]=_T('\\');
 srcpth[srcpl]=_T('*'); srcpth[srcpl+1]=0;
 if ((fnd_h=FindFirstFile(srcpth,&fnd_d))== 
                    INVALID_HANDLE_VALUE) ERRX(4)
 while (1)
 {if (fnd_d.cFileName[0]!='.') //skip . and ..
  {if (!cpy_rec(fnd_d.cFileName)) return FALSE;}
  if (!FindNextFile(fnd_h,&fnd_d)) break;
 }
 FindClose(fnd_h);
 xit: srcpl=psl; dstpl=pdl; return TRUE;
}

// OK flag; should be added to class, or files should return value
unsigned short unwrok = 0;

// registry key for system environment variables
const TCHAR sys_env_key[]=
_T("SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment");

// Subroutine to convert TCHAR length to byte length
// doesn't handle MCBS
int _tclb(int l)
{
#ifdef UNICODE
 l<<=1;
#endif
 return l;
}


void CUnwrap::Files()
{

// DATA, files / directories to copy
#define N_CPY_NMS 3
    const TCHAR *cpy_nms[N_CPY_NMS]={"readme.txt","bin","doc"};

    // recursively copy items in list
    m_nFiles = N_CPY_NMS;
    if (GetCurrentDirectory(MAX_PATH,srcpth)==0)
    {AfxMessageBox
     ("GetCurrentDirectory failed; aborting file copy",
      MB_ICONEXCLAMATION | MB_OK);
     return;
    }
    srcpl=_tcslen(srcpth);
    if (srcpth[srcpl-1]!=_T('\\')) srcpth[srcpl++]=_T('\\');
    _tcscpy(dstpth,(LPCTSTR)m_path);
    dstpl=_tcslen(dstpth);
    for(int idx = 0; idx < (int)m_nFiles; idx++)
        if (!cpy_rec(cpy_nms[idx])) return;

// DATA, shortcuts, etc.
// full path to executable
#define N_SHORTCUTS 0
    const TCHAR *shortcuts[1]={""};
#define N_STARTUPS 0
    const TCHAR *startups[1]={""};
#define N_MENUITMS 0
    const TCHAR *menuitms[1]={""};

    //create shortcuts, etc.
    LPCSTR path;
    TCHAR dir[MAX_PATH];
    TCHAR drive[MAX_PATH];
    TCHAR file[MAX_PATH];
    TCHAR ext[MAX_PATH];
    CString menuDir;

// CreateShortcut arguments:
// 1: full path of executable
// 2: link file name
// 3: working directory
// 4: description

    CoInitialize(NULL);
    if (m_shortcut) for (idx=0; idx<N_SHORTCUTS; idx++) {
        path=shortcuts[idx];
        _tsplitpath(path, drive, dir, file, ext);
        CShellUtils::CreateShortcut(path,file,dir,file,CSIDL_DESKTOP);}
    if (m_startup) for (idx=0; idx<N_STARTUPS; idx++) {
        path=startups[idx];
        _tsplitpath(path, drive, dir, file, ext);
        CShellUtils::CreateShortcut(path,file,dir,file,CSIDL_STARTUP);}
    if (m_startmenu) for (idx=0; idx<N_STARTUPS; idx++) {
        path=menuitms[idx];
        _tsplitpath(path, drive, dir, file, ext);
        menuDir.Format("%s\\%s", m_prjName, file);
        CShellUtils::CreateShortcut(path,menuDir,dir,file,CSIDL_PROGRAMS);}
    CoUninitialize();

// DATA, dlls to register
// The extension (e.g. ".dll") must be present in the name in the list.
#define N_DLL_FILES 0
    const TCHAR *dll_files[1]={""};

    // register DLLs
    for (idx = 0; idx < N_DLL_FILES; idx++)
        Register(dll_files[idx]);

// DATA, flag for setting environment variables
// 1 for system, 2 for user
#define SETENV 1

#if SETENV
    // set environment variables
    HKEY key;

#if SETENV==1
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,sys_env_key,0,KEY_WRITE,&key)
#elif SETENV==2
    if (RegOpenKeyEx(HKEY_CURRENT_USER,_T("Environment"),0,KEY_WRITE,&key)
#endif
     != ERROR_SUCCESS)
     {AfxMessageBox(errm[5],MB_ICONEXCLAMATION|MB_OK); return;}

    // destination path
    dstpth[dstpl]=0; //see above; gets text added
    RegSetValueEx(key,_T("FEM"),0,REG_SZ,
        (unsigned char *)dstpth,_tclb(dstpl+1));
    // single machine licence key
    extern CString af_id_txt;
    RegSetValueEx(key,_T("AFEMS_KEY"),0,REG_SZ,
        (unsigned char *)(LPCTSTR)af_id_txt,
        _tclb(af_id_txt.GetLength()+1));
    RegCloseKey(key);
#endif

    // set success flag
    unwrok=1;
}

BOOL CUnwrap::CheckExecutables()
{

// DATA, Executables to check if active
// The extension (e.g. ".exe") must be present in the name in the list.
#define N_EXE_FILES 0
    const char *exe_files[1]={""};

    for (int idx = 0; idx < N_EXE_FILES; idx++) {
    DWORD dwId;
    HANDLE hProcess = kill.FindProcess(exe_files[idx], dwId);
    if(hProcess) return TRUE;
    }
    return FALSE;
}

void CUnwrap::Register(CString dll)
{
    BOOL rc = FALSE;
    HMODULE hm = LoadLibrary(dll);
    if(hm)
    {
        rc = GetProcAddress(hm, TEXT("DllRegisterServer") ) != NULL;
        FreeLibrary(hm);
    }
    if(!rc)
        return;

    CString str;
    str.Format("Regsvr32.exe /s \"" + dll + "\"");
    WinExec(str, SW_SHOW);
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Martin Dowd
Web Developer
United States United States
Martin Dowd is a computer scientist trying to start a software company, and meanwhile doing FEA
related programming. He prefers C to C++ because it's more powerful, more efficient, and easier to compile.

Comments and Discussions

 
Questionplease send the source code of this example. Pinmembervivek_pawar31-Jan-07 21:57 
GeneralNice :-) PinmemberAbu Mami10-Aug-05 7:14 
GeneralRe: Nice :-) PinmemberProdelos17-Aug-05 21:28 
Thank you, thank you, thank you! I sincerely appreciate all of the efort that went into the original project and this wonderful improvement. More importantly, I appreciate the spirit of cooperation in Abu's post above. It's so much nicer than the usual hostility that seems to dominate the posts on this site.
 
A 5 vote for both articles and a hearty 5++ for the post above! Big Grin | :-D

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140421.2 | Last Updated 10 Aug 2005
Article Copyright 2005 by Martin Dowd
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid