// 2003.09.08, 2003.12.28, 2004.10.15, 2004.12.05, 2004.12.10, 2005.01.20, 2005.02.23, 2005.07.15, 2005.08.17
//****************************************
//**  Copyright  (C)  W.ch  1999-2005   **
//**  Web:  http://www.winchiphead.com  **
//****************************************
//**  DLL for USB interface chip CH375  **
//**  C, VC5.0                          **
//****************************************
//
// USB总线接口芯片CH375的应用层接口库  V2.2
// 南京沁恒电子有限公司  作者: W.ch 2005.08
// CH375-DLL  V2.2 , Support: Ctrl/Bulk/Int
// 运行环境: Windows 98/ME, Windows 2000/XP
// support USB chip: CH372/CH375
//

#include	<windows.h>
#include	<stdlib.h>
#include	<stdio.h>
#include	<conio.h>
#include	<setupapi.h>
#include	<winioctl.h>
#include	<dbt.h>

#include	"CH375DLL.H"

#define		DEVICE_INDEX	( iIndex >= mCH375_MAX_NUMBER ? (HANDLE)iIndex : dllHandle[ iIndex ] )

BOOL	WINAPI	DLLMain(  // 动态链接库的入口
	HINSTANCE	iInstance,
	ULONG		iReason,
	LPVOID		iReserved );


ULONG	WINAPI	dllInterruptThread(  // 中断模拟线程
	LPVOID		iParameter );

PUCHAR	WINAPI	CmpSubStrInStr(  // 在字符串中搜索比较子字符串
	PUCHAR		iSourceString,
	PUCHAR		iSubString );

LRESULT	CALLBACK	dllNotifyWndProc(  // 窗口消息处理程序
	HWND		hWnd,
	UINT		message,
	WPARAM		wParam,
	LPARAM		lParam );


#define		mTHIS_VERSION			0x22				// 当前版本


static const GUID CH375Guid = { 0x5e7f6bdf, 0x1ce5, 0x4d78, { 0xbb, 0xcf, 0xd2, 0xc, 0x44, 0x32, 0x9f, 0x7d } };	// GUID


HANDLE				dllHandle[ mCH375_MAX_NUMBER ] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
													INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
													INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
													INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };	// CH375设备句柄
mPCH375_INT_ROUTINE	dllIntRoutine[ mCH375_MAX_NUMBER ] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
														NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };	// CH375中断服务回调程序
ULONG				dllIntThreadId[ mCH375_MAX_NUMBER ] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };	// CH375中断模拟线程的标识符
UCHAR				dllDeviceName[ mCH375_MAX_NUMBER ][ MAX_DEVICE_PATH_SIZE ];	// CH375设备名字符串
UCHAR				dllI2cSpeed[ mCH375_MAX_NUMBER ] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };	// CH375的I2C接口的速度设置

HINSTANCE			dllModuleInstance[ mCH375_MAX_NUMBER ] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
													INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
													INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
													INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };	// 程序模块的句柄/实例
HWND				dllWindowsHandle[ mCH375_MAX_NUMBER ] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
													INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
													INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
													INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };	// 设备事件监控窗体的句柄
HDEVNOTIFY			dllNotifyHandle[ mCH375_MAX_NUMBER ] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
													INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
													INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
													INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };	// 设备事件通知句柄
mPCH375_NOTIFY_ROUTINE	dllNotifyRoutine[ mCH375_MAX_NUMBER ] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
																NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };	// CH375设备事件回调程序
UCHAR				dllNotifyID[ mCH375_MAX_NUMBER ][ MAX_DEVICE_ID_SIZE ];	// 被监控的CH375设备的ID字符串


BOOL	WINAPI	DLLMain(  // 动态链接库的入口
	HINSTANCE	iInstance,
	ULONG		iReason,
	LPVOID		iReserved )
{
	return( TRUE );
}

ULONG	WINAPI	dllInterruptThread(  // 中断模拟线程,每个需要中断功能的CH375设备都具有一个线程
	LPVOID		iParameter )
{
	HANDLE				dllHandleOverlap;	// CH375设备句柄,可以执行重叠操作
	HANDLE				mEvent;
	BOOL				mResult;
	ULONG				mIndex, mLength;
	mWIN32_COMMAND		mCommand;
	OVERLAPPED			mOverlapped;
	mPCH375_INT_ROUTINE	mIntRoutine;
	mIndex = (ULONG)iParameter;  // CH375设备序号
	dllHandleOverlap = CreateFile( dllDeviceName[ mIndex ], GENERIC_READ | GENERIC_WRITE,  // 打开设备,支持重叠操作
									FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL );
	if ( dllHandleOverlap != INVALID_HANDLE_VALUE ) {  // 打开设备成功
		mEvent = CreateEvent( NULL, TRUE, FALSE, NULL );  // 初始化完成事件
		while ( dllIntRoutine[ mIndex ] ) {  // 等待中断直到取消
			mCommand.mFunction = mPipeInterUp;  // 从中断数据上传管道读取数据
			mCommand.mLength = mCH375_PKT_LEN_SHORT;  // 数据长度
			mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,数据长度加上命令结构头的长度
			mOverlapped.Internal = 0;
			mOverlapped.InternalHigh = 0;
			mOverlapped.Offset = 0;
			mOverlapped.OffsetHigh = 0;
			mOverlapped.hEvent = mEvent;
			ResetEvent( mEvent );  // 初始化完成事件
			mResult = DeviceIoControl( dllHandleOverlap, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, &mOverlapped );
			if ( mResult == FALSE && GetLastError() == ERROR_IO_PENDING )  // 操作未完成,即暂时无中断
				mResult = GetOverlappedResult( dllHandleOverlap, &mOverlapped, &mLength, TRUE );  // 等待中断
			if ( mResult ) {  // 发生中断事件则调用中断服务回调程序
				if ( mIntRoutine = dllIntRoutine[ mIndex ] ) mIntRoutine( & mCommand.mBuffer[0] );  // 发生中断事件则调用中断服务回调程序,将当前的中断特征数据提供给中断服务程序
			}
			if ( mResult == FALSE && dllIntRoutine[ mIndex ] ) Sleep( 5 );  // 出错则延时5毫秒
		}
		CloseHandle( mEvent );
		CloseHandle( dllHandleOverlap );  // 关闭句柄
	}
	dllIntThreadId[ mIndex ] = 0;
	ExitThread( 0 );  // 退出
	return( 0 );
}

