commit b047fe3fabc09550ce4f51b325765c70a077734c
Author: gb <741021719@qq.com>
Date: Tue May 3 16:54:08 2022 +0800
initialize
diff --git a/device/device.def b/device/device.def
new file mode 100644
index 0000000..c62a1b4
--- /dev/null
+++ b/device/device.def
@@ -0,0 +1,17 @@
+LIBRARY hgscanner
+EXPORTS
+ hg_scanner_initialize
+ hg_scanner_uninitialize
+ hg_scanner_get_version
+ hg_scanner_enum
+ hg_scanner_open
+ hg_scanner_close
+ hg_scanner_get_parameter
+ hg_scanner_set_parameter
+ hg_scanner_start
+ hg_scanner_stop
+ hg_scanner_get_img_info
+ hg_scanner_read_img_data
+ hg_scanner_get_status
+ hg_scanner_reset
+ hg_scanner_control
\ No newline at end of file
diff --git a/device/hgscanner.vcxproj b/device/hgscanner.vcxproj
new file mode 100644
index 0000000..c264d44
--- /dev/null
+++ b/device/hgscanner.vcxproj
@@ -0,0 +1,325 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ Win32Proj
+ {9ed4b425-73e0-423e-9712-455e777481b4}
+ hgscanner
+ 10.0
+
+
+
+ DynamicLibrary
+ true
+ v142
+ Unicode
+
+
+ DynamicLibrary
+ false
+ v142
+ true
+ Unicode
+
+
+ Application
+ true
+ v142
+ Unicode
+
+
+ Application
+ false
+ v142
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)..\..\device\hgdriver\3rdparty\nick;$(SolutionDir)..\..\device\hgdriver\3rdparty\opencv\include\win;$(SolutionDir)..\..\device\hgdriver\3rdparty\cyusb\inc\;$(SolutionDir)..\..\device\hgdriver\3rdparty\libtiff\include\;$(SolutionDir)..\..\device\hgdriver\3rdparty\log4cplus\include\;$(SolutionDir)..\..\device\sdk\;$(SolutionDir)..\..\device\hgdriver\ImageProcess\;$(SolutionDir)..\..\sdk\include\;$(SolutionDir)..\..\device\hgdriver\hgdev\;$(SolutionDir)..\..\device\hgdriver\wrapper\;$(ProjectDir);$(IncludePath)
+ $(ProjectDir)lib\$(PlatformTarget)\$(Configuration);$(LibraryPath)
+ $(SolutionDir)..\..\tmp\$(PlatformTarget)\$(Configuration)\$(ProjectName)\
+ $(SolutionDir)..\..\release\win\$(PlatformTarget)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)..\..\device\hgdriver\3rdparty\nick;$(SolutionDir)..\..\device\hgdriver\3rdparty\opencv\include\win;$(SolutionDir)..\..\device\hgdriver\3rdparty\cyusb\inc\;$(SolutionDir)..\..\device\hgdriver\3rdparty\libtiff\include\;$(SolutionDir)..\..\device\hgdriver\3rdparty\log4cplus\include\;$(SolutionDir)..\..\device\sdk\;$(SolutionDir)..\..\device\hgdriver\ImageProcess\;$(SolutionDir)..\..\sdk\include\;$(SolutionDir)..\..\device\hgdriver\hgdev\;$(SolutionDir)..\..\device\hgdriver\wrapper\;$(ProjectDir);$(IncludePath)
+ $(ProjectDir)lib\$(PlatformTarget)\$(Configuration);$(LibraryPath)
+ $(SolutionDir)..\..\tmp\$(PlatformTarget)\$(Configuration)\$(ProjectName)\
+ $(SolutionDir)..\..\release\win\$(PlatformTarget)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)..\..\release\win\$(PlatformTarget)\$(Configuration)\
+ $(SolutionDir)..\..\tmp\$(PlatformTarget)\$(Configuration)\$(ProjectName)\
+ $(SolutionDir)..\..\device\hgdriver\3rdparty\nick;$(SolutionDir)..\..\device\hgdriver\3rdparty\opencv\include\win;$(SolutionDir)..\..\device\hgdriver\3rdparty\cyusb\inc\;$(SolutionDir)..\..\device\hgdriver\3rdparty\libtiff\include\;$(SolutionDir)..\..\device\hgdriver\3rdparty\log4cplus\include\;$(SolutionDir)..\..\device\sdk\;$(SolutionDir)..\..\device\hgdriver\ImageProcess\;$(SolutionDir)..\..\sdk\include\;$(SolutionDir)..\..\device\hgdriver\hgdev\;$(SolutionDir)..\..\device\hgdriver\wrapper\;$(ProjectDir);$(IncludePath)
+ $(ProjectDir)lib\$(PlatformTarget)\$(Configuration);$(LibraryPath)
+
+
+ false
+ $(SolutionDir)..\..\release\win\$(PlatformTarget)\$(Configuration)\
+ $(SolutionDir)..\..\tmp\$(PlatformTarget)\$(Configuration)\$(ProjectName)\
+ $(SolutionDir)..\..\device\hgdriver\3rdparty\nick;$(SolutionDir)..\..\device\hgdriver\3rdparty\opencv\include\win;$(SolutionDir)..\..\device\hgdriver\3rdparty\cyusb\inc\;$(SolutionDir)..\..\device\hgdriver\3rdparty\libtiff\include\;$(SolutionDir)..\..\device\hgdriver\3rdparty\log4cplus\include\;$(SolutionDir)..\..\device\sdk\;$(SolutionDir)..\..\device\hgdriver\ImageProcess\;$(SolutionDir)..\..\sdk\include\;$(SolutionDir)..\..\device\hgdriver\hgdev\;$(SolutionDir)..\..\device\hgdriver\wrapper\;$(ProjectDir);$(IncludePath)
+ $(ProjectDir)lib\$(PlatformTarget)\$(Configuration);$(LibraryPath)
+
+
+
+ Level3
+ true
+ WIN32;HGSCANNER_EXPORT;CUSTOM_USBVIEW;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ 4996
+ MultiThreadedDebugDLL
+ stdcpp17
+
+
+ Console
+ true
+ opencv_world3414.lib;IlmImf.lib;ittnotify.lib;leptonica.lib;libjasper.lib;libjpeg-turbo.lib;libpng.lib;libtiff.lib;libwebp.lib;log4cplusS.lib;quirc.lib;tiff.lib;zlib.lib;user32.lib
+
+
+ $(ProjectDir)device.def
+
+
+
+
+ mkdir $(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)
+move /Y "$(OutDirFullPath)$(ProjectName).exp" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+move /Y "$(OutDirFullPath)$(ProjectName).lib" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+move /Y "$(OutDirFullPath)$(ProjectName).pdb" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;HGSCANNER_EXPORT;CUSTOM_USBVIEW;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ 4996
+ stdcpp17
+
+
+ Console
+ true
+ true
+ true
+ opencv_world3414.lib;IlmImf.lib;ittnotify.lib;leptonica.lib;libjasper.lib;libjpeg-turbo.lib;libpng.lib;libtiff.lib;libwebp.lib;log4cplusS.lib;quirc.lib;tiff.lib;zlib.lib;user32.lib
+ $(ProjectDir)device.def
+
+
+
+
+
+
+ mkdir $(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)
+move /Y "$(OutDirFullPath)$(ProjectName).exp" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+move /Y "$(OutDirFullPath)$(ProjectName).lib" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+move /Y "$(OutDirFullPath)$(ProjectName).pdb" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ mkdir $(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)
+move /Y "$(OutDirFullPath)$(ProjectName).exp" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+move /Y "$(OutDirFullPath)$(ProjectName).lib" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+move /Y "$(OutDirFullPath)$(ProjectName).pdb" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ mkdir $(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)
+move /Y "$(OutDirFullPath)$(ProjectName).exp" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+move /Y "$(OutDirFullPath)$(ProjectName).lib" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+move /Y "$(OutDirFullPath)$(ProjectName).pdb" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/device/hgscanner.vcxproj.filters b/device/hgscanner.vcxproj.filters
new file mode 100644
index 0000000..6ea6739
--- /dev/null
+++ b/device/hgscanner.vcxproj.filters
@@ -0,0 +1,365 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+ {69bf4790-4420-4dc8-bc81-a63a902ce9c9}
+
+
+ {f707cca2-7b0e-41a8-b091-4340f974ff02}
+
+
+ {01c7b72c-12be-466b-b93e-9a9c8fea21b4}
+
+
+ {06629bf8-5d3e-4855-916b-260169824839}
+
+
+
+
+ 源文件
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ usb
+
+
+ usb\usbview
+
+
+ usb\usbview
+
+
+ dev
+
+
+ dev
+
+
+ 源文件
+
+
+
+
+ 头文件
+
+
+ 头文件
+
+
+ 头文件
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ image
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ dev
+
+
+ usb
+
+
+ usb\usbview
+
+
+ usb\usbview
+
+
+ usb\usbview
+
+
+ dev
+
+
+ dev
+
+
+ 头文件
+
+
+
+
+ 源文件
+
+
+
\ No newline at end of file
diff --git a/device/lib/x86/Debug/CyAPI.lib b/device/lib/x86/Debug/CyAPI.lib
new file mode 100644
index 0000000..a888ef7
Binary files /dev/null and b/device/lib/x86/Debug/CyAPI.lib differ
diff --git a/device/lib/x86/Debug/HG_OCR.lib b/device/lib/x86/Debug/HG_OCR.lib
new file mode 100644
index 0000000..a6efaea
Binary files /dev/null and b/device/lib/x86/Debug/HG_OCR.lib differ
diff --git a/device/lib/x86/Debug/IlmImf.lib b/device/lib/x86/Debug/IlmImf.lib
new file mode 100644
index 0000000..5ac1a3c
Binary files /dev/null and b/device/lib/x86/Debug/IlmImf.lib differ
diff --git a/device/lib/x86/Debug/ittnotify.lib b/device/lib/x86/Debug/ittnotify.lib
new file mode 100644
index 0000000..33cb642
Binary files /dev/null and b/device/lib/x86/Debug/ittnotify.lib differ
diff --git a/device/lib/x86/Debug/leptonica.lib b/device/lib/x86/Debug/leptonica.lib
new file mode 100644
index 0000000..432fce4
Binary files /dev/null and b/device/lib/x86/Debug/leptonica.lib differ
diff --git a/device/lib/x86/Debug/libjasper.lib b/device/lib/x86/Debug/libjasper.lib
new file mode 100644
index 0000000..104465c
Binary files /dev/null and b/device/lib/x86/Debug/libjasper.lib differ
diff --git a/device/lib/x86/Debug/libjpeg-turbo.lib b/device/lib/x86/Debug/libjpeg-turbo.lib
new file mode 100644
index 0000000..a2322dd
Binary files /dev/null and b/device/lib/x86/Debug/libjpeg-turbo.lib differ
diff --git a/device/lib/x86/Debug/libpng.lib b/device/lib/x86/Debug/libpng.lib
new file mode 100644
index 0000000..8d63e9e
Binary files /dev/null and b/device/lib/x86/Debug/libpng.lib differ
diff --git a/device/lib/x86/Debug/libtiff.lib b/device/lib/x86/Debug/libtiff.lib
new file mode 100644
index 0000000..743bba8
Binary files /dev/null and b/device/lib/x86/Debug/libtiff.lib differ
diff --git a/device/lib/x86/Debug/libwebp.lib b/device/lib/x86/Debug/libwebp.lib
new file mode 100644
index 0000000..0d5aef7
Binary files /dev/null and b/device/lib/x86/Debug/libwebp.lib differ
diff --git a/device/lib/x86/Debug/log4cplusS.lib b/device/lib/x86/Debug/log4cplusS.lib
new file mode 100644
index 0000000..d411cf5
Binary files /dev/null and b/device/lib/x86/Debug/log4cplusS.lib differ
diff --git a/device/lib/x86/Debug/opencv_world3414.lib b/device/lib/x86/Debug/opencv_world3414.lib
new file mode 100644
index 0000000..a867962
Binary files /dev/null and b/device/lib/x86/Debug/opencv_world3414.lib differ
diff --git a/device/lib/x86/Debug/quirc.lib b/device/lib/x86/Debug/quirc.lib
new file mode 100644
index 0000000..0472624
Binary files /dev/null and b/device/lib/x86/Debug/quirc.lib differ
diff --git a/device/lib/x86/Debug/tiff.lib b/device/lib/x86/Debug/tiff.lib
new file mode 100644
index 0000000..fff6095
Binary files /dev/null and b/device/lib/x86/Debug/tiff.lib differ
diff --git a/device/lib/x86/Debug/zlib.lib b/device/lib/x86/Debug/zlib.lib
new file mode 100644
index 0000000..a018c31
Binary files /dev/null and b/device/lib/x86/Debug/zlib.lib differ
diff --git a/device/lib/x86/Release/CyAPI.lib b/device/lib/x86/Release/CyAPI.lib
new file mode 100644
index 0000000..a888ef7
Binary files /dev/null and b/device/lib/x86/Release/CyAPI.lib differ
diff --git a/device/lib/x86/Release/HG_OCR.lib b/device/lib/x86/Release/HG_OCR.lib
new file mode 100644
index 0000000..f71a368
Binary files /dev/null and b/device/lib/x86/Release/HG_OCR.lib differ
diff --git a/device/lib/x86/Release/IlmImf.lib b/device/lib/x86/Release/IlmImf.lib
new file mode 100644
index 0000000..9f812b6
Binary files /dev/null and b/device/lib/x86/Release/IlmImf.lib differ
diff --git a/device/lib/x86/Release/ittnotify.lib b/device/lib/x86/Release/ittnotify.lib
new file mode 100644
index 0000000..e9e2726
Binary files /dev/null and b/device/lib/x86/Release/ittnotify.lib differ
diff --git a/device/lib/x86/Release/leptonica.lib b/device/lib/x86/Release/leptonica.lib
new file mode 100644
index 0000000..49e41d2
Binary files /dev/null and b/device/lib/x86/Release/leptonica.lib differ
diff --git a/device/lib/x86/Release/libjasper.lib b/device/lib/x86/Release/libjasper.lib
new file mode 100644
index 0000000..0e16fa0
Binary files /dev/null and b/device/lib/x86/Release/libjasper.lib differ
diff --git a/device/lib/x86/Release/libjpeg-turbo.lib b/device/lib/x86/Release/libjpeg-turbo.lib
new file mode 100644
index 0000000..45d949f
Binary files /dev/null and b/device/lib/x86/Release/libjpeg-turbo.lib differ
diff --git a/device/lib/x86/Release/libpng.lib b/device/lib/x86/Release/libpng.lib
new file mode 100644
index 0000000..1c67165
Binary files /dev/null and b/device/lib/x86/Release/libpng.lib differ
diff --git a/device/lib/x86/Release/libtiff.lib b/device/lib/x86/Release/libtiff.lib
new file mode 100644
index 0000000..3abcdd8
Binary files /dev/null and b/device/lib/x86/Release/libtiff.lib differ
diff --git a/device/lib/x86/Release/libwebp.lib b/device/lib/x86/Release/libwebp.lib
new file mode 100644
index 0000000..b07abf9
Binary files /dev/null and b/device/lib/x86/Release/libwebp.lib differ
diff --git a/device/lib/x86/Release/log4cplusS.lib b/device/lib/x86/Release/log4cplusS.lib
new file mode 100644
index 0000000..c6a6163
Binary files /dev/null and b/device/lib/x86/Release/log4cplusS.lib differ
diff --git a/device/lib/x86/Release/opencv_world3414.lib b/device/lib/x86/Release/opencv_world3414.lib
new file mode 100644
index 0000000..884436b
Binary files /dev/null and b/device/lib/x86/Release/opencv_world3414.lib differ
diff --git a/device/lib/x86/Release/quirc.lib b/device/lib/x86/Release/quirc.lib
new file mode 100644
index 0000000..a84419c
Binary files /dev/null and b/device/lib/x86/Release/quirc.lib differ
diff --git a/device/lib/x86/Release/tiff.lib b/device/lib/x86/Release/tiff.lib
new file mode 100644
index 0000000..5941979
Binary files /dev/null and b/device/lib/x86/Release/tiff.lib differ
diff --git a/device/lib/x86/Release/zlib.lib b/device/lib/x86/Release/zlib.lib
new file mode 100644
index 0000000..be9c719
Binary files /dev/null and b/device/lib/x86/Release/zlib.lib differ
diff --git a/device/win_usb/twain_flow.txt b/device/win_usb/twain_flow.txt
new file mode 100644
index 0000000..59af402
--- /dev/null
+++ b/device/win_usb/twain_flow.txt
@@ -0,0 +1,60 @@
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_IDENTITY, MSG = MSG_GET, data: 0x02ee50d4,
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_IDENTITY, MSG = MSG_OPENDS, data: 0x02ee6b84, Identity(ID: 0x1b, Ver: 0x1, Proto: 1000d.79736145, Group: 0x6e616353, Manufacturer: EasyScan, ProductFamily: EasyScan, ProductName: EasyScan);
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GETCURRENT, data: 0x02ee6b34, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b34, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GET, data: 0x02ee6b24, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GET, data: 0x02ee6b24, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GETCURRENT, data: 0x02ee6b24, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GETCURRENT, data: 0x02ee6b24, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b34, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GETCURRENT, data: 0x02ee6b34, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b34, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_STATUS, MSG = MSG_GET, data: 0x02ee6b1c, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GET, data: 0x02ee6b24, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GET, data: 0x02ee6b24, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b34, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GET, data: 0x02ee6b24, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GET, data: 0x02ee6b24, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GET, data: 0x02ee6b24, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GET, data: 0x02ee6b24, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GET, data: 0x02ee6b24, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GET, data: 0x02ee6b24, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b34, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b2c, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b2c, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_STATUS, MSG = MSG_GET, data: 0x02ee6b14, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b2c, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b2c, Identity(same as the latest)
+[21164] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_IDENTITY, MSG = MSG_GET, data: 0x0d468504,
+[21164] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_IDENTITY, MSG = MSG_OPENDS, data: 0x0d46be54, Identity(ID: 0x1e, Ver: 0x1, Proto: 1000d.79736145, Group: 0x6e616353, Manufacturer: EasyScan, ProductFamily: EasyScan, ProductName: EasyScan);
+[21164] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_STATUS, MSG = MSG_GET, data: 0x0d46beb0, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b8c, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b6c, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b6c, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b8c, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b7c, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b8c, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_STATUS, MSG = MSG_GET, data: 0x02ee6b74, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b6c, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b6c, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_IMAGE, DAT = DAT_IMAGELAYOUT, MSG = MSG_SET, data: 0x02ee6bac, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_STATUS, MSG = MSG_GET, data: 0x02ee6b94, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_IMAGE, DAT = DAT_IMAGELAYOUT, MSG = MSG_RESET, data: 0x02ee6bac, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6b8c, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_USERINTERFACE, MSG = MSG_ENABLEDS, data: 0x02ee6b80, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_IMAGE, DAT = DAT_IMAGENATIVEXFER, MSG = MSG_GET, data: 0x02ee5518, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_STATUS, MSG = MSG_GET, data: 0x02ee5500, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_PENDINGXFERS, MSG = MSG_ENDXFER, data: 0x02ee5510, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_USERINTERFACE, MSG = MSG_DISABLEDS, data: 0x02ee5674, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6f84, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6f64, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6f64, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GETCURRENT, data: 0x02ee6f74, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_GETCURRENT, data: 0x02ee6f74, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6f84, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6f74, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6f84, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_STATUS, MSG = MSG_GET, data: 0x02ee6f6c, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6f64, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_CONTROL, DAT = DAT_CAPABILITY, MSG = MSG_SET, data: 0x02ee6f64, Identity(same as the latest)
+[22772] [TWAIN-monitor] DG = DG_IMAGE, DAT = DAT_IMAGELAYOUT, MSG = MSG_SET, data: 0x02ee6fa4, Identity(same as the latest)
diff --git a/device/win_usb/usbview/devnode.c b/device/win_usb/usbview/devnode.c
new file mode 100644
index 0000000..ef81aee
--- /dev/null
+++ b/device/win_usb/usbview/devnode.c
@@ -0,0 +1,336 @@
+/*++
+
+ Copyright (c) 1998-2011 Microsoft Corporation
+
+ Module Name:
+
+ DEVNODE.C
+
+ --*/
+
+/*****************************************************************************
+ I N C L U D E S
+ *****************************************************************************/
+
+#include "enum.h"
+
+/*****************************************************************************
+
+ DriverNameToDeviceInst()
+
+ Finds the Device instance of the DevNode with the matching DriverName.
+ Returns FALSE if the matching DevNode is not found and TRUE if found
+
+ *****************************************************************************/
+BOOL
+DriverNameToDeviceInst(
+ _In_reads_bytes_(cbDriverName) PCHAR DriverName,
+ _In_ size_t cbDriverName,
+ _Out_ HDEVINFO *pDevInfo,
+ _Out_writes_bytes_(sizeof(SP_DEVINFO_DATA)) PSP_DEVINFO_DATA pDevInfoData
+ )
+{
+ HDEVINFO deviceInfo = INVALID_HANDLE_VALUE;
+ BOOL status = TRUE;
+ ULONG deviceIndex;
+ SP_DEVINFO_DATA deviceInfoData;
+ BOOL bResult = FALSE;
+ PCHAR pDriverName = NULL;
+ PSTR buf = NULL;
+ BOOL done = FALSE;
+
+ if (pDevInfo == NULL)
+ {
+ return FALSE;
+ }
+
+ if (pDevInfoData == NULL)
+ {
+ return FALSE;
+ }
+
+ memset(pDevInfoData, 0, sizeof(SP_DEVINFO_DATA));
+
+ *pDevInfo = INVALID_HANDLE_VALUE;
+
+ // Use local string to guarantee zero termination
+ pDriverName = (PCHAR) ALLOC((DWORD) cbDriverName + 1);
+ if (NULL == pDriverName)
+ {
+ status = FALSE;
+ goto Done;
+ }
+ StringCbCopyN(pDriverName, cbDriverName + 1, DriverName, cbDriverName);
+
+ //
+ // We cannot walk the device tree with CM_Get_Sibling etc. unless we assume
+ // the device tree will stabilize. Any devnode removal (even outside of USB)
+ // would force us to retry. Instead we use Setup API to snapshot all
+ // devices.
+ //
+
+ // Examine all present devices to see if any match the given DriverName
+ //
+ deviceInfo = SetupDiGetClassDevs(NULL,
+ NULL,
+ NULL,
+ DIGCF_ALLCLASSES | DIGCF_PRESENT);
+
+ if (deviceInfo == INVALID_HANDLE_VALUE)
+ {
+ status = FALSE;
+ goto Done;
+ }
+
+ deviceIndex = 0;
+ deviceInfoData.cbSize = sizeof(deviceInfoData);
+
+ while (done == FALSE)
+ {
+ //
+ // Get devinst of the next device
+ //
+
+ status = SetupDiEnumDeviceInfo(deviceInfo,
+ deviceIndex,
+ &deviceInfoData);
+
+ deviceIndex++;
+
+ if (!status)
+ {
+ //
+ // This could be an error, or indication that all devices have been
+ // processed. Either way the desired device was not found.
+ //
+
+ done = TRUE;
+ break;
+ }
+
+ //
+ // Get the DriverName value
+ //
+
+ bResult = GetDeviceProperty(deviceInfo,
+ &deviceInfoData,
+ SPDRP_DRIVER,
+ &buf);
+
+ // If the DriverName value matches, return the DeviceInstance
+ //
+ if (bResult == TRUE && buf != NULL && _stricmp(pDriverName, buf) == 0)
+ {
+ done = TRUE;
+ *pDevInfo = deviceInfo;
+ CopyMemory(pDevInfoData, &deviceInfoData, sizeof(deviceInfoData));
+ FREE(buf);
+ break;
+ }
+
+ if(buf != NULL)
+ {
+ FREE(buf);
+ buf = NULL;
+ }
+ }
+
+Done:
+
+ if (bResult == FALSE)
+ {
+ if (deviceInfo != INVALID_HANDLE_VALUE)
+ {
+ SetupDiDestroyDeviceInfoList(deviceInfo);
+ }
+ }
+
+ if (pDriverName != NULL)
+ {
+ FREE(pDriverName);
+ }
+
+ return status;
+}
+
+/*****************************************************************************
+
+ DriverNameToDeviceProperties()
+
+ Returns the Device properties of the DevNode with the matching DriverName.
+ Returns NULL if the matching DevNode is not found.
+
+ The caller should free the returned structure using FREE() macro
+
+ *****************************************************************************/
+PUSB_DEVICE_PNP_STRINGS
+DriverNameToDeviceProperties(
+ _In_reads_bytes_(cbDriverName) PCHAR DriverName,
+ _In_ size_t cbDriverName
+ )
+{
+ HDEVINFO deviceInfo = INVALID_HANDLE_VALUE;
+ SP_DEVINFO_DATA deviceInfoData = {0};
+ ULONG len;
+ BOOL status;
+ PUSB_DEVICE_PNP_STRINGS DevProps = NULL;
+ DWORD lastError;
+
+ // Allocate device propeties structure
+ DevProps = (PUSB_DEVICE_PNP_STRINGS) ALLOC(sizeof(USB_DEVICE_PNP_STRINGS));
+
+ if(NULL == DevProps)
+ {
+ status = FALSE;
+ goto Done;
+ }
+
+ // Get device instance
+ status = DriverNameToDeviceInst(DriverName, cbDriverName, &deviceInfo, &deviceInfoData);
+ if (status == FALSE)
+ {
+ goto Done;
+ }
+
+ len = 0;
+ status = SetupDiGetDeviceInstanceId(deviceInfo,
+ &deviceInfoData,
+ NULL,
+ 0,
+ &len);
+ lastError = GetLastError();
+
+
+ if (status != FALSE && lastError != ERROR_INSUFFICIENT_BUFFER)
+ {
+ status = FALSE;
+ goto Done;
+ }
+
+ //
+ // An extra byte is required for the terminating character
+ //
+
+ len++;
+ DevProps->DeviceId = ALLOC(len);
+
+ if (DevProps->DeviceId == NULL)
+ {
+ status = FALSE;
+ goto Done;
+ }
+
+ status = SetupDiGetDeviceInstanceId(deviceInfo,
+ &deviceInfoData,
+ DevProps->DeviceId,
+ len,
+ &len);
+ if (status == FALSE)
+ {
+ goto Done;
+ }
+
+ status = GetDeviceProperty(deviceInfo,
+ &deviceInfoData,
+ SPDRP_DEVICEDESC,
+ &DevProps->DeviceDesc);
+
+ if (status == FALSE)
+ {
+ goto Done;
+ }
+
+
+ //
+ // We don't fail if the following registry query fails as these fields are additional information only
+ //
+
+ GetDeviceProperty(deviceInfo,
+ &deviceInfoData,
+ SPDRP_HARDWAREID,
+ &DevProps->HwId);
+
+ GetDeviceProperty(deviceInfo,
+ &deviceInfoData,
+ SPDRP_SERVICE,
+ &DevProps->Service);
+
+ GetDeviceProperty(deviceInfo,
+ &deviceInfoData,
+ SPDRP_CLASS,
+ &DevProps->DeviceClass);
+Done:
+
+ if (deviceInfo != INVALID_HANDLE_VALUE)
+ {
+ SetupDiDestroyDeviceInfoList(deviceInfo);
+ }
+
+ if (status == FALSE)
+ {
+ if (DevProps != NULL)
+ {
+ FreeDeviceProperties(&DevProps);
+ }
+ }
+ return DevProps;
+}
+
+/*****************************************************************************
+
+ FreeDeviceProperties()
+
+ Free the device properties structure
+
+ *****************************************************************************/
+VOID FreeDeviceProperties(_In_ PUSB_DEVICE_PNP_STRINGS *ppDevProps)
+{
+ if(ppDevProps == NULL)
+ {
+ return;
+ }
+
+ if(*ppDevProps == NULL)
+ {
+ return;
+ }
+
+ if ((*ppDevProps)->DeviceId != NULL)
+ {
+ FREE((*ppDevProps)->DeviceId);
+ }
+
+ if ((*ppDevProps)->DeviceDesc != NULL)
+ {
+ FREE((*ppDevProps)->DeviceDesc);
+ }
+
+ //
+ // The following are not necessary, but left in case
+ // in the future there is a later failure where these
+ // pointer fields would be allocated.
+ //
+
+ if ((*ppDevProps)->HwId != NULL)
+ {
+ FREE((*ppDevProps)->HwId);
+ }
+
+ if ((*ppDevProps)->Service != NULL)
+ {
+ FREE((*ppDevProps)->Service);
+ }
+
+ if ((*ppDevProps)->DeviceClass != NULL)
+ {
+ FREE((*ppDevProps)->DeviceClass);
+ }
+
+ if ((*ppDevProps)->PowerState != NULL)
+ {
+ FREE((*ppDevProps)->PowerState);
+ }
+
+ FREE(*ppDevProps);
+ *ppDevProps = NULL;
+}
diff --git a/device/win_usb/usbview/enum.c b/device/win_usb/usbview/enum.c
new file mode 100644
index 0000000..dc4dfbe
--- /dev/null
+++ b/device/win_usb/usbview/enum.c
@@ -0,0 +1,3857 @@
+/*++
+
+Copyright (c) 1997-2011 Microsoft Corporation
+
+Module Name:
+
+ ENUM.C
+
+Abstract:
+
+ This source file contains the routines which enumerate the USB bus
+ and populate the TreeView control.
+
+ The enumeration process goes like this:
+
+ (1) Enumerate Host Controllers and Root Hubs
+ EnumerateHostControllers()
+ EnumerateHostController()
+ Host controllers currently have symbolic link names of the form HCDx,
+ where x starts at 0. Use CreateFile() to open each host controller
+ symbolic link. Create a node in the TreeView to represent each host
+ controller.
+
+ GetRootHubName()
+ After a host controller has been opened, send the host controller an
+ IOCTL_USB_GET_ROOT_HUB_NAME request to get the symbolic link name of
+ the root hub that is part of the host controller.
+
+ (2) Enumerate Hubs (Root Hubs and External Hubs)
+ EnumerateHub()
+ Given the name of a hub, use CreateFile() to map the hub. Send the
+ hub an IOCTL_USB_GET_NODE_INFORMATION request to get info about the
+ hub, such as the number of downstream ports. Create a node in the
+ TreeView to represent each hub.
+
+ (3) Enumerate Downstream Ports
+ EnumerateHubPorts()
+ Given an handle to an open hub and the number of downstream ports on
+ the hub, send the hub an IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX
+ request for each downstream port of the hub to get info about the
+ device (if any) attached to each port. If there is a device attached
+ to a port, send the hub an IOCTL_USB_GET_NODE_CONNECTION_NAME request
+ to get the symbolic link name of the hub attached to the downstream
+ port. If there is a hub attached to the downstream port, recurse to
+ step (2).
+
+ GetAllStringDescriptors()
+ GetConfigDescriptor()
+ Create a node in the TreeView to represent each hub port
+ and attached device.
+
+
+Environment:
+
+ user mode
+
+Revision History:
+
+ 04-25-97 : created
+
+--*/
+
+//*****************************************************************************
+// I N C L U D E S
+//*****************************************************************************
+
+#include "enum.h"
+
+//*****************************************************************************
+// D E F I N E S
+//*****************************************************************************
+
+#define NUM_STRING_DESC_TO_GET 32
+
+//*****************************************************************************
+// L O C A L F U N C T I O N P R O T O T Y P E S
+//*****************************************************************************
+
+VOID
+EnumerateHostControllers (
+ HTREEITEM hTreeParent,
+ ULONG *DevicesConnected
+);
+
+VOID
+EnumerateHostController (
+ HTREEITEM hTreeParent,
+ HANDLE hHCDev,
+ _Inout_ PCHAR leafName,
+ _In_ HANDLE deviceInfo,
+ _In_ PSP_DEVINFO_DATA deviceInfoData
+);
+
+VOID
+EnumerateHub (
+ HTREEITEM hTreeParent,
+ _In_reads_(cbHubName) PCHAR HubName,
+ _In_ size_t cbHubName,
+ _In_opt_ PUSB_NODE_CONNECTION_INFORMATION_EX ConnectionInfo,
+ _In_opt_ PUSB_NODE_CONNECTION_INFORMATION_EX_V2 ConnectionInfoV2,
+ _In_opt_ PUSB_PORT_CONNECTOR_PROPERTIES PortConnectorProps,
+ _In_opt_ PUSB_DESCRIPTOR_REQUEST ConfigDesc,
+ _In_opt_ PUSB_DESCRIPTOR_REQUEST BosDesc,
+ _In_opt_ PSTRING_DESCRIPTOR_NODE StringDescs,
+ _In_opt_ PUSB_DEVICE_PNP_STRINGS DevProps
+);
+
+VOID
+EnumerateHubPorts (
+ HTREEITEM hTreeParent,
+ HANDLE hHubDevice,
+ ULONG NumPorts
+);
+
+PCHAR GetRootHubName (
+ HANDLE HostController
+);
+
+PCHAR GetExternalHubName (
+ HANDLE Hub,
+ ULONG ConnectionIndex
+);
+
+PCHAR GetHCDDriverKeyName (
+ HANDLE HCD
+);
+
+PCHAR GetDriverKeyName (
+ HANDLE Hub,
+ ULONG ConnectionIndex
+);
+
+PUSB_DESCRIPTOR_REQUEST
+GetConfigDescriptor (
+ HANDLE hHubDevice,
+ ULONG ConnectionIndex,
+ UCHAR DescriptorIndex
+ );
+
+PUSB_DESCRIPTOR_REQUEST
+GetBOSDescriptor (
+ HANDLE hHubDevice,
+ ULONG ConnectionIndex
+ );
+
+DWORD
+GetHostControllerPowerMap(
+ HANDLE hHCDev,
+ PUSBHOSTCONTROLLERINFO hcInfo);
+
+DWORD
+GetHostControllerInfo(
+ HANDLE hHCDev,
+ PUSBHOSTCONTROLLERINFO hcInfo);
+
+PCHAR WideStrToMultiStr (
+ _In_reads_bytes_(cbWideStr) PWCHAR WideStr,
+ _In_ size_t cbWideStr
+ );
+
+BOOL
+AreThereStringDescriptors (
+ PUSB_DEVICE_DESCRIPTOR DeviceDesc,
+ PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc
+);
+
+PSTRING_DESCRIPTOR_NODE
+GetAllStringDescriptors (
+ HANDLE hHubDevice,
+ ULONG ConnectionIndex,
+ PUSB_DEVICE_DESCRIPTOR DeviceDesc,
+ PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc
+);
+
+PSTRING_DESCRIPTOR_NODE
+GetStringDescriptor (
+ HANDLE hHubDevice,
+ ULONG ConnectionIndex,
+ UCHAR DescriptorIndex,
+ USHORT LanguageID
+);
+
+HRESULT
+GetStringDescriptors (
+ _In_ HANDLE hHubDevice,
+ _In_ ULONG ConnectionIndex,
+ _In_ UCHAR DescriptorIndex,
+ _In_ ULONG NumLanguageIDs,
+ _In_reads_(NumLanguageIDs) USHORT *LanguageIDs,
+ _In_ PSTRING_DESCRIPTOR_NODE StringDescNodeHead
+);
+
+void
+EnumerateAllDevices();
+
+
+void
+EnumerateAllDevicesWithGuid(
+ PDEVICE_GUID_LIST DeviceList,
+ LPGUID Guid
+ );
+
+void
+FreeDeviceInfoNode(
+ _In_ PDEVICE_INFO_NODE *ppNode
+ );
+
+PDEVICE_INFO_NODE
+FindMatchingDeviceNodeForDriverName(
+ _In_ PSTR DriverKeyName,
+ _In_ BOOLEAN IsHub
+ );
+
+
+//*****************************************************************************
+// G L O B A L S
+//*****************************************************************************
+
+// List of enumerated host controllers.
+//
+LIST_ENTRY EnumeratedHCListHead =
+{
+ &EnumeratedHCListHead,
+ &EnumeratedHCListHead
+};
+
+DEVICE_GUID_LIST gHubList;
+DEVICE_GUID_LIST gDeviceList;
+
+
+//*****************************************************************************
+// G L O B A L S P R I V A T E T O T H I S F I L E
+//*****************************************************************************
+
+PCHAR ConnectionStatuses[] =
+{
+ "", // 0 - NoDeviceConnected
+ "", // 1 - DeviceConnected
+ "FailedEnumeration", // 2 - DeviceFailedEnumeration
+ "GeneralFailure", // 3 - DeviceGeneralFailure
+ "Overcurrent", // 4 - DeviceCausedOvercurrent
+ "NotEnoughPower", // 5 - DeviceNotEnoughPower
+ "NotEnoughBandwidth", // 6 - DeviceNotEnoughBandwidth
+ "HubNestedTooDeeply", // 7 - DeviceHubNestedTooDeeply
+ "InLegacyHub", // 8 - DeviceInLegacyHub
+ "Enumerating", // 9 - DeviceEnumerating
+ "Reset" // 10 - DeviceReset
+};
+
+ULONG TotalDevicesConnected;
+
+
+//*****************************************************************************
+//
+// EnumerateHostControllers()
+//
+// hTreeParent - Handle of the TreeView item under which host controllers
+// should be added.
+//
+//*****************************************************************************
+
+VOID
+EnumerateHostControllers (
+ HTREEITEM hTreeParent,
+ ULONG *DevicesConnected
+)
+{
+ HANDLE hHCDev = NULL;
+ HDEVINFO deviceInfo = NULL;
+ SP_DEVINFO_DATA deviceInfoData;
+ SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
+ PSP_DEVICE_INTERFACE_DETAIL_DATA deviceDetailData = NULL;
+ ULONG index = 0;
+ ULONG requiredLength = 0;
+ BOOL success;
+
+ TotalDevicesConnected = 0;
+ TotalHubs = 0;
+
+ EnumerateAllDevices();
+
+ // Iterate over host controllers using the new GUID based interface
+ //
+ deviceInfo = SetupDiGetClassDevs((LPGUID)&GUID_CLASS_USB_HOST_CONTROLLER,
+ NULL,
+ NULL,
+ (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
+
+ deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
+
+ for (index=0;
+ SetupDiEnumDeviceInfo(deviceInfo,
+ index,
+ &deviceInfoData);
+ index++)
+ {
+ deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+
+ success = SetupDiEnumDeviceInterfaces(deviceInfo,
+ 0,
+ (LPGUID)&GUID_CLASS_USB_HOST_CONTROLLER,
+ index,
+ &deviceInterfaceData);
+
+ if (!success)
+ {
+ OOPS();
+ break;
+ }
+
+ success = SetupDiGetDeviceInterfaceDetail(deviceInfo,
+ &deviceInterfaceData,
+ NULL,
+ 0,
+ &requiredLength,
+ NULL);
+
+ if (!success && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ {
+ OOPS();
+ break;
+ }
+
+ deviceDetailData = ALLOC(requiredLength);
+ if (deviceDetailData == NULL)
+ {
+ OOPS();
+ break;
+ }
+
+ deviceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+
+ success = SetupDiGetDeviceInterfaceDetail(deviceInfo,
+ &deviceInterfaceData,
+ deviceDetailData,
+ requiredLength,
+ &requiredLength,
+ NULL);
+
+ if (!success)
+ {
+ OOPS();
+ break;
+ }
+
+ hHCDev = CreateFile(deviceDetailData->DevicePath,
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+
+ // If the handle is valid, then we've successfully opened a Host
+ // Controller. Display some info about the Host Controller itself,
+ // then enumerate the Root Hub attached to the Host Controller.
+ //
+ if (hHCDev != INVALID_HANDLE_VALUE)
+ {
+ EnumerateHostController(hTreeParent,
+ hHCDev,
+ deviceDetailData->DevicePath,
+ deviceInfo,
+ &deviceInfoData);
+
+ CloseHandle(hHCDev);
+ }
+
+ FREE(deviceDetailData);
+ }
+
+ SetupDiDestroyDeviceInfoList(deviceInfo);
+
+ *DevicesConnected = TotalDevicesConnected;
+
+ return;
+}
+
+//*****************************************************************************
+//
+// EnumerateHostController()
+//
+// hTreeParent - Handle of the TreeView item under which host controllers
+// should be added.
+//
+//*****************************************************************************
+
+VOID
+EnumerateHostController (
+ HTREEITEM hTreeParent,
+ HANDLE hHCDev, _Inout_ PCHAR leafName,
+ _In_ HANDLE deviceInfo,
+ _In_ PSP_DEVINFO_DATA deviceInfoData
+)
+{
+ PCHAR driverKeyName = NULL;
+ HTREEITEM hHCItem = NULL;
+ PCHAR rootHubName = NULL;
+ PLIST_ENTRY listEntry = NULL;
+ PUSBHOSTCONTROLLERINFO hcInfo = NULL;
+ PUSBHOSTCONTROLLERINFO hcInfoInList = NULL;
+ DWORD dwSuccess;
+ BOOL success = FALSE;
+ ULONG deviceAndFunction = 0;
+ PUSB_DEVICE_PNP_STRINGS DevProps = NULL;
+
+
+ // Allocate a structure to hold information about this host controller.
+ //
+ hcInfo = (PUSBHOSTCONTROLLERINFO)ALLOC(sizeof(USBHOSTCONTROLLERINFO));
+
+ // just return if could not alloc memory
+ if (NULL == hcInfo)
+ return;
+
+ hcInfo->DeviceInfoType = HostControllerInfo;
+
+ // Obtain the driver key name for this host controller.
+ //
+ driverKeyName = GetHCDDriverKeyName(hHCDev);
+
+ if (NULL == driverKeyName)
+ {
+ // Failure obtaining driver key name.
+ OOPS();
+ FREE(hcInfo);
+ return;
+ }
+
+ // Don't enumerate this host controller again if it already
+ // on the list of enumerated host controllers.
+ //
+ listEntry = EnumeratedHCListHead.Flink;
+
+ while (listEntry != &EnumeratedHCListHead)
+ {
+ hcInfoInList = CONTAINING_RECORD(listEntry,
+ USBHOSTCONTROLLERINFO,
+ ListEntry);
+
+ if (strcmp(driverKeyName, hcInfoInList->DriverKey) == 0)
+ {
+ // Already on the list, exit
+ //
+ FREE(driverKeyName);
+ FREE(hcInfo);
+ return;
+ }
+
+ listEntry = listEntry->Flink;
+ }
+
+ // Obtain host controller device properties
+ {
+ size_t cbDriverName = 0;
+ HRESULT hr = S_OK;
+
+ hr = StringCbLength(driverKeyName, MAX_DRIVER_KEY_NAME, &cbDriverName);
+ if (SUCCEEDED(hr))
+ {
+ DevProps = DriverNameToDeviceProperties(driverKeyName, cbDriverName);
+ }
+ }
+
+ hcInfo->DriverKey = driverKeyName;
+
+ if (DevProps)
+ {
+ ULONG ven, dev, subsys, rev;
+ ven = dev = subsys = rev = 0;
+
+ if (sscanf_s(DevProps->DeviceId,
+ "PCI\\VEN_%x&DEV_%x&SUBSYS_%x&REV_%x",
+ &ven, &dev, &subsys, &rev) != 4)
+ {
+ OOPS();
+ }
+
+ hcInfo->VendorID = ven;
+ hcInfo->DeviceID = dev;
+ hcInfo->SubSysID = subsys;
+ hcInfo->Revision = rev;
+ hcInfo->UsbDeviceProperties = DevProps;
+ }
+ else
+ {
+ OOPS();
+ }
+
+ if (DevProps != NULL && DevProps->DeviceDesc != NULL)
+ {
+ leafName = DevProps->DeviceDesc;
+ }
+ else
+ {
+ OOPS();
+ }
+
+ // Get the USB Host Controller power map
+ dwSuccess = GetHostControllerPowerMap(hHCDev, hcInfo);
+
+ if (ERROR_SUCCESS != dwSuccess)
+ {
+ OOPS();
+ }
+
+
+ // Get bus, device, and function
+ //
+ hcInfo->BusDeviceFunctionValid = FALSE;
+
+ success = SetupDiGetDeviceRegistryProperty(deviceInfo,
+ deviceInfoData,
+ SPDRP_BUSNUMBER,
+ NULL,
+ (PBYTE)&hcInfo->BusNumber,
+ sizeof(hcInfo->BusNumber),
+ NULL);
+
+ if (success)
+ {
+ success = SetupDiGetDeviceRegistryProperty(deviceInfo,
+ deviceInfoData,
+ SPDRP_ADDRESS,
+ NULL,
+ (PBYTE)&deviceAndFunction,
+ sizeof(deviceAndFunction),
+ NULL);
+ }
+
+ if (success)
+ {
+ hcInfo->BusDevice = deviceAndFunction >> 16;
+ hcInfo->BusFunction = deviceAndFunction & 0xffff;
+ hcInfo->BusDeviceFunctionValid = TRUE;
+ }
+
+ // Get the USB Host Controller info
+ dwSuccess = GetHostControllerInfo(hHCDev, hcInfo);
+
+ if (ERROR_SUCCESS != dwSuccess)
+ {
+ OOPS();
+ }
+
+ // Add this host controller to the USB device tree view.
+ //
+ //hHCItem = AddLeaf(hTreeParent,
+ // (LPARAM)hcInfo,
+ // leafName,
+ // hcInfo->Revision == UsbSuperSpeed ? GoodSsDeviceIcon : GoodDeviceIcon);
+
+ //if (NULL == hHCItem)
+ //{
+ // // Failure adding host controller to USB device tree
+ // // view.
+
+ // OOPS();
+ // FREE(driverKeyName);
+ // FREE(hcInfo);
+ // return;
+ //}
+
+ // Add this host controller to the list of enumerated
+ // host controllers.
+ //
+ InsertTailList(&EnumeratedHCListHead,
+ &hcInfo->ListEntry);
+
+ // Get the name of the root hub for this host
+ // controller and then enumerate the root hub.
+ //
+ rootHubName = GetRootHubName(hHCDev);
+
+ if (rootHubName != NULL)
+ {
+ size_t cbHubName = 0;
+ HRESULT hr = S_OK;
+
+ hr = StringCbLength(rootHubName, MAX_DRIVER_KEY_NAME, &cbHubName);
+ if (SUCCEEDED(hr))
+ {
+ EnumerateHub(hHCItem,
+ rootHubName,
+ cbHubName,
+ NULL, // ConnectionInfo
+ NULL, // ConnectionInfoV2
+ NULL, // PortConnectorProps
+ NULL, // ConfigDesc
+ NULL, // BosDesc
+ NULL, // StringDescs
+ NULL); // We do not pass DevProps for RootHub
+ }
+ }
+ else
+ {
+ // Failure obtaining root hub name.
+
+ OOPS();
+ }
+
+ return;
+}
+
+
+//*****************************************************************************
+//
+// EnumerateHub()
+//
+// hTreeParent - Handle of the TreeView item under which this hub should be
+// added.
+//
+// HubName - Name of this hub. This pointer is kept so the caller can neither
+// free nor reuse this memory.
+//
+// ConnectionInfo - NULL if this is a root hub, else this is the connection
+// info for an external hub. This pointer is kept so the caller can neither
+// free nor reuse this memory.
+//
+// ConfigDesc - NULL if this is a root hub, else this is the Configuration
+// Descriptor for an external hub. This pointer is kept so the caller can
+// neither free nor reuse this memory.
+//
+// StringDescs - NULL if this is a root hub.
+//
+// DevProps - Device properties of the hub
+//
+//*****************************************************************************
+
+VOID
+EnumerateHub (
+ HTREEITEM hTreeParent,
+ _In_reads_(cbHubName) PCHAR HubName,
+ _In_ size_t cbHubName,
+ _In_opt_ PUSB_NODE_CONNECTION_INFORMATION_EX ConnectionInfo,
+ _In_opt_ PUSB_NODE_CONNECTION_INFORMATION_EX_V2 ConnectionInfoV2,
+ _In_opt_ PUSB_PORT_CONNECTOR_PROPERTIES PortConnectorProps,
+ _In_opt_ PUSB_DESCRIPTOR_REQUEST ConfigDesc,
+ _In_opt_ PUSB_DESCRIPTOR_REQUEST BosDesc,
+ _In_opt_ PSTRING_DESCRIPTOR_NODE StringDescs,
+ _In_opt_ PUSB_DEVICE_PNP_STRINGS DevProps
+ )
+{
+ // Initialize locals to not allocated state so the error cleanup routine
+ // only tries to cleanup things that were successfully allocated.
+ //
+ PUSB_NODE_INFORMATION hubInfo = NULL;
+ PUSB_HUB_INFORMATION_EX hubInfoEx = NULL;
+ PUSB_HUB_CAPABILITIES_EX hubCapabilityEx = NULL;
+ HANDLE hHubDevice = INVALID_HANDLE_VALUE;
+ HTREEITEM hItem = NULL;
+ PVOID info = NULL;
+ PCHAR deviceName = NULL;
+ ULONG nBytes = 0;
+ BOOL success = 0;
+ DWORD dwSizeOfLeafName = 0;
+ CHAR leafName[512] = {0};
+ HRESULT hr = S_OK;
+ size_t cchHeader = 0;
+ size_t cchFullHubName = 0;
+
+ // Allocate some space for a USBDEVICEINFO structure to hold the
+ // hub info, hub name, and connection info pointers. GPTR zero
+ // initializes the structure for us.
+ //
+ info = ALLOC(sizeof(USBEXTERNALHUBINFO));
+ if (info == NULL)
+ {
+ OOPS();
+ goto EnumerateHubError;
+ }
+
+ // Allocate some space for a USB_NODE_INFORMATION structure for this Hub
+ //
+ hubInfo = (PUSB_NODE_INFORMATION)ALLOC(sizeof(USB_NODE_INFORMATION));
+ if (hubInfo == NULL)
+ {
+ OOPS();
+ goto EnumerateHubError;
+ }
+
+ hubInfoEx = (PUSB_HUB_INFORMATION_EX)ALLOC(sizeof(USB_HUB_INFORMATION_EX));
+ if (hubInfoEx == NULL)
+ {
+ OOPS();
+ goto EnumerateHubError;
+ }
+
+ hubCapabilityEx = (PUSB_HUB_CAPABILITIES_EX)ALLOC(sizeof(USB_HUB_CAPABILITIES_EX));
+ if(hubCapabilityEx == NULL)
+ {
+ OOPS();
+ goto EnumerateHubError;
+ }
+
+ // Keep copies of the Hub Name, Connection Info, and Configuration
+ // Descriptor pointers
+ //
+ ((PUSBROOTHUBINFO)info)->HubInfo = hubInfo;
+ ((PUSBROOTHUBINFO)info)->HubName = HubName;
+
+ if (ConnectionInfo != NULL)
+ {
+ ((PUSBEXTERNALHUBINFO)info)->DeviceInfoType = ExternalHubInfo;
+ ((PUSBEXTERNALHUBINFO)info)->ConnectionInfo = ConnectionInfo;
+ ((PUSBEXTERNALHUBINFO)info)->ConfigDesc = ConfigDesc;
+ ((PUSBEXTERNALHUBINFO)info)->StringDescs = StringDescs;
+ ((PUSBEXTERNALHUBINFO)info)->PortConnectorProps = PortConnectorProps;
+ ((PUSBEXTERNALHUBINFO)info)->HubInfoEx = hubInfoEx;
+ ((PUSBEXTERNALHUBINFO)info)->HubCapabilityEx = hubCapabilityEx;
+ ((PUSBEXTERNALHUBINFO)info)->BosDesc = BosDesc;
+ ((PUSBEXTERNALHUBINFO)info)->ConnectionInfoV2 = ConnectionInfoV2;
+ ((PUSBEXTERNALHUBINFO)info)->UsbDeviceProperties = DevProps;
+ }
+ else
+ {
+ ((PUSBROOTHUBINFO)info)->DeviceInfoType = RootHubInfo;
+ ((PUSBROOTHUBINFO)info)->HubInfoEx = hubInfoEx;
+ ((PUSBROOTHUBINFO)info)->HubCapabilityEx = hubCapabilityEx;
+ ((PUSBROOTHUBINFO)info)->PortConnectorProps = PortConnectorProps;
+ ((PUSBROOTHUBINFO)info)->UsbDeviceProperties = DevProps;
+ }
+
+ // Allocate a temp buffer for the full hub device name.
+ //
+ hr = StringCbLength("\\\\.\\", MAX_DEVICE_PROP, &cchHeader);
+ if (FAILED(hr))
+ {
+ goto EnumerateHubError;
+ }
+ cchFullHubName = cchHeader + cbHubName + 1;
+ deviceName = (PCHAR)ALLOC((DWORD) cchFullHubName);
+ if (deviceName == NULL)
+ {
+ OOPS();
+ goto EnumerateHubError;
+ }
+
+ // Create the full hub device name
+ //
+ hr = StringCchCopyN(deviceName, cchFullHubName, "\\\\.\\", cchHeader);
+ if (FAILED(hr))
+ {
+ goto EnumerateHubError;
+ }
+ hr = StringCchCatN(deviceName, cchFullHubName, HubName, cbHubName);
+ if (FAILED(hr))
+ {
+ goto EnumerateHubError;
+ }
+
+ // Try to hub the open device
+ //
+ hHubDevice = CreateFile(deviceName,
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+
+ // Done with temp buffer for full hub device name
+ //
+ FREE(deviceName);
+
+ if (hHubDevice == INVALID_HANDLE_VALUE)
+ {
+ OOPS();
+ goto EnumerateHubError;
+ }
+
+ //
+ // Now query USBHUB for the USB_NODE_INFORMATION structure for this hub.
+ // This will tell us the number of downstream ports to enumerate, among
+ // other things.
+ //
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_NODE_INFORMATION,
+ hubInfo,
+ sizeof(USB_NODE_INFORMATION),
+ hubInfo,
+ sizeof(USB_NODE_INFORMATION),
+ &nBytes,
+ NULL);
+
+ if (!success)
+ {
+ OOPS();
+ goto EnumerateHubError;
+ }
+
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_HUB_INFORMATION_EX,
+ hubInfoEx,
+ sizeof(USB_HUB_INFORMATION_EX),
+ hubInfoEx,
+ sizeof(USB_HUB_INFORMATION_EX),
+ &nBytes,
+ NULL);
+
+ //
+ // Fail gracefully for downlevel OS's from Win8
+ //
+ if (!success || nBytes < sizeof(USB_HUB_INFORMATION_EX))
+ {
+ FREE(hubInfoEx);
+ hubInfoEx = NULL;
+ if (ConnectionInfo != NULL)
+ {
+ ((PUSBEXTERNALHUBINFO)info)->HubInfoEx = NULL;
+ }
+ else
+ {
+ ((PUSBROOTHUBINFO)info)->HubInfoEx = NULL;
+ }
+ }
+
+ //
+ // Obtain Hub Capabilities
+ //
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_HUB_CAPABILITIES_EX,
+ hubCapabilityEx,
+ sizeof(USB_HUB_CAPABILITIES_EX),
+ hubCapabilityEx,
+ sizeof(USB_HUB_CAPABILITIES_EX),
+ &nBytes,
+ NULL);
+
+ //
+ // Fail gracefully
+ //
+ if (!success || nBytes < sizeof(USB_HUB_CAPABILITIES_EX))
+ {
+ FREE(hubCapabilityEx);
+ hubCapabilityEx = NULL;
+ if (ConnectionInfo != NULL)
+ {
+ ((PUSBEXTERNALHUBINFO)info)->HubCapabilityEx = NULL;
+ }
+ else
+ {
+ ((PUSBROOTHUBINFO)info)->HubCapabilityEx = NULL;
+ }
+ }
+
+ // Build the leaf name from the port number and the device description
+ //
+ dwSizeOfLeafName = sizeof(leafName);
+ if (ConnectionInfo)
+ {
+ StringCchPrintf(leafName, dwSizeOfLeafName, "[Port%d] ", ConnectionInfo->ConnectionIndex);
+ StringCchCat(leafName,
+ dwSizeOfLeafName,
+ ConnectionStatuses[ConnectionInfo->ConnectionStatus]);
+ StringCchCatN(leafName,
+ dwSizeOfLeafName,
+ " : ",
+ sizeof(" : "));
+ }
+
+ if (DevProps)
+ {
+ size_t cbDeviceDesc = 0;
+ hr = StringCbLength(DevProps->DeviceDesc, MAX_DRIVER_KEY_NAME, &cbDeviceDesc);
+ if(SUCCEEDED(hr))
+ {
+ StringCchCatN(leafName,
+ dwSizeOfLeafName,
+ DevProps->DeviceDesc,
+ cbDeviceDesc);
+ }
+ }
+ else
+ {
+ if(ConnectionInfo != NULL)
+ {
+ // External hub
+ StringCchCatN(leafName,
+ dwSizeOfLeafName,
+ HubName,
+ cbHubName);
+ }
+ else
+ {
+ // Root hub
+ StringCchCatN(leafName,
+ dwSizeOfLeafName,
+ "RootHub",
+ sizeof("RootHub"));
+ }
+ }
+
+ // Now add an item to the TreeView with the PUSBDEVICEINFO pointer info
+ // as the LPARAM reference value containing everything we know about the
+ // hub.
+ //
+ //hItem = AddLeaf(hTreeParent,
+ // (LPARAM)info,
+ // leafName,
+ // HubIcon);
+
+ //if (hItem == NULL)
+ //{
+ // OOPS();
+ // goto EnumerateHubError;
+ //}
+
+ // Now recursively enumerate the ports of this hub.
+ //
+ EnumerateHubPorts(
+ hItem,
+ hHubDevice,
+ hubInfo->u.HubInformation.HubDescriptor.bNumberOfPorts
+ );
+
+
+ CloseHandle(hHubDevice);
+ return;
+
+EnumerateHubError:
+ //
+ // Clean up any stuff that got allocated
+ //
+
+ if (hHubDevice != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(hHubDevice);
+ hHubDevice = INVALID_HANDLE_VALUE;
+ }
+
+ if (hubInfo)
+ {
+ FREE(hubInfo);
+ }
+
+ if (hubInfoEx)
+ {
+ FREE(hubInfoEx);
+ }
+
+ if (info)
+ {
+ FREE(info);
+ }
+
+ if (HubName)
+ {
+ FREE(HubName);
+ }
+
+ if (ConnectionInfo)
+ {
+ FREE(ConnectionInfo);
+ }
+
+ if (ConfigDesc)
+ {
+ FREE(ConfigDesc);
+ }
+
+ if (BosDesc)
+ {
+ FREE(BosDesc);
+ }
+
+ if (StringDescs != NULL)
+ {
+ PSTRING_DESCRIPTOR_NODE Next;
+
+ do {
+
+ Next = StringDescs->Next;
+ FREE(StringDescs);
+ StringDescs = Next;
+
+ } while (StringDescs != NULL);
+ }
+}
+
+//*****************************************************************************
+//
+// EnumerateHubPorts()
+//
+// hTreeParent - Handle of the TreeView item under which the hub port should
+// be added.
+//
+// hHubDevice - Handle of the hub device to enumerate.
+//
+// NumPorts - Number of ports on the hub.
+//
+//*****************************************************************************
+
+VOID
+EnumerateHubPorts (
+ HTREEITEM hTreeParent,
+ HANDLE hHubDevice,
+ ULONG NumPorts
+)
+{
+ ULONG index = 0;
+ BOOL success = 0;
+ HRESULT hr = S_OK;
+ PCHAR driverKeyName = NULL;
+ PUSB_DEVICE_PNP_STRINGS DevProps;
+ DWORD dwSizeOfLeafName = 0;
+ CHAR leafName[512];
+ int icon = 0;
+
+ PUSB_NODE_CONNECTION_INFORMATION_EX connectionInfoEx;
+ PUSB_PORT_CONNECTOR_PROPERTIES pPortConnectorProps;
+ USB_PORT_CONNECTOR_PROPERTIES portConnectorProps;
+ PUSB_DESCRIPTOR_REQUEST configDesc;
+ PUSB_DESCRIPTOR_REQUEST bosDesc;
+ PSTRING_DESCRIPTOR_NODE stringDescs;
+ PUSBDEVICEINFO info;
+ PUSB_NODE_CONNECTION_INFORMATION_EX_V2 connectionInfoExV2;
+ PDEVICE_INFO_NODE pNode;
+
+ // Loop over all ports of the hub.
+ //
+ // Port indices are 1 based, not 0 based.
+ //
+ for (index = 1; index <= NumPorts; index++)
+ {
+ ULONG nBytesEx;
+ ULONG nBytes = 0;
+
+ connectionInfoEx = NULL;
+ pPortConnectorProps = NULL;
+ ZeroMemory(&portConnectorProps, sizeof(portConnectorProps));
+ configDesc = NULL;
+ bosDesc = NULL;
+ stringDescs = NULL;
+ info = NULL;
+ connectionInfoExV2 = NULL;
+ pNode = NULL;
+ DevProps = NULL;
+ ZeroMemory(leafName, sizeof(leafName));
+
+ //
+ // Allocate space to hold the connection info for this port.
+ // For now, allocate it big enough to hold info for 30 pipes.
+ //
+ // Endpoint numbers are 0-15. Endpoint number 0 is the standard
+ // control endpoint which is not explicitly listed in the Configuration
+ // Descriptor. There can be an IN endpoint and an OUT endpoint at
+ // endpoint numbers 1-15 so there can be a maximum of 30 endpoints
+ // per device configuration.
+ //
+ // Should probably size this dynamically at some point.
+ //
+
+ nBytesEx = sizeof(USB_NODE_CONNECTION_INFORMATION_EX) +
+ (sizeof(USB_PIPE_INFO) * 30);
+
+ connectionInfoEx = (PUSB_NODE_CONNECTION_INFORMATION_EX)ALLOC(nBytesEx);
+
+ if (connectionInfoEx == NULL)
+ {
+ OOPS();
+ break;
+ }
+
+ connectionInfoExV2 = (PUSB_NODE_CONNECTION_INFORMATION_EX_V2)
+ ALLOC(sizeof(USB_NODE_CONNECTION_INFORMATION_EX_V2));
+
+ if (connectionInfoExV2 == NULL)
+ {
+ OOPS();
+ FREE(connectionInfoEx);
+ break;
+ }
+
+ //
+ // Now query USBHUB for the structures
+ // for this port. This will tell us if a device is attached to this
+ // port, among other things.
+ // The fault tolerate code is executed first.
+ //
+
+ portConnectorProps.ConnectionIndex = index;
+
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_PORT_CONNECTOR_PROPERTIES,
+ &portConnectorProps,
+ sizeof(USB_PORT_CONNECTOR_PROPERTIES),
+ &portConnectorProps,
+ sizeof(USB_PORT_CONNECTOR_PROPERTIES),
+ &nBytes,
+ NULL);
+
+ if (success && nBytes == sizeof(USB_PORT_CONNECTOR_PROPERTIES))
+ {
+ pPortConnectorProps = (PUSB_PORT_CONNECTOR_PROPERTIES)
+ ALLOC(portConnectorProps.ActualLength);
+
+ if (pPortConnectorProps != NULL)
+ {
+ pPortConnectorProps->ConnectionIndex = index;
+
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_PORT_CONNECTOR_PROPERTIES,
+ pPortConnectorProps,
+ portConnectorProps.ActualLength,
+ pPortConnectorProps,
+ portConnectorProps.ActualLength,
+ &nBytes,
+ NULL);
+
+ if (!success || nBytes < portConnectorProps.ActualLength)
+ {
+ FREE(pPortConnectorProps);
+ pPortConnectorProps = NULL;
+ }
+ }
+ }
+
+ connectionInfoExV2->ConnectionIndex = index;
+ connectionInfoExV2->Length = sizeof(USB_NODE_CONNECTION_INFORMATION_EX_V2);
+ connectionInfoExV2->SupportedUsbProtocols.Usb300 = 1;
+
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2,
+ connectionInfoExV2,
+ sizeof(USB_NODE_CONNECTION_INFORMATION_EX_V2),
+ connectionInfoExV2,
+ sizeof(USB_NODE_CONNECTION_INFORMATION_EX_V2),
+ &nBytes,
+ NULL);
+
+ if (!success || nBytes < sizeof(USB_NODE_CONNECTION_INFORMATION_EX_V2))
+ {
+ FREE(connectionInfoExV2);
+ connectionInfoExV2 = NULL;
+ }
+
+ connectionInfoEx->ConnectionIndex = index;
+
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
+ connectionInfoEx,
+ nBytesEx,
+ connectionInfoEx,
+ nBytesEx,
+ &nBytesEx,
+ NULL);
+
+ if (success)
+ {
+ //
+ // Since the USB_NODE_CONNECTION_INFORMATION_EX is used to display
+ // the device speed, but the hub driver doesn't support indication
+ // of superspeed, we overwrite the value if the super speed
+ // data structures are available and indicate the device is operating
+ // at SuperSpeed.
+ //
+
+ if (connectionInfoEx->Speed == UsbHighSpeed
+ && connectionInfoExV2 != NULL
+ && (connectionInfoExV2->Flags.DeviceIsOperatingAtSuperSpeedOrHigher ||
+ connectionInfoExV2->Flags.DeviceIsOperatingAtSuperSpeedPlusOrHigher))
+ {
+ connectionInfoEx->Speed = UsbSuperSpeed;
+ }
+ }
+ else
+ {
+ PUSB_NODE_CONNECTION_INFORMATION connectionInfo = NULL;
+
+ // Try using IOCTL_USB_GET_NODE_CONNECTION_INFORMATION
+ // instead of IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX
+ //
+
+ nBytes = sizeof(USB_NODE_CONNECTION_INFORMATION) +
+ sizeof(USB_PIPE_INFO) * 30;
+
+ connectionInfo = (PUSB_NODE_CONNECTION_INFORMATION)ALLOC(nBytes);
+
+ if (connectionInfo == NULL)
+ {
+ OOPS();
+
+ FREE(connectionInfoEx);
+ if (pPortConnectorProps != NULL)
+ {
+ FREE(pPortConnectorProps);
+ }
+ if (connectionInfoExV2 != NULL)
+ {
+ FREE(connectionInfoExV2);
+ }
+ continue;
+ }
+
+ connectionInfo->ConnectionIndex = index;
+
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_NODE_CONNECTION_INFORMATION,
+ connectionInfo,
+ nBytes,
+ connectionInfo,
+ nBytes,
+ &nBytes,
+ NULL);
+
+ if (!success)
+ {
+ OOPS();
+
+ FREE(connectionInfo);
+ FREE(connectionInfoEx);
+ if (pPortConnectorProps != NULL)
+ {
+ FREE(pPortConnectorProps);
+ }
+ if (connectionInfoExV2 != NULL)
+ {
+ FREE(connectionInfoExV2);
+ }
+ continue;
+ }
+
+ // Copy IOCTL_USB_GET_NODE_CONNECTION_INFORMATION into
+ // IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX structure.
+ //
+ connectionInfoEx->ConnectionIndex = connectionInfo->ConnectionIndex;
+ connectionInfoEx->DeviceDescriptor = connectionInfo->DeviceDescriptor;
+ connectionInfoEx->CurrentConfigurationValue = connectionInfo->CurrentConfigurationValue;
+ connectionInfoEx->Speed = connectionInfo->LowSpeed ? UsbLowSpeed : UsbFullSpeed;
+ connectionInfoEx->DeviceIsHub = connectionInfo->DeviceIsHub;
+ connectionInfoEx->DeviceAddress = connectionInfo->DeviceAddress;
+ connectionInfoEx->NumberOfOpenPipes = connectionInfo->NumberOfOpenPipes;
+ connectionInfoEx->ConnectionStatus = connectionInfo->ConnectionStatus;
+
+ memcpy(&connectionInfoEx->PipeList[0],
+ &connectionInfo->PipeList[0],
+ sizeof(USB_PIPE_INFO) * 30);
+
+ FREE(connectionInfo);
+ }
+
+ // Update the count of connected devices
+ //
+ if (connectionInfoEx->ConnectionStatus == DeviceConnected)
+ {
+ TotalDevicesConnected++;
+ }
+
+ if (connectionInfoEx->DeviceIsHub)
+ {
+ TotalHubs++;
+ }
+
+ // If there is a device connected, get the Device Description
+ //
+ if (connectionInfoEx->ConnectionStatus != NoDeviceConnected)
+ {
+ driverKeyName = GetDriverKeyName(hHubDevice, index);
+
+ if (driverKeyName)
+ {
+ size_t cbDriverName = 0;
+
+ hr = StringCbLength(driverKeyName, MAX_DRIVER_KEY_NAME, &cbDriverName);
+ if (SUCCEEDED(hr))
+ {
+ DevProps = DriverNameToDeviceProperties(driverKeyName, cbDriverName);
+ pNode = FindMatchingDeviceNodeForDriverName(driverKeyName, connectionInfoEx->DeviceIsHub);
+ }
+ FREE(driverKeyName);
+ }
+
+ }
+
+ // If there is a device connected to the port, try to retrieve the
+ // Configuration Descriptor from the device.
+ //
+ if (gDoConfigDesc &&
+ connectionInfoEx->ConnectionStatus == DeviceConnected)
+ {
+ configDesc = GetConfigDescriptor(hHubDevice,
+ index,
+ 0);
+ }
+ else
+ {
+ configDesc = NULL;
+ }
+
+ if (configDesc != NULL &&
+ connectionInfoEx->DeviceDescriptor.bcdUSB > 0x0200)
+ {
+ bosDesc = GetBOSDescriptor(hHubDevice,
+ index);
+ }
+ else
+ {
+ bosDesc = NULL;
+ }
+
+ if (configDesc != NULL &&
+ AreThereStringDescriptors(&connectionInfoEx->DeviceDescriptor,
+ (PUSB_CONFIGURATION_DESCRIPTOR)(configDesc+1)))
+ {
+ stringDescs = GetAllStringDescriptors (
+ hHubDevice,
+ index,
+ &connectionInfoEx->DeviceDescriptor,
+ (PUSB_CONFIGURATION_DESCRIPTOR)(configDesc+1));
+ }
+ else
+ {
+ stringDescs = NULL;
+ }
+
+ // If the device connected to the port is an external hub, get the
+ // name of the external hub and recursively enumerate it.
+ //
+ if (connectionInfoEx->DeviceIsHub)
+ {
+ PCHAR extHubName;
+ size_t cbHubName = 0;
+
+ extHubName = GetExternalHubName(hHubDevice, index);
+ if (extHubName != NULL)
+ {
+ hr = StringCbLength(extHubName, MAX_DRIVER_KEY_NAME, &cbHubName);
+ if (SUCCEEDED(hr))
+ {
+ EnumerateHub(hTreeParent, //hPortItem,
+ extHubName,
+ cbHubName,
+ connectionInfoEx,
+ connectionInfoExV2,
+ pPortConnectorProps,
+ configDesc,
+ bosDesc,
+ stringDescs,
+ DevProps);
+ }
+ }
+ }
+ else
+ {
+ // Allocate some space for a USBDEVICEINFO structure to hold the
+ // hub info, hub name, and connection info pointers. GPTR zero
+ // initializes the structure for us.
+ //
+ info = (PUSBDEVICEINFO) ALLOC(sizeof(USBDEVICEINFO));
+
+ if (info == NULL)
+ {
+ OOPS();
+ if (configDesc != NULL)
+ {
+ FREE(configDesc);
+ }
+ if (bosDesc != NULL)
+ {
+ FREE(bosDesc);
+ }
+ FREE(connectionInfoEx);
+
+ if (pPortConnectorProps != NULL)
+ {
+ FREE(pPortConnectorProps);
+ }
+ if (connectionInfoExV2 != NULL)
+ {
+ FREE(connectionInfoExV2);
+ }
+ break;
+ }
+
+ info->DeviceInfoType = DeviceInfo;
+ info->ConnectionInfo = connectionInfoEx;
+ info->PortConnectorProps = pPortConnectorProps;
+ info->ConfigDesc = configDesc;
+ info->StringDescs = stringDescs;
+ info->BosDesc = bosDesc;
+ info->ConnectionInfoV2 = connectionInfoExV2;
+ info->UsbDeviceProperties = DevProps;
+ info->DeviceInfoNode = pNode;
+
+ StringCchPrintf(leafName, sizeof(leafName), "[Port%d] ", index);
+
+ // Add error description if ConnectionStatus is other than NoDeviceConnected / DeviceConnected
+ StringCchCat(leafName,
+ sizeof(leafName),
+ ConnectionStatuses[connectionInfoEx->ConnectionStatus]);
+
+ if (DevProps)
+ {
+ size_t cchDeviceDesc = 0;
+
+ hr = StringCbLength(DevProps->DeviceDesc, MAX_DEVICE_PROP, &cchDeviceDesc);
+ if (FAILED(hr))
+ {
+ OOPS();
+ }
+ dwSizeOfLeafName = sizeof(leafName);
+ StringCchCatN(leafName,
+ dwSizeOfLeafName - 1,
+ " : ",
+ sizeof(" : "));
+ StringCchCatN(leafName,
+ dwSizeOfLeafName - 1,
+ DevProps->DeviceDesc,
+ cchDeviceDesc );
+ }
+
+ if (connectionInfoEx->ConnectionStatus == NoDeviceConnected)
+ {
+ if (connectionInfoExV2 != NULL &&
+ connectionInfoExV2->SupportedUsbProtocols.Usb300 == 1)
+ {
+ icon = NoSsDeviceIcon;
+ }
+ else
+ {
+ icon = NoDeviceIcon;
+ }
+ }
+ else if (connectionInfoEx->CurrentConfigurationValue)
+ {
+ if (connectionInfoEx->Speed == UsbSuperSpeed)
+ {
+ icon = GoodSsDeviceIcon;
+ }
+ else
+ {
+ icon = GoodDeviceIcon;
+ }
+ }
+ else
+ {
+ icon = BadDeviceIcon;
+ }
+
+ //AddLeaf(hTreeParent, //hPortItem,
+ // (LPARAM)info,
+ // leafName,
+ // icon);
+ }
+ } // for
+}
+
+
+//*****************************************************************************
+//
+// WideStrToMultiStr()
+//
+//*****************************************************************************
+
+PCHAR WideStrToMultiStr (
+ _In_reads_bytes_(cbWideStr) PWCHAR WideStr,
+ _In_ size_t cbWideStr
+ )
+{
+ ULONG nBytes = 0;
+ PCHAR MultiStr = NULL;
+ PWCHAR pWideStr = NULL;
+
+ // Use local string to guarantee zero termination
+ pWideStr = (PWCHAR) ALLOC((DWORD) cbWideStr + sizeof(WCHAR));
+ if (NULL == pWideStr)
+ {
+ return NULL;
+ }
+ memset(pWideStr, 0, cbWideStr + sizeof(WCHAR));
+ memcpy(pWideStr, WideStr, cbWideStr);
+
+ // Get the length of the converted string
+ //
+ nBytes = WideCharToMultiByte(
+ CP_ACP,
+ WC_NO_BEST_FIT_CHARS,
+ pWideStr,
+ -1,
+ NULL,
+ 0,
+ NULL,
+ NULL);
+
+ if (nBytes == 0)
+ {
+ FREE(pWideStr);
+ return NULL;
+ }
+
+ // Allocate space to hold the converted string
+ //
+ MultiStr = ALLOC(nBytes);
+ if (MultiStr == NULL)
+ {
+ FREE(pWideStr);
+ return NULL;
+ }
+
+ // Convert the string
+ //
+ nBytes = WideCharToMultiByte(
+ CP_ACP,
+ WC_NO_BEST_FIT_CHARS,
+ pWideStr,
+ -1,
+ MultiStr,
+ nBytes,
+ NULL,
+ NULL);
+
+ if (nBytes == 0)
+ {
+ FREE(MultiStr);
+ FREE(pWideStr);
+ return NULL;
+ }
+
+ FREE(pWideStr);
+ return MultiStr;
+}
+
+//*****************************************************************************
+//
+// GetRootHubName()
+//
+//*****************************************************************************
+
+PCHAR GetRootHubName (
+ HANDLE HostController
+)
+{
+ BOOL success = 0;
+ ULONG nBytes = 0;
+ USB_ROOT_HUB_NAME rootHubName;
+ PUSB_ROOT_HUB_NAME rootHubNameW = NULL;
+ PCHAR rootHubNameA = NULL;
+
+ // Get the length of the name of the Root Hub attached to the
+ // Host Controller
+ //
+ success = DeviceIoControl(HostController,
+ IOCTL_USB_GET_ROOT_HUB_NAME,
+ 0,
+ 0,
+ &rootHubName,
+ sizeof(rootHubName),
+ &nBytes,
+ NULL);
+
+ if (!success)
+ {
+ OOPS();
+ goto GetRootHubNameError;
+ }
+
+ // Allocate space to hold the Root Hub name
+ //
+ nBytes = rootHubName.ActualLength;
+
+ rootHubNameW = ALLOC(nBytes);
+ if (rootHubNameW == NULL)
+ {
+ OOPS();
+ goto GetRootHubNameError;
+ }
+
+ // Get the name of the Root Hub attached to the Host Controller
+ //
+ success = DeviceIoControl(HostController,
+ IOCTL_USB_GET_ROOT_HUB_NAME,
+ NULL,
+ 0,
+ rootHubNameW,
+ nBytes,
+ &nBytes,
+ NULL);
+ if (!success)
+ {
+ OOPS();
+ goto GetRootHubNameError;
+ }
+
+ // Convert the Root Hub name
+ //
+ rootHubNameA = WideStrToMultiStr(rootHubNameW->RootHubName, nBytes - sizeof(USB_ROOT_HUB_NAME) + sizeof(WCHAR));
+
+ // All done, free the uncoverted Root Hub name and return the
+ // converted Root Hub name
+ //
+ FREE(rootHubNameW);
+
+ return rootHubNameA;
+
+GetRootHubNameError:
+ // There was an error, free anything that was allocated
+ //
+ if (rootHubNameW != NULL)
+ {
+ FREE(rootHubNameW);
+ rootHubNameW = NULL;
+ }
+ return NULL;
+}
+
+
+//*****************************************************************************
+//
+// GetExternalHubName()
+//
+//*****************************************************************************
+
+PCHAR GetExternalHubName (
+ HANDLE Hub,
+ ULONG ConnectionIndex
+)
+{
+ BOOL success = 0;
+ ULONG nBytes = 0;
+ USB_NODE_CONNECTION_NAME extHubName;
+ PUSB_NODE_CONNECTION_NAME extHubNameW = NULL;
+ PCHAR extHubNameA = NULL;
+
+ // Get the length of the name of the external hub attached to the
+ // specified port.
+ //
+ extHubName.ConnectionIndex = ConnectionIndex;
+
+ success = DeviceIoControl(Hub,
+ IOCTL_USB_GET_NODE_CONNECTION_NAME,
+ &extHubName,
+ sizeof(extHubName),
+ &extHubName,
+ sizeof(extHubName),
+ &nBytes,
+ NULL);
+
+ if (!success)
+ {
+ OOPS();
+ goto GetExternalHubNameError;
+ }
+
+ // Allocate space to hold the external hub name
+ //
+ nBytes = extHubName.ActualLength;
+
+ if (nBytes <= sizeof(extHubName))
+ {
+ OOPS();
+ goto GetExternalHubNameError;
+ }
+
+ extHubNameW = ALLOC(nBytes);
+
+ if (extHubNameW == NULL)
+ {
+ OOPS();
+ goto GetExternalHubNameError;
+ }
+
+ // Get the name of the external hub attached to the specified port
+ //
+ extHubNameW->ConnectionIndex = ConnectionIndex;
+
+ success = DeviceIoControl(Hub,
+ IOCTL_USB_GET_NODE_CONNECTION_NAME,
+ extHubNameW,
+ nBytes,
+ extHubNameW,
+ nBytes,
+ &nBytes,
+ NULL);
+
+ if (!success)
+ {
+ OOPS();
+ goto GetExternalHubNameError;
+ }
+
+ // Convert the External Hub name
+ //
+ extHubNameA = WideStrToMultiStr(extHubNameW->NodeName, nBytes - sizeof(USB_NODE_CONNECTION_NAME) + sizeof(WCHAR));
+
+ // All done, free the uncoverted external hub name and return the
+ // converted external hub name
+ //
+ FREE(extHubNameW);
+
+ return extHubNameA;
+
+
+GetExternalHubNameError:
+ // There was an error, free anything that was allocated
+ //
+ if (extHubNameW != NULL)
+ {
+ FREE(extHubNameW);
+ extHubNameW = NULL;
+ }
+
+ return NULL;
+}
+
+
+//*****************************************************************************
+//
+// GetDriverKeyName()
+//
+//*****************************************************************************
+
+PCHAR GetDriverKeyName (
+ HANDLE Hub,
+ ULONG ConnectionIndex
+)
+{
+ BOOL success = 0;
+ ULONG nBytes = 0;
+ USB_NODE_CONNECTION_DRIVERKEY_NAME driverKeyName;
+ PUSB_NODE_CONNECTION_DRIVERKEY_NAME driverKeyNameW = NULL;
+ PCHAR driverKeyNameA = NULL;
+
+ // Get the length of the name of the driver key of the device attached to
+ // the specified port.
+ //
+ driverKeyName.ConnectionIndex = ConnectionIndex;
+
+ success = DeviceIoControl(Hub,
+ IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME,
+ &driverKeyName,
+ sizeof(driverKeyName),
+ &driverKeyName,
+ sizeof(driverKeyName),
+ &nBytes,
+ NULL);
+
+ if (!success)
+ {
+ OOPS();
+ goto GetDriverKeyNameError;
+ }
+
+ // Allocate space to hold the driver key name
+ //
+ nBytes = driverKeyName.ActualLength;
+
+ if (nBytes <= sizeof(driverKeyName))
+ {
+ OOPS();
+ goto GetDriverKeyNameError;
+ }
+
+ driverKeyNameW = ALLOC(nBytes);
+ if (driverKeyNameW == NULL)
+ {
+ OOPS();
+ goto GetDriverKeyNameError;
+ }
+
+ // Get the name of the driver key of the device attached to
+ // the specified port.
+ //
+ driverKeyNameW->ConnectionIndex = ConnectionIndex;
+
+ success = DeviceIoControl(Hub,
+ IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME,
+ driverKeyNameW,
+ nBytes,
+ driverKeyNameW,
+ nBytes,
+ &nBytes,
+ NULL);
+
+ if (!success)
+ {
+ OOPS();
+ goto GetDriverKeyNameError;
+ }
+
+ // Convert the driver key name
+ //
+ driverKeyNameA = WideStrToMultiStr(driverKeyNameW->DriverKeyName, nBytes - sizeof(USB_NODE_CONNECTION_DRIVERKEY_NAME) + sizeof(WCHAR));
+
+ // All done, free the uncoverted driver key name and return the
+ // converted driver key name
+ //
+ FREE(driverKeyNameW);
+
+ return driverKeyNameA;
+
+
+GetDriverKeyNameError:
+ // There was an error, free anything that was allocated
+ //
+ if (driverKeyNameW != NULL)
+ {
+ FREE(driverKeyNameW);
+ driverKeyNameW = NULL;
+ }
+
+ return NULL;
+}
+
+
+//*****************************************************************************
+//
+// GetHCDDriverKeyName()
+//
+//*****************************************************************************
+
+PCHAR GetHCDDriverKeyName (
+ HANDLE HCD
+)
+{
+ BOOL success = 0;
+ ULONG nBytes = 0;
+ USB_HCD_DRIVERKEY_NAME driverKeyName = {0};
+ PUSB_HCD_DRIVERKEY_NAME driverKeyNameW = NULL;
+ PCHAR driverKeyNameA = NULL;
+
+ ZeroMemory(&driverKeyName, sizeof(driverKeyName));
+
+ // Get the length of the name of the driver key of the HCD
+ //
+ success = DeviceIoControl(HCD,
+ IOCTL_GET_HCD_DRIVERKEY_NAME,
+ &driverKeyName,
+ sizeof(driverKeyName),
+ &driverKeyName,
+ sizeof(driverKeyName),
+ &nBytes,
+ NULL);
+
+ if (!success)
+ {
+ OOPS();
+ goto GetHCDDriverKeyNameError;
+ }
+
+ // Allocate space to hold the driver key name
+ //
+ nBytes = driverKeyName.ActualLength;
+ if (nBytes <= sizeof(driverKeyName))
+ {
+ OOPS();
+ goto GetHCDDriverKeyNameError;
+ }
+
+ driverKeyNameW = ALLOC(nBytes);
+ if (driverKeyNameW == NULL)
+ {
+ OOPS();
+ goto GetHCDDriverKeyNameError;
+ }
+
+ // Get the name of the driver key of the device attached to
+ // the specified port.
+ //
+
+ success = DeviceIoControl(HCD,
+ IOCTL_GET_HCD_DRIVERKEY_NAME,
+ driverKeyNameW,
+ nBytes,
+ driverKeyNameW,
+ nBytes,
+ &nBytes,
+ NULL);
+ if (!success)
+ {
+ OOPS();
+ goto GetHCDDriverKeyNameError;
+ }
+
+ //
+ // Convert the driver key name
+ // Pass the length of the DriverKeyName string
+ //
+
+ driverKeyNameA = WideStrToMultiStr(driverKeyNameW->DriverKeyName, nBytes - sizeof(USB_HCD_DRIVERKEY_NAME) + sizeof(WCHAR));
+
+ // All done, free the uncoverted driver key name and return the
+ // converted driver key name
+ //
+ FREE(driverKeyNameW);
+
+ return driverKeyNameA;
+
+GetHCDDriverKeyNameError:
+ // There was an error, free anything that was allocated
+ //
+ if (driverKeyNameW != NULL)
+ {
+ FREE(driverKeyNameW);
+ driverKeyNameW = NULL;
+ }
+
+ return NULL;
+}
+
+
+//*****************************************************************************
+//
+// GetConfigDescriptor()
+//
+// hHubDevice - Handle of the hub device containing the port from which the
+// Configuration Descriptor will be requested.
+//
+// ConnectionIndex - Identifies the port on the hub to which a device is
+// attached from which the Configuration Descriptor will be requested.
+//
+// DescriptorIndex - Configuration Descriptor index, zero based.
+//
+//*****************************************************************************
+
+PUSB_DESCRIPTOR_REQUEST
+GetConfigDescriptor (
+ HANDLE hHubDevice,
+ ULONG ConnectionIndex,
+ UCHAR DescriptorIndex
+)
+{
+ BOOL success = 0;
+ ULONG nBytes = 0;
+ ULONG nBytesReturned = 0;
+
+ UCHAR configDescReqBuf[sizeof(USB_DESCRIPTOR_REQUEST) +
+ sizeof(USB_CONFIGURATION_DESCRIPTOR)];
+
+ PUSB_DESCRIPTOR_REQUEST configDescReq = NULL;
+ PUSB_CONFIGURATION_DESCRIPTOR configDesc = NULL;
+
+
+ // Request the Configuration Descriptor the first time using our
+ // local buffer, which is just big enough for the Cofiguration
+ // Descriptor itself.
+ //
+ nBytes = sizeof(configDescReqBuf);
+
+ configDescReq = (PUSB_DESCRIPTOR_REQUEST)configDescReqBuf;
+ configDesc = (PUSB_CONFIGURATION_DESCRIPTOR)(configDescReq+1);
+
+ // Zero fill the entire request structure
+ //
+ memset(configDescReq, 0, nBytes);
+
+ // Indicate the port from which the descriptor will be requested
+ //
+ configDescReq->ConnectionIndex = ConnectionIndex;
+
+ //
+ // USBHUB uses URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE to process this
+ // IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION request.
+ //
+ // USBD will automatically initialize these fields:
+ // bmRequest = 0x80
+ // bRequest = 0x06
+ //
+ // We must inititialize these fields:
+ // wValue = Descriptor Type (high) and Descriptor Index (low byte)
+ // wIndex = Zero (or Language ID for String Descriptors)
+ // wLength = Length of descriptor buffer
+ //
+ configDescReq->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8)
+ | DescriptorIndex;
+
+ configDescReq->SetupPacket.wLength = (USHORT)(nBytes - sizeof(USB_DESCRIPTOR_REQUEST));
+
+ // Now issue the get descriptor request.
+ //
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
+ configDescReq,
+ nBytes,
+ configDescReq,
+ nBytes,
+ &nBytesReturned,
+ NULL);
+
+ if (!success)
+ {
+ OOPS();
+ return NULL;
+ }
+
+ if (nBytes != nBytesReturned)
+ {
+ OOPS();
+ return NULL;
+ }
+
+ if (configDesc->wTotalLength < sizeof(USB_CONFIGURATION_DESCRIPTOR))
+ {
+ OOPS();
+ return NULL;
+ }
+
+ // Now request the entire Configuration Descriptor using a dynamically
+ // allocated buffer which is sized big enough to hold the entire descriptor
+ //
+ nBytes = sizeof(USB_DESCRIPTOR_REQUEST) + configDesc->wTotalLength;
+
+ configDescReq = (PUSB_DESCRIPTOR_REQUEST)ALLOC(nBytes);
+
+ if (configDescReq == NULL)
+ {
+ OOPS();
+ return NULL;
+ }
+
+ configDesc = (PUSB_CONFIGURATION_DESCRIPTOR)(configDescReq+1);
+
+ // Indicate the port from which the descriptor will be requested
+ //
+ configDescReq->ConnectionIndex = ConnectionIndex;
+
+ //
+ // USBHUB uses URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE to process this
+ // IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION request.
+ //
+ // USBD will automatically initialize these fields:
+ // bmRequest = 0x80
+ // bRequest = 0x06
+ //
+ // We must inititialize these fields:
+ // wValue = Descriptor Type (high) and Descriptor Index (low byte)
+ // wIndex = Zero (or Language ID for String Descriptors)
+ // wLength = Length of descriptor buffer
+ //
+ configDescReq->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8)
+ | DescriptorIndex;
+
+ configDescReq->SetupPacket.wLength = (USHORT)(nBytes - sizeof(USB_DESCRIPTOR_REQUEST));
+
+ // Now issue the get descriptor request.
+ //
+
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
+ configDescReq,
+ nBytes,
+ configDescReq,
+ nBytes,
+ &nBytesReturned,
+ NULL);
+
+ if (!success)
+ {
+ OOPS();
+ FREE(configDescReq);
+ return NULL;
+ }
+
+ if (nBytes != nBytesReturned)
+ {
+ OOPS();
+ FREE(configDescReq);
+ return NULL;
+ }
+
+ if (configDesc->wTotalLength != (nBytes - sizeof(USB_DESCRIPTOR_REQUEST)))
+ {
+ OOPS();
+ FREE(configDescReq);
+ return NULL;
+ }
+
+ return configDescReq;
+}
+
+
+
+//*****************************************************************************
+//
+// GetBOSDescriptor()
+//
+// hHubDevice - Handle of the hub device containing the port from which the
+// Configuration Descriptor will be requested.
+//
+// ConnectionIndex - Identifies the port on the hub to which a device is
+// attached from which the BOS Descriptor will be requested.
+//
+//*****************************************************************************
+
+PUSB_DESCRIPTOR_REQUEST
+GetBOSDescriptor (
+ HANDLE hHubDevice,
+ ULONG ConnectionIndex
+)
+{
+ BOOL success = 0;
+ ULONG nBytes = 0;
+ ULONG nBytesReturned = 0;
+
+ UCHAR bosDescReqBuf[sizeof(USB_DESCRIPTOR_REQUEST) +
+ sizeof(USB_BOS_DESCRIPTOR)];
+
+ PUSB_DESCRIPTOR_REQUEST bosDescReq = NULL;
+ PUSB_BOS_DESCRIPTOR bosDesc = NULL;
+
+
+ // Request the BOS Descriptor the first time using our
+ // local buffer, which is just big enough for the BOS
+ // Descriptor itself.
+ //
+ nBytes = sizeof(bosDescReqBuf);
+
+ bosDescReq = (PUSB_DESCRIPTOR_REQUEST)bosDescReqBuf;
+ bosDesc = (PUSB_BOS_DESCRIPTOR)(bosDescReq+1);
+
+ // Zero fill the entire request structure
+ //
+ memset(bosDescReq, 0, nBytes);
+
+ // Indicate the port from which the descriptor will be requested
+ //
+ bosDescReq->ConnectionIndex = ConnectionIndex;
+
+ //
+ // USBHUB uses URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE to process this
+ // IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION request.
+ //
+ // USBD will automatically initialize these fields:
+ // bmRequest = 0x80
+ // bRequest = 0x06
+ //
+ // We must inititialize these fields:
+ // wValue = Descriptor Type (high) and Descriptor Index (low byte)
+ // wIndex = Zero (or Language ID for String Descriptors)
+ // wLength = Length of descriptor buffer
+ //
+ bosDescReq->SetupPacket.wValue = (USB_BOS_DESCRIPTOR_TYPE << 8);
+
+ bosDescReq->SetupPacket.wLength = (USHORT)(nBytes - sizeof(USB_DESCRIPTOR_REQUEST));
+
+ // Now issue the get descriptor request.
+ //
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
+ bosDescReq,
+ nBytes,
+ bosDescReq,
+ nBytes,
+ &nBytesReturned,
+ NULL);
+
+ if (!success)
+ {
+ OOPS();
+ return NULL;
+ }
+
+ if (nBytes != nBytesReturned)
+ {
+ OOPS();
+ return NULL;
+ }
+
+ if (bosDesc->wTotalLength < sizeof(USB_BOS_DESCRIPTOR))
+ {
+ OOPS();
+ return NULL;
+ }
+
+ // Now request the entire BOS Descriptor using a dynamically
+ // allocated buffer which is sized big enough to hold the entire descriptor
+ //
+ nBytes = sizeof(USB_DESCRIPTOR_REQUEST) + bosDesc->wTotalLength;
+
+ bosDescReq = (PUSB_DESCRIPTOR_REQUEST)ALLOC(nBytes);
+
+ if (bosDescReq == NULL)
+ {
+ OOPS();
+ return NULL;
+ }
+
+ bosDesc = (PUSB_BOS_DESCRIPTOR)(bosDescReq+1);
+
+ // Indicate the port from which the descriptor will be requested
+ //
+ bosDescReq->ConnectionIndex = ConnectionIndex;
+
+ //
+ // USBHUB uses URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE to process this
+ // IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION request.
+ //
+ // USBD will automatically initialize these fields:
+ // bmRequest = 0x80
+ // bRequest = 0x06
+ //
+ // We must inititialize these fields:
+ // wValue = Descriptor Type (high) and Descriptor Index (low byte)
+ // wIndex = Zero (or Language ID for String Descriptors)
+ // wLength = Length of descriptor buffer
+ //
+ bosDescReq->SetupPacket.wValue = (USB_BOS_DESCRIPTOR_TYPE << 8);
+
+ bosDescReq->SetupPacket.wLength = (USHORT)(nBytes - sizeof(USB_DESCRIPTOR_REQUEST));
+
+ // Now issue the get descriptor request.
+ //
+
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
+ bosDescReq,
+ nBytes,
+ bosDescReq,
+ nBytes,
+ &nBytesReturned,
+ NULL);
+
+ if (!success)
+ {
+ OOPS();
+ FREE(bosDescReq);
+ return NULL;
+ }
+
+ if (nBytes != nBytesReturned)
+ {
+ OOPS();
+ FREE(bosDescReq);
+ return NULL;
+ }
+
+ if (bosDesc->wTotalLength != (nBytes - sizeof(USB_DESCRIPTOR_REQUEST)))
+ {
+ OOPS();
+ FREE(bosDescReq);
+ return NULL;
+ }
+
+ return bosDescReq;
+}
+
+
+//*****************************************************************************
+//
+// AreThereStringDescriptors()
+//
+// DeviceDesc - Device Descriptor for which String Descriptors should be
+// checked.
+//
+// ConfigDesc - Configuration Descriptor (also containing Interface Descriptor)
+// for which String Descriptors should be checked.
+//
+//*****************************************************************************
+
+BOOL
+AreThereStringDescriptors (
+ PUSB_DEVICE_DESCRIPTOR DeviceDesc,
+ PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc
+)
+{
+ PUCHAR descEnd = NULL;
+ PUSB_COMMON_DESCRIPTOR commonDesc = NULL;
+
+ //
+ // Check Device Descriptor strings
+ //
+
+ if (DeviceDesc->iManufacturer ||
+ DeviceDesc->iProduct ||
+ DeviceDesc->iSerialNumber
+ )
+ {
+ return TRUE;
+ }
+
+
+ //
+ // Check the Configuration and Interface Descriptor strings
+ //
+
+ descEnd = (PUCHAR)ConfigDesc + ConfigDesc->wTotalLength;
+
+ commonDesc = (PUSB_COMMON_DESCRIPTOR)ConfigDesc;
+
+ while ((PUCHAR)commonDesc + sizeof(USB_COMMON_DESCRIPTOR) < descEnd &&
+ (PUCHAR)commonDesc + commonDesc->bLength <= descEnd)
+ {
+ switch (commonDesc->bDescriptorType)
+ {
+ case USB_CONFIGURATION_DESCRIPTOR_TYPE:
+ case USB_OTHER_SPEED_CONFIGURATION_DESCRIPTOR_TYPE:
+ if (commonDesc->bLength != sizeof(USB_CONFIGURATION_DESCRIPTOR))
+ {
+ OOPS();
+ break;
+ }
+ if (((PUSB_CONFIGURATION_DESCRIPTOR)commonDesc)->iConfiguration)
+ {
+ return TRUE;
+ }
+ commonDesc = (PUSB_COMMON_DESCRIPTOR) ((PUCHAR) commonDesc + commonDesc->bLength);
+ continue;
+
+ case USB_INTERFACE_DESCRIPTOR_TYPE:
+ if (commonDesc->bLength != sizeof(USB_INTERFACE_DESCRIPTOR) &&
+ commonDesc->bLength != sizeof(USB_INTERFACE_DESCRIPTOR2))
+ {
+ OOPS();
+ break;
+ }
+ if (((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->iInterface)
+ {
+ return TRUE;
+ }
+ commonDesc = (PUSB_COMMON_DESCRIPTOR) ((PUCHAR) commonDesc + commonDesc->bLength);
+ continue;
+
+ default:
+ commonDesc = (PUSB_COMMON_DESCRIPTOR) ((PUCHAR) commonDesc + commonDesc->bLength);
+ continue;
+ }
+ break;
+ }
+
+ return FALSE;
+}
+
+
+//*****************************************************************************
+//
+// GetAllStringDescriptors()
+//
+// hHubDevice - Handle of the hub device containing the port from which the
+// String Descriptors will be requested.
+//
+// ConnectionIndex - Identifies the port on the hub to which a device is
+// attached from which the String Descriptors will be requested.
+//
+// DeviceDesc - Device Descriptor for which String Descriptors should be
+// requested.
+//
+// ConfigDesc - Configuration Descriptor (also containing Interface Descriptor)
+// for which String Descriptors should be requested.
+//
+//*****************************************************************************
+
+PSTRING_DESCRIPTOR_NODE
+GetAllStringDescriptors (
+ HANDLE hHubDevice,
+ ULONG ConnectionIndex,
+ PUSB_DEVICE_DESCRIPTOR DeviceDesc,
+ PUSB_CONFIGURATION_DESCRIPTOR ConfigDesc
+)
+{
+ PSTRING_DESCRIPTOR_NODE supportedLanguagesString = NULL;
+ ULONG numLanguageIDs = 0;
+ USHORT *languageIDs = NULL;
+
+ PUCHAR descEnd = NULL;
+ PUSB_COMMON_DESCRIPTOR commonDesc = NULL;
+ UCHAR uIndex = 1;
+ UCHAR bInterfaceClass = 0;
+ BOOL getMoreStrings = FALSE;
+ HRESULT hr = S_OK;
+
+ //
+ // Get the array of supported Language IDs, which is returned
+ // in String Descriptor 0
+ //
+ supportedLanguagesString = GetStringDescriptor(hHubDevice,
+ ConnectionIndex,
+ 0,
+ 0);
+
+ if (supportedLanguagesString == NULL)
+ {
+ return NULL;
+ }
+
+ numLanguageIDs = (supportedLanguagesString->StringDescriptor->bLength - 2) / 2;
+
+ languageIDs = &supportedLanguagesString->StringDescriptor->bString[0];
+
+ //
+ // Get the Device Descriptor strings
+ //
+
+ if (DeviceDesc->iManufacturer)
+ {
+ GetStringDescriptors(hHubDevice,
+ ConnectionIndex,
+ DeviceDesc->iManufacturer,
+ numLanguageIDs,
+ languageIDs,
+ supportedLanguagesString);
+ }
+
+ if (DeviceDesc->iProduct)
+ {
+ GetStringDescriptors(hHubDevice,
+ ConnectionIndex,
+ DeviceDesc->iProduct,
+ numLanguageIDs,
+ languageIDs,
+ supportedLanguagesString);
+ }
+
+ if (DeviceDesc->iSerialNumber)
+ {
+ GetStringDescriptors(hHubDevice,
+ ConnectionIndex,
+ DeviceDesc->iSerialNumber,
+ numLanguageIDs,
+ languageIDs,
+ supportedLanguagesString);
+ }
+
+ //
+ // Get the Configuration and Interface Descriptor strings
+ //
+
+ descEnd = (PUCHAR)ConfigDesc + ConfigDesc->wTotalLength;
+
+ commonDesc = (PUSB_COMMON_DESCRIPTOR)ConfigDesc;
+
+ while ((PUCHAR)commonDesc + sizeof(USB_COMMON_DESCRIPTOR) < descEnd &&
+ (PUCHAR)commonDesc + commonDesc->bLength <= descEnd)
+ {
+ switch (commonDesc->bDescriptorType)
+ {
+ case USB_CONFIGURATION_DESCRIPTOR_TYPE:
+ if (commonDesc->bLength != sizeof(USB_CONFIGURATION_DESCRIPTOR))
+ {
+ OOPS();
+ break;
+ }
+ if (((PUSB_CONFIGURATION_DESCRIPTOR)commonDesc)->iConfiguration)
+ {
+ GetStringDescriptors(hHubDevice,
+ ConnectionIndex,
+ ((PUSB_CONFIGURATION_DESCRIPTOR)commonDesc)->iConfiguration,
+ numLanguageIDs,
+ languageIDs,
+ supportedLanguagesString);
+ }
+ commonDesc = (PUSB_COMMON_DESCRIPTOR) ((PUCHAR) commonDesc + commonDesc->bLength);
+ continue;
+
+ case USB_IAD_DESCRIPTOR_TYPE:
+ if (commonDesc->bLength < sizeof(USB_IAD_DESCRIPTOR))
+ {
+ OOPS();
+ break;
+ }
+ if (((PUSB_IAD_DESCRIPTOR)commonDesc)->iFunction)
+ {
+ GetStringDescriptors(hHubDevice,
+ ConnectionIndex,
+ ((PUSB_IAD_DESCRIPTOR)commonDesc)->iFunction,
+ numLanguageIDs,
+ languageIDs,
+ supportedLanguagesString);
+ }
+ commonDesc = (PUSB_COMMON_DESCRIPTOR) ((PUCHAR) commonDesc + commonDesc->bLength);
+ continue;
+
+ case USB_INTERFACE_DESCRIPTOR_TYPE:
+ if (commonDesc->bLength != sizeof(USB_INTERFACE_DESCRIPTOR) &&
+ commonDesc->bLength != sizeof(USB_INTERFACE_DESCRIPTOR2))
+ {
+ OOPS();
+ break;
+ }
+ if (((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->iInterface)
+ {
+ GetStringDescriptors(hHubDevice,
+ ConnectionIndex,
+ ((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->iInterface,
+ numLanguageIDs,
+ languageIDs,
+ supportedLanguagesString);
+ }
+
+ //
+ // We need to display more string descriptors for the following
+ // interface classes
+ //
+ bInterfaceClass = ((PUSB_INTERFACE_DESCRIPTOR)commonDesc)->bInterfaceClass;
+ if (bInterfaceClass == USB_DEVICE_CLASS_VIDEO)
+ {
+ getMoreStrings = TRUE;
+ }
+ commonDesc = (PUSB_COMMON_DESCRIPTOR) ((PUCHAR) commonDesc + commonDesc->bLength);
+ continue;
+
+ default:
+ commonDesc = (PUSB_COMMON_DESCRIPTOR) ((PUCHAR) commonDesc + commonDesc->bLength);
+ continue;
+ }
+ break;
+ }
+
+ if (getMoreStrings)
+ {
+ //
+ // We might need to display strings later that are referenced only in
+ // class-specific descriptors. Get String Descriptors 1 through 32 (an
+ // arbitrary upper limit for Strings needed due to "bad devices"
+ // returning an infinite repeat of Strings 0 through 4) until one is not
+ // found.
+ //
+ // There are also "bad devices" that have issues even querying 1-32, but
+ // historically USBView made this query, so the query should be safe for
+ // video devices.
+ //
+ for (uIndex = 1; SUCCEEDED(hr) && (uIndex < NUM_STRING_DESC_TO_GET); uIndex++)
+ {
+ hr = GetStringDescriptors(hHubDevice,
+ ConnectionIndex,
+ uIndex,
+ numLanguageIDs,
+ languageIDs,
+ supportedLanguagesString);
+ }
+ }
+
+ return supportedLanguagesString;
+}
+
+
+
+//*****************************************************************************
+//
+// GetStringDescriptor()
+//
+// hHubDevice - Handle of the hub device containing the port from which the
+// String Descriptor will be requested.
+//
+// ConnectionIndex - Identifies the port on the hub to which a device is
+// attached from which the String Descriptor will be requested.
+//
+// DescriptorIndex - String Descriptor index.
+//
+// LanguageID - Language in which the string should be requested.
+//
+//*****************************************************************************
+
+PSTRING_DESCRIPTOR_NODE
+GetStringDescriptor (
+ HANDLE hHubDevice,
+ ULONG ConnectionIndex,
+ UCHAR DescriptorIndex,
+ USHORT LanguageID
+)
+{
+ BOOL success = 0;
+ ULONG nBytes = 0;
+ ULONG nBytesReturned = 0;
+
+ UCHAR stringDescReqBuf[sizeof(USB_DESCRIPTOR_REQUEST) +
+ MAXIMUM_USB_STRING_LENGTH];
+
+ PUSB_DESCRIPTOR_REQUEST stringDescReq = NULL;
+ PUSB_STRING_DESCRIPTOR stringDesc = NULL;
+ PSTRING_DESCRIPTOR_NODE stringDescNode = NULL;
+
+ nBytes = sizeof(stringDescReqBuf);
+
+ stringDescReq = (PUSB_DESCRIPTOR_REQUEST)stringDescReqBuf;
+ stringDesc = (PUSB_STRING_DESCRIPTOR)(stringDescReq+1);
+
+ // Zero fill the entire request structure
+ //
+ memset(stringDescReq, 0, nBytes);
+
+ // Indicate the port from which the descriptor will be requested
+ //
+ stringDescReq->ConnectionIndex = ConnectionIndex;
+
+ //
+ // USBHUB uses URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE to process this
+ // IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION request.
+ //
+ // USBD will automatically initialize these fields:
+ // bmRequest = 0x80
+ // bRequest = 0x06
+ //
+ // We must inititialize these fields:
+ // wValue = Descriptor Type (high) and Descriptor Index (low byte)
+ // wIndex = Zero (or Language ID for String Descriptors)
+ // wLength = Length of descriptor buffer
+ //
+ stringDescReq->SetupPacket.wValue = (USB_STRING_DESCRIPTOR_TYPE << 8)
+ | DescriptorIndex;
+
+ stringDescReq->SetupPacket.wIndex = LanguageID;
+
+ stringDescReq->SetupPacket.wLength = (USHORT)(nBytes - sizeof(USB_DESCRIPTOR_REQUEST));
+
+ // Now issue the get descriptor request.
+ //
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
+ stringDescReq,
+ nBytes,
+ stringDescReq,
+ nBytes,
+ &nBytesReturned,
+ NULL);
+
+ //
+ // Do some sanity checks on the return from the get descriptor request.
+ //
+
+ if (!success)
+ {
+ OOPS();
+ return NULL;
+ }
+
+ if (nBytesReturned < 2)
+ {
+ OOPS();
+ return NULL;
+ }
+
+ if (stringDesc->bDescriptorType != USB_STRING_DESCRIPTOR_TYPE)
+ {
+ OOPS();
+ return NULL;
+ }
+
+ if (stringDesc->bLength != nBytesReturned - sizeof(USB_DESCRIPTOR_REQUEST))
+ {
+ OOPS();
+ return NULL;
+ }
+
+ if (stringDesc->bLength % 2 != 0)
+ {
+ OOPS();
+ return NULL;
+ }
+
+ //
+ // Looks good, allocate some (zero filled) space for the string descriptor
+ // node and copy the string descriptor to it.
+ //
+
+ stringDescNode = (PSTRING_DESCRIPTOR_NODE)ALLOC(sizeof(STRING_DESCRIPTOR_NODE) +
+ stringDesc->bLength);
+
+ if (stringDescNode == NULL)
+ {
+ OOPS();
+ return NULL;
+ }
+
+ stringDescNode->DescriptorIndex = DescriptorIndex;
+ stringDescNode->LanguageID = LanguageID;
+
+ memcpy(stringDescNode->StringDescriptor,
+ stringDesc,
+ stringDesc->bLength);
+
+ return stringDescNode;
+}
+
+
+//*****************************************************************************
+//
+// GetStringDescriptors()
+//
+// hHubDevice - Handle of the hub device containing the port from which the
+// String Descriptor will be requested.
+//
+// ConnectionIndex - Identifies the port on the hub to which a device is
+// attached from which the String Descriptor will be requested.
+//
+// DescriptorIndex - String Descriptor index.
+//
+// NumLanguageIDs - Number of languages in which the string should be
+// requested.
+//
+// LanguageIDs - Languages in which the string should be requested.
+//
+// StringDescNodeHead - First node in linked list of device's string descriptors
+//
+// Return Value: HRESULT indicating whether the string is on the list
+//
+//*****************************************************************************
+
+HRESULT
+GetStringDescriptors (
+ _In_ HANDLE hHubDevice,
+ _In_ ULONG ConnectionIndex,
+ _In_ UCHAR DescriptorIndex,
+ _In_ ULONG NumLanguageIDs,
+ _In_reads_(NumLanguageIDs) USHORT *LanguageIDs,
+ _In_ PSTRING_DESCRIPTOR_NODE StringDescNodeHead
+)
+{
+ PSTRING_DESCRIPTOR_NODE tail = NULL;
+ PSTRING_DESCRIPTOR_NODE trailing = NULL;
+ ULONG i = 0;
+
+ //
+ // Go to the end of the linked list, searching for the requested index to
+ // see if we've already retrieved it
+ //
+ for (tail = StringDescNodeHead; tail != NULL; tail = tail->Next)
+ {
+ if (tail->DescriptorIndex == DescriptorIndex)
+ {
+ return S_OK;
+ }
+
+ trailing = tail;
+ }
+
+ tail = trailing;
+
+ //
+ // Get the next String Descriptor. If this is NULL, then we're done (return)
+ // Otherwise, loop through all Language IDs
+ //
+ for (i = 0; (tail != NULL) && (i < NumLanguageIDs); i++)
+ {
+ tail->Next = GetStringDescriptor(hHubDevice,
+ ConnectionIndex,
+ DescriptorIndex,
+ LanguageIDs[i]);
+
+ tail = tail->Next;
+ }
+
+ if (tail == NULL)
+ {
+ return E_FAIL;
+ } else {
+ return S_OK;
+ }
+}
+
+
+//*****************************************************************************
+//
+// CleanupItem()
+//
+//*****************************************************************************
+
+VOID
+CleanupItem (
+ HWND hTreeWnd,
+ HTREEITEM hTreeItem,
+ PVOID pContext
+)
+{
+ TV_ITEM tvi;
+ PVOID info = NULL;
+
+ UNREFERENCED_PARAMETER(pContext);
+
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM;
+ tvi.hItem = hTreeItem;
+
+ TreeView_GetItem(hTreeWnd,
+ &tvi);
+
+ info = (PVOID)tvi.lParam;
+
+ if (info)
+ {
+ PCHAR DriverKey = NULL;
+ PUSB_NODE_INFORMATION HubInfo = NULL;
+ PCHAR HubName = NULL;
+ PUSB_NODE_CONNECTION_INFORMATION_EX ConnectionInfoEx = NULL;
+ PUSB_DESCRIPTOR_REQUEST ConfigDesc = NULL;
+ PUSB_DESCRIPTOR_REQUEST BosDesc = NULL;
+ PSTRING_DESCRIPTOR_NODE StringDescs = NULL;
+ PUSB_HUB_INFORMATION_EX HubInfoEx = NULL;
+ PUSB_PORT_CONNECTOR_PROPERTIES PortConnectorProps = NULL;
+ PUSB_NODE_CONNECTION_INFORMATION_EX_V2 ConnectionInfoV2 = NULL;
+ PUSB_HUB_CAPABILITIES_EX HubCapabilityEx = NULL;
+ PUSB_DEVICE_PNP_STRINGS UsbDeviceProperties = NULL;
+ PUSB_CONTROLLER_INFO_0 ControllerInfo = NULL;
+
+ //
+ // All structures except DEVICE_INFO_NODE are free'd up here. DEVICE_INFO_NODE structures are free'd while
+ // destroying device info lists (ClearDeviceList())
+ //
+ switch (*(PUSBDEVICEINFOTYPE)info)
+ {
+ case HostControllerInfo:
+ //
+ // Remove this host controller from the list of enumerated
+ // host controllers.
+ //
+ RemoveEntryList(&((PUSBHOSTCONTROLLERINFO)info)->ListEntry);
+ DriverKey = ((PUSBHOSTCONTROLLERINFO)info)->DriverKey;
+ ControllerInfo = ((PUSBHOSTCONTROLLERINFO)info)->ControllerInfo;
+ UsbDeviceProperties = ((PUSBHOSTCONTROLLERINFO)info)->UsbDeviceProperties;
+ break;
+
+ case RootHubInfo:
+ HubInfo = ((PUSBROOTHUBINFO)info)->HubInfo;
+ HubInfoEx = ((PUSBROOTHUBINFO)info)->HubInfoEx;
+ HubName = ((PUSBROOTHUBINFO)info)->HubName;
+ PortConnectorProps = ((PUSBROOTHUBINFO)info)->PortConnectorProps;
+ UsbDeviceProperties = ((PUSBROOTHUBINFO)info)->UsbDeviceProperties;
+ HubCapabilityEx = ((PUSBROOTHUBINFO)info)->HubCapabilityEx;
+ break;
+
+ case ExternalHubInfo:
+ HubInfo = ((PUSBEXTERNALHUBINFO)info)->HubInfo;
+ HubInfoEx = ((PUSBEXTERNALHUBINFO)info)->HubInfoEx;
+ HubName = ((PUSBEXTERNALHUBINFO)info)->HubName;
+ ConnectionInfoEx = ((PUSBEXTERNALHUBINFO)info)->ConnectionInfo;
+ PortConnectorProps = ((PUSBEXTERNALHUBINFO)info)->PortConnectorProps;
+ ConfigDesc = ((PUSBEXTERNALHUBINFO)info)->ConfigDesc;
+ BosDesc = ((PUSBEXTERNALHUBINFO)info)->BosDesc;
+ StringDescs = ((PUSBEXTERNALHUBINFO)info)->StringDescs;
+ ConnectionInfoV2 = ((PUSBEXTERNALHUBINFO)info)->ConnectionInfoV2;
+ UsbDeviceProperties = ((PUSBEXTERNALHUBINFO)info)->UsbDeviceProperties;
+ HubCapabilityEx = ((PUSBEXTERNALHUBINFO)info)->HubCapabilityEx;
+ break;
+
+ case DeviceInfo:
+ ConnectionInfoEx = ((PUSBDEVICEINFO)info)->ConnectionInfo;
+ PortConnectorProps = ((PUSBDEVICEINFO)info)->PortConnectorProps;
+ ConfigDesc = ((PUSBDEVICEINFO)info)->ConfigDesc;
+ BosDesc = ((PUSBDEVICEINFO)info)->BosDesc;
+ StringDescs = ((PUSBDEVICEINFO)info)->StringDescs;
+ ConnectionInfoV2 = ((PUSBDEVICEINFO)info)->ConnectionInfoV2;
+ UsbDeviceProperties = ((PUSBDEVICEINFO)info)->UsbDeviceProperties;
+ break;
+ }
+
+ if(UsbDeviceProperties)
+ {
+ FreeDeviceProperties(&UsbDeviceProperties);
+ }
+
+ if(ControllerInfo)
+ {
+ FREE(ControllerInfo);
+ }
+
+ if(HubCapabilityEx)
+ {
+ FREE(HubCapabilityEx);
+ }
+
+ if (DriverKey)
+ {
+ FREE(DriverKey);
+ }
+
+ if (HubInfo)
+ {
+ FREE(HubInfo);
+ }
+
+ if (HubName)
+ {
+ FREE(HubName);
+ }
+
+ if (ConfigDesc)
+ {
+ FREE(ConfigDesc);
+ }
+
+ if (BosDesc)
+ {
+ FREE(BosDesc);
+ }
+
+ if (StringDescs)
+ {
+ PSTRING_DESCRIPTOR_NODE Next;
+
+ do {
+
+ Next = StringDescs->Next;
+ FREE(StringDescs);
+ StringDescs = Next;
+
+ } while (StringDescs);
+ }
+
+ if (ConnectionInfoEx)
+ {
+ FREE(ConnectionInfoEx);
+ }
+
+ if (HubInfoEx)
+ {
+ FREE(HubInfoEx);
+ }
+
+ if (PortConnectorProps)
+ {
+ FREE(PortConnectorProps);
+ }
+
+ if (ConnectionInfoV2)
+ {
+ FREE(ConnectionInfoV2);
+ }
+
+ FREE(info);
+ }
+}
+
+//*****************************************************************************
+//
+// GetHostControllerPowerMap()
+//
+// HANDLE hHCDev
+// - handle to USB Host Controller
+//
+// PUSBHOSTCONTROLLERINFO hcInfo
+// - data structure to receive the Power Map Info
+//
+// return DWORD dwError
+// - return ERROR_SUCCESS or last error
+//
+//*****************************************************************************
+
+DWORD
+GetHostControllerPowerMap(
+ HANDLE hHCDev,
+ PUSBHOSTCONTROLLERINFO hcInfo)
+{
+ USBUSER_POWER_INFO_REQUEST UsbPowerInfoRequest;
+ PUSB_POWER_INFO pUPI = &UsbPowerInfoRequest.PowerInformation ;
+ DWORD dwError = 0;
+ DWORD dwBytes = 0;
+ BOOL bSuccess = FALSE;
+ int nIndex = 0;
+ int nPowerState = WdmUsbPowerSystemWorking;
+
+ for ( ; nPowerState <= WdmUsbPowerSystemShutdown; nIndex++, nPowerState++)
+ {
+ // zero initialize our request
+ memset(&UsbPowerInfoRequest, 0, sizeof(UsbPowerInfoRequest));
+
+ // set the header and request sizes
+ UsbPowerInfoRequest.Header.UsbUserRequest = USBUSER_GET_POWER_STATE_MAP;
+ UsbPowerInfoRequest.Header.RequestBufferLength = sizeof(UsbPowerInfoRequest);
+ UsbPowerInfoRequest.PowerInformation.SystemState = nPowerState;
+
+ //
+ // Now query USBHUB for the USB_POWER_INFO structure for this hub.
+ // For Selective Suspend support
+ //
+ bSuccess = DeviceIoControl(hHCDev,
+ IOCTL_USB_USER_REQUEST,
+ &UsbPowerInfoRequest,
+ sizeof(UsbPowerInfoRequest),
+ &UsbPowerInfoRequest,
+ sizeof(UsbPowerInfoRequest),
+ &dwBytes,
+ NULL);
+
+ if (!bSuccess)
+ {
+ dwError = GetLastError();
+ OOPS();
+ }
+ else
+ {
+ // copy the data into our USB Host Controller's info structure
+ memcpy( &(hcInfo->USBPowerInfo[nIndex]), pUPI, sizeof(USB_POWER_INFO));
+ }
+ }
+
+ return dwError;
+}
+
+void
+EnumerateAllDevices()
+{
+ EnumerateAllDevicesWithGuid(&gDeviceList,
+ (LPGUID)&GUID_DEVINTERFACE_USB_DEVICE);
+
+ EnumerateAllDevicesWithGuid(&gHubList,
+ (LPGUID)&GUID_DEVINTERFACE_USB_HUB);
+}
+
+
+//*****************************************************************************
+//
+// GetHostControllerInfo()
+//
+// HANDLE hHCDev
+// - handle to USB Host Controller
+//
+// PUSBHOSTCONTROLLERINFO hcInfo
+// - data structure to receive the Power Map Info
+//
+// return DWORD dwError
+// - return ERROR_SUCCESS or last error
+//
+//*****************************************************************************
+
+DWORD
+GetHostControllerInfo(
+ HANDLE hHCDev,
+ PUSBHOSTCONTROLLERINFO hcInfo)
+{
+ USBUSER_CONTROLLER_INFO_0 UsbControllerInfo;
+ DWORD dwError = 0;
+ DWORD dwBytes = 0;
+ BOOL bSuccess = FALSE;
+
+ memset(&UsbControllerInfo, 0, sizeof(UsbControllerInfo));
+
+ // set the header and request sizes
+ UsbControllerInfo.Header.UsbUserRequest = USBUSER_GET_CONTROLLER_INFO_0;
+ UsbControllerInfo.Header.RequestBufferLength = sizeof(UsbControllerInfo);
+
+ //
+ // Query for the USB_CONTROLLER_INFO_0 structure
+ //
+ bSuccess = DeviceIoControl(hHCDev,
+ IOCTL_USB_USER_REQUEST,
+ &UsbControllerInfo,
+ sizeof(UsbControllerInfo),
+ &UsbControllerInfo,
+ sizeof(UsbControllerInfo),
+ &dwBytes,
+ NULL);
+
+ if (!bSuccess)
+ {
+ dwError = GetLastError();
+ OOPS();
+ }
+ else
+ {
+ hcInfo->ControllerInfo = (PUSB_CONTROLLER_INFO_0) ALLOC(sizeof(USB_CONTROLLER_INFO_0));
+ if(NULL == hcInfo->ControllerInfo)
+ {
+ dwError = GetLastError();
+ OOPS();
+ }
+ else
+ {
+ // copy the data into our USB Host Controller's info structure
+ memcpy(hcInfo->ControllerInfo, &UsbControllerInfo.Info0, sizeof(USB_CONTROLLER_INFO_0));
+ }
+ }
+ return dwError;
+}
+
+_Success_(return == TRUE)
+BOOL
+GetDeviceProperty(
+ _In_ HDEVINFO DeviceInfoSet,
+ _In_ PSP_DEVINFO_DATA DeviceInfoData,
+ _In_ DWORD Property,
+ _Outptr_ LPTSTR *ppBuffer
+ )
+{
+ BOOL bResult;
+ DWORD requiredLength = 0;
+ DWORD lastError;
+
+ if (ppBuffer == NULL)
+ {
+ return FALSE;
+ }
+
+ *ppBuffer = NULL;
+
+ bResult = SetupDiGetDeviceRegistryPropertyA(DeviceInfoSet,
+ DeviceInfoData,
+ Property ,
+ NULL,
+ NULL,
+ 0,
+ &requiredLength);
+ lastError = GetLastError();
+
+ if ((requiredLength == 0) || (bResult != FALSE && lastError != ERROR_INSUFFICIENT_BUFFER))
+ {
+ return FALSE;
+ }
+
+ requiredLength *= 4;
+ *ppBuffer = ALLOC(requiredLength);
+
+ if (*ppBuffer == NULL)
+ {
+ return FALSE;
+ }
+
+ bResult = SetupDiGetDeviceRegistryPropertyA(DeviceInfoSet,
+ DeviceInfoData,
+ Property ,
+ NULL,
+ (PBYTE) *ppBuffer,
+ requiredLength,
+ &requiredLength);
+ if(bResult == FALSE)
+ {
+ FREE(*ppBuffer);
+ *ppBuffer = NULL;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+void
+EnumerateAllDevicesWithGuid(
+ PDEVICE_GUID_LIST DeviceList,
+ LPGUID Guid
+ )
+{
+ if (DeviceList->DeviceInfo != INVALID_HANDLE_VALUE)
+ {
+ ClearDeviceList(DeviceList);
+ }
+
+ DeviceList->DeviceInfo = SetupDiGetClassDevs(Guid,
+ NULL,
+ NULL,
+ (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
+
+ if (DeviceList->DeviceInfo != INVALID_HANDLE_VALUE)
+ {
+ ULONG index;
+ DWORD error;
+
+ error = 0;
+ index = 0;
+
+ while (error != ERROR_NO_MORE_ITEMS)
+ {
+ BOOL success;
+ PDEVICE_INFO_NODE pNode;
+
+ pNode = ALLOC(sizeof(DEVICE_INFO_NODE));
+ if (pNode == NULL)
+ {
+ OOPS();
+ break;
+ }
+ pNode->DeviceInfo = DeviceList->DeviceInfo;
+ pNode->DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ pNode->DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
+
+ success = SetupDiEnumDeviceInfo(DeviceList->DeviceInfo,
+ index,
+ &pNode->DeviceInfoData);
+
+ index++;
+
+ if (success == FALSE)
+ {
+ error = GetLastError();
+
+ if (error != ERROR_NO_MORE_ITEMS)
+ {
+ OOPS();
+ }
+
+ FreeDeviceInfoNode(&pNode);
+ }
+ else
+ {
+ BOOL bResult;
+ ULONG requiredLength;
+
+ bResult = GetDeviceProperty(DeviceList->DeviceInfo,
+ &pNode->DeviceInfoData,
+ SPDRP_DEVICEDESC,
+ &pNode->DeviceDescName);
+ if (bResult == FALSE)
+ {
+ FreeDeviceInfoNode(&pNode);
+ OOPS();
+ break;
+ }
+
+ bResult = GetDeviceProperty(DeviceList->DeviceInfo,
+ &pNode->DeviceInfoData,
+ SPDRP_DRIVER,
+ &pNode->DeviceDriverName);
+ if (bResult == FALSE)
+ {
+ FreeDeviceInfoNode(&pNode);
+ OOPS();
+ break;
+ }
+
+ pNode->DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+
+ success = SetupDiEnumDeviceInterfaces(DeviceList->DeviceInfo,
+ 0,
+ Guid,
+ index-1,
+ &pNode->DeviceInterfaceData);
+ if (!success)
+ {
+ FreeDeviceInfoNode(&pNode);
+ OOPS();
+ break;
+ }
+
+ success = SetupDiGetDeviceInterfaceDetail(DeviceList->DeviceInfo,
+ &pNode->DeviceInterfaceData,
+ NULL,
+ 0,
+ &requiredLength,
+ NULL);
+
+ error = GetLastError();
+
+ if (!success && error != ERROR_INSUFFICIENT_BUFFER)
+ {
+ FreeDeviceInfoNode(&pNode);
+ OOPS();
+ break;
+ }
+
+ pNode->DeviceDetailData = ALLOC(requiredLength);
+
+ if (pNode->DeviceDetailData == NULL)
+ {
+ FreeDeviceInfoNode(&pNode);
+ OOPS();
+ break;
+ }
+
+ pNode->DeviceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+
+ success = SetupDiGetDeviceInterfaceDetail(DeviceList->DeviceInfo,
+ &pNode->DeviceInterfaceData,
+ pNode->DeviceDetailData,
+ requiredLength,
+ &requiredLength,
+ NULL);
+ if (!success)
+ {
+ FreeDeviceInfoNode(&pNode);
+ OOPS();
+ break;
+ }
+
+ InsertTailList(&DeviceList->ListHead, &pNode->ListEntry);
+ }
+ }
+ }
+}
+
+DEVICE_POWER_STATE
+AcquireDevicePowerState(
+ _Inout_ PDEVICE_INFO_NODE pNode
+ )
+{
+ CM_POWER_DATA cmPowerData = {0};
+ BOOL bResult;
+
+ bResult = SetupDiGetDeviceRegistryProperty(pNode->DeviceInfo,
+ &pNode->DeviceInfoData,
+ SPDRP_DEVICE_POWER_DATA,
+ NULL,
+ (PBYTE)&cmPowerData,
+ sizeof(cmPowerData),
+ NULL);
+
+ pNode->LatestDevicePowerState = bResult ? cmPowerData.PD_MostRecentPowerState : PowerDeviceUnspecified;
+
+ return pNode->LatestDevicePowerState;
+}
+
+
+void
+ClearDeviceList(
+ PDEVICE_GUID_LIST DeviceList
+ )
+{
+ if (DeviceList->DeviceInfo != INVALID_HANDLE_VALUE)
+ {
+ SetupDiDestroyDeviceInfoList(DeviceList->DeviceInfo);
+ DeviceList->DeviceInfo = INVALID_HANDLE_VALUE;
+ }
+
+ while (!IsListEmpty(&DeviceList->ListHead))
+ {
+ PDEVICE_INFO_NODE pNode = NULL;
+ PLIST_ENTRY pEntry;
+
+ pEntry = RemoveHeadList(&DeviceList->ListHead);
+
+ pNode = CONTAINING_RECORD(pEntry,
+ DEVICE_INFO_NODE,
+ ListEntry);
+
+ FreeDeviceInfoNode(&pNode);
+ }
+}
+
+VOID
+FreeDeviceInfoNode(
+ _In_ PDEVICE_INFO_NODE *ppNode
+ )
+{
+ if (ppNode == NULL)
+ {
+ return;
+ }
+
+ if (*ppNode == NULL)
+ {
+ return;
+ }
+
+ if ((*ppNode)->DeviceDetailData != NULL)
+ {
+ FREE((*ppNode)->DeviceDetailData);
+ }
+
+ if ((*ppNode)->DeviceDescName != NULL)
+ {
+ FREE((*ppNode)->DeviceDescName);
+ }
+
+ if ((*ppNode)->DeviceDriverName != NULL)
+ {
+ FREE((*ppNode)->DeviceDriverName);
+ }
+
+ FREE(*ppNode);
+ *ppNode = NULL;
+}
+
+PDEVICE_INFO_NODE
+FindMatchingDeviceNodeForDriverName(
+ _In_ PSTR DriverKeyName,
+ _In_ BOOLEAN IsHub
+ )
+{
+ PDEVICE_INFO_NODE pNode = NULL;
+ PDEVICE_GUID_LIST pList = NULL;
+ PLIST_ENTRY pEntry = NULL;
+
+ pList = IsHub ? &gHubList : &gDeviceList;
+
+ pEntry = pList->ListHead.Flink;
+
+ while (pEntry && pEntry != &pList->ListHead)
+ {
+ pNode = CONTAINING_RECORD(pEntry,
+ DEVICE_INFO_NODE,
+ ListEntry);
+ if (_stricmp(DriverKeyName, pNode->DeviceDriverName) == 0)
+ {
+ return pNode;
+ }
+
+ pEntry = pEntry->Flink;
+ }
+
+ return NULL;
+}
+
+#ifdef CUSTOM_USBVIEW
+
+PUSBDEVICEINFO
+enumerate_hub_port(HANDLE hHubDevice, ULONG port_index)
+{
+ ULONG index = port_index;
+ BOOL success = 0;
+ HRESULT hr = S_OK;
+ PCHAR driverKeyName = NULL;
+ PUSB_DEVICE_PNP_STRINGS DevProps;
+ DWORD dwSizeOfLeafName = 0;
+ CHAR leafName[512];
+ int icon = 0;
+
+ PUSB_NODE_CONNECTION_INFORMATION_EX connectionInfoEx;
+ PUSB_PORT_CONNECTOR_PROPERTIES pPortConnectorProps;
+ USB_PORT_CONNECTOR_PROPERTIES portConnectorProps;
+ PUSB_DESCRIPTOR_REQUEST configDesc;
+ PUSB_DESCRIPTOR_REQUEST bosDesc;
+ PSTRING_DESCRIPTOR_NODE stringDescs;
+ PUSBDEVICEINFO info;
+ PUSB_NODE_CONNECTION_INFORMATION_EX_V2 connectionInfoExV2;
+ PDEVICE_INFO_NODE pNode;
+
+ // Loop over all ports of the hub.
+ //
+ // Port indices are 1 based, not 0 based.
+ //
+ //for (index = 1; index <= NumPorts; index++)
+ {
+ ULONG nBytesEx;
+ ULONG nBytes = 0;
+
+ connectionInfoEx = NULL;
+ pPortConnectorProps = NULL;
+ ZeroMemory(&portConnectorProps, sizeof(portConnectorProps));
+ configDesc = NULL;
+ bosDesc = NULL;
+ stringDescs = NULL;
+ info = NULL;
+ connectionInfoExV2 = NULL;
+ pNode = NULL;
+ DevProps = NULL;
+ ZeroMemory(leafName, sizeof(leafName));
+
+ //
+ // Allocate space to hold the connection info for this port.
+ // For now, allocate it big enough to hold info for 30 pipes.
+ //
+ // Endpoint numbers are 0-15. Endpoint number 0 is the standard
+ // control endpoint which is not explicitly listed in the Configuration
+ // Descriptor. There can be an IN endpoint and an OUT endpoint at
+ // endpoint numbers 1-15 so there can be a maximum of 30 endpoints
+ // per device configuration.
+ //
+ // Should probably size this dynamically at some point.
+ //
+
+ nBytesEx = sizeof(USB_NODE_CONNECTION_INFORMATION_EX) +
+ (sizeof(USB_PIPE_INFO) * 30);
+
+ connectionInfoEx = (PUSB_NODE_CONNECTION_INFORMATION_EX)ALLOC(nBytesEx);
+
+ if (connectionInfoEx == NULL)
+ {
+ OOPS();
+
+ return NULL;
+ }
+
+ connectionInfoExV2 = (PUSB_NODE_CONNECTION_INFORMATION_EX_V2)
+ ALLOC(sizeof(USB_NODE_CONNECTION_INFORMATION_EX_V2));
+
+ if (connectionInfoExV2 == NULL)
+ {
+ OOPS();
+ FREE(connectionInfoEx);
+ return NULL;
+ }
+
+ //
+ // Now query USBHUB for the structures
+ // for this port. This will tell us if a device is attached to this
+ // port, among other things.
+ // The fault tolerate code is executed first.
+ //
+
+ portConnectorProps.ConnectionIndex = index;
+
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_PORT_CONNECTOR_PROPERTIES,
+ &portConnectorProps,
+ sizeof(USB_PORT_CONNECTOR_PROPERTIES),
+ &portConnectorProps,
+ sizeof(USB_PORT_CONNECTOR_PROPERTIES),
+ &nBytes,
+ NULL);
+
+ if (success && nBytes == sizeof(USB_PORT_CONNECTOR_PROPERTIES))
+ {
+ pPortConnectorProps = (PUSB_PORT_CONNECTOR_PROPERTIES)
+ ALLOC(portConnectorProps.ActualLength);
+
+ if (pPortConnectorProps != NULL)
+ {
+ pPortConnectorProps->ConnectionIndex = index;
+
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_PORT_CONNECTOR_PROPERTIES,
+ pPortConnectorProps,
+ portConnectorProps.ActualLength,
+ pPortConnectorProps,
+ portConnectorProps.ActualLength,
+ &nBytes,
+ NULL);
+
+ if (!success || nBytes < portConnectorProps.ActualLength)
+ {
+ FREE(pPortConnectorProps);
+ pPortConnectorProps = NULL;
+ }
+ }
+ }
+
+ connectionInfoExV2->ConnectionIndex = index;
+ connectionInfoExV2->Length = sizeof(USB_NODE_CONNECTION_INFORMATION_EX_V2);
+ connectionInfoExV2->SupportedUsbProtocols.Usb300 = 1;
+
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2,
+ connectionInfoExV2,
+ sizeof(USB_NODE_CONNECTION_INFORMATION_EX_V2),
+ connectionInfoExV2,
+ sizeof(USB_NODE_CONNECTION_INFORMATION_EX_V2),
+ &nBytes,
+ NULL);
+
+ if (!success || nBytes < sizeof(USB_NODE_CONNECTION_INFORMATION_EX_V2))
+ {
+ FREE(connectionInfoExV2);
+ connectionInfoExV2 = NULL;
+ }
+
+ connectionInfoEx->ConnectionIndex = index;
+
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
+ connectionInfoEx,
+ nBytesEx,
+ connectionInfoEx,
+ nBytesEx,
+ &nBytesEx,
+ NULL);
+
+ if (success)
+ {
+ //
+ // Since the USB_NODE_CONNECTION_INFORMATION_EX is used to display
+ // the device speed, but the hub driver doesn't support indication
+ // of superspeed, we overwrite the value if the super speed
+ // data structures are available and indicate the device is operating
+ // at SuperSpeed.
+ //
+
+ if (connectionInfoEx->Speed == UsbHighSpeed
+ && connectionInfoExV2 != NULL
+ && (connectionInfoExV2->Flags.DeviceIsOperatingAtSuperSpeedOrHigher ||
+ connectionInfoExV2->Flags.DeviceIsOperatingAtSuperSpeedPlusOrHigher))
+ {
+ connectionInfoEx->Speed = UsbSuperSpeed;
+ }
+ }
+ else
+ {
+ PUSB_NODE_CONNECTION_INFORMATION connectionInfo = NULL;
+
+ // Try using IOCTL_USB_GET_NODE_CONNECTION_INFORMATION
+ // instead of IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX
+ //
+
+ nBytes = sizeof(USB_NODE_CONNECTION_INFORMATION) +
+ sizeof(USB_PIPE_INFO) * 30;
+
+ connectionInfo = (PUSB_NODE_CONNECTION_INFORMATION)ALLOC(nBytes);
+
+ if (connectionInfo == NULL)
+ {
+ OOPS();
+
+ FREE(connectionInfoEx);
+ if (pPortConnectorProps != NULL)
+ {
+ FREE(pPortConnectorProps);
+ }
+ if (connectionInfoExV2 != NULL)
+ {
+ FREE(connectionInfoExV2);
+ }
+
+ return NULL;
+ }
+
+ connectionInfo->ConnectionIndex = index;
+
+ success = DeviceIoControl(hHubDevice,
+ IOCTL_USB_GET_NODE_CONNECTION_INFORMATION,
+ connectionInfo,
+ nBytes,
+ connectionInfo,
+ nBytes,
+ &nBytes,
+ NULL);
+
+ if (!success)
+ {
+ OOPS();
+
+ FREE(connectionInfo);
+ FREE(connectionInfoEx);
+ if (pPortConnectorProps != NULL)
+ {
+ FREE(pPortConnectorProps);
+ }
+ if (connectionInfoExV2 != NULL)
+ {
+ FREE(connectionInfoExV2);
+ }
+
+ return NULL;
+ }
+
+ // Copy IOCTL_USB_GET_NODE_CONNECTION_INFORMATION into
+ // IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX structure.
+ //
+ connectionInfoEx->ConnectionIndex = connectionInfo->ConnectionIndex;
+ connectionInfoEx->DeviceDescriptor = connectionInfo->DeviceDescriptor;
+ connectionInfoEx->CurrentConfigurationValue = connectionInfo->CurrentConfigurationValue;
+ connectionInfoEx->Speed = connectionInfo->LowSpeed ? UsbLowSpeed : UsbFullSpeed;
+ connectionInfoEx->DeviceIsHub = connectionInfo->DeviceIsHub;
+ connectionInfoEx->DeviceAddress = connectionInfo->DeviceAddress;
+ connectionInfoEx->NumberOfOpenPipes = connectionInfo->NumberOfOpenPipes;
+ connectionInfoEx->ConnectionStatus = connectionInfo->ConnectionStatus;
+
+ memcpy(&connectionInfoEx->PipeList[0],
+ &connectionInfo->PipeList[0],
+ sizeof(USB_PIPE_INFO) * 30);
+
+ FREE(connectionInfo);
+ }
+
+ // If there is a device connected, get the Device Description
+ //
+ if (connectionInfoEx->ConnectionStatus != NoDeviceConnected)
+ {
+ driverKeyName = GetDriverKeyName(hHubDevice, index);
+
+ if (driverKeyName)
+ {
+ size_t cbDriverName = 0;
+
+ hr = StringCbLength(driverKeyName, MAX_DRIVER_KEY_NAME, &cbDriverName);
+ if (SUCCEEDED(hr))
+ {
+ DevProps = DriverNameToDeviceProperties(driverKeyName, cbDriverName);
+ pNode = FindMatchingDeviceNodeForDriverName(driverKeyName, connectionInfoEx->DeviceIsHub);
+ }
+ FREE(driverKeyName);
+ }
+
+ }
+
+ // If there is a device connected to the port, try to retrieve the
+ // Configuration Descriptor from the device.
+ //
+ if (/*gDoConfigDesc &&*/
+ connectionInfoEx->ConnectionStatus == DeviceConnected)
+ {
+ configDesc = GetConfigDescriptor(hHubDevice,
+ index,
+ 0);
+ }
+ else
+ {
+ configDesc = NULL;
+ }
+
+ if (configDesc != NULL &&
+ connectionInfoEx->DeviceDescriptor.bcdUSB > 0x0200)
+ {
+ bosDesc = GetBOSDescriptor(hHubDevice,
+ index);
+ }
+ else
+ {
+ bosDesc = NULL;
+ }
+
+ if (configDesc != NULL &&
+ AreThereStringDescriptors(&connectionInfoEx->DeviceDescriptor,
+ (PUSB_CONFIGURATION_DESCRIPTOR)(configDesc + 1)))
+ {
+ stringDescs = GetAllStringDescriptors(
+ hHubDevice,
+ index,
+ &connectionInfoEx->DeviceDescriptor,
+ (PUSB_CONFIGURATION_DESCRIPTOR)(configDesc + 1));
+ }
+ else
+ {
+ stringDescs = NULL;
+ }
+
+ // If the device connected to the port is an external hub, get the
+ // name of the external hub and recursively enumerate it.
+ //
+ if (connectionInfoEx->DeviceIsHub)
+ {
+ OOPS();
+ if (configDesc != NULL)
+ {
+ FREE(configDesc);
+ }
+ if (bosDesc != NULL)
+ {
+ FREE(bosDesc);
+ }
+ FREE(connectionInfoEx);
+
+ if (pPortConnectorProps != NULL)
+ {
+ FREE(pPortConnectorProps);
+ }
+ if (connectionInfoExV2 != NULL)
+ {
+ FREE(connectionInfoExV2);
+ }
+
+ return NULL;
+ }
+ else
+ {
+ // Allocate some space for a USBDEVICEINFO structure to hold the
+ // hub info, hub name, and connection info pointers. GPTR zero
+ // initializes the structure for us.
+ //
+ info = (PUSBDEVICEINFO)ALLOC(sizeof(USBDEVICEINFO));
+
+ if (info == NULL)
+ {
+ OOPS();
+ if (configDesc != NULL)
+ {
+ FREE(configDesc);
+ }
+ if (bosDesc != NULL)
+ {
+ FREE(bosDesc);
+ }
+ FREE(connectionInfoEx);
+
+ if (pPortConnectorProps != NULL)
+ {
+ FREE(pPortConnectorProps);
+ }
+ if (connectionInfoExV2 != NULL)
+ {
+ FREE(connectionInfoExV2);
+ }
+
+ return NULL;
+ }
+
+ info->DeviceInfoType = DeviceInfo;
+ info->ConnectionInfo = connectionInfoEx;
+ info->PortConnectorProps = pPortConnectorProps;
+ info->ConfigDesc = configDesc;
+ info->StringDescs = stringDescs;
+ info->BosDesc = bosDesc;
+ info->ConnectionInfoV2 = connectionInfoExV2;
+ info->UsbDeviceProperties = DevProps;
+ info->DeviceInfoNode = pNode;
+
+ StringCchPrintf(leafName, sizeof(leafName), "[Port%d] ", index);
+
+ // Add error description if ConnectionStatus is other than NoDeviceConnected / DeviceConnected
+ StringCchCat(leafName,
+ sizeof(leafName),
+ ConnectionStatuses[connectionInfoEx->ConnectionStatus]);
+
+ if (DevProps)
+ {
+ size_t cchDeviceDesc = 0;
+
+ hr = StringCbLength(DevProps->DeviceDesc, MAX_DEVICE_PROP, &cchDeviceDesc);
+ if (FAILED(hr))
+ {
+ OOPS();
+ }
+ dwSizeOfLeafName = sizeof(leafName);
+ StringCchCatN(leafName,
+ dwSizeOfLeafName - 1,
+ " : ",
+ sizeof(" : "));
+ StringCchCatN(leafName,
+ dwSizeOfLeafName - 1,
+ DevProps->DeviceDesc,
+ cchDeviceDesc);
+ }
+
+ if (connectionInfoEx->ConnectionStatus == NoDeviceConnected)
+ {
+ if (connectionInfoExV2 != NULL &&
+ connectionInfoExV2->SupportedUsbProtocols.Usb300 == 1)
+ {
+ icon = NoSsDeviceIcon;
+ }
+ else
+ {
+ icon = NoDeviceIcon;
+ }
+ }
+ else if (connectionInfoEx->CurrentConfigurationValue)
+ {
+ if (connectionInfoEx->Speed == UsbSuperSpeed)
+ {
+ icon = GoodSsDeviceIcon;
+ }
+ else
+ {
+ icon = GoodDeviceIcon;
+ }
+ }
+ else
+ {
+ icon = BadDeviceIcon;
+ }
+ }
+ } // for
+
+ return info;
+}
+PUSBDEVICEINFO get_usb_device_info(const char* device_name)
+{
+ PUSBDEVICEINFO info = NULL;
+ HANDLE hub = NULL;
+ ULONG port = -1;
+ DWORD bytes = 0;
+ BOOL ret = FALSE;
+ HDEVINFO dev = SetupDiGetClassDevsW(&GUID_DEVINTERFACE_USB_HUB, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+
+ if (dev)
+ {
+ SetupDiDestroyDeviceInfoList(dev);
+ }
+
+ if (hub)
+ {
+ info = enumerate_hub_port(hub, port);
+ CloseHandle(hub);
+ }
+
+ return info;
+}
+void free_usb_device_info(PUSBDEVICEINFO pudi)
+{
+ if (pudi)
+ {
+ if (pudi->ConnectionInfo)
+ FREE(pudi->ConnectionInfo);
+ if (pudi->PortConnectorProps)
+ FREE(pudi->PortConnectorProps);
+ if (pudi->ConfigDesc)
+ FREE(pudi->ConfigDesc);
+ if (pudi->StringDescs)
+ FREE(pudi->StringDescs);
+ if (pudi->BosDesc)
+ FREE(pudi->BosDesc);
+ if (pudi->ConnectionInfoV2)
+ FREE(pudi->ConnectionInfoV2);
+ if (pudi->UsbDeviceProperties)
+ FREE(pudi->UsbDeviceProperties);
+ if (pudi->DeviceInfoNode)
+ FREE(pudi->DeviceInfoNode);
+
+ FREE(pudi);
+ }
+}
+
+
+#endif
diff --git a/device/win_usb/usbview/enum.h b/device/win_usb/usbview/enum.h
new file mode 100644
index 0000000..3b37763
--- /dev/null
+++ b/device/win_usb/usbview/enum.h
@@ -0,0 +1,682 @@
+/*++
+
+Copyright (c) 1997-2008 Microsoft Corporation
+
+Module Name:
+
+ UVCVIEW.H
+
+Abstract:
+
+ This is the header file for UVCVIEW
+
+Environment:
+
+ user mode
+
+Revision History:
+
+ 04-25-97 : created
+ 04/13/2005 : major bug fixing
+
+--*/
+
+/*****************************************************************************
+ I N C L U D E S
+*****************************************************************************/
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// This is mostly a private USB Audio descriptor header
+#include "usbdesc.h"
+
+// This is the inbox USBVideo driver descriptor header (copied locally)
+#include "uvcdesc.h"
+
+/*****************************************************************************
+ P R A G M A S
+*****************************************************************************/
+
+#pragma once
+
+/*****************************************************************************
+ D E F I N E S
+*****************************************************************************/
+
+// define H264_SUPPORT to add H.264 support to uvcview.exe
+#define H264_SUPPORT
+
+#define TEXT_ITEM_LENGTH 64
+
+#ifdef DEBUG
+#undef DBG
+#define DBG 1
+#endif
+
+#if DBG
+#define OOPS() Oops(__FILE__, __LINE__)
+#else
+#define OOPS()
+#endif
+
+#if DBG
+
+#define ALLOC(dwBytes) MyAlloc(__FILE__, __LINE__, (dwBytes))
+
+#define REALLOC(hMem, dwBytes) MyReAlloc((hMem), (dwBytes))
+
+#define FREE(hMem) MyFree((hMem))
+
+#define CHECKFORLEAKS() MyCheckForLeaks()
+
+#else
+
+#define ALLOC(dwBytes) GlobalAlloc(GPTR,(dwBytes))
+
+#define REALLOC(hMem, dwBytes) GlobalReAlloc((hMem), (dwBytes), (GMEM_MOVEABLE|GMEM_ZEROINIT))
+
+#define FREE(hMem) GlobalFree((hMem))
+
+#define CHECKFORLEAKS()
+
+#endif
+
+#define DEVICE_CONFIGURATION_TEXT_LENGTH 10240
+
+#define STR_INVALID_POWER_STATE "(invalid state) "
+#define STR_UNKNOWN_CONTROLLER_FLAVOR "Unknown"
+
+FORCEINLINE
+VOID
+InitializeListHead(
+ _Out_ PLIST_ENTRY ListHead
+ )
+{
+ ListHead->Flink = ListHead->Blink = ListHead;
+}
+
+//
+// BOOLEAN
+// IsListEmpty(
+// PLIST_ENTRY ListHead
+// );
+//
+
+#define IsListEmpty(ListHead) \
+ ((ListHead)->Flink == (ListHead))
+
+//
+// PLIST_ENTRY
+// RemoveHeadList(
+// PLIST_ENTRY ListHead
+// );
+//
+
+#define RemoveHeadList(ListHead) \
+ (ListHead)->Flink;\
+ {RemoveEntryList((ListHead)->Flink)}
+
+//
+// VOID
+// RemoveEntryList(
+// PLIST_ENTRY Entry
+// );
+//
+
+#define RemoveEntryList(Entry) {\
+ PLIST_ENTRY _EX_Blink;\
+ PLIST_ENTRY _EX_Flink;\
+ _EX_Flink = (Entry)->Flink;\
+ _EX_Blink = (Entry)->Blink;\
+ _EX_Blink->Flink = _EX_Flink;\
+ _EX_Flink->Blink = _EX_Blink;\
+ }
+
+//
+// VOID
+// InsertTailList(
+// PLIST_ENTRY ListHead,
+// PLIST_ENTRY Entry
+// );
+//
+
+#define InsertTailList(ListHead,Entry) {\
+ PLIST_ENTRY _EX_Blink;\
+ PLIST_ENTRY _EX_ListHead;\
+ _EX_ListHead = (ListHead);\
+ _EX_Blink = _EX_ListHead->Blink;\
+ (Entry)->Flink = _EX_ListHead;\
+ (Entry)->Blink = _EX_Blink;\
+ _EX_Blink->Flink = (Entry);\
+ _EX_ListHead->Blink = (Entry);\
+ }
+
+// global version for USB Video Class spec version (pre-release)
+#define BCDVDC 0x0083
+
+// A.2 Video Interface Subclass Codes
+#define SC_VIDEO_INTERFACE_COLLECTION 0x03
+
+// A.3 Video Interface Protocol Codes
+#define PC_PROTOCOL_UNDEFINED 0x00
+
+// USB Video Class spec version
+#define NOT_UVC 0x0
+#define UVC10 0x100
+#define UVC11 0x110
+
+#ifdef H264_SUPPORT
+#define UVC15 0x150
+#endif
+
+#define OUTPUT_MESSAGE_MAX_LENGTH 1024
+#define MAX_DEVICE_PROP 200
+#define MAX_DRIVER_KEY_NAME 256
+
+/*****************************************************************************
+ T Y P E D E F S
+*****************************************************************************/
+
+typedef enum _TREEICON
+{
+ ComputerIcon,
+ HubIcon,
+ NoDeviceIcon,
+ GoodDeviceIcon,
+ BadDeviceIcon,
+ GoodSsDeviceIcon,
+ NoSsDeviceIcon
+} TREEICON;
+
+// Callback function for walking TreeView items
+//
+typedef VOID
+(*LPFNTREECALLBACK)(
+ HWND hTreeWnd,
+ HTREEITEM hTreeItem,
+ PVOID pContext
+);
+
+
+// Callback notification function called at end of every tree depth
+typedef VOID
+(*LPFNTREENOTIFYCALLBACK)(PVOID pContext);
+
+//
+// Structure used to build a linked list of String Descriptors
+// retrieved from a device.
+//
+
+typedef struct _STRING_DESCRIPTOR_NODE
+{
+ struct _STRING_DESCRIPTOR_NODE *Next;
+ UCHAR DescriptorIndex;
+ USHORT LanguageID;
+ USB_STRING_DESCRIPTOR StringDescriptor[1];
+} STRING_DESCRIPTOR_NODE, *PSTRING_DESCRIPTOR_NODE;
+
+//
+// A collection of device properties. The device can be hub, host controller or usb device
+//
+typedef struct _USB_DEVICE_PNP_STRINGS
+{
+ PCHAR DeviceId;
+ PCHAR DeviceDesc;
+ PCHAR HwId;
+ PCHAR Service;
+ PCHAR DeviceClass;
+ PCHAR PowerState;
+} USB_DEVICE_PNP_STRINGS, *PUSB_DEVICE_PNP_STRINGS;
+
+typedef struct _DEVICE_INFO_NODE {
+ HDEVINFO DeviceInfo;
+ LIST_ENTRY ListEntry;
+ SP_DEVINFO_DATA DeviceInfoData;
+ SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
+ PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceDetailData;
+ PSTR DeviceDescName;
+ ULONG DeviceDescNameLength;
+ PSTR DeviceDriverName;
+ ULONG DeviceDriverNameLength;
+ DEVICE_POWER_STATE LatestDevicePowerState;
+} DEVICE_INFO_NODE, *PDEVICE_INFO_NODE;
+
+//
+// Structures assocated with TreeView items through the lParam. When an item
+// is selected, the lParam is retrieved and the structure it which it points
+// is used to display information in the edit control.
+//
+
+typedef enum _USBDEVICEINFOTYPE
+{
+ HostControllerInfo,
+ RootHubInfo,
+ ExternalHubInfo,
+ DeviceInfo
+} USBDEVICEINFOTYPE, *PUSBDEVICEINFOTYPE;
+
+typedef struct _USBHOSTCONTROLLERINFO
+{
+ USBDEVICEINFOTYPE DeviceInfoType;
+ LIST_ENTRY ListEntry;
+ PCHAR DriverKey;
+ ULONG VendorID;
+ ULONG DeviceID;
+ ULONG SubSysID;
+ ULONG Revision;
+ USB_POWER_INFO USBPowerInfo[6];
+ BOOL BusDeviceFunctionValid;
+ ULONG BusNumber;
+ USHORT BusDevice;
+ USHORT BusFunction;
+ PUSB_CONTROLLER_INFO_0 ControllerInfo;
+ PUSB_DEVICE_PNP_STRINGS UsbDeviceProperties;
+} USBHOSTCONTROLLERINFO, *PUSBHOSTCONTROLLERINFO;
+
+typedef struct _USBROOTHUBINFO
+{
+ USBDEVICEINFOTYPE DeviceInfoType;
+ PUSB_NODE_INFORMATION HubInfo;
+ PUSB_HUB_INFORMATION_EX HubInfoEx;
+ PCHAR HubName;
+ PUSB_PORT_CONNECTOR_PROPERTIES PortConnectorProps;
+ PUSB_DEVICE_PNP_STRINGS UsbDeviceProperties;
+ PDEVICE_INFO_NODE DeviceInfoNode;
+ PUSB_HUB_CAPABILITIES_EX HubCapabilityEx;
+
+} USBROOTHUBINFO, *PUSBROOTHUBINFO;
+
+typedef struct _USBEXTERNALHUBINFO
+{
+ USBDEVICEINFOTYPE DeviceInfoType;
+ PUSB_NODE_INFORMATION HubInfo;
+ PUSB_HUB_INFORMATION_EX HubInfoEx;
+ PCHAR HubName;
+ PUSB_NODE_CONNECTION_INFORMATION_EX ConnectionInfo;
+ PUSB_PORT_CONNECTOR_PROPERTIES PortConnectorProps;
+ PUSB_DESCRIPTOR_REQUEST ConfigDesc;
+ PUSB_DESCRIPTOR_REQUEST BosDesc;
+ PSTRING_DESCRIPTOR_NODE StringDescs;
+ PUSB_NODE_CONNECTION_INFORMATION_EX_V2 ConnectionInfoV2; // NULL if root HUB
+ PUSB_DEVICE_PNP_STRINGS UsbDeviceProperties;
+ PDEVICE_INFO_NODE DeviceInfoNode;
+ PUSB_HUB_CAPABILITIES_EX HubCapabilityEx;
+} USBEXTERNALHUBINFO, *PUSBEXTERNALHUBINFO;
+
+
+// HubInfo, HubName may be in USBDEVICEINFOTYPE, so they can be removed
+typedef struct
+{
+ USBDEVICEINFOTYPE DeviceInfoType;
+ PUSB_NODE_INFORMATION HubInfo; // NULL if not a HUB
+ PUSB_HUB_INFORMATION_EX HubInfoEx; // NULL if not a HUB
+ PCHAR HubName; // NULL if not a HUB
+ PUSB_NODE_CONNECTION_INFORMATION_EX ConnectionInfo; // NULL if root HUB
+ PUSB_PORT_CONNECTOR_PROPERTIES PortConnectorProps;
+ PUSB_DESCRIPTOR_REQUEST ConfigDesc; // NULL if root HUB
+ PUSB_DESCRIPTOR_REQUEST BosDesc; // NULL if root HUB
+ PSTRING_DESCRIPTOR_NODE StringDescs;
+ PUSB_NODE_CONNECTION_INFORMATION_EX_V2 ConnectionInfoV2; // NULL if root HUB
+ PUSB_DEVICE_PNP_STRINGS UsbDeviceProperties;
+ PDEVICE_INFO_NODE DeviceInfoNode;
+ PUSB_HUB_CAPABILITIES_EX HubCapabilityEx; // NULL if not a HUB
+} USBDEVICEINFO, *PUSBDEVICEINFO;
+
+typedef struct _STRINGLIST
+{
+#ifdef H264_SUPPORT
+ ULONGLONG ulFlag;
+#else
+ ULONG ulFlag;
+#endif
+ PCHAR pszString;
+ PCHAR pszModifier;
+
+} STRINGLIST, * PSTRINGLIST;
+
+typedef struct _DEVICE_GUID_LIST {
+ HDEVINFO DeviceInfo;
+ LIST_ENTRY ListHead;
+} DEVICE_GUID_LIST, *PDEVICE_GUID_LIST;
+
+
+/*****************************************************************************
+ G L O B A L S
+*****************************************************************************/
+
+//
+// USBVIEW.C
+//
+
+BOOL gDoConfigDesc;
+BOOL gDoAnnotation;
+BOOL gLogDebug;
+int TotalHubs;
+
+//
+// ENUM.C
+//
+
+PCHAR ConnectionStatuses[];
+
+//
+// DISPVID.C
+//
+DEFINE_GUID(YUY2_Format,0x32595559L,0x0000,0x0010,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71);
+DEFINE_GUID(NV12_Format,0x3231564EL,0x0000,0x0010,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71);
+
+#ifdef H264_SUPPORT
+DEFINE_GUID(H264_Format,0x34363248, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
+#endif
+
+// The following flags/variables are all initialized in Display.c InitializePerDeviceSettings()
+//
+// Save the default frame from the MJPEG, Uncompressed, Vendor and Frame Based Format descriptor
+// Check for this when processing the individual Frame descriptors
+UCHAR g_chMJPEGFrameDefault;
+UCHAR g_chUNCFrameDefault;
+UCHAR g_chVendorFrameDefault;
+UCHAR g_chFrameBasedFrameDefault;
+
+// Spec version of UVC device
+UINT g_chUVCversion;
+
+// Base address of the USBDEVICEINFO for device we're parsing
+PUSBDEVICEINFO CurrentUSBDeviceInfo;
+
+// Base address of the Configuration descriptor we're parsing
+PUSB_CONFIGURATION_DESCRIPTOR CurrentConfigDesc;
+
+// Length of the current configuration descriptor
+DWORD dwConfigLength;
+// Our current position from the beginning of the config descriptor
+DWORD dwConfigIndex;
+
+//
+// DISPLAY.C
+//
+int gDeviceSpeed;
+
+// Save the current Configuration starting and ending addresses
+// Used in ValidateDescAddress()
+//
+PUSB_CONFIGURATION_DESCRIPTOR g_pConfigDesc;
+PSTRING_DESCRIPTOR_NODE g_pStringDescs;
+PUCHAR g_descEnd;
+
+/*****************************************************************************
+ F U N C T I O N P R O T O T Y P E S
+*****************************************************************************/
+
+//
+// USBVIEW.C
+//
+
+HTREEITEM
+AddLeaf (
+ HTREEITEM hTreeParent,
+ LPARAM lParam,
+ _In_ LPTSTR lpszText,
+ TREEICON TreeIcon
+);
+
+VOID
+Oops
+(
+ _In_ PCHAR File,
+ ULONG Line
+);
+
+//
+// DISPLAY.C
+//
+
+EXTERN_C UINT IsIADDevice (PUSBDEVICEINFO info);
+EXTERN_C UINT IsUVCDevice (PUSBDEVICEINFO info);
+EXTERN_C PCHAR GetVendorString(USHORT idVendor);
+EXTERN_C PCHAR GetLangIDString(USHORT idLang);
+EXTERN_C UINT GetConfigurationSize (PUSBDEVICEINFO info);
+EXTERN_C PUSB_COMMON_DESCRIPTOR
+GetNextDescriptor(
+ _In_reads_bytes_(TotalLength)
+ PUSB_COMMON_DESCRIPTOR FirstDescriptor,
+ _In_
+ ULONG TotalLength,
+ _In_
+ PUSB_COMMON_DESCRIPTOR StartDescriptor,
+ _In_ long
+ DescriptorType
+ );
+
+HRESULT
+UpdateTreeItemDeviceInfo(
+ HWND hTreeWnd,
+ HTREEITEM hTreeItem
+ );
+
+PCHAR
+GetTextBuffer(
+);
+
+BOOL
+ResetTextBuffer(
+);
+
+BOOL
+CreateTextBuffer (
+);
+
+VOID
+DestroyTextBuffer (
+);
+
+UINT
+GetTextBufferPos (
+);
+
+VOID
+UpdateEditControl (
+ HWND hEditWnd,
+ HWND hTreeWnd,
+ HTREEITEM hTreeItem
+);
+
+
+VOID __cdecl
+AppendBuffer (
+ LPCTSTR lpFormat,
+ ...
+);
+
+VOID __cdecl
+AppendTextBuffer (
+ LPCTSTR lpFormat,
+ ...
+);
+
+VOID
+DisplayStringDescriptor (
+ UCHAR Index,
+ PSTRING_DESCRIPTOR_NODE StringDescs,
+ DEVICE_POWER_STATE LatestDevicePowerState
+);
+
+PCHAR
+GetStringFromList(
+ PSTRINGLIST slPowerState,
+ ULONG ulNumElements,
+
+#ifdef H264_SUPPORT
+ ULONGLONG ulFlag,
+#else
+ ULONG ulFlag,
+#endif
+ _In_ PCHAR szDefault
+ );
+
+EXTERN_C PCHAR GetPowerStateString(
+ WDMUSB_POWER_STATE powerState
+ );
+
+EXTERN_C PCHAR GetControllerFlavorString(
+ USB_CONTROLLER_FLAVOR flavor
+ );
+
+EXTERN_C ULONG GetEhciDebugPort(
+ ULONG vendorId,
+ ULONG deviceId
+ );
+
+VOID
+WalkTreeTopDown(
+ _In_ HTREEITEM hTreeItem,
+ _In_ LPFNTREECALLBACK lpfnTreeCallback,
+ _In_opt_ PVOID pContext,
+ _In_opt_ LPFNTREENOTIFYCALLBACK lpfnTreeNotifyCallback
+ );
+
+VOID RefreshTree (VOID);
+
+//
+// ENUM.C
+//
+
+VOID
+EnumerateHostControllers (
+ HTREEITEM hTreeParent,
+ ULONG *DevicesConnected
+ );
+
+
+VOID
+CleanupItem (
+ HWND hTreeWnd,
+ HTREEITEM hTreeItem,
+ PVOID pContext
+ );
+
+DEVICE_POWER_STATE
+AcquireDevicePowerState(
+ _Inout_ PDEVICE_INFO_NODE pNode
+ );
+
+_Success_(return == TRUE)
+BOOL
+GetDeviceProperty(
+ _In_ HDEVINFO DeviceInfoSet,
+ _In_ PSP_DEVINFO_DATA DeviceInfoData,
+ _In_ DWORD Property,
+ _Outptr_ LPTSTR *ppBuffer
+ );
+
+void
+ClearDeviceList(
+ PDEVICE_GUID_LIST DeviceList
+ );
+
+//
+// DEBUG.C
+//
+
+_Success_(return != NULL)
+_Post_writable_byte_size_(dwBytes)
+HGLOBAL
+MyAlloc (
+ _In_ PCHAR File,
+ ULONG Line,
+ DWORD dwBytes
+ );
+
+_Success_(return != NULL)
+_Post_writable_byte_size_(dwBytes)
+HGLOBAL
+MyReAlloc (
+ HGLOBAL hMem,
+ DWORD dwBytes
+ );
+
+HGLOBAL
+MyFree (
+ HGLOBAL hMem
+ );
+
+VOID
+MyCheckForLeaks (
+ VOID
+ );
+
+//
+// DEVNODE.C
+//
+
+
+PUSB_DEVICE_PNP_STRINGS
+DriverNameToDeviceProperties(
+ _In_reads_bytes_(cbDriverName) PCHAR DriverName,
+ _In_ size_t cbDriverName
+ );
+
+VOID FreeDeviceProperties(
+ _In_ PUSB_DEVICE_PNP_STRINGS *ppDevProps
+ );
+//
+// DISPAUD.C
+//
+
+BOOL
+DisplayAudioDescriptor (
+ PUSB_AUDIO_COMMON_DESCRIPTOR CommonDesc,
+ UCHAR bInterfaceSubClass
+ );
+
+//
+// DISPVID.C
+//
+
+BOOL
+DisplayVideoDescriptor (
+ PVIDEO_SPECIFIC VidCommonDesc,
+ UCHAR bInterfaceSubClass,
+ PSTRING_DESCRIPTOR_NODE StringDescs,
+ DEVICE_POWER_STATE LatestDevicePowerState
+ );
+
+//
+// DISPLAY.C
+//
+
+BOOL
+ValidateDescAddress (
+ PUSB_COMMON_DESCRIPTOR commonDesc
+ );
+
+#ifdef CUSTOM_USBVIEW
+EXTERN_C PUSBDEVICEINFO enumerate_hub_port(HANDLE hHubDevice, ULONG port_index);
+EXTERN_C PUSBDEVICEINFO get_usb_device_info(const char* device_name/*\\\\.\\USB..*/);
+EXTERN_C void free_usb_device_info(PUSBDEVICEINFO pudi);
+#endif
\ No newline at end of file
diff --git a/device/win_usb/usbview/usbdesc.h b/device/win_usb/usbview/usbdesc.h
new file mode 100644
index 0000000..23420cf
--- /dev/null
+++ b/device/win_usb/usbview/usbdesc.h
@@ -0,0 +1,394 @@
+/*++
+
+Copyright (c) 1997-2008 Microsoft Corporation
+
+Module Name:
+
+ USBDESC.H
+
+Abstract:
+
+ This is a header file for USB descriptors which are not yet in
+ a standard system header file.
+
+Environment:
+
+ user mode
+
+Revision History:
+
+ 03-06-1998 : created
+ 03-28-2003 : minor changes to support UVC and USB200
+
+--*/
+
+#pragma pack(push, 1)
+
+/*****************************************************************************
+ D E F I N E S
+*****************************************************************************/
+
+//
+//Device Descriptor bDeviceClass values
+//
+#define USB_INTERFACE_CLASS_DEVICE 0x00
+#define USB_COMMUNICATION_DEVICE 0x02
+#define USB_HUB_DEVICE 0x09
+#define USB_DEVICE_CLASS_BILLBOARD 0x11
+#define USB_DIAGNOSTIC_DEVICE 0xDC
+#define USB_WIRELESS_CONTROLLER_DEVICE 0xE0
+#define USB_MISCELLANEOUS_DEVICE 0xEF
+#define USB_VENDOR_SPECIFIC_DEVICE 0xFF
+
+//
+//Device Descriptor bDeviceSubClass values
+//
+#define USB_COMMON_SUB_CLASS 0x02
+
+//
+//Interface Descriptor bInterfaceClass values:
+//
+//#define USB_AUDIO_INTERFACE 0x01
+//#define USB_CDC_CONTROL_INTERFACE 0x02
+//#define USB_HID_INTERFACE 0x03
+//#define USB_PHYSICAL_INTERFACE 0x05
+//#define USB_IMAGE_INTERFACE 0x06
+//#define USB_PRINTER_INTERFACE 0x07
+//#define USB_MASS_STORAGE_INTERFACE 0x08
+//#define USB_HUB_INTERFACE 0x09
+#define USB_CDC_DATA_INTERFACE 0x0A
+#define USB_CHIP_SMART_CARD_INTERFACE 0x0B
+#define USB_CONTENT_SECURITY_INTERFACE 0x0D
+#define USB_DIAGNOSTIC_DEVICE_INTERFACE 0xDC
+#define USB_WIRELESS_CONTROLLER_INTERFACE 0xE0
+#define USB_APPLICATION_SPECIFIC_INTERFACE 0xFE
+//#define USB_VENDOR_SPECIFIC_INTERFACE 0xFF
+#define USB_HID_DESCRIPTOR_TYPE 0x21
+
+//
+//IAD protocol values
+//
+#define USB_IAD_PROTOCOL 0x01
+
+//
+//Device class specific values
+//
+#define BILLBOARD_MAX_NUM_ALT_MODE 0x34
+
+//
+//USB 2.0 Specification Changes - New Descriptors
+//
+#define USB_OTHER_SPEED_CONFIGURATION_DESCRIPTOR_TYPE 0x07
+#define USB_INTERFACE_POWER_DESCRIPTOR_TYPE 0x08
+#define USB_OTG_DESCRIPTOR_TYPE 0x09
+#define USB_DEBUG_DESCRIPTOR_TYPE 0x0A
+#define USB_IAD_DESCRIPTOR_TYPE 0x0B
+
+//
+// USB Device Class Definition for Audio Devices
+// Appendix A. Audio Device Class Codes
+//
+
+// A.2 Audio Interface Subclass Codes
+//
+#define USB_AUDIO_SUBCLASS_UNDEFINED 0x00
+#define USB_AUDIO_SUBCLASS_AUDIOCONTROL 0x01
+#define USB_AUDIO_SUBCLASS_AUDIOSTREAMING 0x02
+#define USB_AUDIO_SUBCLASS_MIDISTREAMING 0x03
+
+// A.4 Audio Class-Specific Descriptor Types
+//
+#define USB_AUDIO_CS_UNDEFINED 0x20
+#define USB_AUDIO_CS_DEVICE 0x21
+#define USB_AUDIO_CS_CONFIGURATION 0x22
+#define USB_AUDIO_CS_STRING 0x23
+#define USB_AUDIO_CS_INTERFACE 0x24
+#define USB_AUDIO_CS_ENDPOINT 0x25
+
+// A.5 Audio Class-Specific AC (Audio Control) Interface Descriptor Subtypes
+//
+#define USB_AUDIO_AC_UNDEFINED 0x00
+#define USB_AUDIO_AC_HEADER 0x01
+#define USB_AUDIO_AC_INPUT_TERMINAL 0x02
+#define USB_AUDIO_AC_OUTPUT_TERMINAL 0x03
+#define USB_AUDIO_AC_MIXER_UNIT 0x04
+#define USB_AUDIO_AC_SELECTOR_UNIT 0x05
+#define USB_AUDIO_AC_FEATURE_UNIT 0x06
+#define USB_AUDIO_AC_PROCESSING_UNIT 0x07
+#define USB_AUDIO_AC_EXTENSION_UNIT 0x08
+
+// A.6 Audio Class-Specific AS (Audio Streaming) Interface Descriptor Subtypes
+//
+#define USB_AUDIO_AS_UNDEFINED 0x00
+#define USB_AUDIO_AS_GENERAL 0x01
+#define USB_AUDIO_AS_FORMAT_TYPE 0x02
+#define USB_AUDIO_AS_FORMAT_SPECIFIC 0x03
+
+// A.7 Processing Unit Process Types
+//
+#define USB_AUDIO_PROCESS_UNDEFINED 0x00
+#define USB_AUDIO_PROCESS_UPDOWNMIX 0x01
+#define USB_AUDIO_PROCESS_DOLBYPROLOGIC 0x02
+#define USB_AUDIO_PROCESS_3DSTEREOEXTENDER 0x03
+#define USB_AUDIO_PROCESS_REVERBERATION 0x04
+#define USB_AUDIO_PROCESS_CHORUS 0x05
+#define USB_AUDIO_PROCESS_DYNRANGECOMP 0x06
+
+
+/*****************************************************************************
+ T Y P E D E F S
+*****************************************************************************/
+
+// HID Class HID Descriptor
+//
+typedef struct _USB_HID_DESCRIPTOR
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ USHORT bcdHID;
+ UCHAR bCountryCode;
+ UCHAR bNumDescriptors;
+ struct
+ {
+ UCHAR bDescriptorType;
+ USHORT wDescriptorLength;
+ } OptionalDescriptors[1];
+} USB_HID_DESCRIPTOR, *PUSB_HID_DESCRIPTOR;
+
+
+// OTG Descriptor
+//
+typedef struct _USB_OTG_DESCRIPTOR
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bmAttributes;
+} USB_OTG_DESCRIPTOR, *PUSB_OTG_DESCRIPTOR;
+
+// IAD Descriptor
+//
+typedef struct _USB_IAD_DESCRIPTOR
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bFirstInterface;
+ UCHAR bInterfaceCount;
+ UCHAR bFunctionClass;
+ UCHAR bFunctionSubClass;
+ UCHAR bFunctionProtocol;
+ UCHAR iFunction;
+} USB_IAD_DESCRIPTOR, *PUSB_IAD_DESCRIPTOR;
+
+
+// Common Class Endpoint Descriptor
+//
+typedef struct _USB_ENDPOINT_DESCRIPTOR2 {
+ UCHAR bLength; // offset 0, size 1
+ UCHAR bDescriptorType; // offset 1, size 1
+ UCHAR bEndpointAddress; // offset 2, size 1
+ UCHAR bmAttributes; // offset 3, size 1
+ USHORT wMaxPacketSize; // offset 4, size 2
+ USHORT wInterval; // offset 6, size 2
+ UCHAR bSyncAddress; // offset 8, size 1
+} USB_ENDPOINT_DESCRIPTOR2, *PUSB_ENDPOINT_DESCRIPTOR2;
+
+// Common Class Interface Descriptor
+//
+typedef struct _USB_INTERFACE_DESCRIPTOR2 {
+ UCHAR bLength; // offset 0, size 1
+ UCHAR bDescriptorType; // offset 1, size 1
+ UCHAR bInterfaceNumber; // offset 2, size 1
+ UCHAR bAlternateSetting; // offset 3, size 1
+ UCHAR bNumEndpoints; // offset 4, size 1
+ UCHAR bInterfaceClass; // offset 5, size 1
+ UCHAR bInterfaceSubClass; // offset 6, size 1
+ UCHAR bInterfaceProtocol; // offset 7, size 1
+ UCHAR iInterface; // offset 8, size 1
+ USHORT wNumClasses; // offset 9, size 2
+} USB_INTERFACE_DESCRIPTOR2, *PUSB_INTERFACE_DESCRIPTOR2;
+
+
+//
+// USB Device Class Definition for Audio Devices
+//
+
+typedef struct _USB_AUDIO_COMMON_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+} USB_AUDIO_COMMON_DESCRIPTOR,
+*PUSB_AUDIO_COMMON_DESCRIPTOR;
+
+// 4.3.2 Class-Specific AC (Audio Control) Interface Descriptor
+//
+typedef struct _USB_AUDIO_AC_INTERFACE_HEADER_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ USHORT bcdADC;
+ USHORT wTotalLength;
+ UCHAR bInCollection;
+ UCHAR baInterfaceNr[1];
+} USB_AUDIO_AC_INTERFACE_HEADER_DESCRIPTOR,
+*PUSB_AUDIO_AC_INTERFACE_HEADER_DESCRIPTOR;
+
+// 4.3.2.1 Input Terminal Descriptor
+//
+typedef struct _USB_AUDIO_INPUT_TERMINAL_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bTerminalID;
+ USHORT wTerminalType;
+ UCHAR bAssocTerminal;
+ UCHAR bNrChannels;
+ USHORT wChannelConfig;
+ UCHAR iChannelNames;
+ UCHAR iTerminal;
+} USB_AUDIO_INPUT_TERMINAL_DESCRIPTOR,
+*PUSB_AUDIO_INPUT_TERMINAL_DESCRIPTOR;
+
+// 4.3.2.2 Output Terminal Descriptor
+//
+typedef struct _USB_AUDIO_OUTPUT_TERMINAL_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bTerminalID;
+ USHORT wTerminalType;
+ UCHAR bAssocTerminal;
+ UCHAR bSourceID;
+ UCHAR iTerminal;
+} USB_AUDIO_OUTPUT_TERMINAL_DESCRIPTOR,
+*PUSB_AUDIO_OUTPUT_TERMINAL_DESCRIPTOR;
+
+// 4.3.2.3 Mixer Unit Descriptor
+//
+typedef struct _USB_AUDIO_MIXER_UNIT_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bUnitID;
+ UCHAR bNrInPins;
+ UCHAR baSourceID[1];
+} USB_AUDIO_MIXER_UNIT_DESCRIPTOR,
+*PUSB_AUDIO_MIXER_UNIT_DESCRIPTOR;
+
+// 4.3.2.4 Selector Unit Descriptor
+//
+typedef struct _USB_AUDIO_SELECTOR_UNIT_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bUnitID;
+ UCHAR bNrInPins;
+ UCHAR baSourceID[1];
+} USB_AUDIO_SELECTOR_UNIT_DESCRIPTOR,
+*PUSB_AUDIO_SELECTOR_UNIT_DESCRIPTOR;
+
+// 4.3.2.5 Feature Unit Descriptor
+//
+typedef struct _USB_AUDIO_FEATURE_UNIT_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bUnitID;
+ UCHAR bSourceID;
+ UCHAR bControlSize;
+ UCHAR bmaControls[1];
+} USB_AUDIO_FEATURE_UNIT_DESCRIPTOR,
+*PUSB_AUDIO_FEATURE_UNIT_DESCRIPTOR;
+
+// 4.3.2.6 Processing Unit Descriptor
+//
+typedef struct _USB_AUDIO_PROCESSING_UNIT_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bUnitID;
+ USHORT wProcessType;
+ UCHAR bNrInPins;
+ UCHAR baSourceID[1];
+} USB_AUDIO_PROCESSING_UNIT_DESCRIPTOR,
+*PUSB_AUDIO_PROCESSING_UNIT_DESCRIPTOR;
+
+// 4.3.2.7 Extension Unit Descriptor
+//
+typedef struct _USB_AUDIO_EXTENSION_UNIT_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bUnitID;
+ USHORT wExtensionCode;
+ UCHAR bNrInPins;
+ UCHAR baSourceID[1];
+} USB_AUDIO_EXTENSION_UNIT_DESCRIPTOR,
+*PUSB_AUDIO_EXTENSION_UNIT_DESCRIPTOR;
+
+// 4.5.2 Class-Specific AS Interface Descriptor
+//
+typedef struct _USB_AUDIO_GENERAL_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bTerminalLink;
+ UCHAR bDelay;
+ USHORT wFormatTag;
+} USB_AUDIO_GENERAL_DESCRIPTOR,
+*PUSB_AUDIO_GENERAL_DESCRIPTOR;
+
+// 4.6.1.2 Class-Specific AS Endpoint Descriptor
+//
+typedef struct _USB_AUDIO_ENDPOINT_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bmAttributes;
+ UCHAR bLockDelayUnits;
+ USHORT wLockDelay;
+} USB_AUDIO_ENDPOINT_DESCRIPTOR,
+*PUSB_AUDIO_ENDPOINT_DESCRIPTOR;
+
+//
+// USB Device Class Definition for Audio Data Formats
+//
+
+typedef struct _USB_AUDIO_COMMON_FORMAT_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFormatType;
+} USB_AUDIO_COMMON_FORMAT_DESCRIPTOR,
+*PUSB_AUDIO_COMMON_FORMAT_DESCRIPTOR;
+
+
+// 2.1.5 Type I Format Type Descriptor
+// 2.3.1 Type III Format Type Descriptor
+//
+typedef struct _USB_AUDIO_TYPE_I_OR_III_FORMAT_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFormatType;
+ UCHAR bNrChannels;
+ UCHAR bSubframeSize;
+ UCHAR bBitResolution;
+ UCHAR bSamFreqType;
+} USB_AUDIO_TYPE_I_OR_III_FORMAT_DESCRIPTOR,
+*PUSB_AUDIO_TYPE_I_OR_III_FORMAT_DESCRIPTOR;
+
+
+// 2.2.6 Type II Format Type Descriptor
+//
+typedef struct _USB_AUDIO_TYPE_II_FORMAT_DESCRIPTOR {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFormatType;
+ USHORT wMaxBitRate;
+ USHORT wSamplesPerFrame;
+ UCHAR bSamFreqType;
+} USB_AUDIO_TYPE_II_FORMAT_DESCRIPTOR,
+*PUSB_AUDIO_TYPE_II_FORMAT_DESCRIPTOR;
+
+#pragma pack(pop)
diff --git a/device/win_usb/usbview/uvcdesc.h b/device/win_usb/usbview/uvcdesc.h
new file mode 100644
index 0000000..b1d197a
--- /dev/null
+++ b/device/win_usb/usbview/uvcdesc.h
@@ -0,0 +1,1106 @@
+//+-------------------------------------------------------------------------
+//
+// Microsoft Windows
+//
+// Copyright (C) Microsoft Corporation, 1999 - 2008
+//
+// File: uvcdesc.h
+//
+// This header is from the UVC 1.1 USBVideo driver
+//
+//--------------------------------------------------------------------------
+
+#ifndef ___UVCDESC_H___
+#define ___UVCDESC_H___
+
+
+// USB Video Device Class Code
+#define USB_DEVICE_CLASS_VIDEO 0x0E
+
+// Video sub-classes
+#define SUBCLASS_UNDEFINED 0x00
+#define VIDEO_SUBCLASS_CONTROL 0x01
+#define VIDEO_SUBCLASS_STREAMING 0x02
+
+// Video Class-Specific Descriptor Types
+#define CS_UNDEFINED 0x20
+#define CS_DEVICE 0x21
+#define CS_CONFIGURATION 0x22
+#define CS_STRING 0x23
+#define CS_INTERFACE 0x24
+#define CS_ENDPOINT 0x25
+
+// Video Class-Specific VC Interface Descriptor Subtypes
+#define VC_HEADER 0x01
+#define INPUT_TERMINAL 0x02
+#define OUTPUT_TERMINAL 0x03
+#define SELECTOR_UNIT 0x04
+#define PROCESSING_UNIT 0x05
+#define EXTENSION_UNIT 0x06
+#define MAX_TYPE_UNIT 0x07
+
+// Video Class-Specific VS Interface Descriptor Subtypes
+#define VS_DESCRIPTOR_UNDEFINED 0x00
+#define VS_INPUT_HEADER 0x01
+#define VS_OUTPUT_HEADER 0x02
+#define VS_STILL_IMAGE_FRAME 0x03
+#define VS_FORMAT_UNCOMPRESSED 0x04
+#define VS_FRAME_UNCOMPRESSED 0x05
+#define VS_FORMAT_MJPEG 0x06
+#define VS_FRAME_MJPEG 0x07
+#define VS_FORMAT_MPEG1 0x08
+#define VS_FORMAT_MPEG2PS 0x09
+#define VS_FORMAT_MPEG2TS 0x0A
+#define VS_FORMAT_MPEG4SL 0x0B
+#define VS_FORMAT_DV 0x0C
+#define VS_COLORFORMAT 0x0D
+#define VS_FORMAT_VENDOR 0x0E
+#define VS_FRAME_VENDOR 0x0F
+
+// Video Class-Specific Endpoint Descriptor Subtypes
+#define EP_UNDEFINED 0x00
+#define EP_GENERAL 0x01
+#define EP_ENDPOINT 0x02
+#define EP_INTERRUPT 0x03
+
+// Video Class-Specific Terminal Types
+#define TERMINAL_TYPE_VENDOR_SPECIFIC 0x0100
+#define TERMINAL_TYPE_USB_STREAMING 0x0101
+#define TERMINAL_TYPE_INPUT_MASK 0x0200
+#define TERMINAL_TYPE_INPUT_VENDOR_SPECIFIC 0x0200
+#define TERMINAL_TYPE_INPUT_CAMERA 0x0201
+#define TERMINAL_TYPE_INPUT_MEDIA_TRANSPORT 0x0202
+#define TERMINAL_TYPE_OUTPUT_MASK 0x0300
+#define TERMINAL_TYPE_OUTPUT_VENDOR_SPECIFIC 0x0300
+#define TERMINAL_TYPE_OUTPUT_DISPLAY 0x0301
+#define TERMINAL_TYPE_OUTPUT_MEDIA_TRANSPORT 0x0302
+#define TERMINAL_TYPE_EXTERNAL_VENDOR_SPECIFIC 0x0400
+#define TERMINAL_TYPE_EXTERNAL_UNDEFINED 0x0400
+#define TERMINAL_TYPE_EXTERNAL_COMPOSITE 0x0401
+#define TERMINAL_TYPE_EXTERNAL_SVIDEO 0x0402
+#define TERMINAL_TYPE_EXTERNAL_COMPONENT 0x0403
+
+
+// Controls for error checking only
+#define DEV_SPECIFIC_CONTROL 0x1001
+
+// Map KSNODE_TYPE GUIDs to Indexes
+#define NODE_TYPE_NONE 0
+#define NODE_TYPE_STREAMING 1
+#define NODE_TYPE_INPUT_TERMINAL 2
+#define NODE_TYPE_OUTPUT_TERMINAL 3
+#define NODE_TYPE_SELECTOR 4
+#define NODE_TYPE_PROCESSING 5
+#define NODE_TYPE_CAMERA_TERMINAL 6
+#define NODE_TYPE_INPUT_MTT 7
+#define NODE_TYPE_OUTPUT_MTT 8
+#define NODE_TYPE_DEV_SPEC 9
+#define NODE_TYPE_MAX 9
+
+// USB bmRequestType values
+#define USBVIDEO_INTERFACE_SET 0x21
+#define USBVIDEO_ENDPOINT_SET 0x22
+#define USBVIDEO_INTERFACE_GET 0xA1
+#define USBVIDEO_ENDPOINT_GET 0xA2
+
+// Video Class-specific specific requests
+#define CLASS_SPECIFIC_GET_MASK 0x80
+
+#define RC_UNDEFINED 0x00
+#define SET_CUR 0x01
+#define GET_CUR 0x81
+#define GET_MIN 0x82
+#define GET_MAX 0x83
+#define GET_RES 0x84
+#define GET_LEN 0x85
+#define GET_INFO 0x86
+#define GET_DEF 0x87
+
+// Power Mode Control constants
+#define POWER_MODE_CONTROL_FULL 0x0
+#define POWER_MODE_CONTROL_DEV_DEPENDENT 0x1
+
+// Video Class-specific Processing Unit Controls
+#define PU_CONTROL_UNDEFINED 0x00
+#define PU_BACKLIGHT_COMPENSATION_CONTROL 0x01
+#define PU_BRIGHTNESS_CONTROL 0x02
+#define PU_CONTRAST_CONTROL 0x03
+#define PU_GAIN_CONTROL 0x04
+#define PU_POWER_LINE_FREQUENCY_CONTROL 0x05
+#define PU_HUE_CONTROL 0x06
+#define PU_SATURATION_CONTROL 0x07
+#define PU_SHARPNESS_CONTROL 0x08
+#define PU_GAMMA_CONTROL 0x09
+#define PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0A
+#define PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0B
+#define PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0C
+#define PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0D
+#define PU_DIGITAL_MULTIPLIER_CONTROL 0x0E
+#define PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0F
+#define PU_HUE_AUTO_CONTROL 0x10
+#define PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11
+#define PU_ANALOG_LOCK_STATUS_CONTROL 0x12
+
+// Video Class-specific Camera Terminal Controls
+#define CT_CONTROL_UNDEFINED 0x00
+#define CT_SCANNING_MODE_CONTROL 0x01
+#define CT_AE_MODE_CONTROL 0x02
+#define CT_AE_PRIORITY_CONTROL 0x03
+#define CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04
+#define CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05
+#define CT_FOCUS_ABSOLUTE_CONTROL 0x06
+#define CT_FOCUS_RELATIVE_CONTROL 0x07
+#define CT_FOCUS_AUTO_CONTROL 0x08
+#define CT_IRIS_ABSOLUTE_CONTROL 0x09
+#define CT_IRIS_RELATIVE_CONTROL 0x0A
+#define CT_ZOOM_ABSOLUTE_CONTROL 0x0B
+#define CT_ZOOM_RELATIVE_CONTROL 0x0C
+#define CT_PANTILT_ABSOLUTE_CONTROL 0x0D
+#define CT_PANTILT_RELATIVE_CONTROL 0x0E
+#define CT_ROLL_ABSOLUTE_CONTROL 0x0F
+#define CT_ROLL_RELATIVE_CONTROL 0x10
+#define CT_PRIVACY_CONTROL 0x11
+
+#define CT_RELATIVE_INCREASE 0x01
+#define CT_RELATIVE_DECREASE 0xff
+#define CT_RELATIVE_STOP 0x00
+
+// Selector Unit Control Selector
+#define SU_INPUT_SELECT_CONTROL 0x01
+
+// Media Tape Transport Control Selector
+#define MTT_CONTROL_UNDEFINED 0x00
+#define MTT_TRANSPORT_CONTROL 0x01
+#define MTT_ATN_INFORMATION_CONTROL 0x02
+#define MTT_MEDIA_INFORMATION_CONTROL 0x03
+#define MTT_TIME_CODE_INFORMATION_CONTROL 0x04
+
+// Media Transport Terminal States
+#define MTT_STATE_PLAY_NEXT_FRAME 0x00
+#define MTT_STATE_PLAY_FWD_SLOWEST 0x01
+#define MTT_STATE_PLAY_SLOW_FWD_4 0x02
+#define MTT_STATE_PLAY_SLOW_FWD_3 0x03
+#define MTT_STATE_PLAY_SLOW_FWD_2 0x04
+#define MTT_STATE_PLAY_SLOW_FWD_1 0x05
+#define MTT_STATE_PLAY_X1 0x06
+#define MTT_STATE_PLAY_FAST_FWD_1 0x07
+#define MTT_STATE_PLAY_FAST_FWD_2 0x08
+#define MTT_STATE_PLAY_FAST_FWD_3 0x09
+#define MTT_STATE_PLAY_FAST_FWD_4 0x0A
+#define MTT_STATE_PLAY_FASTEST_FWD 0x0B
+#define MTT_STATE_PLAY_PREV_FRAME 0x0C
+#define MTT_STATE_PLAY_SLOWEST_REV 0x0D
+#define MTT_STATE_PLAY_SLOW_REV_4 0x0E
+#define MTT_STATE_PLAY_SLOW_REV_3 0x0F
+#define MTT_STATE_PLAY_SLOW_REV_2 0x10
+#define MTT_STATE_PLAY_SLOW_REV_1 0x11
+#define MTT_STATE_PLAY_REV 0x12
+#define MTT_STATE_PLAY_FAST_REV_1 0x13
+#define MTT_STATE_PLAY_FAST_REV_2 0x14
+#define MTT_STATE_PLAY_FAST_REV_3 0x15
+#define MTT_STATE_PLAY_FAST_REV_4 0x16
+#define MTT_STATE_PLAY_FASTEST_REV 0x17
+#define MTT_STATE_PLAY 0x18
+#define MTT_STATE_PAUSE 0x19
+#define MTT_STATE_PLAY_REVERSE_PAUSE 0x1A
+
+
+#define MTT_STATE_STOP 0x40
+#define MTT_STATE_FAST_FORWARD 0x41
+#define MTT_STATE_REWIND 0x42
+#define MTT_STATE_HIGH_SPEED_REWIND 0x43
+
+#define MTT_STATE_RECORD_START 0x50
+#define MTT_STATE_RECORD_PAUSE 0x51
+
+#define MTT_STATE_EJECT 0x60
+
+#define MTT_STATE_PLAY_SLOW_FWD_X 0x70
+#define MTT_STATE_PLAY_FAST_FWD_X 0x71
+#define MTT_STATE_PLAY_SLOW_REV_X 0x72
+#define MTT_STATE_PLAY_FAST_REV_X 0x73
+#define MTT_STATE_STOP_START 0x74
+#define MTT_STATE_STOP_END 0x75
+#define MTT_STATE_STOP_EMERGENCY 0x76
+#define MTT_STATE_STOP_CONDENSATION 0x77
+#define MTT_STATE_UNSPECIFIED 0x7F
+
+// Video Control Interface Control Selectors
+#define VC_UNDEFINED_CONTROL 0x00
+#define VC_VIDEO_POWER_MODE_CONTROL 0x01
+#define VC_REQUEST_ERROR_CODE_CONTROL 0x02
+
+// VideoStreaming Interface Control Selectors
+#define VS_CONTROL_UNDEFINED 0x00
+#define VS_PROBE_CONTROL 0x01
+#define VS_COMMIT_CONTROL 0x02
+#define VS_STILL_PROBE_CONTROL 0x03
+#define VS_STILL_COMMIT_CONTROL 0x04
+#define VS_STILL_IMAGE_TRIGGER_CONTROL 0x05
+#define VS_STREAM_ERROR_CODE_CONTROL 0x06
+#define VS_GENERATE_KEY_FRAME_CONTROL 0x07
+#define VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08
+#define VS_SYNC_DELAY_CONTROL 0x09
+
+// Probe commit bitmap framing info
+#define VS_PROBE_COMMIT_BIT_FID 0x01
+#define VS_PROBE_COMMIT_BIT_EOF 0x02
+
+// Stream payload header Bit Field Header bits
+#define BFH_FID 0x01 // Frame ID bit
+#define BFH_EOF 0x02 // End of Frame bit
+#define BFH_PTS 0x04 // Presentation Time Stamp bit
+#define BFH_SCR 0x08 // Source Clock Reference bit
+#define BFH_RES 0x10 // Reserved bit
+#define BFH_STI 0x20 // Still image bit
+#define BFH_ERR 0x40 // Error bit
+#define BFH_EOH 0x80 // End of header bit
+
+#define HDR_LENGTH 1 // Length of header length field in bytes
+#define BFH_LENGTH 1 // Length of BFH field in bytes
+#define PTS_LENGTH 4 // Length of PTS field in bytes
+#define SCR_LENGTH 6 // Length of SCR field in bytes
+
+// USB Video Status Codes (Request Error Code Control)
+#define USBVIDEO_RE_STATUS_NOERROR 0x00
+#define USBVIDEO_RE_STATUS_NOT_READY 0x01
+#define USBVIDEO_RE_STATUS_WRONG_STATE 0x02
+#define USBVIDEO_RE_STATUS_POWER 0x03
+#define USBVIDEO_RE_STATUS_OUT_OF_RANGE 0x04
+#define USBVIDEO_RE_STATUS_INVALID_UNIT 0x05
+#define USBVIDEO_RE_STATUS_INVALID_CONTROL 0x06
+#define USBVIDEO_RE_STATUS_UNKNOWN 0x07
+
+// USB Video Device Status Codes (Stream Error Code Control)
+#define USBVIDEO_SE_STATUS_NOERROR 0x00
+#define USBVIDEO_SE_STATUS_PROTECTED_CONTENT 0x01
+#define USBVIDEO_SE_STATUS_INPUT_BUFFER_UNDERRUN 0x02
+#define USBVIDEO_SE_STATUS_DATA_DICONTINUITY 0x03
+#define USBVIDEO_SE_STATUS_OUTPUT_BUFFER_UNDERRUN 0x04
+#define USBVIDEO_SE_STATUS_OUTPUT_BUFFER_OVERRUN 0x05
+#define USBVIDEO_SE_STATUS_FORMAT_CHANGE 0x06
+#define USBVIDEO_SE_STATUS_STILL_IMAGE_ERROR 0x07
+#define USBVIDEO_SE_STATUS_UNKNOWN 0x08
+
+// Status Interrupt Types
+#define STATUS_INTERRUPT_VC 1
+#define STATUS_INTERRUPT_VS 2
+
+// Status Interrupt Attributes
+#define STATUS_INTERRUPT_ATTRIBUTE_VALUE 0x00
+#define STATUS_INTERRUPT_ATTRIBUTE_INFO 0x01
+#define STATUS_INTERRUPT_ATTRIBUTE_FAILURE 0x02
+
+// VideoStreaming interface interrupt types
+#define VS_INTERRUPT_EVENT_BUTTON_PRESS 0x00
+#define VS_INTERRUPT_VALUE_BUTTON_RELEASE 0x00
+#define VS_INTERRUPT_VALUE_BUTTON_PRESS 0x01
+
+// Get Info Values
+#define USBVIDEO_ASYNC_CONTROL 0x10
+#define USBVIDEO_SETTABLE_CONTROL 0x2
+
+#define MAX_INTERRUPT_PACKET_VALUE_SIZE 8
+
+// Frame descriptor frame interval array offsets
+#define MIN_FRAME_INTERVAL_OFFSET 0
+#define MAX_FRAME_INTERVAL_OFFSET 1
+#define FRAME_INTERVAL_STEP_OFFSET 2
+
+// Still image capture methods
+#define STILL_CAPTURE_METHOD_NONE 0
+#define STILL_CAPTURE_METHOD_1 1
+#define STILL_CAPTURE_METHOD_2 2
+#define STILL_CAPTURE_METHOD_3 3
+
+// Still image trigger control states
+#define STILL_IMAGE_TRIGGER_NORMAL 0
+#define STILL_IMAGE_TRIGGER_TRANSMIT 1
+#define STILL_IMAGE_TRIGGER_TRANSMIT_BULK 2
+#define STILL_IMAGE_TRIGGER_TRANSMIT_ABORT 3
+
+// Endpoint descriptor masks
+#define EP_DESCRIPTOR_TRANSACTION_SIZE_MASK 0x07ff
+#define EP_DESCRIPTOR_NUM_TRANSACTION_MASK 0x1800
+#define EP_DESCRIPTOR_NUM_TRANSACTION_OFFSET 11
+
+
+// Copy protection flag defined in the Uncompressed Payload Spec
+#define USB_VIDEO_UNCOMPRESSED_RESTRICT_DUPLICATION 1
+
+// Interlace flags
+#define INTERLACE_FLAGS_SUPPORTED_MASK 0x01
+#define INTERLACE_FLAGS_FIELDS_PER_FRAME_MASK 0x02
+#define INTERLACE_FLAGS_FIELDS_PER_FRAME_2 0x00
+#define INTERLACE_FLAGS_FIELDS_PER_FRAME_1 0x02
+#define INTERLACE_FLAGS_FIELD_1_FIRST_MASK 0x04
+#define INTERLACE_FLAGS_FIELD_PATTERN_MASK 0x30
+#define INTERLACE_FLAGS_FIELD_PATTERN_FIELD1 0x00
+#define INTERLACE_FLAGS_FIELD_PATTERN_FIELD2 0x10
+#define INTERLACE_FLAGS_FIELD_PATTERN_REGULAR 0x20
+#define INTERLACE_FLAGS_FIELD_PATTERN_RANDOM 0x30
+#define INTERLACE_FLAGS_DISPLAY_MODE_MASK 0xC0
+#define INTERLACE_FLAGS_DISPLAY_MODE_BOB 0x00
+#define INTERLACE_FLAGS_DISPLAY_MODE_WEAVE 0x40
+#define INTERLACE_FLAGS_DISPLAY_MODE_BOB_WEAVE 0x80
+
+// Color Matching Flags
+#define UVC_PRIMARIES_UNKNOWN 0x0
+#define UVC_PRIMARIES_BT709 0x1
+#define UVC_PRIMARIES_BT470_2M 0x2
+#define UVC_PRIMARIES_BT470_2BG 0x3
+#define UVC_PRIMARIES_SMPTE_170M 0x4
+#define UVC_PRIMARIES_SMPTE_240M 0x5
+
+#define UVC_GAMMA_UNKNOWN 0x0
+#define UVC_GAMMA_BT709 0x1
+#define UVC_GAMMA_BT470_2M 0x2
+#define UVC_GAMMA_BT470_2BG 0x3
+#define UVC_GAMMA_SMPTE_170M 0x4
+#define UVC_GAMMA_SMPTE_240M 0x5
+#define UVC_GAMMA_LINEAR 0x6
+#define UVC_GAMMA_sRGB 0x7
+
+#define UVC_TRANSFER_MATRIX_UNKNOWN 0x0
+#define UVC_TRANSFER_MATRIX_BT709 0x1
+#define UVC_TRANSFER_MATRIX_FCC 0x2
+#define UVC_TRANSFER_MATRIX_BT470_2BG 0x3
+#define UVC_TRANSFER_MATRIX_BT601 0x4
+#define UVC_TRANSFER_MATRIX_SMPTE_240M 0x5
+
+//
+// BEGIN - VDC Descriptor and Control Structures
+//
+#pragma warning( disable : 4200 ) // Allow zero-sized arrays at end of structs
+#pragma pack( push, vdc_descriptor_structs, 1)
+
+// Video Specific Descriptor
+typedef struct {
+ UCHAR bLength; // Size of this descriptor in bytes
+ UCHAR bDescriptorType; // CS_INTERFACE descriptor type
+ UCHAR bDescriptorSubtype; // descriptor subtype
+} VIDEO_SPECIFIC, *PVIDEO_SPECIFIC;
+
+#define SIZEOF_VIDEO_SPECIFIC(pDesc) sizeof(VIDEO_SPECIFIC)
+
+
+// Video Unit Descriptor
+typedef struct {
+ UCHAR bLength; // Size of this descriptor in bytes
+ UCHAR bDescriptorType; // CS_INTERFACE descriptor type
+ UCHAR bDescriptorSubtype; // descriptor subtype
+ UCHAR bUnitID; // Constant uniquely identifying the Unit
+} VIDEO_UNIT, *PVIDEO_UNIT;
+
+#define SIZEOF_VIDEO_UNIT(pDesc) sizeof(VIDEO_UNIT)
+
+// VideoControl Header Descriptor
+typedef struct {
+ UCHAR bLength; // Size of this descriptor in bytes
+ UCHAR bDescriptorType; // CS_INTERFACE descriptor type
+ UCHAR bDescriptorSubtype; // VC_HEADER descriptor subtype
+ USHORT bcdVideoSpec; // USB video class spec revision number
+ USHORT wTotalLength; // Total length, including all units and terminals
+ ULONG dwClockFreq; // Device clock frequency in Hz
+ UCHAR bInCollection; // number of video streaming interfaces
+ UCHAR baInterfaceNr[]; // interface number array
+} VIDEO_CONTROL_HEADER_UNIT, *PVIDEO_CONTROL_HEADER_UNIT;
+
+#define SIZEOF_VIDEO_CONTROL_HEADER_UNIT(pDesc) \
+ ((sizeof(VIDEO_CONTROL_HEADER_UNIT) + (pDesc)->bInCollection))
+
+
+// VideoControl Input Terminal Descriptor
+typedef struct {
+ UCHAR bLength; // Size of this descriptor in bytes
+ UCHAR bDescriptorType; // CS_INTERFACE descriptor type
+ UCHAR bDescriptorSubtype; // INPUT_TERMINAL descriptor subtype
+ UCHAR bTerminalID; // Constant uniquely identifying the Terminal
+ USHORT wTerminalType; // Constant characterizing the terminal type
+ UCHAR bAssocTerminal; // ID of associated output terminal
+ UCHAR iTerminal; // Index of string descriptor
+} VIDEO_INPUT_TERMINAL, *PVIDEO_INPUT_TERMINAL;
+
+#define SIZEOF_VIDEO_INPUT_TERMINAL(pDesc) sizeof(VIDEO_INPUT_TERMINAL)
+
+
+// VideoControl Output Terminal Descriptor
+typedef struct {
+ UCHAR bLength; // Size of this descriptor in bytes
+ UCHAR bDescriptorType; // CS_INTERFACE descriptor type
+ UCHAR bDescriptorSubtype; // OUTPUT_TERMINAL descriptor subtype
+ UCHAR bTerminalID; // Constant uniquely identifying the Terminal
+ USHORT wTerminalType; // Constant characterizing the terminal type
+ UCHAR bAssocTerminal; // ID of associated input terminal
+ UCHAR bSourceID; // ID of source unit/terminal
+ UCHAR iTerminal; // Index of string descriptor
+} VIDEO_OUTPUT_TERMINAL, *PVIDEO_OUTPUT_TERMINAL;
+
+#define SIZEOF_VIDEO_OUTPUT_TERMINAL(pDesc) sizeof(VIDEO_OUTPUT_TERMINAL)
+
+
+// VideoControl Camera Terminal Descriptor
+typedef struct {
+ UCHAR bLength; // Size of this descriptor in bytes
+ UCHAR bDescriptorType; // CS_INTERFACE descriptor type
+ UCHAR bDescriptorSubtype; // INPUT_TERMINAL descriptor subtype
+ UCHAR bTerminalID; // Constant uniquely identifying the Terminal
+ USHORT wTerminalType; // Sensor type
+ UCHAR bAssocTerminal; // ID of associated output terminal
+ UCHAR iTerminal; // Index of string descriptor
+ USHORT wObjectiveFocalLengthMin; // Min focal length for zoom
+ USHORT wObjectiveFocalLengthMax; // Max focal length for zoom
+ USHORT wOcularFocalLength; // Ocular focal length for zoom
+ UCHAR bControlSize; // Size of bmControls field
+ UCHAR bmControls[]; // Bitmap of controls supported
+} VIDEO_CAMERA_TERMINAL, *PVIDEO_CAMERA_TERMINAL;
+
+#define SIZEOF_VIDEO_CAMERA_TERMINAL(pDesc) \
+ (sizeof(VIDEO_CAMERA_TERMINAL) + (pDesc)->bControlSize)
+
+
+// Media Transport Input Terminal Descriptor
+typedef struct {
+ UCHAR bLength; // Size of this descriptor in bytes
+ UCHAR bDescriptorType; // CS_INTERFACE descriptor type
+ UCHAR bDescriptorSubtype; // INPUT_TERMINAL descriptor subtype
+ UCHAR bTerminalID; // Constant uniquely identifying the Terminal
+ USHORT wTerminalType; // Media Transport type
+ UCHAR bAssocTerminal; // ID of associated output terminal
+ UCHAR iTerminal; // Index of string descriptor
+ UCHAR bControlSize; // Size of bmControls field
+ UCHAR bmControls[]; // Bitmap of controls supported
+} VIDEO_INPUT_MTT, *PVIDEO_INPUT_MTT;
+
+
+__inline size_t SizeOfVideoInputMTT(_In_ PVIDEO_INPUT_MTT pDesc)
+{
+ UCHAR bTransportModeSize;
+ PUCHAR pbCurr;
+
+ pbCurr = pDesc->bmControls + pDesc->bControlSize;
+ bTransportModeSize = *pbCurr;
+
+ return sizeof(VIDEO_INPUT_MTT) + pDesc->bControlSize + 1 + bTransportModeSize;
+}
+
+#define SIZEOF_VIDEO_INPUT_MTT(pDesc) SizeOfVideoInputMTT(pDesc)
+
+
+// Media Transport Output Terminal Descriptor
+typedef struct {
+ UCHAR bLength; // Size of this descriptor in bytes
+ UCHAR bDescriptorType; // CS_INTERFACE descriptor type
+ UCHAR bDescriptorSubtype; // OUTPUT_TERMINAL descriptor subtype
+ UCHAR bTerminalID; // Constant uniquely identifying the Terminal
+ USHORT wTerminalType; // Media Transport type
+ UCHAR bAssocTerminal; // ID of associated output terminal
+ UCHAR bSourceID; // ID of source unit/terminal
+ UCHAR iTerminal; // Index of string descriptor
+ UCHAR bControlSize; // Size of bmControls field
+ UCHAR bmControls[]; // Bitmap of controls supported
+} VIDEO_OUTPUT_MTT, *PVIDEO_OUTPUT_MTT;
+
+
+__inline size_t SizeOfVideoOutputMTT(_In_ PVIDEO_OUTPUT_MTT pDesc)
+{
+ UCHAR bTransportModeSize;
+ PUCHAR pbCurr;
+
+ pbCurr = pDesc->bmControls + pDesc->bControlSize;
+ bTransportModeSize = *pbCurr;
+
+ return sizeof(VIDEO_OUTPUT_MTT) + pDesc->bControlSize + 1+ bTransportModeSize;
+}
+
+#define SIZEOF_VIDEO_OUTPUT_MTT(pDesc) SizeOfVideoOutputMTT(pDesc)
+
+
+// VideoControl Selector Unit Descriptor
+typedef struct {
+ UCHAR bLength; // Size of this descriptor in bytes
+ UCHAR bDescriptorType; // CS_INTERFACE descriptor type
+ UCHAR bDescriptorSubtype; // SELECTOR_UNIT descriptor subtype
+ UCHAR bUnitID; // Constant uniquely identifying the Unit
+ UCHAR bNrInPins; // Number of input pins
+ UCHAR baSourceID[]; // IDs of connected units/terminals
+} VIDEO_SELECTOR_UNIT, *PVIDEO_SELECTOR_UNIT;
+
+#define SIZEOF_VIDEO_SELECTOR_UNIT(pDesc) \
+ (sizeof(VIDEO_SELECTOR_UNIT) + (pDesc)->bNrInPins + 1)
+
+
+// VideoControl Processing Unit Descriptor
+typedef struct {
+ UCHAR bLength; // Size of this descriptor in bytes
+ UCHAR bDescriptorType; // CS_INTERFACE descriptor type
+ UCHAR bDescriptorSubtype; // PROCESSING_UNIT descriptor subtype
+ UCHAR bUnitID; // Constant uniquely identifying the Unit
+ UCHAR bSourceID; // ID of connected unit/terminal
+ USHORT wMaxMultiplier; // Maximum digital magnification
+ UCHAR bControlSize; // Size of bmControls field
+ UCHAR bmControls[]; // Bitmap of controls supported
+} VIDEO_PROCESSING_UNIT, *PVIDEO_PROCESSING_UNIT;
+
+#define SIZEOF_VIDEO_PROCESSING_UNIT(pDesc) \
+ (sizeof(VIDEO_PROCESSING_UNIT) + 1 + (pDesc)->bControlSize)
+
+
+// VideoControl Extension Unit Descriptor
+typedef struct {
+ UCHAR bLength; // Size of this descriptor in bytes
+ UCHAR bDescriptorType; // CS_INTERFACE descriptor type
+ UCHAR bDescriptorSubtype; // EXTENSION_UNIT descriptor subtype
+ UCHAR bUnitID; // Constant uniquely identifying the Unit
+ GUID guidExtensionCode; // Vendor-specific code identifying extension unit
+ UCHAR bNumControls; // Number of controls in Extension Unit
+ UCHAR bNrInPins; // Number of input pins
+ UCHAR baSourceID[]; // IDs of connected units/terminals
+} VIDEO_EXTENSION_UNIT, *PVIDEO_EXTENSION_UNIT;
+// this is followed by bControlSize, bmControls and iExtension (1 byte)
+
+__inline size_t SizeOfVideoExtensionUnit(PVIDEO_EXTENSION_UNIT pDesc)
+{
+ UCHAR bControlSize;
+ PUCHAR pbCurr;
+
+ // baSourceID is an array, and hence understood to be an address
+ pbCurr = pDesc->baSourceID + pDesc->bNrInPins;
+ if (((ULONG_PTR) pbCurr < (ULONG_PTR) pDesc->baSourceID) ||
+ (ULONG_PTR) pbCurr >= (ULONG_PTR)((UCHAR *) pDesc + pDesc->bLength))
+ return 0;
+
+ bControlSize = *pbCurr;
+ return 24 + pDesc->bNrInPins + bControlSize;
+}
+
+#define SIZEOF_VIDEO_EXTENSION_UNIT(pDesc) SizeOfVideoExtensionUnit(pDesc)
+
+
+// Class-specific Interrupt Endpoint Descriptor
+typedef struct {
+ UCHAR bLength; // Size of this descriptor in bytes
+ UCHAR bDescriptorType; // CS_ENDPOINT descriptor type
+ UCHAR bDescriptorSubtype; // EP_INTERRUPT descriptor subtype
+ USHORT wMaxTransferSize; // Max interrupt payload size
+} VIDEO_CS_INTERRUPT, *PVIDEO_CS_INTERRUPT;
+
+#define SIZEOF_VIDEO_CS_INTERRUPT(pDesc) sizeof(VIDEO_CS_INTERRUPT)
+
+
+// VideoStreaming Input Header Descriptor
+typedef struct _VIDEO_STREAMING_INPUT_HEADER
+{
+ UCHAR bLength; // Size of this descriptor in bytes
+ UCHAR bDescriptorType; // CS_INTERFACE descriptor type
+ UCHAR bDescriptorSubtype; // VS_INPUT_HEADER descriptor subtype
+ UCHAR bNumFormats;
+ USHORT wTotalLength;
+ UCHAR bEndpointAddress;
+ UCHAR bmInfo;
+ UCHAR bTerminalLink;
+ UCHAR bStillCaptureMethod;
+ UCHAR bTriggerSupport;
+ UCHAR bTriggerUsage;
+ UCHAR bControlSize;
+ UCHAR bmaControls[];
+} VIDEO_STREAMING_INPUT_HEADER, *PVIDEO_STREAMING_INPUT_HEADER;
+
+#define SIZEOF_VIDEO_STREAMING_INPUT_HEADER(pDesc) \
+ (sizeof(VIDEO_STREAMING_INPUT_HEADER) + (pDesc->bNumFormats * pDesc->bControlSize))
+
+
+// VideoStreaming Output Header Descriptor
+typedef struct _VIDEO_STREAMING_OUTPUT_HEADER
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bNumFormats;
+ USHORT wTotalLength;
+ UCHAR bEndpointAddress;
+ UCHAR bTerminalLink;
+} VIDEO_STREAMING_OUTPUT_HEADER, *PVIDEO_STREAMING_OUTPUT_HEADER;
+
+#define SIZEOF_VIDEO_STREAMING_OUTPUT_HEADER(pDesc) sizeof(VIDEO_STREAMING_OUTPUT_HEADER)
+
+
+typedef struct _VIDEO_STILL_IMAGE_RECT
+{
+ USHORT wWidth;
+ USHORT wHeight;
+} VIDEO_STILL_IMAGE_RECT;
+
+// VideoStreaming Still Image Frame Descriptor
+typedef struct _VIDEO_STILL_IMAGE_FRAME
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bEndpointAddress;
+ UCHAR bNumImageSizePatterns;
+ VIDEO_STILL_IMAGE_RECT aStillRect[];
+} VIDEO_STILL_IMAGE_FRAME, *PVIDEO_STILL_IMAGE_FRAME;
+
+__inline size_t SizeOfVideoStillImageFrame(PVIDEO_STILL_IMAGE_FRAME pDesc)
+{
+ UCHAR bNumCompressionPatterns;
+ PUCHAR pbCurr;
+
+ pbCurr = (PUCHAR) pDesc->aStillRect + (sizeof(VIDEO_STILL_IMAGE_RECT) * pDesc->bNumImageSizePatterns);
+ bNumCompressionPatterns = *pbCurr;
+
+ return (sizeof(VIDEO_STILL_IMAGE_FRAME) +
+ (sizeof(VIDEO_STILL_IMAGE_RECT) * pDesc->bNumImageSizePatterns) +
+ 1 + bNumCompressionPatterns);
+}
+
+#define SIZEOF_VIDEO_STILL_IMAGE_FRAME(pDesc) SizeOfVideoStillImageFrame(pDesc)
+
+
+// VideoStreaming Color Matching Descriptor
+typedef struct _VIDEO_COLORFORMAT
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bColorPrimaries;
+ UCHAR bTransferCharacteristics;
+ UCHAR bMatrixCoefficients;
+} VIDEO_COLORFORMAT, *PVIDEO_COLORFORMAT;
+
+#define SIZEOF_VIDEO_COLORFORMAT(pDesc) sizeof(VIDEO_COLORFORMAT)
+
+
+// VideoStreaming Uncompressed Format Descriptor
+typedef struct _VIDEO_FORMAT_UNCOMPRESSED
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFormatIndex;
+ UCHAR bNumFrameDescriptors;
+ GUID guidFormat;
+ UCHAR bBitsPerPixel;
+ UCHAR bDefaultFrameIndex;
+ UCHAR bAspectRatioX;
+ UCHAR bAspectRatioY;
+ UCHAR bmInterlaceFlags;
+ UCHAR bCopyProtect;
+} VIDEO_FORMAT_UNCOMPRESSED, *PVIDEO_FORMAT_UNCOMPRESSED;
+
+#define SIZEOF_VIDEO_FORMAT_UNCOMPRESSED(pDesc) sizeof(VIDEO_FORMAT_UNCOMPRESSED)
+
+
+// VideoStreaming Uncompressed Frame Descriptor
+typedef struct _VIDEO_FRAME_UNCOMPRESSED
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFrameIndex;
+ UCHAR bmCapabilities;
+ USHORT wWidth;
+ USHORT wHeight;
+ ULONG dwMinBitRate;
+ ULONG dwMaxBitRate;
+ ULONG dwMaxVideoFrameBufferSize;
+ ULONG dwDefaultFrameInterval;
+ UCHAR bFrameIntervalType;
+ ULONG adwFrameInterval[];
+} VIDEO_FRAME_UNCOMPRESSED, *PVIDEO_FRAME_UNCOMPRESSED;
+
+
+__inline size_t SizeOfVideoFrameUncompressed(_In_ PVIDEO_FRAME_UNCOMPRESSED pDesc)
+{
+ if (pDesc->bFrameIntervalType == 0) { // Continuous
+ return sizeof(VIDEO_FRAME_UNCOMPRESSED) + (3 * sizeof(ULONG));
+ }
+ else { // Discrete
+ return sizeof(VIDEO_FRAME_UNCOMPRESSED) + (pDesc->bFrameIntervalType * sizeof(ULONG));
+ }
+}
+
+#define SIZEOF_VIDEO_FRAME_UNCOMPRESSED(pDesc) SizeOfVideoFrameUncompressed(pDesc)
+
+
+// VideoStreaming MJPEG Format Descriptor
+typedef struct _VIDEO_FORMAT_MJPEG
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFormatIndex;
+ UCHAR bNumFrameDescriptors;
+ UCHAR bmFlags;
+ UCHAR bDefaultFrameIndex;
+ UCHAR bAspectRatioX;
+ UCHAR bAspectRatioY;
+ UCHAR bmInterlaceFlags;
+ UCHAR bCopyProtect;
+} VIDEO_FORMAT_MJPEG, *PVIDEO_FORMAT_MJPEG;
+
+#define SIZEOF_VIDEO_FORMAT_MJPEG(pDesc) sizeof(VIDEO_FORMAT_MJPEG)
+
+
+// VideoStreaming MJPEG Frame Descriptor
+typedef struct _VIDEO_FRAME_MJPEG
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFrameIndex;
+ UCHAR bmCapabilities;
+ USHORT wWidth;
+ USHORT wHeight;
+ ULONG dwMinBitRate;
+ ULONG dwMaxBitRate;
+ ULONG dwMaxVideoFrameBufferSize;
+ ULONG dwDefaultFrameInterval;
+ UCHAR bFrameIntervalType;
+ ULONG adwFrameInterval[];
+} VIDEO_FRAME_MJPEG, *PVIDEO_FRAME_MJPEG;
+
+
+__inline size_t SizeOfVideoFrameMjpeg(_In_ PVIDEO_FRAME_MJPEG pDesc)
+{
+ if (pDesc->bFrameIntervalType == 0) { // Continuous
+ return sizeof(VIDEO_FRAME_MJPEG) + (3 * sizeof(ULONG));
+ }
+ else { // Discrete
+ return sizeof(VIDEO_FRAME_MJPEG) + (pDesc->bFrameIntervalType * sizeof(ULONG));
+ }
+}
+
+#define SIZEOF_VIDEO_FRAME_MJPEG(pDesc) SizeOfVideoFrameMjpeg(pDesc)
+
+
+// VideoStreaming Vendor Format Descriptor
+typedef struct _VIDEO_FORMAT_VENDOR
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFormatIndex;
+ UCHAR bNumFrameDescriptors;
+ GUID guidMajorFormat;
+ GUID guidSubFormat;
+ GUID guidSpecifier;
+ UCHAR bPayloadClass;
+ UCHAR bDefaultFrameIndex;
+ UCHAR bCopyProtect;
+} VIDEO_FORMAT_VENDOR, *PVIDEO_FORMAT_VENDOR;
+
+#define SIZEOF_VIDEO_FORMAT_VENDOR(pDesc) sizeof(VIDEO_FORMAT_VENDOR)
+
+
+// VideoStreaming Vendor Frame Descriptor
+typedef struct _VIDEO_FRAME_VENDOR
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFrameIndex;
+ UCHAR bmCapabilities;
+ USHORT wWidth;
+ USHORT wHeight;
+ ULONG dwMinBitRate;
+ ULONG dwMaxBitRate;
+ ULONG dwMaxVideoFrameBufferSize;
+ ULONG dwDefaultFrameInterval;
+ UCHAR bFrameIntervalType;
+ DWORD adwFrameInterval[];
+} VIDEO_FRAME_VENDOR, *PVIDEO_FRAME_VENDOR;
+
+__inline size_t SizeOfVideoFrameVendor(_In_ PVIDEO_FRAME_VENDOR pDesc)
+{
+ if (pDesc->bFrameIntervalType == 0) { // Continuous
+ return sizeof(VIDEO_FRAME_VENDOR) + (3 * sizeof(ULONG));
+ }
+ else { // Discrete
+ return sizeof(VIDEO_FRAME_VENDOR) + (pDesc->bFrameIntervalType * sizeof(ULONG));
+ }
+}
+
+#define SIZEOF_VIDEO_FRAME_VENDOR(pDesc) SizeOfVideoFrameVendor(pDesc)
+
+
+// VideoStreaming DV Format Descriptor
+typedef struct _VIDEO_FORMAT_DV
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFormatIndex;
+ ULONG dwMaxVideoFrameBufferSize;
+ UCHAR bFormatType;
+} VIDEO_FORMAT_DV, *PVIDEO_FORMAT_DV;
+
+#define SIZEOF_VIDEO_FORMAT_DV(pDesc) sizeof(VIDEO_FORMAT_DV)
+
+
+// VideoStreaming MPEG2-TS Format Descriptor
+typedef struct _VIDEO_FORMAT_MPEG2TS
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFormatIndex;
+ UCHAR bDataOffset;
+ UCHAR bPacketLength;
+ UCHAR bStrideLength;
+} VIDEO_FORMAT_MPEG2TS, *PVIDEO_FORMAT_MPEG2TS;
+
+#define SIZEOF_VIDEO_FORMAT_MPEG2TS(pDesc) sizeof(VIDEO_FORMAT_MPEG2TS)
+
+
+// VideoStreaming MPEG1 System Stream Format Descriptor
+typedef struct _VIDEO_FORMAT_MPEG1SS
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFormatIndex;
+ UCHAR bPacketLength;
+ UCHAR bPackLength;
+ UCHAR bPackDataType;
+} VIDEO_FORMAT_MPEG1SS, *PVIDEO_FORMAT_MPEG1SS;
+
+#define SIZEOF_VIDEO_FORMAT_MPEG1SS(pDesc) sizeof(VIDEO_FORMAT_MPEG1SS)
+
+
+// VideoStreaming MPEG2-PS Format Descriptor
+typedef struct _VIDEO_FORMAT_MPEG2PS
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFormatIndex;
+ UCHAR bPacketLength;
+ UCHAR bPackLength;
+ UCHAR bPackDataType;
+} VIDEO_FORMAT_MPEG2PS, *PVIDEO_FORMAT_MPEG2PS;
+
+#define SIZEOF_VIDEO_FORMAT_MPEG2PS(pDesc) sizeof(VIDEO_FORMAT_MPEG2PS)
+
+
+// VideoStreaming MPEG4-SL Format Descriptor
+typedef struct _VIDEO_FORMAT_MPEG4SL
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFormatIndex;
+ UCHAR bPacketLength;
+} VIDEO_FORMAT_MPEG4SL, *PVIDEO_FORMAT_MPEG4SL;
+
+#define SIZEOF_VIDEO_FORMAT_MPEG4SL(pDesc) sizeof(VIDEO_FORMAT_MPEG4SL)
+
+// VideoStreaming Probe/Commit Control
+typedef struct _VS_PROBE_COMMIT_CONTROL
+{
+ USHORT bmHint;
+ UCHAR bFormatIndex;
+ UCHAR bFrameIndex;
+ ULONG dwFrameInterval;
+ USHORT wKeyFrameRate;
+ USHORT wPFrameRate;
+ USHORT wCompQuality;
+ USHORT wCompWindowSize;
+ USHORT wDelay;
+ ULONG dwMaxVideoFrameSize;
+ ULONG dwMaxPayloadTransferSize;
+} VS_PROBE_COMMIT_CONTROL, *PVS_PROBE_COMMIT_CONTROL;
+
+// VideoStreaming Still Probe/Commit Control
+typedef struct _VS_STILL_PROBE_COMMIT_CONTROL
+{
+ UCHAR bFormatIndex;
+ UCHAR bFrameIndex;
+ UCHAR bCompressionIndex;
+ ULONG dwMaxVideoFrameSize;
+ ULONG dwMaxPayloadTransferSize;
+} VS_STILL_PROBE_COMMIT_CONTROL, *PVS_STILL_PROBE_COMMIT_CONTROL;
+
+
+// Status Interrupt Packet (Video Control)
+typedef struct _VC_INTERRUPT_PACKET
+{
+ UCHAR bStatusType;
+ UCHAR bOriginator;
+ UCHAR bEvent;
+ UCHAR bSelector;
+ UCHAR bAttribute;
+ UCHAR bValue[1];
+} VC_INTERRUPT_PACKET, *PVC_INTERRUPT_PACKET;
+
+// Status Interrupt Packet (Video Control)
+typedef struct _VC_INTERRUPT_PACKET_EX
+{
+ UCHAR bStatusType;
+ UCHAR bOriginator;
+ UCHAR bEvent;
+ UCHAR bSelector;
+ UCHAR bAttribute;
+ UCHAR bValue[MAX_INTERRUPT_PACKET_VALUE_SIZE];
+} VC_INTERRUPT_PACKET_EX, *PVC_INTERRUPT_PACKET_EX;
+
+// Status Interrupt Packet (Video Streaming)
+typedef struct _VS_INTERRUPT_PACKET
+{
+ UCHAR bStatusType;
+ UCHAR bOriginator;
+ UCHAR bEvent;
+ UCHAR bValue[1];
+} VS_INTERRUPT_PACKET, *PVS_INTERRUPT_PACKET;
+
+// Status Interrupt Packet (Generic)
+typedef struct _VIDEO_INTERRUPT_PACKET
+{
+ UCHAR bStatusType;
+ UCHAR bOriginator;
+} VIDEO_INTERRUPT_PACKET, *PVIDEO_INTERRUPT_PACKET;
+
+
+// Relative property struct
+typedef struct _VIDEO_RELATIVE_PROPERTY
+{
+ UCHAR bValue;
+ UCHAR bSpeed;
+} VIDEO_RELATIVE_PROPERTY, *PVIDEO_RELATIVE_PROPERTY;
+
+// Relative Zoom control struct
+typedef struct _ZOOM_RELATIVE_PROPERTY
+{
+ UCHAR bZoom;
+ UCHAR bDigitalZoom;
+ UCHAR bSpeed;
+} ZOOM_RELATIVE_PROPERTY, *PZOOM_RELATIVE_PROPERTY;
+
+// Relative pan-tilt struct
+typedef struct _PANTILT_RELATIVE_PROPERTY
+{
+ UCHAR bPanRelative;
+ UCHAR bPanSpeed;
+ UCHAR bTiltRelative;
+ UCHAR bTiltSpeed;
+} PANTILT_RELATIVE_PROPERTY, *PPANTILT_RELATIVE_PROPERTY;
+
+typedef struct _MEDIA_INFORMATION_CONTROL
+{
+ UCHAR bmMediaType;
+ UCHAR bmWriteProtect;
+} MEDIA_INFORMATION_CONTROL, *PMEDIA_INFORMATION_CONTROL;
+
+typedef struct _TIME_CODE_INFORMATION_CONTROL
+{
+ UCHAR bcdFrame;
+ UCHAR bcdSecond;
+ UCHAR bcdMinute;
+ UCHAR bcdHour;
+} TIME_CODE_INFORMATION_CONTROL, *PTIME_CODE_INFORMATION_CONTROL;
+
+typedef struct _ATN_INFORMATION_CONTROL
+{
+ UCHAR bmMediaType;
+ DWORD dwATN_Data;
+} ATN_INFORMATION_CONTROL, *PATN_INFORMATION_CONTROL;
+
+#define VS_FORMAT_FRAME_BASED 0x10
+#define VS_FRAME_FRAME_BASED 0x11
+#define VS_FORMAT_STREAM_BASED 0x12
+
+// Format Descriptor for UVC 1.1 frame based format
+typedef struct _VIDEO_FORMAT_FRAME
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFormatIndex;
+ UCHAR bNumFrameDescriptors;
+ GUID guidFormat;
+ UCHAR bBitsPerPixel;
+ UCHAR bDefaultFrameIndex;
+ UCHAR bAspectRatioX;
+ UCHAR bAspectRatioY;
+ UCHAR bmInterlaceFlags;
+ UCHAR bCopyProtect;
+ UCHAR bVariableSize;
+} VIDEO_FORMAT_FRAME, *PVIDEO_FORMAT_FRAME;
+
+#define SIZEOF_VIDEO_FORMAT_FRAME(pDesc) sizeof(VIDEO_FORMAT_FRAME)
+
+
+// Frame Descriptor for UVC 1.1 frame based format
+typedef struct _VIDEO_FRAME_FRAME
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFrameIndex;
+ UCHAR bmCapabilities;
+ USHORT wWidth;
+ USHORT wHeight;
+ ULONG dwMinBitRate;
+ ULONG dwMaxBitRate;
+ ULONG dwDefaultFrameInterval;
+ UCHAR bFrameIntervalType;
+ ULONG dwBytesPerLine;
+ ULONG adwFrameInterval[];
+} VIDEO_FRAME_FRAME, *PVIDEO_FRAME_FRAME;
+
+__inline size_t SizeOfVideoFrameFrame(_In_ PVIDEO_FRAME_FRAME pDesc)
+{
+ if (pDesc->bFrameIntervalType == 0) { // Continuous
+ return sizeof(VIDEO_FRAME_FRAME) + (3 * sizeof(ULONG));
+ }
+ else { // Discrete
+ return sizeof(VIDEO_FRAME_FRAME) + (pDesc->bFrameIntervalType * sizeof(ULONG));
+ }
+}
+
+#define SIZEOF_VIDEO_FRAME_FRAME(pDesc) SizeOfVideoFrameFrame(pDesc)
+
+// VideoStreaming Stream Based Format Descriptor
+typedef struct _VIDEO_FORMAT_STREAM
+{
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ UCHAR bDescriptorSubtype;
+ UCHAR bFormatIndex;
+ GUID guidFormat;
+ ULONG dwPacketLength;
+} VIDEO_FORMAT_STREAM, *PVIDEO_FORMAT_STREAM;
+
+#define SIZEOF_VIDEO_FORMAT_STREAM(pDesc) sizeof(VIDEO_FORMAT_STREAM)
+
+// VideoStreaming Probe/Commit Control
+typedef struct _VS_PROBE_COMMIT_CONTROL2
+{
+ USHORT bmHint;
+ UCHAR bFormatIndex;
+ UCHAR bFrameIndex;
+ ULONG dwFrameInterval;
+ USHORT wKeyFrameRate;
+ USHORT wPFrameRate;
+ USHORT wCompQuality;
+ USHORT wCompWindowSize;
+ USHORT wDelay;
+ ULONG dwMaxVideoFrameSize;
+ ULONG dwMaxPayloadTransferSize;
+ ULONG dwClockFrequency;
+ UCHAR bmFramingInfo;
+ UCHAR bPreferredVersion;
+ UCHAR bMinVersion;
+ UCHAR bMaxVersion;
+} VS_PROBE_COMMIT_CONTROL2, *PVS_PROBE_COMMIT_CONTROL2;
+
+#pragma pack( pop, vdc_descriptor_structs )
+#pragma warning( default : 4200 )
+
+
+//
+// END - VDC Descriptor and Control Structures
+//
+
+#endif // ___UVCDESC_H___
diff --git a/device/win_usb/win_usb.cpp b/device/win_usb/win_usb.cpp
new file mode 100644
index 0000000..e872ed4
--- /dev/null
+++ b/device/win_usb/win_usb.cpp
@@ -0,0 +1,1268 @@
+#include "win_usb.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+//#include
+#include
+#include
+
+#pragma comment(lib, "setupapi.lib")
+#pragma comment(lib, "hid.lib")
+#pragma comment(lib, "winusb.lib")
+#pragma comment(lib, "rpcrt4.lib")
+
+#include "hginclude/hg_log.h"
+#include "scanner_manager.h" // for hg_scanner_mgr::ui_default_callback
+#include "usbview/enum.h"
+
+#define MSG_DEVICE_PNP WM_USER + 1 // wParam: (bool)arrive; lParam: usb_device*
+#define bzero(a, l) memset(a, 0, l)
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// usb_callback ...
+usb_callback::usb_callback(libusb_hotplug_callback_fn cb
+ , void* param)
+ : usb_cb_(cb), usb_cb_param_(param)
+{}
+usb_callback::~usb_callback()
+{}
+
+void usb_callback::notify(libusb_context* ctx, usb_device* dev, int ev)
+{
+ usb_cb_(ctx, (libusb_device*)dev, (libusb_hotplug_event)ev, usb_cb_param_);
+}
+std::string u2utf8(const wchar_t* u)
+{
+ //*
+ return hg_log::u2utf8(u);
+ /*/
+ int len = WideCharToMultiByte(CP_UTF8, 0, u, lstrlenW(u), NULL, 0, NULL, NULL);
+ char *ansi = new char[len + 4];
+
+ len = WideCharToMultiByte(CP_UTF8, 0, u, lstrlenW(u), ansi, len, NULL, NULL);
+ ansi[len--] = 0;
+
+ std::string utf8(ansi);
+ delete[] ansi;
+
+ return utf8;
+ /////*///////////////////////
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// usb_device ...
+usb_device::usb_device(const char* name) : ref_(1), name_(name ? name : ""), is_ok_(false)
+ , dev_desc_(NULL), handle_(NULL), online_(true), timout_ms_(1000)
+{
+ bzero(&guid_, sizeof(guid_));
+ id_ = usb_device::vid_pid_from_name(name);
+}
+usb_device::~usb_device()
+{
+ clear();
+}
+
+HANDLE usb_device::find_pipe(UCHAR addr, int type)
+{
+ for (size_t i = 0; i < pipes_.size(); ++i)
+ {
+ if (pipes_[i].address == addr && pipes_[i].type == type)
+ return pipes_[i].pipe;
+ }
+
+ return NULL;
+}
+int usb_device::set_timeout(HANDLE h)
+{
+ USBSCAN_TIMEOUT uto = { 0 };
+ DWORD cbr = 0;
+
+ uto.TimeoutEvent = uto.TimeoutRead = uto.TimeoutWrite = (timout_ms_ + 500) / 1000;
+
+ return DeviceIoControl(h, IOCTL_SET_TIMEOUT, &uto, sizeof(uto), NULL, 0, &cbr, NULL) ? LIBUSB_SUCCESS : LIBUSB_ERROR_IO;
+}
+
+DEVID usb_device::vid_pid_from_name(const char* name)
+{
+ // name: \\?\usb#vid_3072&pid_0239#01234567aabbccddee#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
+ DEVID id = { 0 };
+ std::string s(name ? name : "");
+ size_t pos = 0;
+
+ std::transform(s.begin(), s.end(), s.begin(), tolower);
+ pos = s.find("vid_");
+ if (pos != std::string::npos)
+ id.vid = usb_device::from_hex_string(s.c_str() + pos + 4);
+
+ pos = s.find("pid_");
+ if (pos != std::string::npos)
+ id.pid = usb_device::from_hex_string(s.c_str() + pos + 4);
+
+ return id;
+}
+DWORD usb_device::from_hex_string(const char* hex_str)
+{
+ DWORD v = 0;
+
+ for (int i = 0; hex_str[i]; ++i)
+ {
+ DWORD now = 0;
+ if (hex_str[i] >= '0' && hex_str[i] <= '9')
+ now = hex_str[i] - '0';
+ else if (hex_str[i] >= 'a' && hex_str[i] <= 'f')
+ now = hex_str[i] - 'a' + 10;
+ else if (hex_str[i] >= 'A' && hex_str[i] <= 'F')
+ now = hex_str[i] - 'A' + 10;
+ else
+ break;
+
+ v <<= 4;
+ v += now;
+ }
+
+ return v;
+}
+std::string usb_device::driver_key_name(HANDLE file)
+{
+ ULONG nBytes = 0;
+ USB_HCD_DRIVERKEY_NAME driverKeyName = { 0 };
+ BOOL success = DeviceIoControl(file, IOCTL_GET_HCD_DRIVERKEY_NAME, &driverKeyName, sizeof(driverKeyName), &driverKeyName, sizeof(driverKeyName), &nBytes, NULL);
+ std::string ret("");
+
+ if (success && driverKeyName.ActualLength > nBytes)
+ {
+ PUSB_HCD_DRIVERKEY_NAME buf = (PUSB_HCD_DRIVERKEY_NAME)GlobalAlloc(GPTR, driverKeyName.ActualLength);
+ nBytes = driverKeyName.ActualLength;
+ success = DeviceIoControl(file, IOCTL_GET_HCD_DRIVERKEY_NAME, &buf, nBytes, &buf, nBytes, &nBytes, NULL);
+ if (success)
+ {
+ ret = u2utf8(buf->DriverKeyName);
+ }
+ GlobalFree(buf);
+ }
+
+ return ret;
+}
+std::string usb_device::parent_hub_path_name(int vid, int pid, int *addr)
+{
+ std::string ret("");
+ GUID guid = GUID_DEVINTERFACE_USB_HUB;
+ HDEVINFO dev = SetupDiGetClassDevsW(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ int port = addr ? *addr : -1;
+
+ if (dev)
+ {
+ SP_DEVICE_INTERFACE_DATA id = { 0 };
+ SP_DEVINFO_DATA did;
+ int ind = 0;
+
+ id.cbSize = sizeof(id);
+ while (SetupDiEnumDeviceInterfaces(dev, NULL, &guid, ind++, &id))
+ {
+ PSP_DEVICE_INTERFACE_DETAIL_DATA_W buf = NULL;
+ DWORD size = 0;
+
+ if (!SetupDiGetDeviceInterfaceDetailW(dev, &id, buf, 0, &size, NULL) &&
+ GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ int bytes = size + sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + 40;
+ buf = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)new wchar_t[bytes];
+ memset(buf, 0, bytes * 2);
+ buf->cbSize = sizeof(*buf);
+ if (SetupDiGetDeviceInterfaceDetailW(dev, &id, buf, buf->cbSize + size, NULL, NULL))
+ {
+ std::string n(u2utf8(buf->DevicePath));
+ if (find_vid_pid_in_hub(n.c_str(), vid, pid, &port))
+ ret = n;
+ }
+ delete[] buf;
+ if (ret.length())
+ break;
+ }
+ }
+ SetupDiDestroyDeviceInfoList(dev);
+ }
+ if (addr)
+ *addr = port;
+ HG_VLOG_MINI_4(HG_LOG_LEVEL_DEBUG_INFO, "Parent hub for %04X:%04X is: %s (+%d)\r\n", vid, pid, ret.c_str(), port);
+
+ return ret;
+}
+bool usb_device::find_vid_pid_in_hub(const char* utf8_hub_path_name, int vid, int pid, int* addr)
+{
+ bool ret = false;
+ HANDLE h = CreateFileA(utf8_hub_path_name, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+
+ if (h == INVALID_HANDLE_VALUE)
+ return ret;
+
+ int bytes = sizeof(USB_NODE_CONNECTION_INFORMATION_EX) + (sizeof(USB_PIPE_INFO) * 30);
+ PUSB_NODE_CONNECTION_INFORMATION_EX connectionInfoEx = (PUSB_NODE_CONNECTION_INFORMATION_EX)new char[bytes];
+ memset(connectionInfoEx, 0, bytes);
+ if (addr && *addr != -1)
+ {
+ connectionInfoEx->ConnectionIndex = *addr;
+ DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, connectionInfoEx,
+ bytes, connectionInfoEx, bytes, (LPDWORD)&bytes, NULL);
+ ret = connectionInfoEx->DeviceDescriptor.idVendor == vid && connectionInfoEx->DeviceDescriptor.idProduct == pid;
+ }
+ else
+ {
+ for (int i = 1; !ret; ++i)
+ {
+ DWORD len = bytes;
+ connectionInfoEx->ConnectionIndex = i;
+ if (!DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, connectionInfoEx,
+ bytes, connectionInfoEx, bytes, (LPDWORD)&bytes, NULL))
+ break;
+ ret = connectionInfoEx->DeviceDescriptor.idVendor == vid && connectionInfoEx->DeviceDescriptor.idProduct == pid;
+ }
+ if (ret && addr)
+ *addr = connectionInfoEx->ConnectionIndex;
+ }
+ delete[] connectionInfoEx;
+ CloseHandle(h);
+
+ return ret;
+}
+int usb_device::get_device_address(const char* device_name, LPGUID lpguid)
+{
+ int addr = -1;
+ GUID guid = lpguid ? *lpguid : GUID_DEVINTERFACE_USB_DEVICE;
+ HDEVINFO dev = NULL;
+ std::string ret(""), src(device_name);
+
+ dev = SetupDiGetClassDevsW(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ if (dev)
+ {
+ SP_DEVICE_INTERFACE_DATA id = { 0 };
+ SP_DEVINFO_DATA did;
+ int ind = 0;
+ size_t pos = src.find("{");
+
+ if (pos != std::string::npos)
+ src.erase(pos);
+ id.cbSize = sizeof(id);
+ while (SetupDiEnumDeviceInterfaces(dev, NULL, &guid, ind++, &id))
+ {
+ PSP_DEVICE_INTERFACE_DETAIL_DATA_W buf = NULL;
+ DWORD size = 0;
+
+ ret = "";
+ if (!SetupDiGetDeviceInterfaceDetailW(dev, &id, buf, 0, &size, NULL) &&
+ GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ buf = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)new wchar_t[size + 4];
+ memset(buf, 0, (size + 4) * 2);
+ buf->cbSize = sizeof(*buf);
+ if (SetupDiGetDeviceInterfaceDetailW(dev, &id, buf, buf->cbSize + size, NULL, NULL))
+ {
+ wchar_t* l = wcsstr(buf->DevicePath, L"{");
+ if (l)
+ *l = 0;
+ ret = u2utf8(buf->DevicePath);
+ }
+ delete buf;
+ }
+
+ if (stricmp(ret.c_str(), src.c_str()))
+ continue;
+
+ SP_DEVINFO_DATA dd = { 0 };
+ dd.cbSize = sizeof(dd);
+ // for (int i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dd); ++i)
+ if (SetupDiEnumDeviceInfo(dev, ind - 1, &dd))
+ {
+ SetupDiGetDeviceRegistryPropertyA(dev, &dd, SPDRP_ADDRESS, NULL, (PBYTE)&addr, sizeof(addr), NULL);
+ }
+ break;
+ }
+ SetupDiDestroyDeviceInfoList(dev);
+ }
+
+ return addr;
+}
+
+long usb_device::add_ref(void)
+{
+ return InterlockedIncrement(&ref_);
+}
+long usb_device::release(void)
+{
+ long ref = InterlockedDecrement(&ref_);
+
+ if (ref == 0)
+ delete this;
+
+ return ref;
+}
+
+bool usb_device::operator==(const char* name)
+{
+ return name_ == name;
+}
+bool usb_device::operator==(const DEVID& id)
+{
+ return id_.vid == id.vid && id_.pid == id.pid;
+}
+
+usb_device& usb_device::operator=(const DEVID& id)
+{
+ id_ = id;
+
+ return *this;
+}
+usb_device& usb_device::operator=(const GUID& guid)
+{
+ guid_ = guid;
+
+ return *this;
+}
+
+std::string usb_device::name(void)
+{
+ return name_;
+}
+GUID usb_device::guid(void)
+{
+ return guid_;
+}
+DEVID usb_device::id(void)
+{
+ return id_;
+}
+bool usb_device::is_ok(void)
+{
+ return is_ok_;
+}
+bool usb_device::is_open(void)
+{
+ return handle_ != NULL;
+}
+bool usb_device::is_online(void)
+{
+ return online_;
+}
+void usb_device::set_online(bool online)
+{
+ online_ = online;
+}
+uint8_t usb_device::port(void)
+{
+ if (!dev_desc_)
+ init();
+ return port_;
+}
+
+bool usb_device::init(void)
+{
+ PUSBDEVICEINFO info = NULL;
+ GUID guid;
+ int addr = -1;
+
+ clear();
+ for (size_t i = 0; i < _countof(ovl_); ++i)
+ ovl_[i].hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+
+ UuidFromStringA((RPC_CSTR)HG_SCANNER_GUID, &guid);
+ addr = usb_device::get_device_address(name_.c_str(), &guid);
+ //if (addr != -1)
+ {
+ std::string path(usb_device::parent_hub_path_name(id_.vid, id_.pid, &addr));
+ if (!path.empty())
+ {
+ HANDLE h = CreateFileA(path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ info = enumerate_hub_port(h, addr);
+ CloseHandle(h);
+ }
+ }
+ }
+
+#define COPY_MEMBER(d, s, m) \
+ (d)->m = (s)->m
+
+ if (info)
+ {
+ port_ = addr;
+ if (info->ConnectionInfo)
+ {
+ dev_desc_ = new libusb_device_descriptor;
+ COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bLength);
+ COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bDescriptorType);
+ COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bcdUSB);
+ COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bDeviceClass);
+ COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bDeviceSubClass);
+ COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bDeviceProtocol);
+ COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bMaxPacketSize0);
+ COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, idVendor);
+ COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, idProduct);
+ COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bcdDevice);
+ COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, iManufacturer);
+ COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, iProduct);
+ COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, iSerialNumber);
+ COPY_MEMBER(dev_desc_, &info->ConnectionInfo->DeviceDescriptor, bNumConfigurations);
+ }
+ if (info->ConfigDesc)
+ {
+ libusb_config_descriptor* desc = NULL;
+ PUSB_COMMON_DESCRIPTOR cmn = (PUSB_COMMON_DESCRIPTOR)(info->ConfigDesc + 1);
+ int if_ind = 0, ep_ind = 0;
+
+ while (cmn->bLength)
+ {
+ if (USB_CONFIGURATION_DESCRIPTOR_TYPE == cmn->bDescriptorType)
+ {
+ PUSB_CONFIGURATION_DESCRIPTOR conf = (PUSB_CONFIGURATION_DESCRIPTOR)cmn;
+ if (desc)
+ cfg_desc_.push_back(desc);
+
+ desc = new libusb_config_descriptor;
+ memset(desc, 0, sizeof(*desc));
+ COPY_MEMBER(desc, conf, bLength);
+ COPY_MEMBER(desc, conf, bDescriptorType);
+ COPY_MEMBER(desc, conf, wTotalLength);
+ COPY_MEMBER(desc, conf, bNumInterfaces);
+ COPY_MEMBER(desc, conf, bConfigurationValue);
+ COPY_MEMBER(desc, conf, iConfiguration);
+ COPY_MEMBER(desc, conf, bmAttributes);
+ COPY_MEMBER(desc, conf, MaxPower);
+ desc->usb_if = new libusb_interface[desc->bNumInterfaces];
+ memset(desc->usb_if, 0, desc->bNumInterfaces * sizeof(desc->usb_if[0]));
+ ep_ind = if_ind = 0;
+ }
+ else if (USB_INTERFACE_DESCRIPTOR_TYPE == cmn->bDescriptorType)
+ {
+ PUSB_INTERFACE_DESCRIPTOR ifd = (PUSB_INTERFACE_DESCRIPTOR)cmn;
+ libusb_interface_descriptor* uid = NULL;
+
+ desc->usb_if[if_ind].num_altsetting = 1;
+ desc->usb_if[if_ind].altsetting = uid = new libusb_interface_descriptor;
+ COPY_MEMBER(uid, ifd, bLength);
+ COPY_MEMBER(uid, ifd, bDescriptorType);
+ COPY_MEMBER(uid, ifd, bInterfaceNumber);
+ COPY_MEMBER(uid, ifd, bAlternateSetting);
+ COPY_MEMBER(uid, ifd, bNumEndpoints);
+ COPY_MEMBER(uid, ifd, bInterfaceClass);
+ COPY_MEMBER(uid, ifd, bInterfaceSubClass);
+ COPY_MEMBER(uid, ifd, bInterfaceProtocol);
+ COPY_MEMBER(uid, ifd, iInterface);
+ uid->endpoint = new libusb_endpoint_descriptor[desc->usb_if[if_ind].altsetting->bNumEndpoints];
+ if_ind++;
+ ep_ind = 0;
+ }
+ else if (USB_ENDPOINT_DESCRIPTOR_TYPE == cmn->bDescriptorType)
+ {
+ PUSB_ENDPOINT_DESCRIPTOR endp = (PUSB_ENDPOINT_DESCRIPTOR)cmn;
+ libusb_endpoint_descriptor* uep = (libusb_endpoint_descriptor*)((void*)&desc->usb_if[if_ind - 1].altsetting->endpoint[ep_ind]);
+
+ COPY_MEMBER(uep, endp, bLength);
+ COPY_MEMBER(uep, endp, bDescriptorType);
+ COPY_MEMBER(uep, endp, bEndpointAddress);
+ COPY_MEMBER(uep, endp, bmAttributes);
+ COPY_MEMBER(uep, endp, wMaxPacketSize);
+ COPY_MEMBER(uep, endp, bInterval);
+ uep->extra = NULL;
+ ep_ind++;
+ }
+ cmn = (PUSB_COMMON_DESCRIPTOR)((PCHAR)cmn + cmn->bLength);
+ }
+
+ if (desc)
+ cfg_desc_.push_back(desc);
+
+ is_ok_ = !cfg_desc_.empty();
+ }
+
+ free_usb_device_info(info);
+ }
+
+ return is_ok_;
+}
+void usb_device::clear(void)
+{
+ close();
+ for (size_t i = 0; i < _countof(ovl_); ++i)
+ CloseHandle(ovl_[i].hEvent);
+
+
+ if (dev_desc_)
+ delete dev_desc_;
+ dev_desc_ = NULL;
+
+ for (size_t i = 0; i < cfg_desc_.size(); ++i)
+ {
+ if (cfg_desc_[i]->usb_if)
+ {
+ for (size_t j = 0; j < cfg_desc_[i]->bNumInterfaces; ++j)
+ {
+ if (cfg_desc_[i]->usb_if[j].altsetting->endpoint)
+ delete[] cfg_desc_[i]->usb_if[j].altsetting->endpoint;
+ }
+ delete[] cfg_desc_[i]->usb_if;
+ }
+ delete cfg_desc_[i];
+ }
+ cfg_desc_.clear();
+ is_ok_ = false;
+}
+void usb_device::online_statu_changed(bool online)
+{
+ online_ = online;
+}
+int usb_device::get_descriptor(libusb_device_descriptor* desc)
+{
+ if (dev_desc_)
+ memcpy(desc, dev_desc_, sizeof(*desc));
+ else
+ {
+ char cls[128] = { 0 };
+
+ SetupDiGetClassDescriptionA(&guid_, cls, _countof(cls) - 1, NULL);
+ std::transform(cls, cls + lstrlenA(cls), cls, tolower);
+ if (strcmp(cls, "usb") == 0)
+ desc->bDeviceClass = libusb_class_code::LIBUSB_CLASS_HUB;
+ else if (strcmp(cls, "image") == 0)
+ desc->bDeviceClass = libusb_class_code::LIBUSB_CLASS_IMAGE;
+ else if (strcmp(cls, "hidclass") == 0)
+ desc->bDeviceClass = libusb_class_code::LIBUSB_CLASS_HID;
+ else
+ desc->bDeviceClass = libusb_class_code::LIBUSB_CLASS_VENDOR_SPEC;
+ desc->idVendor = id_.vid;
+ desc->idProduct = id_.pid;
+ desc->bcdUSB = 0x200; // USB2.0 ?
+ desc->bcdDevice = 0; // ?
+ desc->bDescriptorType = libusb_descriptor_type::LIBUSB_DT_DEVICE;
+ desc->bDeviceProtocol = 0;
+ desc->bDeviceSubClass = libusb_class_code::LIBUSB_CLASS_IMAGE;
+ desc->bLength = sizeof(*desc);
+ desc->bMaxPacketSize0 = 512;
+ desc->bNumConfigurations = 1; // ?
+ desc->iManufacturer = 0;
+ desc->iSerialNumber = 0;
+ desc->iProduct = 0;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+int usb_device::get_config_descriptor(int index, libusb_config_descriptor** desc)
+{
+ if (index >= 0 && index < cfg_desc_.size())
+ *desc = cfg_desc_[index];
+ else
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ return LIBUSB_SUCCESS;
+}
+int usb_device::open(libusb_device_handle** dev_handle)
+{
+ if (handle_)
+ return LIBUSB_ERROR_BUSY;
+
+ if (!dev_desc_)
+ init();
+ HANDLE h = CreateFileA(name_.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ *dev_handle = NULL;
+
+ return online_ ? LIBUSB_ERROR_IO : LIBUSB_ERROR_NO_DEVICE;
+ }
+
+ USBSCAN_PIPE_CONFIGURATION upc = { 0 };
+ DWORD cbr = 0;
+ if (DeviceIoControl(h, IOCTL_GET_PIPE_CONFIGURATION, NULL, 0, &upc, sizeof(upc), &cbr, NULL))
+ {
+ for (int i = 0; i < upc.NumberOfPipes; ++i)
+ {
+ USBPIPE up = { 0 };
+ char ind[20] = { 0 };
+ up.address = upc.PipeInfo[i].EndpointAddress;
+ up.type = upc.PipeInfo[i].PipeType;
+ sprintf_s(ind, _countof(ind) - 1, "\\%04d", i);
+ up.pipe = CreateFileA((name_ + ind).c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ if (up.pipe != INVALID_HANDLE_VALUE)
+ {
+ set_timeout(up.pipe);
+ pipes_.push_back(up);
+ }
+ }
+ }
+ set_timeout(h);
+ handle_ = (libusb_device_handle*)h;
+ *dev_handle = (libusb_device_handle*)this;
+
+ return LIBUSB_SUCCESS;
+}
+int usb_device::close(void)
+{
+ for (size_t i = 0; i < pipes_.size(); ++i)
+ {
+ CancelIo(pipes_[i].pipe);
+ CloseHandle(pipes_[i].pipe);
+ }
+ pipes_.clear();
+
+ if (handle_)
+ {
+ OVERLAPPED ovl = { 0 };
+ PIPE_TYPE type = ALL_PIPE;
+ DWORD cbr = 0;
+
+ ovl.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
+ DeviceIoControl(handle_, IOCTL_CANCEL_IO, &type, sizeof(type), NULL, 0, &cbr, &ovl);
+ WaitForSingleObject(ovl.hEvent, 1000);
+ CloseHandle(ovl.hEvent);
+
+ CancelIo(handle_);
+ CloseHandle((HANDLE)handle_);
+ handle_ = NULL;
+ }
+
+ return LIBUSB_SUCCESS;
+}
+int usb_device::set_timeout(unsigned milliseconds)
+{
+ timout_ms_ = milliseconds;
+ for (size_t i = 0; i < pipes_.size(); ++i)
+ {
+ set_timeout(pipes_[i].pipe);
+ }
+ if (handle_)
+ set_timeout(handle_);
+
+ return LIBUSB_SUCCESS;
+}
+int usb_device::transfer_bulk(unsigned endpoint, unsigned char* data, int* length, unsigned int timeout)
+{
+ typedef BOOL(WINAPI* file_io)(HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED);
+ int ret = LIBUSB_ERROR_PIPE;
+ HANDLE h = find_pipe(endpoint, USBSCAN_PIPE_BULK);
+ file_io fio = (endpoint & BULKIN_FLAG) ? ReadFile : (file_io)WriteFile;
+ LPOVERLAPPED ovl = (endpoint & BULKIN_FLAG) ? &ovl_[OVL_BULK_IN] : &ovl_[OVL_BULK_OUT];
+
+ if (h)
+ {
+ DWORD io = 0;
+ BOOL result = FALSE;
+ HANDLE he = ovl->hEvent;
+
+ memset(ovl, 0, sizeof(*ovl));
+ ovl->hEvent = he;
+ result = fio(h, data, *length, &io, ovl);
+ if (result)
+ {
+ FlushFileBuffers(h);
+ *length = io;
+ ret = LIBUSB_SUCCESS;
+ }
+ else
+ {
+ io = GetLastError();
+ if (io == ERROR_IO_PENDING)
+ {
+ WaitForSingleObject(he, 500);
+ GetOverlappedResult(h, &ovl_[OVL_BULK_IN], &io, FALSE);
+ *length = io;
+ ret = LIBUSB_SUCCESS;
+ }
+ }
+ }
+
+ return ret;
+}
+int usb_device::transfer_control(uint8_t type, uint8_t req, uint16_t val, uint16_t ind, unsigned char* data, uint16_t len, unsigned timeout)
+{
+ int ret = LIBUSB_ERROR_PIPE;
+ _IO_BLOCK_EX irp;
+
+ if ((HANDLE)handle_ != INVALID_HANDLE_VALUE)
+ {
+ DWORD io = 0;
+ LPOVERLAPPED ovl = ovl_ + OVL_CONTROL;
+
+ ResetEvent(ovl->hEvent);
+ irp.bmRequestType = (type >> 5) & 0x03;
+ irp.bRequest = req;
+ irp.fTransferDirectionIn = type >> 7;
+ irp.pbyData = data;
+ irp.uIndex = ind;
+ irp.uLength = len;
+ irp.uOffset = val;
+ if (DeviceIoControl((HANDLE)handle_, IOCTL_SEND_USB_REQUEST, &irp, sizeof(irp), data, len, &io, ovl))
+ ret = LIBUSB_SUCCESS;
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ ret = WaitForSingleObject(ovl->hEvent, timeout) == WAIT_TIMEOUT ? LIBUSB_ERROR_TIMEOUT : LIBUSB_SUCCESS;
+ }
+ }
+
+ return ret;
+}
+int usb_device::transfer_interrupt(unsigned endpoint, unsigned char* data, int* length, unsigned int timeout)
+{
+ int ret = LIBUSB_ERROR_PIPE;
+ HANDLE h = find_pipe(endpoint, USBSCAN_PIPE_INTERRUPT);
+ DWORD io = 0;
+
+ if (h)
+ {
+ if (DeviceIoControl(h, IOCTL_WAIT_ON_DEVICE_EVENT, NULL, 0, data, *length, &io, &ovl_[OVL_INTERRUPT]))
+ {
+ ret = LIBUSB_SUCCESS;
+ *length = io;
+ }
+ else
+ {
+ io = GetLastError();
+ if (io == ERROR_IO_PENDING)
+ {
+ GetOverlappedResult(h, &ovl_[OVL_INTERRUPT], &io, TRUE);
+ ret = LIBUSB_SUCCESS;
+ *length = io;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// usb_monitor ...
+
+usb_monitor* usb_monitor::usb_monitor_ = NULL;
+
+usb_monitor::usb_monitor() : wnd_monitor_(NULL), handle_msg_id_(0), run_(true)
+{
+ handle_msg_.reset(new std::thread(&usb_monitor::thread_handle_device_change_msg, this));
+}
+usb_monitor::~usb_monitor()
+{
+ quit();
+}
+
+LRESULT CALLBACK usb_monitor::monitor_wnd_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
+{
+ usb_monitor* monitor = (usb_monitor*)GetPropW(hwnd, MONITOR_WINDOW_OWNER);
+
+ if (msg == WM_CREATE)
+ {
+ LPCREATESTRUCTW data = (LPCREATESTRUCTW)lp;
+
+ SetPropW(hwnd, MONITOR_WINDOW_OWNER, (HANDLE)data->lpCreateParams);
+
+ return 0;
+ }
+ else if (!monitor || msg != WM_DEVICECHANGE)
+ return DefWindowProcW(hwnd, msg, wp, lp);
+
+ return monitor->on_usb_pnp(wp, lp);
+}
+void usb_monitor::register_monitor_wnd(const wchar_t* cls)
+{
+ WNDCLASSW wc = { 0 };
+
+ wc.lpfnWndProc = &usb_monitor::monitor_wnd_proc;
+ wc.lpszClassName = cls;
+ wc.hInstance = GetModuleHandleW(NULL);
+
+ RegisterClassW(&wc);
+}
+
+void usb_monitor::notify_usb_event(usb_device*& dev, bool arrive)
+{
+ std::lock_guard lock(lock_);
+ int ev = arrive ? LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED : LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT;
+ DEVID id = dev->id();
+
+ if (arrive)
+ {
+ bool found = false;
+ for(size_t i = 0; i < devices_.size(); ++i)
+ {
+ if (!(devices_[i]->id() == id))
+ continue;
+
+ if (!devices_[i]->is_online() && devices_[i]->is_open())
+ {
+ dev->release();
+ dev = devices_[i];
+ dev->add_ref();
+ dev->set_online(true);
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ devices_.push_back(dev);
+ dev->add_ref();
+ }
+ }
+ else
+ {
+ for (size_t i = 0; i < devices_.size(); ++i)
+ {
+ if (!(devices_[i]->id() == id))
+ continue;
+
+ dev->release();
+ dev = devices_[i];
+ dev->add_ref();
+ if (dev->is_open())
+ {
+ dev->set_online(false);
+ }
+ else
+ {
+ devices_.erase(devices_.begin() + i);
+ dev->release();
+ }
+ break;
+ }
+ }
+
+ for (size_t i = 0; i < cbs_.size(); ++i)
+ cbs_[i]->notify((libusb_context*)this, dev, ev);
+}
+int usb_monitor::on_usb_pnp(WPARAM wp, LPARAM lp)
+{
+ int ret = 0;
+ DEV_BROADCAST_HDR* dbt = (DEV_BROADCAST_HDR*)lp;
+
+ if (!dbt || dbt->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
+ return wp == DBT_DEVICEQUERYREMOVE;
+
+ PDEV_BROADCAST_DEVICEINTERFACE_W dev = (PDEV_BROADCAST_DEVICEINTERFACE_W)lp;
+ if (dev->dbcc_classguid == GUID_DEVINTERFACE_USB_DEVICE)
+ {
+ return wp == DBT_DEVICEQUERYREMOVE;
+ }
+
+ if (wp == DBT_DEVICEQUERYREMOVE)
+ return cur_dev_name_ != u2utf8(dev->dbcc_name);
+
+ usb_device* ud = new usb_device(u2utf8(dev->dbcc_name).c_str());
+ *ud = dev->dbcc_classguid;
+ if (!PostThreadMessageW(handle_msg_id_, MSG_DEVICE_PNP, wp == DBT_DEVICEARRIVAL, (LPARAM)ud))
+ ud->release();
+
+ return ret;
+}
+void usb_monitor::find_usb(std::vector& usb_devs)
+{
+ GUID hid = GUID_DEVINTERFACE_USB_DEVICE; // GUID_DEVINTERFACE_USB_HUB
+ HDEVINFO dev_info = NULL;
+ int ind = 0;
+ SP_DEVICE_INTERFACE_DATA id = { 0 };
+
+ // HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\DeviceClasses\{a5dcbf10-6530-11d2-901f-00c04fb951ed}
+ // HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Enum\USB
+ // UuidFromStringW((RPC_WSTR)WIN_USB_GUID, &hid);
+
+ dev_info = SetupDiGetClassDevsW(&hid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ if (!dev_info)
+ return;
+
+ id.cbSize = sizeof(id);
+ while (SetupDiEnumDeviceInterfaces(dev_info, NULL, &hid, ind++, &id))
+ {
+ PSP_DEVICE_INTERFACE_DETAIL_DATA_W buf = NULL;
+ DWORD size = 0;
+
+ SetupDiGetDeviceInterfaceDetailW(dev_info, &id, buf, 0, &size, NULL);
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ buf = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)new wchar_t[size + 4];
+ memset(buf, 0, (size + 4) * 2);
+ buf->cbSize = sizeof(*buf);
+ if (SetupDiGetDeviceInterfaceDetailW(dev_info, &id, buf, buf->cbSize + size, NULL, NULL))
+ {
+ usb_devs.push_back(u2utf8(buf->DevicePath));
+ }
+ delete[](char*)buf;
+ }
+ id.cbSize = sizeof(id);
+ }
+ SetupDiDestroyDeviceInfoList(dev_info);
+}
+
+usb_callback* usb_monitor::reg_callback(libusb_hotplug_callback_fn cb, void* param)
+{
+ usb_callback* obj = new usb_callback(cb, param);
+
+ {
+ std::lock_guard lock(lock_);
+ cbs_.push_back(obj);
+ }
+
+ return obj;
+}
+void usb_monitor::unreg_callback(usb_callback* cb)
+{
+ std::lock_guard lock(lock_);
+
+ for (int i = 0; i < cbs_.size(); ++i)
+ {
+ if (cbs_[i] == cb)
+ {
+ cbs_.erase(cbs_.begin() + i);
+ delete cb;
+ break;
+ }
+ }
+}
+
+void usb_monitor::thread_run_device_event_wnd(void)
+{
+ MSG msg = { 0 };
+ const wchar_t* cls = L"usb_pnp_wnd";
+
+ register_monitor_wnd(cls);
+ wnd_monitor_ = CreateWindowW(cls, L"usb", WS_POPUP, 0, 0, 0, 0, NULL, NULL, GetModuleHandleW(NULL), this);
+ if (!IsWindow(wnd_monitor_))
+ {
+ // HG_VLOG_MINI_1(HG_LOG_LEVEL_FATAL, "create monitor window failed: %d\n", GetLastError());
+ if(run_)
+ Sleep(1000);
+
+ return;
+ }
+
+ if (run_)
+ {
+ BOOL ret = TRUE;
+ DEV_BROADCAST_HDR dbh = { 0 };
+ HDEVNOTIFY notify = NULL;
+
+ std::vector first;
+ find_usb(first);
+ for (size_t i = 0; i < first.size(); ++i)
+ {
+ usb_device* dev = new usb_device(first[i].c_str());
+ notify_usb_event(dev, true);
+ dev->release();
+ }
+
+ dbh.dbch_size = sizeof(dbh);
+ dbh.dbch_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+ notify = RegisterDeviceNotificationW(wnd_monitor_, &dbh, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
+ ret = GetLastError();
+ // find_usb();
+ while (run_ && (ret = GetMessageW(&msg, wnd_monitor_, 0, 0)))
+ {
+ if (ret == -1)
+ {
+ // HG_VLOG_MINI_1(HG_LOG_LEVEL_FATAL, "GetMessageW returned -1 with error: %d\n", GetLastError());
+ break;
+ }
+
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ UnregisterDeviceNotification(notify);
+ }
+
+ DestroyWindow(wnd_monitor_);
+ UnregisterClassW(cls, GetModuleHandleW(NULL));
+}
+void usb_monitor::thread_handle_device_change_msg(void)
+{
+ MSG msg = { 0 };
+ BOOL ret = FALSE;
+
+ handle_msg_id_ = GetCurrentThreadId();
+ while ((ret = GetMessageW(&msg, NULL, 0, 0)))
+ {
+ if (ret == -1)
+ break;
+
+ if (msg.message == MSG_DEVICE_PNP)
+ {
+ char buf[40] = { 0 };
+ usb_device* dev = (usb_device*)msg.lParam;
+ //if(msg.wParam)
+ // dev->init();
+ notify_usb_event(dev, msg.wParam);
+
+ // dev->release ?
+ dev->release();
+ }
+ else
+ {
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+ }
+}
+void usb_monitor::quit(void)
+{
+ run_ = false;
+ if (handle_msg_.get())
+ {
+ PostThreadMessageW(handle_msg_id_, WM_QUIT, 0, 0);
+ if (handle_msg_->joinable())
+ handle_msg_->join();
+ handle_msg_.reset();
+ }
+
+ if (IsWindow(wnd_monitor_))
+ {
+ PostMessage(wnd_monitor_, WM_QUIT, 0, 0);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// libusb APIs ...
+int LIBUSB_CALL libusb_init(libusb_context** ctx)
+{
+ if (ctx)
+ *ctx = (libusb_context*)new usb_monitor();
+ else if(!usb_monitor::usb_monitor_)
+ usb_monitor::usb_monitor_ = new usb_monitor();
+
+ return LIBUSB_SUCCESS;
+}
+void LIBUSB_CALL libusb_exit(libusb_context* ctx)
+{
+ usb_monitor* usb = (usb_monitor*)ctx;
+ if (usb)
+ {
+ usb->quit();
+ delete usb;
+ }
+}
+int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context* ctx,
+ libusb_hotplug_event events,
+ libusb_hotplug_flag flags,
+ int vendor_id, int product_id,
+ int dev_class,
+ libusb_hotplug_callback_fn cb_fn,
+ void* user_data,
+ libusb_hotplug_callback_handle* callback_handle)
+{
+ if (ctx)
+ *callback_handle = (libusb_hotplug_callback_handle)((usb_monitor*)ctx)->reg_callback(cb_fn, user_data);
+ else if (usb_monitor::usb_monitor_)
+ *callback_handle = (libusb_hotplug_callback_handle)usb_monitor::usb_monitor_->reg_callback(cb_fn, user_data);
+
+ return LIBUSB_SUCCESS;
+}
+void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context* ctx, libusb_hotplug_callback_handle callback_handle)
+{
+ if (ctx)
+ ((usb_monitor*)ctx)->unreg_callback((usb_callback*)callback_handle);
+ else if (usb_monitor::usb_monitor_)
+ usb_monitor::usb_monitor_->unreg_callback((usb_callback*)callback_handle);
+}
+int LIBUSB_CALL libusb_handle_events_timeout(libusb_context* ctx, struct timeval* tv)
+{
+ // NOTE: for the caller is in a separate thread, this call will blocked while process exited, or libusb_exit called
+ ((usb_monitor*)ctx)->thread_run_device_event_wnd();
+
+ return LIBUSB_SUCCESS;
+}
+libusb_device* LIBUSB_CALL libusb_ref_device(libusb_device* dev)
+{
+ if (dev)
+ ((usb_device*)dev)->add_ref();
+
+ return dev;
+}
+void LIBUSB_CALL libusb_unref_device(libusb_device* dev)
+{
+ if (dev)
+ ((usb_device*)dev)->release();
+}
+int LIBUSB_CALL libusb_get_device_descriptor(libusb_device* dev, struct libusb_device_descriptor* desc)
+{
+ if (!dev)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ return ((usb_device*)dev)->get_descriptor(desc);
+}
+int LIBUSB_CALL libusb_get_config_descriptor(libusb_device* dev, uint8_t config_index, struct libusb_config_descriptor** config)
+{
+ if (!dev)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ return ((usb_device*)dev)->get_config_descriptor(config_index, config);
+}
+void LIBUSB_CALL libusb_free_config_descriptor(struct libusb_config_descriptor* config)
+{
+ // because the configuration descriptor is member of device, nothing to do here ...
+}
+
+int LIBUSB_CALL libusb_reset_device(libusb_device_handle* dev_handle)
+{
+ // to be implemented later ...
+
+ return LIBUSB_SUCCESS;
+}
+ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context* ctx, libusb_device*** list)
+{
+ // to be implemented later ...
+
+ return 0;
+}
+void LIBUSB_CALL libusb_free_device_list(libusb_device** list, int unref_devices)
+{
+ // to be implemented later ...
+
+}
+int LIBUSB_CALL libusb_set_auto_detach_kernel_driver(libusb_device_handle* dev_handle, int enable)
+{
+ // to be implemented later ...
+
+ return LIBUSB_SUCCESS;
+}
+int LIBUSB_CALL libusb_claim_interface(libusb_device_handle* dev_handle, int interface_number)
+{
+ // to be implemented later ...
+
+ return LIBUSB_SUCCESS;
+}
+int LIBUSB_CALL libusb_release_interface(libusb_device_handle* dev_handle, int interface_number)
+{
+ // to be implemented later ...
+
+ return LIBUSB_SUCCESS;
+}
+int LIBUSB_CALL libusb_set_configuration(libusb_device_handle* dev_handle, int configuration)
+{
+ // to be implemented later ...
+
+ return LIBUSB_SUCCESS;
+}
+int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle* dev_handle, int interface_number)
+{
+ // to be implemented later ...
+
+ return LIBUSB_SUCCESS;
+}
+int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle* dev_handle, int interface_number)
+{
+ // to be implemented later ...
+
+ return LIBUSB_SUCCESS;
+}
+int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle* dev_handle, int interface_number)
+{
+ // to be implemented later ...
+
+ return LIBUSB_SUCCESS;
+}
+libusb_device_handle* LIBUSB_CALL libusb_open_device_with_vid_pid(libusb_context* ctx, uint16_t vendor_id, uint16_t product_id)
+{
+ // to be implemented later ...
+
+ return NULL;
+}
+int LIBUSB_CALL libusb_clear_halt(libusb_device_handle* dev_handle, unsigned char endpoint)
+{
+ // to be implemented later ...
+
+ return LIBUSB_SUCCESS;
+}
+const char* LIBUSB_CALL libusb_error_name(int errcode)
+{
+#define RETURN_IF(e) \
+ if(errcode == e) \
+ return #e;
+
+ RETURN_IF(LIBUSB_SUCCESS);
+ RETURN_IF(LIBUSB_ERROR_IO);
+ RETURN_IF(LIBUSB_ERROR_INVALID_PARAM);
+ RETURN_IF(LIBUSB_ERROR_ACCESS);
+ RETURN_IF(LIBUSB_ERROR_NO_DEVICE);
+ RETURN_IF(LIBUSB_ERROR_NOT_FOUND);
+ RETURN_IF(LIBUSB_ERROR_BUSY);
+ RETURN_IF(LIBUSB_ERROR_TIMEOUT);
+ RETURN_IF(LIBUSB_ERROR_OVERFLOW);
+ RETURN_IF(LIBUSB_ERROR_PIPE);
+ RETURN_IF(LIBUSB_ERROR_INTERRUPTED);
+ RETURN_IF(LIBUSB_ERROR_NO_MEM);
+ RETURN_IF(LIBUSB_ERROR_NOT_SUPPORTED);
+ RETURN_IF(LIBUSB_ERROR_OTHER);
+
+ return "Unknown error";
+}
+
+int LIBUSB_CALL libusb_open(libusb_device* dev, libusb_device_handle** dev_handle)
+{
+ if (!dev)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ return ((usb_device*)dev)->open(dev_handle);
+}
+void LIBUSB_CALL libusb_close(libusb_device_handle* dev_handle)
+{
+ if (dev_handle)
+ {
+ ((usb_device*)dev_handle)->close();
+ }
+}
+int LIBUSB_CALL libusb_control_transfer(libusb_device_handle* dev_handle,
+ uint8_t request_type, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
+ unsigned char* data, uint16_t wLength, unsigned int timeout)
+{
+ if (!dev_handle)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ return ((usb_device*)dev_handle)->transfer_control(request_type, bRequest, wValue, wIndex, data, wLength, timeout);
+}
+int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle* dev_handle,
+ unsigned char endpoint, unsigned char* data, int length,
+ int* actual_length, unsigned int timeout)
+{
+ if (!dev_handle)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ if (actual_length)
+ *actual_length = length;
+ else
+ actual_length = &length;
+
+ return ((usb_device*)dev_handle)->transfer_bulk(endpoint, data, actual_length, timeout);
+}
+int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle* dev_handle,
+ unsigned char endpoint, unsigned char* data, int length,
+ int* actual_length, unsigned int timeout)
+{
+ if (!dev_handle)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ if (actual_length)
+ *actual_length = length;
+ else
+ actual_length = &length;
+
+ return ((usb_device*)dev_handle)->transfer_interrupt(endpoint, data, actual_length, timeout);
+}
+
+int LIBUSB_CALL libusb_set_timeout(libusb_device_handle* h, unsigned int milliseconds)
+{
+ if (!h)
+ return LIBUSB_ERROR_INVALID_PARAM;
+
+ return ((usb_device*)h)->set_timeout(milliseconds);
+}
+void LIBUSB_CALL libusb_quit(libusb_context* ctx)
+{
+ if (ctx)
+ ((usb_monitor*)ctx)->quit();
+}
+uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device* device)
+{
+ if(!device)
+ return 0;
+
+ return ((usb_device*)device)->port();
+}
+
diff --git a/device/win_usb/win_usb.h b/device/win_usb/win_usb.h
new file mode 100644
index 0000000..cc4d610
--- /dev/null
+++ b/device/win_usb/win_usb.h
@@ -0,0 +1,234 @@
+#pragma once
+
+// implements USB as libusb on windows
+//
+// Date: 2022-04-01
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "libusb-1.0/libusb.h"
+
+// HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{6bdd1fc6-810f-11d0-bec7-08002be2092f}
+#define HG_SCANNER_GUID "6BDD1FC6-810F-11D0-BEC7-08002BE2092F"
+#define MONITOR_WINDOW_OWNER L"monitor_wnd_owner"
+
+
+typedef struct _dev_id
+{
+ int vid;
+ int pid;
+
+ bool operator==(const struct _dev_id& r)
+ {
+ return vid == r.vid && pid == r.pid;
+ }
+}DEVID;
+
+class usb_device // consider as libusb_device
+{
+ volatile long ref_;
+ GUID guid_;
+ std::string name_;
+ DEVID id_;
+ bool is_ok_;
+ bool online_;
+ uint8_t port_;
+
+ enum
+ {
+ OVL_BULK_IN,
+ OVL_BULK_OUT,
+ OVL_CONTROL,
+ OVL_INTERRUPT,
+
+ OVL_MAX,
+ };
+ OVERLAPPED ovl_[OVL_MAX];
+
+ libusb_device_handle *handle_; // as file handle returned by CreateFile
+ libusb_device_descriptor *dev_desc_;
+ std::vector cfg_desc_;
+
+ typedef struct _usb_pipe
+ {
+ UCHAR address;
+ int type;
+ HANDLE pipe;
+ }USBPIPE;
+ std::vector pipes_;
+ DWORD timout_ms_;
+ HANDLE find_pipe(UCHAR addr, int type);
+ int set_timeout(HANDLE h);
+
+public:
+ usb_device(const char* name);
+
+ static DEVID vid_pid_from_name(const char* name); // device name like '\\?\usb#vid_3072&pid_0239#01234567aabbccddee#{a5dcbf10-6530-11d2-901f-00c04fb951ed}'
+ static DWORD from_hex_string(const char* hex_str);
+ static std::string driver_key_name(HANDLE file);
+ static std::string parent_hub_path_name(int vid, int pid, int *addr = NULL);
+ static bool find_vid_pid_in_hub(const char* utf8_hub_path_name, int vid, int pid, int *addr/*if *addr is not -1 or NULL, search the device with vid:pid and set the address in addr if it was not null, or-else chekc the device at *addr is vid:pid or not*/);
+ static int get_device_address(const char* device_name, LPGUID lpguid);
+
+ long add_ref(void);
+ long release(void);
+
+protected:
+ ~usb_device();
+
+public:
+ bool operator==(const char* name);
+ bool operator==(const DEVID& id);
+
+ usb_device& operator=(const DEVID& id);
+ usb_device& operator=(const GUID& guid);
+
+ std::string name(void);
+ GUID guid(void);
+ DEVID id(void);
+ bool is_ok(void);
+ bool is_open(void);
+ bool is_online(void);
+ void set_online(bool online);
+ uint8_t port(void);
+
+ bool init(void);
+ void clear(void);
+ void online_statu_changed(bool online);
+ int get_descriptor(libusb_device_descriptor* desc);
+ int get_config_descriptor(int index, libusb_config_descriptor** desc);
+ int open(libusb_device_handle** dev_handle);
+ int close(void);
+ int set_timeout(unsigned milliseconds);
+
+ int transfer_bulk(unsigned endpoint, unsigned char* data, int* length, unsigned int timeout);
+ int transfer_control(uint8_t type, uint8_t req, uint16_t val, uint16_t ind, unsigned char* data, uint16_t len, unsigned timeout);
+ int transfer_interrupt(unsigned endpoint, unsigned char* data, int* length, unsigned int timeout);
+};
+class usb_callback
+{
+ libusb_hotplug_callback_fn usb_cb_;
+ void* usb_cb_param_;
+
+public:
+ usb_callback(libusb_hotplug_callback_fn cb, void* param);
+ ~usb_callback();
+
+public:
+ void notify(libusb_context* ctx, usb_device* dev, int ev);
+};
+class usb_monitor // consider as libusb_context
+{
+ std::vector cbs_;
+ std::mutex lock_;
+ std::vector devices_;
+ std::shared_ptr handle_msg_;
+ DWORD handle_msg_id_;
+ std::string cur_dev_name_;
+ volatile bool run_;
+
+ HWND wnd_monitor_;
+ static LRESULT CALLBACK monitor_wnd_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
+ static void register_monitor_wnd(const wchar_t* cls);
+
+ void notify_usb_event(usb_device*& dev, bool arrive);
+ int on_usb_pnp(WPARAM wp, LPARAM lp);
+ void find_usb(std::vector& usb_devs);
+
+public:
+ usb_monitor();
+ ~usb_monitor();
+
+ static usb_monitor* usb_monitor_;
+
+public:
+ usb_callback* reg_callback(libusb_hotplug_callback_fn cb, void* param);
+ void unreg_callback(usb_callback* cb);
+
+ void thread_run_device_event_wnd(void); // called in a seperate thread from libusb
+ void thread_handle_device_change_msg(void); // a seperate thread for WM_DEVICECHANGE cannot be blocked
+
+ void quit(void);
+};
+
+//1: \\?\usb#vid_0bda&pid_0129#20100201396000000#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
+// DeviceIoControl error: 31
+// USB\VID_0BDA&PID_0129\20100201396000000
+// class: USB
+// desc: Realtek USB 2.0 Card Reader
+// HID: USB\VID_0BDA&PID_0129&REV_3960
+// name: \Device\USBPDO-3
+// bus : 1
+// addr: 9
+// guid: {36fc9e60-c465-11cf-8056-444553540000}
+// path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(9)
+//
+//2: \\?\usb#vid_06cb&pid_00ea#9bec419959f6#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
+// Open error: 5
+// USB\VID_06CB&PID_00EA\9BEC419959F6
+// class: Biometric
+// desc: Synaptics UWP WBDI
+// HID: USB\VID_06CB&PID_00EA&REV_0000
+// name: \Device\USBPDO-5
+// bus : 1
+// addr: 12
+// guid: {53d29ef7-377c-4d14-864b-eb3a85769359}
+// path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(12)
+//
+//3: \\?\usb#vid_3072&pid_0239#01234567aabbccddee#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
+// VID: 3072
+// PID: 0239
+// USB: 4.4
+// USB\VID_3072&PID_0239\01234567AABBCCDDEE
+// class: Image
+// desc: HUAGOSCAN G200 TWAIN
+// HID: USB\VID_3072&PID_0239&REV_0404
+// name: \Device\USBPDO-7
+// bus : 1
+// addr: 8
+// guid: {6bdd1fc6-810f-11d0-bec7-08002be2092f}
+// path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(8)
+//
+//4: \\?\usb#vid_17ef&pid_6100#5&4adfeed&0&6#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
+// VID: 0000
+// PID: 0000
+// USB: 0.0
+// USB\VID_17EF&PID_6100\5&4ADFEED&0&6
+// class: USB
+// desc: USB Composite Device
+// HID: USB\VID_17EF&PID_6100&REV_0201
+// name: \Device\USBPDO-1
+// bus : 1
+// addr: 6
+// guid: {36fc9e60-c465-11cf-8056-444553540000}
+// path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(6)
+//
+//5: \\?\usb#vid_8087&pid_0026#5&4adfeed&0&14#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
+// DeviceIoControl error: 50
+// USB\VID_8087&PID_0026\5&4ADFEED&0&14
+// class: Bluetooth
+// desc: 英特尔(R) 无线 Bluetooth(R)
+// HID: USB\VID_8087&PID_0026&REV_0002
+// name: \Device\USBPDO-4
+// bus : 1
+// addr: 14
+// guid: {e0cbf06c-cd8b-4647-bb8a-263b43f0f974}
+// path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(14)
+//
+//6: \\?\usb#vid_17ef&pid_608d#5&4adfeed&0&7#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
+// Open error: 31
+// USB\VID_17EF&PID_608D\5&4ADFEED&0&7
+// class: HIDClass
+// desc: USB 输入设备
+// HID: USB\VID_17EF&PID_608D&REV_0100
+// name: \Device\USBPDO-2
+// bus : 1
+// addr: 7
+// guid: {745a17a0-74d3-11d0-b6fe-00a0c90f57da}
+// path: PCIROOT(0)#PCI(1400)#USBROOT(0)#USB(7)
+//
diff --git a/huagaotwain/dllmain.cpp b/huagaotwain/dllmain.cpp
new file mode 100644
index 0000000..ae98183
--- /dev/null
+++ b/huagaotwain/dllmain.cpp
@@ -0,0 +1,22 @@
+#include "pch.h"
+#include "huagaotwain.h"
+
+HMODULE me_ = NULL;
+
+BOOL APIENTRY DllMain(HMODULE hModule , DWORD ul_reason_for_call, LPVOID /* lpReserved */)
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ //sane_invoker::initialize(hModule);
+ me_ = hModule;
+ break;
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ case DLL_PROCESS_DETACH:
+ //sane_invoker::uninitialize();
+ break;
+ }
+ return TRUE;
+}
diff --git a/huagaotwain/huagaotwain.cpp b/huagaotwain/huagaotwain.cpp
new file mode 100644
index 0000000..876d9ae
--- /dev/null
+++ b/huagaotwain/huagaotwain.cpp
@@ -0,0 +1,2189 @@
+#include "pch.h"
+#include "huagaotwain.h"
+#include "huagao/hgscanner_error.h"
+#include "./twain/twain_2.4.h"
+#include "../../device/sdk/hginclude/hg_log.h"
+
+#define STR(s) #s
+#define PASTE_STR(a, b) STR(a##b)
+#define SANE_API(api) PASTE_STR(sane_hgsane_, api)
+#define API_ELEM(api) {SANE_API(api), (FARPROC*)&real_sane_##api##_}
+#ifdef VLOG_OK
+#define vlog_debug_info sane_invoker::log_debug_info
+#else
+#define vlog_debug_info
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// utilities ...
+namespace local_utility
+{
+ std::wstring reg_read(HKEY root, const wchar_t* path, const wchar_t* name)
+ {
+ HKEY key = NULL;
+
+ RegOpenKeyW(root, path, &key);
+ if (!key)
+ return L"";
+
+ wchar_t* buf = NULL;
+ DWORD len = 0;
+ DWORD type = REG_SZ;
+ std::wstring ret(L"");
+
+ RegQueryValueExW(key, name, NULL, &type, (LPBYTE)buf, &len);
+ if(len)
+ {
+ buf = new wchar_t[len + 4];
+ memset(buf, 0, (len + 4) * sizeof(*buf));
+ RegQueryValueExW(key, name, NULL, &type, (LPBYTE)buf, &len);
+ ret = buf;
+ delete[] buf;
+ }
+ RegCloseKey(key);
+
+ return ret;
+ }
+ std::wstring reg_get_app_installing_path(void)
+ {
+ return reg_read(HKEY_LOCAL_MACHINE, L"SOFTWARE\\HuaGoScan", L"AppDirectory");
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// scanned image ...
+scanned_img::scanned_img(SANE_Image* img, SANE_FinalImgFormat* header) : param_(img->header)
+{
+ // PBITMAPINFOHEADER
+ size_t bytes = line_bytes() * height();
+ std::string h(file_header(header, bytes));
+ unsigned char *src = img->data + img->bytes - param_.bytes_per_line,
+ *dst = NULL;
+
+ data_.resize(bytes + h.length());
+ memcpy(&data_[0], h.c_str(), h.length());
+ dst = (unsigned char*)&data_[0] + h.length();
+
+ if (param_.format == SANE_FRAME_RGB)
+ {
+ for (int i = 0; i < height(); ++i)
+ {
+ for (int j = 0; j < param_.pixels_per_line; ++j)
+ {
+ dst[j * 3 + 0] = src[j * 3 + 2];
+ dst[j * 3 + 1] = src[j * 3 + 1];
+ dst[j * 3 + 2] = src[j * 3 + 0];
+ }
+ src -= param_.bytes_per_line;
+ dst += line_bytes();
+ }
+ }
+ else
+ {
+ for (int i = 0; i < height(); ++i, dst += line_bytes(), src -= param_.bytes_per_line)
+ memcpy(dst, src, param_.bytes_per_line);
+ }
+}
+scanned_img::~scanned_img()
+{}
+
+std::string scanned_img::file_header(SANE_FinalImgFormat* header, float resolution)
+{
+ std::string h("");
+
+ if (header->img_format == SANE_IMAGE_TYPE_BMP)
+ {
+ BITMAPINFOHEADER bih = { 0 };
+
+ bih.biSize = sizeof(bih);
+ bih.biWidth = width();
+ bih.biBitCount = depth();
+ bih.biSizeImage = line_bytes() * height();
+ bih.biPlanes = 1;
+ bih.biHeight = height();
+ bih.biCompression = BI_RGB;
+ bih.biXPelsPerMeter = bih.biYPelsPerMeter = resolution * 39.37f + .5f;
+
+ h = std::string((char*)&bih, sizeof(bih));
+ }
+
+ return h;
+}
+
+int scanned_img::width(void)
+{
+ return param_.pixels_per_line;
+}
+int scanned_img::line_bytes(void)
+{
+ return (param_.bytes_per_line + 3) / 4 * 4;
+}
+int scanned_img::height(void)
+{
+ return param_.lines;
+}
+int scanned_img::depth(void)
+{
+ if (param_.format == SANE_FRAME_RGB)
+ return param_.depth * 3;
+ else
+ return param_.depth;
+}
+int scanned_img::channel(void)
+{
+ return param_.format == SANE_FRAME_RGB ? 3 : 1;
+}
+SANE_Frame scanned_img::type(void)
+{
+ return param_.format;
+}
+unsigned int scanned_img::bytes(void)
+{
+ return data_.size();
+}
+unsigned char* scanned_img::bits(void)
+{
+ return &data_[0];
+}
+void scanned_img::copy_header(SANE_Parameters* head)
+{
+ *head = param_;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// sane_invoker ...
+
+sane_invoker* sane_invoker::inst_ = NULL;
+sane_invoker::sane_invoker(const wchar_t* path) : ok_(false), ver_(0), sane_(NULL), first_cb_(NULL)
+{
+ memset(&sane_api_, 0, sizeof(sane_api_));
+ cfg_file_ = std::wstring(path) + L"config.txt";
+ if (load_sane())
+ {
+ first_cb_ = CreateEventA(NULL, FALSE, FALSE, NULL);
+ real_sane_init_ex_(&ver_, &sane_invoker::sane_callback_handler, this);
+
+ char msg[128] = { 0 };
+ sprintf_s(msg, _countof(msg) - 1, "Wait sane first callback = %d\r\n", WaitForSingleObject(first_cb_, 1000));
+ sane_invoker::log_debug_info(msg);
+ }
+}
+sane_invoker::~sane_invoker()
+{
+ if (sane_)
+ {
+ if(real_sane_exit_)
+ real_sane_exit_();
+ FreeLibrary(sane_);
+ sane_ = NULL;
+ }
+ if (first_cb_)
+ CloseHandle(first_cb_);
+}
+
+bool sane_invoker::load_sane()
+{
+ //sane_hgsane_init
+ //sane_hgsane_exit
+ //sane_hgsane_get_devices
+ //sane_hgsane_open
+ //sane_hgsane_close
+ //sane_hgsane_get_option_descriptor
+ //sane_hgsane_control_option
+ //sane_hgsane_get_parameters
+ //sane_hgsane_start
+ //sane_hgsane_read
+ //sane_hgsane_cancel
+ //sane_hgsane_set_io_mode
+ //sane_hgsane_get_select_fd
+ //sane_hgsane_strstatus
+ //sane_hgsane_init_ex
+ //sane_hgsane_io_control
+ struct {
+ std::string name;
+ FARPROC* api;
+ }apis[] = { API_ELEM(init),
+ API_ELEM(exit),
+ API_ELEM(get_devices),
+ API_ELEM(open),
+ API_ELEM(close),
+ API_ELEM(get_option_descriptor),
+ API_ELEM(control_option),
+ API_ELEM(get_parameters),
+ API_ELEM(start),
+ API_ELEM(read),
+ API_ELEM(cancel),
+ API_ELEM(set_io_mode),
+ API_ELEM(get_select_fd),
+ API_ELEM(strstatus),
+ API_ELEM(io_control),
+ API_ELEM(init_ex)
+ };
+
+ wchar_t sane_path[MAX_PATH] = { 0 };
+ DWORD size = _countof(sane_path);
+
+ log_ = &sane_invoker::no_log;
+ for (int i = 0; i < _countof(apis); ++i)
+ *apis[i].api = NULL;
+
+ ok_ = false;
+#ifdef USE_LOCAL_CONFIG
+ if (GetPrivateProfileStringW(L"sane", L"pe", L"", sane_path, size - 1, cfg_file_.c_str()))
+#else
+ std::wstring reg(local_utility::reg_get_app_installing_path());
+ wcscpy_s(sane_path, size - 1, reg.c_str());
+ if(reg.length())
+#endif
+ {
+ wcscat(sane_path, L"\\hgsane.dll");
+ size = sane_invoker::load_dll(sane_path, &sane_);
+ if (sane_)
+ {
+ ok_ = true;
+ *((FARPROC*)&log_) = GetProcAddress(sane_, "hg_debug_log");
+ if (!log_)
+ log_ = &sane_invoker::no_log;
+ for (int i = 0; i < _countof(apis); ++i)
+ {
+ *apis[i].api = GetProcAddress(sane_, apis[i].name.c_str());
+ if (!(*apis[i].api))
+ {
+ vlog_debug_info(1024, "GetProcAddress(\"sane.dll\", \"%s\") failed with error: %d\r\n", apis[i].name.c_str(), GetLastError());
+ ok_ = false;
+ }
+ }
+ if (ok_)
+ {
+ vlog_debug_info(1024, "sane component load success, path = \"%s\"\r\n", sane_invoker::u2ansi(sane_path).c_str());
+ for (int i = 0; i < sizeof(sane_api_) / sizeof(sane_api_.sane_cancel_api); ++i)
+ ((FARPROC*)&sane_api_)[i] = *apis[i + 2].api;
+ }
+ }
+ else
+ {
+ vlog_debug_info(1024, "LoadLibraryW(\"%s\") failed with error: %d\r\n", sane_invoker::u2ansi(sane_path).c_str(), GetLastError());
+ }
+ }
+ else
+ {
+ vlog_debug_info(1024, "GetPrivateProfileStringW(\"sane\", \"pe\", \"%s\") failed\r\n", sane_invoker::u2ansi(cfg_file_.c_str()).c_str());
+ }
+
+ return ok_;
+}
+int sane_invoker::handle_sane_event(SANE_Handle hdev, int code, void* data, unsigned int* len)
+{
+ SetEvent(first_cb_);
+ if (code == SANE_EVENT_DEVICE_ARRIVED)
+ {
+ SANE_Device* sdev = (SANE_Device*)data;
+ SANEDEV dev;
+ std::lock_guard lock(lock_dev_);
+ std::vector::iterator it = std::find(devices_.begin(), devices_.end(), sdev->name);
+
+ if (it == devices_.end())
+ {
+ dev.name = sdev->name;
+ dev.type = sdev->model;
+ dev.product = sdev->type;
+ dev.vendor = sdev->vendor;
+ devices_.push_back(dev);
+ }
+ else if (it->scanner)
+ it->scanner->set_online(true);
+ }
+ else if (code == SANE_EVENT_DEVICE_LEFT)
+ {
+ SANE_Device* sdev = (SANE_Device*)data;
+ std::lock_guard lock(lock_dev_);
+ std::vector::iterator it = std::find(devices_.begin(), devices_.end(), sdev->name);
+ if (it != devices_.end())
+ {
+ if (it->scanner)
+ it->scanner->set_online(false);
+ else
+ devices_.erase(it);
+ }
+ }
+ else if (code == SANE_EVENT_IMAGE_OK)
+ {
+ scanner* dev = find_scanner(hdev);
+ if (dev)
+ {
+ dev->put_image((SANE_Image*)data, len);
+ dev->release();
+ }
+ }
+ else if (code == SANE_EVENT_SCAN_FINISHED)
+ {
+ scanner* dev = find_scanner(hdev);
+ if (dev)
+ {
+ dev->scan_finished((char*)data, *len);
+ }
+ }
+ else if (code == SANE_EVENT_ERROR)
+ {
+ std::wstring msg(sane_invoker::utf82u((char*)data));
+ MessageBoxW(NULL, msg.c_str(), L"Error", MB_OK);
+ }
+
+ return HG_ERR_OK;
+}
+void sane_invoker::get_online_devices(std::vector& devs)
+{
+ std::lock_guard lock(lock_dev_);
+
+ devs = devices_;
+}
+int sane_invoker::get_online_device_count(void)
+{
+ std::lock_guard lock(lock_dev_);
+
+ return devices_.size();
+}
+scanner* sane_invoker::find_scanner(SANE_Handle hdev)
+{
+ std::lock_guard lock(lock_dev_);
+ std::vector::iterator it = std::find(devices_.begin(), devices_.end(), hdev);
+
+ if (it == devices_.end())
+ return NULL;
+ else
+ {
+ it->scanner->add_ref();
+
+ return it->scanner;
+ }
+}
+scanner* sane_invoker::open(const char* name, int* err)
+{
+ std::lock_guard lock(lock_dev_);
+ std::vector::iterator it = std::find(devices_.begin(), devices_.end(), name);
+
+ if (it == devices_.end())
+ {
+ if (err)
+ *err = HG_ERR_DEVICE_NOT_FOUND;
+
+ return NULL;
+ }
+
+ scanner *s = new scanner(this, *it);
+ if (s->last_error() != SANE_STATUS_GOOD)
+ {
+ if (err)
+ *err = s->last_error();
+ s->release();
+ s = NULL;
+ }
+
+ return s;
+}
+
+void sane_invoker::no_log(int, const char* info)
+{
+ OutputDebugStringA(info);
+}
+int sane_invoker::sane_callback_handler(SANE_Handle hdev, int code, void* data, unsigned int* len, void* param)
+{
+ return ((sane_invoker*)param)->handle_sane_event(hdev, code, data, len);
+}
+
+int sane_invoker::load_dll(const wchar_t* path_dll, HMODULE* dll)
+{
+ HMODULE h = LoadLibraryW(path_dll);
+ int ret = 0;
+
+ if (!h && GetLastError() == ERROR_MOD_NOT_FOUND)
+ {
+ std::wstring dir(path_dll);
+ size_t pos = dir.rfind(L'\\');
+ wchar_t path[MAX_PATH] = { 0 };
+
+ GetCurrentDirectoryW(_countof(path) - 1, path);
+ if (pos != std::wstring::npos)
+ dir.erase(pos);
+ SetCurrentDirectoryW(dir.c_str());
+ h = LoadLibraryW(path_dll);
+ ret = GetLastError();
+ SetCurrentDirectoryW(path);
+ }
+ else
+ ret = GetLastError();
+
+ if (dll)
+ *dll = h;
+
+ return ret;
+}
+bool sane_invoker::initialize(HMODULE me)
+{
+ if (sane_invoker::inst_)
+ return false;
+ else
+ {
+ wchar_t path[MAX_PATH] = { 0 }, * last = NULL;
+
+ GetModuleFileNameW(me, path, _countof(path) - 1);
+ last = wcsrchr(path, L'\\');
+ if (last++)
+ *last = 0;
+ sane_invoker::inst_ = new sane_invoker(path);
+
+ return true;
+ }
+}
+void sane_invoker::uninitialize(void)
+{
+ if (sane_invoker::inst_)
+ delete sane_invoker::inst_;
+ sane_invoker::inst_ = NULL;
+}
+std::string sane_invoker::u2m(const wchar_t* u, int page)
+{
+ char *ansi = NULL;
+ int len = 0;
+ std::string mb("");
+
+ len = WideCharToMultiByte(page, 0, u, lstrlenW(u), NULL, 0, NULL, NULL);
+ ansi = new char[len + 2];
+ len = WideCharToMultiByte(page, 0, u, lstrlenW(u), ansi, len, NULL, NULL);
+ ansi[len--] = 0;
+ mb = ansi;
+ delete[] ansi;
+
+ return mb;
+}
+std::wstring sane_invoker::m2u(const char* m, int page)
+{
+ wchar_t *unic = NULL;
+ int len = 0;
+ std::wstring u(L"");
+
+ len = MultiByteToWideChar(page, 0, m, lstrlenA(m), NULL, 0);
+ unic = new wchar_t[len + 2];
+ len = MultiByteToWideChar(page, 0, m, lstrlenA(m), unic, len);
+ unic[len--] = 0;
+ u = unic;
+ delete[] unic;
+
+ return u;
+}
+std::string sane_invoker::u2ansi(const wchar_t* u)
+{
+ return sane_invoker::u2m(u, CP_ACP);
+}
+std::string sane_invoker::u2utf8(const wchar_t* u)
+{
+ return sane_invoker::u2m(u, CP_UTF8);
+}
+std::string sane_invoker::utf82ansi(const char* utf8)
+{
+ return sane_invoker::u2m(sane_invoker::m2u(utf8, CP_UTF8).c_str(), CP_ACP);
+}
+std::string sane_invoker::ansi2utf8(const char* ansi)
+{
+ return sane_invoker::u2m(sane_invoker::m2u(ansi, CP_ACP).c_str(), CP_UTF8);
+}
+std::wstring sane_invoker::utf82u(const char* utf8)
+{
+ return sane_invoker::m2u(utf8, CP_UTF8);
+}
+std::wstring sane_invoker::ansi2u(const char* ansi)
+{
+ return sane_invoker::m2u(ansi, CP_ACP);
+}
+void sane_invoker::log_debug_info(const char* info)
+{
+ if (sane_invoker::inst_)
+ sane_invoker::inst_->log_(HG_LOG_LEVEL_DEBUG_INFO, info);
+ else
+ OutputDebugStringA(info);
+}
+#ifdef VLOG_OK
+void __cdecl sane_invoker::log_debug_info(int bytes, const char* fmt, ...)
+{
+ std::string str(bytes + 20, 0);
+
+ va_list args;
+
+ va_start(args, fmt);
+ sprintf_s(&str[0], bytes, fmt, args);
+ va_end(args);
+
+ vlog_debug_info(str.c_str());
+}
+#endif
+
+SANE_Status sane_invoker::invoke_sane_init(SANE_Int* version_code, SANE_Auth_Callback authorize)
+{
+ if (!sane_invoker::inst_)
+ return (SANE_Status)HG_ERR_NOT_OPEN;
+
+ if (!sane_invoker::inst_->real_sane_init_)
+ return SANE_STATUS_UNSUPPORTED;
+
+ return sane_invoker::inst_->real_sane_init_(version_code, authorize);
+}
+void sane_invoker::invoke_sane_exit(void)
+{
+ if (sane_invoker::inst_ && sane_invoker::inst_->real_sane_exit_)
+ sane_invoker::inst_->real_sane_exit_();
+}
+SANE_Status sane_invoker::invoke_sane_get_devices(const SANE_Device*** device_list, SANE_Bool local_only)
+{
+ if (!sane_invoker::inst_)
+ return (SANE_Status)HG_ERR_NOT_OPEN;
+
+ if (!sane_invoker::inst_->real_sane_get_devices_)
+ return SANE_STATUS_UNSUPPORTED;
+
+ return sane_invoker::inst_->real_sane_get_devices_(device_list, local_only);
+}
+SANE_Status sane_invoker::invoke_sane_open(SANE_String_Const devicename, SANE_Handle* handle)
+{
+ if (!sane_invoker::inst_)
+ return (SANE_Status)HG_ERR_NOT_OPEN;
+
+ if (!sane_invoker::inst_->real_sane_open_)
+ return SANE_STATUS_UNSUPPORTED;
+
+ return sane_invoker::inst_->real_sane_open_(devicename, handle);
+}
+void sane_invoker::invoke_sane_close(SANE_Handle handle)
+{
+ if (sane_invoker::inst_ && sane_invoker::inst_->real_sane_close_)
+ sane_invoker::inst_->real_sane_close_(handle);
+}
+const SANE_Option_Descriptor* sane_invoker::invoke_sane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
+{
+ if (!sane_invoker::inst_)
+ return NULL;
+
+ if (!sane_invoker::inst_->real_sane_get_option_descriptor_)
+ return NULL;
+
+ return sane_invoker::inst_->real_sane_get_option_descriptor_(handle, option);
+}
+SANE_Status sane_invoker::invoke_sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void* value, SANE_Int* info)
+{
+ if (!sane_invoker::inst_)
+ return (SANE_Status)HG_ERR_NOT_OPEN;
+
+ if (!sane_invoker::inst_->real_sane_control_option_)
+ return SANE_STATUS_UNSUPPORTED;
+
+ return sane_invoker::inst_->real_sane_control_option_(handle, option, action, value, info);
+}
+SANE_Status sane_invoker::invoke_sane_get_parameters(SANE_Handle handle, SANE_Parameters* params)
+{
+ if (!sane_invoker::inst_)
+ return (SANE_Status)HG_ERR_NOT_OPEN;
+
+ if (!sane_invoker::inst_->real_sane_get_parameters_)
+ return SANE_STATUS_UNSUPPORTED;
+
+ return sane_invoker::inst_->real_sane_get_parameters_(handle, params);
+}
+SANE_Status sane_invoker::invoke_sane_start(SANE_Handle handle)
+{
+ if (!sane_invoker::inst_)
+ return (SANE_Status)HG_ERR_NOT_OPEN;
+
+ if (!sane_invoker::inst_->real_sane_start_)
+ return SANE_STATUS_UNSUPPORTED;
+
+ return sane_invoker::inst_->real_sane_start_(handle);
+}
+SANE_Status sane_invoker::invoke_sane_read(SANE_Handle handle, SANE_Byte* data, SANE_Int max_length, SANE_Int* length)
+{
+ if (!sane_invoker::inst_)
+ return (SANE_Status)HG_ERR_NOT_OPEN;
+
+ if (!sane_invoker::inst_->real_sane_read_)
+ return SANE_STATUS_UNSUPPORTED;
+
+ return sane_invoker::inst_->real_sane_read_(handle, data, max_length, length);
+}
+void sane_invoker::invoke_sane_cancel(SANE_Handle handle)
+{
+ if (sane_invoker::inst_ && sane_invoker::inst_->real_sane_cancel_)
+ sane_invoker::inst_->invoke_sane_cancel(handle);
+}
+SANE_Status sane_invoker::invoke_sane_set_io_mode(SANE_Handle handle, SANE_Bool non_blocking)
+{
+ if (!sane_invoker::inst_)
+ return (SANE_Status)HG_ERR_NOT_OPEN;
+
+ if (!sane_invoker::inst_->real_sane_set_io_mode_)
+ return SANE_STATUS_UNSUPPORTED;
+
+ return sane_invoker::inst_->real_sane_set_io_mode_(handle, non_blocking);
+}
+SANE_Status sane_invoker::invoke_sane_get_select_fd(SANE_Handle handle, SANE_Int* fd)
+{
+ if (!sane_invoker::inst_)
+ return (SANE_Status)HG_ERR_NOT_OPEN;
+
+ if (!sane_invoker::inst_->real_sane_get_select_fd_)
+ return SANE_STATUS_UNSUPPORTED;
+
+ return sane_invoker::inst_->real_sane_get_select_fd_(handle, fd);
+}
+SANE_String_Const sane_invoker::invoke_sane_strstatus(SANE_Status status)
+{
+ if (sane_invoker::inst_ && sane_invoker::inst_->real_sane_strstatus_)
+ return sane_invoker::inst_->real_sane_strstatus_(status);
+ else
+ return "";
+}
+SANE_Status sane_invoker::invoke_sane_init_ex(SANE_Int* version_code, sane_callback cb, void* param)
+{
+ if (!sane_invoker::inst_)
+ return (SANE_Status)HG_ERR_NOT_OPEN;
+
+ if (!sane_invoker::inst_->real_sane_init_ex_)
+ return SANE_STATUS_UNSUPPORTED;
+
+ return sane_invoker::inst_->real_sane_init_ex_(version_code, cb, param);
+}
+SANE_Status sane_invoker::invoke_sane_io_control(SANE_Handle h, unsigned long code, void* data, unsigned* len)
+{
+ if (!sane_invoker::inst_)
+ return (SANE_Status)HG_ERR_NOT_OPEN;
+
+ if (!sane_invoker::inst_->real_sane_io_control_)
+ return SANE_STATUS_UNSUPPORTED;
+
+ return sane_invoker::inst_->real_sane_io_control_(h, code, data, len);
+}
+
+LPSANEAPI sane_invoker::get_api(void)
+{
+ if (sane_invoker::inst_)
+ return &sane_invoker::inst_->sane_api_;
+ else
+ return NULL;
+}
+
+bool sane_invoker::is_ok(void)
+{
+ if (sane_invoker::inst_)
+ return sane_invoker::inst_->ok_;
+ else
+ return false;
+}
+std::string sane_invoker::version(LPDWORD v)
+{
+ char vs[40] = { 0 };
+ DWORD ver = 0;
+
+ if (sane_invoker::inst_)
+ ver = sane_invoker::inst_->ver_;
+
+ sprintf_s(vs, _countof(vs) - 1, "%u.%u.%u", (ver >> 24) & 0x0ff, (ver >> 16) & 0x0ff, ver & 0x0ffff);
+ if (v)
+ *v = ver;
+
+ return vs;
+}
+void sane_invoker::get_devices(std::vector& devs)
+{
+ if (sane_invoker::inst_)
+ sane_invoker::inst_->get_online_devices(devs);
+}
+scanner* sane_invoker::open_scanner(const char* name, int* err)
+{
+ if (!sane_invoker::inst_)
+ {
+ if (err)
+ *err = HG_ERR_NOT_OPEN;
+
+ return NULL;
+ }
+
+ return sane_invoker::inst_->open(name, err);
+}
+int sane_invoker::online_devices(void)
+{
+ if (sane_invoker::inst_)
+ sane_invoker::inst_->get_online_device_count();
+ else
+ return 0;
+}
+
+
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// scanner ...
+#define FIND_OPTION(opt) \
+ std::vector::iterator it = std::find(options_.begin(), options_.end(), opt);
+
+scanner::scanner(sane_invoker* host, struct _dev& dev) : host_(host), hdev_(NULL), dpi_(200.0f), option_count_(0), event_cb_(NULL)
+ , name_(dev.name), type_(dev.type), vendor_(dev.vendor), product_(dev.product), cb_param_(NULL)
+ , scanning_(false), wait_img_(CreateEventA(NULL, TRUE, FALSE, NULL)), fmt_(NULL)
+ , opt_ind_dpi_(-1), opt_ind_color_mode_(-1), opt_ind_paper_(-1), opt_ind_scann_count_(-1)
+ , jpeg_quality_(80), opt_ind_text_direction_(-1), opt_ind_page_(-1), img_fmt_(SANE_IMAGE_TYPE_BMP)
+ , opt_ind_auto_descrew_(-1), opt_ind_erase_black_frame_(-1), opt_ind_filter_(-1), opt_ind_bright_(-1)
+ , opt_ind_contrast_(-1), opt_ind_gamma_(-1), opt_ind_ultrasonic_(-1), err_(HG_ERR_OK), desc_("")
+ , compression_(SANE_COMPRESSION_NONE), opt_ind_flip_(-1), auto_crop_(false), opt_ind_rotate_bkg_(-1)
+ , opt_ind_fill_blank_bkg_(-1), opt_ind_edge_ident_(-1), opt_ind_threshold_(-1), opt_ind_bkg_filling_method_(-1)
+ , opt_ind_fill_hole_(-1), opt_ind_fill_hole_ratio_(-1), opt_ind_noise_(-1), opt_ind_noise_threshold_(-1)
+ , opt_ind_rid_red_hsv_(-1), opt_ind_rid_red_(-1), opt_ind_sharpen_(-1), opt_ind_screw_detect_(-1), opt_ind_screw_detect_level_(-1)
+ , opt_ind_staple_(-1), opt_ind_dogear_(-1), opt_ind_dark_sample_(-1), opt_ind_split_(-1), opt_ind_fade_bkg_(-1)
+ , opt_ind_fade_bkg_val_(-1), opt_ind_size_detect_(-1), opt_ind_multi_out_(-1)
+{
+ dev.scanner = this;
+
+ SANE_Status statu = sane_invoker::invoke_sane_open(name_.c_str(), &hdev_);
+ online_ = statu == SANE_STATUS_GOOD;
+ {
+ vlog_debug_info(128, "scanner(0x%x) generated, open '%s' = %d\r\n", this, name_.c_str(), statu);
+ }
+ if (online_)
+ load_options();
+ init_image_format();
+ err_ = statu;
+}
+scanner::~scanner()
+{
+ if (fmt_)
+ delete[](char*)fmt_;
+ for (auto& v : options_)
+ {
+ if (v.desc->type == SANE_Value_Type::SANE_TYPE_STRING)
+ delete v.val.sv;
+ }
+
+ vlog_debug_info(128, "scanner(0x%x) destroyed\r\n", this);
+}
+
+std::string scanner::type(SANE_Value_Type st)
+{
+ switch (st)
+ {
+ case SANE_Value_Type::SANE_TYPE_BOOL:
+ return "bool";
+ case SANE_Value_Type::SANE_TYPE_BUTTON:
+ return "button";
+ case SANE_Value_Type::SANE_TYPE_FIXED:
+ return "float";
+ case SANE_Value_Type::SANE_TYPE_INT:
+ return "int";
+ case SANE_Value_Type::SANE_TYPE_STRING:
+ return "string";
+ default:
+ return "";
+ }
+}
+value_limit scanner::limit(SANE_Constraint_Type st)
+{
+ switch (st)
+ {
+ case SANE_Constraint_Type::SANE_CONSTRAINT_RANGE:
+ return VAL_LIMIT_RANGE;
+ case SANE_Constraint_Type::SANE_CONSTRAINT_STRING_LIST:
+ case SANE_Constraint_Type::SANE_CONSTRAINT_WORD_LIST:
+ return VAL_LIMIT_ENUM;
+ default:
+ return VAL_LIMIT_NONE;
+ }
+}
+
+void scanner::load_options(void)
+{
+ SANE_Int count = 0, afterdo = 0;
+ SANE_Status statu = sane_invoker::invoke_sane_control_option(hdev_, 0, SANE_ACTION_GET_VALUE, &count, &afterdo);
+
+ option_count_ = 0;
+ if (statu != SANE_STATUS_GOOD)
+ {
+ vlog_debug_info(128, "get option count failed with error: %d\r\n", statu);
+ return;
+ }
+ else
+ vlog_debug_info(128, "\"%s\" has %d options.\r\n", count);
+
+ option_count_ = count;
+ for (int i = 1; i < option_count_; ++i)
+ {
+ const SANE_Option_Descriptor* desc = sane_invoker::invoke_sane_get_option_descriptor(hdev_, i);
+ SANEOPTION op;
+
+ op.ind = i;
+ op.desc = desc;
+ if (desc->type == SANE_Value_Type::SANE_TYPE_BOOL)
+ op.val.bv = get_boolean(i);
+ else if (desc->type == SANE_Value_Type::SANE_TYPE_FIXED)
+ op.val.dv = get_double(i);
+ else if (desc->type == SANE_Value_Type::SANE_TYPE_INT)
+ op.val.iv = get_integer(i);
+ else if (desc->type == SANE_Value_Type::SANE_TYPE_STRING)
+ op.val.sv = new std::string(get_string(i, desc->size));
+ options_.push_back(op);
+ if (color_mode::is_me(desc->title))
+ opt_ind_color_mode_ = i;
+ else if (dpi::is_me(desc->title))
+ opt_ind_dpi_ = i;
+ else if (paper::is_me(desc->title))
+ opt_ind_paper_ = i;
+ else if (scan_count::is_me(desc->title))
+ opt_ind_scann_count_ = MAKELPARAM(i, HIWORD(opt_ind_scann_count_));
+ else if (scan_count::is_parent(desc->title))
+ opt_ind_scann_count_ = MAKELPARAM(LOWORD(opt_ind_scann_count_), i);
+ else if (text_direction::is_me(desc->title))
+ opt_ind_text_direction_ = i;
+ else if (page::is_me(desc->title))
+ opt_ind_page_ = i;
+ else if (auto_descrew::is_me(desc->title))
+ opt_ind_auto_descrew_ = i;
+ else if (erase_black_frame::is_me(desc->title))
+ opt_ind_erase_black_frame_ = i;
+ else if (filter::is_me(desc->title))
+ opt_ind_filter_ = i;
+ else if (bright::is_me(desc->title))
+ opt_ind_bright_ = i;
+ else if (contrast::is_me(desc->title))
+ opt_ind_contrast_ = i;
+ else if (gamma::is_me(desc->title))
+ opt_ind_gamma_ = i;
+ else if (ultrasonic::is_me(desc->title))
+ opt_ind_ultrasonic_ = i;
+ else if (flip::is_me(desc->title))
+ opt_ind_flip_ = i;
+ else if (rotate_bg::is_me(desc->title))
+ opt_ind_rotate_bkg_ = i;
+ else if (fill_black_border::is_me(desc->title))
+ opt_ind_fill_blank_bkg_ = i;
+ else if (edge_ident::is_me(desc->title))
+ opt_ind_edge_ident_ = i;
+ else if (threshold::is_me(desc->title))
+ opt_ind_threshold_ = i;
+ else if (bkg_filling_method::is_me(desc->title))
+ opt_ind_bkg_filling_method_ = i;
+ else if (fill_hole::is_me(desc->title))
+ opt_ind_fill_hole_ = i;
+ else if (fill_hole::is_ratio(desc->title))
+ opt_ind_fill_hole_ratio_ = i;
+ else if (noise::is_me(desc->title))
+ opt_ind_noise_ = i;
+ else if (noise::is_threshold(desc->title))
+ opt_ind_noise_threshold_ = i;
+ else if (rid_red::is_me(desc->title, false))
+ opt_ind_rid_red_ = i;
+ else if (rid_red::is_me(desc->title, true))
+ opt_ind_rid_red_hsv_ = i;
+ else if (sharpen::is_me(desc->title))
+ opt_ind_sharpen_ = i;
+ else if (screw::is_me(desc->title))
+ opt_ind_screw_detect_ = i;
+ else if (screw::is_level(desc->title))
+ opt_ind_screw_detect_level_ = i;
+ else if (staple::is_me(desc->title))
+ opt_ind_staple_ = i;
+ else if (dogear::is_me(desc->title))
+ opt_ind_dogear_ = i;
+ else if (sample::is_me(desc->title))
+ opt_ind_dark_sample_ = i;
+ else if (split::is_me(desc->title))
+ opt_ind_split_ = i;
+ else if (fade_bkg::is_me(desc->title))
+ opt_ind_fade_bkg_ = i;
+ else if (fade_bkg::is_value(desc->title))
+ opt_ind_fade_bkg_val_ = i;
+ else if (size_detect::is_me(desc->title))
+ opt_ind_size_detect_ = i;
+ else if (multi_out::is_me(desc->title))
+ opt_ind_multi_out_ = i;
+ }
+}
+void scanner::init_image_format(void)
+{
+ if (fmt_)
+ delete[](char*)fmt_;
+
+ int len = sizeof(SANE_FinalImgFormat) + sizeof(BITMAPINFOHEADER);
+ PBITMAPINFOHEADER pbi = NULL;
+
+ fmt_ = (SANE_FinalImgFormat*)new char[len];
+ memset(fmt_, 0, len);
+ fmt_->img_format = SANE_IMAGE_TYPE_BMP;
+ pbi = (PBITMAPINFOHEADER)fmt_->detail;
+}
+std::string scanner::get_string(int opt, int bytes)
+{
+ std::string ret("");
+ char* v = new char[bytes + 4];
+ SANE_Int afterdo = 0;
+
+ memset(v, 0, bytes + 4);
+ sane_invoker::invoke_sane_control_option(hdev_, opt, SANE_ACTION_GET_VALUE, v, &afterdo);
+ ret = v;
+ delete[] v;
+
+ return ret;
+}
+bool scanner::get_boolean(int opt)
+{
+ SANE_Int afterdo = 0;
+ SANE_Bool v = 0;
+
+ sane_invoker::invoke_sane_control_option(hdev_, opt, SANE_ACTION_GET_VALUE, &v, &afterdo);
+
+ return v;
+}
+int scanner::get_integer(int opt)
+{
+ SANE_Int afterdo = 0;
+ SANE_Int v = 0;
+
+ sane_invoker::invoke_sane_control_option(hdev_, opt, SANE_ACTION_GET_VALUE, &v, &afterdo);
+
+ return v;
+}
+double scanner::get_double(int opt)
+{
+ SANE_Int afterdo = 0;
+ SANE_Fixed f = 0;
+
+ sane_invoker::invoke_sane_control_option(hdev_, opt, SANE_ACTION_GET_VALUE, &f, &afterdo);
+
+ return SANE_UNFIX(f);
+}
+int scanner::set_string(int opt, std::string& val, int size, SANE_Int* afterdo)
+{
+ SANE_Int after = 0;
+ char* buf = new char[size + 20];
+ int ret = 0;
+
+ memset(buf, 0, size + 20);
+ strcpy(buf, val.c_str());
+ ret = sane_invoker::invoke_sane_control_option(hdev_, opt, SANE_ACTION_SET_VALUE, buf, &after);
+ val = buf;
+ delete[] buf;
+ if (afterdo)
+ *afterdo = after;
+
+ return ret;
+}
+
+int scanner::close(void)
+{
+ if (hdev_)
+ {
+ sane_invoker::invoke_sane_close(hdev_);
+ hdev_ = NULL;
+ }
+
+ return HG_ERR_OK;
+}
+int scanner::start(void)
+{
+ int ret = HG_ERR_NOT_OPEN;
+
+ ResetEvent(wait_img_);
+ if (hdev_)
+ ret = sane_invoker::invoke_sane_start(hdev_);
+ scanning_ = ret == SANE_STATUS_GOOD;
+
+ return ret;
+}
+int scanner::stop(void)
+{
+ scanning_ = false;
+ if (hdev_)
+ sane_invoker::invoke_sane_cancel(hdev_);
+
+ return HG_ERR_OK;
+}
+void scanner::set_event_callback(void(*cb)(int, void*, unsigned int*, void*), void* param)
+{
+ cb_param_ = param;
+ event_cb_ = cb;
+}
+bool scanner::wait_image(DWORD milliseconds)
+{
+ {
+ std::lock_guard lock(lock_img_);
+ if (img_.size())
+ return true;
+ }
+
+ if (!scanning_)
+ {
+ if (err_)
+ {
+ std::wstring tips(sane_invoker::utf82u(desc_.c_str()));
+ MessageBoxW(NULL, tips.c_str(), L"\u9519\u8bef", MB_OK);
+ }
+ return false;
+ }
+
+ WaitForSingleObject(wait_img_, milliseconds);
+
+ return img_.size() > 0;
+}
+int scanner::get_scanned_images(DWORD milliseconds)
+{
+ if (milliseconds && !wait_image(milliseconds))
+ return 0;
+ else
+ {
+ std::lock_guard lock(lock_img_);
+
+ return img_.size();
+ }
+}
+scanned_img* scanner::take_first_image(void) // delete returned value, plz
+{
+ std::lock_guard lock(lock_img_);
+ scanned_img* img = NULL;
+
+ if (img_.size())
+ {
+ img = img_[0];
+ img_.erase(img_.begin());
+ ResetEvent(wait_img_);
+ }
+
+ return img;
+}
+bool scanner::get_first_image_header(SANE_Parameters* header)
+{
+ std::lock_guard lock(lock_img_);
+ if (img_.size())
+ {
+ img_[0]->copy_header(header);
+ header->bytes_per_line = img_[0]->line_bytes();
+ if (header->format == SANE_FRAME_RGB)
+ header->depth *= 3;
+
+ return true;
+ }
+
+ return false;
+}
+int scanner::last_error(void)
+{
+ return err_;
+}
+
+SANE_Handle scanner::handle(void)
+{
+ return hdev_;
+}
+bool scanner::is_online(void)
+{
+ return online_;
+}
+void scanner::set_online(bool online)
+{
+ online_ = online;
+}
+
+
+void scanner::put_image(SANE_Image* img, unsigned int* len)
+{
+ std::lock_guard lock(lock_img_);
+ img_.push_back(new scanned_img(img, fmt_));
+ SetEvent(wait_img_);
+ if (event_cb_)
+ event_cb_(SANE_EVENT_IMAGE_OK, (void*)img, len, cb_param_);
+}
+void scanner::scan_finished(const char* desc, int err)
+{
+ desc_ = desc ? desc : "OK";
+ err_ = err;
+ scanning_ = false;
+ SetEvent(wait_img_);
+ if (event_cb_)
+ event_cb_(SANE_EVENT_SCAN_FINISHED, (void*)desc, (unsigned int*)&err, cb_param_);
+}
+
+// up to sane, we set the CAP_xxx according to settings display in UI ...
+bool scanner::get_value_info(int sn, std::string& type, value_limit& limit)
+{
+ if (sn <= 0 || sn >= option_count_)
+ return false;
+
+ FIND_OPTION(sn);
+ if (it == options_.end())
+ return false;
+
+ type = scanner::type(it->desc->type);
+ limit = scanner::limit(it->desc->constraint_type);
+
+ return true;
+}
+bool scanner::get_value(int sn, std::list& values, std::string& now, std::string& init)
+{
+ if (sn <= 0 || sn >= option_count_)
+ return false;
+
+ FIND_OPTION(sn);
+ if (it == options_.end())
+ return false;
+
+ if (scanner::type(it->desc->type) != "string" ||
+ scanner::limit(it->desc->constraint_type) != VAL_LIMIT_ENUM)
+ return false;
+
+ sane_trans::get_value_list(it->desc, &values);
+ init = *it->val.sv;
+ now = get_string(sn, it->desc->size);
+
+ return true;
+}
+bool scanner::get_value(int sn, std::list& values, float& now, float& init)
+{
+ if (sn <= 0 || sn >= option_count_)
+ return false;
+
+ FIND_OPTION(sn);
+ if (it == options_.end())
+ return false;
+
+ if (scanner::type(it->desc->type) != "float" ||
+ scanner::limit(it->desc->constraint_type) != VAL_LIMIT_ENUM)
+ return false;
+
+ std::list vals;
+ sane_trans::get_value_list(it->desc, &vals);
+ for(const auto& v : vals)
+ values.push_back(v);
+
+ init = it->val.dv;
+ now = get_double(sn);
+
+ return true;
+}
+bool scanner::get_value(int sn, std::list& values, bool& now, bool& init)
+{
+ if (sn <= 0 || sn >= option_count_)
+ return false;
+
+ FIND_OPTION(sn);
+ if (it == options_.end())
+ return false;
+
+ values.push_back(false);
+ values.push_back(true);
+ now = get_boolean(sn);
+ init = it->val.bv;
+
+ return true;
+}
+bool scanner::get_value(int sn, int& now, int& init, int* lower, int* upper, int* step)
+{
+ if (sn <= 0 || sn >= option_count_)
+ return false;
+
+ FIND_OPTION(sn);
+ if (it == options_.end())
+ return false;
+
+ if (scanner::type(it->desc->type) != "int")
+ return false;
+
+ bool ret = true;
+ now = get_integer(sn);
+ init = it->val.iv;
+ if (lower && upper && step)
+ {
+ *lower = *upper = *step = 0;
+ if (scanner::limit(it->desc->constraint_type) == VAL_LIMIT_RANGE)
+ {
+ *lower = it->desc->constraint.range->min;
+ *upper = it->desc->constraint.range->max;
+ *step = it->desc->constraint.range->quant;
+ }
+ else
+ ret = false;
+ }
+
+ return ret;
+}
+bool scanner::get_value(int sn, float& now, float& init, float* lower, float* upper, float* step)
+{
+ if (sn <= 0 || sn >= option_count_)
+ return false;
+
+ std::vector::iterator it = std::find(options_.begin(), options_.end(), sn);
+ if (it == options_.end())
+ return false;
+
+ if (scanner::type(it->desc->type) != "float")
+ return false;
+
+ bool ret = true;
+ double ratio = sn == opt_ind_fill_hole_ratio_ ? 100.0f : 1.0f;
+ now = get_double(sn) * ratio;
+ init = it->val.dv * ratio;
+ if (lower && upper && step)
+ {
+ *lower = *upper = *step = 0;
+ if (scanner::limit(it->desc->constraint_type) == VAL_LIMIT_RANGE)
+ {
+ *lower = SANE_UNFIX(it->desc->constraint.range->min) * ratio;
+ *upper = SANE_UNFIX(it->desc->constraint.range->max) * ratio;
+ *step = SANE_UNFIX(it->desc->constraint.range->quant) * ratio;
+ }
+ else
+ ret = false;
+ }
+
+ return ret;
+}
+
+int scanner::set_value(int sn, std::string val)
+{
+ if (sn <= 0 || sn >= option_count_)
+ return HG_ERR_OUT_OF_RANGE;
+
+ std::vector::iterator it = std::find(options_.begin(), options_.end(), sn);
+ if (it == options_.end())
+ return HG_ERR_NO_DATA;
+
+ if (scanner::type(it->desc->type) != "string" ||
+ val.length() > it->desc->size)
+ return HG_ERR_INVALID_PARAMETER;
+
+ char* buf = new char[it->desc->size + 20];
+ int ret = HG_ERR_OK;
+ SANE_Int afterdo = 0;
+
+ memset(buf, 0, it->desc->size + 20);
+ strcpy(buf, val.c_str());
+ ret = sane_invoker::invoke_sane_control_option(hdev_, sn, SANE_ACTION_SET_VALUE, buf, &afterdo);
+ if (sn == opt_ind_paper_ && (ret == HG_ERR_OK || ret == HG_ERR_NOT_EXACT))
+ {
+ auto_crop_ = paper::is_auto_crop(buf);
+ }
+ delete[] buf;
+
+ return ret;
+}
+int scanner::set_value(int sn, bool val)
+{
+ if (sn <= 0 || sn >= option_count_)
+ return HG_ERR_OUT_OF_RANGE;
+
+ std::vector::iterator it = std::find(options_.begin(), options_.end(), sn);
+ if (it == options_.end())
+ return HG_ERR_NO_DATA;
+
+ if (scanner::type(it->desc->type) != "bool" &&
+ scanner::type(it->desc->type) != "button")
+ return HG_ERR_INVALID_PARAMETER;
+
+ int ret = HG_ERR_OK;
+ SANE_Int afterdo = 0;
+ SANE_Bool v = val;
+
+ ret = sane_invoker::invoke_sane_control_option(hdev_, sn, SANE_ACTION_SET_VALUE, &v, &afterdo);
+
+ return ret;
+}
+int scanner::set_value(int sn, int val)
+{
+ if (sn <= 0 || sn >= option_count_)
+ return HG_ERR_OUT_OF_RANGE;
+
+ std::vector::iterator it = std::find(options_.begin(), options_.end(), sn);
+ if (it == options_.end())
+ return HG_ERR_NO_DATA;
+
+ if (scanner::type(it->desc->type) != "int")
+ return HG_ERR_INVALID_PARAMETER;
+
+ int ret = HG_ERR_OK;
+ SANE_Int afterdo = 0;
+ SANE_Int v = val;
+
+ ret = sane_invoker::invoke_sane_control_option(hdev_, sn, SANE_ACTION_SET_VALUE, &v, &afterdo);
+
+ return ret;
+}
+int scanner::set_value(int sn, double val)
+{
+ if (sn <= 0 || sn >= option_count_)
+ return HG_ERR_OUT_OF_RANGE;
+
+ std::vector::iterator it = std::find(options_.begin(), options_.end(), sn);
+ if (it == options_.end())
+ return HG_ERR_NO_DATA;
+
+ if (scanner::type(it->desc->type) != "float")
+ return HG_ERR_INVALID_PARAMETER;
+
+ if (sn == opt_ind_fill_hole_ratio_)
+ val /= 100.0f;
+
+ int ret = HG_ERR_OK;
+ SANE_Int afterdo = 0;
+ SANE_Fixed v = SANE_FIX(val);
+
+ ret = sane_invoker::invoke_sane_control_option(hdev_, sn, SANE_ACTION_SET_VALUE, &v, &afterdo);
+
+ return ret;
+}
+
+int scanner::twain_set_resolution(float dpi)
+{
+ float l = .0f, u = .0f, n = .0f, i = .0f;
+
+ if(opt_ind_dpi_ == -1 ||
+ !get_value(opt_ind_dpi_, n, i, &l, &u))
+ return dpi >= 200.0f && dpi <= 600.0f ? HG_ERR_OK : HG_ERR_INVALID_PARAMETER;
+
+ if (dpi <= l || dpi >= u)
+ return HG_ERR_INVALID_PARAMETER;
+
+ std::vector::iterator it = std::find(options_.begin(), options_.end(), opt_ind_dpi_);
+ SANE_Fixed v = it->desc->type == SANE_TYPE_FIXED ? SANE_FIX(dpi) : dpi;
+ SANE_Int afterdo = 0;
+ int ret = sane_invoker::invoke_sane_control_option(hdev_, opt_ind_dpi_, SANE_ACTION_SET_VALUE, &v, &afterdo);
+
+ dpi_ = it->desc->type == SANE_TYPE_FIXED ? SANE_UNFIX(v) : v;
+
+ return ret;
+}
+float scanner::twain_get_resolution(float* init, std::vector* values, value_limit* limit)
+{
+ if (opt_ind_dpi_ != -1)
+ {
+ std::vector< SANEOPTION>::iterator it = std::find(options_.begin(), options_.end(), opt_ind_dpi_);
+ if (it != options_.end())
+ {
+ if(init)
+ *init = it->val.dv;
+
+ if (limit)
+ *limit = scanner::limit(it->desc->constraint_type);
+ if (values && (it->desc->type == SANE_TYPE_FIXED || it->desc->type == SANE_TYPE_INT))
+ {
+ if (it->desc->constraint_type == SANE_CONSTRAINT_WORD_LIST)
+ {
+ const SANE_Word* val = it->desc->constraint.word_list;
+ for (SANE_Word i = 1; i < val[0]; ++i)
+ values->push_back(it->desc->type == SANE_TYPE_FIXED ? SANE_UNFIX(val[i]) : val[i]);
+ }
+ else if (it->desc->constraint_type == SANE_CONSTRAINT_RANGE)
+ {
+ const SANE_Range* r = it->desc->constraint.range;
+ values->push_back(it->desc->type == SANE_TYPE_FIXED ? SANE_UNFIX(r->min) : r->min);
+ values->push_back(it->desc->type == SANE_TYPE_FIXED ? SANE_UNFIX(r->max) : r->max);
+ }
+ }
+ }
+ }
+ if (values && values->empty())
+ {
+ values->push_back(100.0f);
+ values->push_back(600.0f);
+ if (limit)
+ *limit = VAL_LIMIT_RANGE;
+ }
+
+ return dpi_;
+}
+
+int scanner::twain_set_color_mode(int twain_pixel_type)
+{
+ int ret = HG_ERR_DEVICE_NOT_SUPPORT;
+
+ for (const auto& v : options_)
+ {
+ if (v.ind != opt_ind_color_mode_)
+ continue;
+
+ std::string val(color_mode::from_twain_pixel_type(twain_pixel_type));
+ char* buf = new char[v.desc->size + 8];
+ SANE_Int afterdo = 0;
+
+ memset(buf, 0, v.desc->size + 8);
+ strcpy(buf, val.c_str());
+ ret = sane_invoker::invoke_sane_control_option(hdev_, v.ind, SANE_ACTION_SET_VALUE, buf, &afterdo);
+ delete buf;
+ break;
+ }
+
+ return HG_ERR_OK;
+}
+int scanner::twain_get_color_mode(int* init, std::vector* values, value_limit* limit)
+{
+ int tw_pixel_type = TWPT_RGB;
+
+ for(const auto& v : options_)
+ {
+ if (v.ind != opt_ind_color_mode_)
+ continue;
+
+ if (v.desc->type == SANE_Value_Type::SANE_TYPE_STRING)
+ {
+ SANE_Int afterdo = 0;
+ char* now = new char[v.desc->size + 8];
+
+ memset(now, 0, v.desc->size + 8);
+ sane_invoker::invoke_sane_control_option(hdev_, v.ind, SANE_ACTION_GET_VALUE, now, &afterdo);
+ tw_pixel_type = color_mode::to_twain_pixel_type(now);
+ if (init)
+ *init = color_mode::to_twain_pixel_type(v.val.sv->c_str());
+ if (values && limit)
+ {
+ std::list vals;
+ if (sane_trans::get_value_list(v.desc, &vals))
+ {
+ *limit = VAL_LIMIT_ENUM;
+ for (const auto& var : vals)
+ values->push_back(color_mode::to_twain_pixel_type(var.c_str()));
+ }
+ }
+
+ delete[] now;
+ }
+ break;
+ }
+
+ if (values && values->empty())
+ {
+ values->push_back(TWPT_BW);
+ values->push_back(TWPT_GRAY);
+ values->push_back(TWPT_RGB);
+ if (limit)
+ *limit = VAL_LIMIT_ENUM;
+ }
+ return tw_pixel_type;
+}
+int scanner::twain_set_auto_color_type(int type)
+{
+ unsigned int len = sizeof(type);
+
+ return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_SET_AUTO_COLOR_TYPE, &type, &len);
+}
+
+int scanner::twain_get_paper_ind(void)
+{
+ return opt_ind_paper_;
+}
+int scanner::twain_set_paper_lateral(bool lateral)
+{
+ FIND_OPTION(opt_ind_paper_);
+ if (it == options_.end())
+ return HG_ERR_INVALID_PARAMETER;
+
+ std::list vals;
+ std::string now(get_string(opt_ind_paper_, it->desc->size));
+ size_t pos = now.find(paper::lateral_title());
+ int ret = HG_ERR_OK;
+
+ if (lateral)
+ {
+ if (!paper::is_lateral(now.c_str()))
+ {
+ now += paper::lateral_title();
+ sane_trans::get_value_list(it->desc, &vals);
+ if (std::find(vals.begin(), vals.end(), now) == vals.end())
+ ret = HG_ERR_INVALID_PARAMETER;
+ else
+ {
+ ret = set_string(opt_ind_paper_, now, it->desc->size);
+ }
+ }
+ }
+ else
+ {
+ if (paper::is_lateral(now.c_str()))
+ {
+ size_t pos = now.find(paper::lateral_title());
+
+ if (pos != std::string::npos)
+ {
+ now.erase(pos);
+ ret = set_string(opt_ind_paper_, now, it->desc->size);
+ }
+ }
+ }
+
+ return ret;
+}
+bool scanner::twain_is_paper_lateral(void)
+{
+ FIND_OPTION(opt_ind_paper_);
+ if (it == options_.end())
+ return false;
+
+ std::string now(get_string(opt_ind_paper_, it->desc->size));
+
+ return paper::is_lateral(now.c_str());
+}
+int scanner::twain_set_paper_auto_match_size(bool match)
+{
+ FIND_OPTION(opt_ind_paper_);
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ std::string val(match ? paper::auto_size_title() : *it->val.sv);
+
+ return set_string(opt_ind_paper_, val, it->desc->size);
+}
+bool scanner::twain_is_paper_auto_match_size(void)
+{
+ FIND_OPTION(opt_ind_paper_);
+ if (it == options_.end())
+ return false;
+
+ std::string now(get_string(opt_ind_paper_, it->desc->size));
+
+ return paper::is_auto_size(now.c_str());
+}
+
+int scanner::twain_set_scan_count(int count)
+{
+ int ret = HG_ERR_DEVICE_NOT_SUPPORT;
+
+ if (opt_ind_scann_count_ != -1)
+ {
+ SANE_Int afterdo = 0;
+ char buf[80] = { 0 };
+
+ strcpy(buf, scan_count::scan_continous_val().c_str());
+ if (count == -1)
+ {
+ ret = sane_invoker::invoke_sane_control_option(hdev_, HIWORD(opt_ind_scann_count_), SANE_ACTION_SET_VALUE, buf, &afterdo);
+ }
+ else
+ {
+ std::list vals;
+ std::string now(""), init("");
+
+ get_value(HIWORD(opt_ind_scann_count_), vals, now, init);
+ for (const auto& v : vals)
+ {
+ if (v != buf)
+ {
+ init = v;
+ break;
+ }
+ }
+ strcpy(buf, init.c_str());
+ ret = sane_invoker::invoke_sane_control_option(hdev_, HIWORD(opt_ind_scann_count_), SANE_ACTION_SET_VALUE, buf, &afterdo);
+ if (ret == SANE_STATUS_GOOD)
+ {
+ SANE_Int v = count;
+ ret = sane_invoker::invoke_sane_control_option(hdev_, LOWORD(opt_ind_scann_count_), SANE_ACTION_SET_VALUE, &v, &afterdo);
+ }
+ }
+ }
+
+ return ret;
+}
+int scanner::twain_get_scan_count(void)
+{
+ int ret = -1;
+
+ if (opt_ind_scann_count_ != -1)
+ {
+ char buf[80] = { 0 };
+ SANE_Int afterdo = 0, count = -1;
+
+ ret = sane_invoker::invoke_sane_control_option(hdev_, HIWORD(opt_ind_scann_count_), SANE_ACTION_GET_VALUE, buf, &afterdo);
+ if (scan_count::scan_continous_val() == buf)
+ ret = -1;
+ else
+ {
+ sane_invoker::invoke_sane_control_option(hdev_, LOWORD(opt_ind_scann_count_), SANE_ACTION_GET_VALUE, &count, &afterdo);
+ ret = count;
+ }
+ }
+
+ return ret;
+}
+
+int scanner::twain_set_final_format(SANE_ImageType type, void* param)
+{
+ SANE_FinalImgFormat fmt;
+ unsigned int len = sizeof(fmt);
+ int ret = HG_ERR_OK;
+
+ fmt.img_format = type;
+ fmt.detail = param;
+ ret = sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_SET_FINAL_IMAGE_FORMAT, &fmt, &len);
+ if (ret == HG_ERR_OK)
+ {
+ if (type == SANE_IMAGE_TYPE_JPG)
+ jpeg_quality_ = (int)param;
+ img_fmt_ = type;
+ }
+
+ return ret;
+}
+int scanner::twain_get_jpeg_quality(void)
+{
+ return jpeg_quality_;
+}
+SANE_ImageType scanner::get_final_format(void)
+{
+ return img_fmt_;
+}
+
+int scanner::twain_set_final_compression(int compression)
+{
+ SANE_Compression compr;
+ unsigned int len = sizeof(compr);
+ int ret = HG_ERR_OK;
+
+ compr.compression = (SANE_CompressionType)compression;
+ compr.detail = NULL;
+
+ ret = sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_SET_FINAL_COMPRESSION, &compr, &len);
+ if (ret == HG_ERR_OK || ret == HG_ERR_NOT_EXACT)
+ compression_ = compr.compression;
+
+ return ret;
+}
+int scanner::twain_get_final_compression(int* init, std::vector* values)
+{
+ if (init || values)
+ {
+ SANE_Int vals[80] = { 0 };
+ unsigned int len = _countof(vals) - 4;
+ int ret = sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_FINAL_COMPRESSION, vals, &len);
+
+ if (ret == HG_ERR_OK)
+ {
+ if (init)
+ *init = vals[1];
+ if (values)
+ {
+ for (int i = 0; i < vals[0]; ++i)
+ values->push_back(vals[3 + i]);
+ }
+ }
+ compression_ = (SANE_CompressionType)vals[2];
+ }
+
+ return compression_;
+}
+
+int scanner::twain_set_text_direction(double degree)
+{
+ int ret = HG_ERR_INVALID_PARAMETER;
+ std::string val(text_direction::from_twain_angle(degree));
+
+ if (val.length())
+ {
+ FIND_OPTION(opt_ind_text_direction_);
+ if (it != options_.end())
+ {
+ ret = set_string(opt_ind_text_direction_, val, it->desc->size);
+ }
+ }
+
+ return ret;
+}
+double scanner::twain_get_text_direction(double* init, std::list* vals, value_limit* limit)
+{
+ double dir = .0f;
+ FIND_OPTION(opt_ind_text_direction_);
+
+ if (it != options_.end())
+ {
+ if (init)
+ *init = text_direction::to_twain_angle(it->val.sv->c_str());
+
+ if (vals)
+ {
+ std::list values;
+ sane_trans::get_value_list(it->desc, &values);
+ for (const auto& v : values)
+ vals->push_back(text_direction::to_twain_angle(v.c_str()));
+
+ if (limit)
+ *limit = scanner::limit(it->desc->constraint_type);
+ }
+
+ dir = text_direction::to_twain_angle(get_string(opt_ind_text_direction_, it->desc->size).c_str());
+ }
+
+ return dir;
+}
+int scanner::twain_set_text_auto_matic(bool am)
+{
+ FIND_OPTION(opt_ind_text_direction_);
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ std::string val(am ? text_direction::auto_val() : text_direction::from_twain_angle(0));
+
+ return set_string(opt_ind_text_direction_, val, it->desc->size);
+}
+bool scanner::twain_is_text_auto_matic(void)
+{
+ FIND_OPTION(opt_ind_text_direction_);
+ if (it == options_.end())
+ return false;
+
+ return text_direction::is_auto(get_string(opt_ind_text_direction_, it->desc->size).c_str());
+}
+
+int scanner::twain_set_page_duplex(bool dup)
+{
+ std::string val(page::from_duplex(dup));
+ FIND_OPTION(opt_ind_page_);
+
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ return set_string(opt_ind_page_, val, it->desc->size);
+}
+bool scanner::twain_is_page_duplex(void)
+{
+ FIND_OPTION(opt_ind_page_);
+ if (it == options_.end())
+ return true;
+
+ std::string now(get_string(opt_ind_page_, it->desc->size));
+
+ return page::is_duplex(now.c_str());
+}
+int scanner::twain_set_page_discarding_blank_page(bool discard, bool receipt)
+{
+ std::string val(discard ? page::discard_blank_page_title(receipt) : page::from_duplex(true));
+ FIND_OPTION(opt_ind_page_);
+
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ return set_string(opt_ind_page_, val, it->desc->size);
+}
+bool scanner::twain_is_page_discarding_blank_page(bool receipt)
+{
+ FIND_OPTION(opt_ind_page_);
+ if (it == options_.end())
+ return true;
+
+ std::string now(get_string(opt_ind_page_, it->desc->size));
+
+ return page::is_discard_blank_page(now.c_str(), receipt);
+}
+int scanner::twain_set_page_fold(bool fold)
+{
+ std::string val(page::fold_page_title());
+ FIND_OPTION(opt_ind_page_);
+
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ return set_string(opt_ind_page_, val, it->desc->size);
+}
+bool scanner::twain_is_page_fold(void)
+{
+ FIND_OPTION(opt_ind_page_);
+ if (it == options_.end())
+ return true;
+
+ std::string now(get_string(opt_ind_page_, it->desc->size));
+
+ return page::is_fold(now.c_str());
+}
+
+int scanner::twain_set_auto_descrew(bool enable)
+{
+ FIND_OPTION(opt_ind_auto_descrew_);
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ SANE_Bool v = enable;
+ SANE_Int after = 0;
+
+ return sane_invoker::invoke_sane_control_option(hdev_, opt_ind_auto_descrew_, SANE_ACTION_SET_VALUE, &v, &after);
+}
+bool scanner::twain_is_auto_descrew(void)
+{
+ return get_boolean(opt_ind_auto_descrew_);
+}
+
+int scanner::twain_set_erase_black_frame(bool erase)
+{
+ FIND_OPTION(opt_ind_erase_black_frame_);
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ SANE_Bool v = erase;
+ SANE_Int after = 0;
+
+ return sane_invoker::invoke_sane_control_option(hdev_, opt_ind_erase_black_frame_, SANE_ACTION_SET_VALUE, &v, &after);
+}
+bool scanner::twain_is_erase_black_frame(bool* init)
+{
+ FIND_OPTION(opt_ind_erase_black_frame_);
+ if (it == options_.end())
+ return false;
+
+ if (init)
+ *init = it->val.bv;
+
+ return get_boolean(opt_ind_erase_black_frame_);
+}
+
+int scanner::twain_set_filter(int tw_filter, bool enhance)
+{
+ FIND_OPTION(opt_ind_filter_);
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ std::string val(filter::from_filter_type(tw_filter, enhance));
+
+ return set_string(opt_ind_filter_, val, it->desc->size);
+}
+int scanner::twain_get_filter(bool enhance)
+{
+ FIND_OPTION(opt_ind_filter_);
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ std::string val(get_string(opt_ind_filter_, it->desc->size));
+
+ return filter::to_filter_type(val.c_str(), enhance);
+}
+
+int scanner::twain_set_bright(double bright)
+{
+ FIND_OPTION(opt_ind_bright_);
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ SANE_Fixed v = SANE_FIX(bright);
+ SANE_Int after = 0;
+
+ return sane_invoker::invoke_sane_control_option(hdev_, opt_ind_bright_, SANE_ACTION_SET_VALUE, &v, &after);
+}
+double scanner::twain_get_bright(double* init, double* lower, double* upper, double* step)
+{
+ FIND_OPTION(opt_ind_bright_);
+ if (it == options_.end())
+ return .0f;
+
+ if(lower && upper)
+ sane_trans::get_value_range(it->desc, lower, upper);
+ if (init)
+ *init = it->val.dv;
+ if (step)
+ *step = 1.0f;
+
+ return get_double(opt_ind_bright_);
+}
+
+int scanner::twain_set_contrast(double bright)
+{
+ FIND_OPTION(opt_ind_contrast_);
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ SANE_Fixed v = SANE_FIX(bright);
+ SANE_Int after = 0;
+
+ return sane_invoker::invoke_sane_control_option(hdev_, opt_ind_contrast_, SANE_ACTION_SET_VALUE, &v, &after);
+}
+double scanner::twain_get_contrast(double* init, double* lower, double* upper, double* step)
+{
+ FIND_OPTION(opt_ind_contrast_);
+ if (it == options_.end())
+ return .0f;
+
+ if (lower && upper)
+ sane_trans::get_value_range(it->desc, lower, upper);
+ if (init)
+ *init = it->val.dv;
+ if (step)
+ *step = 1.0f;
+
+ return get_double(opt_ind_contrast_);
+}
+
+int scanner::twain_set_gamma(double bright)
+{
+ FIND_OPTION(opt_ind_gamma_);
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ SANE_Fixed v = SANE_FIX(bright);
+ SANE_Int after = 0;
+
+ return sane_invoker::invoke_sane_control_option(hdev_, opt_ind_gamma_, SANE_ACTION_SET_VALUE, &v, &after);
+}
+double scanner::twain_get_gamma(double* init, double* lower, double* upper, double* step)
+{
+ FIND_OPTION(opt_ind_gamma_);
+ if (it == options_.end())
+ return .0f;
+
+ if (lower && upper)
+ sane_trans::get_value_range(it->desc, lower, upper);
+ if (init)
+ *init = it->val.dv;
+ if (step)
+ *step = 1.0f;
+
+ return get_double(opt_ind_gamma_);
+}
+
+int scanner::twain_set_ultrasonic_check(bool check)
+{
+ FIND_OPTION(opt_ind_ultrasonic_);
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ SANE_Bool v = check;
+ SANE_Int after = 0;
+
+ return sane_invoker::invoke_sane_control_option(hdev_, opt_ind_ultrasonic_, SANE_ACTION_SET_VALUE, &v, &after);
+}
+bool scanner::twain_is_ultrasonic_check(bool* init)
+{
+ FIND_OPTION(opt_ind_ultrasonic_);
+ if (it == options_.end())
+ return false;
+
+ if (init)
+ *init = it->val.bv;
+
+ return get_boolean(opt_ind_ultrasonic_);
+}
+
+bool scanner::twain_is_auto_crop(void)
+{
+ return auto_crop_;
+}
+bool scanner::twain_is_paper_on(void)
+{
+ SANE_Bool on = false;
+ unsigned int len = sizeof(on);
+
+ sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_PAPER_ON, &on, &len);
+
+ return on;
+}
+int scanner::twain_get_device_code(char* buf, unsigned int len)
+{
+ return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_DEVICE_CODE, buf, &len);
+}
+
+int scanner::twain_set_sharpen(int sharpen)
+{
+ std::string val(sharpen::from_type(sharpen));
+ FIND_OPTION(opt_ind_sharpen_);
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ return set_string(opt_ind_sharpen_, val, it->desc->size);
+}
+int scanner::twain_get_sharpen(void)
+{
+ FIND_OPTION(opt_ind_sharpen_);
+ if (it == options_.end())
+ return SHARPEN_NONE;
+
+ std::string now(get_string(opt_ind_sharpen_, it->desc->size));
+
+ return sharpen::to_type(now.c_str());
+}
+
+int scanner::twain_get_serial_num(char buf[])
+{
+ unsigned int len = 240;
+
+ return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_SERIAL, buf, &len);
+}
+int scanner::twain_get_hareware_version(char buf[])
+{
+ unsigned int len = 240;
+
+ return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_HARDWARE_VERSION, buf, &len);
+}
+int scanner::twain_get_ip(char buf[])
+{
+ unsigned int len = 240;
+
+ return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_IP, buf, &len);
+}
+
+int scanner::twain_get_dogear_distance(void)
+{
+ SANE_Int v = 0;
+ unsigned int len = sizeof(v);
+
+ sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_DOGEAR_DISTANCE, &v, &len);
+
+ return v;
+}
+int scanner::twain_set_dogear_distance(int dist)
+{
+ SANE_Int v = dist;
+ unsigned int len = sizeof(v);
+
+ return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_SET_DOGEAR_DISTANCE, &v, &len);
+}
+
+int scanner::twain_set_power_level(int level)
+{
+ SANE_Power v = (SANE_Power)level;
+ unsigned int len = sizeof(v);
+
+ return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_SET_POWER_LEVEL, &v, &len);
+}
+int scanner::twain_get_power_level(void)
+{
+ SANE_Power v = SANE_POWER_NONE;
+ unsigned int len = sizeof(v);
+
+ sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_POWER_LEVEL, &v, &len);
+
+ return v;
+}
+
+int scanner::twain_set_to_be_scan(bool yes)
+{
+ SANE_Bool v = yes;
+ unsigned int len = sizeof(v);
+
+ return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_SET_SCAN_WHEN_PAPER_ON, &v, &len);
+}
+bool scanner::twain_get_to_be_scan(void)
+{
+ SANE_Bool v = 0;
+ unsigned int len = sizeof(v);
+
+ sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_SCAN_WHEN_PAPER_ON, &v, &len);
+
+ return v;
+}
+
+int scanner::twain_set_scan_with_hole(bool yes)
+{
+ SANE_Bool v = yes;
+ unsigned int len = sizeof(v);
+
+ return sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_SET_SCAN_WITH_HOLE, &v, &len);
+}
+bool scanner::twain_get_scan_with_hole(void)
+{
+ SANE_Bool v = 0;
+ unsigned int len = sizeof(v);
+
+ sane_invoker::invoke_sane_io_control(hdev_, IO_CTRL_CODE_GET_SCAN_WITH_HOLE, &v, &len);
+
+ return v;
+}
+
+int scanner::twain_set_multioutput_type(int type)
+{
+ FIND_OPTION(opt_ind_multi_out_);
+ if (it == options_.end())
+ return HG_ERR_NOT_OPEN;
+
+ std::string val(multi_out::from_twain_type(type));
+
+ return set_string(opt_ind_multi_out_, val, it->desc->size);
+}
+int scanner::twain_get_multioutput_type(void)
+{
+ FIND_OPTION(opt_ind_multi_out_);
+ if (it == options_.end())
+ return MULTI_OUT_NONE;
+
+ std::string now(get_string(opt_ind_multi_out_, it->desc->size));
+
+ return multi_out::to_twain_type(now.c_str());
+}
+
+int scanner::twain_get_flip_ind(void)
+{
+ return opt_ind_flip_;
+}
+int scanner::twain_get_rotate_bkg_ind(void)
+{
+ return opt_ind_rotate_bkg_;
+}
+int scanner::twain_get_fill_black_bkg_ind(void)
+{
+ return opt_ind_fill_blank_bkg_;
+}
+int scanner::twain_get_edge_ident_ind(void)
+{
+ return opt_ind_edge_ident_;
+}
+int scanner::twain_get_threshold_ind(void)
+{
+ return opt_ind_threshold_;
+}
+int scanner::twain_bkg_filling_method_ind(void)
+{
+ return opt_ind_bkg_filling_method_;
+}
+int scanner::twain_fill_hole_ind(void)
+{
+ return opt_ind_fill_hole_;
+}
+int scanner::twain_fill_hole_ratio_ind(void)
+{
+ return opt_ind_fill_hole_ratio_;
+}
+int scanner::twain_detach_noise_ind(void)
+{
+ return opt_ind_noise_;
+}
+int scanner::twain_detach_noise_threshold_ind(void)
+{
+ return opt_ind_noise_threshold_;
+}
+int scanner::twain_rid_red_ind(void)
+{
+ return opt_ind_rid_red_;
+}
+int scanner::twain_rid_red_hsv_ind(void)
+{
+ return opt_ind_rid_red_hsv_;
+}
+int scanner::twain_screw_detect_ind(void)
+{
+ return opt_ind_screw_detect_;
+}
+int scanner::twain_screw_detect_level_ind(void)
+{
+ return opt_ind_screw_detect_level_;
+}
+int scanner::twain_staple_detect_ind(void)
+{
+ return opt_ind_staple_;
+}
+int scanner::twain_dogear_detect_ind(void)
+{
+ return opt_ind_dogear_;
+}
+int scanner::twain_dark_sample_ind(void)
+{
+ return opt_ind_dark_sample_;
+}
+int scanner::twain_image_split_ind(void)
+{
+ return opt_ind_split_;
+}
+int scanner::twain_fade_bkground_ind(void)
+{
+ return opt_ind_fade_bkg_;
+}
+int scanner::twain_fade_bkground_val_ind(void)
+{
+ return opt_ind_fade_bkg_val_;
+}
+int scanner::twain_size_detect_ind(void)
+{
+ return opt_ind_size_detect_;
+}
+
+
diff --git a/huagaotwain/huagaotwain.h b/huagaotwain/huagaotwain.h
new file mode 100644
index 0000000..0e25600
--- /dev/null
+++ b/huagaotwain/huagaotwain.h
@@ -0,0 +1,446 @@
+#pragma once
+#include "huagao/hgscanner_error.h"
+#include
+#include
+#include
+#include
+#include "sane_option_trans.h"
+
+
+class sane_invoker;
+class scanner;
+struct _dev;
+
+
+class refer
+{
+ volatile long ref_;
+
+public:
+ refer() : ref_(1)
+ {}
+protected:
+ virtual ~refer()
+ {}
+
+public:
+ long add_ref(void)
+ {
+ return InterlockedIncrement(&ref_);
+ }
+ long release(void)
+ {
+ long ref = InterlockedDecrement(&ref_);
+
+ if (ref <= 0)
+ delete this;
+
+ return ref;
+ }
+};
+
+class scanned_img
+{
+ SANE_Parameters param_;
+ std::vector data_;
+
+ std::string file_header(SANE_FinalImgFormat* header, float resolution);
+
+public:
+ scanned_img(SANE_Image* img, SANE_FinalImgFormat* header);
+ ~scanned_img();
+
+public:
+ int width(void);
+ int line_bytes(void);
+ int height(void);
+ int depth(void);
+ int channel(void);
+ SANE_Frame type(void);
+ unsigned int bytes(void);
+ unsigned char* bits(void);
+ void copy_header(SANE_Parameters* head);
+};
+
+extern HMODULE me_;
+namespace local_utility
+{
+ std::wstring reg_read(HKEY root, const wchar_t* path, const wchar_t* name);
+ std::wstring reg_get_app_installing_path(void);
+}
+
+class scanner : public refer
+{
+ SANE_Handle hdev_;
+ sane_invoker* host_;
+ std::string name_;
+ std::string type_;
+ std::string vendor_;
+ std::string product_;
+ bool online_;
+ int option_count_;
+ std::mutex lock_img_;
+ std::vector img_;
+ SANE_FinalImgFormat *fmt_;
+
+ typedef struct _sane_option
+ {
+ int ind;
+ const SANE_Option_Descriptor* desc;
+ union
+ {
+ bool bv;
+ int iv;
+ double dv;
+ std::string* sv;
+ }val;
+ bool operator==(int id)
+ {
+ return ind == id;
+ }
+ }SANEOPTION;
+ std::vector options_;
+
+ float dpi_;
+ HANDLE wait_img_;
+ volatile bool scanning_;
+ int err_;
+ std::string desc_;
+
+ void load_options(void);
+ void init_image_format(void);
+ std::string get_string(int opt, int bytes);
+ bool get_boolean(int opt);
+ int get_integer(int opt);
+ double get_double(int opt);
+ int set_string(int opt, std::string& val, int size, SANE_Int* afterdo = NULL);
+
+ enum twain_essential
+ {
+ TWAIN_RESOLUTION,
+ TWAIN_PAPER_SIZE,
+ };
+
+ int opt_ind_dpi_;
+ int opt_ind_color_mode_;
+ int opt_ind_paper_;
+ int opt_ind_scann_count_; // MAKELONG(count, scan_continue)
+ int opt_ind_text_direction_;
+ int opt_ind_page_;
+ int opt_ind_auto_descrew_;
+ int opt_ind_erase_black_frame_;
+ int opt_ind_filter_;
+ int opt_ind_bright_;
+ int opt_ind_contrast_;
+ int opt_ind_gamma_;
+ int opt_ind_ultrasonic_;
+ int opt_ind_flip_;
+ int opt_ind_rotate_bkg_;
+ int opt_ind_fill_blank_bkg_;
+ int opt_ind_edge_ident_;
+ int opt_ind_threshold_;
+ int opt_ind_bkg_filling_method_;
+ int opt_ind_fill_hole_;
+ int opt_ind_fill_hole_ratio_;
+ int opt_ind_noise_;
+ int opt_ind_noise_threshold_;
+ int opt_ind_rid_red_;
+ int opt_ind_rid_red_hsv_;
+ int opt_ind_sharpen_;
+ int opt_ind_screw_detect_;
+ int opt_ind_screw_detect_level_;
+ int opt_ind_staple_;
+ int opt_ind_dogear_;
+ int opt_ind_dark_sample_;
+ int opt_ind_split_;
+ int opt_ind_fade_bkg_;
+ int opt_ind_fade_bkg_val_;
+ int opt_ind_size_detect_;
+ int opt_ind_multi_out_;
+
+ SANE_ImageType img_fmt_;
+ int jpeg_quality_;
+
+ SANE_CompressionType compression_;
+ bool auto_crop_;
+
+ void(*event_cb_)(int, void*, unsigned int*, void*);
+ void* cb_param_;
+
+public:
+ scanner(sane_invoker* host, struct _dev& dev);
+
+ static std::string type(SANE_Value_Type st);
+ static value_limit limit(SANE_Constraint_Type st);
+
+protected:
+ ~scanner();
+
+public:
+ int close(void);
+ int start(void);
+ int stop(void);
+ void set_event_callback(void(*cb)(int, void*, unsigned int*, void*), void* param);
+ bool wait_image(DWORD milliseconds = -1);
+ int get_scanned_images(DWORD milliseconds = 0);
+ scanned_img* take_first_image(void); // delete returned value, plz
+ bool get_first_image_header(SANE_Parameters* header);
+ int last_error(void);
+
+ SANE_Handle handle(void);
+ bool is_online(void);
+ void set_online(bool online);
+
+ void put_image(SANE_Image* img, unsigned int* len);
+ void scan_finished(const char* desc, int err);
+
+ // up to sane, we set the CAP_xxx according to settings display in UI ...
+ bool get_value_info(int sn, std::string& type, value_limit& limit);
+ bool get_value(int sn, std::list& values, std::string& now, std::string& init);
+ bool get_value(int sn, std::list& values, float& now, float& init);
+ bool get_value(int sn, std::list& values, bool& now, bool& init);
+ bool get_value(int sn, int& now, int& init, int* lower = NULL, int* upper = NULL, int* step = NULL);
+ bool get_value(int sn, float& now, float& init, float* lower = NULL, float* upper = NULL, float* step = NULL);
+
+ int set_value(int sn, std::string val);
+ int set_value(int sn, bool val);
+ int set_value(int sn, int val);
+ int set_value(int sn, double val);
+
+ // attribute for twain ...
+public:
+ int twain_set_resolution(float dpi);
+ float twain_get_resolution(float* init = NULL, std::vector* values = NULL, value_limit* limit = NULL);
+
+ int twain_set_color_mode(int twain_pixel_type);
+ int twain_get_color_mode(int* init = NULL, std::vector* values = NULL, value_limit* limit = NULL);
+ int twain_set_auto_color_type(int type);
+
+ int twain_get_paper_ind(void);
+ int twain_set_paper_lateral(bool lateral);
+ bool twain_is_paper_lateral(void);
+ int twain_set_paper_auto_match_size(bool match);
+ bool twain_is_paper_auto_match_size(void);
+
+ int twain_set_scan_count(int count);
+ int twain_get_scan_count(void);
+
+ int twain_set_final_format(SANE_ImageType type, void* param);
+ int twain_get_jpeg_quality(void);
+ SANE_ImageType get_final_format(void);
+
+ int twain_set_final_compression(int compression);
+ int twain_get_final_compression(int *init = NULL, std::vector* values = NULL);
+
+ int twain_set_text_direction(double degree);
+ double twain_get_text_direction(double* init = NULL, std::list* vals = NULL, value_limit* limit = NULL);
+ int twain_set_text_auto_matic(bool am);
+ bool twain_is_text_auto_matic(void);
+
+ int twain_set_page_duplex(bool dup);
+ bool twain_is_page_duplex(void);
+ int twain_set_page_discarding_blank_page(bool discard, bool receipt);
+ bool twain_is_page_discarding_blank_page(bool receipt);
+ int twain_set_page_fold(bool fold);
+ bool twain_is_page_fold(void);
+
+ int twain_set_auto_descrew(bool enable);
+ bool twain_is_auto_descrew(void);
+
+ int twain_set_erase_black_frame(bool erase);
+ bool twain_is_erase_black_frame(bool* init);
+
+ int twain_set_filter(int tw_filter, bool enhance);
+ int twain_get_filter(bool enhance);
+
+ int twain_set_bright(double bright);
+ double twain_get_bright(double* init = NULL, double* lower = NULL, double* upper = NULL, double* step = NULL);
+
+ int twain_set_contrast(double bright);
+ double twain_get_contrast(double* init = NULL, double* lower = NULL, double* upper = NULL, double* step = NULL);
+
+ int twain_set_gamma(double bright);
+ double twain_get_gamma(double* init = NULL, double* lower = NULL, double* upper = NULL, double* step = NULL);
+
+ int twain_set_ultrasonic_check(bool check);
+ bool twain_is_ultrasonic_check(bool* init = NULL);
+
+ bool twain_is_auto_crop(void);
+ bool twain_is_paper_on(void);
+ int twain_get_device_code(char* buf, unsigned int len);
+
+ int twain_set_sharpen(int sharpen);
+ int twain_get_sharpen(void);
+
+ int twain_get_serial_num(char buf[256]);
+ int twain_get_hareware_version(char buf[256]);
+ int twain_get_ip(char buf[256]);
+
+ int twain_get_dogear_distance(void);
+ int twain_set_dogear_distance(int dist);
+
+ int twain_set_power_level(int level);
+ int twain_get_power_level(void);
+
+ int twain_set_to_be_scan(bool yes);
+ bool twain_get_to_be_scan(void);
+
+ int twain_set_scan_with_hole(bool yes);
+ bool twain_get_scan_with_hole(void);
+
+ int twain_set_multioutput_type(int type);
+ int twain_get_multioutput_type(void);
+
+ int twain_get_flip_ind(void);
+ int twain_get_rotate_bkg_ind(void);
+ int twain_get_fill_black_bkg_ind(void);
+ int twain_get_edge_ident_ind(void);
+ int twain_get_threshold_ind(void);
+ int twain_bkg_filling_method_ind(void);
+ int twain_fill_hole_ind(void);
+ int twain_fill_hole_ratio_ind(void);
+ int twain_detach_noise_ind(void);
+ int twain_detach_noise_threshold_ind(void);
+ int twain_rid_red_ind(void);
+ int twain_rid_red_hsv_ind(void);
+ int twain_screw_detect_ind(void);
+ int twain_screw_detect_level_ind(void);
+ int twain_staple_detect_ind(void);
+ int twain_dogear_detect_ind(void);
+ int twain_dark_sample_ind(void);
+ int twain_image_split_ind(void);
+ int twain_fade_bkground_ind(void);
+ int twain_fade_bkground_val_ind(void);
+ int twain_size_detect_ind(void);
+};
+struct delete_scanner
+{
+ void operator()(scanner* p)
+ {
+ p->release();
+ }
+};
+typedef struct _dev
+{
+ scanner* scanner;
+ std::string name;
+ std::string type;
+ std::string vendor;
+ std::string product;
+
+ struct _dev()
+ {
+ scanner = NULL;
+ name = type = vendor = product = "";
+ }
+ bool operator==(const char* n)
+ {
+ return name == n;
+ }
+ bool operator==(SANE_Handle h)
+ {
+ return scanner && scanner->handle() == h;
+ }
+}SANEDEV;
+
+
+
+
+class sane_invoker
+{
+ bool ok_;
+ std::wstring cfg_file_;
+ SANE_Int ver_;
+ HMODULE sane_;
+ HANDLE first_cb_;
+ SANEAPI sane_api_;
+
+ std::mutex lock_dev_;
+ std::vector devices_;
+
+ SANE_Status (*real_sane_init_)(SANE_Int* version_code, SANE_Auth_Callback authorize);
+ void (*real_sane_exit_)(void);
+ SANE_Status(*real_sane_get_devices_)(const SANE_Device*** device_list, SANE_Bool local_only);
+ SANE_Status(*real_sane_open_)(SANE_String_Const devicename, SANE_Handle* handle);
+ void (*real_sane_close_)(SANE_Handle handle);
+ const SANE_Option_Descriptor* (*real_sane_get_option_descriptor_)(SANE_Handle handle, SANE_Int option);
+ SANE_Status(*real_sane_control_option_)(SANE_Handle handle, SANE_Int option, SANE_Action action, void* value, SANE_Int* info);
+ SANE_Status(*real_sane_get_parameters_)(SANE_Handle handle, SANE_Parameters* params);
+ SANE_Status(*real_sane_start_)(SANE_Handle handle);
+ SANE_Status(*real_sane_read_)(SANE_Handle handle, SANE_Byte* data, SANE_Int max_length, SANE_Int* length);
+ void (*real_sane_cancel_)(SANE_Handle handle);
+ SANE_Status(*real_sane_set_io_mode_)(SANE_Handle handle, SANE_Bool non_blocking);
+ SANE_Status(*real_sane_get_select_fd_)(SANE_Handle handle, SANE_Int* fd);
+ SANE_String_Const(*real_sane_strstatus_)(SANE_Status status);
+ SANE_Status(*real_sane_init_ex_)(SANE_Int* version_code, sane_callback cb, void* param);
+ SANE_Status(*real_sane_io_control_)(SANE_Handle h, unsigned long code, void* data, unsigned* len);
+
+ void(*log_)(int, const char*);
+
+ bool load_sane(void);
+ int handle_sane_event(SANE_Handle hdev, int code, void* data, unsigned int* len);
+ void get_online_devices(std::vector& devs);
+ int get_online_device_count(void);
+ scanner* find_scanner(SANE_Handle hdev);
+ scanner* open(const char* name, int* err);
+
+ static void no_log(int, const char*);
+ static int sane_callback_handler( // 注册回调的对象,需要保证该回调是多线程安全的
+ SANE_Handle hdev // 产生事件的设备句柄
+ , int code // 回调事件代码
+ , void* data // 回调事件数据,根据事件代码有所不同,参照具体事件定义
+ , unsigned int* len // 数据长度(字节),或者event_data的缓冲区长度,详细请看相应的事件代码
+ , void* param // 用户自定义数据,与调用sane_init_ex传入时的保持一致
+ ); // 返回值依不同的事件代码而定,通常为“0”
+
+ static sane_invoker* inst_;
+
+protected:
+ sane_invoker(const wchar_t* path);
+ ~sane_invoker();
+
+public:
+ static int load_dll(const wchar_t* path_dll, HMODULE* dll);
+ static bool initialize(HMODULE me);
+ static void uninitialize(void);
+ static std::string u2m(const wchar_t* u, int page = CP_ACP);
+ static std::wstring m2u(const char* m, int page = CP_ACP);
+ static std::string u2ansi(const wchar_t* u);
+ static std::string u2utf8(const wchar_t* u);
+ static std::string utf82ansi(const char* utf8);
+ static std::string ansi2utf8(const char* ansi);
+ static std::wstring utf82u(const char* utf8);
+ static std::wstring ansi2u(const char* ansi);
+ static void log_debug_info(const char* info);
+#ifdef VLOG_OK
+ static void __cdecl log_debug_info(int bytes, const char* fmt, ...);
+#endif
+
+ static SANE_Status invoke_sane_init(SANE_Int* version_code, SANE_Auth_Callback authorize);
+ static void invoke_sane_exit(void);
+ static SANE_Status invoke_sane_get_devices(const SANE_Device*** device_list, SANE_Bool local_only);
+ static SANE_Status invoke_sane_open(SANE_String_Const devicename, SANE_Handle* handle);
+ static void invoke_sane_close(SANE_Handle handle);
+ static const SANE_Option_Descriptor* invoke_sane_get_option_descriptor(SANE_Handle handle, SANE_Int option);
+ static SANE_Status invoke_sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void* value, SANE_Int* info);
+ static SANE_Status invoke_sane_get_parameters(SANE_Handle handle, SANE_Parameters* params);
+ static SANE_Status invoke_sane_start(SANE_Handle handle);
+ static SANE_Status invoke_sane_read(SANE_Handle handle, SANE_Byte* data, SANE_Int max_length, SANE_Int* length);
+ static void invoke_sane_cancel(SANE_Handle handle);
+ static SANE_Status invoke_sane_set_io_mode(SANE_Handle handle, SANE_Bool non_blocking);
+ static SANE_Status invoke_sane_get_select_fd(SANE_Handle handle, SANE_Int* fd);
+ static SANE_String_Const invoke_sane_strstatus(SANE_Status status);
+ static SANE_Status invoke_sane_init_ex(SANE_Int* version_code, sane_callback cb, void* param);
+ static SANE_Status invoke_sane_io_control(SANE_Handle h, unsigned long code, void* data, unsigned* len);
+
+ static LPSANEAPI get_api(void);
+
+public:
+ static bool is_ok(void);
+ static std::string version(LPDWORD v = NULL);
+ static void get_devices(std::vector& devs);
+ static scanner* open_scanner(const char* name, int* err);
+ static int online_devices(void);
+};
+
+
diff --git a/huagaotwain/huagaotwain.rc b/huagaotwain/huagaotwain.rc
new file mode 100644
index 0000000..a3d184e
--- /dev/null
+++ b/huagaotwain/huagaotwain.rc
@@ -0,0 +1,61 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// (壬й) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
+LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
+#pragma code_page(936)
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // (壬й) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/huagaotwain/huagaotwain.vcxproj b/huagaotwain/huagaotwain.vcxproj
new file mode 100644
index 0000000..67b80f6
--- /dev/null
+++ b/huagaotwain/huagaotwain.vcxproj
@@ -0,0 +1,184 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+
+ {c3b47ce2-27ce-4509-ab59-3c0f194f0fce}
+ DynamicLibrary
+ huagaotwain
+ Win32Proj
+ 10.0
+
+
+
+ DynamicLibrary
+ true
+ v142
+ Unicode
+ Static
+
+
+ DynamicLibrary
+ false
+ true
+ v142
+ Unicode
+ Static
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)..\..\sdk\include\;$(IncludePath)
+ $(SolutionDir)..\..\release\win\$(PlatformTarget)\$(Configuration)\
+ $(SolutionDir)..\..\tmp\$(PlatformTarget)\$(Configuration)\$(ProjectName)\
+
+
+ false
+ $(SolutionDir)..\..\sdk\include\;$(IncludePath)
+ $(SolutionDir)..\..\release\win\$(PlatformTarget)\$(Configuration)\
+ $(SolutionDir)..\..\tmp\$(PlatformTarget)\$(Configuration)\$(ProjectName)\
+
+
+
+ Use
+ Level3
+ Disabled
+ TWPP_NO_NOTES;TWPP_IS_DS;HGSCANNER_EXPORT;CUSTOM_USBVIEW;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
+ true
+ 4996
+ pch.h
+
+
+ Console
+ true
+ $(ProjectDir)twain.def
+
+
+ mkdir $(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)
+move /Y "$(OutDirFullPath)$(ProjectName).exp" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+move /Y "$(OutDirFullPath)$(ProjectName).lib" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+move /Y "$(OutDirFullPath)$(ProjectName).pdb" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+copy $(TargetPath) $(WinDir)\twain_32\HuaGoScan\$(ProjectName).ds /y
+
+
+
+
+ Level3
+ Use
+ MaxSpeed
+ true
+ true
+ TWPP_NO_NOTES;TWPP_IS_DS;HGSCANNER_EXPORT;CUSTOM_USBVIEW;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)
+ true
+ 4996
+ pch.h
+
+
+ Console
+ true
+ true
+ true
+ $(ProjectDir)twain.def
+
+
+ mkdir $(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)
+move /Y "$(OutDirFullPath)$(ProjectName).exp" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+move /Y "$(OutDirFullPath)$(ProjectName).lib" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+move /Y "$(OutDirFullPath)$(ProjectName).pdb" "$(SolutionDir)..\..\sdk\lib\win\$(PlatformTarget)\$(Configuration)"
+copy $(TargetPath) $(WinDir)\twain_32\HuaGoScan\$(ProjectName).ds /y
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/huagaotwain/huagaotwain.vcxproj.filters b/huagaotwain/huagaotwain.vcxproj.filters
new file mode 100644
index 0000000..e82d13b
--- /dev/null
+++ b/huagaotwain/huagaotwain.vcxproj.filters
@@ -0,0 +1,165 @@
+
+
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms
+
+
+ {668e9513-f6db-4f9d-9e0e-76cb8b5b7882}
+
+
+ {df21031b-938a-4a08-ae64-e869e2586201}
+
+
+
+
+
+
+
+ twain
+
+
+
+
+
+
+
+
+
+ twain
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain\twpp
+
+
+ twain
+
+
+ twain
+
+
+
+
+
+
+
+
+
+
+
+ Resource Files
+
+
+
\ No newline at end of file
diff --git a/huagaotwain/pch.cpp b/huagaotwain/pch.cpp
new file mode 100644
index 0000000..bcb5590
--- /dev/null
+++ b/huagaotwain/pch.cpp
@@ -0,0 +1 @@
+#include "pch.h"
diff --git a/huagaotwain/pch.h b/huagaotwain/pch.h
new file mode 100644
index 0000000..529bbb1
--- /dev/null
+++ b/huagaotwain/pch.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "targetver.h"
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include
diff --git a/huagaotwain/resource.h b/huagaotwain/resource.h
new file mode 100644
index 0000000..477d3de
--- /dev/null
+++ b/huagaotwain/resource.h
@@ -0,0 +1,15 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ ɵİļ
+// huagaotwain.rc ʹ
+//
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 103
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/huagaotwain/sane_option_trans.cpp b/huagaotwain/sane_option_trans.cpp
new file mode 100644
index 0000000..1216bff
--- /dev/null
+++ b/huagaotwain/sane_option_trans.cpp
@@ -0,0 +1,630 @@
+#include "sane_option_trans.h"
+#include
+#include "./twain/twain_2.4.h"
+
+
+namespace sane_trans
+{
+ bool get_value_list(const SANE_Option_Descriptor* desc, std::list* values)
+ {
+ if (desc->type != SANE_TYPE_STRING)
+ return false;
+ if (desc->constraint_type != SANE_CONSTRAINT_STRING_LIST)
+ return false;
+
+ const SANE_String_Const* str = desc->constraint.string_list;
+ int ind = 0;
+ while (str[ind])
+ values->push_back(str[ind++]);
+
+ return true;
+ }
+ bool get_value_list(const SANE_Option_Descriptor* desc, std::list* values)
+ {
+ if (desc->type != SANE_TYPE_INT && desc->type != SANE_TYPE_FIXED)
+ return false;
+ if (desc->constraint_type != SANE_CONSTRAINT_WORD_LIST)
+ return false;
+
+ const SANE_Word* val = desc->constraint.word_list;
+ if (desc->type == SANE_TYPE_FIXED)
+ {
+ for (int i = 0; i < val[0]; ++i)
+ values->push_back(SANE_UNFIX(val[i + 1]));
+ }
+ else
+ {
+ for (int i = 0; i < val[0]; ++i)
+ values->push_back(val[i + 1]);
+ }
+
+ return true;
+ }
+ bool get_value_range(const SANE_Option_Descriptor* desc, double* lower, double* upper)
+ {
+ if (desc->type != SANE_TYPE_INT && desc->type != SANE_TYPE_FIXED)
+ return false;
+ if (desc->constraint_type != SANE_CONSTRAINT_RANGE)
+ return false;
+
+ if (desc->type == SANE_TYPE_FIXED)
+ {
+ *lower = SANE_UNFIX(desc->constraint.range->min);
+ *upper = SANE_UNFIX(desc->constraint.range->max);
+ }
+ else
+ {
+ *lower = desc->constraint.range->min;
+ *upper = desc->constraint.range->max;
+ }
+
+ return true;
+ }
+}
+
+
+namespace color_mode
+{
+ static char g_name[] = { "\351\242\234\350\211\262\346\250\241\345\274\217" }; // 颜色模式
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+
+ struct
+ {
+ int tw_pixel;
+ char op_val[80];
+ }g_ops[] = { {TWPT_BW, { "\351\273\221\347\231\275" }}, // 黑白
+ {TWPT_GRAY, { "256\347\272\247\347\201\260\345\272\246" }}, // 256级灰度
+ {TWPT_RGB, { "24\344\275\215\345\275\251\350\211\262" }}, // 24位彩色
+ {TWPT_AUTOMATIC_COLOR, {"\351\242\234\350\211\262\350\207\252\345\212\250\350\257\206\345\210\253"}} // 颜色自动识别
+ };
+ int to_twain_pixel_type(const char* op_val)
+ {
+ for (size_t i = 0; i < _countof(g_ops); ++i)
+ {
+ if (strcmp(g_ops[i].op_val, op_val) == 0)
+ return g_ops[i].tw_pixel;
+ }
+
+ return TWPT_RGB;
+ }
+ std::string from_twain_pixel_type(int twpt)
+ {
+ for (size_t i = 0; i < _countof(g_ops); ++i)
+ {
+ if (g_ops[i].tw_pixel == twpt)
+ return g_ops[i].op_val;
+ }
+
+ return g_ops[_countof(g_ops) - 1].op_val;
+ }
+}
+
+namespace dpi
+{
+ static char g_name[] = { "\345\210\206\350\276\250\347\216\207" }; // 分辨率
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace paper
+{
+ static char g_name[] = { "\347\272\270\345\274\240\345\260\272\345\257\270" }; // 纸张尺寸
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+
+ static char g_lateral[] = { "\346\250\252\345\220\221" };
+ bool is_lateral(const char* paper_val)
+ {
+ return strstr(paper_val, g_lateral) != NULL;
+ }
+ std::string lateral_title(void)
+ {
+ return g_lateral;
+ }
+
+ static char g_auto_size[] = { "\345\214\271\351\205\215\345\216\237\345\247\213\345\260\272\345\257\270" }; // 匹配原始尺寸
+ bool is_auto_size(const char* paper_val)
+ {
+ return strstr(paper_val, g_auto_size) != NULL;
+ }
+ std::string auto_size_title(void)
+ {
+ return g_auto_size;
+ }
+
+ static char g_auto_crop[] = { "\346\234\200\345\244\247\346\211\253\346\217\217\345\260\272\345\257\270\350\207\252\345\212\250\350\243\201\345\210\207" }; // 最大扫描尺寸自动裁切
+ bool is_auto_crop(const char* paper_val)
+ {
+ return strstr(paper_val, g_auto_crop) != NULL;
+ }
+}
+
+namespace scan_count
+{
+ static char g_parent[] = { "\346\211\253\346\217\217\345\274\240\346\225\260" };// 扫描张数
+ bool is_parent(const char* desc_title)
+ {
+ return strcmp(g_parent, desc_title) == 0;
+ }
+ static char g_name[] = { "\346\211\253\346\217\217\346\225\260\351\207\217" }; // 扫描数量
+ bool is_me(const char* desc_title)
+ {
+ return strstr(desc_title, g_name);
+ }
+ std::string scan_continous_val(void)
+ {
+ return "\350\277\236\347\273\255\346\211\253\346\217\217"; // 连续扫描
+ }
+}
+
+namespace text_direction
+{
+ static char g_direction[] = { "\346\226\207\347\250\277\346\226\271\345\220\221" }; // 文稿方向
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(desc_title, g_direction) == 0;
+ }
+ struct
+ {
+ double angle;
+ std::string op_val;
+ }g_ops[] = { {.0f, "0\302\260"}
+ , {90.0f, "90\302\260"}
+ , {180.0f, "180\302\260"}
+ , {270.0f, "270\302\260"}
+ };
+ static char g_auto_dir[] = { "\350\207\252\345\212\250\346\226\207\346\234\254\346\226\271\345\220\221\350\257\206\345\210\253\302\260" }; // 自动文本方向识别°
+ double to_twain_angle(const char* opt_val)
+ {
+ for (size_t i = 0; i < _countof(g_ops); ++i)
+ {
+ if (g_ops[i].op_val == opt_val)
+ return g_ops[i].angle;
+ }
+
+ return g_ops[0].angle;
+ }
+ std::string from_twain_angle(double angle)
+ {
+ for (size_t i = 0; i < _countof(g_ops); ++i)
+ {
+ if (IS_DOUBLE_EQUAL(g_ops[i].angle, angle))
+ return g_ops[i].op_val;
+ }
+
+ return g_ops[0].op_val;
+ }
+ bool is_auto(const char* opt_val)
+ {
+ return strcmp(opt_val, g_auto_dir) == 0;
+ }
+ std::string auto_val(void)
+ {
+ return g_auto_dir;
+ }
+}
+
+namespace page
+{
+ static char g_name[] = { "\346\211\253\346\217\217\351\241\265\351\235\242" }; // 扫描页面
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+
+ static char g_not_duplex[] = { "\345\215\225\351\235\242" }; // 单面
+ static char g_duplex[] = { "\345\217\214\351\235\242" }; // 双面
+ bool is_duplex(const char* opval)
+ {
+ return strcmp(opval, g_duplex) == 0;
+ }
+ std::string from_duplex(bool duplex)
+ {
+ return duplex ? g_duplex : g_not_duplex;
+ }
+
+ static char g_discard_blank[] = { "\350\267\263\350\277\207\347\251\272\347\231\275\351\241\265\357\274\210\351\200\232\347\224\250\357\274\211" }; // 跳过空白页(通用)
+ static char g_discard_blank_receipt[] = { "\350\267\263\350\277\207\347\251\272\347\231\275\351\241\265\357\274\210\345\217\221\347\245\250\347\272\270\357\274\211" }; // 跳过空白页(发票纸)
+ bool is_discard_blank_page(const char* opval, bool receipt)
+ {
+ return receipt ? strcmp(g_discard_blank_receipt, opval) == 0 :
+ strcmp(g_discard_blank, opval) == 0;
+ }
+ std::string discard_blank_page_title(bool receipt)
+ {
+ return receipt ? g_discard_blank_receipt : g_discard_blank;
+ }
+
+ static char g_fold[] = { "\345\257\271\346\212\230" }; // 对折
+ bool is_fold(const char* opval)
+ {
+ return strcmp(g_fold, opval) == 0;
+ }
+ std::string fold_page_title(void)
+ {
+ return g_fold;
+ }
+
+}
+
+namespace auto_descrew
+{
+ static char g_name[] = { "\350\207\252\345\212\250\347\272\240\345\201\217" }; // 自动纠偏
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace erase_black_frame
+{
+ static char g_name[] = { "\346\266\210\351\231\244\351\273\221\346\241\206" }; // 消除黑框
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace filter
+{
+ static char g_name[] = { "\347\201\260\345\272\246\346\210\226\351\273\221\347\231\275\345\233\276\345\203\217 - \351\231\244\350\211\262" }; // 灰度或黑白图像 - 除色
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+
+ struct
+ {
+ int twfilter;
+ std::string opt_val;
+ }g_ops[] = { {TWFT_RED, "\351\231\244\347\272\242\350\211\262"} // 除红色
+ , {TWFT_GREEN, "\351\231\244\347\273\277\350\211\262"} // 除绿色
+ , {TWFT_BLUE, "\351\231\244\350\223\235\350\211\262"} // 除蓝色
+ , {TWFT_NONE, "\344\270\215\351\231\244\350\211\262"} // 不除色
+
+ // 颜色增强
+ // , {ENHANCE_COLOR_NONE, "\351\231\244\347\273\277\350\211\262"} // 不增强
+ , {ENHANCE_COLOR_RED, "\347\272\242\350\211\262\345\242\236\345\274\272"} // 红色增强
+ , {ENHANCE_COLOR_GREEN,"\347\273\277\350\211\262\345\242\236\345\274\272"} // 绿色增强
+ , {ENHANCE_COLOR_BLUE, "\350\223\235\350\211\262\345\242\236\345\274\272"} // 蓝色增强
+ };
+ static int g_enhance_base = 4;
+ int to_filter_type(const char* opt_val, bool enhance)
+ {
+ if (enhance)
+ {
+ for (size_t i = g_enhance_base; i < _countof(g_ops); ++i)
+ {
+ if (g_ops[i].opt_val == opt_val)
+ return g_ops[i].twfilter;
+ }
+
+ return ENHANCE_COLOR_NONE;
+ }
+
+ for (size_t i = 0; i < g_enhance_base; ++i)
+ {
+ if (g_ops[i].opt_val == opt_val)
+ return g_ops[i].twfilter;
+ }
+
+ return TWFT_NONE;
+ }
+ std::string from_filter_type(int filter, bool enhance)
+ {
+ if (enhance)
+ {
+ for (size_t i = g_enhance_base; i < _countof(g_ops); ++i)
+ {
+ if (g_ops[i].twfilter == filter)
+ return g_ops[i].opt_val;
+ }
+ }
+ else
+ {
+ for (size_t i = 0; i < g_enhance_base; ++i)
+ {
+ if (g_ops[i].twfilter == filter)
+ return g_ops[i].opt_val;
+ }
+ }
+
+ return g_ops[g_enhance_base - 1].opt_val;
+ }
+}
+
+namespace bright
+{
+ static char g_name[] = { "\344\272\256\345\272\246" }; // 亮度
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+namespace contrast
+{
+ static char g_name[] = { "\345\257\271\346\257\224\345\272\246" }; // 对比度
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+namespace gamma
+{
+ static char g_name[] = { "\344\274\275\347\216\233" }; // 伽玛
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace ultrasonic
+{
+ static char g_name[] = { "\350\266\205\345\243\260\346\263\242\346\243\200\346\265\213" }; // 超声波检测
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace flip
+{
+ static char g_name[] = { "\344\272\244\346\215\242\346\255\243\345\217\215\351\235\242" }; // 交换正反面
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace rotate_bg
+{
+ static char g_name[] = { "\350\203\214\351\235\242\346\227\213\350\275\254180\302\260" }; // 背面旋转180°
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace fill_black_border
+{
+ static char g_name[] = { "\346\266\210\351\231\244\351\273\221\346\241\206" }; // 消除黑框
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace edge_ident
+{
+ static char g_name[] = { "\350\276\271\347\274\230\347\274\251\350\277\233" }; // 边缘缩进
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace threshold
+{
+ static char g_name[] = { "\351\230\210\345\200\274" }; // 阈值
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace bkg_filling_method
+{
+ static char g_name[] = { "\350\203\214\346\231\257\345\241\253\345\205\205\346\226\271\345\274\217" }; // 背景填充方式
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+
+ static char g_convex[] = { "\345\207\270\345\244\232\350\276\271\345\275\242" };// 凸多边形
+ bool is_convex(const char* opval)
+ {
+ return strcmp(opval, g_convex) == 0;
+ }
+}
+
+namespace fill_hole
+{
+ static char g_name[] = { "\347\251\277\345\255\224\347\247\273\351\231\244" }; // 穿孔移除
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+
+ static char g_ratio[] = { "\347\251\277\345\255\224\346\220\234\347\264\242\350\214\203\345\233\264\345\215\240\345\271\205\351\235\242\346\257\224\344\276\213" }; // 穿孔搜索范围占幅面比例
+ bool is_ratio(const char* desc_title)
+ {
+ return strstr(desc_title, g_ratio) != NULL;
+ }
+}
+
+namespace noise
+{
+ static char g_name[] = { "\351\273\221\347\231\275\345\233\276\345\203\217\345\231\252\347\202\271\344\274\230\345\214\226" }; // 黑白图像噪点优化
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+
+ static char g_threshold[] = { "\345\231\252\347\202\271\344\274\230\345\214\226\345\260\272\345\257\270" }; // 噪点优化尺寸
+ bool is_threshold(const char* desc_title)
+ {
+ return strstr(desc_title, g_threshold) != NULL;
+ }
+}
+
+namespace rid_red
+{
+ static char g_name[] = { "24\344\275\215\345\275\251\350\211\262\345\233\276\345\203\217 - \345\244\232\346\265\201\350\276\223\345\207\272\351\231\244\347\272\242" }; // 24位彩色图像 - 多流输出除红
+ static char g_hsv[] = { "24\344\275\215\345\275\251\350\211\262\345\233\276\345\203\217 - \347\255\224\351\242\230\345\215\241\351\231\244\347\272\242" }; // 24位彩色图像 - 答题卡除红
+ bool is_me(const char* desc_title, bool hsv)
+ {
+ return strcmp(hsv ? g_hsv : g_name, desc_title) == 0;
+ }
+}
+
+namespace sharpen
+{
+ static char g_name[] = { "\351\224\220\345\214\226\344\270\216\346\250\241\347\263\212" }; // 锐化与模糊
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+
+ struct
+ {
+ int type;
+ std::string op_val;
+ }g_ops[] = { {SHARPEN_NONE, "\346\227\240"} // 无
+ , {SHARPEN_NORMAL, "\351\224\220\345\214\226"} // 锐化
+ , {SHARPEN_MORE, "\350\277\233\344\270\200\346\255\245\351\224\220\345\214\226"} // 进一步锐化
+ , {SHARPEN_BLUR, "\346\250\241\347\263\212"} // 模糊
+ , {SHARPEN_BLUR_MORE, "\350\277\233\344\270\200\346\255\245\346\250\241\347\263\212"} // 进一步模糊
+ };
+ int to_type(const char* opval)
+ {
+ for (size_t i = 0; i < _countof(g_ops); ++i)
+ {
+ if (g_ops[i].op_val == opval)
+ return g_ops[i].type;
+ }
+
+ return SHARPEN_NONE;
+ }
+ std::string from_type(int type)
+ {
+ for (size_t i = 0; i < _countof(g_ops); ++i)
+ {
+ if (g_ops[i].type == type)
+ return g_ops[i].op_val;
+ }
+
+ return g_ops[0].op_val;
+ }
+}
+
+namespace screw
+{
+ static char g_name[] = { "\346\255\252\346\226\234\346\243\200\346\265\213" }; // 歪斜检测
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+
+ static char g_level[] = { "\346\255\252\346\226\234\346\243\200\346\265\213\345\244\215\346\235\202\345\272\246" }; // 歪斜检测复杂度
+ bool is_level(const char* desc_title)
+ {
+ return strstr(desc_title, g_level) != NULL;
+ }
+}
+
+namespace staple
+{
+ static char g_name[] = { "\350\243\205\350\256\242\346\243\200\346\265\213" }; // 装订检测
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace dogear
+{
+ static char g_name[] = { "\346\212\230\350\247\222\346\243\200\346\265\213" }; // 折角检测
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace sample
+{
+ static char g_name[] = { "\346\267\261\350\211\262\346\240\267\345\274\240" }; // 深色样张
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace split
+{
+ static char g_name[] = { "\345\233\276\345\203\217\346\213\206\345\210\206" }; // 图像拆分
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace fade_bkg
+{
+ static char g_name[] = { "\350\203\214\346\231\257\347\247\273\351\231\244" }; // 背景移除
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+
+ static char g_val[] = { "\350\203\214\346\231\257\350\211\262\345\275\251\346\265\256\345\212\250\350\214\203\345\233\264" }; // 背景色彩浮动范围
+ bool is_value(const char* desc_title)
+ {
+ return strstr(desc_title, g_val) != NULL;
+ }
+}
+
+namespace size_detect
+{
+ static char g_name[] = { "\345\260\272\345\257\270\346\243\200\346\265\213" }; // 尺寸检测
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+}
+
+namespace multi_out
+{
+ static char g_name[] = { "24\344\275\215\345\275\251\350\211\262\345\233\276\345\203\217-\345\244\232\346\265\201\350\276\223\345\207\272" }; // 24位彩色图像-多流输出
+ bool is_me(const char* desc_title)
+ {
+ return strcmp(g_name, desc_title) == 0;
+ }
+
+ struct
+ {
+ int type;
+ std::string op_val;
+ }g_ops[] = { {MULTI_OUT_NONE, "\344\270\215\351\200\211\346\213\251\350\276\223\345\207\272\346\250\241\345\274\217"} // 不选择输出模式
+ , {MULTI_OUT_ALL, "\345\275\251\350\211\262+\347\201\260\345\272\246+\351\273\221\347\231\275"} // 彩色+灰度+黑白
+ , {MULTI_OUT_COLOR_GRAY, "\345\275\251\350\211\262+\347\201\260\345\272\246"} // 彩色+灰度
+ , {MULTI_OUT_COLOR_BW, "\345\275\251\350\211\262+\351\273\221\347\231\275"} // 彩色+黑白
+ , {MULTI_OUT_GRAY_BW, "\347\201\260\345\272\246+\351\273\221\347\231\275"} // 灰度+黑白
+ };
+ int to_twain_type(const char* opval)
+ {
+ for (int i = 0; i < _countof(g_ops); ++i)
+ {
+ if (g_ops[i].op_val == opval)
+ return g_ops[i].type;
+ }
+
+ return MULTI_OUT_NONE;
+ }
+ std::string from_twain_type(int type)
+ {
+ for (int i = 0; i < _countof(g_ops); ++i)
+ {
+ if (g_ops[i].type == type)
+ return g_ops[i].op_val;
+ }
+
+ return g_ops[0].op_val;
+ }
+}
diff --git a/huagaotwain/sane_option_trans.h b/huagaotwain/sane_option_trans.h
new file mode 100644
index 0000000..e551fa2
--- /dev/null
+++ b/huagaotwain/sane_option_trans.h
@@ -0,0 +1,230 @@
+// utilities for transfroming options between TWAIN and sane ...
+//
+// Date: 2022-04-14
+//
+
+#pragma once
+
+#include "sane/sane_ex.h"
+#include
+#include
+#include
+
+#define TWPT_AUTOMATIC_COLOR 0x0a0c
+#define IS_DOUBLE_EQUAL(a, b) fabs((a) - (b)) < .000001
+
+#define ENHANCE_COLOR_NONE 0
+#define ENHANCE_COLOR_RED 1
+#define ENHANCE_COLOR_GREEN 2
+#define ENHANCE_COLOR_BLUE 3
+
+#define SHARPEN_NONE 0
+#define SHARPEN_NORMAL 1
+#define SHARPEN_MORE 2
+#define SHARPEN_BLUR 3
+#define SHARPEN_BLUR_MORE 4
+
+#define MULTI_OUT_NONE -1
+#define MULTI_OUT_ALL 0
+#define MULTI_OUT_COLOR_GRAY 1
+#define MULTI_OUT_COLOR_BW 2
+#define MULTI_OUT_GRAY_BW 3
+
+enum value_limit
+{
+ VAL_LIMIT_NONE = 0, //
+ VAL_LIMIT_ENUM, //
+ VAL_LIMIT_RANGE, //
+};
+
+namespace sane_trans
+{
+ bool get_value_list(const SANE_Option_Descriptor* desc, std::list* values);
+ bool get_value_list(const SANE_Option_Descriptor* desc, std::list* values);
+ bool get_value_range(const SANE_Option_Descriptor* desc, double* lower, double* upper);
+}
+
+
+namespace color_mode
+{
+ bool is_me(const char* desc_title);
+ int to_twain_pixel_type(const char* op_val);
+ std::string from_twain_pixel_type(int twpt);
+}
+
+namespace dpi
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace paper
+{
+ bool is_me(const char* desc_title);
+ bool is_lateral(const char* paper_val);
+ std::string lateral_title(void);
+ bool is_auto_size(const char* paper_val);
+ std::string auto_size_title(void);
+ bool is_auto_crop(const char* paper_val);
+}
+
+namespace scan_count
+{
+ bool is_parent(const char* desc_title);
+ bool is_me(const char* desc_title);
+ std::string scan_continous_val(void);
+}
+
+namespace text_direction
+{
+ bool is_me(const char* desc_title);
+ double to_twain_angle(const char* opt_val);
+ std::string from_twain_angle(double angle);
+
+ bool is_auto(const char* opt_val);
+ std::string auto_val(void);
+}
+
+namespace page
+{
+ bool is_me(const char* desc_title);
+ bool is_duplex(const char* opval);
+ std::string from_duplex(bool duplex);
+ bool is_discard_blank_page(const char* opval, bool receipt);
+ std::string discard_blank_page_title(bool receipt);
+ bool is_fold(const char* opval);
+ std::string fold_page_title(void);
+}
+
+namespace auto_descrew
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace erase_black_frame
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace filter
+{
+ bool is_me(const char* desc_title);
+ int to_filter_type(const char* opt_val, bool enhance);
+ std::string from_filter_type(int filter, bool enhance);
+}
+
+namespace bright
+{
+ bool is_me(const char* desc_title);
+}
+namespace contrast
+{
+ bool is_me(const char* desc_title);
+}
+namespace gamma
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace ultrasonic
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace flip
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace rotate_bg
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace fill_black_border
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace edge_ident
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace threshold
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace bkg_filling_method
+{
+ bool is_me(const char* desc_title);
+ bool is_convex(const char* opval);
+}
+
+namespace fill_hole
+{
+ bool is_me(const char* desc_title);
+ bool is_ratio(const char* desc_title);
+}
+
+namespace noise
+{
+ bool is_me(const char* desc_title);
+ bool is_threshold(const char* desc_title);
+}
+
+namespace rid_red
+{
+ bool is_me(const char* desc_title, bool hsv/*是否为答题卡除红*/);
+}
+
+namespace sharpen
+{
+ bool is_me(const char* desc_title);
+ int to_type(const char* opval);
+ std::string from_type(int type);
+}
+
+namespace screw
+{
+ bool is_me(const char* desc_title);
+ bool is_level(const char* desc_title);
+}
+
+namespace staple
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace dogear
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace sample
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace split
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace fade_bkg
+{
+ bool is_me(const char* desc_title);
+ bool is_value(const char* desc_title);
+}
+
+namespace size_detect
+{
+ bool is_me(const char* desc_title);
+}
+
+namespace multi_out
+{
+ bool is_me(const char* desc_title);
+ int to_twain_type(const char* opval);
+ std::string from_twain_type(int type);
+}
diff --git a/huagaotwain/targetver.h b/huagaotwain/targetver.h
new file mode 100644
index 0000000..79934a3
--- /dev/null
+++ b/huagaotwain/targetver.h
@@ -0,0 +1,8 @@
+#pragma once
+
+// 包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。
+
+//如果要为以前的 Windows 平台生成应用程序,请包括 WinSDKVer.h,并
+// 将 _WIN32_WINNT 宏设置为要支持的平台,然后再包括 SDKDDKVer.h。
+
+#include
diff --git a/huagaotwain/twain.def b/huagaotwain/twain.def
new file mode 100644
index 0000000..f7da6af
--- /dev/null
+++ b/huagaotwain/twain.def
@@ -0,0 +1,3 @@
+LIBRARY huagaotwain
+EXPORTS
+ DS_Entry
\ No newline at end of file
diff --git a/huagaotwain/twain/huagaods.cpp b/huagaotwain/twain/huagaods.cpp
new file mode 100644
index 0000000..b5339f1
--- /dev/null
+++ b/huagaotwain/twain/huagaods.cpp
@@ -0,0 +1,2026 @@
+#ifdef max
+#undef max
+#endif
+#ifdef min
+#undef min
+#endif
+
+#include
+#include "huagaods.hpp"
+//#include "twpp/twglue.hpp"
+#include
+#include