Updating environment variable from C++ fails erratically in version 14...

#1
I have C++ code that updates an environment variable in a TCC process from a C++ program. This code no longer works consistently (sometimes it works, sometimes it doesn't, and I've been unable to identify a pattern, particularly since it appears that it both sometimes fails and sometmes works correctly for exactly the same input data - although failure is the most common result) but has worked fine for several years in previous versions (up through and including version 13; and I tested it again under version 13 immediately prior to making this posting) of TCC.

This is seriously complicated, which can't be helped, and probably only Rex knows/can do anything about it.

So, on to C++ code:

First the structure representing memory that is going to be shared between the TCC process and the running C++ program.
Code:
//    Structure to share data between this program and TCC
#pragma pack(push)
#pragma pack(4)

struct SharedData {
    BOOL     bAddToExisting;
    wchar_t     strVariable[1024];
    wchar_t     strValue[1024];

    SharedData() {
        ZeroMemory(strVariable, sizeof(strVariable));
        ZeroMemory(strValue, sizeof(strValue));
        bAddToExisting = TRUE;
    }
};

#pragma pack(pop)

Since the shared memory has to be updated by a DLL this is the class that handles all of the shared memory/DLL interaction.
Code:
class CDLLInjector {
    void FreeShareMem() {
        if (m_hMapFile) {
            CloseHandle(m_hMapFile);

            m_hMapFile = 0;
        }
    }

    bool InjectLibW(DWORD, bool);
    bool EjectLibW(DWORD);

    HANDLE m_hMapFile;

public:
    CDLLInjector();
    ~CDLLInjector();

    bool SetEnvironmentVariable(const DWORD,            //    Process ID
                                const char * const,        //    Variable Name
                                const char * const,        //    Variable Value
                                const bool);            //    Add To Existing flag

    bool CreateAndCopyToShareMem(const char * const, const char * const, const bool);
};

CDLLInjector::CDLLInjector() : m_hMapFile(0) {
}

CDLLInjector::~CDLLInjector() {
}

This is the code that actually (at least attempts to!) set an environment variable in a given process (which is assumed to be a TCC instance, of course):
Code:
bool CDLLInjector::SetEnvironmentVariable(const DWORD             dwProcessID,
                                          const char * const     strVarName,
                                          const char * const     strVarVal,
                                          const bool             bAddToExisting = false) {
    size_t             sztLngthVar    = strlen(strVarName);
    
    if (!sztLngthVar)
        return FALSE;

    CreateAndCopyToShareMem(strVarName, strVarVal, bAddToExisting);

    bool             bSuccess = InjectLibW(dwProcessID, false);

    //    If unable to update environment variable and
    //    new value was not null,,,
    if (!bSuccess && strVarVal) {
        Number             ldTemp;

        if (!GetVariableValue("NoSetEnvlibDLL", ldTemp, false)) {
            ReformatMessageToErrorStack(0,
                                        "Note:  Unable to store the results "
                                        "of the last calculation in an "
                                        "environment variable.  Using a "
                                        "\"Variables\" file instead. (You "
                                        "may want to check if "
                                        "\"SetEnvLib.DLL\" is in a "
                                        "directory that is in your path.)");

            AddOrUpdateVariable("NoSetEnvlibDLL");
        }

        bVariableChange = true;        //    Set to write environment information to a file
    }

    FreeShareMem();

    if (bSuccess)
        EjectLibW(dwProcessID);

    return bSuccess;
}

