【问题标题】:WriteFile failing depending on length of data to write?WriteFile 失败取决于要写入的数据长度?
【发布时间】:2011-09-09 15:51:07
【问题描述】:

编辑

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


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

我在三种情况下尝试使用以下代码块,它将字节流写入 USB 设备(两个罗技 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 键盘的人都可以通过禁用罗技游戏面板软件并重新插入键盘来看到这一点。 G3 会像记事本中的 F3 一样(查找下一个),但重新启动游戏面板软件后将不再这样做。

【问题讨论】:

  • 如果在实际的 WriteData() 调用之外构建 7 和 19 字节缓冲区会发生什么?
  • 结果都是一样的
  • 能否在 WriteData 中设置断点,并验证您认为正在传递的缓冲区是否有效?此外,BytesWritten 需要作为参考,因为 WriteFile 想要将实际写入的字节数放置到该位置。是吗?抱歉我不懂VB。
  • 我已经设置了断点,可以确认数据正在正确地传递给该函数,并且在调用WriteFile之前和之后都是有效的。
  • GSeriesKeyboard 类来自什么库?我假设它来自键盘附带的一些SDK,在哪里可以下载它?

标签: .net vb.net winapi hardware-interface writefile


【解决方案1】:

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

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

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

您是说您可以看到它们被 USBTrace 接受。这意味着有一个正确的状态可以正确地写在那里。

我会开始调查您使用键盘进行的交互顺序,并思考哪些因素会影响键盘处理您的输入的能力。

【讨论】:

  • @zespri 我遇到的问题是这两个特定的写入是唯一工作的;我可以很好地获取/设置所有功能报告以及写入键盘上的 LCD (WriteFile) 并毫无问题地读取宏或 LCD 按钮 (ReadFile) 等特殊键数据。
  • 你能为我制作一个我可以实际运行的复制品吗?我有一个G15。也许这样我们就能到达某个地方。
  • 那是蓝色的 G15 (v1) 还是橙色的 (v2)?如果它是 v1,我无法获得实际添加对库的支持所需的信息(即,它的正常设备路径)
  • 对。它是蓝色的 (v1)。几年前我为它做了一些编程,但我总是使用 SDK,而不是这种 CreateFile / WriteFile 方法。您正在使用的 API 是否有任何文档?特别是 {2, 0, 0, 0, 0, 0, 0} 是什么意思?
  • New Byte() {2, 0, 0, 0, 0, 0, 0} 表示用 {} 中的值预先填充的新字节数组。我使用的 API 只是 Windows API,“文档”来自我自己的很多工作,主要是监控流量并找出哪些位对应于哪些按键等。
【解决方案2】:

尝试写入偶数字节?

【讨论】:

  • 由于命令是奇数字节数,这对我不起作用吗?
  • 这只是一个猜测。一个有效的是偶数字节,而那些不是奇数。
猜你喜欢
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-23
  • 2019-07-16
相关资源
最近更新 更多