Search code examples
winapivisual-c++zipsystemcall

How to zip a folder in my Win32 Visual C++ app using a system call


I am trying to zip a folder in my Win32 Visual C++ app using a system call. On my computer, I can do this by the cmd command:

PowerShell -Command "Compress-Archive -Path  C:\Users\ttyler\Desktop\Software\Folder2\RUN -DestinationPath C:\Users\ttyler\Desktop\Software\Folder2\RUN"

Where "Try" is a folder.

I am trying to do this on my app using a System call, but in my app, I have the "Path" and the "DestinationPath" stored in variables (because the destination path is being pulled in by the user). I'm not sure I'm using the variables correctly in my system call because this is giving me the error: "'+': cannot add two pointers" and "expression must have integral or unscoped enum type".

TCHAR path = L"C:\Users\ttyler\Desktop\Software\Folder2\RUN";
TCHAR DestinationPath = L"C:\Users\ttyler\Desktop\Software\Folder2\RUN.zip";
system("Compress-Archive -Path" + path[0] + "-DestinationPath" + DestinationPath[0]);

What am I doing wrong when using the variables in my system call?


Solution

  • Your code won't work for many reasons:

    • you are trying to assign wide string literals to single-char variables. You need to use arrays or pointers instead, eg:
    TCHAR path[] = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN";
    TCHAR DestinationPath[] = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN.zip";
    
    LPCTSTR path = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN";
    LPCTSTR DestinationPath = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN.zip";
    
    • you cannot concatenate string literals with array/single-chars the way you are trying to do. You will have to allocate new arrays and copy/format the various substrings into it, eg:
    WCHAR path[] = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN";
    WCHAR DestinationPath[] = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN.zip";
    
    WCHAR cmd[(MAX_PATH*2)+50] = {};
    swprintf(cmd, L"Compress-Archive -Path \"%s\" -DestinationPath \"%s\"", path, DestinationPath);
    
    // use cmd as needed...
    

    Or, just use std::wstring instead, eg:

    std::wstring path = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN";
    std::wstring DestinationPath = L"C:\\Users\\ttyler\\Desktop\\Software\\Folder2\\RUN.zip";
    
    std::wstring cmd = L"Compress-Archive -Path \"" + path + L"\" -DestinationPath \"" + DestinationPath + L"\"";
    
    // use cmd as needed...
    
    • system() takes a const char* pointer as input. But you are using TCHAR strings, which will only work correctly for your wide string literals if TCHAR is WCHAR. Which means you can't pass your concatenated string to system() anyway. You will have to use _wsystem() instead, eg:
    _wsystem(cmd); // when using WCHAR[]/LPWSTR
    
    _wsystem(cmd.c_str()); // when using std::wstring
    
    • system()/_wsystem() runs an instance of cmd.exe /C <command-line>, where <command-line> is the specified input string. But PowerShell.exe is its own app with its own command-line parameters. PowerShell.exe commands ARE NOT valid cmd.exe commands, ie you are trying to execute this:
    cmd.exe /C Compress-Archive -Path <path> -DestinationPath <dest>
    

    Which is not a valid command, as cmd.exe does not know what Compress-Archive is. You would need to execute this instead:

    cmd.exe /C PowerShell -Command "Compress-Archive -Path \"<path>\" -DestinationPath \"<dest>\""
    
    WCHAR cmd[(MAX_PATH*2)+70] = {};
    swprintf(cmd, L"PowerShell -Command \"Compress-Archive -Path \\\"%s\\\" -DestinationPath \\\"%s\\\"", path, DestinationPath);
    
    std::wstring cmd = L"PowerShell -Command \"Compress-Archive -Path \\\"" + path + L"\\\" -DestinationPath \\\"" + DestinationPath + L"\\\"";
    

    But, you really should use CreateProcess() instead to run PowerShell.exe directly, don't use cmd.exe at all, eg:

    STARTUPINFO si = {sizeof(STARTUPINFO), 0};
    PROCESS_INFORMATION pi = {};
    
    if (CreateProcess(NULL, cmd/*cmd.data()*/, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
    {
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);
    }
    

    Otherwise, put your PowerShell commands into a .bat script, eg:

    @echo off
    PowerShell -Command "Compress-Archive -Path \"%0\" -DestinationPath \"%1\""
    

    And then you can use system()/_wsystem() to run that script, eg:

    WCHAR cmd[(MAX_PATH*2)+25] = {};
    swprintf(cmd, L"myscript.bat \"%s\" \"%s\"", path, DestinationPath);
    _wsystem(cmd);
    
    std::wstring cmd = L"myscript.bat \"" + path + L"\" \"" + DestinationPath + L"\"";
    _wsystem(cmd.c_str());