tags:

views:

236

answers:

7

Hello,

I am writing a simple console application which will allow me to create a number of threads from a set of parameters passed through the arguments I provide.

DWORD WINAPI ThreadFunc(LPVOID threadData)
{
}

I am packing them into a struct and passing them as a parameter into the CreateThread method and trying to unpack them by casting them to the same type as my struct from the LPVOID.

I'm not sure how to cast it to the struct after getting it through so I can use it in the method itself, i've tried various combinations (Example attatched) but it won't compile.

Struct:

#define numThreads 1

struct Data
{
    int threads;
    int delay;
    int messages;
};

Call to method:

HANDLE hThread;
    DWORD threadId;
    struct Data *tData;

    tData->threads = numThreads;
    tData->messages = 3;
    tData->delay = 1000;


    // Create child thread
    hThread = CreateThread(
                            NULL,       // lpThreadAttributes (default)
                            0,          // dwStackSize (default)
                            ThreadFunc, // lpStartAddress
                            &tData,     // lpParameter
                            0,          // dwCreationFlags
                            &threadId   // lpThreadId (returned by function)
                           );

My attempt:

DWORD WINAPI ThreadFunc(LPVOID threadData)
    {
        struct Data tData = (struct Data)threadData;

        int msg;

        for(msg = 0; msg<5; msg++)
        {
            printf("Message %d from child\n", msg);
        }
        return 0;
}

Compiler error:

error C2440: 'type cast' : cannot convert from 'LPVOID' to 'Data'

As you can see I have implemented a way to loop through a number of messages already, I'm trying to make things slightly more advanced and add some further functionality.

+1  A: 

You are attempting to cast a pointer value (LPVOID is equivalent to void*) into a non-pointer value. This is not allowed. Instead you need to convert it to a different pointer value such as struct Data*.

Try the following

struct Data* pData = (struct Data*)threadData;
JaredPar
A: 

Cast your data in the CreateThread function as an LPVOID

hThread = CreateThread(
                        NULL,       // lpThreadAttributes (default)
                        0,          // dwStackSize (default)
                        ThreadFunc, // lpStartAddress
                        (LPVOID)tData,     // lpParameter
                        0,          // dwCreationFlags
                        &threadId   // lpThreadId (returned by function)
                       )

Also in ThreadFunc Data should be a Data*

If you go to the definitaion of LPVOID you'll see it's just a pointer to a void. So since tData is already a pointer, you don't need the address of the pointer.

Robert
A: 

Replace with:

struct Data *tData = (struct Data*)threadData; 
Seva Alekseyev
+3  A: 

The LPVOID is coming in as a pointer to the struct, not the struct itself. So you'd want something like:

struct Data * ptData = (struct Data *)threadData;

And then you need to access the fields with the -> operator:

blahblah = ptData->messages;

HOWEVER. You're also missing allocated storage for the structure when you fill it in, since you're using a pointer without any backing memory. You need to malloc the structure. You should read up on memory management in C. (Among many other links, this one talks about structures)

quixoto
Thank you for the explanation
Jamie Keeling
+3  A: 

First, you only create a pointer to a struct and not a struct itself, you then use the uninitialized pointer.

struct Data *tData;

tData->threads = numThreads;
tData->messages = 3;
tData->delay = 1000;

You need to create a struct. If the function creating the thread is going to exit before the thread has finished then you need to create it dynamically. e.g.

struct Data *tData = malloc(sizeof *tData);

You should ensure that the struct is then freed at an appropriate point somewhere else, perhaps at the end of the thread function if it isn't used elsewhere.

Then you try to cast the LPVOID parameter to a struct, where as what you passed is a pointer to a pointer to your struct. The parameter is not going to be large enough to pass the entire struct by value. You probably want to pass in just a pointer to your struct (pass in tData and not &tData to CreateThread and then you can intialize from LPVOID to a pointer to your struct. (As this is C a cast isn't required and you should avoid unnecessary casts - incorrect ones can mask genuine errors.)

e.g.

struct Data *tData = threadData;
Charles Bailey
Thank you for your answer
Jamie Keeling
A: 

there are other bugs in your code ... tData seems not allocated .. also you are passing it with &address operator so you are passing the address of the pointer not the pointer. Probably as you get the pointer and use it you get an AccessViolation

luca
+5  A: 

OK, for starters, this is going to blow up:

struct Data *tData;

tData->threads = numThreads;
tData->messages = 3;
tData->delay = 1000;

...because you've created a variable of type 'pointer to a struct', but you haven't initialized the pointer to point to anything. tData is uninitialized, so you're writing to a wild pointer.

You might want something like this:

// Allocate memory for the struct on the heap
struct Data *tData = malloc( sizeof(struct Data) );

// Initialize _all_ fields of the struct (malloc won't zero fill)
tData->threads = numThreads;
tData->messages = 3;
tData->delay = 1000;

Secondly, you're passing the address of tData (the location in memory where the tData variable resides), rather than the location in memory that tData is pointing to:

// Create child thread
hThread = CreateThread( ...,
                        &tData, // OOPS - ADDRESS OF THE POINTER VARIABLE ITSELF!
                        ... );

You probably want to pass the value of the pointer (the address of the struct that it points to):

// Create child thread
hThread = CreateThread( ...,
                        tData,  // Value of the pointer
                        ... );

When you receive the address of the struct in your callback function, cast it back to the original pointer-to-struct type, dereference and enjoy:

DWORD WINAPI ThreadFunc(LPVOID threadData)
{
    struct Data *tData = (struct Data *)threadData;

    int numMessages = tData->messages;
    // ...
}
Scott Smith
Thank you for the detailed explanation, i'm from a C# background so the concept of pointers etc.. is fairly new to me.
Jamie Keeling