Is there a safe way to cast DWORD_PTR into DWORD? - c++

I implemented this function to convert from dword_ptr to dword but if there is other way with less code that will be appreciated.
this code should be compatible with 32bit and 64bit windows
I have seen this Question but the answer contains new and delete operations
inline unsigned long Convert_to_DWORD(unsigned long long data)
{
//typedef unsigned __int64 ULONG_PTR
//typedef ULONG_PTR DWORD_PTR
//typedef unsigned long DWORD;
assert(UINT_MAX > data);
unsigned long* value = reinterpret_cast<unsigned long*>(&data);
return *value;
}

Is there a safe way to cast DWORD_PTR into DWORD?
Integer types are implicitly convertible to each other. Such conversion is safe in the sense that there is no UB involved
DWORD_PTR original;
DWORD converted = original;
On a 64 bit system DWORD may be smaller than DWORD_PTR, in which case it cannot represent all the values representable by DWORD_PTR. Therefore if you do the inverse conversion, you do not get the same original value back unless the high order bytes happened to be zero. Furthermore, if DWORD_PTR represented a pointer value, then converting the value to DWORD then back to DWORD_PTR then back into a pointer then using the resulting pointer would not be safe.
Since the conversion is narrowing, a compiler might generate a warning. An explicit conversion makes your intention unambiguous and should silence such warning:
DWORD converted = DWORD(original);

Related

What is the purpose of adding a pointer in: typedef unsigned char UCHAR, *PUCHAR;

What is the purpose of adding a pointer in: typedef unsigned char UCHAR, *PUCHAR;
(there are a lot of other examples of typedefs with additional pointers) I found that next to UCHAR stands *PUCHAR pointer and checked sizeof(UCHAR) and sizeof(PUCHAR) the results was 1 byte and 8 bytes. Is this pointer size fixed or is flexible? Is that correct to make typedef with data types of different size in one line? What if the OS will extend the addressing to 128 bits (16 bytes) in the future?
What is the purpose of making so much data types when it's sure, that addressing is extending?
typedef unsigned char UCHAR, *PUCHAR;
(...)
2.2.16 HANDLE
2.2.17 HCALL
2.2.18 HRESULT
2.2.19 INT
2.2.20 INT8
2.2.21 INT16
2.2.22 INT32
2.2.23 INT64
2.2.24 LDAP_UDP_HANDLE
2.2.25 LMCSTR
2.2.26 LMSTR
2.2.27 LONG
2.2.28 LONGLONG
2.2.29 LONG_PTR
2.2.30 LONG32
2.2.31 LONG64
2.2.32 LPCSTR
2.2.33 LPCVOID
2.2.34 LPCWSTR
2.2.35 LPSTR
2.2.36 LPWSTR
2.2.37 NET_API_STATUS
2.2.38 NTSTATUS
2.2.39 PCONTEXT_HANDLE
2.2.40 QWORD
2.2.41 RPC_BINDING_HANDLE
2.2.42 SHORT
2.2.43 SIZE_T
2.2.44 STRING
2.2.45 UCHAR
2.2.46 UINT
2.2.47 UINT8
2.2.48 UINT16
2.2.49 UINT32
2.2.50 UINT64
2.2.51 ULONG
2.2.52 ULONG_PTR
2.2.53 ULONG32
2.2.54 ULONG64
2.2.55 ULONGLONG
2.2.56 UNICODE
2.2.57 UNC
2.2.58 USHORT
2.2.59 VOID
2.2.60 WCHAR
2.2.61 WORD
UCHAR stands for unsigned char which is 1 byte in size. PUCHAR stands for unsigned char*. To store a pointer you need 8 bytes (in a 64 bit application). That's why the size of UCHAR is 1 byte and size of PUCHAR is 8 bytes.
Why a pointer is 8 bytes?
That's because in a 64 bit application, the address of a single byte has 64 bits. To represent this, you need 64 bits. That's why it takes 8 bits.
Can't we just use *char instead of *PUCHAR or maybe it's some hidden meaning for making so much data types?
*PUCHAR is unsigned char**, not char*. But if I understand your question correctly and what you mean is "using unsigned char* instead of PUCHAR", yes you can. But only if you know about the underlying type.
There can be few reasons for why the developers have gone with a custom typedef like that. It could be to fit in multiple platforms, to fit different architectures, personal opinion, etc... Without knowing the real underlying type, we might actually lose information in the process of casting to another type.

Dangerous conversion from int to longlong: No warning?