HANDLE	WINAPI	CH375OpenDevice(  // 打开CH375设备,返回句柄,出错则无效
	ULONG			iIndex )  // 指定CH375设备序号,0对应第一个设备,-1则自动搜索一个可以被打开的设备并返回序号
{
	ULONG								mIndex;
	HANDLE								mHandle;
	UCHAR								mBuffer[1024];
	LPGUID								mClassGuid;
	HDEVINFO							mDevInfo;
	SP_INTERFACE_DEVICE_DATA			mDevIfData;
	PSP_INTERFACE_DEVICE_DETAIL_DATA	mDevIfDetail;
	if ( iIndex >= mCH375_MAX_NUMBER && iIndex != 0xFFFFFFFF ) return( INVALID_HANDLE_VALUE );  // 太多设备,不支持
	mIndex = iIndex;
	if ( iIndex == 0xFFFFFFFF ) mIndex ++;
OpenDeviceNext:
	mClassGuid = ( LPGUID )&CH375Guid;  // 设备类
	mDevInfo = SetupDiGetClassDevs( mClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );  // 获得指定设备类
	if ( mDevInfo == INVALID_HANDLE_VALUE ) return( INVALID_HANDLE_VALUE );
	mDevIfData.cbSize = sizeof( mDevIfData );
	if ( !SetupDiEnumDeviceInterfaces( mDevInfo, NULL, mClassGuid, mIndex, &mDevIfData ) ) {  // 枚举指定序号的设备
		SetupDiDestroyDeviceInfoList( mDevInfo );
		return( INVALID_HANDLE_VALUE );
	}
	mDevIfDetail = ( PSP_INTERFACE_DEVICE_DETAIL_DATA ) mBuffer;
	mDevIfDetail -> cbSize = sizeof( SP_INTERFACE_DEVICE_DETAIL_DATA );
	if ( !SetupDiGetDeviceInterfaceDetail( mDevInfo, &mDevIfData, mDevIfDetail, 1020, NULL, NULL ) ) {  // 获得设备详细信息
		SetupDiDestroyDeviceInfoList( mDevInfo );
		return( INVALID_HANDLE_VALUE );
	}
	mHandle = CreateFile( mDevIfDetail -> DevicePath, GENERIC_READ | GENERIC_WRITE,  // 打开设备,不支持重叠操作
							FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );  // 共享
	if ( mHandle != INVALID_HANDLE_VALUE ) lstrcpy( dllDeviceName[ mIndex ], mDevIfDetail -> DevicePath );  // 设备名称
	SetupDiDestroyDeviceInfoList( mDevInfo );
	if ( mHandle == INVALID_HANDLE_VALUE && iIndex == 0xFFFFFFFF ) {
		if ( ++ mIndex < mCH375_MAX_NUMBER ) goto OpenDeviceNext;
	}
	else dllHandle[ mIndex ] = mHandle;
	if ( mHandle != INVALID_HANDLE_VALUE && iIndex == 0xFFFFFFFF ) return( (HANDLE)mIndex );
	return( mHandle );
}

VOID	WINAPI	CH375CloseDevice(  // 关闭CH375设备
	ULONG			iIndex )  // 指定CH375设备序号
{
	dllIntRoutine[ iIndex ] = NULL;  // 取消中断服务程序
	if ( dllIntThreadId[ iIndex ] ) CH375SetIntRoutine( iIndex, NULL );  // 等待线程结束
//	CH375AbortRead( iIndex );  // 放弃数据块读操作
//	CH375AbortWrite( iIndex );  // 放弃数据块写操作
	CloseHandle( dllHandle[ iIndex ] );  // 关闭句柄
	dllHandle[ iIndex ] = INVALID_HANDLE_VALUE;
}

ULONG	WINAPI	CH375GetVersion( )  // 获得DLL版本号,返回版本号
{
	return( mTHIS_VERSION );
}

ULONG	WINAPI	CH375DriverCommand(  // 直接传递命令给驱动程序,出错则返回0,否则返回数据长度
	ULONG			iIndex,  // 指定CH375设备序号,V1.6以上DLL也可以是设备打开后的句柄
	mPWIN32_COMMAND	ioCommand )  // 命令结构的指针
// 该程序在调用后返回数据长度,并且仍然返回命令结构,如果是读操作,则数据返回在命令结构中,
// 返回的数据长度在操作失败时为0,操作成功时为整个命令结构的长度,例如读一个字节,则返回mWIN32_COMMAND_HEAD+1,
// 命令结构在调用前,分别提供:管道号或者命令功能代码,存取数据的长度(可选),数据(可选)
// 命令结构在调用后,分别返回:操作状态代码,后续数据的长度(可选),
//   操作状态代码是由WINDOWS定义的代码,可以参考NTSTATUS.H,
//   后续数据的长度是指读操作返回的数据长度,数据存放在随后的缓冲区中,对于写操作一般为0
{
	HANDLE	mHandle;
	ULONG	mLength;
	mHandle = DEVICE_INDEX;
	if ( mHandle == NULL || mHandle == INVALID_HANDLE_VALUE ) return( 0 );
	mLength = ioCommand -> mLength + mWIN32_COMMAND_HEAD;  // 数据缓冲区长度加上命令结构头的长度
	if ( DeviceIoControl( mHandle, IOCTL_CH375_COMMAND, ioCommand, mLength, ioCommand, mLength, &mLength, NULL ) )
		return( mLength );  // 返回数据长度
	else return( 0 );  // 出错
}

