Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SetupAPI (setupdi functions) don't link

I want a function to enumerate serial (COM) ports on Windows. For this purpose I mostly copied code from http://www.codeguru.com/cpp/w-p/system/hardwareinformation/article.php/c5721/Determining-What-Serial-Ports-Are-Available-on-a-Windows-Machine.htm

in header file:

#include "SerialPort.h"
#include <list>
#include <objbase.h>
#include <initguid.h>
#include <Setupapi.h>
typedef std::list<SerialPort> PortList;
class SerialConnection
{
private:
static PortList availible_ports;
public:
static void enumerateSerialPorts(bool);
static const PortList& getPortList(){ return availible_ports; }
}

implementation:

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*)&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->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
        for (DWORD ii = 0; bOk; ii++) {
            bOk = SetupDiEnumDeviceInterfaces(hDevInfo,
                NULL, guidDev, ii, &ifcData);
            if (bOk) {
                // Got a device. Get the details.
                SP_DEVINFO_DATA devdata = { sizeof(SP_DEVINFO_DATA) };
                bOk = SetupDiGetDeviceInterfaceDetail(hDevInfo,
                    &ifcData, pDetData, dwDetDataSize, NULL, &devdata);
                if (bOk) {
                    CString strDevPath(pDetData->DevicePath);
                    // Got a path to the device. Try to get some more info.
                    TCHAR fname[256];
                    TCHAR desc[256];
                    BOOL bSuccess = SetupDiGetDeviceRegistryProperty(
                        hDevInfo, &devdata, SPDRP_FRIENDLYNAME, NULL,
                        (PBYTE)fname, sizeof(fname), NULL);
                    bSuccess = bSuccess && SetupDiGetDeviceRegistryProperty(
                        hDevInfo, &devdata, SPDRP_DEVICEDESC, NULL,
                        (PBYTE)desc, sizeof(desc), NULL);
                    BOOL bUsbDevice = FALSE;
                    TCHAR locinfo[256];
                    if (SetupDiGetDeviceRegistryProperty(
                        hDevInfo, &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->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);
            }
        }
    }

this code comiles filne, but if I try to use it like

#include "SerialConnection.h"
int main()
{
    SerialConnection::enumerateSerialPorts(false);
    auto cl = SerialConnection::getPortList();
    return 0;
}

error LNK2019: unresolved external symbol __imp__SetupDiDestroyDeviceInfoList@4 referenced in function __catch$?enumerateSerialPorts@SerialConnection@@SAX_N@Z$0

error LNK2019: unresolved external symbol __imp__SetupDiEnumDeviceInterfaces@20 referenced in function "public: static void __cdecl SerialConnection::enumerateSerialPorts(bool)" (?enumerateSerialPorts@SerialConnection@@SAX_N@Z)

error LNK2019: unresolved external symbol __imp__SetupDiGetDeviceInterfaceDetailW@24 referenced in function "public: static void __cdecl SerialConnection::enumerateSerialPorts(bool)" (?enumerateSerialPorts@SerialConnection@@SAX_N@Z)

error LNK2019: unresolved external symbol __imp__SetupDiGetClassDevsW@16 referenced in function "public: static void __cdecl SerialConnection::enumerateSerialPorts(bool)" (?enumerateSerialPorts@SerialConnection@@SAX_N@Z)

error LNK2019: unresolved external symbol __imp__SetupDiGetDeviceRegistryPropertyW@28 referenced in function "public: static void __cdecl SerialConnection::enumerateSerialPorts(bool)" (?enumerateSerialPorts@SerialConnection@@SAX_N@Z)

What have I missed? I use MVS 2013

like image 331
Andrey Pro Avatar asked Dec 18 '22 22:12

Andrey Pro


1 Answers

You have to link your project with Setupapi.lib. You can do that in 2 ways:

  1. use a #pragma comment
#pragma comment (lib, "Setupapi.lib")
  1. Go to Properties > Configuration Propertoes > Linker > Input > Additional Dependencies and add the Setupapi.lib library.

see also : https://social.msdn.microsoft.com/Forums/vstudio/en-US/e14ce1a2-08b9-4291-b035-f72171bcbdbc/error-lnk2019-unresolved-external-symbol?forum=vclanguage

like image 57
cai cx Avatar answered Dec 30 '22 08:12

cai cx