AN4053-C # code analysis PC Streamer

Disclaimer: This article is a blogger original article, shall not be reproduced without the bloggers allowed. https://blog.csdn.net/snaking616/article/details/87931690

1. void GetStreamerDevice()

void GetStreamerDevice()
{
	StartBtn->Enabled = false;

	EndPointsBox->Items->Clear();
	EndPointsBox->Text = "";

	USBDevice = new CCyUSBDevice((HANDLE)this->Handle,CYUSBDRV_GUID,true);

	if (USBDevice == NULL) return;

	int n = USBDevice->DeviceCount();

	// Walk through all devices looking for VENDOR_ID/PRODUCT_ID
	for (int i=0; i<n; i++)
	{
		if ((USBDevice->VendorID == VENDOR_ID) && (USBDevice->ProductID == PRODUCT_ID)) 
			break;

		USBDevice->Open(i);
	}

	if ((USBDevice->VendorID == VENDOR_ID) && (USBDevice->ProductID == PRODUCT_ID)) 
	{
		StartBtn->Enabled = true;

		int interfaces = USBDevice->AltIntfcCount()+1;

		bHighSpeedDevice = USBDevice->bHighSpeed;

		for (int i=0; i< interfaces; i++)
		{
			// Alt setting #1 has issues.  Don't use it.
			//if (i != 1)
			{
				USBDevice->SetAltIntfc(i);

				int eptCnt = USBDevice->EndPointCount();

				// Fill the EndPointsBox
			for (int e=1; e<eptCnt; e++)
			{
				CCyUSBEndPoint *ept = USBDevice->EndPoints[e];
				if ((ept->Attributes == 1) || (ept->Attributes == 2))
				{
				String *s = "";
				s = String::Concat(s, (ept->Attributes == 2) ? "BULK " : "ISOC ");
				s = String::Concat(s, ept->bIn ? "IN,       " : "OUT,   ");
				s = String::Concat(s, ept->MaxPktSize.ToString(), " Bytes,");
				s = String::Concat(s, "   (", i.ToString(), " - ");
				s = String::Concat(s, "0x", ept->Address.ToString("X02"), ")");
				EndPointsBox->Items->Add(s);
				}
			}
			}
		}

		EndPointsBox->SelectedIndex = 0;
	}


}

Role: to add an optional check box control EndPointsBox endpoint configuration items.

Design ideas: open USB devices -> Get the number of USB ports -> record configuration information for all endpoints with different Interface Configuration -> endpoint configuration information added to the check box options.

Related operations:

Code

effect

StartBtn->Enabled = false;

Disable the Start button

StartBtn->Enabled = true;

Enable the Start button

EndPointsBox->Items->Clear();

Clear endpoint configuration check box

USBDevice = new CCyUSBDevice((HANDLE)this->Handle,CYUSBDRV_GUID,true);

New CCyUSBDevice structure

USBDevice->DeviceCount()

Gets the number of USB devices

USBDevice->Open(i)

Open the i-th USB devices

USBDevice->VendorID

Get Device VENDOR_ID

USBDevice->ProductID

Get Device PRODUCT_ID

interfaces = USBDevice->AltIntfcCount()+1;

Gets the number of interface devices

USBDevice->SetAltIntfc(i);

The device is set to the i-th Interface Configuration

eptCnt = USBDevice->EndPointCount();

Get the number of devices available endpoints arranged at an interface configuration

CCyUSBEndPoint *ept = USBDevice->EndPoints[e];

Using the device configuration of endpoints e

ept->Attributes == 2

Determining if the endpoint for bulk transport endpoint

Attributes=0,Control

Attributes=1,Isochronous

Attributes=2,Bulk

Attributes=3,Interupt

s = String::Concat(s, (ept->Attributes == 2) ? "BULK " : "ISOC ");

BULK or ISOC added back to the string s

EndPointsBox->Items->Add(s);

Add a new endpoint configuration information in the check box

EndPointsBox->SelectedIndex = 0;

Select the checkbox Item 1 endpoint configuration information

2. void Form1_Load(System::Object *  sender, System::EventArgs *  e)

