#include "device.h" // windows #include #include // standard #include // other #include "nuiouser.h" // new const GUID GUID_DEVINTERFACE_RT2560 = {0x470091b3, 0x70d8, 0x4674, 0xb5, 0x91, 0x82, 0xda, 0x5d, 0x83, 0x22, 0x67}; #define NUM_RXD 32 // max. is 64 (see WaitForMultipleObjects) #define MAC_ADDR_LEN 6 typedef struct _READFILE_DATA { OVERLAPPED Overlapped; UCHAR BufferData[4096]; ULONG BufferSize; BOOLEAN Busy; } READFILE_DATA, *PREADFILE_DATA; typedef struct _THREAD_READFILE_DATA { HANDLE hThread; BOOL Terminated; PDEVICE_INFO DeviceInfo; } THREAD_READFILE_DATA, *PTHREAD_READFILE_DATA; THREAD_READFILE_DATA ThreadReadFileData; //------------------------------------------------------------------------------ HANDLE RT2560_Open( CHAR *DevicePath) { return CreateFile( DevicePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); } //------------------------------------------------------------------------------ BOOL RT2560_Close( HANDLE hDevice) { return CloseHandle( hDevice); } //------------------------------------------------------------------------------ BOOL GetDevicePropertyValue( LPSTR DevicePath, DWORD dwProperty, LPSTR lpOutput) { HDEVINFO hDevInfo; SP_DEVICE_INTERFACE_DATA DeviceInterfaceData; SP_DEVINFO_DATA DeviceInfoData; DWORD dwRegType; BOOL ret = FALSE, bOK; // create device info list hDevInfo = SetupDiCreateDeviceInfoList( NULL, NULL); if (hDevInfo != INVALID_HANDLE_VALUE) { // get device interface data ZeroMemory( &DeviceInterfaceData, sizeof( DeviceInterfaceData)); DeviceInterfaceData.cbSize = sizeof( DeviceInterfaceData); bOK = SetupDiOpenDeviceInterface( hDevInfo, DevicePath, 0, &DeviceInterfaceData); if (bOK) { // get device info data ZeroMemory( &DeviceInfoData, sizeof( DeviceInfoData)); DeviceInfoData.cbSize = sizeof( DeviceInfoData); bOK = SetupDiGetDeviceInterfaceDetail( hDevInfo, &DeviceInterfaceData, NULL, 0, NULL, &DeviceInfoData); if ((!bOK) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { // get property bOK = SetupDiGetDeviceRegistryProperty( hDevInfo, &DeviceInfoData, dwProperty, &dwRegType, (BYTE*)lpOutput, MAX_PATH, NULL); if (bOK) { // ok ret = TRUE; } } } // destroy device info list SetupDiDestroyDeviceInfoList( hDevInfo); } // exit return ret; } //------------------------------------------------------------------------------ BOOL DeviceFind( PDEVICE_INFO lpDeviceInfo, DWORD dwIndex) { HDEVINFO hDevInfo; SP_DEVICE_INTERFACE_DATA DeviceInterfaceData; PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData = NULL; ULONG DeviceInterfaceDetailDataSize, i; BOOL bOK, ret = FALSE; GUID InterfaceGuid; // InterfaceGuid = GUID_DEVINTERFACE_RT2560; // get device info handle hDevInfo = SetupDiGetClassDevs( &InterfaceGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (hDevInfo != INVALID_HANDLE_VALUE) { // process all devices i = 0; while (!ret) { // enumerate devices of a specific interface class ZeroMemory( &DeviceInterfaceData, sizeof( DeviceInterfaceData)); DeviceInterfaceData.cbSize = sizeof( DeviceInterfaceData); bOK = SetupDiEnumDeviceInterfaces( hDevInfo, 0, &InterfaceGuid, i, &DeviceInterfaceData); if (!bOK) break; // find out required length of the buffer bOK = SetupDiGetDeviceInterfaceDetail( hDevInfo, &DeviceInterfaceData, NULL, 0, &DeviceInterfaceDetailDataSize, NULL); if ((!bOK) && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { // allocate detail data DeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, DeviceInterfaceDetailDataSize); DeviceInterfaceDetailData->cbSize = sizeof( SP_DEVICE_INTERFACE_DETAIL_DATA); // get detail data bOK = SetupDiGetDeviceInterfaceDetail( hDevInfo, &DeviceInterfaceData, DeviceInterfaceDetailData, DeviceInterfaceDetailDataSize, NULL, NULL); if (bOK) { // check device index if (i == dwIndex) { // get device description bOK = GetDevicePropertyValue( DeviceInterfaceDetailData->DevicePath, SPDRP_DEVICEDESC, lpDeviceInfo->Description); if (!bOK) strcpy( lpDeviceInfo->Description, "unknown"); // get device location bOK = GetDevicePropertyValue( DeviceInterfaceDetailData->DevicePath, SPDRP_LOCATION_INFORMATION, lpDeviceInfo->Location); if (!bOK) strcpy( lpDeviceInfo->Location, "unknown"); // get device hardware id bOK = GetDevicePropertyValue( DeviceInterfaceDetailData->DevicePath, SPDRP_HARDWAREID, lpDeviceInfo->HardwareID); if (!bOK) strcpy( lpDeviceInfo->HardwareID, "unknown"); // copy device info & exit strcpy( lpDeviceInfo->Path, DeviceInterfaceDetailData->DevicePath); // other lpDeviceInfo->Index = dwIndex; // exit ret = TRUE; } } // free detail data if (DeviceInterfaceDetailData) { HeapFree( GetProcessHeap(), 0, DeviceInterfaceDetailData); DeviceInterfaceDetailData = NULL; } } // next device i++; } // free device info handle SetupDiDestroyDeviceInfoList( hDevInfo); } // exit return ret; } //------------------------------------------------------------------------------ DWORD WINAPI ThreadReadFileProc( LPVOID lpParameter) { PTHREAD_READFILE_DATA pThreadData = (PTHREAD_READFILE_DATA)lpParameter; READFILE_DATA ReadFileData[NUM_RXD]; HANDLE EventArray[NUM_RXD]; DWORD i, Status, NumberOfBytesRead; BOOL bOK; // init readfile data for (i=0;iTerminated) { // post read requests for (i=0;iDeviceInfo->Handle, ReadFileData[i].BufferData, ReadFileData[i].BufferSize, NULL, &ReadFileData[i].Overlapped); if ((bOK) || (GetLastError() != ERROR_IO_PENDING)) printf( "ReadFile() failed\n"); ReadFileData[i].Busy = TRUE; //((!bOK) && (GetLastError() == ERROR_IO_PENDING)); } } // wait until one or more requests complete Status = WaitForMultipleObjects( NUM_RXD, EventArray, FALSE, 1000); if ((Status >= (WAIT_OBJECT_0 + 0)) && (Status < (WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS))) { i = Status - WAIT_OBJECT_0; bOK = GetOverlappedResult( pThreadData->DeviceInfo->Handle, &ReadFileData[i].Overlapped, &NumberOfBytesRead, FALSE); if (!bOK) printf( "GetOverlappedResult() error 0x%08X\n", GetLastError()); /* { LARGE_INTEGER freq, host_time_drv, host_time_app; ULONG temp; PAVS_WLAN_HEADER_V1 pAvsHdr; pAvsHdr = (PAVS_WLAN_HEADER_V1)&ReadFileData[i].BufferData[0]; QueryPerformanceFrequency( &freq); host_time_drv.QuadPart = _byteswap_uint64( pAvsHdr->hosttime); QueryPerformanceCounter( &host_time_app); temp = (ULONG)((host_time_app.QuadPart - host_time_drv.QuadPart) * 1000 * 1000 / freq.QuadPart); printf( "rx delay = %d µs\n", temp); } */ //printf( "ReadFile() number %02d completed, size is %04d byte(s)\n", i, NumberOfBytesRead); //printf( "(%d)", NumberOfBytesRead); // callback function if (pThreadData->DeviceInfo->ReadPacketCallback) pThreadData->DeviceInfo->ReadPacketCallback( pThreadData->DeviceInfo, ReadFileData[i].BufferData, NumberOfBytesRead); ReadFileData[i].Busy = FALSE; } } // cancel pending reads (this function only cancels operations issued by the calling thread for the specified file handle) bOK = CancelIo( pThreadData->DeviceInfo->Handle); if (!bOK) printf( "CancelIo() error 0x%08X\n", GetLastError()); // clean up readfile data for (i=0;iHandle = RT2560_Open( lpDeviceInfo->Path); if (lpDeviceInfo->Handle == INVALID_HANDLE_VALUE) return FALSE; // exit return TRUE; } //------------------------------------------------------------------------------ BOOL DeviceClose( PDEVICE_INFO lpDeviceInfo) { // close device if (lpDeviceInfo->Handle != INVALID_HANDLE_VALUE) { RT2560_Close( lpDeviceInfo->Handle); lpDeviceInfo->Handle = INVALID_HANDLE_VALUE; } // exit return TRUE; } //------------------------------------------------------------------------------ BOOL DeviceThreadReceiveStart( PDEVICE_INFO lpDeviceInfo) { BOOL bOK; DWORD ThreadId; // create readfile thread ZeroMemory( &ThreadReadFileData, sizeof( ThreadReadFileData)); ThreadReadFileData.DeviceInfo = lpDeviceInfo; ThreadReadFileData.hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)&ThreadReadFileProc, &ThreadReadFileData, 0, &ThreadId); // set thread priority bOK = SetThreadPriority( ThreadReadFileData.hThread, THREAD_PRIORITY_HIGHEST); if (!bOK) printf( "SetThreadPriority() error 0x%08X\n", GetLastError()); // exit return TRUE; } //------------------------------------------------------------------------------ BOOL DeviceThreadReceiveStop( PDEVICE_INFO lpDeviceInfo) { DWORD result; // free readfile thread if (ThreadReadFileData.hThread != INVALID_HANDLE_VALUE) { // terminate ThreadReadFileData.Terminated = TRUE; // wait result = WaitForSingleObject( ThreadReadFileData.hThread, 5 * 1000); if (result == WAIT_TIMEOUT) TerminateThread( ThreadReadFileData.hThread, 0); // close CloseHandle( ThreadReadFileData.hThread); ThreadReadFileData.hThread = INVALID_HANDLE_VALUE; } // exit return TRUE; } //------------------------------------------------------------------------------ // OBSOLETE, SEE READPACKET CALLBACK FUNCTION //------------------------------------------------------------------------------ /* DWORD DeviceReadPacket( PDEVICE_INFO lpDeviceInfo, PVOID lpBufferData, ULONG BufferLength, ULONG dwTimeout) { DWORD NumberOfBytesRead; OVERLAPPED Overlapped; BOOL bOK; // init overlapped RtlZeroMemory( &Overlapped, sizeof( Overlapped)); Overlapped.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL); // attempt an asynchronous read operation bOK = ReadFile( lpDeviceInfo->Handle, lpBufferData, BufferLength, &NumberOfBytesRead, &Overlapped); if ((!bOK) && (GetLastError() == ERROR_IO_PENDING)) { // wait for the async. operation to complete bOK = GetOverlappedResult( lpDeviceInfo->Handle, &Overlapped, &NumberOfBytesRead, TRUE); } // clean up overlapped CloseHandle( Overlapped.hEvent); // exit if (bOK) return NumberOfBytesRead; else return 0; } */ //------------------------------------------------------------------------------ BOOL DeviceWritePacket( PDEVICE_INFO lpDeviceInfo, PVOID lpBufferData, ULONG BufferLength) { DWORD NumberOfBytesWritten; OVERLAPPED Overlapped; BOOL bOK; // init overlapped RtlZeroMemory( &Overlapped, sizeof( Overlapped)); Overlapped.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL); // attempt an asynchronous write operation bOK = WriteFile( lpDeviceInfo->Handle, lpBufferData, BufferLength, &NumberOfBytesWritten, &Overlapped); if ((!bOK) && (GetLastError() == ERROR_IO_PENDING)) { // wait for the async. operation to complete bOK = GetOverlappedResult( lpDeviceInfo->Handle, &Overlapped, &NumberOfBytesWritten, TRUE); if (bOK) { if (NumberOfBytesWritten != BufferLength) bOK = FALSE; } } // clean up overlapped CloseHandle( Overlapped.hEvent); // exit return bOK; } //------------------------------------------------------------------------------ BOOL DeviceSetReadPacketCallback( PDEVICE_INFO lpDeviceInfo, FN_DeviceReadPacket ReadPacketCallback) { lpDeviceInfo->ReadPacketCallback = ReadPacketCallback; return true; } //------------------------------------------------------------------------------ BOOL DeviceQueryValue( PDEVICE_INFO lpDeviceInfo, NDIS_OID Oid, PULONG lpValue) { DWORD BytesReturned; BOOL bOK; NDISPROT_QUERY_OID QueryOid; // prepare query buffer ZeroMemory( &QueryOid, sizeof( QueryOid)); QueryOid.Oid = Oid; // device io control bOK = DeviceIoControl( lpDeviceInfo->Handle, IOCTL_NDISPROT_QUERY_OID_VALUE, (LPVOID)&QueryOid, sizeof( QueryOid), (LPVOID)&QueryOid, sizeof( QueryOid), &BytesReturned, NULL); if (!bOK) printf( "DeviceGetField: DeviceIoControl() error %x\n", GetLastError()); // data if (bOK) *lpValue = *(PULONG)&QueryOid.Data[0]; // exit return bOK; } //------------------------------------------------------------------------------ ULONG wifi_channel_to_frequency( UCHAR channel) { if (channel >= 14) return 2484 + (channel - 14) * 5; if (channel >= 1) return 2412 + (channel - 1) * 5; return 0; } //------------------------------------------------------------------------------ UCHAR wifi_frequency_to_channel( ULONG frequency) { if (frequency >= 2484) return (UCHAR)(((frequency - 2484) / 5) + 14); if (frequency >= 2412) return (UCHAR)(((frequency - 2412) / 5) + 1); return 0; } //------------------------------------------------------------------------------ BOOL DeviceGetChannel( PDEVICE_INFO lpDeviceInfo, PUCHAR lpChannel) { DWORD BytesReturned; BOOL bOK; UCHAR Buffer[sizeof(NDISPROT_QUERY_OID) - sizeof(ULONG) + sizeof(NDIS_802_11_CONFIGURATION)]; PNDISPROT_QUERY_OID pQueryOid; PNDIS_802_11_CONFIGURATION pWiFiConfig; // prepare buffer ZeroMemory( Buffer, sizeof( Buffer)); pQueryOid = (PNDISPROT_QUERY_OID)&Buffer[0]; pQueryOid->Oid = OID_802_11_CONFIGURATION; // device io control bOK = DeviceIoControl( lpDeviceInfo->Handle, IOCTL_NDISPROT_QUERY_OID_VALUE, (LPVOID)&Buffer[0], sizeof( Buffer), (LPVOID)&Buffer[0], sizeof( Buffer), &BytesReturned, NULL); if (!bOK) printf( "DeviceGetChannel: DeviceIoControl() error %x\n", GetLastError()); // data if (bOK) { pWiFiConfig = (PNDIS_802_11_CONFIGURATION)&pQueryOid->Data[0]; *lpChannel = wifi_frequency_to_channel( pWiFiConfig->DSConfig / 1000); } // exit return bOK; } //------------------------------------------------------------------------------ BOOL DeviceSetChannel( PDEVICE_INFO lpDeviceInfo, UCHAR Channel) { DWORD BytesReturned; BOOL bOK; UCHAR Buffer[sizeof(NDISPROT_QUERY_OID) - sizeof(ULONG) + sizeof(NDIS_802_11_CONFIGURATION)]; PNDISPROT_QUERY_OID pQueryOid; PNDIS_802_11_CONFIGURATION pWiFiConfig; // prepare buffer ZeroMemory( Buffer, sizeof( Buffer)); pQueryOid = (PNDISPROT_QUERY_OID)&Buffer[0]; pQueryOid->Oid = OID_802_11_CONFIGURATION; pWiFiConfig = (PNDIS_802_11_CONFIGURATION)&pQueryOid->Data[0]; pWiFiConfig->Length = sizeof( NDIS_802_11_CONFIGURATION); pWiFiConfig->DSConfig = wifi_channel_to_frequency( Channel) * 1000; // device io control bOK = DeviceIoControl( lpDeviceInfo->Handle, IOCTL_NDISPROT_SET_OID_VALUE, (LPVOID)&Buffer[0], sizeof( Buffer), NULL, 0, &BytesReturned, NULL); if (!bOK) printf( "DeviceSetChannel: DeviceIoControl() error %x\n", GetLastError()); // exit return bOK; } //------------------------------------------------------------------------------ BOOL DeviceSetPacketFilter( PDEVICE_INFO lpDeviceInfo, ULONG PacketFilter) { DWORD BytesReturned; BOOL bOK; NDISPROT_SET_OID Buffer; // prepare buffer ZeroMemory( &Buffer, sizeof( Buffer)); Buffer.Oid = OID_GEN_CURRENT_PACKET_FILTER; memcpy( &Buffer.Data[0], &PacketFilter, sizeof( ULONG)); // device io control bOK = (BOOL)DeviceIoControl( lpDeviceInfo->Handle, IOCTL_NDISPROT_SET_OID_VALUE, (LPVOID)&Buffer, sizeof( Buffer), NULL, 0, &BytesReturned, NULL); if (!bOK) printf( "DeviceSetPacketFilter: DeviceIoControl() error %x\n", GetLastError()); // exit return bOK; } //------------------------------------------------------------------------------ BOOL DeviceGetMacAddress( PDEVICE_INFO lpDeviceInfo, PUCHAR lpMacAddress) { DWORD BytesReturned; BOOL bOK; UCHAR Buffer[sizeof( NDISPROT_QUERY_OID) - sizeof( ULONG) + MAC_ADDR_LEN]; PNDISPROT_QUERY_OID pQueryOid; // prepare buffer ZeroMemory( &Buffer[0], sizeof( Buffer)); pQueryOid = (PNDISPROT_QUERY_OID)&Buffer[0]; pQueryOid->Oid = OID_802_3_CURRENT_ADDRESS; // device io control bOK = DeviceIoControl( lpDeviceInfo->Handle, IOCTL_NDISPROT_QUERY_OID_VALUE, (LPVOID)&Buffer[0], sizeof( Buffer), (LPVOID)&Buffer[0], sizeof( Buffer), &BytesReturned, NULL); if (!bOK) printf( "DeviceGetMacAddress: DeviceIoControl() error %x\n", GetLastError()); // data if (bOK) memcpy( lpMacAddress, &pQueryOid->Data[0], MAC_ADDR_LEN); // exit return (bOK); } //------------------------------------------------------------------------------ // NOT SUPPORTED BY NDIS, THIS OID IS READ-ONLY //------------------------------------------------------------------------------ BOOL DeviceSetMacAddress( PDEVICE_INFO lpDeviceInfo, PUCHAR lpMacAddress) { DWORD BytesReturned; BOOL bOK; UCHAR Buffer[sizeof( NDISPROT_SET_OID) - sizeof( ULONG) + MAC_ADDR_LEN]; PNDISPROT_SET_OID pSetOid; // prepare buffer ZeroMemory( &Buffer[0], sizeof( Buffer)); pSetOid = (PNDISPROT_SET_OID)&Buffer[0]; pSetOid->Oid = OID_802_3_CURRENT_ADDRESS; memcpy( &pSetOid->Data[0], lpMacAddress, MAC_ADDR_LEN); // device io control bOK = DeviceIoControl( lpDeviceInfo->Handle, IOCTL_NDISPROT_SET_OID_VALUE, (LPVOID)&Buffer[0], sizeof( Buffer), NULL, 0, &BytesReturned, NULL); if (!bOK) printf( "DeviceSetMacAddress: DeviceIoControl() error %x\n", GetLastError()); // exit return (bOK); } //------------------------------------------------------------------------------ BOOL DeviceGetStatistics( PDEVICE_INFO lpDeviceInfo, PDEVICE_STATISTICS lpDeviceStats) { DWORD BytesReturned; BOOL bOK; UCHAR Buffer[sizeof(NDISPROT_QUERY_OID) - sizeof(ULONG) + sizeof(NDIS_802_11_STATISTICS)]; PNDISPROT_QUERY_OID pQueryOid; PNDIS_802_11_STATISTICS pWiFiStats; // prepare buffer ZeroMemory( Buffer, sizeof( Buffer)); pQueryOid = (PNDISPROT_QUERY_OID)&Buffer[0]; pQueryOid->Oid = OID_802_11_STATISTICS; // device io control bOK = DeviceIoControl( lpDeviceInfo->Handle, IOCTL_NDISPROT_QUERY_OID_VALUE, (LPVOID)&Buffer[0], sizeof( Buffer), (LPVOID)&Buffer[0], sizeof( Buffer), &BytesReturned, NULL); if (!bOK) printf( "DeviceDumpStatistics: DeviceIoControl() error %x\n", GetLastError()); // data if (bOK) { memset( lpDeviceStats, 0, sizeof( DEVICE_STATISTICS)); pWiFiStats = (PNDIS_802_11_STATISTICS)&pQueryOid->Data[0]; lpDeviceStats->FCSErrorCount = pWiFiStats->FCSErrorCount; DeviceQueryValue( lpDeviceInfo, OID_GEN_RCV_OK, &lpDeviceStats->ReceivedSuccessCount); DeviceQueryValue( lpDeviceInfo, OID_GEN_XMIT_OK, &lpDeviceStats->TransmittedSuccessCount); } // exit return bOK; } //------------------------------------------------------------------------------ BOOL RegistryReadString( HKEY hKey, LPSTR lpSubKey, LPSTR lpValueName, LPSTR lpOutput) { HKEY Key; DWORD err, cbData; BYTE Data[MAX_PATH]; BOOL bOK = false; // open key err = RegOpenKeyEx( hKey, lpSubKey, 0, KEY_READ, &Key); if (err == ERROR_SUCCESS) { // query value cbData = sizeof( Data); err = RegQueryValueEx( Key, lpValueName, NULL, NULL, Data, &cbData); if (err == ERROR_SUCCESS) { // copy string strcpy( lpOutput, (LPSTR)Data); bOK = true; } // close key RegCloseKey( Key); } // exit return bOK; } //------------------------------------------------------------------------------ BOOL GetFileVersion( LPSTR FileName, PUSHORT lpVersion) { DWORD Handle, InfoSize; PBYTE pData; VS_FIXEDFILEINFO *FixedFileInfo; UINT Len; BOOL bOK = false; // get file version info size InfoSize = GetFileVersionInfoSize( FileName, &Handle); if (InfoSize != 0) { // alloc pData = (PBYTE)malloc( InfoSize); // get file version info bOK = GetFileVersionInfo( FileName, Handle, InfoSize, pData); if (bOK) { // get fixed file info bOK = VerQueryValue( pData, "\\", (LPVOID*)&FixedFileInfo, &Len); if (bOK) { // copy file version lpVersion[0] = (USHORT)((FixedFileInfo->dwFileVersionMS >> 16) & 0xFFFF); lpVersion[1] = (USHORT)((FixedFileInfo->dwFileVersionMS >> 0) & 0xFFFF); lpVersion[2] = (USHORT)((FixedFileInfo->dwFileVersionLS >> 16) & 0xFFFF); lpVersion[3] = (USHORT)((FixedFileInfo->dwFileVersionLS >> 0) & 0xFFFF); bOK = true; } } // free free( pData); } // exit return bOK; } //------------------------------------------------------------------------------ BOOL DeviceGetDriverVersion( PDEVICE_INFO lpDeviceInfo, PUSHORT lpVersion) { BOOL bOK; CHAR ServiceName[MAX_PATH], RegKey[MAX_PATH], ImagePath[MAX_PATH], FileName[MAX_PATH], PathWindows[MAX_PATH]; BYTE pos; // get service name bOK = GetDevicePropertyValue( lpDeviceInfo->Path, SPDRP_SERVICE, ServiceName); if (!bOK) return false; // get driver image path sprintf( RegKey, "SYSTEM\\CurrentControlSet\\Services\\%s", ServiceName); bOK = RegistryReadString( HKEY_LOCAL_MACHINE, RegKey, "ImagePath", ImagePath); if (!bOK) return false; // get windows directory if (GetWindowsDirectory( PathWindows, sizeof( PathWindows)) == 0) return false; // make filename if (strnicmp( ImagePath, "%SystemRoot%", 12) == 0) pos = 13; else pos = 0; sprintf( FileName, "%s\\%s", PathWindows, ImagePath + pos); // get file version bOK = GetFileVersion( FileName, lpVersion); if (!bOK) return false; // exit return true; }