ULONG	WINAPI	CH375GetDrvVersion( )  // 获得驱动程序版本号,返回版本号,出错则返回0
{
	ULONG			mIndex;
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mLength = sizeof( mWIN32_COMMAND );
	for ( mIndex = 0; mIndex < mCH375_MAX_NUMBER; ++mIndex ) if ( dllHandle[ mIndex ] != INVALID_HANDLE_VALUE ) break;  // 搜索一个有效句柄
	if ( mIndex < mCH375_MAX_NUMBER ) {  // 设备句柄有效
		mCommand.mFunction = mFuncGetVersion;
		mCommand.mLength = 0;
		if ( DeviceIoControl( dllHandle[ mIndex ], IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
			return( mCommand.mBuffer[0] );  // 正常返回驱动程序的版本号
		else return( 0 );  // 出错
	}
	else return( 0 );  // 没有有效的设备句柄,出错
}

BOOL	WINAPI	CH375ResetDevice(  // 复位USB设备
	ULONG			iIndex )  // 指定CH375设备序号
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncResetDevice;  // 复位设备
	mCommand.mLength = 0;
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( TRUE );
	else return( FALSE );  // 出错
}

BOOL	WINAPI	CH375GetDeviceDescr(  // 读取设备描述符
	ULONG			iIndex,  // 指定CH375设备序号
	PVOID			oBuffer,  // 指向一个足够大的缓冲区,用于保存描述符
	PULONG			ioLength )  // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度
{
	UCHAR			mBuffer[ mDEFAULT_COMMAND_LEN ];
	ULONG			mLength;
	mPWIN32_COMMAND	mpCommand;
	if ( *ioLength > mDEFAULT_BUFFER_LEN ) *ioLength = mDEFAULT_BUFFER_LEN;  // 限制长度
	mpCommand = (PVOID)mBuffer;
	mpCommand -> mFunction = mPipeDeviceCtrl;  // 通过综合控制管道获取USB设备描述符
	mpCommand -> mLength = sizeof( mUSB_SETUP_PKT );  // 数据长度,指写入综合控制管道的命令请求包的长度
	mpCommand -> mSetupPkt.mUspReqType = 0x80;  // 请求类型
	mpCommand -> mSetupPkt.mUspRequest = mUSB_GET_DESCR;  // 请求码 USB_REQUEST_GET_DESCRIPTOR
	mpCommand -> mSetupPkt.mUspValueLow = 0x00;  // 描述符索引
	mpCommand -> mSetupPkt.mUspValueHigh = 0x01;  // 设备描述符 USB_DEVICE_DESCRIPTOR_TYPE
	mpCommand -> mSetupPkt.mUspIndex = 0x0000;  // 索引参数
	mpCommand -> mSetupPkt.mLength = (USHORT)*ioLength;  // 需要读取的数据长度
	mLength = *ioLength + mWIN32_COMMAND_HEAD;  // 缓冲区长度,数据长度加上命令结构头的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, mpCommand, sizeof( mWIN32_COMMAND ), mpCommand, mLength, &mLength, NULL ) ) {
		*ioLength = mpCommand -> mLength;  //  返回实际读取数据的长度
		CopyMemory( oBuffer, & mpCommand -> mBuffer[0], *ioLength );  // 返回读取的数据
		return( TRUE );
	}
	else {  // 出错
		*ioLength = 0;
		return( FALSE );
	}
}

BOOL	WINAPI	CH375GetConfigDescr(  // 读取配置描述符
	ULONG			iIndex,  // 指定CH375设备序号
	PVOID			oBuffer,  // 指向一个足够大的缓冲区,用于保存描述符
	PULONG			ioLength )  // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度
{
	UCHAR			mBuffer[ mDEFAULT_COMMAND_LEN ];
	ULONG			mLength;
	mPWIN32_COMMAND	mpCommand;
	if ( *ioLength > mDEFAULT_BUFFER_LEN ) *ioLength = mDEFAULT_BUFFER_LEN;  // 限制长度
	mpCommand = (PVOID)mBuffer;
	mpCommand -> mFunction = mFuncGetConfig;  // 获取USB配置描述符
	mpCommand -> mLength = *ioLength;  // 数据长度
	mLength = *ioLength + mWIN32_COMMAND_HEAD;  // 缓冲区长度,数据长度加上命令结构头的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, mpCommand, mWIN32_COMMAND_HEAD, mpCommand, mLength, &mLength, NULL ) ) {
		*ioLength = mpCommand -> mLength;  //  返回实际读取数据的长度
		CopyMemory( oBuffer, & mpCommand -> mBuffer[0], *ioLength );  // 返回读取的数据
		return( TRUE );
	}
	else {  // 出错
		*ioLength = 0;
		return( FALSE );
	}
}

BOOL	WINAPI	CH375ReadInter(  // 读取中断数据
	ULONG			iIndex,  // 指定CH375设备序号
	PVOID			oBuffer,  // 指向一个足够大的缓冲区,用于保存读取的中断数据
	PULONG			ioLength )  // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度
{
	UCHAR			mBuffer[ mDEFAULT_COMMAND_LEN ];
	ULONG			mLength;
	mPWIN32_COMMAND	mpCommand;
	if ( *ioLength > mDEFAULT_BUFFER_LEN ) *ioLength = mDEFAULT_BUFFER_LEN;  // 限制长度
	mpCommand = (PVOID)mBuffer;
	mpCommand -> mFunction = mPipeInterUp;  // 从中断数据上传管道读取数据
	mpCommand -> mLength = max( *ioLength, mCH375_PKT_LEN_SHORT );  // 数据长度,不能小于CH375的数据包长度
	mLength = mpCommand -> mLength + mWIN32_COMMAND_HEAD;  // 缓冲区长度,数据长度加上命令结构头的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, mpCommand, mWIN32_COMMAND_HEAD, mpCommand, mLength, &mLength, NULL ) ) {
		*ioLength = min( mpCommand -> mLength, *ioLength );  //  返回实际读取数据的长度
		CopyMemory( oBuffer, & mpCommand -> mBuffer[0], *ioLength );  // 返回读取的数据
		return( TRUE );
	}
	else {  // 出错
		*ioLength = 0;
		return( FALSE );
	}
}

