赞
踩
设备ID
设备 ID(Device ID) 是一个字符串,由设备的报告枚举器。 设备只有一个设备 id。如USB存储设备ID:
USB\VID_23A9&PID_EF18\5&19353383&0&2
实例ID
实例 ID (Instance ID)是将设备与计算机上相同类型的其他设备进行区分的设备标识字符串。 实例 ID 包含序列号信息(如果基础总线支持)或某些类型的位置信息。
设备实例 ID
设备树的每个节点都称为一个设备节点中,或devnode。设备实例 ID 是系统提供的设备标识字符串,用于在系统中唯一标识设备。 即插即用(PnP)管理器为系统设备树中的每个设备节点(devnode)分配一个设备实例 ID。
此字符串的格式由连接到设备 ID的实例 ID组成,如下所示:
<instance-specific-ID>
设备实例 ID 的字符数(不包括 NULL 终止符)必须小于 MAX_DEVICE_ID_LEN。 此约束适用于设备 ID和实例特定 ID字段之间所有字段的长度与 “” 字段分隔符之和。
在系统重新启动期间,设备实例 ID 是永久性的。
下面是连接到 PCI 设备的设备 ID 的实例 ID (“1 & 08”)的示例:
PCI\VEN_1000&DEV_0001&SUBSYS_00000000&REV_02\1&08
CM 是Configuration Manager的简写。Configuration Manager functions是用来查询Windows系统中接入的设备信息。
CMAPI CONFIGRET CM_Locate_DevNode(
PDEVINST pdnDevInst,
DEVINSTID_A pDeviceID,
ULONG ulFlags);
CMAPI CONFIGRET CM_Get_DevNode_Registry_PropertyW(
DEVINST dnDevInst,
ULONG ulProperty,
PULONG pulRegDataType,
PVOID Buffer,
PULONG pulLength,
ULONG ulFlags
);
CMAPI CONFIGRET CM_Get_Child(
PDEVINST pdnDevInst,
DEVINST dnDevInst,
ULONG ulFlags
);
CMAPI CONFIGRET CM_Get_Sibling(
PDEVINST pdnDevInst,
DEVINST dnDevInst,
ULONG ulFlags
);
CMAPI CONFIGRET CM_Get_Parent(
PDEVINST pdnDevInst,
DEVINST dnDevInst,
ULONG ulFlags
);
CMAPI CONFIGRET CM_Get_Device_IDW(
DEVINST dnDevInst,
PWSTR Buffer,
ULONG BufferLen,
ULONG ulFlags
);
UINT EnumeAllDeviceByCM() { DEVINST devInst = {0}; DEVINST devInstNext = {0}; CONFIGRET cr = {0}; ULONG walkDone = 0; ULONG len = 0; char buf[1024] = {0}; // Get Root DevNode cr = CM_Locate_DevNode(&devInst, NULL, 0); // 可以指定根设备ID //cr = CM_Locate_DevNode(&devInst, "PCI\\VEN_8086&DEV_9D2F&SUBSYS_17091D72&REV_21\\3&11583659&1&A0", 0); if (cr != CR_SUCCESS) { return 1; } // Do a depth first search for the DevNode with a matching // DriverName value while (!walkDone) { char szDeviceID[1024] = {0}; char szProperty[1024] = {0}; len = 1024; cr = CM_Get_Device_ID(devInst, szDeviceID, len, 0); if (cr == CR_SUCCESS) { for (UINT uIdx = 1; uIdx <= _countof(CM_PROPERTY); uIdx++) { len = 1024; cr = CM_Get_DevNode_Registry_Property(devInst, uIdx, NULL, szProperty, &len, 0); if (cr == CR_SUCCESS) { std::cout<<CM_PROPERTY[uIdx-1]<<std::endl; std::cout<<szProperty<<std::endl; OutputDebugString(CM_PROPERTY[uIdx-1]);OutputDebugString(":"); OutputDebugString(szProperty);OutputDebugString("\r\n"); } } } else { return 1; } // This DevNode didn't match, go down a level to the first child. cr = CM_Get_Child(&devInstNext, devInst, 0); if (cr == CR_SUCCESS) { devInst = devInstNext; continue; } // Can't go down any further, go across to the next sibling. If // there are no more siblings, go back up until there is a sibling. // If we can't go up any further, we're back at the root and we're // done. for (;;) { cr = CM_Get_Sibling(&devInstNext,devInst,0); if (cr == CR_SUCCESS) { devInst = devInstNext; break; } cr = CM_Get_Parent(&devInstNext,devInst,0); if (cr == CR_SUCCESS) { devInst = devInstNext; } else { walkDone = 1; break; } } } return 0; }
Windows的安装的设备都归为设备信息集中(device information set)中,并提供一套Setup开头的API来访问设备的相关信息。
WINSETUPAPI HDEVINFO SetupDiGetClassDevsW(
const GUID *ClassGuid,
PCWSTR Enumerator,
HWND hwndParent,
DWORD Flags
);
WINSETUPAPI BOOL SetupDiEnumDeviceInfo(
HDEVINFO DeviceInfoSet,
DWORD MemberIndex,
PSP_DEVINFO_DATA DeviceInfoData
);
WINSETUPAPI BOOL SetupDiGetDeviceRegistryPropertyA(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
DWORD Property,
PDWORD PropertyRegDataType,
PBYTE PropertyBuffer,
DWORD PropertyBufferSize,
PDWORD RequiredSize
);
WINSETUPAPI BOOL SetupDiGetDevicePropertyW(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
const DEVPROPKEY *PropertyKey,
DEVPROPTYPE *PropertyType,
PBYTE PropertyBuffer,
DWORD PropertyBufferSize,
PDWORD RequiredSize,
DWORD Flags
);
WINSETUPAPI BOOL SetupDiGetDeviceInterfaceDetailA(
HDEVINFO DeviceInfoSet,
PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
PSP_DEVICE_INTERFACE_DETAIL_DATA_A DeviceInterfaceDetailData,
DWORD DeviceInterfaceDetailDataSize,
PDWORD RequiredSize,
PSP_DEVINFO_DATA DeviceInfoData
);
WINSETUPAPI BOOL SetupDiGetDeviceInstanceIdA(
HDEVINFO DeviceInfoSet,
PSP_DEVINFO_DATA DeviceInfoData,
PSTR DeviceInstanceId,
DWORD DeviceInstanceIdSize,
PDWORD RequiredSize
);
UINT EnumAllDeviceBySetup() { HDEVINFO DeviceInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT); if (DeviceInfoSet == INVALID_HANDLE_VALUE) return 1; for (int i = 0; ; i++) { SP_DEVINFO_DATA DeviceInfoData; DeviceInfoData.cbSize = sizeof(DeviceInfoData); if (!SetupDiEnumDeviceInfo(DeviceInfoSet, i, &DeviceInfoData)) break; char szDeviceID[256] = {}; DWORD dwTemp = 0; SetupDiGetDeviceInstanceId(DeviceInfoSet, &DeviceInfoData, szDeviceID, 256, &dwTemp); DEVPROPKEY devicePropertyKey[256] = {0}; DWORD RequiredSize = 0; DEVPROPTYPE PropertyType = 0; CHAR PropertyBuffer[8192] = { 0 }; for (UINT uIdx = 0; uIdx < _countof(szSPDRPName); uIdx++) { DWORD DataT = 0; DWORD buffersize = 0; if (SetupDiGetDeviceRegistryProperty(DeviceInfoSet, &DeviceInfoData, uIdx, &DataT, reinterpret_cast<PBYTE>(PropertyBuffer), 8192, &RequiredSize)) { CString strLog = szSPDRPName[uIdx]; strLog += ":"; strLog += PropertyBuffer; strLog += "\r\n"; OutputDebugString(strLog); std::cout<<strLog.GetString(); } } } return 0; } UINT GetAllUSBDeviceID(char** _ppDeviceID, char** _ppHubID) { GUID* pGUID = (GUID*)&GUID_DEVINTERFACE_DISK; HDEVINFO hDevInfoSet = SetupDiGetClassDevs(pGUID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if ( hDevInfoSet == INVALID_HANDLE_VALUE ) { return 0; } SP_DEVICE_INTERFACE_DATA ifdata; PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(1024); if (pDetail == NULL) { return 0; } pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); U32 dwDeviceIdx = 0; U32 dwUSBDeviceCnt = 0; BOOL result = TRUE; SP_DEVINFO_DATA spdd; // device index = 0, 1, 2... test the device interface one by one while (result) { ifdata.cbSize = sizeof(ifdata); //enumerates the device interfaces that are contained in a device information set result = SetupDiEnumDeviceInterfaces( hDevInfoSet, // DeviceInfoSet NULL, // DeviceInfoData pGUID, // GUID dwDeviceIdx, // MemberIndex &ifdata // DeviceInterfaceData ); if (result) { // get the buffer size of details U32 dwSize = 0; result = SetupDiGetDeviceInterfaceDetail(hDevInfoSet, &ifdata, NULL, 0, &dwSize, NULL); if ((ERROR_INSUFFICIENT_BUFFER == GetLastError()) && (dwSize > 0)) { pDetail->cbSize = sizeof(*pDetail); ZeroMemory((PVOID)&spdd, sizeof(spdd)); spdd.cbSize = sizeof(spdd); // get details about a device interface result = SetupDiGetDeviceInterfaceDetail( hDevInfoSet, // DeviceInfoSet &ifdata, // DeviceInterfaceData pDetail, // DeviceInterfaceDetailData 1024, // DeviceInterfaceDetailDataSize &dwSize, // RequiredSize &spdd // DeviceInfoData ); dwDeviceIdx++; // 设备路径必须是usbstor if (NULL == strstr(pDetail->DevicePath, "usb")) { continue; } if (result) { DEVINST DevInstParent = 0; CONFIGRET cr = CM_Get_Parent(&DevInstParent, spdd.DevInst, 0); if (cr != CR_SUCCESS) { return 0; } char szBuff[512] = {0}; cr = CM_Get_Device_ID(DevInstParent, szBuff, 512, 0); if (cr != CR_SUCCESS) { return 0; } strcpy_s(_ppDeviceID[dwUSBDeviceCnt], DEVICE_ID_SIZE, szBuff); // 再向上查找一级即为HUBID DEVINST hubDevInst = 0; cr = CM_Get_Parent(&hubDevInst, DevInstParent, 0); if (cr != CR_SUCCESS) { return 0; } char szHubBuff[512] = {0}; cr = CM_Get_Device_ID(hubDevInst, szHubBuff, 512, 0); if (cr != CR_SUCCESS) { return 0; } strcpy_s(_ppHubID[dwUSBDeviceCnt++], DEVICE_ID_SIZE, szHubBuff); } } } } free(pDetail); SetupDiDestroyDeviceInfoList(hDevInfoSet); return dwUSBDeviceCnt; } int EnumAllDeviceBySetupEx() { //HDEVINFO DeviceInfoSet = SetupDiGetClassDevs(NULL, "USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT); HDEVINFO DeviceInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT); if (DeviceInfoSet == INVALID_HANDLE_VALUE) return 1; for (int nIdx = 0; ; nIdx++) { SP_DEVINFO_DATA DeviceInfoData; DeviceInfoData.cbSize = sizeof(DeviceInfoData); if (!SetupDiEnumDeviceInfo(DeviceInfoSet, nIdx, &DeviceInfoData)) break; char szDeviceID[256] = {}; DWORD dwTemp = 0; SetupDiGetDeviceInstanceId(DeviceInfoSet, &DeviceInfoData, szDeviceID, 256, &dwTemp); DEVPROPKEY devicePropertyKey[256] = {0}; DWORD dwCount = 128; if (!SetupDiGetDevicePropertyKeys(DeviceInfoSet, &DeviceInfoData, devicePropertyKey, dwCount, 0, 0)) continue; DWORD RequiredSize = 0; DEVPROPTYPE PropertyType = 0; WCHAR PropertyBuffer[1024] = {0}; for (UINT uIdx = 0; uIdx < _countof(devicePropertyKey); uIdx++) { if (0 == devicePropertyKey[uIdx].pid) continue; if (SetupDiGetDevicePropertyW(DeviceInfoSet, &DeviceInfoData, &devicePropertyKey[uIdx], &PropertyType, reinterpret_cast<PBYTE>(PropertyBuffer), sizeof(PropertyBuffer), NULL, 0)) { for (UINT uKeyIdx = 0; uKeyIdx < _countof(ARR_DEV_KEYS); uKeyIdx++) { if ((devicePropertyKey[uIdx].fmtid == ARR_DEV_KEYS[uKeyIdx].fmtid) && (devicePropertyKey[uIdx].pid == ARR_DEV_KEYS[uKeyIdx].pid)) { CString strLog = ARR_STR_DEV_KEYS[uKeyIdx]; strLog += ":"; USES_CONVERSION; strLog += W2A(PropertyBuffer); strLog += "\r\n"; OutputDebugString(strLog); std::cout<<strLog.GetString(); break; } } } } } return 0; }
sprintf_s(hcdName, "\\\\.\\HCD%d", nHCDNo);
HANDLE hHCDev = CreateFile(hcdName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
std::string strRootHubName = GetRootHubName(hHCDev);
const std::string GetDriverKeyName(HANDLE Hub, ULONG ConnectionIndex) { BOOL success; ULONG nBytes; USB_NODE_CONNECTION_DRIVERKEY_NAME driverKeyName; PUSB_NODE_CONNECTION_DRIVERKEY_NAME driverKeyNameW = 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) { return ""; } // Allocate space to hold the driver key name // nBytes = driverKeyName.ActualLength; if (nBytes <= sizeof(driverKeyName)) { return ""; } driverKeyNameW = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)new BYTE[nBytes]; if (driverKeyNameW == NULL) { delete[] driverKeyNameW; return ""; } // 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) { delete[] driverKeyNameW; return ""; } // Convert the driver key name // USES_CONVERSION; std::string driverKeyNameA = W2A(driverKeyNameW->DriverKeyName); delete[] driverKeyNameW; return driverKeyNameA; } const std::string GetExternalHubName(HANDLE Hub, ULONG ConnectionIndex) { BOOL success; ULONG nBytes; USB_NODE_CONNECTION_NAME extHubName; PUSB_NODE_CONNECTION_NAME extHubNameW = NULL; std::string extHubNameA; // 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) { goto GetExternalHubNameError; } // Allocate space to hold the external hub name // nBytes = extHubName.ActualLength; if (nBytes <= sizeof(extHubName)) { goto GetExternalHubNameError; } extHubNameW = (PUSB_NODE_CONNECTION_NAME) new BYTE[nBytes]; if (extHubNameW == NULL) { 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) { goto GetExternalHubNameError; } // Convert the External Hub name // USES_CONVERSION; extHubNameA = W2A(extHubNameW->NodeName); // All done, free the uncoverted external hub name and return the // converted external hub name // delete []extHubNameW; return extHubNameA; GetExternalHubNameError: // There was an error, free anything that was allocated // if (extHubNameW != NULL) { delete [](extHubNameW); extHubNameW = NULL; } return NULL; } void EnumerateHub(std::string rootHubName, bool bRoot = false); void EnumerateHubPorts(LPCTSTR _lpcHubName, HANDLE hHubDevice, ULONG NumPorts, bool bFindAnc, bool bRoot) { UNREFERENCED_PARAMETER(bFindAnc); UNREFERENCED_PARAMETER(bRoot); ULONG index; BOOL success; std::string strDebug; PUSB_NODE_CONNECTION_INFORMATION_EX connectionInfoEx; std::string driverKeyName; std::string deviceDesc; BOOL bHaveOneHubPort = FALSE; // Loop over all ports of the hub. // // Port indices are 1 based, not 0 based. // for (index=1; index <= NumPorts; index++) { ULONG nBytesEx; // 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)new BYTE[nBytesEx]; if (connectionInfoEx == NULL) { break; } // // Now query USBHUB for the USB_NODE_CONNECTION_INFORMATION_EX structure // for this port. This will tell us if a device is attached to this // port, among other things. // connectionInfoEx->ConnectionIndex = index; success = DeviceIoControl(hHubDevice, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, connectionInfoEx, nBytesEx, connectionInfoEx, nBytesEx, &nBytesEx, NULL); if (!success) { PUSB_NODE_CONNECTION_INFORMATION connectionInfo; ULONG nBytes; // 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)new BYTE[nBytes]; connectionInfo->ConnectionIndex = index; success = DeviceIoControl(hHubDevice, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION, connectionInfo, nBytes, connectionInfo, nBytes, &nBytes, NULL); if (!success) { delete []connectionInfo; delete []connectionInfoEx; 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 ? 0 : 1; 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); delete []connectionInfo; } // If there is a device connected, get the Device Description // if (connectionInfoEx->ConnectionStatus != NoDeviceConnected) { driverKeyName = GetDriverKeyName(hHubDevice,index); _strupr_s(const_cast<char*>(deviceDesc.c_str()), deviceDesc.length()+1); if (connectionInfoEx->DeviceIsHub) { std::string extHubName = GetExternalHubName(hHubDevice, index); EnumerateHub(extHubName); bHaveOneHubPort = TRUE; } } delete []connectionInfoEx; } } std::string GetRootHubName(HANDLE HostController) { BOOL success; ULONG nBytes; USB_ROOT_HUB_NAME rootHubName; PUSB_ROOT_HUB_NAME rootHubNameW= NULL; std::string rootHubNameA; // 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) { goto GetRootHubNameError; } // Allocate space to hold the Root Hub name // nBytes = rootHubName.ActualLength; rootHubNameW = (PUSB_ROOT_HUB_NAME)new BYTE[nBytes]; if (rootHubNameW == NULL) { 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) { goto GetRootHubNameError; } // Convert the Root Hub name // USES_CONVERSION; rootHubNameA = W2A(rootHubNameW->RootHubName); // All done, free the uncoverted Root Hub name and return the // converted Root Hub name // delete []rootHubNameW; return rootHubNameA; GetRootHubNameError: // There was an error, free anything that was allocated // if (rootHubNameW != NULL) { delete []rootHubNameW; rootHubNameW = NULL; } return ""; } void EnumerateHub(std::string rootHubName, bool bRoot /*= false*/) { if (rootHubName.empty()) { return; } _strupr_s(const_cast<char*>(rootHubName.c_str()), rootHubName.length()+1); std::string DeviceName; HANDLE hHubDevice; USB_NODE_INFORMATION hubInfo; ULONG nBytes; DeviceName = "\\\\.\\"; DeviceName += rootHubName; hHubDevice = CreateFile(DeviceName.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hHubDevice != INVALID_HANDLE_VALUE) { if (DeviceIoControl(hHubDevice, IOCTL_USB_GET_NODE_INFORMATION, &hubInfo, sizeof(USB_NODE_INFORMATION), &hubInfo, sizeof(USB_NODE_INFORMATION), &nBytes, NULL)) { EnumerateHubPorts( rootHubName.c_str(), hHubDevice, hubInfo.u.HubInformation.HubDescriptor.bNumberOfPorts, 0, bRoot); CloseHandle(hHubDevice); } } } DWORD EnumeAllDeviceByDeviceIOControl() { const int MAX_HCD_COUNT = 10; char hcdName[16] = {0}; for (int nHCDNo = 0; nHCDNo < MAX_HCD_COUNT; nHCDNo++) { sprintf_s(hcdName, "\\\\.\\HCD%d", nHCDNo); HANDLE hHCDev = CreateFile(hcdName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hHCDev == INVALID_HANDLE_VALUE) continue; std::string strRootHubName = GetRootHubName(hHCDev); if (strRootHubName.empty()) continue; EnumerateHub(strRootHubName.c_str(), true); } return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。