void Form1_Load(System::Object *  sender, System::EventArgs *  e)
 {
    XferRateBar		= RateProgressBar;
	XferRateLabel	= RateLabel;
	DataBox			= DataTextBox;
	SuccessBox		= SuccessesBox;
	FailureBox		= FailuresBox;

	EptsBox			= EndPointsBox;
	PpxBox			= PacketsPerXferBox;
	QueueBox		= QueueLenBox;
	StartButton		= StartBtn;
	TimeoutBox		= TimeOutBox;
	ShowBox			= ShowDataBox;

	GetStreamerDevice();

	XferThread = new Thread(new ThreadStart(0,&XferLoop));
 }

Role: initialization functions C # project, call GetStreamerDevice (), the new process XferThread.

Design ideas: omitted.

 

3. void Form1_Closed(System::Object *  sender, System::EventArgs *  e)

void Form1_Closed(System::Object *  sender, System::EventArgs *  e)
 {
	 //if (XferThread->ThreadState == System::Threading::ThreadState::Suspended)
	 //XferThread->Resume();
			
	bStreaming = false;  // Stop the thread's xfer loop

	if (XferThread->ThreadState == System::Threading::ThreadState::Running)
		XferThread->Join(10);
 }

Role: shutdown process XferThread.

Design ideas: omitted.

 

4. void StartBtn_Click(System::Object *  sender, System::EventArgs *  e)

void StartBtn_Click(System::Object *  sender, System::EventArgs *  e)
{
	Decimal db;
	
	if(!Decimal::TryParse(this->TimeOutBox->Text, &db))
	{
		::MessageBox(NULL,"Invalid Input : TimeOut Per Xfer(ms)","Streamer",0);
		this->TimeOutBox->Text = "";
		return;
	}
    if (XferThread) {
		switch (XferThread->ThreadState)
		{
		case System::Threading::ThreadState::Stopped:
		case System::Threading::ThreadState::Unstarted:

			EnforceValidPPX();

			StartBtn->Text = "Stop";
			StartBtn->BackColor = Color::MistyRose;
			StartBtn->Refresh();

			bStreaming = true;

			// Start-over, initializing counters, etc.
			if ((XferThread->ThreadState) == System::Threading::ThreadState::Stopped)
				XferThread = new Thread(new ThreadStart(0,&XferLoop));

			PPX = Convert::ToInt32(PacketsPerXferBox->Text);

			QueueSize = Convert::ToInt32(QueueLenBox->Text);
			TimeOut = Convert::ToInt32(TimeOutBox->Text);
			bShowData = ShowDataBox->Checked;

			EndPointsBox->Enabled		= false;
			PacketsPerXferBox->Enabled	= false;
			QueueLenBox->Enabled		= false;
			TimeOutBox->Enabled			= false;
			ShowDataBox->Enabled		= false;

			XferThread->Start();
			break;
		case System::Threading::ThreadState::Running:
			StartBtn->Text = "Start";
			StartBtn->BackColor = Color::Aquamarine;
			StartBtn->Refresh();
					
			bStreaming = false;  // Stop the thread's xfer loop
			XferThread->Join(10);

			EndPointsBox->Enabled		= true;
			PacketsPerXferBox->Enabled	= true;
			QueueLenBox->Enabled		= true;
			TimeOutBox->Enabled			= true;
			ShowDataBox->Enabled		= true;

			break;
		}

	} 

 }

Effect: the input content TimeOutBox detection of whether the norms. XferThread process to determine the current status, or if you are in Stopped Unstarted, start the process XferThread, if Running, shut down the process XferThread.

Design: determining whether the contents of the control TimeOutBox compliance -> Analyzing Process XferThread current state, or when in the Unstarted Stopped, PPX acquisition variable size, the process loop flag = bStreaming  to true , the PC is operable to disable all the controls, starts data transfer process . -> the current state is determined XferThread process, when in Running, disable all controls to the available state, the process loop flag bStreaming = false, closing the data transfer process.

Related operations:

Code

effect

XferThread->ThreadState

The process of obtaining state

StartBtn->Refresh();

Refresh the display state of the button

XferThread = new Thread(new ThreadStart(0,&XferLoop));

New process

PPX = Convert::ToInt32(PacketsPerXferBox->Text);

The PPX converted from character to int32 type integer

 

5. void EndPointsBox_SelectedIndexChanged(System::Object *  sender, System::EventArgs *  e)

