0
*(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.

Ilmari Karonen
  • 49,047
  • 9
  • 93
  • 153
w4j3d
  • 341
  • 2
  • 6
  • 12
  • Seems like it assigns pointer to some function to variable `functionA`. `functionB` may be [`GetWindowLong() / GetWindowLongPtr()`](https://msdn.microsoft.com/en-us/library/windows/desktop/ms633584(v=vs.85).aspx) or something similar, that has integral return type but actually may return function pointer. – Sergio Aug 19 '16 at 11:46
  • 1
    it's technically undefined behavior... so it could do well... anything – Mgetz Aug 19 '16 at 11:48

2 Answers2

3

I'm assuming it's the *(DWORD_PTR*)&functionA part that confuses you?

Lets break it down into parts. Lets begin with &functionA. It takes the address of functionA (whatever that is) and returns a pointer to it.

Then we have (DWORD_PTR*) which casts the pointer to functionA to a pointer to DWORD_PTR.

Lastly the the lone * at the beginning, which is the dereference operator, which takes a pointer and gives you the value of what if points to.

Put together with the function call, it simply assigns the result of calling functionB to the variable functionA.

I'm just guessing here, but it's possible that functionB returns a pointer to a function in some generic way (as a DWORD_PTR would be my guess) and then assign this to the variable functionA which probably have the type pointer to function. The casting and address-of and dereference operators are because the return type of functionB. I guess. Without more information or context it's impossible to say anything for certain.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 1
    I think it's worth mentioning that casting a function pointer to a non-function pointer type **IS** undefined behavior, despite POSIX requiring it (and most compilers supporting it) – Mgetz Aug 19 '16 at 11:56
  • @Mgetz Yes, it is surely UB, but it is the fact that some systems widely use such manipulations even in their API (e.g. WinAPI). – Sergio Aug 19 '16 at 12:00
  • 1
    @Serhio actually WIN32 is remarkably free of `void*` callbacks, POSIX is the only one that has them as far as I'm aware – Mgetz Aug 19 '16 at 12:05
  • "Lets break it down into parts. Lets begin with &functionA. It takes the address of functionA (whatever that is) and returns a pointer to it." isn't it (like variables) just that it returns an address? (which is what you mentioned) but not return a pointer (as that would require the asterisk)? – w4j3d Aug 19 '16 at 12:08
  • Also, "Put together with the function call, it simply assigns the result of calling functionB to functionA." What does it mean to assign a value to a function? – w4j3d Aug 19 '16 at 12:10
  • @w4j3d Say you have e.g. `int a;`. Then if you do `&a` you get a pointer to `a` and the result is of type `int*`. – Some programmer dude Aug 19 '16 at 12:10
  • @w4j3d `functionA` has to be a variable, added that. – Some programmer dude Aug 19 '16 at 12:10
  • @JoachimPileborg I think it is a function (functionA), as previously in the code there is this: BOOL (WINAPI * functionA) (var1, var2, etc...) = NULL; – w4j3d Aug 19 '16 at 12:59
  • 1
    @w4j3d No that declares `functionA` as a pointer to a function, it's a variable that can point to any function taking the specified arguments and returning `BOOL`. That declaration is also crucial information that should have been in the question body. Please [read about how to ask good questions](http://stackoverflow.com/help/how-to-ask), and learn how to create a [Minimal, Complete, and Verifiable Example](http://stackoverflow.com/help/mcve). – Some programmer dude Aug 19 '16 at 13:00
  • I've realized this only afterwards. Should I reask the question? – w4j3d Aug 19 '16 at 13:08
  • @w4j3d No. If you're not happy with my answer (that's okay) then keep on refining your question until you get an acceptable answer. Editing the question will put it back up on the front page for all to see. – Some programmer dude Aug 19 '16 at 13:11
  • OK! if it's (functionA) a pointer to a function - which is assigned the value NULL at the beginning - shouldn't it be assigned "&functionB" and not "functionB"? – w4j3d Aug 19 '16 at 13:18
  • @w4j3d No, you *call* `functionB` to get a result, and it's the result of that call that is assigned to `functionA`. – Some programmer dude Aug 19 '16 at 14:02
  • @w4j3d That's what it seems like. – Some programmer dude Aug 19 '16 at 14:05
  • (Also putting it here.) For what it's worth, the C standard does not allow you to cast a pointer to data to a pointer to function. The trick here is the workaround [suggested by POSIX for users of its equivalent to `GetProcAddress()`](http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html). I think some people mentioned the first part above, but here's the second part :/ The OP's snippet uses `DWORD_PTR` instead of `void *`; I'm not sure if that's less safe... (They are the same size.) – andlabs Aug 19 '16 at 17:03
1

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 (an integer type that matches the size of a pointer on the underlying platform). 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, 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... );
Community
  • 1
  • 1
Ilmari Karonen
  • 49,047
  • 9
  • 93
  • 153
  • 1
    (Also putting it here.) For what it's worth, the C standard does not allow you to cast a pointer to data to a pointer to function. The trick here is the workaround [suggested by POSIX for users of its equivalent to `GetProcAddress()`](http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html). I think some people mentioned the first part above, but here's the second part :/ The OP's snippet uses `DWORD_PTR` instead of `void *`; I'm not sure if that's less safe... (They are the same size.) – andlabs Aug 19 '16 at 17:02
  • 1
    @andlabs: To be pedantic, while you're right that "the C standard does not allow you to cast a pointer to data to a pointer to function", AFAICT it doesn't expressly forbid it, either. It's simply undefined behavior, meaning that compilers are free to do anything if you try it, from issuing a warning to releasing nasal demons to just making it work as expected. Also, the "mandatory warning" note seems to have been a mistake in older editions of POSIX; [it has been removed from the 2013 edition](http://pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym.html), along with the workaround. – Ilmari Karonen Aug 19 '16 at 17:38
  • 1
    `DWORD_PTR` is NOT a **pointer to a `DWORD`** (that would be `LPDWORD` instead). A `DWORD_PTR` is a **pointer-sized DWORD** instead. It is a 32bit number on 32bit systems, and a 64bit number on 64bit systems. – Remy Lebeau Aug 19 '16 at 22:52