WriteFile 失败取决于要写入的数据长度?

发布于 2024-11-13 10:57:06 字数 5080 浏览 3 评论 0原文

编辑

奇怪的是,我已经解决了这个问题,但它仍然让我烦恼。我通过发送太长的写入并用填充的零来解决这个问题;该代码可以工作,但发送了数百个不必要的字节。具体来说,我需要发送恰好 992 字节的数据包,而不是 7 或 19 字节的数据包。然而,我的问题仍然存在,为什么 Logitech 代码能够进行 7 或 19 字节写入,而我却不能。


我遇到了一个问题,其中特定代码块的失败或成功显然取决于传递给它的数据的长度。这对我来说毫无意义;我显然做错了什么,但我不知道是什么。

我在三种情况下尝试使用以下代码块,该代码块将字节流写入 USB 设备(两个 Logitech G 系列键盘之一):

Friend Function WriteData(ByVal Keyboard As GSeriesKeyboard, ByVal Data() As Byte) As Integer
   Dim BytesWritten As Integer = Data.Length
   Dim Success As Boolean = Win32.WriteFile(Keyboard.ExternalIOHandle, Data, Data.Length, BytesWritten, Nothing)
   Dim ErrorCode As Integer = GetLastError()
   Return ErrorCode
End Function

<DllImport("kernel32.dll", SetlastError:=True)> Friend Shared Function WriteFile( _
    ByVal File As SafeFileHandle, _
    ByVal Buffer() As Byte, _
    ByVal NumberOfBytesToWrite As Integer, _
    ByRef NumberOfBytesWritten As Integer, _
    ByRef Overlapped As System.Threading.NativeOverlapped) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function

在第一种情况下,我传递一个 992 字节流并且写入成功完成。在第二种和第三种情况下,我写入 7 或 19 个字节,WriteFile 会生成 7 个字节的错误 ERROR_INVALID_USER_BUFFER 或 19 个字节的 ERROR_INVALID_PARAMETER 错误。我打开了读写的手柄,不重叠。

当使用USBTrace时,我可以看到默认的Logitech程序能够毫无问题地写入所有三种情况,但我自己的代码只能写入992字节的情况。无论我将代码编译为 x86 还是 x64,此行为都是相同的。