This is the routine that actually copies the data that is to be the contents of the environment variable in the "remote" TCC instance to shared memory.
Code:
//    Update the user entered data to sharememory
bool CDLLInjector::CreateAndCopyToShareMem(const char * const     strVarName,
                                           const char * const     strVarVal,
                                           const bool             bAddToExisting) {

    SharedData stData;

    size_t             sztLngthVar = strlen(strVarName),
                     sztLngthVal;

    if (!sztLngthVar || sztLngthVar >= (_countof(stData.strVariable) - 1)) {
        //    Not being done through error stack because total message length
        //    might be longer than the size of szMessageBuffer
        //    (But this is highly unlikely to ever occur.)
        cerr << "Variable name\n \"" << strVarName <<
                "\"\n""is too long. Currently supports a maximum of 1024 chars." << endl;

        return FALSE;
    }

    if (strVarVal)
        sztLngthVal = strlen(strVarVal);

    LPWSTR pBuf;

    size_t         sztReturnedValue1,
                 sztReturnedValue2;

    //    Prepare data for copying
    mbstowcs_s(&sztReturnedValue1, stData.strVariable, _countof(stData.strVariable), strVarName, sztLngthVar);

    if (strVarVal)
        mbstowcs_s(&sztReturnedValue2, stData.strValue, _countof(stData.strValue), strVarVal, sztLngthVal);
    else
        *(stData.strValue) = 0;

    stData.bAddToExisting = bAddToExisting;

    m_hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE, 0, 
                                    PAGE_READWRITE, 0, sizeof(stData),
                                    SHAREMEM_NAME);

    if (!m_hMapFile) { 
        cerr << "Could not create file mapping object" << endl;

        return FALSE;
    }

    pBuf = (LPWSTR) MapViewOfFile(m_hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(stData));

    if (!pBuf) { 
        cerr << "Could not map view of file!" << endl; 

        CloseHandle(m_hMapFile);

        m_hMapFile = 0;

        return FALSE;
    }

    // Copy the data
    CopyMemory((PVOID) pBuf, &stData, sizeof(stData));

    UnmapViewOfFile(pBuf);

    return TRUE;
}

This is the code that actually "injects" the DLL ("SetEnvLib.DLL") into the "remote" process (TCC instance). This is the routine that is now (version 14 of TCC) usually failing ("Failed to update selected process!").
Code:
//    Function to inject the library
bool CDLLInjector::InjectLibW(DWORD dwProcessId, bool bDisplayErrors = false) {
    HANDLE         hProcess            = 0;    // Process handle
    PWSTR         wszLibFileRemote    = 0;
    HANDLE         hRemoteThread        = 0; 
    bool         bSuccess            = true;

    __try {
        hProcess = OpenProcess(
            PROCESS_QUERY_INFORMATION    | PROCESS_CREATE_THREAD    |
            PROCESS_VM_OPERATION        | PROCESS_VM_WRITE,  // For CreateRemoteThread
            FALSE, dwProcessId);

        if(!hProcess) {
            if (bDisplayErrors)
                cerr << "Failed to update selected process!" << endl;

            bSuccess = false;

            __leave;
        }

        wchar_t         wszModulePath[MAX_PATH];

        if (!ExistsInPath(L"SetEnvLib.DLL", wszModulePath, _countof(wszModulePath))) {
            if (bDisplayErrors)
                cerr << "Error: Unable to determine existence of \"SetEnvLib.DLL\"!" << endl;

            bSuccess = false;

            __leave;
        }

        int             cb  = sizeof(wchar_t) * (wcslen(wszModulePath) + 1);

        //    Allocate space in the remote process for the pathname
        wszLibFileRemote = (PWSTR) VirtualAllocEx(hProcess, 0, cb, MEM_COMMIT, PAGE_READWRITE);

        if (!wszLibFileRemote) {
            if (bDisplayErrors)
                cerr << "Unable to allocate memory!" << endl;

            bSuccess = false;

            __leave;
        }

        //    Copy the DLL's pathname to the remote process' address space
        if (!WriteProcessMemory(hProcess, wszLibFileRemote, (PVOID) wszModulePath, cb, 0)) {
            if (bDisplayErrors)
                cerr << "Failed to write to Process Memory!" << endl;

            bSuccess = false;

            __leave;
        };

        //    Create remote thread and inject the library
        hRemoteThread = CreateRemoteThread(hProcess, 0, 0, 
            (LPTHREAD_START_ROUTINE)LoadLibraryW, 
            wszLibFileRemote, 0, 0);

        if(!hRemoteThread) {
            cerr << "Failed to update selected process!" << endl;

            return false;
        }

        WaitForSingleObject(hRemoteThread, INFINITE);
    }
    __finally {        // Do the cleanup
        // Free the remote memory that contained the DLL's pathname
        if (wszLibFileRemote) 
            VirtualFreeEx(hProcess, wszLibFileRemote, 0, MEM_RELEASE);

        if (hRemoteThread) 
            CloseHandle(hRemoteThread);

        if (hProcess) 
            CloseHandle(hProcess);
    }

    return bSuccess;
}