void EndPointsBox_SelectedIndexChanged(System::Object *  sender, System::EventArgs *  e)
{
	// Parse the alt setting and endpoint address from the EndPointsBox->Text
	String *tmp = EndPointsBox->Text->Substring(EndPointsBox->Text->IndexOf("("),10);
	int  alt = Convert::ToInt32(tmp->Substring(1,1));

	String *addr = tmp->Substring(7,2);
	//changed int to __int64 to avoid data loss
	__int64 eptAddr = HexToInt(addr);

	int clrAlt = (USBDevice->AltIntfc() == 0) ? 1 : 0;

	// Attempt to set the selected Alt setting and get the endpoint
	if (! USBDevice->SetAltIntfc(alt))
	{
		MessageBox::Show("Alt interface could not be selected.","USB Exception",MessageBoxButtons::OK,MessageBoxIcon::Hand);
		StartBtn->Enabled = false;
		USBDevice->SetAltIntfc(clrAlt); // Cleans-up
		return;
	}
	
	
	EndPt = USBDevice->EndPointOf((UCHAR)eptAddr);

	StartBtn->Enabled = true;


	if (EndPt->Attributes == 1)
	{
		SuccessLabel->Text = "Good Pkts";
		FailureLabel->Text = "Bad Pkts";
	}
	else
	{
		SuccessLabel->Text = "Successes";
		FailureLabel->Text = "Failures";
	}

	EnforceValidPPX();
}

Role: Reconfigure the USB endpoint to be tested

Design ideas: Get the current interface information and endpoint information from the control -> modify the configuration of the information USB interface and endpoint

Related operations: using the type information transmission, transmission direction, and the interface information extracted endpoint address and other control Substring function currently displayed -> according to the extracted information to reconfigure the USB interface and endpoint.

Related operations:

Code

effect

Substring(EndPointsBox->Text->IndexOf("("),10)

Interception character "(" behind the characters 10

alt = Convert::ToInt32(tmp->Substring(1,1));

The first character tmp converted to int type 32

String *addr = tmp->Substring(7,2);

7 position, taken from the first string of length 2

__int64 eptAddr = HexToInt(addr);

Addr value by the character type to type int

EndPt = USBDevice->EndPointOf((UCHAR)eptAddr);

Reconfiguring the USB endpoint

SuccessLabel->Text = "Good Pkts";

Modify the static text control content for Good Pkts

 

6. void EnforceValidPPX()

void EnforceValidPPX()
{
	PPX = Convert::ToInt32(PacketsPerXferBox->Text);

	// Limit total transfer length to 64K
	int len = ((EndPt->MaxPktSize) * PPX);
	int maxLen = 0x10000; 
	if (len > maxLen)
	{
		PPX = maxLen / EndPt->MaxPktSize;
		DataBox->Text = String::Concat(DataBox->Text,"Total xfer length limited to 64K.\r\n");
		DataBox->Text = String::Concat(DataBox->Text,"Packets per Xfer has been adjusted.\r\n");
		DataBox->SelectionStart = DataBox->Text->Length;
		DataBox->ScrollToCaret();
	}

	if (bHighSpeedDevice && (EndPt->Attributes == 1))  // HS ISOC Xfers must use PPX >= 8
	{
		if (PPX < 8)
		{
			PPX = 8;
			Display("ISOC xfers require at least 8 Packets per Xfer.");
			Display("Packets per Xfer has been adjusted.");
		}

		PPX = (PPX / 8) * 8;
	}

	PacketsPerXferBox->Text = PPX.ToString();			
}

Action: Check the total length of a single data transfer exceeds 64K, and if it exceeds the recalculated PPX

设计思路:单次传输总长度len = ((EndPt->MaxPktSize) * PPX) --> 如果len>0x10000,PPX = maxLen / EndPt->MaxPktSize --> 如果USB工作在高速同步传输的状态下,PPX不应小于8。

相关操作:

代码

作用

PPX = Convert::ToInt32(PacketsPerXferBox->Text);

将字符型变量转换为32为int类型

DataBox->Text = String::Concat(DataBox->Text,"Total xfer length limited to 64K.\r\n");

往DataBox->Text后方添加指定字符串

DataBox->ScrollToCaret();

光标滚动到插入位置的最后面

 

7. static UInt64 HexToInt(String *hexString)

