title: SetupAPI (setupdi functions) don't link
body: <p>I want a function to enumerate serial (COM) ports on Windows. For this purpose I mostly copied code from <a href="http://www.codeguru.com/cpp/w-p/system/hardwareinformation/article.php/c5721/Determining-What-Serial-Ports-Are-Available-on-a-Windows-Machine.htm" rel="noreferrer">http://www.codeguru.com/cpp/w-p/system/hardwareinformation/article.php/c5721/Determining-What-Serial-Ports-Are-Available-on-a-Windows-Machine.htm</a></p>
  
  <p>in header file: </p>
  
  <pre><code>#include "SerialPort.h"
  #include &lt;list&gt;
  #include &lt;objbase.h&gt;
  #include &lt;initguid.h&gt;
  #include &lt;Setupapi.h&gt;
  typedef std::list&lt;SerialPort&gt; PortList;
  class SerialConnection
  {
  private:
  static PortList availible_ports;
  public:
  static void enumerateSerialPorts(bool);
  static const PortList&amp; getPortList(){ return availible_ports; }
  }
  </code></pre>
  
  <p>implementation:</p>
  
  <pre><code>void SerialConnection::enumerateSerialPorts(bool check)
  {
      availible_ports.clear();
      CString strErr;
      // Create a device information set that will be the container for 
      // the device interfaces.
      GUID *guidDev = (GUID*)&amp;GUID_CLASS_COMPORT;
  
      HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
      SP_DEVICE_INTERFACE_DETAIL_DATA *pDetData = NULL;
  
      try {
          hDevInfo = SetupDiGetClassDevs(guidDev,
              NULL,
              NULL,
              DIGCF_PRESENT | DIGCF_DEVICEINTERFACE
              );
  
          if (hDevInfo == INVALID_HANDLE_VALUE)
          {
              strErr.Format(_T("SetupDiGetClassDevs failed. (err=%lx)"),
                  GetLastError());
              throw strErr;
          }
  
          // Enumerate the serial ports
          BOOL bOk = TRUE;
          SP_DEVICE_INTERFACE_DATA ifcData;
          DWORD dwDetDataSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + 256;
          pDetData = (SP_DEVICE_INTERFACE_DETAIL_DATA*) new char[dwDetDataSize];
          // This is required, according to the documentation. Yes,
          // it's weird.
          ifcData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
          pDetData-&gt;cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
          for (DWORD ii = 0; bOk; ii++) {
              bOk = SetupDiEnumDeviceInterfaces(hDevInfo,
                  NULL, guidDev, ii, &amp;ifcData);
              if (bOk) {
                  // Got a device. Get the details.
                  SP_DEVINFO_DATA devdata = { sizeof(SP_DEVINFO_DATA) };
                  bOk = SetupDiGetDeviceInterfaceDetail(hDevInfo,
                      &amp;ifcData, pDetData, dwDetDataSize, NULL, &amp;devdata);
                  if (bOk) {
                      CString strDevPath(pDetData-&gt;DevicePath);
                      // Got a path to the device. Try to get some more info.
                      TCHAR fname[256];
                      TCHAR desc[256];
                      BOOL bSuccess = SetupDiGetDeviceRegistryProperty(
                          hDevInfo, &amp;devdata, SPDRP_FRIENDLYNAME, NULL,
                          (PBYTE)fname, sizeof(fname), NULL);
                      bSuccess = bSuccess &amp;&amp; SetupDiGetDeviceRegistryProperty(
                          hDevInfo, &amp;devdata, SPDRP_DEVICEDESC, NULL,
                          (PBYTE)desc, sizeof(desc), NULL);
                      BOOL bUsbDevice = FALSE;
                      TCHAR locinfo[256];
                      if (SetupDiGetDeviceRegistryProperty(
                          hDevInfo, &amp;devdata, SPDRP_LOCATION_INFORMATION, NULL,
                          (PBYTE)locinfo, sizeof(locinfo), NULL))
                      {
                          // Just check the first three characters to determine
                          // if the port is connected to the USB bus. This isn't
                          // an infallible method; it would be better to use the
                          // BUS GUID. Currently, Windows doesn't let you query
                          // that though (SPDRP_BUSTYPEGUID seems to exist in
                          // documentation only).
                      //  bUsbDevice = (strncmp(locinfo, "USB", 3) == 0);
                      }
                      if (bSuccess) {
                          // Add an entry to the array
                          SerialPort sp;
                          sp.full_path = strDevPath;
                          sp.port_name = fname;
                          sp.full_name = desc;
  
                          availible_ports.push_back(sp);
                      }
  
                  }
                  else {
                      strErr.Format(_T("SetupDiGetDeviceInterfaceDetail failed. (err=%lx)"),
                          GetLastError());
                      throw strErr;
                  }
              }
              else {
                  DWORD err = GetLastError();
                  if (err != ERROR_NO_MORE_ITEMS) {
                      strErr.Format(_T("SetupDiEnumDeviceInterfaces failed. (err=%lx)"), err);
                      throw strErr;
                  }
              }
          }
      }
      catch (CString strCatchErr) {
          strErr = strCatchErr;
      }
  
      if (pDetData != NULL)
          delete[](char*)pDetData;
      if (hDevInfo != INVALID_HANDLE_VALUE)
          SetupDiDestroyDeviceInfoList(hDevInfo);
  
      if (!strErr.IsEmpty())
          throw strErr;
  
      if (check)
      {
          for (auto it = availible_ports.begin(); it != availible_ports.end(); it++)
          {
              HANDLE hCom = CreateFile(it-&gt;full_path,
                  GENERIC_READ | GENERIC_WRITE,
                  0,    /* comm devices must be opened w/exclusive-access */
                  NULL, /* no security attrs */
                  OPEN_EXISTING, /* comm devices must use OPEN_EXISTING */
                  0,    /* not overlapped I/O */
                  NULL  /* hTemplate must be NULL for comm devices */
                  );
              if (hCom == INVALID_HANDLE_VALUE) {
                  // It can't be opened; remove it.
                  auto erase_it = it;
                  it++;
                  availible_ports.erase(erase_it);
  
              }
              else {
                  // It can be opened! Close it and add it to the list
                  ::CloseHandle(hCom);
              }
          }
      }
  </code></pre>
  
  <p>this code comiles filne, but if I try to use it like</p>
  
  <pre><code>#include "SerialConnection.h"
  int main()
  {
      SerialConnection::enumerateSerialPorts(false);
      auto cl = SerialConnection::getPortList();
      return 0;
  }
  </code></pre>
  
  <p>error LNK2019: unresolved external symbol __imp__SetupDiDestroyDeviceInfoList@4 referenced in function __catch$?enumerateSerialPorts@SerialConnection@@SAX_N@Z$0</p>
  
  <p>error LNK2019: unresolved external symbol __imp__SetupDiEnumDeviceInterfaces@20 referenced in function "public: static void __cdecl SerialConnection::enumerateSerialPorts(bool)" (?enumerateSerialPorts@SerialConnection@@SAX_N@Z)</p>
  
  <p>error LNK2019: unresolved external symbol __imp__SetupDiGetDeviceInterfaceDetailW@24 referenced in function "public: static void __cdecl SerialConnection::enumerateSerialPorts(bool)" (?enumerateSerialPorts@SerialConnection@@SAX_N@Z)</p>
  
  <p>error LNK2019: unresolved external symbol __imp__SetupDiGetClassDevsW@16 referenced in function "public: static void __cdecl SerialConnection::enumerateSerialPorts(bool)" (?enumerateSerialPorts@SerialConnection@@SAX_N@Z)</p>
  
  <p>error LNK2019: unresolved external symbol __imp__SetupDiGetDeviceRegistryPropertyW@28 referenced in function "public: static void __cdecl SerialConnection::enumerateSerialPorts(bool)" (?enumerateSerialPorts@SerialConnection@@SAX_N@Z)</p>
  
  <p>What have I missed? I use MVS 2013</p>