BOOL	WINAPI	CH375AbortInter(  // 放弃中断数据读操作
	ULONG			iIndex )  // 指定CH375设备序号
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncAbortPipe;  // 取消指定的USB管道的数据操作
	mCommand.mLength = sizeof( ULONG );
	*(PULONG) & mCommand.mBuffer[0] = mPipeInterUp;  // 中断数据上传管道
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( TRUE );
	else return( FALSE );  // 出错
}

BOOL	WINAPI	CH375SetIntRoutine(  // 设定中断服务程序
	ULONG			iIndex,  // 指定CH375设备序号
	mPCH375_INT_ROUTINE	iIntRoutine )  // 指定中断服务回调程序,为NULL则取消中断服务,否则在中断时调用该程序
{
	HANDLE			mHandle;
	dllIntRoutine[ iIndex ] = NULL;
	while ( dllIntThreadId[ iIndex ] ) {  /* 已经创建线程 */
		CH375AbortInter( iIndex );  // 放弃在此之前的等待中断,等待线程结束
		Sleep( 20 );  // 延时20毫秒
	}
	if ( iIntRoutine == NULL ) return( TRUE );  // 取消中断服务
	dllIntRoutine[ iIndex ] = iIntRoutine;
	mHandle = CreateThread( NULL, 0, dllInterruptThread, (PVOID)iIndex, 0, & dllIntThreadId[ iIndex ] );  // 创建线程模拟中断事件
	if ( mHandle ) {  // 线程创建成功
		SetThreadPriority( mHandle, THREAD_PRIORITY_TIME_CRITICAL ); // 为了缩短中断服务回调程序响应时间,应该提高线程的优先级
		return ( TRUE );
	}
	else return ( FALSE );  // 操作成功
}

BOOL	WINAPI	CH375ReadData(  // 读取数据块
	ULONG			iIndex,  // 指定CH375设备序号
	PVOID			oBuffer,  // 指向一个足够大的缓冲区,用于保存读取的数据
	PULONG			ioLength )  // 指向长度单元,输入时为准备读取的长度,返回后为实际读取的长度
{
	UCHAR			mBuffer[ mDEFAULT_COMMAND_LEN ];
	ULONG			mLength;
	mPWIN32_COMMAND	mpCommand;
	if ( *ioLength <= mDEFAULT_BUFFER_LEN ) mpCommand = (PVOID)mBuffer;  // 不超过默认缓冲区长度
	else {  // 超过则需要另外分配内存
		if ( *ioLength > mMAX_BUFFER_LENGTH ) *ioLength = mMAX_BUFFER_LENGTH;  // 限制最大长度
		mpCommand = (mPWIN32_COMMAND)LocalAlloc( LMEM_FIXED, mMAX_COMMAND_LENGTH );  // 分配内存
		if ( mpCommand == NULL ) {  // 分配内存失败则仍然使用默认缓冲区
			mpCommand = (PVOID)mBuffer;
			*ioLength = mDEFAULT_BUFFER_LEN;  // 限制长度
		}
	}
	mpCommand -> mFunction = mPipeDataUp;  // 从数据块上传管道读取数据
	mpCommand -> mLength = max( *ioLength, mCH375_PACKET_LENGTH );  // 数据长度,不能小于CH375的数据包长度
	mLength = mpCommand -> mLength + mWIN32_COMMAND_HEAD;  // 缓冲区长度,数据长度加上命令结构头的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, mpCommand, mWIN32_COMMAND_HEAD, mpCommand, mLength, &mLength, NULL ) ) {
		*ioLength = min( mpCommand -> mLength, *ioLength );  //  返回实际读取数据的长度
		CopyMemory( oBuffer, & mpCommand -> mBuffer[0], *ioLength );  // 返回读取的数据
		if ( (PUCHAR)mpCommand != mBuffer ) LocalFree( mpCommand );  // 如果是分配的内存则释放
		return( TRUE );
	}
	else {  // 出错
		*ioLength = 0;
		if ( (PUCHAR)mpCommand != mBuffer ) LocalFree( mpCommand );  // 如果是分配的内存则释放
		return( FALSE );
	}
}

BOOL	WINAPI	CH375AbortRead(  // 放弃数据块读操作
	ULONG			iIndex )  // 指定CH375设备序号
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncAbortPipe;  // 取消指定的USB管道的数据操作
	mCommand.mLength = sizeof( ULONG );
	*(PULONG) & mCommand.mBuffer[0] = mPipeDataUp;  // 数据块上传管道
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( TRUE );
	else return( FALSE );  // 出错
}

BOOL	WINAPI	CH375WriteData(  // 写出数据块
	ULONG			iIndex,  // 指定CH375设备序号
	PVOID			iBuffer,  // 指向一个缓冲区,放置准备写出的数据
	PULONG			ioLength )  // 指向长度单元,输入时为准备写出的长度,返回后为实际写出的长度
{
	UCHAR			mBuffer[ mDEFAULT_COMMAND_LEN ];
	ULONG			mLength;
	mPWIN32_COMMAND	mpCommand;
	if ( *ioLength <= mDEFAULT_BUFFER_LEN ) mpCommand = (PVOID)mBuffer;  // 不超过默认缓冲区长度
	else {  // 超过则需要另外分配内存
		if ( *ioLength > mMAX_BUFFER_LENGTH ) *ioLength = mMAX_BUFFER_LENGTH;  // 限制最大长度
		mpCommand = (mPWIN32_COMMAND)LocalAlloc( LMEM_FIXED, mMAX_COMMAND_LENGTH );  // 分配内存
		if ( mpCommand == NULL ) {  // 分配内存失败则仍然使用默认缓冲区
			mpCommand = (PVOID)mBuffer;
			*ioLength = mDEFAULT_BUFFER_LEN;  // 限制长度
		}
	}
	mpCommand -> mFunction = mPipeDataDown;  // 向数据块下传管道写出数据
	mpCommand -> mLength = *ioLength;  // 数据长度
	CopyMemory( & mpCommand -> mBuffer[0], iBuffer, *ioLength );  // 准备写出的数据
	mLength = *ioLength + mWIN32_COMMAND_HEAD;  // 缓冲区长度,数据长度加上命令结构头的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, mpCommand, mLength, mpCommand, mWIN32_COMMAND_HEAD, &mLength, NULL ) ) {
		*ioLength = mpCommand -> mLength;  //  返回实际写出数据的长度
		if ( (PUCHAR)mpCommand != mBuffer ) LocalFree( mpCommand );  // 如果是分配的内存则释放
		return( TRUE );
	}
	else {  // 出错
		*ioLength = 0;
		if ( (PUCHAR)mpCommand != mBuffer ) LocalFree( mpCommand );  // 如果是分配的内存则释放
		return( FALSE );
	}
}

