当前位置:首页 > 嵌入式 > 嵌入式软件
[导读] //=====================================================================//TITLE:// WinCE获取SD卡序列号//AUTHOR:// norains//DATE:// Thursday 25-February-2011//En

 //=====================================================================

//TITLE:

// WinCE获取SD卡序列号

//AUTHOR:

// norains

//DATE:

// Thursday 25-February-2011

//Environment:

// Visual Studio 2005

// Windows CE 6.0

// Telechips TCC89x

//=====================================================================

WinCE的设备,估计会和SD卡打交道的应该不在少数。特别是一些软件,比如导航地图之类,加密数据用的就是SD卡的序列号。不过,严格来说,在WinCE下面并没有专门针对于SD卡序列号的获取函数,而是针对Storage的。只不过SD卡也是Storage的一种,所以自然也能够被获取。

SD卡序列号的获取,是需要通过驱动的的。这么一说的话,熟悉的朋友可能就明白流程了:首先调用CreateFile打开驱动,接着使用DeviceIoControl来获取序列号,最后则是调用CloseHandle进行关闭。

一步一步来,先看看CreateFile的调用,如:

[cpp] view plaincopyHANDLE hDisk = CreateFile(TEXT("DSK2:"),

GENERIC_READ,

FILE_SHARE_READ | FILE_SHARE_WRITE,

NULL,

OPEN_EXISTING,

0,

NULL);

这段代码没什么问题,可能大家比较关心的是"DSK2:"这个参数的来源。或是说,我如何确定这个参数。很多朋友可能会错认为,当我们SD卡插入到设备中,在"我的设备"会出现"Storage Card"分区,那么CreateFile的第一个形参就应该是它。但实际上这样是错误的,传入的形参并不是分区名,而应该是驱动名。而这个驱动名的确认,可以通过"控制面板"的"存储器"确认,如图:

图中的"DSK1:"是设备中NAND FLASH的驱动,而"DSK2:"则是SD卡的。可能有朋友问了,我如何判断哪个是SD卡的呢?其实这是一个很简单的事情:没插入SD卡的时候查看一次,插入SD卡的时候再查看一次,多出的那个就是SD卡了。囧~

接着我们就必须调用DeviceIoControl函数了,如下代码所示:

[cpp] view plaincopyPSTORAGE_IDENTIFICATION pStoreInfo = (PSTORAGE_IDENTIFICATION) new BYTE[300];

DeviceIoControl(hDisk,

IOCTL_DISK_GET_STORAGEID,

NULL,

0,

pStoreInfo,

BUFFER_SIZE,

&dwBytesRet,

NULL);

IOCTL_DISK_GET_STORAGEID是设备请求标识符,这个应该也好理解。如果你的代码编译时该标识符没有定义,那么你应该是没有包含Diskio.h文件。可能比较费解的是pStoreInfo,为什么要new个300byte的空间,然后又转换为PSTORAGE_IDENTIFICATION指针呢?这个我们必须要从STORAGE_IDENTIFICATION结构说起。

STORAGE_IDENTIFICATION结构定义如下:

[cpp] view plaincopytypedef struct _STORAGE_IDENTIFICATION {

DWORD dwSize;

DWORD dwFlags;

DWORD dwManufactureIDOffset;

DWORD dwSerialNumOffset;

} STORAGE_IDENTIFICATION, *PSTORAGE_IDENTIFICATION;

dwSize是结构体和标识符的大小,dwFlags标志着标识符是否可用,dwManufactureIDOffset是标识符的中的厂家ID偏移位置,最后的dwSerialNumOffset则是标识符中的序列号偏移位置。完了?是的,完了,就是这几样东西。那么,疑问来了,那标识符在哪里?别急,我们来看看返回回来数据的存储方式,如图:

