w4j3d w4j3d - 2 months ago 10x
C Question

What does *(DWORD_PTR*)&functionA = functionB( var1, var2); do?

*(DWORD_PTR*)&FunctionA = FunctionB( var1, var2, etc...);

What does this code mean? What does it do?

I tried to search Google, but I didn't know what terms to use.

Previously in the code there is:

BOOL (WINAPI * FunctionA) (var1, var2, etc...) = NULL;

Which identifies what "FunctionA" is.


It's ugly code, that's what it is.

What it's basically doing is calling a function (FunctionB) that returns a pointer to another function, and assigning that pointer to the variable FunctionA. Now, if that was all that was going on, then the code would look very simple:

FunctionA = FunctionB( var1, var2, etc... );

Unfortunately, for some weird reason (that presumably made a lot of sense to whoever originally designed that API), the return type of FunctionB is not declared to be a function pointer, but rather a DWORD_PTR (which, presumably, is a pointer to a DWORD, which in turn is presumably an integer type of some kind). That means that the return value of FunctionB cannot be directly assigned to the variable FunctionA, but has to be cast into an actual function pointer first (which, as has been noted in the comments, is technically undefined behavior according to the C standard, although e.g. POSIX does define semantics for it).

Now, if the type of the FunctionA variable was something simple, like, say my_func_ptr_t, then this would still be easy to write:

FunctionA = (my_func_ptr_t) FunctionB( var1, var2, etc... );

But alas, in your code FunctionA is declared as:

BOOL (WINAPI * FunctionA) (arg1, arg2, etc...) = NULL;

which means that its type is something like BOOL (WINAPI *) (arg1, arg2, etc...), and so the cast would look like this:

FunctionA = (BOOL (WINAPI *) (arg1, arg2, etc...)) FunctionB( var1, var2, etc... );

It seems that whoever wrote that code decided that such a cast was too complicated, and instead came up with a trick: instead of casting the return value of FunctionB into a function pointer, wouldn't it be so much easier to cast FunctionA into a DWORD_PTR, like this:

(DWORD_PTR) FunctionA = FunctionB( var1, var2, etc... ); /* this doesn't work :( */

But of course, that won't compile; a cast just converts the value of a variable into another type, and you can't assign something to the converted value. (In technical terms, a cast is not an lvalue.) But wait, you can assign to a dereferenced pointer! So if we...

  1. take a pointer to FunctionA (which will thus be a pointer to a function pointer),
  2. cast it into a pointer to a DWORD_PTR (i.e. a pointer to a pointer to a DWORD), and
  3. dereference that pointer,

then we get a valid DWORD_PTR lvalue that we can assign to, and which just happens to share the same memory address as the variable FunctionA. Never mind that all this invokes undefined behavior six ways to Sunday according to the C standard — we know our compiler will handle it the way we expect, right?

So, this is precisely what the code does:

  1. &FunctionA returns a pointer to the variable FunctionA,
  2. (DWORD_PTR *) casts this pointer so that it now claims to point to a variable of type DWORD_PTR, and
  3. the * on the left dereferences this pointer, so that the return value of FunctionB gets assigned to whatever memory location it points to (which, conveniently, just happens to be the location of FunctionA).

But of course, all this relies on the (non-standard) assumption that, on the particular compiler and platform this code is meant for, writing a DWORD_PTR into memory and then using that same memory location as a function pointer actually works and calls the expected function, as opposed to e.g triggering an access violation, jumping into the wrong address, or making demons fly out of your nose.

OK, so it's ugly code. How would I do it better, then?

Well, ideally, I'd change FunctionB so that it would actually return a function pointer of the appropriate type, so that none of these casting shenanigans would be needed in the first place.

If that really wasn't possible for some reason (and I'd try pretty hard to make it possible, even redesigning the API if necessary), the next best solution would be explicitly casting the return value of FunctionB. Sure, it's undefined behavior, but at least it's explicit about what it tries to do.

Either way, I would definitely typedef the actual function pointer type into something more readable. That way, the code (with an explicit cast) might end up looking something like this:

typedef BOOL (WINAPI * my_func_ptr_t) (arg1, arg2, etc...);
my_func_ptr_t FunctionA = NULL;
/* ... */
FunctionA = (my_func_ptr_t) FunctionB( var1, var2, etc... );