static UInt64 HexToInt(String *hexString)
{
	String *HexChars = "0123456789abcdef";

	String *s = hexString->ToLower();

	// Trim off the 0x prefix
	if (s->Length > 2)
		if (s->Substring(0,2)->Equals("0x"))
			s = s->Substring(2,s->Length-2);

	
	String *_s = "";
	int len = s->Length;

	// Reverse the digits
	for (int i=len-1; i>=0; i--) _s  = String::Concat(_s,s->Substring(i,1));

	UInt64 sum = 0;
	UInt64 pwrF = 1;
	for (int i=0; i<len; i++)
	{
		UInt32 ordinal = (UInt32) HexChars->IndexOf(_s->Substring(i,1));
		sum += (i==0) ? ordinal : pwrF*ordinal;
		pwrF *= 16;
	}


	return sum;
}

作用:将字符串格式的数字转换为无符号64位整形

设计思路:由低到高的将字符串hexString的每一位字符和字符串HexChars = "0123456789abcdef"进行对比,找到该字符串对应的序号,每个序号对应不同的权值,第1到第16位依次为0,16的1次方、16的2次方...16的15次方,将hexString的每一位位号和对应权值相乘并叠加,即可完成字符串到无符号64位整形的转换。

相关操作:

代码

作用

String *s = hexString->ToLower();

字母字符转换成小写

UInt32 ordinal = (UInt32) HexChars->IndexOf(_s->Substring(i,1));

查找字符串_s第i个元素在HexChars中所在位号

 

8. static void ShowCpuLoad(Object* state) 

static void ShowCpuLoad(Object* state) 
{
	Form1 *form = dynamic_cast<Form1 *>(state);

	if (form->CPUCounter != NULL)
	{
		//thread safe-commented
		CheckForIllegalCrossThreadCalls = false;

		float load = form->CPUCounter->NextValue();
		int a = Convert::ToInt32(load);
		form->CPUBar->Value = a;
		form->CPULabel->Text = String::Concat(a.ToString()," %"); 
	}
}

作用:显示CPU使用比例

设计思路:省略。

相关操作:省略。

 

9. static void Display(String *s)

static void Display(String *s)
 {
	DataBox->Text = String::Concat(DataBox->Text, s, "\r\n");
	DataBox->SelectionStart = DataBox->Text->Length;
	DataBox->ScrollToCaret();
 }

作用:在文本框DataBox中显示指定字符串

设计思路:省略。

相关操作:省略。

 

10. static void Display16Bytes(PUCHAR data)

static void Display16Bytes(PUCHAR data)
{
	String *xData = "";

	for (int i=0; i<16; i++) 
		xData = String::Concat(xData,data[i].ToString("X02"), " ");

	Display(xData);
} 

作用:显示字符数组data前16个元素,相邻元素之间用空格分开。

设计思路:省略。

相关操作:省略。

 

11. static void XferLoop()

