1

我一直在使用 C 语言使用 HIDAPI,并尝试让 Mono 使用互操作与 HIDAPI 进行通信。我进行了很多搜索,但找不到任何让 HIDAPI 在 OS X 上使用 Mono 的人。

有谁知道我是否可以将使用 HIDAPI 的 HID 设备的输出重定向到本地虚拟串行端口,然后让 Mono 从串行端口读取?

另一种选择,有人知道我是否可以使用 Arduino leonardo 或 Circuits@Home USB Host Shield 之类的东西吗?

至少在我可以整理出 Mono 上的 PInvoke 之前。

谢谢

4

2 回答 2

2

我从其他地方找到并改编了这段代码,尽管我一生都找不到源代码。如果有人知道,请告诉我,以便我可以正确归因并链接到它。它在 Windows 和 OS X 上对我来说都运行良好。显然,你必须为每个平台构建 hidapi。

using System;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace HidApiCommunicationLayer
{
    internal class HidApiInteropCommLayer
    {
        #region Interop

#if WINDOWS
        private const string HIDAPI_DLL = "hidapi.dll";
#else
        private const string HIDAPI_DLL = "hidapi.dylib";
#endif

        protected IntPtr _device;

        private Object _locker = new object();

        public bool IsOpen()
        {
            return _device != IntPtr.Zero;
        }

        public void Open(ushort vid, ushort hid, string serial)
        {
            if (_device != IntPtr.Zero) throw new Exception("a device is already opened; close it first.");
            IntPtr ret = hid_open(vid, hid, serial);
            _device = ret;
            //if (_device != IntPtr.Zero)
            //    hid_set_nonblocking(_device, true);
        }

        public int Read(byte[] buffer, int length)
        {
            lock (_locker)
            {
                AssertValidDev();
                int ret = hid_read_timeout(_device, buffer, (uint)length, 1);
                if (ret < 0)
                    throw new Exception("Failed to Read.");

                return ret;
            }
        }

        public void Close()
        {
            AssertValidDev();
            hid_close(_device);
            _device = IntPtr.Zero;
        }

        public int ExitHidAPI()
        {
            return hid_exit();
        }

        public String GetProductString()
        {
            AssertValidDev();
            byte[] buf = new byte[1000];
            int ret = HidApiInteropCommLayer.hid_get_product_string(_device, buf, (uint)(buf.Length / 4) - 1);
            if (ret < 0)
                throw new Exception("failed to receive product string");
            return EncodeBuffer(buf);
        }

        public String GetManufacturerString()
        {
            AssertValidDev();
            byte[] buf = new byte[1000];
            int ret = HidApiInteropCommLayer.hid_get_manufacturer_string(_device, buf, (uint)(buf.Length / 4) - 1);
            if (ret < 0)
                throw new Exception("failed to receive manufacturer string");
            return EncodeBuffer(buf);
        }

        public int GetFeatureReport(byte[] buffer, int length)
        {
            AssertValidDev();
            int ret = hid_get_feature_report(_device, buffer, (uint)length);
            if (ret < 0)
                throw new Exception("failed to get feature report");
            return ret;
        }

        public int SendFeatureReport(byte[] buffer)
        {
            int ret = hid_send_feature_report(_device, buffer, (uint)buffer.Length);
            //if (ret < 0)
            //  throw new Exception ("failed to send feature report");
            return ret;
        }

        public int Write(byte[] buffer)
        {
            lock (_locker)
            {
                AssertValidDev();
                int ret = hid_write(_device, buffer, HID_MAX_PACKET_SIZE + 1);
                //if (ret < 0)
                //    Custom logging
                return ret;
            }
        }

        public String Error()
        {
            AssertValidDev();
            IntPtr ret = hid_error(_device);
            return Marshal.PtrToStringAuto(ret);
        }

        public string GetIndexedString(int index)
        {
            AssertValidDev();
            byte[] buf = new byte[1000];
            int ret = HidApiInteropCommLayer.hid_get_indexed_string(_device, index, buf, (uint)(buf.Length / 4) - 1);
            if (ret < 0)
                throw new Exception("failed to receive indexed string");
            return EncodeBuffer(buf);
        }

        public string GetSerialNumberString()
        {
            AssertValidDev();
            byte[] buf = new byte[1000];
            int ret = HidApiInteropCommLayer.hid_get_serial_number_string(_device, buf, (uint)(buf.Length / 4) - 1);
            if (ret < 0)
                throw new Exception("failed to receive serial number string");
            return EncodeBuffer(buf);
        }

        private string EncodeBuffer(byte[] buffer)
        {
            return Encoding.Unicode.GetString(buffer).Trim('\0');
        }

        private void AssertValidDev()
        {
            if (_device == IntPtr.Zero) throw new Exception("No device opened");
        }

        #region DllImports

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_read(IntPtr device, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_read_timeout(IntPtr device, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length, int timeout);

        [DllImport(HIDAPI_DLL)]
        private static extern IntPtr hid_open(ushort vid, ushort pid, [MarshalAs(UnmanagedType.LPWStr)] string serial);

        [DllImport(HIDAPI_DLL)]
        private static extern void hid_close(IntPtr device);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_init();

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_exit();

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_get_product_string(IntPtr device, [Out] byte[] _string, uint length);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_get_manufacturer_string(IntPtr device, [Out] byte[] _string, uint length);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_get_feature_report(IntPtr device, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_get_serial_number_string(IntPtr device, [Out] byte[] serial, uint maxlen);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_get_indexed_string(IntPtr device, int string_index, [Out] byte[] _string, uint maxlen);

        [DllImport(HIDAPI_DLL)]
        private static extern IntPtr hid_error(IntPtr device);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_send_feature_report(IntPtr device, [In, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_set_nonblocking(IntPtr device, [In, MarshalAs(UnmanagedType.SysInt)] bool nonblock);

        [DllImport(HIDAPI_DLL)]
        private static extern int hid_write(IntPtr device, [In, MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);

        [DllImport(HIDAPI_DLL)]
        private static extern IntPtr hid_open_path([In, MarshalAs(UnmanagedType.LPStr)] string path);

        #endregion DllImports

        #endregion Interop

        #region Constructors

        public static HidApiInteropCommLayer GetDevice(ushort vid, ushort pid)
        {
            try
            {
                HidApiInteropCommLayer layer = new HidApiInteropCommLayer();
                layer.Open(vid, pid, null);
                return layer._device == IntPtr.Zero ? null : layer;
            }
            catch (System.BadImageFormatException fx)
            {
                //Custom logging
                return null;
            }
            catch (Exception ex)
            {
                //Custom logging
                return null;
            }
        }

        #endregion Constructors

        private const int HID_MAX_PACKET_SIZE = 1024;

        #region ICommunicationLayer

        public void Init()
        {
            try
            {
                if (IsOpen())
                {
                    ContinueReadProcessing = true;
                    ReadThread = new Thread(new ThreadStart(ReadLoop));
                    ReadThread.Name = "HidApiReadThread";
                    ReadThread.Start();
                }
                else
                {
                    Disconnect();
                }
            }
            catch (Exception ex)
            {
                //Custom logging
                throw;
            }
        }

        public bool SendData(byte[] data)
        {
            try
            {
                MemoryStream stream = new MemoryStream(HID_MAX_PACKET_SIZE + 1);
                stream.WriteByte(0);
                stream.Write(data, 0, HID_MAX_PACKET_SIZE);
                int ret = Write(stream.ToArray());
                if (ret >= 0)
                    return true;
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                //Custom logging
                return false;
            }
        }

        public event EventHandler<DataEventArgs> DataReceived;

        public event EventHandler Disconnected;

        public void Start()
        {
            ContinueReadProcessing = true;
        }

        public void Stop()
        {
            Disconnect();
        }

        #endregion ICommunicationLayer

        private Thread ReadThread = null;

        protected volatile bool ContinueReadProcessing = true;

        private void ReadLoop()
        {
            var culture = CultureInfo.InvariantCulture;
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = culture;
            Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;

            while (ContinueReadProcessing)
            {
                try
                {
                    byte[] report = new byte[HID_MAX_PACKET_SIZE];

                    var result = Read(report, HID_MAX_PACKET_SIZE);

                    if (result > 0)
                    {
                        DataReceived(this, new DataEventArgs(report));
                    }
                    else if (result < 0)
                    {
                        Disconnect();
                    }
                }
                catch (Exception ex)
                {
                    Disconnect();
                }

                Thread.Sleep(1);
            }
        }

        private void Disconnect()
        {
            ContinueReadProcessing = false;
            Disconnected(this, EventArgs.Empty);
        }

        #region IDisposable Members

        public void Dispose()
        {
            ContinueReadProcessing = false;
            ReadThread.Join(500);
            if (ReadThread.IsAlive)
            {
                ReadThread.Abort();
            }

            if (IsOpen())
                Close();
            int res = ExitHidAPI();
        }

        #endregion IDisposable Members
    }

    internal class Utf32Marshaler : ICustomMarshaler
    {
        private static Utf32Marshaler instance = new Utf32Marshaler();

        public static ICustomMarshaler GetInstance(string s)
        {
            return instance;
        }

        public void CleanUpManagedData(object o)
        {
        }

        public void CleanUpNativeData(IntPtr pNativeData)
        {
            Marshal.FreeHGlobal(pNativeData);
            //UnixMarshal.FreeHeap(pNativeData);
        }

        public int GetNativeDataSize()
        {
            return IntPtr.Size;
        }

        public IntPtr MarshalManagedToNative(object obj)
        {
            string s = obj as string;
            if (s == null)
                return IntPtr.Zero;
            return Marshal.StringToHGlobalAuto(s);
        }

        public object MarshalNativeToManaged(IntPtr pNativeData)
        {
            return Marshal.PtrToStringAuto(pNativeData);
        }
    }

    public class DataEventArgs : EventArgs
    {
        public DataEventArgs(byte[] data)
        {
            Data = data;
        }

        public byte[] Data { get; private set; }
    }
}
于 2014-04-23T22:58:11.210 回答
0

Arduino Leonardo 不能充当 USB 主机。

但原则上,是的,您可以让设备充当设备端的 CDC 串行端口,并让它充当 HID 设备的主机 USB。

或者,您可以完全跳过 HIDAPI 步骤并使用HidSharp。:) 它将直接 P/Invoke 到 MacOS X 本机 API。

希望这可以帮助

詹姆士

于 2013-05-07T14:25:50.313 回答