在 C++ 程序中使用 libcurl 会导致 Visual C++ 编译器发出错误

Using libcurl in a C++ program causes the Visual C++ compiler to emit errors

我已经使用 Visual C++ 编译器将 Windows 上的 libcurl 编译成一个 dll。除了已编译的 libcurl 源代码外,该 dll 还包含一个简单的测试程序,如下所示:

Header (HttpClient.h):

#pragma once

#include <string>

#include "curl/curl.h"


namespace My::Project
{
  class HttpClient
  {
  public:
    HttpClient();
    ~HttpClient();
    std::string Get(std::string address);

  private:
    CURL* easy_handle;
  };
}

实施(HttpClient.cpp):

#include "HttpClient.h"

#include <iostream>

using namespace My::Project


HttpClient::HttpClient()
{
  easy_handle = curl_easy_init();
  curl_global_init(CURL_GLOBAL_ALL);
}


HttpClient::~HttpClient()
{
  curl_global_cleanup();
}


static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
  ((std::string*)userp)->append((char*)contents, size * nmemb);
  return size * nmemb;
}


std::string HttpClient::Get(std::string address)
{
  std::string html;

  curl_easy_setopt(easy_handle, CURLOPT_URL, address.c_str());
  curl_easy_setopt(easy_handle, CURLOPT_FOLLOWLOCATION, 1L);
  curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, WriteCallback);
  curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &html);
  CURLcode returnValue = curl_easy_perform(easy_handle);

  if(returnValue != CURLE_OK)
  {
    html = "Failed to retrieve HTML";
  }

  curl_easy_cleanup(easy_handle);
  return html;
}

该测试程序编译得很好。

(顺便说一句:我知道 curl_global_initcurl_global_cleanup 只能调用一次。这只是一个测试程序...)

现在,我想在另一个 dll 中引用该 dll 并创建 HttpClient class 的实例并按如下方式使用它:

#include "<relative-path>/HttpClient.h" 

using namespace My::Project;

// Within a constructor of another class
HttpClient client;
std::string html = client.Get("https://www.google.com/");

用于编译第二个 dll 的编译器调用如下所示:

C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.21.27702\bin\HostX86\x64\CL.exe
/c
/I<project path>
/I"Generated Files\"
/Iobj\Debug\x64\
/I<path to curl source>\curl\include
/ZI
/JMC
/ZW
/ZW:nostdlib
/nologo
/W3
/WX-
/diagnostics:column
/sdl
/MP
/Od
/Oy-
/D _WINRT_DLL
/D _WINDLL
/D _UNICODE
/D UNICODE
/D _DEBUG
/D WINAPI_FAMILY=WINAPI_FAMILY_APP
/D __WRL_NO_DEFAULT_LIB__
/Gm-
/EHsc
/RTC1
/MDd
/GS
/fp:precise
/Zc:wchar_t
/Zc:forScope
/Zc:inline
/std:c++17
/Yu"pch.h"
/Fp"obj\Debug\x64\pch.pch"
/Fo"obj\Debug\x64\"
/Fd"obj\Debug\x64\vc142.pdb"
/Gd
/TP
/wd28204
/FU"C:\Program Files (x86)\Microsoft Visual Studio19\Community\VC\Tools\MSVC.21.27702\lib\x86\store\references\platform.winmd"
/FU"<other winmd files>
/analyze-
/FC
/errorReport:prompt
/bigobj
<path to cpp file invoking HttpClient>

但是,尝试编译第二个 dll 会导致 Visual C++ 编译器发出以下错误:

<path to curl source>\curl\include\curl\curl.h(134,29): error C4430:  missing type specifier - int assumed. Note: C++ does not support default-int
<path to curl source>\curl\include\curl\curl.h(134,16): error C2146:  syntax error: missing ';' before identifier 'curl_socket_t'
<path to curl source>\curl\include\curl\curl.h(397,52): error C2061:  syntax error: identifier 'curl_socket_t'
<path to curl source>\curl\include\curl\curl.h(407,23): error C2079:  'curl_sockaddr::addr' uses undefined struct 'sockaddr'
<path to curl source>\curl\include\curl\curl.h(411,3): error C2065:  'curl_opensocket_callback': undeclared identifier
<path to curl source>\curl\include\curl\curl.h(411,27): error C4430:  missing type specifier - int assumed. Note: C++ does not support default-int
<path to curl source>\curl\include\curl\curl.h(411,27): error C2378:  'curl_socket_t': redefinition; symbol cannot be overloaded with a typedef
<path to curl source>\curl\include\curl\curl.h(134): message :  see declaration of 'curl_socket_t'
<path to curl source>\curl\include\curl\curl.h(411,28): error C2513:  'int': no variable declared before '='
<path to curl source>\curl\include\curl\curl.h(411,28): error C2143:  syntax error: missing ';' before '('
<path to curl source>\curl\include\curl\curl.h(411,34): error C2062:  type 'void' unexpected
<path to curl source>\curl\include\curl\curl.h(416,59): error C2061:  syntax error: identifier 'curl_socket_t'
<path to curl source>\curl\include\curl\multi.h(113,19): error C3646:  'fd': unknown override specifier
<path to curl source>\curl\include\curl\multi.h(113,19): error C4430:  missing type specifier - int assumed. Note: C++ does not support default-int
<path to curl source>\curl\include\curl\multi.h(157,47): error C2061:  syntax error: identifier 'fd_set'
<path to curl source>\curl\include\curl\multi.h(271,51): error C2061:  syntax error: identifier 'curl_socket_t'
<path to curl source>\curl\include\curl\multi.h(292,76): error C2061:  syntax error: identifier 'curl_socket_t'
<path to curl source>\curl\include\curl\multi.h(296,62): error C2061:  syntax error: identifier 'curl_socket_t'
<path to curl source>\curl\include\curl\multi.h(410,55): error C2061:  syntax error: identifier 'curl_socket_t'