For completeness in this missive, this is the routine that "ejects" the DLL injected into the remote process (TCC session).
Code:
//    Eject the library loaded to remote process
bool CDLLInjector::EjectLibW(DWORD dwProcessID) {
    HANDLE     hthSnapshot    = 0,
             hProcess        = 0,
             hThread        = 0;

    bool     bOk            = false,    //    Assume that the function fails
             bFound            = false;

    __try {
        //    Grab a new snapshot of the process
        hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessID);

        if (hthSnapshot == INVALID_HANDLE_VALUE)
            __leave;

        //    Get the HMODULE of the desired library
        MODULEENTRY32W             me            = { sizeof(me) };
        BOOL                     bMoreMods    = Module32FirstW(hthSnapshot, &me);

        //    Iterate through all the loaded modules
        while (bMoreMods) {
            bFound    = (!_wcsicmp(me.szModule,  L"SetEnvLib.dll")) || 
                      (!_wcsicmp(me.szExePath, L"SetEnvLib.dll"));

            if (bFound)
                break;

            bMoreMods = Module32NextW(hthSnapshot, &me);
        }

        if (!bFound)
            __leave;

        //    Get a handle for the target process.
        hProcess = OpenProcess(
            PROCESS_QUERY_INFORMATION |   
            PROCESS_CREATE_THREAD     | 
            PROCESS_VM_OPERATION,  // For CreateRemoteThread
            FALSE, dwProcessID);

        if (!hProcess)
            __leave;

        //    Get the address of FreeLibrary in Kernel32.dll
        PTHREAD_START_ROUTINE     pfnThreadRtn    = (PTHREAD_START_ROUTINE)
            GetProcAddress(GetModuleHandleA("Kernel32"), "FreeLibrary");

        if (!pfnThreadRtn)
            __leave;

        //    Create a remote thread that calls FreeLibrary()
        hThread = CreateRemoteThread(hProcess, 0, 0, pfnThreadRtn, me.modBaseAddr, 0, 0);

        if (!hThread)
            __leave;

        //    Wait for the remote thread to terminate
        WaitForSingleObject(hThread, INFINITE);

        bOk = TRUE;        //    Everything executed successfully
    }
    __finally {
        if (hthSnapshot) 
            CloseHandle(hthSnapshot);

        if (hThread) 
            CloseHandle(hThread);

        if (hProcess)
            CloseHandle(hProcess);
    }

    return bOk;
}

I have nothing more to say on this subject, but I've probably said more than enough! :)
 
#2
I doubt TCC could do anything to thwart this or even inadvertently make it fail. So I'd be looking for a bug.

The text "Failed to update selected process!" appears in more than one place. So you (I) don't know if OpenProcess() or CreateRemoteThread() failed. I'd suggest more detailed messages (name the failing function and report GetLastError()).

My SYSUTILS variable function @PSET[PID,var[=[value]]] (123 lines of code, including blanks) sets/unsets/gets variables in other processes. I just gave it an extensive test and it did not fail in a few thousand attempts when the target process was TCC 14. It does not inject a DLL (injects code and data separately, then calls CreateRemoteThread, then frees the two remote memory areas). There's no need for a memory mapped file or a Win32Snapshot.
 
#3
Vince,

Thank you for noticing the repeated "failed to update selected process!" messages. Just for curiosity sake, I'm going to take the little bit of time needed to track down which one is actually failing. (Since I hadn't noticed that there are two, I just assumed the one I marked was what was failing).

And while you are certainly correct if you've done it yourself, this code was written a very long time ago and may even have been written for cmd.exe! I'll also note that this code has worked just fine for, again, many years, so there is something about version 14 that broke it as it now works only erratically. (I'll also note that in looking around on the web for code related to this all I found was code of a similar nature which would then presumably break under version 14, also.)

So if you can give me a little bit more of a clue as to what's in your 123 lines of code I'd really appreciate it, I have no emotional ties to this (or any other, for that matter) code... (And I'm not nearly as fast as I'd used to be given my disabilities.)

While the actual code actually has a fail-safe mechanism and is producing the correct results despite the error being discussed, it's a much heavier duty process that is overkill if this code would work. And at the moment, the program produces the given warning message whenever the listed code fails before using the alternative process. I'd rather get rid of the problem and not just the warning message. I use this program literally dozens of times a day; it's the number one tool in my arsenal (although with TCC alone I could achieve probably 90% of the functionality of this program in a batch file, with the most of the missing 10% being stuff that's only there to make the program "logically complete" (i. e., it can do any possible task in the nature of the tasks that it does) so I've often considered just replacing it with a batch file). However, since it's worked perfectly up until version 14 and I'm fond of this "completeness", I've had no incentive to replace it with a batch file and still don't really want to do that on general principles alone (i.e., if a program has a small bug you fix it rather than replacing the program as a whole). And I take it back, given how many thousands of times (dozens of times in a given day which makes the given error somewhat more annoying because I'm looking at it all of the time) I've used this program in probably 20+ years (it started out as a C program and was migrated to a C++ program over time), I am fond of it. It was once my proudest programming achievement because it could do, again, literally any task in the nature of the tasks that it performs with truly outstanding accuracy. (Prior to TCC, it was more accurate than anything I could find to compare it against. For those things both TCC and it can do, and except for one factor that's in TCC for the sake of another kind of completeness I would venture, TCC has a definite rather small subset of its total functionality, although it is, again, the only functionality that is actually needed more than about 95% of the time.)

