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
#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()
{
m_prjName = "MCAD_AFEMS";
m_prjExeName = "";
m_webCaption = "";
m_webURL = "";
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.";
m_info = "";
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;
}
#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];
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;
{
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;
}
{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)
}}
if (CreateDirectory(dstpth,NULL)==0)
{if (GetLastError()!=ERROR_ALREADY_EXISTS) ERRX(3)}
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]!='.')
{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;
}
unsigned short unwrok = 0;
const TCHAR sys_env_key[]=
_T("SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment");
int _tclb(int l)
{
#ifdef UNICODE
l<<=1;
#endif
return l;
}
void CUnwrap::Files()
{
#define N_CPY_NMS 3
const TCHAR *cpy_nms[N_CPY_NMS]={"readme.txt","bin","doc"};
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;
#define N_SHORTCUTS 0
const TCHAR *shortcuts[1]={""};
#define N_STARTUPS 0
const TCHAR *startups[1]={""};
#define N_MENUITMS 0
const TCHAR *menuitms[1]={""};
LPCSTR path;
TCHAR dir[MAX_PATH];
TCHAR drive[MAX_PATH];
TCHAR file[MAX_PATH];
TCHAR ext[MAX_PATH];
CString menuDir;
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();
#define N_DLL_FILES 0
const TCHAR *dll_files[1]={""};
for (idx = 0; idx < N_DLL_FILES; idx++)
Register(dll_files[idx]);
#define SETENV 1
#if SETENV
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;}
dstpth[dstpl]=0;
RegSetValueEx(key,_T("FEM"),0,REG_SZ,
(unsigned char *)dstpth,_tclb(dstpl+1));
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
unwrok=1;
}
BOOL CUnwrap::CheckExecutables()
{
#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);
}
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.