Getting unexpected CA1901 warning for ChildWindowFromPointEx function - c#

When I use the code analysis feature in my Visual Studio project, I'm getting a unexpected CA1901 warning for ChildWindowFromPointEx and also for RealChildWindowFromPoint, at their parameters of type POINT.
I just defined a structure with name NativePoint with X and Y fields as Int32 type, which I'm using as an equivalent for those POINT parameters.
I understand what the portability means in terms of value size when running under x86 or x64, however, in this case I'm not sure how to solve this warning, because I'm using the same NativePoint structure for POINT parameters of other functions like for example: ChildWindowFromPoint, ClientToScreen, GetCursorPos and many others, and code analysis is not warning about them, and they work when running on a x86 and x64 process.
In fact, ChildWindowFromPoint and ChildWindowFromPointEx only seems to differ by one additional parameter, both takes the same POINT structure as parameter so I don't understand why ChildWindowFromPointEx is warning about portability issues while for ChildWindowFromPoint is all fine.
My question is: in C# or VB.NET, how do I properly fix (not suppress) this warning for ChildWindowFromPointEx and RealChildWindowFromPoint functions?. Maybe I need to define another different NativePoint struct with different field types only to use with these two functions?. But why these two functions throws a warning while for ChildWindowFromPoint does not if both takes the same POINT (my NativePoint) structure?.
Note that I get the same warning for those two functions if I use System.Drawing.Point structure, but only for those two.
[DllImport("user32.dll", EntryPoint="ChildWindowFromPointEx", SetLastError=false)]
public extern static IntPtr ChildWindowFromPointEx(IntPtr hwndParent,
NativePoint point,
uint flags);
[DllImport("user32.dll", EntryPoint="RealChildWindowFromPoint", SetLastError=false)]
public extern static IntPtr RealChildWindowFromPoint(IntPtr hwndParent,
NativePoint point);
[StructLayout(LayoutKind.Sequential)]
public struct NativePoint {
public int X;
public int Y;
}

After poking around in the responsible analysis assembly, there is a mismatch between the Windows API documentation and the data set used by the analysis plugin.
The rule thinks RealChildWindowFromPoint should have have 3 parameters of each 4 bytes, and ChildWindowFromPointEx 4 parameters of each 4 bytes on x86. ChildWindowFromPoint on the other hand is listed as having one 4 byte parameter and one 8 byte parameter as expected.
Indeed, declaring RealChildWindowFromPoint this way seems to satisfy the code analysis, but I don't know enough about the Windows API calling conventions to say whether this is actually a valid way of calling that method:
[DllImport("user32.dll", EntryPoint = "RealChildWindowFromPoint", SetLastError = false)]
public static extern IntPtr RealChildWindowFromPoint(IntPtr hwndParent, int x, int y);
Considering that rules for RealChildWindowFromPoint and ChildWindowFromPointEx are missing data for the return value size as well as x64 declarations, while the data for ChildWindowFromPoint is complete, I would assume that this is simply a mistake in the data rather than an intentional recommendation.

Related

Legality of changing P/Invoke signature for convenience