And finally, I'll note that since it's using a Microsoft-supplied DLL (specifically, "SetEnvLib.DLL") I'm doing it the logically "official" way. And I've always had a strong incentive to do things the "official" way, and that's something that isn't going to change because it is the safest approach to writing code that continues to work in the long run (and the fact that this code no longer works reliably is a definite exception).

- Dan
 
#4
Vince,

Quick update! It's "CreateRemoteThread" that's now failing, exactly the same API that you are using to run your code in the remote process, only mine's failing erratically - I had to run the program more than 10 times before the bug even showed up after I put markings ("(I)" and "(II)") to identify where the failure was. And, I want to make this perfectly clear re a bug in my code: This has never (ever!!!!) failed until version 14, so even if there's a possibility that it's a bug in my code (I don't really dispute that), it's a bug that's been totally invisible up until version 14, so there's definitely a change of some kind there.

- Dan
 
#5
Vince,

Thank you for noticing the repeated "failed to update selected process!" messages. Just for curiosity sake, I'm going to take the little bit of time needed to track down which one is actually failing. (Since I hadn't noticed that there are two, I just assumed the one I marked was what was failing).


So if you can give me a little bit more of a clue as to what's in your 123 lines of code I'd really appreciate it, I have no emotional ties to this (or any other, for that matter) code... (And I'm not nearly as fast as I'd used to be given my disabilities.)


And finally, I'll note that since it's using a Microsoft-supplied DLL (specifically, "SetEnvLib.DLL") I'm doing it the logically "official" way. And I've always had a strong incentive to do things the "official" way, and that's something that isn't going to change because it is the safest approach to writing code that continues to work in the long run (and the fact that this code no longer works reliably is a definite exception).
Use GetLastError()!!!!!!!

I doubt SetEnvLib.DLL came from MS. Googling it's name gives 7 results (far too few to be from MS). And I doubt there's an "official" way to mess with another process's environment. :)

Below is what I write to two locations in the target process (the code being PAGE_EXECUTE_READWRITE). Wait for the thread and interpret the results (in the "get" case, copying the remote data). The injection techniques you already have. Close/free everything when done.

Code:
/* types */
typedef BOOL    ( WINAPI *SEV_TYPE )( WCHAR*, WCHAR* );
typedef BOOL    ( WINAPI *GEV_TYPE )( WCHAR*, WCHAR*, DWORD );
 
/* A local copy of this struct will be initialized and then
  written to a location, pData, in the remote process's address space.
  Init pSEV and pGEV with GetProcAddress(). */
#define DATA_SIZE_WCHARS 32768 // enough for any value
typedef struct PSET_REMOTE_DATA
{
    SEV_TYPE    pSEV; /* SetEnvironmentVariableW */
    GEV_TYPE    pGEV; /* GetEnvironmentVariableW */
    BOOL        bGet; /* versus Set */
    WCHAR        szVarName[128]; /* big enough? */
    WCHAR        szData[DATA_SIZE_WCHARS];
} PSET_REMOTE_DATA, *LPPSET_REMOTE_DATA;
 
/* This code will be written to a location, pCode, in the remote process's address space.
  CreateRemoteThread() will use pCode as lpStartAddress and pData as lpParameter.
  If you (temporarily) put this code in its own section you can get its size with DUMPBIN. */
//#pragma code_seg(".inject")
DWORD WINAPI remote_code( LPPSET_REMOTE_DATA pData )
{
    if ( pData->bGet ) // get value
    {
        DWORD dwDataSize = 1;                // NUL terminator
 
        BOOL bOK = pData->pGEV(pData->szVarName, pData->szData, DATA_SIZE_WCHARS);
 
        if ( bOK )
        {
            WCHAR *q = pData->szData;
            while ( *q++ ) dwDataSize += 1;    // data size in WCHARs
        }
 
        return bOK ? dwDataSize : 0;        // success : failure
    }
 
    else    // set or unset variable
        return (DWORD) pData->pSEV(pData->szVarName, pData->szData[0] ? pData->szData : NULL);
}
//#pragma code_seg()
 