跟进第一条错误消息后,我找到了 curl.h 中的以下代码片段:

131:  #ifndef curl_socket_typedef
132:  /* socket typedef */
133:  #if defined(WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H)
134:  typedef SOCKET curl_socket_t;
135:  #define CURL_SOCKET_BAD INVALID_SOCKET
136:  #else
137:  typedef int curl_socket_t;
138:  #define CURL_SOCKET_BAD -1
139:  #endif
140:  #define curl_socket_typedef
141:  #endif /* curl_socket_typedef */

因此编译器基本上会抱怨以下行:

134:  typedef SOCKET curl_socket_t;

但是,我目前对问题的确切原因一无所知。对于 Android 和 iOS.

,相同的代码可以通过 Clang 正常编译

有人知道用 Visual C++ 进行编译需要做什么吗?

这是有效的代码。我做了 运行 它并获取了结果。

#include <iostream>
#include <stdlib.h>
#define CURL_STATICLIB
#include "curl\curl.h"
#include <string>

#pragma comment(lib, "crypt32")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "wldap32.lib")


class HttpClient
{
public:
    HttpClient();
    ~HttpClient();
    std::string Get(std::string address);

private:
    CURL* easy_handle;
};

HttpClient::HttpClient()
{
    easy_handle = curl_easy_init();
    curl_global_init(CURL_GLOBAL_ALL);
}

HttpClient::~HttpClient()
{
    curl_global_cleanup();
}

static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
    ((std::string*)userp)->append((char*)contents, size * nmemb);
    return size * nmemb;
}

std::string HttpClient::Get(std::string address)
{
    std::string html;

    curl_easy_setopt(easy_handle, CURLOPT_URL, address.c_str());
    curl_easy_setopt(easy_handle, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &html);
    CURLcode returnValue = curl_easy_perform(easy_handle);

    if (returnValue != CURLE_OK)
    {
        html = "Failed to retrieve HTML";
    }

    curl_easy_cleanup(easy_handle);
    return html;
}

int main()
{
    HttpClient client;
    std::string html = client.Get("https://www.google.com/");
    std::cout << html;
    std::cin.get();
    return 0;
}

还要确保添加以下内容:

链接器 > 附加依赖项 > Normaliz.lib

希望对您有所帮助。

如前所述,SOCKET 由 Windows 定义。更具体地说,它是在WinSock2.h中定义的。我包含了那个特定的 header 但没有解决问题。但是,第二个 dll 使用预编译的 header 文件 (pch.h)。在 pch.h 文件中包含 WinSock2.h 后,编译器错误终于得到解决。

但是,要为 Windows UWP 应用程序构建 libcurl,还需要完成另一个关键步骤。在 pch.h 文件中包含 WinSock2.h 只能解决编译器错误,但会导致两个链接器错误,如下所示:

error LNK2019: unresolved external symbol __imp_VerSetConditionMask referenced in function Curl_verify_windows_version
error LNK2019: unresolved external symbol __imp_VerifyVersionInfoA referenced in function Curl_verify_windows_version
fatal error LNK1120: 2 unresolved externals

这些术语可以通过指示链接器包含附加库 OneCore.lib 来解决。 Microsoft 对此库的描述如下:

For convenience, an umbrella library named OneCore.lib is provided in the Microsoft Windows Software Development Kit (SDK), which provides the exports for the subset of Win32 APIs that are common to all Windows 10 devices. Link your classic desktop app or driver with OneCore.lib (and no other libraries) to access these APIs. If you link your app or driver to OneCore.lib, and you only call Win32 APIs in that library, then your app or driver will load successfully on all Windows 10 devices.

有关 OneCore.lib 的更多详细信息,请参阅此 link