64-bit Interop
-
I was running one of my assemblies through FxCop and got this message: "As it is declared in your code, parameter 'handle' of PInvoke NativeMethods.waveOutGetVolume(Int32, Int32&):Int32 will be 4 bytes wide on 64-bit platforms. This is not correct, as the actual native declaration of this API indicates it should be 8 bytes wide on 64-bit platforms. Consult the MSDN Platform SDK documentation for help determining what data type should be used instead of 'System.Int32'." I've been looking at MSDN and haven't found anything yet specific to this problem. Still looking. But I thought I'd go ahead and ask here. How do you declare your interop methods so that they work correctly on both 32-bit and 64-bit systems?
-
I was running one of my assemblies through FxCop and got this message: "As it is declared in your code, parameter 'handle' of PInvoke NativeMethods.waveOutGetVolume(Int32, Int32&):Int32 will be 4 bytes wide on 64-bit platforms. This is not correct, as the actual native declaration of this API indicates it should be 8 bytes wide on 64-bit platforms. Consult the MSDN Platform SDK documentation for help determining what data type should be used instead of 'System.Int32'." I've been looking at MSDN and haven't found anything yet specific to this problem. Still looking. But I thought I'd go ahead and ask here. How do you declare your interop methods so that they work correctly on both 32-bit and 64-bit systems?
-
Abisodun wrote:
Use IntPtr in place of Int32.
But does this mean having to allocate unmanaged memory for the pointer? If I do this:
IntPtr handle = new IntPtr();
int result = waveOutOpen(handle, deviceID, ref format, 0, 0, CALLBACK_NULL);
waveOutOpen
returns an error. If I have to allocate unmanaged memory for the pointer (ugh), how do I tell how much to allocate? On a 32-bit system it will be 4 bytes. On a 64-bit system it will be 8 bytes. [EDIT] Nevermind. I discovered that I need to declare my interop method like this:[DllImport("winmm.dll")]
public static extern int waveOutOpen(out IntPtr handle, int deviceID, ref WaveFormat format,
int dummy, int instance, int flags);The key is to use the
out
modifier on theIntPtr
parameter. Thanks for your help. :) [/EDIT] -- modified at 13:34 Friday 17th August, 2007 -
Abisodun wrote:
Use IntPtr in place of Int32.
But does this mean having to allocate unmanaged memory for the pointer? If I do this:
IntPtr handle = new IntPtr();
int result = waveOutOpen(handle, deviceID, ref format, 0, 0, CALLBACK_NULL);
waveOutOpen
returns an error. If I have to allocate unmanaged memory for the pointer (ugh), how do I tell how much to allocate? On a 32-bit system it will be 4 bytes. On a 64-bit system it will be 8 bytes. [EDIT] Nevermind. I discovered that I need to declare my interop method like this:[DllImport("winmm.dll")]
public static extern int waveOutOpen(out IntPtr handle, int deviceID, ref WaveFormat format,
int dummy, int instance, int flags);The key is to use the
out
modifier on theIntPtr
parameter. Thanks for your help. :) [/EDIT] -- modified at 13:34 Friday 17th August, 2007The size of IntPtr changes depending on the proc. So, an IntPtr on a 32-bit system is 32-bits wide and on a 64-bit system is 64-bits wide, automatically. You don't have to allocate anything. You're just using the pointer as a self-sizing integer.
IntPtr handle = IntPtr.Zero;
A guide to posting questions on CodeProject[^]
Dave Kreskowiak Microsoft MVP Visual Developer - Visual Basic
2006, 2007 -
Abisodun wrote:
Use IntPtr in place of Int32.
But does this mean having to allocate unmanaged memory for the pointer? If I do this:
IntPtr handle = new IntPtr();
int result = waveOutOpen(handle, deviceID, ref format, 0, 0, CALLBACK_NULL);
waveOutOpen
returns an error. If I have to allocate unmanaged memory for the pointer (ugh), how do I tell how much to allocate? On a 32-bit system it will be 4 bytes. On a 64-bit system it will be 8 bytes. [EDIT] Nevermind. I discovered that I need to declare my interop method like this:[DllImport("winmm.dll")]
public static extern int waveOutOpen(out IntPtr handle, int deviceID, ref WaveFormat format,
int dummy, int instance, int flags);The key is to use the
out
modifier on theIntPtr
parameter. Thanks for your help. :) [/EDIT] -- modified at 13:34 Friday 17th August, 2007Hi Leslie, here is my theory (still theory since I have not run any of this on Win64): - for native pointers you really should use IntPtr, and it is not hard to do so. - Win32 uses 4B pointers, Win64 needs 8B pointers - you must build for either 32-bit or 64-bit - on Win64 everything must be consistent (all modules 64-bit is fine, and all modules 32-bit runs in some compatibility mode, not sure tho) - IntPtr is a value type that holds either 4B or 8B pointer value - IntPtr must be initialized, normally you give its value at construction - a lot of P/Invoke examples are wrong, and use int/uint where it should have been IntPtr. Most notably SendMessage: lParam, wParam and return value should be regarded as IntPtr, and cast to int or whatever if and when appropriate. - correct prototypes would have IntPtr for anything that is or could be a handle (such as lParam). Your problem seems to be you did not initialize handle. :)
Luc Pattyn [Forum Guidelines] [My Articles]
this weeks tips: - make Visual display line numbers: Tools/Options/TextEditor/... - show exceptions with ToString() to see all information - before you ask a question here, search CodeProject, then Google
-
The size of IntPtr changes depending on the proc. So, an IntPtr on a 32-bit system is 32-bits wide and on a 64-bit system is 64-bits wide, automatically. You don't have to allocate anything. You're just using the pointer as a self-sizing integer.
IntPtr handle = IntPtr.Zero;
A guide to posting questions on CodeProject[^]
Dave Kreskowiak Microsoft MVP Visual Developer - Visual Basic
2006, 2007Dave Kreskowiak wrote:
The size of IntPtr changes depending on the proc. So, an IntPtr on a 32-bit system is 32-bits wide and on a 64-bit system is 64-bits wide, automatically. You don't have al allocate anything. You're just using the pointer as a self-sizing integer.
Understood. I think I was running into problems when I tried switching to
IntPtr
because I was using theref
modifier on theIntPtr
parameter instead ofout
. Thanks for your help. -
Hi Leslie, here is my theory (still theory since I have not run any of this on Win64): - for native pointers you really should use IntPtr, and it is not hard to do so. - Win32 uses 4B pointers, Win64 needs 8B pointers - you must build for either 32-bit or 64-bit - on Win64 everything must be consistent (all modules 64-bit is fine, and all modules 32-bit runs in some compatibility mode, not sure tho) - IntPtr is a value type that holds either 4B or 8B pointer value - IntPtr must be initialized, normally you give its value at construction - a lot of P/Invoke examples are wrong, and use int/uint where it should have been IntPtr. Most notably SendMessage: lParam, wParam and return value should be regarded as IntPtr, and cast to int or whatever if and when appropriate. - correct prototypes would have IntPtr for anything that is or could be a handle (such as lParam). Your problem seems to be you did not initialize handle. :)
Luc Pattyn [Forum Guidelines] [My Articles]
this weeks tips: - make Visual display line numbers: Tools/Options/TextEditor/... - show exceptions with ToString() to see all information - before you ask a question here, search CodeProject, then Google
Luc Pattyn wrote:
here is my theory (still theory since I have not run any of this on Win64): - for native pointers you really should use IntPtr, and it is not hard to do so. - Win32 uses 4B pointers, Win64 needs 8B pointers - you must build for either 32-bit or 64-bit - on Win64 everything must be consistent (all modules 64-bit is fine, and all modules 32-bit runs in some compatibility mode, not sure tho) - IntPtr is a value type that holds either 4B or 8B pointer value - IntPtr must be initialized, normally you give its value at construction - a lot of P/Invoke examples are wrong, and use int/uint where it should have been IntPtr. Most notably SendMessage: lParam, wParam and return value should be regarded as IntPtr, and cast to int or whatever if and when appropriate. - correct prototypes would have IntPtr for anything that is or could be a handle (such as lParam).
Thanks for this explanation. I've got a lot of code revision to do. I have older "toolkits" whose P/Invoke methods are apparently wrong, or will at least break on a 64-bit system.
Luc Pattyn wrote:
Your problem seems to be you did not initialize handle.
Actually, I don't think I'm suppose to initialize the handle, rather I should use the
out
modifier on theIntPtr
parameters rather thanref
, as I had initially tried. -
Luc Pattyn wrote:
here is my theory (still theory since I have not run any of this on Win64): - for native pointers you really should use IntPtr, and it is not hard to do so. - Win32 uses 4B pointers, Win64 needs 8B pointers - you must build for either 32-bit or 64-bit - on Win64 everything must be consistent (all modules 64-bit is fine, and all modules 32-bit runs in some compatibility mode, not sure tho) - IntPtr is a value type that holds either 4B or 8B pointer value - IntPtr must be initialized, normally you give its value at construction - a lot of P/Invoke examples are wrong, and use int/uint where it should have been IntPtr. Most notably SendMessage: lParam, wParam and return value should be regarded as IntPtr, and cast to int or whatever if and when appropriate. - correct prototypes would have IntPtr for anything that is or could be a handle (such as lParam).
Thanks for this explanation. I've got a lot of code revision to do. I have older "toolkits" whose P/Invoke methods are apparently wrong, or will at least break on a 64-bit system.
Luc Pattyn wrote:
Your problem seems to be you did not initialize handle.
Actually, I don't think I'm suppose to initialize the handle, rather I should use the
out
modifier on theIntPtr
parameters rather thanref
, as I had initially tried.Leslie Sanford wrote:
Actually, I don't think I'm suppose to initialize the handle, rather I should use the out modifier on the IntPtr parameters rather than ref, as I had initially tried
That depends entirely on the function at hand; you typically need to provide an IntPtr or be ready to receive one (with "out" keyword). I don't recall any Win32 function that provides a handle that way, they typically use the return value (and return zero for error, then you would call GetLastError for details). Greetings
Luc Pattyn [Forum Guidelines] [My Articles]
this weeks tips: - make Visual display line numbers: Tools/Options/TextEditor/... - show exceptions with ToString() to see all information - before you ask a question here, search CodeProject, then Google
-
Leslie Sanford wrote:
Actually, I don't think I'm suppose to initialize the handle, rather I should use the out modifier on the IntPtr parameters rather than ref, as I had initially tried
That depends entirely on the function at hand; you typically need to provide an IntPtr or be ready to receive one (with "out" keyword). I don't recall any Win32 function that provides a handle that way, they typically use the return value (and return zero for error, then you would call GetLastError for details). Greetings
Luc Pattyn [Forum Guidelines] [My Articles]
this weeks tips: - make Visual display line numbers: Tools/Options/TextEditor/... - show exceptions with ToString() to see all information - before you ask a question here, search CodeProject, then Google
Luc Pattyn wrote:
That depends entirely on the function at hand; you typically need to provide an IntPtr or be ready to receive one (with "out" keyword). I don't recall any Win32 function that provides a handle that way, they typically use the return value (and return zero for error, then you would call GetLastError for details).
Oh, I wasn't clear. The example I gave in my earlier post was with
waveOutOpen
; it opens a waveform output device. The native version takes an pointer to a WAVEOUT handle and returns a MMRESULT value indicating the status of the operation. If the operation was successful, the handle is filled with a valid handle to the device. This handle is later used with other waveOutXXX functions to interact with the device. So in this specific case, I needed an unitializedIntPtr
to pass as the handle modified by theout
keyword. After the wave device has been opened, other waveOutXXX functions that are called expect the handle to be initialized. Does this make sense? -
Luc Pattyn wrote:
That depends entirely on the function at hand; you typically need to provide an IntPtr or be ready to receive one (with "out" keyword). I don't recall any Win32 function that provides a handle that way, they typically use the return value (and return zero for error, then you would call GetLastError for details).
Oh, I wasn't clear. The example I gave in my earlier post was with
waveOutOpen
; it opens a waveform output device. The native version takes an pointer to a WAVEOUT handle and returns a MMRESULT value indicating the status of the operation. If the operation was successful, the handle is filled with a valid handle to the device. This handle is later used with other waveOutXXX functions to interact with the device. So in this specific case, I needed an unitializedIntPtr
to pass as the handle modified by theout
keyword. After the wave device has been opened, other waveOutXXX functions that are called expect the handle to be initialized. Does this make sense?Leslie Sanford wrote:
Does this make sense?
Of course, it does. I was not familiar with waveOutOpen, but I looked it up now, and you are right, it does output a handle, hence "out someIntPtr" is the right way to go. The only comment I could make is you don't need the
"=new IntPtr()"
part of your handle declaration, a simple"IntPtr handle;"
should suffice. :)Luc Pattyn [Forum Guidelines] [My Articles]
this weeks tips: - make Visual display line numbers: Tools/Options/TextEditor/... - show exceptions with ToString() to see all information - before you ask a question here, search CodeProject, then Google
-
Leslie Sanford wrote:
Does this make sense?
Of course, it does. I was not familiar with waveOutOpen, but I looked it up now, and you are right, it does output a handle, hence "out someIntPtr" is the right way to go. The only comment I could make is you don't need the
"=new IntPtr()"
part of your handle declaration, a simple"IntPtr handle;"
should suffice. :)Luc Pattyn [Forum Guidelines] [My Articles]
this weeks tips: - make Visual display line numbers: Tools/Options/TextEditor/... - show exceptions with ToString() to see all information - before you ask a question here, search CodeProject, then Google
Thanks for your responses. I have to say that this has been a humbling day. Code that I had written a couple of years ago that I thought was fine has turned out not to be correct. I've had to go back through my code and correct this problem.
-
Thanks for your responses. I have to say that this has been a humbling day. Code that I had written a couple of years ago that I thought was fine has turned out not to be correct. I've had to go back through my code and correct this problem.
First of all, you're welcome. Second, that is a common situation when the playing field changes. Things are working fine, they look good, compile without hassles, run great, so you think they might be correct; and then something changes, it may still work fine, but we got new information or new insights, and we feel we have to adapt the code, so we learn new ways to be even "more correct" about it. I have lots of experience in writing C code for different embedded systems, and I can tell you, if you use five different compilers, it is just impossible to write one page of code that all compilers will digest without giving a single warning. Things have improved, newer languages are more strict; having a single tool vendor (Microsoft) also helps. But then documentation, whatever they try, is not good enough. It is great at describing API functions, and less so for general principles, do's and don'ts, etc. And to make matters worse, you need more than is officially available (examples, suggestions, bug work-arounds, ...) but lots of things you find on Internet (including on CP) seem OK but are plain wrong, often the authors are satisfied they got something that works, but did not bother (enough) about correctness and general fitness. I did investigate the IntPtr stuff when I developed TrayIconBuster (see one of my CP articles); I was hoping it would run on XP, Vista and Win64 and had read some messages signaling pitfalls in Win64. Having only XP-32 available, I had to research a lot before I came up with "my theory", the one I presented earlier. And I must admit, I did not get most of it from Microsoft documentation, and I don't really have proof, having only XP to test on. In earlier code I did not even use IntPtr, I used uint instead (the IntPtr stuff did not seem to have any added value, just some coding overhead), but I too learned it the hard way, so a couple weeks ago I revisited all my P/Invoke stuff and fixed the pointers. Having my own classes (LP_user32, LP_kernel32, ...) holding the prototypes helped a lot in finding them all; so it was a matter of adapting the prototypes, then let the compiler discover the remaining problem spots. One thing I did not understand from day one I started with .NET is why the heck doesn't Microsoft do what www.pinvoke.net is trying to do, i.e. provide complete and accurate prototypes for ALL Win32 functionality; they did provide all the header files in older Visual C versions, and they must have the files internally. Having them f