How do I pinvoke to GetWindowLongPtr and SetWindowLongPtr on 32-bit platforms?
I'd recommend you deal with this the way Windows Forms does it internally:
public static IntPtr GetWindowLong(HandleRef hWnd, int nIndex)
{
if (IntPtr.Size == 4)
{
return GetWindowLong32(hWnd, nIndex);
}
return GetWindowLongPtr64(hWnd, nIndex);
}
[DllImport("user32.dll", EntryPoint="GetWindowLong", CharSet=CharSet.Auto)]
private static extern IntPtr GetWindowLong32(HandleRef hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint="GetWindowLongPtr", CharSet=CharSet.Auto)]
private static extern IntPtr GetWindowLongPtr64(HandleRef hWnd, int nIndex);
SetWindowLong/GetWindowLong and 32-bit/64-bit CPUs
I guess you are wondering if you chose the type UInt32 correctly. The answer is yes. The docs explicitly say it is always 32 bit value: http://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).aspx
Your code is correct.
P/Invoke entry points should exist with what should be correct entry points stated
(This answer also posted in an edit at the bottom of the original question, to help people find it quick and easy)
Turns out the problem is that Visual Studio itself (and therefore the Code Analysis tool) are 32bit. When the code analysis tool checks user32.dll to see if those functions are there, it checks the 32bit version of user32.dll (in C:/Windows/SysWOW64/) instead of the one that the program will actually use (the 64bit version in C:/Windows/System32), and these functions only exist in the 64bit version (32bit version uses GetWindowLong/SetWindowLong instead of GetWindowLongPtr/SetWindowLongPtr (notice the PTR part)).
I keep getting Unable to find an entry point named 'GetWindowLongPtrA' in DLL 'user32.dll'
There is no function named GetWindowLongPtr
, GetWindowLongPtrA
or GetWindowLongPtrW
in the 32-bit version of user32.dll
:
The reason that using GetWindowLongPtr
regardless of target bitness works C and C++ WinAPI code is that in 32-bit code it's a macro that calls GetWindowLong(A|W)
. It only exists in the 64-bit version of user32.dll
:
The documentation for importing GetWindowLongPtr
on pinvoke.net includes a code sample for how to make this importation transparent to target bitness (remember, the error is thrown when you actually try to call the imported function that doesn't exist, not on the DllImport
line):
[DllImport("user32.dll", EntryPoint="GetWindowLong")]
private static extern IntPtr GetWindowLongPtr32(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint="GetWindowLongPtr")]
private static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);
// This static method is required because Win32 does not support
// GetWindowLongPtr directly
public static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex)
{
if (IntPtr.Size == 8)
return GetWindowLongPtr64(hWnd, nIndex);
else
return GetWindowLongPtr32(hWnd, nIndex);
}
GetWindowLong vs GetWindowLongPtr in C#
Unfortunately it's not that easy, because GetWindowLongPtr doesn't exist in 32bit Windows. On 32bit systems GetWindowLongPtr is just a C macro that points to GetWindowLong. If you really need to use GetWindowLongPtr on both 32 and 64 bit systems you'll have to determine the correct one to call at run time. See the description at pinvoke.net
Using pinvoke for both 32 and 64 bit dlls from the same managed dll
.NET does have the equivalent of a DllMain() function, it is called a module initializer. It is however out of reach from C# and VB.NET code, you can only create one in IL or in C++/CLI. C++/CLI itself has a bitness dependency so that only leaves IL. You'll find sample code for one in this answer. Getting it linked into your assembly is pretty awkward, the build system doesn't directly support running the assembly linker.
Next best thing is a "type initializer" as mentioned in the same article, called a static constructor in C#. You do need some semblance of organization in your code to make these pay off, a class that's guaranteed to get used before one of your 'thousands of methods' get called. That ought to be difficult with that many methods.
That doesn't leave much beyond an initialization method that has to be called by the app in its Main() method. And of course the standard solution, two installers, one for 32-bit machines, another for 64-bit machines. Which also ensures your app ends up in the 'right' directory, c:\program files vs c:\program files (x86).
UPDATE: .NET 5 now supports module initializers in C# v9 with the [ModuleInitializer] attribute
Custom windows form controls that could run on both 32bit (x86) and 64bit (x64) platform
You don't have to do anything special. The underlying Windows API is the same irrespective of whether you are on 32 or 64 bit.
Data types that hold pointer sized things, e.g. window handles, float between 32 and 64 bit depending on which platform is targeted. For that reason they are declared as IntPtr
for P/Invoke. So long as you get that right, your code will work on both platforms.
Bit masking an IntPtr
Just convert IntPtr
to an int
(it has a conversion operator) and use logical bit operators to test bits.
const int WS_VISIBLE = 0x10000000;
int n = (int)myIntPtr;
if((n & WS_VISIBLE) == WS_VISIBLE)
DoSomethingWhenVisible()`
Related Topics
How to Embed an Application Manifest into an Application Using VS2008
What's the Equivalent of Vb's Asc() and Chr() Functions in C#
Check If Property Has Attribute
Method Overloading VS Optional Parameter in C# 4.0
How to Enumerate Through a Jobject
Am I Misunderstanding Linq to SQL .Asenumerable()
How to List All Variables of Class
Control Cannot Fall Through from One Case Label
Datetime.Tryparse Century Control C#
Auto Create Database in Entity Framework Core
ASP.NET Webapi2 Enable Cors Not Working with Aspnet.Webapi.Cors 5.2.3
What Is the Use of the Arraysegment<T> Class
If Int32 Is Just an Alias for Int, How Can the Int32 Class Use an Int