// This method executes in it's own thread.  The thread gets re-launched each time the
// Start button is clicked (and the thread isn't already running).
static void XferLoop()
{
	long BytesXferred = 0;
	unsigned long Successes = 0;
	unsigned long Failures = 0;
	int i = 0;
	// Allocate the arrays needed for queueing
	PUCHAR			*buffers		= new PUCHAR[QueueSize];
	CCyIsoPktInfo	**isoPktInfos	= new CCyIsoPktInfo*[QueueSize];
	PUCHAR			*contexts		= new PUCHAR[QueueSize];
	OVERLAPPED		inOvLap[MAX_QUEUE_SZ];
	long len = EndPt->MaxPktSize * PPX; // Each xfer request will get PPX isoc packets
	EndPt->SetXferSize(len);
	// Allocate all the buffers for the queues
	for (i=0; i< QueueSize; i++) 
	{ 
		buffers[i]        = new UCHAR[len];
		isoPktInfos[i]    = new CCyIsoPktInfo[PPX];
		inOvLap[i].hEvent = CreateEvent(NULL, false, false, NULL);

		memset(buffers[i],0,len);
	}
	DateTime t1 = DateTime::Now;	// For calculating xfer rate
	// Queue-up the first batch of transfer requests
	for (i=0; i< QueueSize; i++)	
	{
		contexts[i] = EndPt->BeginDataXfer(buffers[i], len, &inOvLap[i]);
		if (EndPt->NtStatus || EndPt->UsbdStatus) // BeginDataXfer failed
		{
			Display(String::Concat("Xfer request rejected. NTSTATUS = ",EndPt->NtStatus.ToString("x")));
			AbortXferLoop(i+1, buffers,isoPktInfos,contexts,inOvLap);
			return;
		}
	}
	i=0;	
	// The infinite xfer loop.
	for (;bStreaming;)		
	{
		long rLen = len;	// Reset this each time through because
							// FinishDataXfer may modify it

		if (!EndPt->WaitForXfer(&inOvLap[i], TimeOut))
		{
			EndPt->Abort();
			if (EndPt->LastError == ERROR_IO_PENDING)
				WaitForSingleObject(inOvLap[i].hEvent,2000);
		}

		if (EndPt->Attributes == 1) // ISOC Endpoint
		{	
			if (EndPt->FinishDataXfer(buffers[i], rLen, &inOvLap[i], contexts[i], isoPktInfos[i])) 
			{			
	  			CCyIsoPktInfo *pkts = isoPktInfos[i];
				for (int j=0; j< PPX; j++) 
				{
					if (pkts[j].Status == 0) 
					{
						BytesXferred += pkts[j].Length;

						if (bShowData)
							Display16Bytes(buffers[i]);

						Successes++;
					}
					else
						Failures++;

					pkts[j].Length = 0;	// Reset to zero for re-use.
				}

			} 
			else
				Failures++; 

		} 

		else // BULK Endpoint
		{
			if (EndPt->FinishDataXfer(buffers[i], rLen, &inOvLap[i], contexts[i])) 
			{			
				Successes++;
				BytesXferred += len;

				if (bShowData)
					Display16Bytes(buffers[i]);
			} 
			else
				Failures++; 
		}

		if (BytesXferred < 0) // Rollover - reset counters
		{
			BytesXferred = 0;
			t1 = DateTime::Now;
		}

		// Re-submit this queue element to keep the queue full
		contexts[i] = EndPt->BeginDataXfer(buffers[i], len, &inOvLap[i]);
		if (EndPt->NtStatus || EndPt->UsbdStatus) // BeginDataXfer failed
		{
			Display(String::Concat("Xfer request rejected. NTSTATUS = ",EndPt->NtStatus.ToString("x")));
			AbortXferLoop(QueueSize,buffers,isoPktInfos,contexts,inOvLap);
			return;
		}

		i++;

		if (i == QueueSize) //Only update the display once each time through the Queue
		{
			i=0;
			ShowStats(t1, BytesXferred, Successes, Failures);					
		}

	}  // End of the infinite loop

	// Memory clean-up
	AbortXferLoop(QueueSize,buffers,isoPktInfos,contexts,inOvLap);
}

作用:进行USB的BULK或者ISOC传输测试,记录已传输数据的大小、速度以及是否成功传输等信息。

设计思路:第一步定义和初始化异步传输结构体inOvLap[MAX_QUEUE_SZ];第二步记录当前时刻,使用函数BeginDataXfer(buffers[i], len, &inOvLap[i])开启异步传输事件;第三步进入异步传输死循环,调用函数WaitForXfer(&inOvLap[i], TimeOut)检查单次任务是否超时,使用FinishDataXfer(buffers[i], rLen, &inOvLap[i], contexts[i], isoPktInfos[i])检查单次任务是否成功,并对传输数据大小进行统计;最后一步调用函数ShowStats(t1, BytesXferred, Successes, Failures)显示传输信息。

相关操作:

代码

作用

EndPt->SetXferSize(len);

设置端点待传输的数据长度

inOvLap[i].hEvent = CreateEvent(NULL, falsefalse, NULL)

异步传输结构体初始化

memset(buffers[i],0,len)

给字符数组*buffers[i]所有元素赋值0

contexts[i] = EndPt->BeginDataXfer(buffers[i], len, &inOvLap[i]);

使用异步传输结构体记录异步传输事件

AbortXferLoop(i+1, buffers,isoPktInfos,contexts,inOvLap);

如果传输失败,丢弃本次传输事件

EndPt->WaitForXfer(&inOvLap[i], TimeOut)

单次传输任务最大时间不得超过 TimeOut(ms)

FinishDataXfer(buffers[i], rLen, &inOvLap[i], contexts[i], isoPktInfos[i]))

监测同步传输任务是否成功

CCyIsoPktInfo *pkts = isoPktInfos[i];

同步传输包传输状态结构体

DateTime t1 = DateTime::Now

记录当前时刻,用于速度计算

 

12. static void AbortXferLoop(int pending, PUCHAR *buffers, CCyIsoPktInfo **isoPktInfos, PUCHAR *contexts, OVERLAPPED inOvLap __nogc [])