UINT One = 1;
LONG mylong = LONG(-1) * One;
LONGLONG mylonglong1 = LONG(-1) * One;
LONGLONG mylonglong2 = LONG(LONG(-1) * One);
What would be the value stored in mylong, mylonglong1 and mylonglong2 ?
mylong = 0xffffffff (ok)
mylonglong1 = 0x00000000ffffffff (why?)
mylonglong2 = 0xffffffffffffffff (ok)
I was really surprised to know the result of mylonglong1, and more surprised that there is no compilation warning.
Why assigning a LONG to a LONGLONG is causing that left padding with zeros?
What really happened in a project is a problem in using the function CFile::Seek()
virtual LONG Seek( LONG lOff, UINT nFrom );
defined here: https://msdn.microsoft.com/en-us/library/aa270542(v=vs.60).aspx
migrated to
virtual ULONGLONG Seek( LONGLONG lOff, UINT nFrom);
defined here https://msdn.microsoft.com/library/b2eb5757-d499-4e67-b044-dd7d1abaa0f8.aspx#cfile__seek
After migration it no longer worked because the argument conversion of lOff was not giving the expected result.
In expression (-1) * (UINT)1, the first operand gets converted to UINT according to operand conversion rules:
Otherwise, if the unsigned operand's conversion rank is greater or equal to the conversion rank of the signed operand, the signed operand is converted to the unsigned operand's type.
Therefore, we have an unsigned multiplication with result type of UINT. The conversion from UINT to LONGLONG is straightforward, since LONGLONG can represent every number representable by UINT, in this case 0xffffffff. That's how you end up with 0x00000000ffffffff.
Now, when you do LONG(LONG(-1) * (UINT)1), the rule above still applies, though you end up converting unsigned 0xffffffff to -1, which then gets sign-expanded to LONGLONG.
P.S: I am assuming Win32 / Win64 where UINT and LONG are 32-bit and LONGLONG is 64-bit.

LOWORD() receives DWORD (unsigned long) but WPARAM is UINT_PTR (unsigned int)

As a long is wider than an int giving the low order of an unsigned long should return something bigger than just the low order of unsigned int.
Why does the function still know what the low order of the parameter is? Does it analyze the type given thus DWORD to be understood as a maximum in this case?
LOWORD() does not receive a DWORD, it casts it to a DWORD_PTR.
Both DWORD_PTR and UINT_PTR have the same size.
(32 bit or 64 bit, depending on the operating system architecture).

Cannot convert from uint32_t* to LPDWORD

I'm trying to call the Windows API function GetExitCodeProcess which takes a LPDWORD as its second parameter.
According to MSDN, LPDWORD is a pointer to an unsigned 32-bit value. So I tried to pass a uint32_t*, but the compiler (MSVC 11.0) is not happy with it:
error C2664: 'GetExitCodeProcess' : cannot convert parameter 2 from 'uint32_t *' to 'LPDWORD'
Also a static_cast does not help. Why is that? And is it safe to use a reinterpret_cast in this case?
From the documentation:
DWORD
A 32-bit unsigned integer. The range is 0 through 4294967295 decimal.
This type is declared in IntSafe.h as follows:
typedef unsigned long DWORD;
So, LPDWORD is unsigned long int*. But you are trying to pass unsigned int*. I know that the types point to variables that are the same size, but the pointer types are not compatible.
The solution is to declare a variable of type DWORD, and pass the address of that variable. Something like this:
DWORD dwExitCode;
if (!GetExitCodeProcess(hProcess, &dwExitCode))
{
// deal with error
}
uint32_t ExitCode = dwExitCode;

long to HWND (VS8 C++)

How can I cast long to HWND (C++ visual studio 8)?
Long lWindowHandler;
HWND oHwnd = (HWND)lWindowHandler;
But I got the following warning:
warning C4312: 'type cast' : conversion from 'LONG' to 'HWND' of greater size
Thanks.
As long as you're sure that the LONG you have is really an HWND, then it's as simple as:
HWND hWnd = (HWND)(LONG_PTR)lParam;
HWND is a handle to a window.
This type is declared in WinDef.h as follows:
typedef HANDLE HWND;
HANDLE is handle to an object.
This type is declared in WinNT.h as follows:
typedef PVOID HANDLE;
Finally, PVOID is a pointer to any type.
This type is declared in WinNT.h as follows:
typedef void *PVOID;
So, HWND is actually a pointer to void. You can cast a long to a HWND like this:
HWND h = (HWND)my_long_var;
but very careful of what information is stored in my_long_var. You have to make sure that you have a pointer in there.
Later edit:
The warning suggest that you've got 64-bit portability checks turned on. If you're building a 32 bit application you can ignore them.
Doing that is only safe if you are not running on a 64 bit version of windows. The LONG type is 32 bits, but the HANDLE type is probably 64 bits. You'll need to make your code 64 bit clean. In short, you will want to change the LONG to a LONG_PTR.
Rules for using pointer types:
Do not cast pointers to int, long,
ULONG, or DWORD. If you must cast a
pointer to test some bits, set or
clear bits, or otherwise manipulate
its contents, use the UINT_PTR or
INT_PTR type. These types are integral
types that scale to the size of a
pointer for both 32- and 64-bit
Windows (for example, ULONG for 32-bit
Windows and _int64 for 64-bit
Windows). For example, assume you are
porting the following code:
ImageBase = (PVOID)((ULONG)ImageBase |
1);
As a part of the porting process, you
would change the code as follows:
ImageBase =
(PVOID)((ULONG_PTR)ImageBase | 1);
Use UINT_PTR and INT_PTR where
appropriate (and if you are uncertain
whether they are required, there is no
harm in using them just in case). Do
not cast your pointers to the types
ULONG, LONG, INT, UINT, or DWORD.
Note that HANDLE is defined as a
void*, so typecasting a HANDLE value
to a ULONG value to test, set, or
clear the low-order 2 bits is an error
on 64-bit Windows.