├── README └── src └── ArduScan.cpp /README: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ArduScan.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * ArduScan 3 | * Sept 2011 by Bill Westfield ("WestfW") 4 | * 5 | * Scan a windows system and try to figure out which COM ports 6 | * look like they might be Arduinos. 7 | * 8 | * This is based on the article here: 9 | * http://www.codeproject.com/KB/system/serial_portsenum_fifo.aspx 10 | * by Vladimir Afanasyev, which explains how to use the device manager 11 | * API to enumerate all the COM ports on a system, and 12 | * these forum entries: 13 | * http://www.microchip.com/forums/tm.aspx?high=&m=559736&mpage=1#560699 14 | * http://www.microchip.com/forums/tm.aspx?high=&m=364903&mpage=1#365029 15 | * Which explain how to get the USB Vendor/Product info from the same api. 16 | * 17 | * Currently this is set up to run in a CMD or other shell window, and 18 | * writes its findings to stdout. 19 | * 20 | * 21 | * It should compile fine under cygin, including mingw versions: 22 | * 23 | * g++ -mno-cygwin ArduScan.cpp -lsetupapi -o ArduScan.exe 24 | * 25 | * 26 | * Vladimir's code is distributed according to the terms of "The Code 27 | * Project Open License (CPOL)" http://www.codeproject.com/info/cpol10.aspx 28 | * and Bill thinks that sounds fine, and doesn't add any terms for his own code. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | /* 36 | * USB Vendor IDs that are somewhat likely to be Arduinos. 37 | */ 38 | 39 | #define VENDOR_FTDI 0x403 40 | #define VENDOR_ARDUINO 0x2341 41 | 42 | /* 43 | * Vladamir's code for enumerating COM ports. Slightly modifed. 44 | */ 45 | #define MAX_NAME_PORTS 7 46 | #define RegDisposition_OpenExisting (0x00000001) // open key only if exists 47 | 48 | #define CM_REGISTRY_HARDWARE (0x00000000) 49 | 50 | typedef DWORD WINAPI 51 | (* CM_Open_DevNode_Key)(DWORD, DWORD, DWORD, DWORD, ::PHKEY, DWORD); 52 | 53 | HANDLE BeginEnumeratePorts(VOID) 54 | { 55 | BOOL guidTest=FALSE; 56 | DWORD RequiredSize=0; 57 | HDEVINFO DeviceInfoSet; 58 | char* buf; 59 | 60 | guidTest=SetupDiClassGuidsFromNameA("Ports",(LPGUID)0,0,&RequiredSize); 61 | if(RequiredSize < 1) 62 | return (HANDLE) -1; 63 | 64 | buf=(char *)malloc(RequiredSize*sizeof(GUID)); 65 | 66 | guidTest=SetupDiClassGuidsFromNameA( 67 | "Ports",(_GUID *)buf,RequiredSize*sizeof(GUID),&RequiredSize); 68 | 69 | if(!guidTest) 70 | return (HANDLE) -1; 71 | 72 | 73 | DeviceInfoSet=SetupDiGetClassDevs( 74 | (_GUID*)buf,NULL,NULL,DIGCF_PRESENT); 75 | free(buf); 76 | 77 | if(DeviceInfoSet == INVALID_HANDLE_VALUE) 78 | return (HANDLE) -1; 79 | 80 | return DeviceInfoSet; 81 | } 82 | 83 | BOOL EnumeratePortsNext(HANDLE DeviceInfoSet, LPTSTR lpBuffer, int *index) 84 | { 85 | static CM_Open_DevNode_Key OpenDevNodeKey=NULL; 86 | static HINSTANCE CfgMan; 87 | 88 | int res1; 89 | char DevName[MAX_NAME_PORTS]={0}; 90 | static int numDev=0; 91 | int numport; 92 | 93 | SP_DEVINFO_DATA DeviceInfoData={0}; 94 | DeviceInfoData.cbSize=sizeof(SP_DEVINFO_DATA); 95 | 96 | if(!DeviceInfoSet || !lpBuffer) 97 | return -1; 98 | if(!OpenDevNodeKey) { 99 | CfgMan=LoadLibrary("cfgmgr32"); 100 | if(!CfgMan) 101 | return FALSE; 102 | OpenDevNodeKey= 103 | (CM_Open_DevNode_Key)GetProcAddress(CfgMan,"CM_Open_DevNode_Key"); 104 | if(!OpenDevNodeKey) { 105 | FreeLibrary(CfgMan); 106 | return FALSE; 107 | } 108 | } 109 | 110 | while(TRUE){ 111 | HKEY KeyDevice; 112 | DWORD len; 113 | res1=SetupDiEnumDeviceInfo( 114 | DeviceInfoSet,numDev,&DeviceInfoData); 115 | 116 | if(!res1) { 117 | SetupDiDestroyDeviceInfoList(DeviceInfoSet); 118 | FreeLibrary(CfgMan); 119 | OpenDevNodeKey=NULL; 120 | return FALSE; 121 | } 122 | 123 | res1=OpenDevNodeKey(DeviceInfoData.DevInst,KEY_QUERY_VALUE,0, 124 | RegDisposition_OpenExisting,&KeyDevice,CM_REGISTRY_HARDWARE); 125 | if(res1 != ERROR_SUCCESS) 126 | return FALSE; 127 | len=MAX_NAME_PORTS; 128 | 129 | res1=RegQueryValueEx( 130 | KeyDevice, // handle of key to query 131 | "portname", // address of name of value to query 132 | NULL, // reserved 133 | NULL, // address of buffer for value type 134 | (BYTE*)DevName, // address of data buffer 135 | &len // address of data buffer size 136 | ); 137 | 138 | RegCloseKey(KeyDevice); 139 | if(res1 != ERROR_SUCCESS) 140 | return FALSE; 141 | 142 | /* 143 | * Return the index too, so we can look up other info 144 | */ 145 | *index = numDev; 146 | numDev++; 147 | if(memcmp(DevName, "COM", 3)) 148 | continue; 149 | numport=atoi(DevName+3); 150 | if(numport > 0 && numport <= 256) { 151 | strcpy(lpBuffer,DevName); 152 | return TRUE; 153 | } 154 | 155 | FreeLibrary(CfgMan); 156 | OpenDevNodeKey=NULL; 157 | return FALSE; 158 | } 159 | } 160 | 161 | BOOL EndEnumeratePorts(HANDLE DeviceInfoSet) 162 | { 163 | if(SetupDiDestroyDeviceInfoList(DeviceInfoSet)) 164 | return TRUE; 165 | else return FALSE; 166 | } 167 | /* 168 | * End of Vladimir's code 169 | */ 170 | 171 | /* 172 | * Ascii HEX number to integer. Stop at invalid hex char 173 | */ 174 | int htoi(char *p) 175 | { 176 | int n = 0; 177 | while (*p) { 178 | char c = *p; 179 | if (c >= '0' && c <= '9') { 180 | n = n * 16 + (c - '0'); 181 | } else if (c >= 'a' && c <= 'z') { 182 | n = n * 16 + ((c+10) - 'a'); 183 | } else if (c >= 'A' && c <= 'Z') { 184 | n = n * 16 + ((c+10) - 'A'); 185 | } else break; 186 | p++; 187 | } 188 | return n; 189 | } 190 | 191 | /* 192 | * main program 193 | * enumerate the COM ports and split out any USB Vendor and Product info. 194 | * If the vendor is a likely Arduino (FTDI or Arduino), print out the info. 195 | */ 196 | //int WinMain(HINSTANCE, HINSTANCE, LPSTR, int) 197 | int main(int argc, _TCHAR* argv[]) 198 | { 199 | HANDLE h; 200 | SP_DEVINFO_DATA devInfo; 201 | int vendor, product; 202 | int deviceIndex; 203 | /* 204 | * Evil fixed-length strings; should allow variable length, 205 | * should allow univode. Bad non-windows programmer! 206 | */ 207 | char portname[50] = {0}; 208 | char idstring[100] = {0}; 209 | char infostring[100]; 210 | char *sernostr; 211 | char *infop = infostring; 212 | 213 | h = BeginEnumeratePorts(); 214 | /* 215 | * h now has handle from deviceInfoSet (SetupDiGetClassDevs) 216 | */ 217 | devInfo.cbSize = sizeof(SP_DEVINFO_DATA); 218 | 219 | while (EnumeratePortsNext(h, portname, &deviceIndex)) { 220 | char *p; 221 | SetupDiEnumDeviceInfo(h, deviceIndex, &devInfo); 222 | 223 | // SetupDiGetDeviceInstanceId(h, &devInfo, NULL, 0, &di_size); 224 | SetupDiGetDeviceInstanceId(h, &devInfo, idstring, sizeof(idstring)-1, NULL); 225 | infop = infostring; 226 | 227 | p = strstr(idstring, "VID_"); /* See if there is a vendor ID */ 228 | if (p) { 229 | vendor = htoi(p+4); 230 | } else { 231 | vendor = 0; 232 | } 233 | p = strstr(idstring, "PID_"); /* See if there is a Product ID */ 234 | if (p) { 235 | product = htoi(p+4); 236 | sernostr = p+9; /* The Serial number is everything past the PID */ 237 | } else { 238 | product = 0; 239 | sernostr = NULL; 240 | } 241 | if (vendor == VENDOR_FTDI || 242 | vendor == VENDOR_ARDUINO) { 243 | // sprintf(infop, "Possible Arduino on %s, VID 0x%04x PID 0x%04x\n Serno %s", 244 | // portname, vendor, product, sernostr); 245 | // MessageBox(NULL, infop, "ArduinoFinder", 0); 246 | printf("\nPossible Arduino on %s, VID 0x%04x PID 0x%04x\n Serno %s", 247 | portname, vendor, product, sernostr); 248 | } 249 | } 250 | } 251 | --------------------------------------------------------------------------------