RegSetValueEx has the following P/Invoke signature:
[DllImport("advapi32.dll", SetLastError = true)]
static extern uint RegSetValueEx(
UIntPtr hKey,
[MarshalAs(UnmanagedType.LPStr)]
string lpValueName,
int Reserved,
RegistryValueKind dwType,
IntPtr lpData,
int cbData);
The IntPtr lpData parameter can be a pain to work with. To pass a string value, I need to call one of the Marshall.StringToHGlobal functions and then release it when I'm done. My code can be simpler if I change the type of the lpData parameter to [MarshalAs(UnmanagedType.LPStr)]String lpData. This seems to work and I can imagine that behind the scenes the marshalling code is doing exactly what I would do to get an IntPtr. I haven't been able to find any definitive statements if this is legal or not. Can anyone provide one?
[Yes, I know there is managed code to interface with the registry. In my particular case I can't use it, and even if I could I would still be interested in the answer in general]
Yes, this is a perfectly normal and acceptable technique in pinvoke. Just about any winapi function argument of type BYTE*, PVOID, LPARAM lends itself to a customized [DllImport] declaration. A string, array or struct is the usual equivalent C# parameter type. Particularly useful when you declare SendMessage().
Not where it ends, you can also readily take advantage of the C# language allowing method overloads. In other words, you can write any number of RegSetValueEx() declarations with different types for the lpData argument. They all map to the same native function so nothing else you have to do. A practical example is here.
You just have to make sure that the value you pass after marshalling has the expected byte values. Beware that mistakes tend to be hard to debug, you might still want to marshal manually if the call doesn't pan out. One quirk is certainly notable for this question, you always want to use CharSet = CharSet.Unicode in the declaration, calling the ANSI version (RegSetValueExA) is not useful. Now you can also add ExactSpelling = true.

Is it safe to have an external function from unmanaged code that returns an int

[DllImport(MyUnmanagedLibrary, CallingConvention = CallingConvention.Cdecl)]
internal static extern int MyFunction();
Let's say this is a function that I import from an unmanaged C++ library. In this instance the external function is declared as returning an int. The size of int does not change in managed C# code, but it does change depending on the architecture in unmanaged C++ code. Is it safe to assume that it returns an int? Should I use IntPtr instead, as the return value? Or something else?
int in C# is and will always be System.Int32, the point is that every C++ compiler is (in theory) free to do whatever it wants with types sizes (within certain limits), so, if you want to avoid a mismatch, you should change the C++ side of code, making it return a fixed-size integer (e.g. int32_t). IntPtr cannot help, since int is not defined to have the size of a pointer (and actually, on x86_64 Windows int is 32 bit, while int * is 64 bit).
On the other hand, it's extremely unlikely that any compiler on x86 Windows will ever define int as anything different than a 32 bit 2's-complement integer, for obvious binary compatibility reasons.
You need to set the compiler option with "-m32" or "-m64" for g++ for example. More specific choose the best match based on your "Machine Dependent Options"

.NET Framework: Argument type 'uint' is not CLS-compliant on P/Invoke

It is safe to ignore this warning?
Argument type 'uint' is not CLS-compliant
I'm developing a Compact Framework applicatin and I have a lot of P/Invoke and I have to use uint, ushort, etc. as parameter's types.
I've used [CLSCompliantAttribute(false)] to avoid the warning messages but I don't know it is safe.
Some help would be apreciated.
Thanks.
If you are not writing a component intended to be used by others in a different language, then you do not need to be CLS-compliant. Just declare that you are not (as you've already done).
However, it is possible to use uint internally and still provide a CLS compliant interface. Writing CLS-Compliant Code.
(link updated)
You can safely substitute a CLS compliant signed type for its same sized non-CLS compliant unsigned type.
The following are effectively the same (the first User32 function I found on pinvoke.net):
[DllImport("user32.dll")]
static extern int ActivateKeyboardLayout(IntPtr nkl, uint Flags);
[DllImport("user32.dll")]
static extern int ActivateKeyboardLayout(IntPtr nkl, int Flags);
Signed and unsigned bit fields behave identically. The only place you might have an issue is if you need to pass or receive truely huge numbers, which will map to negative numbers (look up 2's complement).

memory leak when calling unmanaged code from managed code in Windows 7

When I call an unmanaged C++ code from my C# code, I seem to have some kind of a memory leak.
The C++ reads data from a file using ifstream.read, and writes it to a Vector.
This happens only after upgrading to Windows 7, doesn't happen on Vista, but if I use a version of the native dll that was compiled on Vista, it doesn't change anything!
If I run the same C++ code directly, without the managed interope, there is no memory leak!
If I run the managed process, but within the vshost process, there is no memory leak!
Here's the call signature:
[DllImport(DllPath, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool MyMethod(
int x,
string y,
string z,
bool v,
bool w);
and the native one:
MyDll_Export bool APIENTRY MyMethod(
int x,
const wchar_t* y,
const wchar_t* z,
bool v,
bool w)
When I call it from C++, I call it like this:
MyMethod(1, L"My String 1", L"My String 2", true, true)
When I look at the performance counters for managed and unmanaged memory, I see that all of the memory comes from the unmanaged code.
Considering that the marshaling is pretty simple, I don't understand why there is a difference between calling the C++ directly or through C#.
I also don't know why would this happen only on Windows 7 (both Windows installations had .net 3.5 SP1).
Does anyone have an idea what's the reason for this?
Also if anyone knows of a native memory profiling tool that works on Window 7, I'd be glad to know (for now I've just printed to console all explicit memory allocation and there are no differences).
I'm sure the problem is related to marshaling the C# data types to their C++ counter parts. Since you are marshaling the return value bool to a signed 1 byte value, maybe you should do the same to the function arguments? The C# bool type is 4 bytes, maybe you are leaking there?
Also, specifying the unmanaged type for the strings may help.
[DllImport(DllPath, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool MyMethod(
int x,
[MarshalAs(UnmanagedType.LPWStr)]
[In] string y,
[MarshalAs(UnmanagedType.LPWStr)]
[In] string z,
[MarshalAs(UnmanagedType.I1)]
bool v,
[MarshalAs(UnmanagedType.I1)]
bool w);
An explanation for the commentor:
For the C++ bool type:
In general, a zero or null-pointer
value is converted to false, any other
value is converted to true.
...
The 1998 C++ Standard Library defines
a specialization of the vector
template for bool. The description of
the class indicates that the
implementation should pack the
elements so that every bool only uses
one bit of memory.
So, pretty much whatever value you use, you'll get a c++ boolean with the value true or false.
Unfortunately once you involve strings, no marshalling is simple.
We're going to need some more data in order to help you track down this problem. Can you provide the following
Native Method Signature
How is the memory for the strings managed in native code?
Perhaps the C++ sample where you use the API?
EDIT
Try the following signature. This tells the CLR not to marshal memory in both directions but instead only pass the data in.
[DllImport(DllPath, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool MyMethod(
int x,
[In] string y,
[In] string z,
bool v,
bool w);
I found the use of the CLR Profiler helpful when finding my memory leak.
Are you sure that there is a memory leak?
What is your basis for determining the memory leak. You say that you can see it from performance counters, but what do you actually observe? Do you see a coninously rising curve, or one that settles on a high level? A high memory consumption is often confused for a memory leak.
btw. Can you post you C++ function definition as well?

.NET Interop IntPtr vs. ref

Probably a noob question but interop isn't one of my strong points yet.
Aside from limiting the number of overloads is there any reason I should declare my DllImports like:
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);
And use them like this:
IntPtr lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(formatrange));
Marshal.StructureToPtr(formatrange, lParam, false);
int returnValue = User32.SendMessage(_RichTextBox.Handle, ApiConstants.EM_FORMATRANGE, wParam, lParam);
Marshal.FreeCoTaskMem(lParam);
Rather than creating a targeted overload:
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, ref FORMATRANGE lParam);
And using it like:
FORMATRANGE lParam = new FORMATRANGE();
int returnValue = User32.SendMessage(_RichTextBox.Handle, ApiConstants.EM_FORMATRANGE, wParam, ref lParam);
The by ref overload ends up being easier to use but I'm wondering if there is a drawback that I'm not aware of.
Edit:
Lots of great info so far guys.
#P Daddy: Do you have an example of basing the struct class off an abstract (or any) class? I changed my signature to:
[DllImport("user32.dll", SetLastError = true)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, [In, Out, MarshalAs(UnmanagedType.LPStruct)] CHARFORMAT2 lParam);
Without the In, Out, and MarshalAs the SendMessage (EM_GETCHARFORMAT in my test) fail. The above example works well but if I change it to:
[DllImport("user32.dll", SetLastError = true)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, [In, Out, MarshalAs(UnmanagedType.LPStruct)] NativeStruct lParam);
I get a System.TypeLoadException that says the CHARFORMAT2 format is not valid (I'll try and capture it for here).
The exception:
Could not load type 'CC.Utilities.WindowsApi.CHARFORMAT2' from assembly 'CC.Utilities, Version=1.0.9.1212, Culture=neutral, PublicKeyToken=111aac7a42f7965e' because the format is invalid.
The NativeStruct class:
public class NativeStruct
{
}
I've tried abstract, adding the StructLayout attribute, etc. and I get the same exception.
[StructLayout(LayoutKind.Sequential)]
public class CHARFORMAT2: NativeStruct
{
...
}
Edit:
I didn't follow the FAQ and I asked a question that can be discussed but not positively answered. Aside from that there has been lot's of insightful information in this thread. So I'll leave it up to the readers to vote up an answer. First one to over 10 up-votes will be the answer. If no answer meets this in two days (12/17 PST) I'll add my own answer that summarizes all the yummy knowledge in the thread :-)
Edit Again:
I lied, accepting P Daddy's answer because he is the man and has been a great help (he has a cute little monkey too :-P)
If the struct is marshalable without custom processing, I greatly prefer the latter approach, where you declare the p/invoke function as taking a ref (pointer to) your type. Alternatively, you can declare your types as classes instead of structs, and then you can pass null, as well.
[StructLayout(LayoutKind.Sequential)]
struct NativeType{
...
}
[DllImport("...")]
static extern bool NativeFunction(ref NativeType foo);
// can't pass null to NativeFunction
// unless you also include an overload that takes IntPtr
[DllImport("...")]
static extern bool NativeFunction(IntPtr foo);
// but declaring NativeType as a class works, too
[StructLayout(LayoutKind.Sequential)]
class NativeType2{
...
}
[DllImport("...")]
static extern bool NativeFunction(NativeType2 foo);
// and now you can pass null
<pedantry>
By the way, in your example passing a pointer as an IntPtr, you've used the wrong Alloc. SendMessage is not a COM function, so you shouldn't be using the COM allocator. Use Marshal.AllocHGlobal and Marshal.FreeHGlobal. They're poorly named; the names only make sense if you've done Windows API programming, and maybe not even then. AllocHGlobal calls GlobalAlloc in kernel32.dll, which returns an HGLOBAL. This used to be different from an HLOCAL, returned by LocalAlloc back in the 16-bit days, but in 32-bit Windows they are the same.
The use of the term HGLOBAL to refer to a block of (native) user-space memory just kind of stuck, I guess, and the people designing the Marshal class must not have taken the time to think about how unintuitive that would be for most .NET developers. On the other hand, most .NET developers don't need to allocate unmanaged memory, so....
</pedantry>
Edit
You mention you're getting a TypeLoadException when using a class instead of a struct, and ask for a sample. I did up a quick test using CHARFORMAT2, since it looks like that's what you're trying to use.
First the ABC1:
[StructLayout(LayoutKind.Sequential)]
abstract class NativeStruct{} // simple enough
The StructLayout attribute is required, or you will get a TypeLoadException.
Now the CHARFORMAT2 class:
[StructLayout(LayoutKind.Sequential, Pack=4, CharSet=CharSet.Auto)]
class CHARFORMAT2 : NativeStruct{
public DWORD cbSize = (DWORD)Marshal.SizeOf(typeof(CHARFORMAT2));
public CFM dwMask;
public CFE dwEffects;
public int yHeight;
public int yOffset;
public COLORREF crTextColor;
public byte bCharSet;
public byte bPitchAndFamily;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
public string szFaceName;
public WORD wWeight;
public short sSpacing;
public COLORREF crBackColor;
public LCID lcid;
public DWORD dwReserved;
public short sStyle;
public WORD wKerning;
public byte bUnderlineType;
public byte bAnimation;
public byte bRevAuthor;
public byte bReserved1;
}
I've used using statements to alias System.UInt32 as DWORD, LCID, and COLORREF, and alias System.UInt16 as WORD. I try to keep my P/Invoke definitions as true to SDK spec as I can. CFM and CFE are enums that contain the flag values for these fields. I've left their definitions out for brevity, but can add them in if needed.
I've declared SendMessage as:
[DllImport("user32.dll", CharSet=CharSet.Auto)]
static extern IntPtr SendMessage(
HWND hWnd, MSG msg, WPARAM wParam, [In, Out] NativeStruct lParam);
HWND is an alias for System.IntPtr, MSG is System.UInt32, and WPARAM is System.UIntPtr.
[In, Out] attribute on lParam is required for this to work, otherwise, it doesn't seem to get marshaled both directions (before and after call to native code).
I call it with:
CHARFORMAT2 cf = new CHARFORMAT2();
SendMessage(rtfControl.Handle, (MSG)EM.GETCHARFORMAT, (WPARAM)SCF.DEFAULT, cf);
EM and SCF are enums I've, again, left out for (relative) brevity.
I check success with:
Console.WriteLine(cf.szFaceName);
and I get:
Microsoft Sans Serif
Works like a charm!
Um, or not, depending on how much sleep you've had and how many things you're trying to do at once, I suppose.
This would work if CHARFORMAT2 were a blittable type. (A blittable type is a type that has the same representation in managed memory as in unmanaged memory.) For instance, the MINMAXINFO type does work as described.
[StructLayout(LayoutKind.Sequential)]
class MINMAXINFO : NativeStruct{
public Point ptReserved;
public Point ptMaxSize;
public Point ptMaxPosition;
public Point ptMinTrackSize;
public Point ptMaxTrackSize;
}
This is because blittable types are not really marshaled. They're just pinned in memory—this keeps the GC from moving them—and the address of their location in managed memory is passed to the native function.
Non-blittable types have to be marshaled. The CLR allocates unmanaged memory and copies the data between the managed object and its unmanaged representation, making the necessary conversions between formats as it goes.
The CHARFORMAT2 structure is non-blittable because of the string member. The CLR can't just pass a pointer to a .NET string object where a fixed-length character array is expected to be. So the CHARFORMAT2 structure must be marshaled.
As it would appear, for correct marshaling to occur, the interop function must be declared with the type to be marshaled. In other words, given the above definition, the CLR must be making some sort of determination based on the static type of NativeStruct. I would guess that it's correctly detecting that the object needs to be marshaled, but then only "marshaling" a zero-byte object, the size of NativeStruct itself.
So in order to get your code working for CHARFORMAT2 (and any other non-blittable types you might use), you'll have to go back to declaring SendMessage as taking a CHARFORMAT2 object. Sorry I led you astray on this one.
Captcha for the previous edit:
the whippet
Yeah, whip it good!
Cory,
This is off topic, but I notice a potential problem for you in the app it looks like you're making.
The rich textbox control uses standard GDI text-measuring and text-drawing functions. Why is this a problem? Because, despite claims that a TrueType font looks the same on screen as on paper, GDI does not accurately place characters. The problem is rounding.
GDI uses all-integer routines to measure text and place characters. The width of each character (and height of each line, for that matter) is rounded to the nearest whole number of pixels, with no error correction.
The error can easily be seen in your test app. Set the font to Courier New at 12 points. This fixed-width font should space characters exactly 10 per inch, or 0.1 inches per character. This should mean that, given your starting line width of 5.5 inches, you should be able to fit 55 characters on the first line before wrap occurs.
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123
But if you try, you'll see that wrap occurs after only 54 characters. What's more the 54th character and part of the 53rd overhang the apparent margin shown on the ruler bar.
This assumes you have your settings at standard 96 DPI (normal fonts). If you use 120 DPI (large fonts), you won't see this problem, although it appears that you size your control incorrectly in this case. You also won't likely see this on the printed page.
What's going on here? The problem is that 0.1 inches (the width of one character) is 9.6 pixels (again, using 96 DPI). GDI doesn't space characters using floating point numbers, so it rounds this up to 10 pixels. So 55 characters takes up 55 * 10 = 550 pixels / 96 DPI = 5.7291666... inches, whereas what we were expecting was 5.5 inches.
While this will probably be less noticeable in the normal use case for a word processor program, there is a likelihood of instances where word wrap occurs at different places on screen versus on page, or that things don't line up the same once printed as they did on screen. This could turn out to be a problem for you if this is a commercial application you're working on.
Unfortunately, the fix for this problem is not easy. It means you'll have to dispense with the rich textbox control, which means a huge hassle of implementing yourself everything it does for you, which is quite a lot. It also means that the text drawing code you'll have to implement becomes fairly complicated. I've got code that does it, but it's too complex to post here. You might, however, find this example or this one helpful.
Good luck!
1 Abstract Base Class
I've had some fun cases where a parameter is something like ref Guid parent and the corresponding documentation says:
"Pointer to a GUID specifying the parent. Pass a null pointer to use [insert some system-defined item]."
If null (or IntPtr.Zero for IntPtr parameters) really is an invalid parameter, then you're fine using a ref parameter - maybe even better off since it's extra clear exactly what you need to pass.
If null is a valid parameter, you can pass ClassType instead of ref StructType. Objects of a reference type (class) are passed as a pointer, and they allow null.
No, you cannot overload SendMessage and make the wparam argument an int. That will make your program fail on a 64-bit version of the operating system. It has to be a pointer, either IntPtr, a blittable reference or an out or ref value type. Overloading the out/ref type is otherwise fine.
EDIT: As the OP pointed out, this is not actually a problem. The 64-bit function calling convention passes the first 4 arguments through registers, not the stack. There is thus no danger of stack mis-alignment for the wparam and lparam arguments.
I don't see any drawbacks.
By-ref is often enough for simple type and simple structure.
IntPtr should be favored if the structure has a variable-size or if you want to do custom processing.
Using ref is simpler and less error-prone than manipulating pointers manually, so I see no good reason for not using it... Another benefit of using ref is that you don't have to worry about freeing unmanaged allocated memory

Categories