图中的灰色部分为STORAGE_IDENTIFICATION的成员,其大小为sizeof(STORAGE_IDENTIFICATION),是固定值;而蓝色的部分,则是我们分配的内存减去结构体后的大小,容量随着我们分配的内存而改变。在这里稍微返回来看一下代码,为什么我们代码中分配了个300Byte的空间呢?其实300这个数值是随意的,如果你设备的标识符大于这个数值,可以进行修改。但对于SD卡来说,标识符也就是10位,加上STORAGE_IDENTIFICATION的大小,300的空间完全足够了。

接着从图中可以知道,在紧随着结构体之后的是ManufactureID和SerialNumber的数值。那么这两个数值的位置应该如何确定呢?那就是通过dwManufactureIDOffset和dwSerialNumOffset的数值来确定,这也就是为什么图中dwManufactureIDOffset方块延伸出来的箭头会指向ManufactureID前端的原因。

对于ManufactureID和SerialNumber还有一些地方需要注意的。这两个号码合起来的长度是不固定的,但我们能通过末尾是否为‘/0‘来判断标识符是否结束。另外还有一点,ManufactureID和SerialNumber之间是没有分隔符的,我们只能通过(dwSerialNumOffset - dwManufactureIDOffset)来确定。但这个还是需要有前提条件的,就是dwSerialNumOffset和dwManufactureIDOffset都不能为0,因为为0时,就意味着该标识符无效。简单点来说,如果dwSerialNumOffset这个偏移量的数值为0,那么意味着返回的数值中并没有ManufactureID。

结构明白之后,我们就很容易获取这两个标志的起始地址了:

[cpp] view plaincopychar *pManufactureID = reinterpret_cast(pStoreInfo) + pStoreInfo->dwManufactureIDOffset;

char *pSerialNum = reinterpret_cast(pStoreInfo) + pStoreInfo->dwSerialNumOffset;

接下来的事情可能就不用细说了,无非就是根据起始地址来复制字符串。最后,就是调用CloseHandle来关闭驱动句柄了。

流程介绍完毕,但还不是文章的结尾。本文的最后,让我们来看看一个完整的获取标识符的函数,代码如下所示:

[cpp] view plaincopyBOOL GetStorageIdentification(std::wstring &strDiskName,std::string &strManufactureID,std::string &strSerialNum)[!--empirenews.page--]

{

BOOL bRes = FALSE;

PSTORAGE_IDENTIFICATION pStoreInfo = NULL;

HANDLE hDisk = INVALID_HANDLE_VALUE;

__try

{

//The buffer for storing data

const DWORD BUFFER_SIZE = 300;

//Allocate the size for the struct

pStoreInfo = (PSTORAGE_IDENTIFICATION) new BYTE[BUFFER_SIZE];

if(pStoreInfo == NULL)

{

__leave;

}

//Open the driver

hDisk = CreateFile(strDiskName.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);

if(hDisk == INVALID_HANDLE_VALUE)

{

__leave;

}

//Get the ID from the driver

DWORD dwBytesRet = 0;

if (DeviceIoControl(hDisk, IOCTL_DISK_GET_STORAGEID, NULL, 0, pStoreInfo, BUFFER_SIZE, &dwBytesRet, NULL) == FALSE)

{

__leave;

}

//Get the manufacture ID

if (pStoreInfo->dwManufactureIDOffset != 0)

{

char *pManufactureID = reinterpret_cast(pStoreInfo) + pStoreInfo->dwManufactureIDOffset;

if(pStoreInfo->dwSerialNumOffset != 0)

{

strManufactureID.assign(pManufactureID,pStoreInfo->dwSerialNumOffset - pStoreInfo->dwManufactureIDOffset);

}

else

{

strManufactureID = pManufactureID;

}

}

if(pStoreInfo->dwSerialNumOffset != 0)

{

char *pSerialNum = reinterpret_cast(pStoreInfo) + pStoreInfo->dwSerialNumOffset;

strSerialNum = pSerialNum;

}

bRes = TRUE;

}

__finally

{

if(pStoreInfo != NULL)

{

delete []pStoreInfo;

}

if(hDisk != INVALID_HANDLE_VALUE)

{

CloseHandle(hDisk);

}

}

return bRes;

}

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