views:

248

answers:

3

I currently have a thread that I created using CreateRemoteThread(). Everything works great. Upon finishing (or an error before completion) the thread returns one of the five return codes that we defined.

I run into a problem and I need to return the results of GetLastError() as well. Is there any way to return two values?

I am using WaitForSingleObject() and then GetExitCodeThread() to get the thread's exit value.

The reason I need two values (as opposed to returning only GetLastError) is because I need to know where in the remote thread the error occurred and that is returned by the 1st value.

code is appreciated.

Thanks

A: 

Return a pointer to a struct that has the fields you need.

Aric TenEyck
This won't work on 64-bit - thread return codes are DWORDs, and DWORD is only 32-bits wide, so it's too small to hold a 64-bit pointer.
bdonlan
Ah. In that case, use bdonlan's suggestion.
Aric TenEyck
+2  A: 

Before starting the thread, allocate a structure on the heap with room for the error return parameters. Pass it to the thread via the lpParameter argument of CreateThread. Prior to termination, leave your error codes there.

Then just remember the location of the structure in whatever thread's waiting for this thread, and read the results out of there.

For example:

struct threadinfo {
    void *someArgument;
    DWORD error_code;
    int error_flag;
};

DWORD WINAPI threadproc(LPVOID arg) {
    struct threadinfo *ti = (struct threadinfo *)arg;
    // do things using ti->someArgument
    ti->error_code = GetLastError();
    ti->error_flag = 42;
    return 0; // return code ignored
}

void start_and_wait() {
    struct threadinfo *ti = (struct threadinfo *)malloc(sizeof(*ti));
    ti->someArgument = &something;

    HANDLE hThread = CreateThread(NULL, 0, threadproc, (LPVOID)ti, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);

    printf("Exit code %08x, flag %d\n", ti->error_code, ti->error_flag);
    free((void*)ti);
}

If you're talking to a remote thread, one option may be to pass a handle to a shared memory segment as lpParameter. You can inject the handle into the remote process via DuplicateHandle, then start up the thread. The thread can then, before returning, map the shared memory segment (if it hasn't already), place its results into the shared memory segment, then unmap it and close the handle.

For example, in the remote process:

DWORD WINAPI threadproc(LPVOID arg) {
    LPVOID shmem = MapViewOfFile((HANDLE)arg, FILE_MAP_ALL_ACCESS, 0, 0, 4096);
    struct threadinfo *ti = (struct threadinfo *)shmem;

    // do stuff
    ti->error_code = GetLastError();
    ti->error_flag = 42;

    UnmapViewOfFile(shmem);
    CloseHandle((HANDLE)arg);
    return 0;
}

And in the calling process:

HANDLE mapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4096, NULL);
LPVOID shmem = MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, 4096);
struct threadinfo *ti = (struct threadinfo *)shmem;
ti->someArgument = ...;

HANDLE remote_handle;
DuplicateHandle(GetCurrentProcess(), mapping, hRemoteProcess, &remote_handle, 0, FALSE, DUPLICATE_SAME_ACCESS);
HANDLE hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0, startAddress, remote_handle, 0, NULL);

WaitForSingleObject(hRemoteThread);
printf("err %08x flag %d\n", ti->error_code, ti->error_flag);
UnmapViewOfFile(shmem);
CloseHandle(mapping);
bdonlan
can I pass a pointer to a struct into a remote thread?
wonderer
oh, I see. The thing is that I am using CreateRemoteThread() not CreateThread. The thread runs in the context of another process so I don't think I can send a pointer to a struct
wonderer
Ah, sorry - didn't see it. The technique still works using shared memory - see my updated post for details.
bdonlan
A: 

One classic, and terribly ugly, way to return two values at once is to shift and or into a larger type.

For example, to return two ints, you can shift one of them up by the number of bits in an int and or it with the other value.

Your calling code then has to pull the pieces apart.

It's for emergencies only, of course.

Nosredna
No such thing as an emergency here - and what happens if an error code uses all 32 bits of a DWORD?
bdonlan
Then you are out of luck. It's a terrible solution. I've had a couple emergencies in my life, but if there are no emergencies where you live, then by all means do _not_ use it. :-)
Nosredna