Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typecast problem in WIndow 10 (DWORD and ULONG_PTR in WinAPI)

In Windows 7, VS2012 compiler

PostQueuedCompletionStatus(hCompletionPort, 0, (DWORD) pContext, &pOverlap->m_ol);

the above API call is working fine.

But on Windows 10 and VS2017 compiler, member variables in structure of pContext are not accessible.

When we change from DWORD to ULONG_PTR, it is working fine in Windows 10

PostQueuedCompletionStatus(hCompletionPort, 0, (ULONG_PTR) pContext, &pOverlap->m_ol);

What is reason for this behaviour?

like image 959
Nithin sri parambudur Avatar asked Dec 18 '25 08:12

Nithin sri parambudur


1 Answers

Most probably the new build differs not only in Windows version, but you are also compiling for 64 bit, while previously you were compiling for 32 bit.

Update: OP has clarified that both builds are 64 bit, but this is easily explained; see below.

That cast to DWORD is conceptually wrong even on Windows 7/32 bit. Indeed, the API definition requires a ULONG_PTR, which is an unsigned integer type big enough to hold a pointer with no loss of data. As such, it's a 32 bit type when compiling for 32 bit, 64 bit when compiling for 64 bit.

Instead, you are casting your pointer to a DWORD; DWORD is always a 32 bit unsigned type, so when building for 32 bit Windows everything goes smoothly (pointers need no more than 32 bit to arrive intact on the other side), but on 64 bit Windows you are losing the top 32 bit of your pointer.

Now, this happens to work even on 64 bit Windows 7; why?

As explained in the comments, this pointer comes from the heap, and by default on Windows 7 the heap starts providing memory from the "low" portion of the 64 bit address space; hence, unless you are consuming lots of memory, you are going to get always addresses with the top 32 bits to zero, so they are going to survive even with the top 32 bits chopped off.

This changed since Windows 8, as ASLR (enabled by default by the linker when building 64 bit executables) randomizes the position of the heap in the virtual address space, which means that you are going to get pointers with non-zero upper 32 bits, that are going to be maimed badly by the cast to DWORD.

Long story short: fix your cast1 and everything will work fine. Also, in future be always wary of casts from a pointer to an integral type that doesn't end with _ptr or _PTR - there are good chances that you are doing a mistake.


Notes

  1. Strictly speaking, IIRC the standard requires "passing through" void * for these casts to work correctly, i.e. the roundtrip through uintptr_t is guaranteed only for void * (and maybe char *? I'll have to check the standard), so it should be (ULONG_PTR)(LPVOID)pContext, if pContext isn't a void * already. However, this being Win32 I'm pretty sure it's guaranteed to work even without the extra cast.
like image 183
Matteo Italia Avatar answered Dec 20 '25 00:12

Matteo Italia



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!