2014-09-18 16 views
8

Sto tentando di utilizzare CreateProcess per avviare un nuovo blocco di ambiente ed eseguire un file batch nel nuovo blocco di ambiente. Ho letto l'esempio di msdn per CreateProcess e ho trovato il codice mostrato di seguito.Utilizzare CreateProcess per eseguire un file batch

Cosa sta succedendo, si aprirà il nuovo prompt dei comandi e si fermerà qui. Non eseguirà il mio file .bat per qualche motivo. L'uso del sistema ("percorso CALL") chiamerà il file .bat.

#include <iostream> 

#define WINDOWS_LEAN_AND_MEAN 
#include <Windows.h> 

#include <strsafe.h> 

#define BUFSIZE 4096 

int main() 
{ 
    //system("CALL C:\\HFSS\\setup_vars.bat"); 

    //return 0; 

    LPWCH chNewEnv; 
    LPTSTR lpszCurrentVariable; 
    DWORD dwFlags = 0; 
    TCHAR szAppName[] = TEXT("C:\\windows\\system32\\cmd.exe"); 
    TCHAR cmdArgs[] = TEXT("C:\\HFSS\\setup_var.bat"); 

    STARTUPINFO si; 
    PROCESS_INFORMATION pi; 
    BOOL fSuccess; 

    // Copy environment strings into an environment block. 
    chNewEnv = GetEnvironmentStrings(); 

    lpszCurrentVariable = (LPTSTR)chNewEnv; 
    if (FAILED(StringCchCopy(lpszCurrentVariable, BUFSIZE, TEXT("MySetting=A")))) 
    { 
     printf("String copy failed\n"); 
     return FALSE; 
    } 

    lpszCurrentVariable += lstrlen(lpszCurrentVariable) + 1; 
    if (FAILED(StringCchCopy(lpszCurrentVariable, BUFSIZE, TEXT("MyVersion=2")))) 
    { 
     printf("String copy failed\n"); 
     return FALSE; 
    } 

    // Terminate the block with a NULL byte. 

    lpszCurrentVariable += lstrlen(lpszCurrentVariable) + 1; 
    *lpszCurrentVariable = (TCHAR)0; 

    // Create the child process, specifying a new environment block. 

    SecureZeroMemory(&si, sizeof(STARTUPINFO)); 
    si.cb = sizeof(STARTUPINFO); 

#ifdef UNICODE 
    dwFlags = CREATE_UNICODE_ENVIRONMENT; 
#endif 

    fSuccess = CreateProcess(szAppName, cmdArgs, NULL, NULL, TRUE, dwFlags, 
     (LPVOID)chNewEnv, // new environment block 
     NULL, &si, &pi); 

    if (!fSuccess) 
    { 
     printf("CreateProcess failed (%d)\n", GetLastError()); 
     return FALSE; 
    } 

    std::cout << "In new environment\n"; 
    WaitForSingleObject(pi.hProcess, INFINITE); 

    return TRUE; 
} 
+0

Su quale linea vuol innescare un punto di rottura? Le linee di cui non sei sicuro stanno cercando di aggiungere due nuove variabili ambientali al blocco di ambiente del processo che sta per essere avviato, e hai indicato che è quello che stai cercando di fare, quindi non sono sicuro di quale sia il tuo la domanda su di loro potrebbe essere. –

+0

In realtà sto provando a impostare le variabili di ambiente nel file batch. Tuttavia, quando arrivo al file batch (senza creare il nuovo processo), la variabile% PATH% ha tutti i dati dell'albero da Visual Studio. Quindi, quando provo ad aggiungere il nuovo percorso alla variabile% PATH%, sono incluse informazioni ridondanti e in eccesso. Inoltre, ho trovato che l'arresto anomalo era dovuto all'utilizzo di "\" invece di "\\" – user2970916

+0

La tua modifica non indirizza tutti i punti che ho generato. –

risposta

15

Alcuni problemi:

  1. è necessario passare l'opzione /C a cmd.exe al fine di renderlo eseguire il file .bat.
  2. Il secondo parametro su CreateProcess deve essere una stringa modificabile. Non un letterale.
  3. È necessario sfuggire ai caratteri di barra rovesciata in letterali.
  4. lpszCurrentVariable punti al buffer restituito da GetEnvironmentStrings. Non è possibile modificare quel buffer. È necessario allocare un nuovo buffer di lunghezza sufficiente e copiare l'ambiente in esso. Quindi aggiungi le tue modifiche.
  5. I blocchi di ambiente terminano con doppio valore nullo. Le funzioni di stringa standard non sono utili con le stringhe con doppio valore nullo.
  6. L'utilizzo di funzioni come StringCchCopy piuttosto che le funzioni di runtime C è solo fonte di confusione. Non prendere il codice di esempio MSDN come il modello di stile.
  7. Le stringhe C sono un vincolo con cui lavorare. Ma tu usi C++, quindi usa std::wstring e altre classi e funzioni di libreria standard.
  8. È necessario definire WINDOWS_LEAN_AND_MEAN prima di importare Windows.h.
  9. Per C++, int main(void) non è corretto. Il no argument main è int main().

Il codice seguente mostra come fare questo:

#include <cstring> 
#include <string> 
#include <iostream> 

#define WINDOWS_LEAN_AND_MEAN 
#include <Windows.h> 

std::wstring GetEnvString() 
{ 
    wchar_t* env = GetEnvironmentStrings(); 
    if (!env) 
     abort(); 
    const wchar_t* var = env; 
    size_t totallen = 0; 
    size_t len; 
    while ((len = wcslen(var)) > 0) 
    { 
     totallen += len + 1; 
     var += len + 1; 
    } 
    std::wstring result(env, totallen); 
    FreeEnvironmentStrings(env); 
    return result; 
} 

int main() 
{ 
    std::wstring env = GetEnvString(); 
    env += L"myvar=boo"; 
    env.push_back('\0'); // somewhat awkward way to embed a null-terminator 

    STARTUPINFO si = { sizeof(STARTUPINFO) }; 
    PROCESS_INFORMATION pi; 

    wchar_t cmdline[] = L"cmd.exe /C C:\\Desktop\\MyBatFile.bat"; 

    if (!CreateProcess(NULL, cmdline, NULL, NULL, false, CREATE_UNICODE_ENVIRONMENT, 
     (LPVOID)env.c_str(), NULL, &si, &pi)) 
    { 
     std::cout << GetLastError(); 
     abort(); 
    } 

    CloseHandle(pi.hProcess); 
    CloseHandle(pi.hThread); 
} 
+0

Grazie per tutto l'aiuto David, comunque, l'esecuzione mi dà ancora il mio problema originale: Il mio file batch legge% PATH% variabile come copia locale (proveniente da Visual Studio). Ecco un collegamento al problema originale: http://stackoverflow.com/questions/25917796/setting-path-variable-is-not-working – user2970916

+0

Funziona bene qui. Inoltre, osserverai che il codice che mostro è molto più semplice del tuo e ti mostra come evitare tutto l'horrid C boilerplate. –

+0

In ogni caso, iniziare con il mio programma esatto. Sostituisci il nome del file .bat con un nome file valido sul tuo sistema. Nel tuo file .bat posiziona il comando 'set' che elenca le variabili di ambiente. Cosa succede quando lo fai? –

Problemi correlati