/* virtual size of ".inject" code segment (from DUMPBIN) is 0x57 */
#define CODEINJECTSIZE 0x57 /* will get a whole page anyway */
 
#6
Vince,

It turns out on even closer examination that the only difference I can see in my code vs. the description of your code is that I'm using "SetEnvLib.DLL" to update the environment variable rather than code that I personally wrote (again, the technique I strongly prefer). So if you would look at my code more closely (and I don't even see the possibility of an uninitialized variable in my code, what I assumed was the most likely cause of the erratic behavior) I would really appreciate it.

- Dan

P. S. And the only explanation I have for the length of my code vs. the length you reported for your code is #1, that I'm explicitly injecting a (Microsoft supplied) DLL rather than code I wrote on my own, and #2 my code is heavily commented and has a lot of white space.
 
#7
Vince,

It turns out on even closer examination that the only difference I can see in my code vs. the description of your code is that I'm using "SetEnvLib.DLL" to update the environment variable rather than code that I personally wrote (again, the technique I strongly prefer). So if you would look at my code more closely (and I don't even see the possibility of an uninitialized variable in my code, what I assumed was the most likely cause of the erratic behavior) I would really appreciate it.
I see nothing glaring. I don't know how setenvlib.dll works. And I don't have the patience to read too carefully. [And if I want some difficult reading, I have a pile of Linear Algebra final exams right next to me!] As I said before, put a lot of messaging in there and output GetLastError() when something goes wrong.
 
#9
Thank you Vince, I'm going to let you off the hook for now (your Linear Algebra exams are far more important!). I no longer remember how/where I found/found out about SetEnvLib.DLL, and, as you pointed out, a Google search produces mostly irrelevant results (other than the code project one, which I haven't looked at as of yet). Nor have I looked at your code yet, but thank you, I will.

However, just as an FYI, the program already had a wrapper function around GetLastError that got the error code and formatted and printed the appropriate error message and which I used. And the error code is (drum roll): FAILED with code 5: Access is denied. Unfortunately, this is not very helpful. Why is access denied apparently completely randomly, and access to what? The memory of the remote process??? And running the program in Administrative mode changes nothing. I'm going to think about things a bit before I proceed further on the coding level.

As I said, I've got a (much slower) alternative which works just fine: specifically writing what would have been in the created/updated environment variable into a small file. (Note that files this small take up literally no space on a hard drive (other than that required for its name and attributes) as files of this nature fit entirely in the MFT ("Master File Table"). The problem is that these files are not (because they can not) be automatically deleted because the program has no way of knowing when any given run in any given address space (TCC session) will be the last run in that address space. (The environment variables are a form of "memory" saving information that is passed from run to successive run). However, the program already had a command line parameter to delete these files, either for that specific process or all of them that may have been created historically.

