cwinapi

Failure running a powershell command though C


I'm working on a C project in Visual Studio 2022 in Windows. This specific solution is essentially a CLI-based "builder", which, based on the arguments its been given, compiles a .c source code with cl.exe (thus generating a new executable).

The target machine needs to have a Visual Studio environment installed so we can run cl.exe.

If a Visual Studio environment has not been located in the target machine, we ask the user if they want to install VS Build Tools. If yes, we run a system command that opens powershell instance to download to VS 2022 Build Tools:

system("powershell.exe Invoke-WebRequest $url\"https://aka.ms/vs/17/release/vs_BuildTools.exe\" -outFile C:\\users\\tamar\\Downloads\\vs_BuildTools.exe");

In theory, the command should correctly download the file. But, when running the C file, PowerShell outputs an error which reflects that the double quotations around the link are not being recognized, despite being properly escaped.

At line:1 char:19
+ Invoke-WebRequest $urlhttps://aka.ms/vs/17/release/vs_BuildTools.exe  ...
+                   ~~~~~~~~~~
Variable reference is not valid. ':' was not followed by a valid variable name character. Consider using ${} to
delimit the name.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : InvalidVariableReferenceWithDrive

When running the command through powershell, it does work:

PS C:\Users\tamar> Invoke-WebRequest $url"https://aka.ms/vs/17/release/vs_BuildTools.exe" -outFile C:\\users\\tamar\\Downloads\\vs_BuildTools.exe

Perhaps this is a sign that the file should be installed using a more "standard" practice? I tried libcurl, but it seems that including the whole library is a bit of an overkill just to download a single file.

Does anyone have a solution for my problem, or can perhaps advise a better way to minimally download an installer from the internet using C?

What I tried: manipulating the command with snprintf, using different ways for escaping

What I expected to happen: Powershell correctly downloading the installer from the given URL.

What actually happened: Powershell threw an error.


Solution

  • I would suggest to use a less hacky approach to avoid C/powershell escape nightmare. Below is a complete test program.

    #include <stdlib.h>
    
    int main(void)
    {
        /* Three layers of quoting to keep straight:
         * ─────────────────────────────────────────
         * 1. C string literal:   "…"
         *    - Every backslash is doubled (\\).
         *    - Every embedded double quote is escaped (\"), unless you switch
         *      to raw-string literals in C23 or C++11.
         *
         * 2. Windows CMD command line seen by system():
         *    - It splits on spaces unless the whole PowerShell command is
         *      wrapped in double quotes.
         *
         * 3. PowerShell itself:
         *    - Inside the -Command string, single quotes are the simplest way
         *      to protect the URL and the output path—no extra escaping needed.
         */
        const char *cmd =
            "powershell.exe -NoLogo -NoProfile -Command "
            "\"Invoke-WebRequest "
            "-Uri 'https://aka.ms/vs/17/release/vs_BuildTools.exe' "
            "-OutFile 'C:\\users\\tamar\\Downloads\\vs_BuildTools.exe'\"";
    
        /* Execute and propagate the exit code back to the shell. */
        return system(cmd);
    }
    

    Building and running:

    1. Save it as test.c
    2. Start Developer PowerShell for VS2022 (it will set the environment for you. Simply start typing "Development Power ..." in the search window and windows will find it for you
    3. Build cl ./test.c
    4. Run ./test