http://www.codeguru.com/forum/showthread.php?t=123783
//Unique PCID.h
#include "smart.h"
struct SPentiumIIISerialNumber
{
DWORD high;
DWORD middle;
DWORD low;
};
class CUniquePCID
{
public:
//returns the MAC address of the FIRST network card in the PC
bool GetMACAddress(__int64 & iMACAddress);
//returns 0 if disabled
bool CPUID(SPentiumIIISerialNumber & serialnumber) const;
//returns serial # of drive C - 0 indicates no drive C
//this function returns the number, as C: should always be there!
DWORD HardDiskVolume() const {return m_dwHD;}
bool GetIDESerialNo(CString & csSerialNumber,CString & csError);
CUniquePCID();
virtual ~CUniquePCID();
private:
DWORD m_dwHD;
//returns 0, and gives error message if fails
HANDLE OpenSMART(CString & csError,bool bWindows9X = true);//not entirerly sure that it works under NT - but worth a try!
bool DoEnableSMART(HANDLE hSMARTIOCTL, PSENDCMDINPARAMS pSCIP, PSENDCMDOUTPARAMS pSCOP, BYTE bDriveNum, PDWORD lpcbBytesReturned);
bool DoIDENTIFY(HANDLE hSMARTIOCTL, PSENDCMDINPARAMS pSCIP,PSENDCMDOUTPARAMS pSCOP, BYTE bIDCmd, BYTE bDriveNum, PDWORD lpcbBytesReturned);
void ChangeByteOrder(PCHAR szString, USHORT uscStrSize);
};
//smart.h
/****************************************************************************
* *
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY *
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE *
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR *
* PURPOSE. *
* *
* Copyright 1993-98 Microsoft Corporation. All Rights Reserved. *
* *
****************************************************************************/
/****************************************************************************
*
* PROGRAM: SMART.H
*
* PURPOSE: Structure definitions for an application that calls SMART Ioctls
*
****************************************************************************/
#ifndef SMARTIOCTL_INCLUDED
#define SMARTIOCTL_INCLUDED
// Miscellaneous
#define MAX_IDE_DRIVES 4 // Max number of drives assuming primary/secondary, master/slave topology
#define READ_ATTRIBUTE_BUFFER_SIZE 512
#define IDENTIFY_BUFFER_SIZE 512
#define READ_THRESHOLD_BUFFER_SIZE 512
//
// IOCTL commands
//
#define DFP_GET_VERSION 0x00074080
#define DFP_SEND_DRIVE_COMMAND 0x0007c084
#define DFP_RECEIVE_DRIVE_DATA 0x0007c088
//---------------------------------------------------------------------
// GETVERSIONOUTPARAMS contains the data returned from the
// Get Driver Version function.
//---------------------------------------------------------------------
typedef struct _GETVERSIONOUTPARAMS {
BYTE bVersion; // Binary driver version.
BYTE bRevision; // Binary driver revision.
BYTE bReserved; // Not used.
BYTE bIDEDeviceMap; // Bit map of IDE devices.
DWORD fCapabilities; // Bit mask of driver capabilities.
DWORD dwReserved[4]; // For future use.
} GETVERSIONOUTPARAMS, *PGETVERSIONOUTPARAMS, *LPGETVERSIONOUTPARAMS;
//
// Bits returned in the fCapabilities member of GETVERSIONOUTPARAMS
//
#define CAP_IDE_ID_FUNCTION 1 // ATA ID command supported
#define CAP_IDE_ATAPI_ID 2 // ATAPI ID command supported
#define CAP_IDE_EXECUTE_SMART_FUNCTION 4 // SMART commannds supported
//---------------------------------------------------------------------
// IDE registers
//---------------------------------------------------------------------
typedef struct _IDEREGS {
BYTE bFeaturesReg; // Used for specifying SMART "commands".
BYTE bSectorCountReg; // IDE sector count register
BYTE bSectorNumberReg; // IDE sector number register
BYTE bCylLowReg; // IDE low order cylinder value
BYTE bCylHighReg; // IDE high order cylinder value
BYTE bDriveHeadReg; // IDE drive/head register
BYTE bCommandReg; // Actual IDE command.
BYTE bReserved; // reserved for future use. Must be zero.
} IDEREGS, *PIDEREGS, *LPIDEREGS;
//---------------------------------------------------------------------
// SENDCMDINPARAMS contains the input parameters for the
// Send Command to Drive function.
//---------------------------------------------------------------------
typedef struct _SENDCMDINPARAMS {
DWORD cBufferSize; // Buffer size in bytes
IDEREGS irDriveRegs; // Structure with drive register values.
BYTE bDriveNumber; // Physical drive number to send
// command to (0,1,2,3).
BYTE bReserved[3]; // Reserved for future expansion.
DWORD dwReserved[4]; // For future use.
BYTE bBuffer[1]; // Input buffer.
} SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS;
//
// Valid values for the bCommandReg member of IDEREGS.
//
#define IDE_ATAPI_ID 0xA1 // Returns ID sector for ATAPI.
#define IDE_ID_FUNCTION 0xEC // Returns ID sector for ATA.
#define IDE_EXECUTE_SMART_FUNCTION 0xB0 // Performs SMART cmd.
// Requires valid bFeaturesReg,
// bCylLowReg, and bCylHighReg
//
// Cylinder register values required when issuing SMART command
//
#define SMART_CYL_LOW 0x4F
#define SMART_CYL_HI 0xC2
//---------------------------------------------------------------------
// Status returned from driver
//---------------------------------------------------------------------
typedef struct _DRIVERSTATUS {
BYTE bDriverError; // Error code from driver,
// or 0 if no error.
BYTE bIDEStatus; // Contents of IDE Error register.
// Only valid when bDriverError
// is SMART_IDE_ERROR.
BYTE bReserved[2]; // Reserved for future expansion.
DWORD dwReserved[2]; // Reserved for future expansion.
} DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS;
//
// bDriverError values
//
#define SMART_NO_ERROR 0 // No error
#define SMART_IDE_ERROR 1 // Error from IDE controller
#define SMART_INVALID_FLAG 2 // Invalid command flag
#define SMART_INVALID_COMMAND 3 // Invalid command byte
#define SMART_INVALID_BUFFER 4 // Bad buffer (null, invalid addr..)
#define SMART_INVALID_DRIVE 5 // Drive number not valid
#define SMART_INVALID_IOCTL 6 // Invalid IOCTL
#define SMART_ERROR_NO_MEM 7 // Could not lock user's buffer
#define SMART_INVALID_REGISTER 8 // Some IDE Register not valid
#define SMART_NOT_SUPPORTED 9 // Invalid cmd flag set
#define SMART_NO_IDE_DEVICE 10 // Cmd issued to device not present
// although drive number is valid
// 11-255 reserved
//---------------------------------------------------------------------
// Structure returned by SMART IOCTL for several commands
//---------------------------------------------------------------------
typedef struct _SENDCMDOUTPARAMS {
DWORD cBufferSize; // Size of bBuffer in bytes
DRIVERSTATUS DriverStatus; // Driver status structure.
char bBuffer[1]; // Buffer of arbitrary length in which to store the data read from the // drive.
} SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS;
//---------------------------------------------------------------------
// Feature register defines for SMART "sub commands"
//---------------------------------------------------------------------
#define SMART_READ_ATTRIBUTE_VALUES 0xD0 // ATA4: Renamed
// SMART READ DATA
#define SMART_READ_ATTRIBUTE_THRESHOLDS 0xD1 // Obsoleted in ATA4!
#define SMART_ENABLE_DISABLE_ATTRIBUTE_AUTOSAVE 0xD2
#define SMART_SAVE_ATTRIBUTE_VALUES 0xD3
#define SMART_EXECUTE_OFFLINE_IMMEDIATE 0xD4 // ATA4
// Vendor specific commands:
#define SMART_ENABLE_SMART_OPERATIONS 0xD8
#define SMART_DISABLE_SMART_OPERATIONS 0xD9
#define SMART_RETURN_SMART_STATUS 0xDA
#endif
//---------------------------------------------------------------------
// The following structure defines the structure of a Drive Attribute
//---------------------------------------------------------------------
typedef struct _DRIVEATTRIBUTE {
BYTE bAttrID; // Identifies which attribute
WORD wStatusFlags; // see bit definitions below
BYTE bAttrValue; // Current normalized value
BYTE bWorstValue; // How bad has it ever been?
BYTE bRawValue[6]; // Un-normalized value
BYTE bReserved; // ...
} DRIVEATTRIBUTE, *PDRIVEATTRIBUTE, *LPDRIVEATTRIBUTE;
//---------------------------------------------------------------------
// The following structure defines the structure of a Warranty Threshold
// Obsoleted in ATA4!
//---------------------------------------------------------------------
typedef struct _ATTRTHRESHOLD {
BYTE bAttrID; // Identifies which attribute
BYTE bWarrantyThreshold; // Triggering value
BYTE bReserved[10]; // ...
} ATTRTHRESHOLD, *PATTRTHRESHOLD, *LPATTRTHRESHOLD;
//---------------------------------------------------------------------
// The following struct defines the interesting part of the IDENTIFY
// buffer:
//---------------------------------------------------------------------
typedef struct _IDSECTOR {
USHORT wGenConfig;
USHORT wNumCyls;
USHORT wReserved;
USHORT wNumHeads;
USHORT wBytesPerTrack;
USHORT wBytesPerSector;
USHORT wSectorsPerTrack;
USHORT wVendorUnique[3];
CHAR szSerialNumber[20];
USHORT wBufferType;
USHORT wBufferSize;
USHORT wECCSize;
CHAR sFirmwareRev[8];
CHAR sModelNumber[40];
USHORT wMoreVendorUnique;
USHORT wDoubleWordIO;
USHORT wCapabilities;
USHORT wReserved1;
USHORT wPIOTiming;
USHORT wDMATiming;
USHORT wBS;
USHORT wNumCurrentCyls;
USHORT wNumCurrentHeads;
USHORT wNumCurrentSectorsPerTrack;
ULONG ulCurrentSectorCapacity;
USHORT wMultSectorStuff;
ULONG ulTotalAddressableSectors;
USHORT wSingleWordDMA;
USHORT wMultiWordDMA;
BYTE bReserved[128];
} IDSECTOR, *PIDSECTOR;
//---------------------------------------------------------------------
// Valid Attribute IDs
//---------------------------------------------------------------------
#define ATTR_INVALID 0
#define ATTR_READ_ERROR_RATE 1
#define ATTR_THROUGHPUT_PERF 2
#define ATTR_SPIN_UP_TIME 3
#define ATTR_START_STOP_COUNT 4
#define ATTR_REALLOC_SECTOR_COUNT 5
#define ATTR_READ_CHANNEL_MARGIN 6
#define ATTR_SEEK_ERROR_RATE 7
#define ATTR_SEEK_TIME_PERF 8
#define ATTR_POWER_ON_HRS_COUNT 9
#define ATTR_SPIN_RETRY_COUNT 10
#define ATTR_CALIBRATION_RETRY_COUNT 11
#define ATTR_POWER_CYCLE_COUNT 12
//---------------------------------------------------------------------
// Status Flags Values
//---------------------------------------------------------------------
#define PRE_FAILURE_WARRANTY 0x1
#define ON_LINE_COLLECTION 0x2
#define PERFORMANCE_ATTRIBUTE 0x4
#define ERROR_RATE_ATTRIBUTE 0x8
#define EVENT_COUNT_ATTRIBUTE 0x10
#define SELF_PRESERVING_ATTRIBUTE 0x20
#define NUM_ATTRIBUTE_STRUCTS 30
//uniquepcid.cpp
#pragma pack(1) // Required to ensure correct SMART IOCTL structure setup
#include "UniquePCID.h"
#include "nb30.h"
#include "winosver.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
// http://support.microsoft.com/support.../Q208/0/48.ASP
CUniquePCID::CUniquePCID()
{
UINT uiOldMode = SetErrorMode(SEM_FAILCRITICALERRORS);
//collect the drive volume info on construction, as it doesn't take long.
//the other calls are more likely to fail and/or take longer to calculate
if (!GetVolumeInformation("C:\\",0,0,&m_dwHD,0,0,0,0))
{
m_dwHD = 0;
}
SetErrorMode(uiOldMode);
}
CUniquePCID::~CUniquePCID()
{
}
bool CUniquePCID::CPUID(SPentiumIIISerialNumber & serialnumber) const
{
SPentiumIIISerialNumber * pSerialNumber = &serialnumber;
serialnumber.high = 0;
serialnumber.middle = 0;
serialnumber.low = 0;
BOOL bGotSerialNumber = FALSE;
BOOL bSupportsCpuID = FALSE;
__asm
{
//
// The CPUID instruction is supported only if bit 21 of
// EFLAGS can be changed.
//
pushfd
pop eax // get current EFLAGS
mov ecx, eax
xor eax, 200000h // toggle bit 21
push eax
popfd // set new EFLAGS
pushfd
pop eax // retrieve EFLAGS again
xor eax, ecx // did bit 21 change?
jz restore_eflags // no, CPUID not supported
mov dword ptr [bSupportsCpuID], TRUE
restore_eflags:
push ecx
popfd // restore original EFLAGS
cmp dword ptr [bSupportsCpuID], FALSE
jz exit_function
//
// CPUID uses the value in EAX to determine what info to
// return. When EAX is 0 on input, CPUID returns the
// highest value allowed for EAX. The return value _must_
// be >= 3, or the serial number is not supported.
//
mov eax, 0
cpuid
cmp eax, 3 // function "3" supported?
jl exit_function // no, there's no serial #
//
// Check bit 18 of the processor "feature flags" to
// determine if the processor serial number is available
// and enabled. The "feature flags" are returned in EDX
// after executing CPUID with EAX=1.
//
mov eax, 1
cpuid
test edx, 40000h // is bit 18 set?
jz exit_function // no, serial # disabled
// or not available
//
// 96-bit processor serial number is available:
// high 32 bits are in EAX after CPUID with EAX=1
// middle 32 bits are in EDX after CPUID with EAX=3
// low 32 bits are in ECX after CPUID with EAX=3
//
mov edi, dword ptr [pSerialNumber]
mov dword ptr [edi.high], eax
mov eax, 3
cpuid
mov dword ptr [edi.middle], edx
mov dword ptr [edi.low], ecx
mov dword ptr [bGotSerialNumber], TRUE
}
exit_function:
return bGotSerialNumber;
}
bool CUniquePCID::GetMACAddress(__int64 & iMACAddress)
{
bool bResult = false;
struct ASTAT
{
ADAPTER_STATUS adapt;
NAME_BUFFER NameBuff [30];
};
ASTAT Adapter;
//firstly enumerate all the network cards
//not good to just assume that one is there
NCB Ncb;
UCHAR uRetCode;
LANA_ENUM lenum;
memset( &Ncb, 0, sizeof(Ncb) );
Ncb.ncb_command = NCBENUM;
Ncb.ncb_buffer = (UCHAR *)&lenum;
Ncb.ncb_length = sizeof(lenum);
uRetCode = Netbios( &Ncb );
if (uRetCode == 0)
{
//reset the card - don't fret you won't lose network traffic!
memset( &Ncb, 0, sizeof(Ncb) );
Ncb.ncb_command = NCBRESET;
Ncb.ncb_lana_num = lenum.lana[0];
uRetCode = Netbios( &Ncb );
//getnet card status
memset( &Ncb, 0, sizeof (Ncb) );
Ncb.ncb_command = NCBASTAT;
Ncb.ncb_lana_num = lenum.lana[0];
strcpy( (char*)Ncb.ncb_callname, "* " );
(Ncb.ncb_buffer) = (PUCHAR)&Adapter;
Ncb.ncb_length = sizeof(Adapter);
uRetCode = Netbios( &Ncb );
if ( uRetCode == 0 )
{
for (int i = 0;i<6; i++)
{
__int64 iCurVal = Adapter.adapt.adapter_address[i];
iCurVal = iCurVal * pow(0x100 , 5-i);
iMACAddress += iCurVal;
}
bResult = true;
}
}
return bResult;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool CUniquePCID::GetIDESerialNo(CString & csSerialNumber,CString & csError)
{
bool bResult = false;
SENDCMDINPARAMS scip;
SENDCMDOUTPARAMS OutCmd;
BYTE byDfpDriveMap = 0;
BYTE byIDCmd;
DWORD cbBytesReturned;
BYTE IdOutCmd[sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1];
CWinOSVer osversion;
bool bWindows9x = !osversion.WinNT();
HANDLE hSMARTIOCtrl = OpenSMART(csError,bWindows9x);
#ifdef _DEBUG
if (!hSMARTIOCtrl)
{
AfxMessageBox(csError,MB_OK);
}
#endif
if (hSMARTIOCtrl)
{
GETVERSIONOUTPARAMS VersionParams;
memset(&VersionParams, 0, sizeof(VersionParams));
if ( DeviceIoControl(hSMARTIOCtrl, DFP_GET_VERSION,
NULL,
0,
&VersionParams,
sizeof(VersionParams),
&cbBytesReturned, NULL) )
{
for (BYTE i = 0; i < MAX_IDE_DRIVES; i++)
{
//
// If there is a IDE device at number "i" issue commands
// to the device.
if (VersionParams.bIDEDeviceMap >> i & 1)
{
// Try to enable SMART so we can tell if a drive supports it.
// Ignore ATAPI devices.
//
if (!(VersionParams.bIDEDeviceMap >> i & 0x10))
{
memset(&scip, 0, sizeof(scip));
memset(&OutCmd, 0, sizeof(OutCmd));
if (DoEnableSMART(hSMARTIOCtrl, &scip, &OutCmd, i, &cbBytesReturned))
{
// Mark the drive as SMART enabled
byDfpDriveMap |= (1 << i);
//Not needed in this app - but may be usefull to have a list of drives later on...
}
}
}
// Now, get the ID sector for all IDE devices in the system.
// If the device is ATAPI use the IDE_ATAPI_ID command,
// otherwise use the IDE_ID_FUNCTION command.
byIDCmd = (VersionParams.bIDEDeviceMap >> i & 0x10) ? IDE_ATAPI_ID : IDE_ID_FUNCTION;
memset(&scip, 0, sizeof(scip));
memset(IdOutCmd, 0,sizeof(IdOutCmd) );
if ( DoIDENTIFY(hSMARTIOCtrl, &scip, (PSENDCMDOUTPARAMS)&IdOutCmd, byIDCmd, i, &cbBytesReturned))
{
PIDSECTOR pIDS = (PIDSECTOR) ((PSENDCMDOUTPARAMS)IdOutCmd)->bBuffer;
//change word array to byte array
ChangeByteOrder(pIDS->szSerialNumber,sizeof pIDS->szSerialNumber);
char szTempSerialNo[41];
memset(szTempSerialNo,0, sizeof(szTempSerialNo));
strncpy(szTempSerialNo,pIDS->szSerialNumber,sizeof(pIDS->szSerialNumber));
csSerialNumber = szTempSerialNo;
bResult = true;
}
}
}
else
{
csError = "DFP_GET_VERSION failed";
}
}
if (hSMARTIOCtrl)
{
CloseHandle(hSMARTIOCtrl);
hSMARTIOCtrl = 0;
}
return bResult;
}
void CUniquePCID::ChangeByteOrder(PCHAR szString, USHORT uscStrSize)
{
USHORT i;
CHAR temp;
for (i = 0; i < uscStrSize; i+=2)
{
temp = szString[i];
szString[i] = szString[i+1];
szString[i+1] = temp;
}
}
HANDLE CUniquePCID::OpenSMART(CString & csError,bool bWindows9X)
{
HANDLE hSMARTIOCTL = 0;
if (bWindows9X)
{
hSMARTIOCTL = CreateFile("\\\\.\\SMARTVSD", 0,0,0,CREATE_NEW, 0, 0);
// Version Windows 95 OSR2, Windows 98
if (hSMARTIOCTL == INVALID_HANDLE_VALUE)
{
csError.Format("Unable to open SMARTVSD, error code: 0x%lX\n", GetLastError());
hSMARTIOCTL = 0;
}
}
else
{
// Windows NT, Windows 2000
hSMARTIOCTL = CreateFile("\\\\.\\PhysicalDrive0",GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,
OPEN_EXISTING,0,NULL);
if ( hSMARTIOCTL == INVALID_HANDLE_VALUE)
{
csError.Format("Unable to open physical drive, error code: 0x%lX\n", GetLastError());
hSMARTIOCTL = 0;
}
}
return hSMARTIOCTL;
}
/****************************************************************************
*
* DoEnableSMART
*
* FUNCTION: Send a SMART_ENABLE_SMART_OPERATIONS command to the drive
* bDriveNum = 0-3
*
****************************************************************************/
bool CUniquePCID:oEnableSMART(HANDLE hSMARTIOCTL, PSENDCMDINPARAMS pSCIP, PSENDCMDOUTPARAMS pSCOP, BYTE bDriveNum, PDWORD lpcbBytesReturned)
{
//
// Set up data structures for Enable SMART Command.
//
pSCIP->cBufferSize = 0;
pSCIP->irDriveRegs.bFeaturesReg = SMART_ENABLE_SMART_OPERATIONS;
pSCIP->irDriveRegs.bSectorCountReg = 1;
pSCIP->irDriveRegs.bSectorNumberReg = 1;
pSCIP->irDriveRegs.bCylLowReg = SMART_CYL_LOW;
pSCIP->irDriveRegs.bCylHighReg = SMART_CYL_HI;
//
// Compute the drive number.
//
pSCIP->irDriveRegs.bDriveHeadReg = 0xA0 | ((bDriveNum & 1) << 4);
pSCIP->irDriveRegs.bCommandReg = IDE_EXECUTE_SMART_FUNCTION;
pSCIP->bDriveNumber = bDriveNum;
bool bResult = ( DeviceIoControl(hSMARTIOCTL, DFP_SEND_DRIVE_COMMAND,
(LPVOID)pSCIP, sizeof(SENDCMDINPARAMS) - 1,
(LPVOID)pSCOP, sizeof(SENDCMDOUTPARAMS) - 1,
lpcbBytesReturned, NULL) !=0 );
return bResult;
}
/****************************************************************************
*
* DoIDENTIFY
*
* FUNCTION: Send an IDENTIFY command to the drive
* bDriveNum = 0-3
* bIDCmd = IDE_ID_FUNCTION or IDE_ATAPI_ID
*
****************************************************************************/
bool CUniquePCID:oIDENTIFY(HANDLE hSMARTIOCTL, PSENDCMDINPARAMS pSCIP,PSENDCMDOUTPARAMS pSCOP, BYTE bIDCmd, BYTE bDriveNum, PDWORD lpcbBytesReturned)
{
//
// Set up data structures for IDENTIFY command.
//
pSCIP->cBufferSize = IDENTIFY_BUFFER_SIZE;
pSCIP->irDriveRegs.bFeaturesReg = 0;
pSCIP->irDriveRegs.bSectorCountReg = 1;
pSCIP->irDriveRegs.bSectorNumberReg = 1;
pSCIP->irDriveRegs.bCylLowReg = 0;
pSCIP->irDriveRegs.bCylHighReg = 0;
//
// Compute the drive number.
//
pSCIP->irDriveRegs.bDriveHeadReg = 0xA0 | ((bDriveNum & 1) << 4);
//
// The command can either be IDE identify or ATAPI identify.
//
pSCIP->irDriveRegs.bCommandReg = bIDCmd;
pSCIP->bDriveNumber = bDriveNum;
pSCIP->cBufferSize = IDENTIFY_BUFFER_SIZE;
bool bResult = ( DeviceIoControl(hSMARTIOCTL, DFP_RECEIVE_DRIVE_DATA,
(LPVOID)pSCIP, sizeof(SENDCMDINPARAMS) - 1,
(LPVOID)pSCOP, sizeof(SENDCMDOUTPARAMS) + IDENTIFY_BUFFER_SIZE - 1,
lpcbBytesReturned, NULL) !=0);
return bResult;
}
"Take a chance while you still got a choice"