BOOL	WINAPI	CH375AbortWrite(  // 放弃数据块写操作
	ULONG			iIndex )  // 指定CH375设备序号
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncAbortPipe;  // 取消指定的USB管道的数据操作
	mCommand.mLength = sizeof( ULONG );
	*(PULONG) & mCommand.mBuffer[0] = mPipeDataDown;  // 数据块下传管道
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( TRUE );
	else return( FALSE );  // 出错
}

BOOL	WINAPI	CH375WriteRead(  // 先写出标准的数据块(命令),再读取标准的数据块(应答)
	ULONG			iIndex,  // 指定CH375设备序号
	PVOID			iBuffer,  // 指向一个缓冲区,放置准备写出的数据,长度不大于mCH375_PACKET_LENGTH
	PVOID			oBuffer,  // 指向一个足够大的缓冲区,长度不小于mCH375_PACKET_LENGTH,用于保存读取的数据
	PULONG			ioLength )  // 指向长度单元,不大于mCH375_PACKET_LENGTH,输入时为准备写出的长度,返回后为实际读取的长度
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	if ( *ioLength > mCH375_PACKET_LENGTH ) *ioLength = mCH375_PACKET_LENGTH;  // 限制长度
	mCommand.mFunction = mPipeDataDown;  // 向数据块下传管道写出数据
	mCommand.mLength = *ioLength;  // 数据长度
	CopyMemory( & mCommand.mBuffer[0], iBuffer, *ioLength );  // 准备写出的命令数据
	mLength = *ioLength + mWIN32_COMMAND_HEAD;  // 缓冲区长度,数据长度加上命令结构头的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) ) {
		mCommand.mFunction = mPipeDataUp;  // 从数据块上传管道读取数据
		mCommand.mLength = mCH375_PACKET_LENGTH;  // 数据长度,CH375的数据包长度
		mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
		if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) ) {
			*ioLength = min( mCommand.mLength, mCH375_PACKET_LENGTH );  //  返回实际读取数据的长度
			CopyMemory( oBuffer, & mCommand.mBuffer[0], *ioLength );  // 返回读取的应答数据
			return( TRUE );
		}
	}
	*ioLength = 0;
	return( FALSE );  // 出错
}

BOOL	WINAPI	CH375GetStatus(  // 通过CH375直接输入数据和状态
	ULONG			iIndex,  // 指定CH375设备序号
	PULONG			iStatus )  // 指向一个双字单元,用于保存状态数据
// 位7-位0对应CH375的D7-D0引脚,位9对应CH375的RXD#引脚,位10对应CH375的REQ#引脚
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mPipeDeviceCtrl;  // 综合控制管道
	mCommand.mLength = sizeof( mUSB_SETUP_PKT );  // 数据长度,指写入综合控制管道的命令请求包的长度
	mCommand.mSetupPkt.mUspReqType = mCH375_VENDOR_READ;  // 请求类型,供应商读请求
	mCommand.mSetupPkt.mUspRequest = mCH375_GET_STATUS;  // 请求码,输入状态信号
	mCommand.mSetupPkt.mUspValue = 0x0000;  // 保留
	mCommand.mSetupPkt.mUspIndex = 0x0000;  // 保留
	mCommand.mSetupPkt.mLength = mCH375_PKT_LEN_SHORT;  // 需要返回一个标准的数据包
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) ) {
		*iStatus = mCommand.mBuffer[0] | ( mCommand.mBuffer[1] & ( mBitInputRxd | mBitInputReq ) ) << 8;
		return( TRUE );
	}
	else return( FALSE );  // 出错
}

BOOL	WINAPI	CH375SetTimeout(  // 设置USB数据读写的超时
	ULONG			iIndex,  // 指定CH375设备序号
	ULONG			iWriteTimeout,  // 指定USB写出数据块的超时时间,以毫秒mS为单位,0xFFFFFFFF指定不超时(默认值)
	ULONG			iReadTimeout )  // 指定USB读取数据块的超时时间,以毫秒mS为单位,0xFFFFFFFF指定不超时(默认值)
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncSetTimeout;  // 设置USB通讯超时
	mCommand.mLength = sizeof( ULONG ) * 2;
	*(PULONG) & mCommand.mBuffer[0] = iWriteTimeout;  // 下传超时
	*(PULONG) & mCommand.mBuffer[4] = iReadTimeout;  // 上传超时
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( TRUE );
	else return( FALSE );  // 出错
}

