Home | History | Annotate | Download | only in SFXSetup
      1 // Main.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../../Common/MyInitGuid.h"
      6 
      7 #include "../../../Common/CommandLineParser.h"
      8 #include "../../../Common/StringConvert.h"
      9 #include "../../../Common/TextConfig.h"
     10 
     11 #include "../../../Windows/DLL.h"
     12 #include "../../../Windows/ErrorMsg.h"
     13 #include "../../../Windows/FileDir.h"
     14 #include "../../../Windows/FileFind.h"
     15 #include "../../../Windows/FileIO.h"
     16 #include "../../../Windows/FileName.h"
     17 #include "../../../Windows/NtCheck.h"
     18 #include "../../../Windows/ResourceString.h"
     19 
     20 #include "../../UI/Explorer/MyMessages.h"
     21 
     22 #include "ExtractEngine.h"
     23 
     24 #include "resource.h"
     25 
     26 using namespace NWindows;
     27 using namespace NFile;
     28 using namespace NDir;
     29 
     30 HINSTANCE g_hInstance;
     31 
     32 static CFSTR kTempDirPrefix = FTEXT("7zS");
     33 
     34 #define _SHELL_EXECUTE
     35 
     36 static bool ReadDataString(CFSTR fileName, LPCSTR startID,
     37     LPCSTR endID, AString &stringResult)
     38 {
     39   stringResult.Empty();
     40   NIO::CInFile inFile;
     41   if (!inFile.Open(fileName))
     42     return false;
     43   const int kBufferSize = (1 << 12);
     44 
     45   Byte buffer[kBufferSize];
     46   int signatureStartSize = MyStringLen(startID);
     47   int signatureEndSize = MyStringLen(endID);
     48 
     49   UInt32 numBytesPrev = 0;
     50   bool writeMode = false;
     51   UInt64 posTotal = 0;
     52   for (;;)
     53   {
     54     if (posTotal > (1 << 20))
     55       return (stringResult.IsEmpty());
     56     UInt32 numReadBytes = kBufferSize - numBytesPrev;
     57     UInt32 processedSize;
     58     if (!inFile.Read(buffer + numBytesPrev, numReadBytes, processedSize))
     59       return false;
     60     if (processedSize == 0)
     61       return true;
     62     UInt32 numBytesInBuffer = numBytesPrev + processedSize;
     63     UInt32 pos = 0;
     64     for (;;)
     65     {
     66       if (writeMode)
     67       {
     68         if (pos > numBytesInBuffer - signatureEndSize)
     69           break;
     70         if (memcmp(buffer + pos, endID, signatureEndSize) == 0)
     71           return true;
     72         char b = buffer[pos];
     73         if (b == 0)
     74           return false;
     75         stringResult += b;
     76         pos++;
     77       }
     78       else
     79       {
     80         if (pos > numBytesInBuffer - signatureStartSize)
     81           break;
     82         if (memcmp(buffer + pos, startID, signatureStartSize) == 0)
     83         {
     84           writeMode = true;
     85           pos += signatureStartSize;
     86         }
     87         else
     88           pos++;
     89       }
     90     }
     91     numBytesPrev = numBytesInBuffer - pos;
     92     posTotal += pos;
     93     memmove(buffer, buffer + pos, numBytesPrev);
     94   }
     95 }
     96 
     97 static char kStartID[] = ",!@Install@!UTF-8!";
     98 static char kEndID[] = ",!@InstallEnd@!";
     99 
    100 class CInstallIDInit
    101 {
    102 public:
    103   CInstallIDInit()
    104   {
    105     kStartID[0] = ';';
    106     kEndID[0] = ';';
    107   };
    108 } g_CInstallIDInit;
    109 
    110 
    111 #define NT_CHECK_FAIL_ACTION ShowErrorMessage(L"Unsupported Windows version"); return 1;
    112 
    113 static void ShowErrorMessageSpec(const UString &name)
    114 {
    115   UString message = NError::MyFormatMessage(::GetLastError());
    116   int pos = message.Find(L"%1");
    117   if (pos >= 0)
    118   {
    119     message.Delete(pos, 2);
    120     message.Insert(pos, name);
    121   }
    122   ShowErrorMessage(NULL, message);
    123 }
    124 
    125 int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE /* hPrevInstance */,
    126     #ifdef UNDER_CE
    127     LPWSTR
    128     #else
    129     LPSTR
    130     #endif
    131     /* lpCmdLine */,int /* nCmdShow */)
    132 {
    133   g_hInstance = (HINSTANCE)hInstance;
    134 
    135   NT_CHECK
    136 
    137   // InitCommonControls();
    138 
    139   UString archiveName, switches;
    140   #ifdef _SHELL_EXECUTE
    141   UString executeFile, executeParameters;
    142   #endif
    143   NCommandLineParser::SplitCommandLine(GetCommandLineW(), archiveName, switches);
    144 
    145   FString fullPath;
    146   NDLL::MyGetModuleFileName(fullPath);
    147 
    148   switches.Trim();
    149   bool assumeYes = false;
    150   if (MyStringCompareNoCase_N(switches, L"-y", 2) == 0)
    151   {
    152     assumeYes = true;
    153     switches = switches.Ptr(2);
    154     switches.Trim();
    155   }
    156 
    157   AString config;
    158   if (!ReadDataString(fullPath, kStartID, kEndID, config))
    159   {
    160     if (!assumeYes)
    161       ShowErrorMessage(L"Can't load config info");
    162     return 1;
    163   }
    164 
    165   UString dirPrefix = L"." WSTRING_PATH_SEPARATOR;
    166   UString appLaunched;
    167   bool showProgress = true;
    168   if (!config.IsEmpty())
    169   {
    170     CObjectVector<CTextConfigPair> pairs;
    171     if (!GetTextConfig(config, pairs))
    172     {
    173       if (!assumeYes)
    174         ShowErrorMessage(L"Config failed");
    175       return 1;
    176     }
    177     UString friendlyName = GetTextConfigValue(pairs, L"Title");
    178     UString installPrompt = GetTextConfigValue(pairs, L"BeginPrompt");
    179     UString progress = GetTextConfigValue(pairs, L"Progress");
    180     if (progress.IsEqualToNoCase(L"no"))
    181       showProgress = false;
    182     int index = FindTextConfigItem(pairs, L"Directory");
    183     if (index >= 0)
    184       dirPrefix = pairs[index].String;
    185     if (!installPrompt.IsEmpty() && !assumeYes)
    186     {
    187       if (MessageBoxW(0, installPrompt, friendlyName, MB_YESNO |
    188           MB_ICONQUESTION) != IDYES)
    189         return 0;
    190     }
    191     appLaunched = GetTextConfigValue(pairs, L"RunProgram");
    192 
    193     #ifdef _SHELL_EXECUTE
    194     executeFile = GetTextConfigValue(pairs, L"ExecuteFile");
    195     executeParameters = GetTextConfigValue(pairs, L"ExecuteParameters");
    196     #endif
    197   }
    198 
    199   CTempDir tempDir;
    200   if (!tempDir.Create(kTempDirPrefix))
    201   {
    202     if (!assumeYes)
    203       ShowErrorMessage(L"Can not create temp folder archive");
    204     return 1;
    205   }
    206 
    207   CCodecs *codecs = new CCodecs;
    208   CMyComPtr<IUnknown> compressCodecsInfo = codecs;
    209   HRESULT result = codecs->Load();
    210   if (result != S_OK)
    211   {
    212     ShowErrorMessage(L"Can not load codecs");
    213     return 1;
    214   }
    215 
    216   const FString tempDirPath = tempDir.GetPath();
    217   // tempDirPath = L"M:\\1\\"; // to test low disk space
    218   {
    219     bool isCorrupt = false;
    220     UString errorMessage;
    221     HRESULT result = ExtractArchive(codecs, fullPath, tempDirPath, showProgress,
    222       isCorrupt, errorMessage);
    223 
    224     if (result != S_OK)
    225     {
    226       if (!assumeYes)
    227       {
    228         if (result == S_FALSE || isCorrupt)
    229         {
    230           NWindows::MyLoadString(IDS_EXTRACTION_ERROR_MESSAGE, errorMessage);
    231           result = E_FAIL;
    232         }
    233         if (result != E_ABORT)
    234         {
    235           if (errorMessage.IsEmpty())
    236             errorMessage = NError::MyFormatMessage(result);
    237           ::MessageBoxW(0, errorMessage, NWindows::MyLoadString(IDS_EXTRACTION_ERROR_TITLE), MB_ICONERROR);
    238         }
    239       }
    240       return 1;
    241     }
    242   }
    243 
    244   #ifndef UNDER_CE
    245   CCurrentDirRestorer currentDirRestorer;
    246   if (!SetCurrentDir(tempDirPath))
    247     return 1;
    248   #endif
    249 
    250   HANDLE hProcess = 0;
    251 #ifdef _SHELL_EXECUTE
    252   if (!executeFile.IsEmpty())
    253   {
    254     CSysString filePath = GetSystemString(executeFile);
    255     SHELLEXECUTEINFO execInfo;
    256     execInfo.cbSize = sizeof(execInfo);
    257     execInfo.fMask = SEE_MASK_NOCLOSEPROCESS
    258       #ifndef UNDER_CE
    259       | SEE_MASK_FLAG_DDEWAIT
    260       #endif
    261       ;
    262     execInfo.hwnd = NULL;
    263     execInfo.lpVerb = NULL;
    264     execInfo.lpFile = filePath;
    265 
    266     if (!switches.IsEmpty())
    267     {
    268       if (!executeParameters.IsEmpty())
    269         executeParameters += L' ';
    270       executeParameters += switches;
    271     }
    272 
    273     CSysString parametersSys = GetSystemString(executeParameters);
    274     if (parametersSys.IsEmpty())
    275       execInfo.lpParameters = NULL;
    276     else
    277       execInfo.lpParameters = parametersSys;
    278 
    279     execInfo.lpDirectory = NULL;
    280     execInfo.nShow = SW_SHOWNORMAL;
    281     execInfo.hProcess = 0;
    282     /* BOOL success = */ ::ShellExecuteEx(&execInfo);
    283     UINT32 result = (UINT32)(UINT_PTR)execInfo.hInstApp;
    284     if(result <= 32)
    285     {
    286       if (!assumeYes)
    287         ShowErrorMessage(L"Can not open file");
    288       return 1;
    289     }
    290     hProcess = execInfo.hProcess;
    291   }
    292   else
    293 #endif
    294   {
    295     if (appLaunched.IsEmpty())
    296     {
    297       appLaunched = L"setup.exe";
    298       if (!NFind::DoesFileExist(us2fs(appLaunched)))
    299       {
    300         if (!assumeYes)
    301           ShowErrorMessage(L"Can not find setup.exe");
    302         return 1;
    303       }
    304     }
    305 
    306     {
    307       FString s2 = tempDirPath;
    308       NName::NormalizeDirPathPrefix(s2);
    309       appLaunched.Replace(L"%%T" WSTRING_PATH_SEPARATOR, fs2us(s2));
    310     }
    311 
    312     UString appNameForError = appLaunched; // actually we need to rtemove parameters also
    313 
    314     appLaunched.Replace(L"%%T", fs2us(tempDirPath));
    315 
    316     if (!switches.IsEmpty())
    317     {
    318       appLaunched += L' ';
    319       appLaunched += switches;
    320     }
    321     STARTUPINFO startupInfo;
    322     startupInfo.cb = sizeof(startupInfo);
    323     startupInfo.lpReserved = 0;
    324     startupInfo.lpDesktop = 0;
    325     startupInfo.lpTitle = 0;
    326     startupInfo.dwFlags = 0;
    327     startupInfo.cbReserved2 = 0;
    328     startupInfo.lpReserved2 = 0;
    329 
    330     PROCESS_INFORMATION processInformation;
    331 
    332     CSysString appLaunchedSys = GetSystemString(dirPrefix + appLaunched);
    333 
    334     BOOL createResult = CreateProcess(NULL, (LPTSTR)(LPCTSTR)appLaunchedSys,
    335       NULL, NULL, FALSE, 0, NULL, NULL /*tempDir.GetPath() */,
    336       &startupInfo, &processInformation);
    337     if (createResult == 0)
    338     {
    339       if (!assumeYes)
    340       {
    341         // we print name of exe file, if error message is
    342         // ERROR_BAD_EXE_FORMAT: "%1 is not a valid Win32 application".
    343         ShowErrorMessageSpec(appNameForError);
    344       }
    345       return 1;
    346     }
    347     ::CloseHandle(processInformation.hThread);
    348     hProcess = processInformation.hProcess;
    349   }
    350   if (hProcess != 0)
    351   {
    352     WaitForSingleObject(hProcess, INFINITE);
    353     ::CloseHandle(hProcess);
    354   }
    355   return 0;
    356 }
    357