Vince, this should be a low priority for you but I'm going to open up a private "conversation" with you where I'll tell you what the program is/does (and you'll immediately understand why I use it so often). I will put all of that information in this conversation which you can peruse if and when you want to just to satisfy any curiosity you may have.

Again, thank you, Vince!

- Dan
 
#10
Vince, the Code Project's executable downloaded directly from the Code Project's website (i.e., I neither compiled nor linked it) doesn't work under version 14 of TCC, either, but works fine under version 13. Whatever Rex broke, it affects more than just my code and how your code escaped is a mystery to me. However, I will note that while the Code Project executable works for version 13 of TCC as I've already stated, it no longer works with cmd.exe. - Dan
 
#11
I just DL'd the demo and used it 20 times with TCC 14 as target process. It did not fail. As I said before, I doubt any target process could thwart this, either deliberately or inadvertently.

CMD must handle it's environment in a very peculiar way ... perhaps keeping a copy of it and **not** looking at the real environment when you query a variable with "SET varname". I'll start with this in in CMD:

Code:
C:\Users\vefatica> set q
q=666
 
C:\Users\vefatica> echo %q%
666
Now I'll use DynamicSetEnvVar (or my PSET) to set CMD's q to 999. Back in CMD I see:

Code:
C:\Users\vefatica> set q
q=666
 
C:\Users\vefatica> echo %q%
999
 
C:\Users\vefatica> set q
q=666
At this point, I can force CMD to update its copy (?) of the environment by (in CMD) setting some other variable!

Code:
C:\Users\vefatica> set x=0
 
C:\Users\vefatica> set q
q=999
 
C:\Users\vefatica> echo %q%
999
TCC does not behave this way.

Rex probably knows more about how CMD works than most. Out of curiosity, I will start another thread asking about that peculiar behavior.
 
#13
Well, Vince, it's black and white with no shades of grey. On the surface it appears that the Code Project program works without issue for version 13 TCC prompts, and fails miserably for version 14 command prompts (message box "Failed to update selected process."). At first I thought that this was the end of the story, but I wondered, and after a little bit more experimentation I found the problem and frankly it's kind of a bad one given my ultimate goal: The version 14 TCC I was trying to update is a 64 bit version, and the Code Project program fails to update it. However, everything's fine when it comes to a 32-bit instance of TCC version 14. Next I'll experiment with your plugin to see if it works, but I strongly suspect that it won't either, even assuming I can run your plugin in a 64-bit TCC session (can I?). (But this will take me a while since I don't even remember what plugin it is and I'm therefore starting from complete scratch.)

So, again, this is bad news re my ultimate goal. It means that I'll have to store the information I want to keep in small files (as it does now when the environment variable code fails) or in the registry. I just don't like the idea of having a bunch of small files sitting around (again, it's impossible to automagically delete them), and I would have an equivalent problem if I used the registry, although maybe the consequences of that are really slight (main problem: picking up the saved information from previously-run instances of the same program that coincidentally had the same PID. Also, over time I can see dozens maybe even hundreds of these registry keys accumulating, although if I have the disciplilne I can run the program with the "/C[lear]" parameter periodically. Maybe I could put a run of the program in my start-up sequence. (Any suggestions as to what is the best bet here?) - Dan
 
#14
I put a 64-bit version of PSET.EXE in ftp://vefatica.net/pset64.zip. I have no way to test it.

As for the plugin @PSET[], I suspect the 32-bit one will work with a 32-bit target process and the 64-bit one will work with a 64-bit target process (but I really don't know about these things).

Please let me know if either works with a 64-bit target process.
 
#16
Summary of running 64- and 32-bit PSets on 64- and 32-bit targets from 64- and 32-bit TCC sessions:

The 64-bit version can update 64-bit TCC sessions irregardless of whether it's being run from a 64- or 32-bit TCC session (no real surprise here).

The 64-bit version crashes 32-bit TCC sessions irregardless of whether it's being run from a 64- or 32-bit TCC session (no real surprise if you think about it). 64-bit run from 64-bit updating 32-bit produces the message "Wait timed out or failed"; 64-bit run from 32-bit updating 32-bit just ends (again after crashing target TCC session) with no message at all. The lack of an error message is somewhat surprising.

And I don't understand this, but I couldn't get the 32-bit PSet to run successfully at all ("CreateRemoteThread failed: Access is denied.") in either a 32- or 64-bit TCC. Real surprising, at least to me at the moment. Did we figure this out already?

I haven't had time to check out your latest offering; coming soon.

- Dan
 
#17
Vince, this was fast. Here's the result:

[E:\DownLoads\DynamicSetEnvVar64.unzip]DynamicSetEnvVar.exe 2904 abc=def
TCC: (Sys) The application has failed to start because its side-by-side configurati
on is incorrect. Please see the application event log or use the command-line sxstr
ace.exe tool for more detail.
"E:\DownLoads\DynamicSetEnvVar64.unzip\DynamicSetEnvVar.exe"

I've personally have never seen this before and I don't have a clue as to what it means.

- Dan
 
#18
Summary of running 64- and 32-bit PSets on 64- and 32-bit targets from 64- and 32-bit TCC sessions:

The 64-bit version can update 64-bit TCC sessions irregardless of whether it's being run from a 64- or 32-bit TCC session (no real surprise here).

The 64-bit version crashes 32-bit TCC sessions irregardless of whether it's being run from a 64- or 32-bit TCC session (no real surprise if you think about it). 64-bit run from 64-bit updating 32-bit produces the message "Wait timed out or failed"; 64-bit run from 32-bit updating 32-bit just ends (again after crashing target TCC session) with no message at all. The lack of an error message is somewhat surprising.

And I don't understand this, but I couldn't get the 32-bit PSet to run successfully at all ("CreateRemoteThread failed: Access is denied.") in either a 32- or 64-bit TCC. Real surprising, at least to me at the moment. Did we figure this out already?

I haven't had time to check out your latest offering; coming soon.

- Dan
There's no way 32-bit PSET and a 64-bit target process will work; likewise for 64-bit PSET and a 32-bit target process. No result would surprise me. Someday, when I have the time and a 64-bit Win7 to test on, I'll see if PSET can figure out what the target process is and abort if it's not a 32/32 or 64/64 match.

I'm glad to hear the 64 bit PSET works with a 64-bit target process. I don't know why the 32-bit PSET doesn't work with a 32-bit target process (would you try again?) ... unless there's some Win7 limitation on 32-bit processes running in Win7/64. What's your user status? ... ordinary? ... admin under UAC? ... elevated?
 
#19
Vince, this was fast. Here's the result:

[E:\DownLoads\DynamicSetEnvVar64.unzip]DynamicSetEnvVar.exe 2904 abc=def
TCC: (Sys) The application has failed to start because its side-by-side configurati
on is incorrect. Please see the application event log or use the command-line sxstr
ace.exe tool for more detail.
"E:\DownLoads\DynamicSetEnvVar64.unzip\DynamicSetEnvVar.exe"

I've personally have never seen this before and I don't have a clue as to what it means.

- Dan
Well, I built it right out of the box (after changing a single "int" to "SIZE_T" because of a compiler warning). I built it with VS2008 (VC9) and it depends on MSVCP90.DLL, MSVCR90.DLL, and MFC90U.DLL. Do you have those on your system?

I'll build again with VS2010 which will probably link it with newer DLLs and make a post later when it has been uploaded.
 
#21
Well, I built it right out of the box (after changing a single "int" to "SIZE_T" because of a compiler warning). I built it with VS2008 (VC9) and it depends on MSVCP90.DLL, MSVCR90.DLL, and MFC90U.DLL. Do you have those on your system?

I'll build again with VS2010 which will probably link it with newer DLLs and make a post later when it has been uploaded.
OK, I re-build it with VC10 and not it depends on the "100" versions of those DLLs. I replaced the one on lucky with the newer one: ftp://vefatica.net/DynamicSetEnvVar64.zip
 
#22
I don't know why the 32-bit PSET doesn't work with a 32-bit target process (would you try again?) ... unless there's some Win7 limitation on 32-bit processes running in Win7/64. What's your user status? ... ordinary? ... admin under UAC? ... elevated?
I built PSET (32 and 64) again, adding the flag PROCESS_QUERY_INFORMATION to OpenProcess(). The docs say it's needed fpr CreateRemoteThread() but it wasn't really needed in the 32-bit version running on a 32-bit OS. Perhaps it is needed when the 32-bit version is run on a 64-bit Win7. Anyway, there's a new PSET.ZIP and PSET64.ZIP on ftp://vefatica.net.
 
#23
Vince, I'm actually pretty sure about the 32/32/32 not working (I actually did it 4 times starting from scratch in brand-new instances of 32-bit Take Command/TCC sessions to totally eliminate the possibility that I was making a stupid mistake (which I'm so fond of) because I didn't quite believe it myself). I don't understand that either but tomorrow I'll do it yet one more time this way with your latest version (but only one more time! :)). I am running as the only user that has been defined on this (again, brand new) machine so I am a pseudo-administrator by default (and I didn't get any UAC prompts). But tomorrow I'll also run your latest version from an elevated command prompt to absolutely eliminate that possibility if the problem reoccurs otherwise. - Dan
 
#24
Vince, I'm actually pretty sure about the 32/32/32 not working (I actually did it 4 times starting from scratch in brand-new instances of 32-bit Take Command/TCC sessions to totally eliminate the possibility that I was making a stupid mistake (which I'm so fond of) because I didn't quite believe it myself). I don't understand that either but tomorrow I'll do it yet one more time this way with your latest version (but only one more time! :)). I am running as the only user that has been defined on this (again, brand new) machine so I am a pseudo-administrator by default (and I didn't get any UAC prompts). But tomorrow I'll also run your latest version from an elevated command prompt to absolutely eliminate that possibility if the problem reoccurs otherwise. - Dan
I thought your machine was 64-bit! I know the 32(pset)/32(target_proc)/32(OS) one works.
 