BOOL	WINAPI	CH375WriteAuxData(  // 写出辅助数据
	ULONG			iIndex,  // 指定CH375设备序号
	PVOID			iBuffer,  // 指向一个缓冲区,放置准备写出的数据
	PULONG			ioLength )  // 指向长度单元,输入时为准备写出的长度,返回后为实际写出的长度
{
	UCHAR			mBuffer[ mDEFAULT_COMMAND_LEN ];
	ULONG			mLength;
	mPWIN32_COMMAND	mpCommand;
	if ( *ioLength <= mDEFAULT_BUFFER_LEN ) mpCommand = (PVOID)mBuffer;  // 不超过默认缓冲区长度
	else {  // 超过则需要另外分配内存
		if ( *ioLength > mMAX_BUFFER_LENGTH ) *ioLength = mMAX_BUFFER_LENGTH;  // 限制最大长度
		mpCommand = (mPWIN32_COMMAND)LocalAlloc( LMEM_FIXED, mMAX_COMMAND_LENGTH );  // 分配内存
		if ( mpCommand == NULL ) {  // 分配内存失败则仍然使用默认缓冲区
			mpCommand = (PVOID)mBuffer;
			*ioLength = mDEFAULT_BUFFER_LEN;  // 限制长度
		}
	}
	mpCommand -> mFunction = mPipeAuxDown;  // 向辅助数据下传管道写出数据
	mpCommand -> mLength = *ioLength;  // 数据长度
	CopyMemory( & mpCommand -> mBuffer[0], iBuffer, *ioLength );  // 准备写出的数据
	mLength = *ioLength + mWIN32_COMMAND_HEAD;  // 缓冲区长度,数据长度加上命令结构头的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, mpCommand, mLength, mpCommand, mWIN32_COMMAND_HEAD, &mLength, NULL ) ) {
		*ioLength = mpCommand -> mLength;  //  返回实际写出数据的长度
		if ( (PUCHAR)mpCommand != mBuffer ) LocalFree( mpCommand );  // 如果是分配的内存则释放
		return( TRUE );
	}
	else {  // 出错
		*ioLength = 0;
		if ( (PUCHAR)mpCommand != mBuffer ) LocalFree( mpCommand );  // 如果是分配的内存则释放
		return( FALSE );
	}
}

BOOL	WINAPI	CH375SetExclusive(  // 设置独占使用当前CH375设备
	ULONG			iIndex,  // 指定CH375设备序号
	ULONG			iExclusive )  // 为0则设备可以共享使用,非0则独占使用
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncSetExclusive;  // 设置独占使用
	mCommand.mLength = sizeof( UCHAR );
	mCommand.mBuffer[0] = iExclusive ? 1 : 0;  // 独占使用或者共享使用
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( TRUE );
	else return( FALSE );  // 出错
}

ULONG	WINAPI	CH375GetUsbID(  // 获取USB设备ID,返回数据中,低16位为厂商ID,高16位为产品ID,错误时返回全0(无效ID)
	ULONG			iIndex )  // 指定CH375设备序号
{
	UCHAR	mBuffer[32];
	ULONG	mLength = 0;
	if ( DEVICE_INDEX == INVALID_HANDLE_VALUE ) {  // USB设备尚未打开
		if ( CH375OpenDevice( iIndex ) != INVALID_HANDLE_VALUE ) {  // 打开CH375设备,返回句柄,出错则无效
			mLength = 18;
			if ( CH375GetDeviceDescr( iIndex, mBuffer, &mLength ) == FALSE ) mLength = 0;  // 读取设备描述符
			CH375CloseDevice( iIndex );
		}
	}
	else {  // USB设备已经打开
		mLength = 18;
		if ( CH375GetDeviceDescr( iIndex, mBuffer, &mLength ) == FALSE ) mLength = 0;  // 读取设备描述符
	}
	if ( mLength != 18 ) return( 0 );
	else return( *(PULONG)( &mBuffer[8] ) );
}

PVOID	WINAPI	CH375GetDeviceName(  // 返回指向CH375设备名称的缓冲区,出错则返回NULL
	ULONG			iIndex )  // 指定CH375设备序号,0对应第一个设备
{
	UCHAR								mBuffer[1024];
	LPGUID								mClassGuid;
	HDEVINFO							mDevInfo;
	SP_INTERFACE_DEVICE_DATA			mDevIfData;
	PSP_INTERFACE_DEVICE_DETAIL_DATA	mDevIfDetail;
	if ( iIndex >= mCH375_MAX_NUMBER ) return( NULL );  // 太多设备,不支持
	mClassGuid = ( LPGUID )&CH375Guid;  // 设备类
	mDevInfo = SetupDiGetClassDevs( mClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE );  // 获得指定设备类
	if ( mDevInfo == INVALID_HANDLE_VALUE ) return( NULL );
	mDevIfData.cbSize = sizeof( mDevIfData );
	if ( !SetupDiEnumDeviceInterfaces( mDevInfo, NULL, mClassGuid, iIndex, &mDevIfData ) ) {  // 枚举指定序号的设备
		SetupDiDestroyDeviceInfoList( mDevInfo );
		return( NULL );
	}
	mDevIfDetail = ( PSP_INTERFACE_DEVICE_DETAIL_DATA ) mBuffer;
	mDevIfDetail -> cbSize = sizeof( SP_INTERFACE_DEVICE_DETAIL_DATA );
	if ( !SetupDiGetDeviceInterfaceDetail( mDevInfo, &mDevIfData, mDevIfDetail, 1020, NULL, NULL ) ) {  // 获得设备详细信息
		SetupDiDestroyDeviceInfoList( mDevInfo );
		return( NULL );
	}
	lstrcpy( dllDeviceName[ iIndex ], mDevIfDetail -> DevicePath );  // 设备名称
	SetupDiDestroyDeviceInfoList( mDevInfo );
	return( dllDeviceName[ iIndex ] );
}

BOOL	WINAPI	CH375SetBufUpload(  // 设定内部缓冲上传模式
	ULONG			iIndex,  // 指定CH375设备序号,0对应第一个设备
	ULONG			iEnableOrClear )  // 为0则禁止内部缓冲上传模式,使用直接上传,非0则启用内部缓冲上传模式并清除缓冲区中的已有数据
// 如果启用内部缓冲上传模式,那么CH375驱动程序创建线程自动接收USB上传数据到内部缓冲区,同时清除缓冲区中的已有数据,当应用程序调用CH375ReadData后将立即返回缓冲区中的已有数据
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncBufferMode;  // 设定缓冲上传模式及查询缓冲区中的数据长度
	mCommand.mLength = sizeof( UCHAR );
	mCommand.mBuffer[0] = iEnableOrClear ? 2 : 0;  // 方式
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( TRUE );
	else return( FALSE );  // 出错
}

LONG	WINAPI	CH375QueryBufUpload(  // 查询内部上传缓冲区中的已有数据包个数,成功返回数据包个数,出错返回-1
	ULONG			iIndex )  // 指定CH375设备序号,0对应第一个设备
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncBufferMode;  // 设定缓冲上传模式及查询缓冲区中的数据长度
	mCommand.mLength = sizeof( UCHAR );
	mCommand.mBuffer[0] = 1;  // 方式
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( *(PLONG) & mCommand.mBuffer[0] );
	else return( -1 );  // 出错
}