static void AbortXferLoop(int pending, PUCHAR *buffers, CCyIsoPktInfo **isoPktInfos, PUCHAR *contexts, OVERLAPPED inOvLap __nogc [])
 {
	EndPt->Abort();
	long len = EndPt->MaxPktSize * PPX;

	for (int j=0; j< QueueSize; j++) 
	{ 
		if (j<pending)
		{
			if (!EndPt->WaitForXfer(&inOvLap[j], TimeOut)) 
			{
				EndPt->Abort();
				if (EndPt->LastError == ERROR_IO_PENDING)
					WaitForSingleObject(inOvLap[j].hEvent,2000);
			}

			EndPt->FinishDataXfer(buffers[j], len, &inOvLap[j], contexts[j]);
		}

		CloseHandle(inOvLap[j].hEvent);

		delete [] buffers[j];
		delete [] isoPktInfos[j];
	}

	delete [] buffers;
	delete [] isoPktInfos;
	delete [] contexts;

	
	bStreaming = false;

	StartButton->Text = "Start";
	StartButton->BackColor = Color::Aquamarine;
	StartButton->Refresh();

	EptsBox->Enabled	= true;
	PpxBox->Enabled		= true;
	QueueBox->Enabled	= true;
	TimeoutBox->Enabled	= true;
	ShowBox->Enabled	= true;
 }

作用:终止当前传输事件,清除内存中的临时变量

设计思路:省略。

相关操作:省略。

 

13. static void ShowStats(DateTime t, long bytesXferred, unsigned long successes, unsigned long failures)

	    static void ShowStats(DateTime t, long bytesXferred, unsigned long successes, unsigned long failures)
		{
			TimeSpan elapsed = DateTime::Now.Subtract(t);

			long totMS = (long)elapsed.TotalMilliseconds;
			if (totMS <= 0)	return;

			long XferRate = bytesXferred / totMS;

			// Convert to KB/s
			XferRate = XferRate * 1000 / 1024;

			// Truncate last 1 or 2 digits
			int rounder = (XferRate > 2000) ? 100 : 10;
			XferRate = XferRate / rounder * rounder;  

			//thread safe-commented
			CheckForIllegalCrossThreadCalls = false;

			if (XferRate > XferRateBar->Maximum) 
				XferRate = XferRateBar->Maximum;


			XferRateBar->Value = XferRate;
			XferRateLabel->Text = XferRate.ToString();

			SuccessBox->Text = successes.ToString();
			FailureBox->Text = failures.ToString();

		}

Role: The number of data transmission speed, transmission success and failure display.

Design ideas: omitted.

Related operations:

Code

effect

TimeSpan elapsed = DateTime::Now.Subtract(t);

Use the current time minus the first time t

long totMS = (long)elapsed.TotalMilliseconds;

The time difference unit conversion is ms

 

14. virtual void WndProc(Message *m)

virtual void WndProc(Message *m)
		{	
			if (m->Msg == WM_DEVICECHANGE) 
			{
				// Tracks DBT_DEVNODES_CHANGED followed by DBT_DEVICEREMOVECOMPLETE
				if (m->WParam == DBT_DEVNODES_CHANGED) 
				{
					bPnP_DevNodeChange = true;
					bPnP_Removal = false;
				}

				// Tracks DBT_DEVICEARRIVAL followed by DBT_DEVNODES_CHANGED
				if (m->WParam == DBT_DEVICEARRIVAL) 
				{
					bPnP_Arrival = true;
					bPnP_DevNodeChange = false;
				}

				if (m->WParam == DBT_DEVICEREMOVECOMPLETE) 
					bPnP_Removal = true;

				// If DBT_DEVICEARRIVAL followed by DBT_DEVNODES_CHANGED
				if (bPnP_DevNodeChange && bPnP_Removal) 
				{
					bPnP_Removal = false;
					bPnP_DevNodeChange = false;
					bStreaming = false;
					GetStreamerDevice();
				}

				// If DBT_DEVICEARRIVAL followed by DBT_DEVNODES_CHANGED
				if (bPnP_DevNodeChange && bPnP_Arrival) 
				{
					bPnP_Arrival = false;
					bPnP_DevNodeChange = false;
					GetStreamerDevice();
				}

			}

			Form::WndProc(m);
		}

Role: for hot-swappable to achieve USB devices

Design ideas: omitted.

Related operations: omitted.

 

Guess you like

Origin blog.csdn.net/snaking616/article/details/87931690