#25
Vince,

The only conclusion I can draw from the above is that put simply, you weren't aware that 64-bit machines are completely capable of running 32-bit programs (and most programs currently on this machine are 32-bit. It's the rare program that's actually 64-bit). And, I repeat, 32/32/32 did not work for me after multiple, completely independent attempts, and that really surprised me.

But I'll do it all again tomorrow (actually, later today at the time I'm writing this! :))

- Dan
 
#26
Vince,

The only conclusion I can draw from the above is that put simply, you weren't aware that 64-bit machines are completely capable of running 32-bit programs (and most programs currently on this machine are 32-bit. It's the rare program that's actually 64-bit). And, I repeat, 32/32/32 did not work for me after multiple, completely independent attempts, and that really surprised me.
I was aware of that. So what do you mean by 32/32/32?
 
#28
First, Vince, I had a real hard time believing that you weren't aware that 64-bit processors ran 32-bit programs in 32-bit address spaces, but it's the only thing that a statement I at least thought you made (remember, I'm half-blind and stupid) meant. But since I can no longer find said statement (looking though your postings in the thread I didn't see it) I must have been wrong and it was a product of my imagination. I apologize.

And by 32/32/32 I meant running the 32-bit PSet from a 32-bit TCC (I have both the 32- and 64-bit versions installed) attempting to set an environment variable in another 32-bit TCC - i.e., every thing is 32-bit and there's no 64-bit anything involved anywhere. And the message I get (using the version of PSet I have) is "CreateRemoteThread failed: Access is denied.". That happens 100% of the time when running the 32-bit version of PSet (I just did it yet again using the 32-bit PSet run from a 32-bit TCC trying to add an environment variable to another 32-bit TCC), and it doesn't matter if the command prompt I'm running PSet from is 32- or 64-bit (as it shouldn't).