BOOL	WINAPI	CH375SetBufDownload(  // 设定内部缓冲下传模式
	ULONG			iIndex,  // 指定CH375设备序号,0对应第一个设备
	ULONG			iEnableOrClear )  // 为0则禁止内部缓冲下传模式,使用直接下传,非0则启用内部缓冲下传模式并清除缓冲区中的已有数据
// 如果启用内部缓冲下传模式,那么当应用程序调用CH375WriteData后将仅仅是将USB下传数据放到内部缓冲区并立即返回,而由CH375驱动程序创建的线程自动发送直到完毕
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncBufferModeDn;  // 设定缓冲下传模式及查询缓冲区中的剩余数据长度
	mCommand.mLength = sizeof( UCHAR );
	mCommand.mBuffer[0] = iEnableOrClear ? 2 : 0;  // 方式
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( TRUE );
	else return( FALSE );  // 出错
}

LONG	WINAPI	CH375QueryBufDownload(  // 查询内部下传缓冲区中的剩余数据包个数(尚未发送),成功返回数据包个数,出错返回-1
	ULONG			iIndex )  // 指定CH375设备序号,0对应第一个设备
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncBufferModeDn;  // 设定缓冲下传模式及查询缓冲区中的剩余数据长度
	mCommand.mLength = sizeof( UCHAR );
	mCommand.mBuffer[0] = 1;  // 方式
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( *(PLONG) & mCommand.mBuffer[0] );
	else return( -1 );  // 出错
}

BOOL	WINAPI	CH375ResetInter(  // 复位中断数据读操作
	ULONG			iIndex )  // 指定CH375设备序号
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncResetPipe;  // 复位指定的USB管道
	mCommand.mLength = sizeof( ULONG );
	*(PULONG) & mCommand.mBuffer[0] = mPipeInterUp;  // 中断数据上传管道
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( TRUE );
	else return( FALSE );  // 出错
}

BOOL	WINAPI	CH375ResetAux(  // 复位辅助数据写操作
	ULONG			iIndex )  // 指定CH375设备序号
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncResetPipe;  // 复位指定的USB管道
	mCommand.mLength = sizeof( ULONG );
	*(PULONG) & mCommand.mBuffer[0] = mPipeAuxDown;  // 辅助数据下传管道
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( TRUE );
	else return( FALSE );  // 出错
}

BOOL	WINAPI	CH375ResetRead(  // 复位数据块读操作
	ULONG			iIndex )  // 指定CH375设备序号
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncResetPipe;  // 复位指定的USB管道
	mCommand.mLength = sizeof( ULONG );
	*(PULONG) & mCommand.mBuffer[0] = mPipeDataUp;  // 数据块上传管道
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( TRUE );
	else return( FALSE );  // 出错
}

BOOL	WINAPI	CH375ResetWrite(  // 复位数据块写操作
	ULONG			iIndex )  // 指定CH375设备序号
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncResetPipe;  // 复位指定的USB管道
	mCommand.mLength = sizeof( ULONG );
	*(PULONG) & mCommand.mBuffer[0] = mPipeDataDown;  // 数据块下传管道
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( TRUE );
	else return( FALSE );  // 出错
}

PUCHAR	WINAPI	CmpSubStrInStr(  // 在字符串中搜索比较子字符串
	PUCHAR		iSourceString,
	PUCHAR		iSubString )
{
	ULONG	i, j;
	for ( i = 0; *( iSourceString + i ) != 0; i ++ ) {
		for ( j = 0; *( iSubString + j ) != 0; j ++ ) {
			if ( *( iSubString + j ) != *( iSourceString + i + j ) ) break;
		}
		if ( *( iSubString + j ) == 0 ) return( iSourceString + i );
	}
	return( NULL );
}

LRESULT	CALLBACK	dllNotifyWndProc(  // 窗口消息处理程序,每个需要设备事件通知功能的CH375设备都具有一个窗体
	HWND		hWnd,
	UINT		message,
	WPARAM		wParam,
	LPARAM		lParam )
{
	ULONG	mIndex;
	ULONG	mEventStatus;
	UCHAR	DevId[ MAX_DEVICE_PATH_SIZE ];
	PDEV_BROADCAST_DEVICEINTERFACE	DevInfo;
	mPCH375_NOTIFY_ROUTINE			mNotifyRoutine;
	if ( message != WM_DEVICECHANGE ) return( DefWindowProc( hWnd, message, wParam, lParam ) );
	DevInfo = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;  // 设备信息
	if ( wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVEPENDING || wParam == DBT_DEVICEREMOVECOMPLETE ) {  // 设备插入或移除
		if ( DevInfo -> dbcc_devicetype == DBT_DEVTYP_DEVICEINTERFACE ) {  // 指定的设备接口类下的设备事件
			strcpy( DevId, DevInfo -> dbcc_name );  // 设备的符号链接名
			CharUpperBuff( DevId, strlen( DevId ) );  // 转为大写
			for ( mIndex = 0; mIndex < mCH375_MAX_NUMBER; ++mIndex ) if ( dllWindowsHandle[ mIndex ] == hWnd ) break;  // 获取当前设备序号
			if ( mIndex >= mCH375_MAX_NUMBER ) mIndex = 0;
			if ( CmpSubStrInStr( DevId, dllNotifyID[ mIndex ] ) ) {  // 发现了指定设备ID
				if ( mNotifyRoutine = dllNotifyRoutine[ mIndex ] ) {
					switch( wParam ) {
						case DBT_DEVICEARRIVAL: mEventStatus = CH375_DEVICE_ARRIVAL; break;
						case DBT_DEVICEREMOVEPENDING: mEventStatus = CH375_DEVICE_REMOVE_PEND; break;
						case DBT_DEVICEREMOVECOMPLETE: mEventStatus = CH375_DEVICE_REMOVE; break;
						default: mEventStatus = CH375_DEVICE_REMOVE; break;
					}
					mNotifyRoutine( mEventStatus );  // 调用设备事件通知回调程序
				}
			}		
		}
	}
	return( 0 );
}

