在C#中赢得api。从IntPtr获取Hi和low字

在C#中赢得api。从IntPtr获取Hi和low字,c#,winapi,intptr,C#,Winapi,Intptr,我正在尝试用C语言处理WM#U MOUSEMOVE消息 从属于IntPtr类型的lParam获取X和Y坐标的正确方法是什么?试试: (请注意,这是初始版本,最终版本见下文) 未选中的通常是不必要的(因为“默认”c#项目未选中) 请考虑以下是所用宏的定义: #define LOWORD(l) ((WORD)(((DWORD_PTR)(l)) & 0xffff)) #define HIWORD(l) ((WORD)((((DWORD_PTR)(l)) >> 16) & 0

我正在尝试用C语言处理WM#U MOUSEMOVE消息

从属于IntPtr类型的lParam获取X和Y坐标的正确方法是什么?

试试:
(请注意,这是初始版本,最终版本见下文)

未选中的
通常是不必要的(因为“默认”c#项目未选中)

请考虑以下是所用宏的定义:

#define LOWORD(l) ((WORD)(((DWORD_PTR)(l)) & 0xffff))
#define HIWORD(l) ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff))

#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
其中
WORD==ushort
DWORD==uint
。我正在削减一些ushort->短期转换

附录:

一年半后,在经历了64位.NET的“变幻莫测”之后,我同意Celess(但请注意,出于兼容性的原因,99%的Windows消息仍然是32位的,所以我认为现在问题并不是很大。这更多是为了将来,因为如果你想做某事,你应该正确地做。)

我唯一想改变的是:

IntPtr xy = value;
int x = unchecked((short)(long)xy);
int y = unchecked((short)((long)xy >> 16));
我用最坏的情况(8字节长)并将
xy
转换为
long
,而不是执行检查“是
IntPtr
4还是8字节长”。如果运气好的话,编译器将优化双转换(到
long
,然后到
short
/到
uint
)(最后,
IntPtr
int
的显式转换是一种转移视线的行为……如果你使用它,你将在将来面临风险。你应该始终使用
长的
转换,然后直接使用它/将其重新转换为你需要的内容,向未来的程序员表明你知道自己在做什么


一个测试示例:(遗憾的是只有32位,但如果您有一台64位的机器,您可以测试相同的代码)

通常,对于低级鼠标处理,我使用了以下帮助程序(它还认为IntPtr大小取决于x86/x64):


适用于32位和64位:

Point GetPoint(IntPtr _xy)
{
    uint xy = unchecked(IntPtr.Size == 8 ? (uint)_xy.ToInt64() : (uint)_xy.ToInt32());
    int x = unchecked((short)xy);
    int y = unchecked((short)(xy >> 16));
    return new Point(x, y);
}
-或-

int GetIntUnchecked(IntPtr value)
{
    return IntPtr.Size == 8 ? unchecked((int)value.ToInt64()) : value.ToInt32();
}
int Low16(IntPtr value)
{
    return unchecked((short)GetIntUnchecked(value));
}
int High16(IntPtr value)
{
    return unchecked((short)(((uint)GetIntUnchecked(value)) >> 16));
}
int Low16(IntPtr value)
{
    return unchecked((short)(long)value);   // presumption about internals
}                                           //  is what framework lib uses
int High16(IntPtr value)
{
    return unchecked((short)((long)value >> 16));
}
public static IntPtr MakeLParam(int low, int high)
{
    return (IntPtr)((high << 16) | (low & 0xffff));  
}                                           // (IntPtr)x is same as 'new IntPtr(x)'
return unchecked((int)(long)value);
return unchecked((uint)value);           // traditional
这些措施也起作用:

int Low16(IntPtr value)
{
    return unchecked((short)(uint)value);   // classic unchecked cast to uint
}
int High16(IntPtr value)
{
    return unchecked((short)((uint)value >> 16));
}
-或-

int GetIntUnchecked(IntPtr value)
{
    return IntPtr.Size == 8 ? unchecked((int)value.ToInt64()) : value.ToInt32();
}
int Low16(IntPtr value)
{
    return unchecked((short)GetIntUnchecked(value));
}
int High16(IntPtr value)
{
    return unchecked((short)(((uint)GetIntUnchecked(value)) >> 16));
}
int Low16(IntPtr value)
{
    return unchecked((short)(long)value);   // presumption about internals
}                                           //  is what framework lib uses
int High16(IntPtr value)
{
    return unchecked((short)((long)value >> 16));
}
public static IntPtr MakeLParam(int low, int high)
{
    return (IntPtr)((high << 16) | (low & 0xffff));  
}                                           // (IntPtr)x is same as 'new IntPtr(x)'
return unchecked((int)(long)value);
return unchecked((uint)value);           // traditional
走另一条路

public static IntPtr GetLParam(Point point)
{
    return (IntPtr)((point.Y << 16) | (point.X & 0xffff));
}                                           // mask ~= unchecked((int)(short)x)
IntPtr.Size属性返回一个常量作为编译时文本,如果跨程序集内联,则该常量可以使用。因此,JIT可以将几乎所有这些内容都优化掉。还可以执行以下操作:

return unchecked((int)value.ToInt64());
-或-

int GetIntUnchecked(IntPtr value)
{
    return IntPtr.Size == 8 ? unchecked((int)value.ToInt64()) : value.ToInt32();
}
int Low16(IntPtr value)
{
    return unchecked((short)GetIntUnchecked(value));
}
int High16(IntPtr value)
{
    return unchecked((short)(((uint)GetIntUnchecked(value)) >> 16));
}
int Low16(IntPtr value)
{
    return unchecked((short)(long)value);   // presumption about internals
}                                           //  is what framework lib uses
int High16(IntPtr value)
{
    return unchecked((short)((long)value >> 16));
}
public static IntPtr MakeLParam(int low, int high)
{
    return (IntPtr)((high << 16) | (low & 0xffff));  
}                                           // (IntPtr)x is same as 'new IntPtr(x)'
return unchecked((int)(long)value);
return unchecked((uint)value);           // traditional
-或-

int GetIntUnchecked(IntPtr value)
{
    return IntPtr.Size == 8 ? unchecked((int)value.ToInt64()) : value.ToInt32();
}
int Low16(IntPtr value)
{
    return unchecked((short)GetIntUnchecked(value));
}
int High16(IntPtr value)
{
    return unchecked((short)(((uint)GetIntUnchecked(value)) >> 16));
}
int Low16(IntPtr value)
{
    return unchecked((short)(long)value);   // presumption about internals
}                                           //  is what framework lib uses
int High16(IntPtr value)
{
    return unchecked((short)((long)value >> 16));
}
public static IntPtr MakeLParam(int low, int high)
{
    return (IntPtr)((high << 16) | (low & 0xffff));  
}                                           // (IntPtr)x is same as 'new IntPtr(x)'
return unchecked((int)(long)value);
return unchecked((uint)value);           // traditional
所有这三个函数都会调用IntPtr.ToInt64().ToInt64()的等价项,和“运算符long”也可以内联,但不太可能内联。32位版本的代码比大小常量多得多。我认为顶部的解决方案可能更符合符号正确性。注意符号扩展工件也很重要,它将填充(long)之类的所有64位无规则代码int_val,虽然我在这里已经几乎掩盖了这一点,但是可能会额外影响32位上的内联

使用

if (Low16(wParam) == NativeMethods.WM_CREATE)) { }

var x = Low16(lParam);

var point = GetPoint(lParam);

下面是一个“安全”的IntPtr模型,供未来的Traverler使用

在不设置WIN32 define on 32位的情况下运行此,以获得64位IntPtr行为的可靠模拟

public struct IntPtrMock
{
    #if WIN32
        int m_value;
    #else
        long m_value;
    #endif

    int IntPtr_ToInt32() {
        #if WIN32
            return (int)m_value;
        #else
            long l = m_value;
            return checked((int)l);
        #endif
    }

    public static explicit operator int(IntPtrMock value) { //(short) resolves here
        #if WIN32 
            return (int)value.m_value;
        #else
            long l = value.m_value;
            return checked((int)l); // throws here if any high 32 bits 
        #endif                      //  check forces sign stay signed
    }

    public static explicit operator long(IntPtrMock value) { //(uint) resolves here
        #if WIN32
            return (long)(int)value.m_value; 
        #else
            return (long)value.m_value;
        #endif 
    }

    public int ToInt32() {
        #if WIN32 
            return (int)value.m_value;
        #else
            long l = m_value;
            return checked((int)l); // throws here if any high 32 bits 
        #endif                            //  check forces sign stay signed
    }

    public long ToInt64() {
        #if WIN32
            return (long)(int)m_value; 
        #else
            return (long)m_value;
        #endif
    }

    public IntPtrMock(long value) { 
        #if WIN32
            m_value = checked((int)value);
        #else
            m_value = value; 
        #endif
    }

}

public static IntPtr MAKELPARAM(int low, int high)
{
    return (IntPtr)((high << 16) | (low & 0xffff));
}

public Main()
{
    var xy = new IntPtrMock(0x0FFFFFFFFFFFFFFF); // simulate 64-bit, overflow smaller

    int x = unchecked((short)xy);                // <-- throws
    int y = unchecked((short)((uint)xy >> 16));  // got lucky, 'uint' implicit 'long'
        y = unchecked((short)((int)xy >> 16));   // <-- throws

    int xy2 = IntPtr.Size == 8 ? (int)xy.ToInt64() : xy.ToInt32();   // <-- throws
    int xy3 = unchecked(IntPtr.Size == 8 ? (int)xy.ToInt64() : xy.ToInt32()); //ok

    // proper 32-bit lParam, overflow signed
    var xy4 = new IntPtrMock(0x00000000FFFFFFFF);       // x = -1, y = -1
    int x2 = unchecked((short)xy4);                                  // <-- throws
    int xy5 = IntPtr.Size == 8 ? (int)xy4.ToInt64() : xy4.ToInt32(); // <-- throws

    var xy6 = new IntPtrMock(0x00000000FFFF0000);       // x = 0, y = -1
    int x3 = unchecked((short)xy6);                                  // <-- throws
    int xy7 = IntPtr.Size == 8 ? (int)xy6.ToInt64() : xy6.ToInt32(); // <-- throws

    var xy8 = MAKELPARAM(-1, -1);                       // WinForms macro 
    int x4 = unchecked((short)xy8);                                  // <-- throws
    int xy9 = IntPtr.Size == 8 ? (int)xy8.ToInt64() : xy8.ToInt32(); // <-- throws
}
public struct IntPtrMock
{
#如果是WIN32
int m_值;
#否则
长m_值;
#恩迪夫
int IntPtr_ToInt32(){
#如果是WIN32
返回(int)m_值;
#否则
长l=m_值;
返回选中((int)l);
#恩迪夫
}
公共静态显式运算符int(IntPtrMock值){/(short)在此处解析
#如果是WIN32
返回(int)值.m_值;
#否则
长l=value.m_值;
返回选中((int)l);//如果有任何高32位,则抛出此处
#endif//检查强制标志保持已签署
}
公共静态显式运算符long(IntPtrMock值){//(uint)在此处解析
#如果是WIN32
返回(长)(int)值.m_值;
#否则
返回(长)值.m_值;
#恩迪夫
}
公共整数到32(){
#如果是WIN32
返回(int)值.m_值;
#否则
长l=m_值;
返回选中((int)l);//如果有任何高32位,则抛出此处
#endif//检查强制标志保持已签署
}
公共长话短说64(){
#如果是WIN32
返回(长)(整数)m_值;
#否则
返回(长)m_值;
#恩迪夫
}
公共IntPtrMock(长值){
#如果是WIN32
m_值=已检查((int)值);
#否则
m_值=值;
#恩迪夫
}
}
公共静态IntPtr MAKELPARAM(int低,int高)
{
return(IntPtr)((高16));//运气好,'uint'隐式'long'

y=unchecked((short)((int)xy>>16));//这可能是一个更好的答案。应该注意大小。在64位系统上,会检查IntPtr上int的显式强制转换运算符并将其抛出。接受的答案是“通常不需要取消选中”这不是真的。@如果在一般情况下使用x64 intrpr的强制转换,则此强制转换可以抛出。但是当
IntPtr
是正确的
点时,它将永远不会抛出!因此上面的代码已完成。LPRAM中有符号的Y将导致(int)ptr.ToInt64()抛出。更新了我的答案。@celless Ohh…我很抱歉。在我们的讨论中,我方似乎存在一些误解。这是正确的,应该考虑选中/未选中状态。更新了我的答案,谢谢。关于64位int(或short),Re:“未选中的通常是不必要的”IntPtr上的强制转换将抛出,因为显式运算符已选中。请参阅DmitryG answer。@Celess已更正,长响应为+1:)是的,延迟响应。我还想补充一点,coods bearinbg lParam“在野外”可能很容易来自windows本身或您测试的对象之外的其他来源。原始响应的行为与C宏不同。需要注意的是,即使只有32位值,负值仍会抛出取消选中如果y为负,则为d((短)xy)。Ala:未选中((短)选中E