Am I missing something (could very well be the case!)? I haven't yet seen a link for your latest 32-bit PSet in any of your previous postings. (Although you do say "make a post later when it has been uploaded".)

- Dan
 
#29
And, Scott, /V "volatile" environment means the opposite of what you indicate. Looking at the docs it says that /V puts the variable also in the registry rather than the opposite (/V means don't put the variable in the registry which is certainly the idea I got when reading your posting). (And so far I haven't figured out the way you read "environment" variables that exist in the registry!)

But it doesn't matter in any case, this is a command-line C++ program (i.e., no explicit "set" commands at all) that (was) attempting to set environment variables that it would read the next time it was run; i.e., the program maintained a "history" and it did so by setting environment variables. However, I'm in the process of replacing the environment variables by a file. Quite a bit of code is involved in the translation (it did everything attempting to use environment variables and only went to the "file" option when that failed, and, in particular, there was significant functionality provided by an environment variable that wasn't included in the file (my mistake which I hadn't noticed because setting environment variables from a C++ program worked prior to TCC version 14). So rewriting that part of the program is a bigger task than I thought it would be. (The program is 32-bit, but it must be runnable in both 32-bit and 64-bit TCC instances.)

- Dan
 
#30
And by 32/32/32 I meant running the 32-bit PSet from a 32-bit TCC (I have both the 32- and 64-bit versions installed) attempting to set an environment variable in another 32-bit TCC - i.e., every thing is 32-bit and there's no 64-bit anything involved anywhere. And the message I get (using the version of PSet I have) is "CreateRemoteThread failed: Access is denied.". That happens 100% of the time when running the 32-bit version of PSet (I just did it yet again using the 32-bit PSet run from a 32-bit TCC trying to add an environment variable to another 32-bit TCC), and it doesn't matter if the command prompt I'm running PSet from is 32- or 64-bit (as it shouldn't).

Am I missing something (could very well be the case!)? I haven't yet seen a link for your latest 32-bit PSet in any of your previous postings. (Although you do say "make a post later when it has been uploaded".)
The parent process of PSET (or DynamicSetEnvVar) is irrelevant.

I have rebuilt both the 32-bit and 64-bit versions of PSET and DynamicSetEnvVar (and DLL) with VC10 and with all the recommended flags in the call to OpenProcess() (DynamicSetEnvVar was also missing one flag). They are all here.

As for Dan's reply to Scott ... think of the registry as THE BIG INI FILE IN THE SKY and think of it as being IN MEMORY (though it is almost constantly synchronized with a disk image). When a user logs off his little part of the registry is written to disk to be used the next time he logs on. When the system is shut down, the whole registry is written to disk to be used when the system is restarted. Anyway, the key "HKEY_CURRENT_USER\Volatile Environment" acts like other environment keys except that it is never saved to disk. Anything you put there disappears when you log off. Access it with TCC's "SET /V". To make using the variables in the various registry environments, my 4UTILS plugin offers @GEV[] which just retrieves a variable's value.
Code:
v:\> help @GEV
@GEV[name[,s|u|v|d]] = value of [registry] environment variable