BOOL	WINAPI	CH375SetDeviceNotify(  // 设定设备事件通知程序
	ULONG					iIndex,  // 指定CH375设备序号,0对应第一个设备
	PCHAR					iDeviceID,  // 可选参数,指向字符串,指定被监控的设备的ID,字符串以\0终止
	mPCH375_NOTIFY_ROUTINE	iNotifyRoutine )  // 指定设备事件回调程序,为NULL则取消事件通知,否则在检测到事件时调用该程序
{
	static	char	mWndClassName[] = "CH375_NotifyWnd";
	WNDCLASS						mWndClass;
	DEV_BROADCAST_DEVICEINTERFACE	mNotifyFilter;
	if ( iIndex >= mCH375_MAX_NUMBER ) iIndex = mCH375_MAX_NUMBER - 1;  // 默认
	if ( iNotifyRoutine == NULL || dllNotifyRoutine[ iIndex ] ) {  // 取消设备事件通知
		dllNotifyRoutine[ iIndex ] = NULL;
		if ( dllNotifyHandle[ iIndex ] != INVALID_HANDLE_VALUE ) {  // 已经注册事件通知
			UnregisterDeviceNotification( dllNotifyHandle[ iIndex ] );  // 注销
			dllNotifyHandle[ iIndex ] = INVALID_HANDLE_VALUE;
		}
		DestroyWindow( dllWindowsHandle[ iIndex ] );  // 关闭监控窗口
		dllWindowsHandle[ iIndex ] = INVALID_HANDLE_VALUE;
		UnregisterClass( mWndClassName, dllModuleInstance[ iIndex ] );  // 注销窗口类
	}
	if ( iNotifyRoutine == NULL ) return( TRUE );
	dllModuleInstance[ iIndex ] = GetModuleHandle( NULL );  // 程序模块的句柄
	ZeroMemory( &mWndClass, sizeof( mWndClass ) );
	mWndClass.lpfnWndProc = (WNDPROC)dllNotifyWndProc;  //  窗口消息处理过程
	mWndClass.hInstance = dllModuleInstance[ iIndex ];
	mWndClass.lpszClassName = mWndClassName;  // 窗口类名
	RegisterClass( &mWndClass );  // 注册窗口类
	dllWindowsHandle[ iIndex ] = CreateWindow( mWndClassName, "CH375_NotifyWnd_0", WS_POPUP, 0, 0, 0, 0, NULL, NULL, dllModuleInstance[ iIndex ], NULL );  // 创建窗体
	if ( dllWindowsHandle[ iIndex ] ) {
		ShowWindow( dllWindowsHandle[ iIndex ], SW_HIDE );  // 隐藏窗体
		if ( iDeviceID ) strcpy( dllNotifyID[ iIndex ], iDeviceID );  // 要监控的设备ID
		else dllNotifyID[ iIndex ][0] = 0;
		CharUpperBuff( dllNotifyID[ iIndex ], strlen( dllNotifyID[ iIndex ] ) );  // 转为大写
		ZeroMemory( &mNotifyFilter, sizeof( mNotifyFilter ) );
		mNotifyFilter.dbcc_size = sizeof( mNotifyFilter );
		mNotifyFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;  // 设置监控的设备类型
		mNotifyFilter.dbcc_classguid = CH375Guid;  // 设置监控的设备接口类:USB接口
		dllNotifyHandle[ iIndex ] = RegisterDeviceNotification( dllWindowsHandle[ iIndex ], &mNotifyFilter, DEVICE_NOTIFY_WINDOW_HANDLE );  // 注册设备事件通知
		if ( dllNotifyHandle[ iIndex ] ) {
			dllNotifyRoutine[ iIndex ] = iNotifyRoutine;  // 设备事件回调程序
			return( TRUE );
		}
		DestroyWindow( dllWindowsHandle[ iIndex ] );  // 关闭监控窗口
		dllWindowsHandle[ iIndex ] = INVALID_HANDLE_VALUE;
	}
	UnregisterClass( mWndClassName, dllModuleInstance[ iIndex ] );  // 注销窗口类
	return( FALSE );
}

BOOL	WINAPI	CH375SetTimeoutEx(  // 设置USB数据读写的超时
	ULONG			iIndex,  // 指定CH375设备序号
	ULONG			iWriteTimeout,  // 指定USB写出数据块的超时时间,以毫秒mS为单位,0xFFFFFFFF指定不超时(默认值)
	ULONG			iReadTimeout,  // 指定USB读取数据块的超时时间,以毫秒mS为单位,0xFFFFFFFF指定不超时(默认值)
	ULONG			iAuxTimeout,  // 指定USB辅助下传数据的超时时间,以毫秒mS为单位,0xFFFFFFFF指定不超时(默认值)
	ULONG			iInterTimeout )  // 指定USB中断上传数据的超时时间,以毫秒mS为单位,0xFFFFFFFF指定不超时(默认值)
{
	ULONG			mLength;
	mWIN32_COMMAND	mCommand;
	mCommand.mFunction = mFuncSetTimeout;  // 设置USB通讯超时
	mCommand.mLength = sizeof( ULONG ) * 4;
	*(PULONG) & mCommand.mBuffer[0] = iWriteTimeout;  // 数据下传超时
	*(PULONG) & mCommand.mBuffer[4] = iReadTimeout;  // 数据上传超时
	*(PULONG) & mCommand.mBuffer[8] = iAuxTimeout;  // 辅助下传超时
	*(PULONG) & mCommand.mBuffer[12] = iInterTimeout;  // 中断上传超时
	mLength = sizeof( mWIN32_COMMAND );  // 缓冲区长度,默认命令结构的长度
	if ( DeviceIoControl( DEVICE_INDEX, IOCTL_CH375_COMMAND, &mCommand, mLength, &mCommand, mLength, &mLength, NULL ) )
		return( TRUE );
	else return( FALSE );  // 出错
}