我用来打开手柄的代码是这样的:

    Friend Function OpenInterface(ByVal KeyboardPath As String) As SafeFileHandle

        Dim SecurityData As New SECURITY_ATTRIBUTES()
        Dim security As New DirectorySecurity()
        Dim DescriptorBinary As Byte() = security.GetSecurityDescriptorBinaryForm()
        Dim SecurityDescriptorPtr As IntPtr = Marshal.AllocHGlobal(DescriptorBinary.Length)

        SecurityData.nLength = Marshal.SizeOf(SecurityData)
        Marshal.Copy(DescriptorBinary, 0, SecurityDescriptorPtr, DescriptorBinary.Length)
        SecurityData.lpSecurityDescriptor = SecurityDescriptorPtr

        Dim Handle As SafeFileHandle = Win32.CreateFile(KeyboardPath, GENERIC_READ Or GENERIC_WRITE, _
                                                        FILE_SHARE_READ Or FILE_SHARE_WRITE, SecurityData, _
                                                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)

        If Handle.IsInvalid Then
            Dim ErrorInfo As New ComponentModel.Win32Exception()
            Debug.Print("Failed to get device IO handle: " & ErrorInfo.Message)
        End If

        Return Handle
    End Function

    Friend Overridable Function BitmapToLcdFormat(ByVal SourceBitmap As Bitmap) As Byte()
        'Base class is for the generic B&W 160x43 LCD.  Override for G19 if I ever get my hands on one
        Dim Data As Byte() = New Byte(991) {}
        ' USBTrace says 992 bytes
        Dim Image(640 * 48) As Byte 'Temp array for image conversion.  Adds additional 5 lines of unused pixels to avoid an out-of-bounds error
        Dim BitmapData As BitmapData = SourceBitmap.LockBits(New Rectangle(0, 0, 160, 43), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
        Marshal.Copy(BitmapData.Scan0, Image, 0, (640 * 43))

        Data(0) = &H3 'Set-LCD
        Dim output As Integer = 32 'First byte of image data starts at byte 32; 960 bytes of image data
        Dim ImageOffset As Integer = 0
        For Row As Integer = 0 To 5
            For Column As Integer = 0 To (SourceBitmap.Width << 2) - 1 Step 4

                Dim r As Integer = _
                ((Image(ImageOffset + Column + BitmapData.Stride * 0) And &H80) >> 7) Or _
                ((Image(ImageOffset + Column + BitmapData.Stride * 1) And &H80) >> 6) Or _
                ((Image(ImageOffset + Column + BitmapData.Stride * 2) And &H80) >> 5) Or _
                ((Image(ImageOffset + Column + BitmapData.Stride * 3) And &H80) >> 4) Or _
                ((Image(ImageOffset + Column + BitmapData.Stride * 4) And &H80) >> 3) Or _
                ((Image(ImageOffset + Column + BitmapData.Stride * 5) And &H80) >> 2) Or _
                ((Image(ImageOffset + Column + BitmapData.Stride * 6) And &H80) >> 1) Or _
                ((Image(ImageOffset + Column + BitmapData.Stride * 7) And &H80) >> 0)

                Data(output) = CByte(r)

                output += 1
            Next
            ImageOffset += BitmapData.Stride * 8
        Next
        SourceBitmap.UnlockBits(BitmapData)
        Return Data
    End Function

调用 WriteData() 的确切代码块如下:

(Logitech G15)
    HardwareInterface.WriteData(Me, New Byte() {2, 0, 0, 0, 0, 0, 0})

(Logitech G510)
    HardwareInterface.WriteData(Me, New Byte() {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})

(Both; the one that works)
    HardwareInterface.WriteData(Me, BitmapToLcdFormat(NewImage)) 'Build 992-byte LCD-format bitmap from source iumage

这些特定命令用于将板上的宏(“G”)键重新映射到不同的字符代码,这种情况下 00 禁用该键。任何拥有 G15/G19/G510 键盘的人都可以通过禁用 Logitech 游戏面板软件并重新插入键盘来看到这一点。 G3 的作用类似于记事本中的 F3(查找下一个),但重新启动游戏面板软件后将不再这样做。

EDIT

Oddly enough, I've worked around this issue, but it's still annoying me. I worked around it by sending too-long writes, with padded zeroes; the code works but sends a few hundred unnecessary bytes. Specifically, I need to send exactly 992-byte packets instead of 7 or 19-byte packets. My question still stands however, why is the Logitech code able to make 7- or 19-byte writes when I cannot.


I'm running into an issue where a specific block of code fails or succeeds apparently depending on the length of the data passed to it. This makes no sense to me; I'm obviously doing something wrong but I can't tell what.

I have three cases where I'm trying to use the following block of code, which writes a byte stream to a USB device (one of two Logitech G-Series keyboards):

Friend Function WriteData(ByVal Keyboard As GSeriesKeyboard, ByVal Data() As Byte) As Integer
   Dim BytesWritten As Integer = Data.Length
   Dim Success As Boolean = Win32.WriteFile(Keyboard.ExternalIOHandle, Data, Data.Length, BytesWritten, Nothing)
   Dim ErrorCode As Integer = GetLastError()
   Return ErrorCode
End Function

<DllImport("kernel32.dll", SetlastError:=True)> Friend Shared Function WriteFile( _
    ByVal File As SafeFileHandle, _
    ByVal Buffer() As Byte, _
    ByVal NumberOfBytesToWrite As Integer, _
    ByRef NumberOfBytesWritten As Integer, _
    ByRef Overlapped As System.Threading.NativeOverlapped) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function

In the first case, I'm passing a 992-byte stream and the write completes successfully. In the second and third cases, I'm writing either 7 or 19 bytes, and WriteFile generates the errors ERROR_INVALID_USER_BUFFER for 7 bytes, or ERROR_INVALID_PARAMETER for 19 bytes. I've opened the handle for reading and writing, non-overlapped.

When using USBTrace, I can see that the default Logitech program is able to write all three cases without trouble, but my own code can only write the 992-byte case. This behavior is the same whether I compile my code as x86 or x64.

The code I'm using to open the handle is this:

    Friend Function OpenInterface(ByVal KeyboardPath As String) As SafeFileHandle

        Dim SecurityData As New SECURITY_ATTRIBUTES()
        Dim security As New DirectorySecurity()
        Dim DescriptorBinary As Byte() = security.GetSecurityDescriptorBinaryForm()
        Dim SecurityDescriptorPtr As IntPtr = Marshal.AllocHGlobal(DescriptorBinary.Length)

        SecurityData.nLength = Marshal.SizeOf(SecurityData)
        Marshal.Copy(DescriptorBinary, 0, SecurityDescriptorPtr, DescriptorBinary.Length)
        SecurityData.lpSecurityDescriptor = SecurityDescriptorPtr

        Dim Handle As SafeFileHandle = Win32.CreateFile(KeyboardPath, GENERIC_READ Or GENERIC_WRITE, _
                                                        FILE_SHARE_READ Or FILE_SHARE_WRITE, SecurityData, _
                                                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)

        If Handle.IsInvalid Then
            Dim ErrorInfo As New ComponentModel.Win32Exception()
            Debug.Print("Failed to get device IO handle: " & ErrorInfo.Message)
        End If

        Return Handle
    End Function

    Friend Overridable Function BitmapToLcdFormat(ByVal SourceBitmap As Bitmap) As Byte()
        'Base class is for the generic B&W 160x43 LCD.  Override for G19 if I ever get my hands on one
        Dim Data As Byte() = New Byte(991) {}
        ' USBTrace says 992 bytes
        Dim Image(640 * 48) As Byte 'Temp array for image conversion.  Adds additional 5 lines of unused pixels to avoid an out-of-bounds error
        Dim BitmapData As BitmapData = SourceBitmap.LockBits(New Rectangle(0, 0, 160, 43), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)
        Marshal.Copy(BitmapData.Scan0, Image, 0, (640 * 43))

        Data(0) = &H3 'Set-LCD
        Dim output As Integer = 32 'First byte of image data starts at byte 32; 960 bytes of image data
        Dim ImageOffset As Integer = 0
        For Row As Integer = 0 To 5
            For Column As Integer = 0 To (SourceBitmap.Width << 2) - 1 Step 4

                Dim r As Integer = _
                ((Image(ImageOffset + Column + BitmapData.Stride * 0) And &H80) >> 7) Or _
                ((Image(ImageOffset + Column + BitmapData.Stride * 1) And &H80) >> 6) Or _
                ((Image(ImageOffset + Column + BitmapData.Stride * 2) And &H80) >> 5) Or _
                ((Image(ImageOffset + Column + BitmapData.Stride * 3) And &H80) >> 4) Or _
                ((Image(ImageOffset + Column + BitmapData.Stride * 4) And &H80) >> 3) Or _
                ((Image(ImageOffset + Column + BitmapData.Stride * 5) And &H80) >> 2) Or _
                ((Image(ImageOffset + Column + BitmapData.Stride * 6) And &H80) >> 1) Or _
                ((Image(ImageOffset + Column + BitmapData.Stride * 7) And &H80) >> 0)

                Data(output) = CByte(r)

                output += 1
            Next
            ImageOffset += BitmapData.Stride * 8
        Next
        SourceBitmap.UnlockBits(BitmapData)
        Return Data
    End Function

The exact code blocks that call WriteData() are the following:

(Logitech G15)
    HardwareInterface.WriteData(Me, New Byte() {2, 0, 0, 0, 0, 0, 0})

(Logitech G510)
    HardwareInterface.WriteData(Me, New Byte() {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})

(Both; the one that works)
    HardwareInterface.WriteData(Me, BitmapToLcdFormat(NewImage)) 'Build 992-byte LCD-format bitmap from source iumage

These specific commands are used to remap the macro ("G") keys on the boards to different character codes, in this case 00 disables the key. Anyone with a G15/G19/G510 keyboard can see this by disabling their Logitech gamepanel software and re-plugging the keyboard. G3 will act like F3 in notepad (find next), but after restarting the gamepanel software it will no longer do so.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

少女情怀诗 2024-11-20 10:57:06

很抱歉,这不是一个真正有用的答案,因为我很确定您所看到的内容特定于您的特定场景,而且我不完全了解这个场景。

但在我看来,你所看到的最可能的原因是这个。您写入的内容不是真正的文件(您无疑知道),它只是键盘的一个接口,以文件的形式出现。因此,您看到与普通文件完全不同的一组行为也就不足为奇了。大多数时候您可以写入文件系统中的文件。对于键盘来说,可能需要遵循一个协议才能使其正常工作。

我认为这里发生的事情是,您尝试编写的内容失败了,它们没有被尝试以正确的状态编写。无论出于何种原因,当您编写它们时,键盘都不会期望它们。

你是说你可以看到它们被 USBTrace 接受了。这意味着存在可以正确写入它们的正确状态。

我将开始调查您与键盘进行的交互顺序,并思考什么可能会影响键盘处理您的输入的能力。

I'm sorry this is not a Really Useful Answer, as I'm quite sure that what you are seeing is specific to your particular scenario, and I don't know this scenario in its entirety.

But the most likely reason to what you are seeing in my opinion is this. What you are writing to is not a real file (as you undoubtedly know) it's just an interface to your keyboard, that is surfaced as a file. Because of this, it is not a surprise, that you see completely different set of behaviour from a normal file. You can write to a file in file system most of the time. With the keyboard there could be a protocol that you have to follow to make it work.

What I think is happening here, is that things you are trying to write that fails, they are not being attempted to be written at the right state. The keyboard for whatever reason does not expect them when you are writing them.

You are saying that you can see that they are accepted ok with the USBTrace. This means that there is a correct state where they could be written there properly.

I'd start investigation at looking at the sequence of interactions that you are doing with the keyboard and thinking what could affect the ability of the keyboard to process your input.

蓬勃野心 2024-11-20 10:57:06

尝试写入偶数个字节?

Try writing an even number of bytes?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文