|
I have made some changes to accomplish this very thing. If you select Add Directory, all subdirectories will be traversed and relative path information is saved and stored in the TOC, then used in the extraction.
The changes that I made to your original files are as follows. As I don't know how similar my current codebase is to yours, I can't think of a better way of showing my changes.
Also, this currently only works when adding a directory from the SelfExtractor.exe, not when adding individual files.
The following changes to SEFileInfo ensure that we remember the relative path of each file.
SEFileInfo.h changes
==============================
BOOL SetData(CString File, CString szAbsolutePathSegment="");
public:
void SetRelatviePath(CString szRelativePath){m_strRelativePath = szRelativePath;};
CString GetRelativePath(){return m_strRelativePath;};
protected:
CString m_strRelativePath;
SEFileInfo.cpp changes
===============================
BOOL CSEFileInfo::SetData(CString Filename, CString szAbsolutePathSegment)
{
...original code...
// ensure trailing backslash
if ('\\' != szAbsolutePathSegment.Right(1))
szAbsolutePathSegment += "\\";
m_strRelativePath = m_strPathname;
m_strRelativePath.Replace(m_strFilename, "");
m_strRelativePath.Replace(szAbsolutePathSegment, "");
return TRUE;
}
SelfExtractor.h
===============================
public:
BOOL AddDirectory(LPCTSTR szDirectory);
void PutAbsolutePath(LPCTSTR lpszAbsolutePath){m_strAbsolutePath = lpszAbsolutePath;}
protected:
BOOL MakeSurePathExists(CString &Path)
{
int Pos=0;
while((Pos=Path.Find('\\',Pos+1))!=-1)
CreateDirectory(Path.Left(Pos),NULL);
CreateDirectory(Path,NULL);
// ensure we can access it
CFileFind finder;
return finder.FindFile(Path);
}
CString m_strAbsolutePath;
SelfExtractor.cpp
===============================
IN CSelfExtractor::ESelfExtractorError CSelfExtractor::CreateArchive(CFile* pFile, funcPtr pFn, void* userData)
I added to the TOC loop.
//Now Write the TOC
for(int j = 0; j < m_nFiles; j++)
{
....Original Code....
//Write the relative path
len = m_InfoArray[j].GetRelativePath().GetLength();
strncpy(buffer, m_InfoArray[j].GetRelativePath(), len);
pFile->Write(buffer, len);
//Write the length of the relative path
pFile->Write(&len, sizeof(int));
}
IN CSelfExtractor::ESelfExtractorError CSelfExtractor::ExtractOne(CFile* file, int index, CString Dir)
I added at the top of the method.
CString szRelativePath = m_InfoArray[index].GetRelativePath();
CString szAbsolutePath = Dir + szRelativePath;
MakeSurePathExists(szAbsolutePath);
I changed...
CString szFileName = Dir + m_InfoArray[index].GetFilename();
to be...
CString szFileName = szAbsolutePath + m_InfoArray[index].GetFilename();
IN CSelfExtractor::ESelfExtractorError CSelfExtractor::ReadTOC(CString Filename)
I added to the beginning of the read TOC loop.
int lenRelPath = 0;
// Get Length of Relative Path
Thisfile.Seek(-LastOffset, CFile::end);
Thisfile.Read(&lenRelPath, sizeof(int));
LastOffset += lenRelPath;
// Get Relative Path
Thisfile.Seek(-LastOffset, CFile::end);
Thisfile.Read(buffer, lenRelPath);
LastOffset += sizeof(int);
CString szRelativePath(buffer);
I added to the end of the read TOC loop.
m_InfoArray[i].SetRelatviePath(szRelativePath.Left(lenRelPath));
IN BOOL CSelfExtractor::AddFile(CString File)
I changed...
if(m_InfoArray[m_nFiles].SetData(File))
to...
if(m_InfoArray[m_nFiles].SetData(File, m_strAbsolutePath))
I added the method
/*******************************************************************************
*
* Function: CSelfExtractor::AddDirectory
*
* Author: Lee W. Spencer, 24/7 Systems, Inc.
* Date: 11/20/2003
* E-mail: spencer_tf7@hotmail.com
*
* Description:
* Add a subdirectory to traverse
*
* Parameters:
* szDirectory: Path to traverse
*
* Return:
* BOOL: Success or Failure
*******************************************************************************/
BOOL CSelfExtractor::AddDirectory(LPCTSTR lpszDirectory)
{
CString szDirectoryPath = lpszDirectory;
// ignore these special cases
if("." == szDirectoryPath.Mid(szDirectoryPath.ReverseFind('\\') + 1))
return FALSE;
if(".." == szDirectoryPath.Mid(szDirectoryPath.ReverseFind('\\') + 1))
return FALSE;
// find all files within this directory
CString szPath = szDirectoryPath + "\\*.*";
CFileFind finder;
BOOL bContinue = finder.FindFile(szPath);
while(bContinue)
{
bContinue = finder.FindNextFile();
CString szFoundFilePath = finder.GetFilePath();
if(!AddFile(szFoundFilePath))
{
// recurse
if(finder.IsDirectory())
AddDirectory(szFoundFilePath);
}
}
return TRUE;
}
Self ExtractorDlg.cpp changes
==============================
IN void CSelfExtractorDlg::OnAddDir()
I changed...
CString Path = dlg.GetPathname();
Path += "\\*.*";
to...
CString Path = dlg.GetPathname();
m_Extractor.PutAbsolutePath(Path);
Path += "\\*.*";
AND...
m_Extractor.AddFile(finder.GetFilePath());
to...
if(!m_Extractor.AddFile(finder.GetFilePath()))
{
if(finder.IsDirectory())
m_Extractor.AddDirectory(finder.GetFilePath());
}
|
|
|
|
|
fix: must make ascii-z c-string:
modify: SelfExtracter.cpp
CSelfExtractor::CreateArchive() and CSelfExtractor::ReadTOC():
fixed project source (self_extractor120220_2.7z) uploaded to :
https://skydrive.live.com/redir.aspx?cid=c17d9b5257172422&resid=C17D9B5257172422!2159&parid=C17D9B5257172422!819&authkey=!AFysZDoThtTrK3k[^]
int CSelfExtractor::CreateArchive(CFile& File, funcPtr pFn, void* userData)
{
char buffer[1000]; // Buffer for data
CFile data; // Input file
try
{
//Copy all the inout files into the archive
for(int i = 0; i < m_nFiles; i++)
{
// Open the input file
if(data.Open(m_InfoArray[i].GetPathname() , CFile::modeRead | CFile::shareDenyNone))
{
// Call the user defined CallBack
if(pFn != NULL)
pFn(SFX_FILE_START, static_cast<void*>(&m_InfoArray[i]), userData);
// Record Start Offset
m_InfoArray[i].SetOffset(File.GetPosition());
// Zlib Stuff
int err = Z_OK;
avail_in = 0;
avail_out = ZLibOutputLength;
next_out = m_ZLibOutputBuffer;
m_ZLibFileLength = data.GetLength();
int level = 6;
deflateInit(this, level);
for ( ; ; )
{
// Load input data
if(!ZLibLoadInput(data))
break;
err = deflate(this, Z_NO_FLUSH);
ZLibFlushOutput(File);
if(err != Z_OK)
break;
// Call the user defined CallBack
if(pFn != NULL)
{
int nPercent = Percent();
pFn(SFX_ZIP_INFO, &nPercent, userData);
}
}
// Close this input file
data.Close();
for ( ; ; )
{
err = deflate(this, Z_FINISH);
// Write the output
if (!ZLibFlushOutput(File))
break;
if(err != Z_OK)
break;
}
deflateEnd(this);
// Update the info array with the new compressed size
m_InfoArray[i].SetCompressedSize(total_out);
// Call the user defined CallBack
if(pFn != NULL)
pFn(SFX_FILE_END, static_cast<void*>(&m_InfoArray[i]), userData);
}
else
return INPUT_FILE_ERROR;
}
//Now Write the TOC //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
for(int j = 0; j < m_nFiles; j++)
{
//Write the File Offset
int Offset = m_InfoArray[j].GetFileOffset();
File.Write(&Offset, sizeof(int));
//Write the File Size
int len = m_InfoArray[j].GetFileSize();
File.Write(&len, sizeof(int));
//Write the Compressed File Size
len = m_InfoArray[j].GetCompressedSize();
File.Write(&len, sizeof(int));
//Write the filename
len = m_InfoArray[j].GetFilename().GetLength();
strncpy(buffer, m_InfoArray[j].GetFilename(), len);
File.Write(buffer, len);
//Write the length of the filename
File.Write(&len, sizeof(int));
//Write the relative path //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
len = m_InfoArray[j].GetRelativePath().GetLength();
strncpy(buffer, m_InfoArray[j].GetRelativePath(), len);
if (len) buffer[len]=0;
if (!len)
{
strcpy(buffer,".\\");
len=2;
}
CString tmp;
tmp.Format("[%s][%d]",buffer,len);
//AfxMessageBox(tmp);
if (len) /*pFile->*/File.Write(buffer, len);
else
{
strcpy(buffer,".\\");
len=2;
}
//Write the length of the relative path
/*pFile->*/File.Write(&len, sizeof(int));
}
//Write the total number of files
File.Write((void*)&m_nFiles, sizeof(int));
//Write the vesion
strcpy(buffer, SFX_VERSION);
File.Write(buffer, strlen(SFX_VERSION));
//Write the SIG
strcpy(buffer, SIGNATURE);
File.Write(buffer, strlen(SIGNATURE));
}
catch(CFileException* e)
{
//Got sick of seeing 'unreferenced local variable'
e->m_cause;
return OUTPUT_FILE_ERROR;
}
return UNKNOWN_ERROR;
}
int CSelfExtractor::ReadTOC(CString Filename)
{
CFile Thisfile; //Archive file
char buffer[1000]; //Buffer to read and write with
//Clear the CSEFileInfo class array
Reset();
//Open the archive
if(!Thisfile.Open(Filename, CFile::modeRead | CFile::shareDenyNone))
return NO_SOURCE;
else
{
//Read in the signature
Thisfile.Seek(- static_cast<int>(strlen(SIGNATURE)), CFile::end);
Thisfile.Read(buffer, strlen(SIGNATURE));
// Check that it matches
buffer[strlen(SIGNATURE)]=0;
CString tmp;
tmp.Format("[%s][%s]",buffer, SIGNATURE);
//AfxMessageBox(tmp);
if(strncmp(buffer, SIGNATURE, strlen(SIGNATURE)) != 0)
{
return INVALID_SIG;
}//else AfxMessageBox("SIGNATURE ok");
// Read in the version
int LastOffset = strlen(SIGNATURE) + strlen(SFX_VERSION);
Thisfile.Seek(-LastOffset, CFile::end);
Thisfile.Read(buffer, strlen(SFX_VERSION));
// Check that the version matches
buffer[strlen(SFX_VERSION)]=0;
CString tmp2;
tmp2.Format("[%s][%s]",buffer, SFX_VERSION);
//AfxMessageBox(tmp2);
if(strncmp(buffer, SFX_VERSION, strlen(SFX_VERSION)) != 0)
{
return INVALID_SIG;
}//else AfxMessageBox("SFX_VERSION ok");
// Read Number of files
LastOffset += sizeof(int);
Thisfile.Seek(-LastOffset, CFile::end);
Thisfile.Read(&m_nFiles, sizeof(int));
//If there are no files in the archive, there is nothing to extract
if(m_nFiles == 0)
{
AfxMessageBox("m_nFiles == 0");
return NOTHING_TO_DO;
}else
{
CString tmp5;
tmp5.Format("m_nFiles=%d,",m_nFiles);
// AfxMessageBox(tmp5);
}
//Read the TOC in. The array is filled in reverse to ensure that it
//corresponds to the data segment
for(int i = (m_nFiles - 1); i >= 0 ; i--)
{
int lenRelPath = 0;
// Get Length of Relative Path
LastOffset += sizeof(int); //<<
Thisfile.Seek(-LastOffset, CFile::end);
Thisfile.Read(&lenRelPath, sizeof(int));
CString tmp6;
tmp6.Format("lenRelPath=%d,",lenRelPath);
//AfxMessageBox(tmp6);
LastOffset += lenRelPath;
// Get Relative Path
Thisfile.Seek(-LastOffset, CFile::end);
Thisfile.Read(buffer, lenRelPath);
//LastOffset += sizeof(int);
buffer[lenRelPath]=0;
CString tmp4;
tmp4.Format("[%s]",buffer);
//AfxMessageBox(tmp4);
CString szRelativePath(buffer); //<<<<<<<<<<<<<<<<<<<<<<<<<<<
int nSize = 0;
int nCompSize = 0;
int nOffset = 0;
int len = 0;
LastOffset += sizeof(int);
// Get Length of Pathname
Thisfile.Seek(-LastOffset, CFile::end);
Thisfile.Read(&len, sizeof(int));
LastOffset += len;
// Get Path Name
Thisfile.Seek(-LastOffset, CFile::end);
Thisfile.Read(buffer, len);
LastOffset += sizeof(int);
buffer[len]=0;
CString tmp3;
tmp3.Format("[%s]",buffer);
// AfxMessageBox(tmp3);
// Get Compressed File Size
Thisfile.Seek(-LastOffset, CFile::end);
Thisfile.Read(&nCompSize, sizeof(int));
LastOffset += sizeof(int);
// Get File Size
Thisfile.Seek(-LastOffset, CFile::end);
Thisfile.Read(&nSize, sizeof(int));
LastOffset += sizeof(int);
// Get File Offset
Thisfile.Seek(-LastOffset, CFile::end);
Thisfile.Read(&nOffset, sizeof(int));
//Set the data in the array
m_InfoArray[i].SetSize(nSize);
m_InfoArray[i].SetCompressedSize(nCompSize);
CString Temp(buffer);
m_InfoArray[i].SetFilename(Temp.Left(len));
m_InfoArray[i].SetOffset(nOffset);
m_InfoArray[i].SetRelatviePath(szRelativePath.Left(lenRelPath));//<<<<<<<<<<<<<<<<
}
//Record the total size of the TOC for use
//when extracting the data segment
m_nTOCSize = LastOffset;
}
//Close the archive
Thisfile.Close();
return SUCCESS;
}
|
|
|
|
|
hi. i wanted to ask how do you add the zlib to your project? i asked this because when i try to add the zlib to my project and buil my project, it give mi 3 errors like this:
LockUpBuilder error LNK2019: unresolved external symbol _deflateEnd referenced in function "public: int __thiscall CSelfExtractor::CreateArchive(class CFile &)" (?CreateArchive@CSelfExtractor@@QAEHAAVCFile@@@Z)
LockUpBuilder error LNK2019: unresolved external symbol _deflate referenced in function "public: int __thiscall CSelfExtractor::CreateArchive(class CFile &)" (?CreateArchive@CSelfExtractor@@QAEHAAVCFile@@@Z)
LockUpBuilder error LNK2019: unresolved external symbol _deflateInit_ referenced in function "public: int __thiscall CSelfExtractor::CreateArchive(class CFile &)" (?CreateArchive@CSelfExtractor@@QAEHAAVCFile@@@Z)
May I know what is wrong?
|
|
|
|
|
Hi,
Can I execute some exe file without extract ?
Rogerio Silva
|
|
|
|
|
Hi Rogerio,
I'm not sure I understand what you are trying to do. Are you saying you want to be able to execute an exe file programatically? If this is the case look for ShellExecuteEx() in MSDN. If you trying to do something else can you give me some more detail.
Cheers
James
|
|
|
|
|
Hi,
I see in your code that you use ExtractAll or ExtractOne to extract a file. Then after extract I can run with ShellExecute.
But I would like to run this app without extract. Run directly from the Self-Extracting file.
It is possible ?
thank you!
|
|
|
|
|
Rogerio Silva wrote:
Run directly from the Self-Extracting file.
It is possible ?
No. The data is compressed in the archive so it would like trying to run a file in a zip file without extracting the zip. The only thing you could do is extract the file in memory but then you wouldn't be able to use ShellExecute with it.
The only solution is to extract it to some temporary directory and run it from there. Why can't you do it like this?
Cheers
James
|
|
|
|
|
I'd like to create a app that protect the software.
This app will check the serial first, then IF correct, execute the app.
But if I copy this file to a temporary folder, someone can get this file without the protection.
|
|
|
|
|
even if you extract it to the memory someone could get the file without the protection...
|
|
|
|
|
And even if you don't extract it a all, someone could get the file from the excutable, or wathever the file is stored to...
|
|
|
|
|
Hello!
First of all i want to say that this SE it's a very good sample for programmers!
Is somebody know how to execute for example "myapp.exe" from WINDIR\Temp? And close self extractor later?
Thanks!
Lukas
|
|
|
|
|
Hey James,
Nice class. I like the way you've implemented it and the TOC setup seems interesting. Mind if I use some of the code in a project I'm doing.. .I'll give you full credit for it
Regards,
Brian Dela
|
|
|
|
|
Of course, that's what it's there for
Cheers
James
|
|
|
|
|
Thanks.. Just said I'd ask though..
Regards,
Brian Dela
|
|
|
|
|
Hi Russ and thanks for the comments. I'd like to address your comments one by one if I may so that it's clear why I made certain decisions.
Russ Freeman wrote:
CSelfExtractor - I think I'd prefer a reader and a writer.
When this class was first written, it didn't contain enough code to warrent splitting into two classes so that's why it was contained in one. You are more than welcome to split it now if you wish.
Russ Freeman wrote:
the example, and the class for this article use the same filenames
You are correct. I have changed the example application to be called 'Extractor Builder' to avoid any confusion.
Russ Freeman wrote:
CSelfExtractor::CreateArchive - takes a CFile*.
You are correct here. There is no error checking done on the pointer which could indeed cause a crash. I have updated this in version 1.12 of the class.
Russ Freeman wrote:
CSelfExtractor::GetThisFileName()
You are correct - there is a certain amount of redundancy here. I have included your suggestions in the next build.
Russ Freeman wrote:
CSelfExtractor::m_nFiles - this seems like it's not needed
No, this variable keeps track of how many files are to be written into the archive when Create() is called and so is required.
Russ Freeman wrote:
The projects use automatic precompiled headers
Hmm, you are correct but I've never noticed this before. I've updated the project file accordingly.
Russ Freeman wrote:
Lastly, the project does not compile with level 4 warnings
Only if you set 'warnings as errors' and as you say most of this is in the zlib stuff which is not part of this article.
Just a final few points. The code presented here was first written about 2 years ago because I was interested in discovering how to create files which were executable but which carried their data with them. As a result of this, I wrote this class and this sample application. It should be noted though that I have never used this code for anything other than this article, hence the code has never been through any formal testing as such. However, I know for a fact that it has been used in various commercial apps with no problems.
I'm sure there are hundreds of ways to achieve the functionality demonstrated here and this solution is just one of those ways. It was never meant to be a defintive answer to the problem but more of a 'you could do it this way'.
Anyway, thanks for your comments, I appreciate them as always. The code has been updated as of now
Cheers
James
|
|
|
|
|
Hi Sir,
I tried executing the output exe of this sample . It worked well at compiled-pc.
But the same output exe , i tried in another machine. It showed internal Error..
Why is it so ??
may be, because other dll need for this exe-module,,,ole-compound-file,,,
who can answer this problem?
thanks all.
|
|
|
|
|
Have you applied the changes as suggested in the thread entitled 'Internal Error' below??
Cheers
James
|
|
|
|
|
This is excellent work, I am very impressed , and I appreciate the hard work you put into it. There is one small bug, that might help some users with the problem of not being able to add files. I am running Windows 2000 and when I first compiled the Add button did nothing like others stated on this forum, see below for the answer.
The problem list with the CFileDialog, you should refer to for correct usage of contructor. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_mfc_cfiledialog.3a3a.domodal.asp[^]
<><><><><><><><><><><><><><>
Matthew R. Miller
mattrmiller@computersmarts.net
www.computersmarts.net
|
|
|
|
|
This is just what I want, it's great
but I have one problem, if I start extracting the files, and then click cancel the program(the GUI part) closes, but it keeps writing to the floppy drive.
If you have a fix for this let me know
-Isaiah
http://www.madboot.com
|
|
|
|
|
Hi everyone.
I was wondering, does anyone know a way to extract a certain file on startup? I've tried using Extract() and ExtractOne() but they either do nothing or it crashes the program.
|
|
|
|
|
Hi Dustin,
I just tested the following code and it worked fine :-
BOOL CExtractorDlg::OnInitDialog()
{
CDialog::OnInitDialog();
SetIcon(m_hIcon, TRUE);
SetIcon(m_hIcon, FALSE);
int ret = m_Extractor.ReadTOC(m_Extractor.GetThisFileName());
if((ret != INVALID_SIG) && (m_Extractor.GetFileCount() > 0))
{
m_Extractor.Extract(1, "C:\\Temp");
}
UpdateData(FALSE);
return TRUE;
}
You might want to add a new field to the HeaderInfo section of the SelfExtractor which is the index in the archive of the file to extract on startup, rather than hard coding the value into the extractor as I have here.
Also, I would recommend that you don't do any extracting in your OnInitDialog routine. If you must extract a file on startup, set a timer with say a 50ms value and do the extraction in OnTimer (remembering to kill the timer once the extraction is done, of course).
Cheers
James
|
|
|
|
|
hmm, Im getting this error:
error C2248: 'GetThisFileName' : cannot access protected member declared in class 'CSelfExtractor'
But that code above looks like it extracts all of the files. What happens is I write some settings to an INI when they click the button to create the self extractor. Then I use AddFile() right before it creates the extractor. So the file I need to extract is the last one in the archive. Is there any way to do this?
|
|
|
|
|
Not sure I understand you correctly. Email me your code and I'll take a look.
|
|
|
|
|
My code is basically yours. I've already emailed you once, I don't know if the address is valid. If you want, you can send me an email at dustin@acm.org so I know for sure what your email address is.
|
|
|
|
|
i compiled both projects in release mode
if i select the output filename first, the select file dialog does not show after clicking "Add File", if i try to add files before selecting output filename, dialog shows and file gets added, exe created successful
when i try to run the self extractor, "Internal Error", every attemp, sorry i am a newbie for c so i can't help alot more than that.
shotgun
|
|
|
|
|