【问题标题】:Using libcurl in a C++ program causes the Visual C++ compiler to emit errors在 C++ 程序中使用 libcurl 会导致 Visual C++ 编译器发出错误
【发布时间】:2019-06-12 07:50:10
【问题描述】:

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

标头(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 类的实例并按如下方式使用它:

#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 Studio\2019\Community\VC\Tools\MSVC\14.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 Studio\2019\Community\VC\Tools\MSVC\14.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++ 编译需要做什么吗?

【问题讨论】:

  • SOCKET 是由 Windows 定义的,这很可能是你的问题。是否有理由在 Windows 中使用 curl?
  • 我也在考虑这个问题,所以我分别包含了windows.hwinsock.h,但问题仍然存在。关于我为什么使用 libcurl 的好问题。我正在进行的项目是一个使用 C++ 的跨平台应用程序(Android、iOS、Windows UWP)。对于网络,我们使用libcurl,它允许我们构建一个跨平台的网络层。
  • 这并不是真正适用于 Android 和 iOS 的“相同”代码。当不为 Windows 编译时,将跳过第 134 行并使用第 137 行。也就是说,对于非 Windows,不需要定义 SOCKET(而且定义为类型的标识符),因为将使用 int
  • 你的例子可以简化吗? minimal reproducible example 可以更轻松地定位问题。如果您的第二个 DLL 显示的代码被简单地替换为 #include "curl/curl.h" 会发生什么?根本不要引用HttpClient。如果您遇到相同的错误,您可以消除问题的大部分开头,因为您的第一个 DLL 的代码将不再相关(除了在 Windows 中成功使用 curl 的项目)。

标签: c++ curl visual-c++ libcurl


【解决方案1】:

正如所指出的,SOCKET 是由 Windows 定义的。更具体地说,它在WinSock2.h 中定义。我包含了那个特定的标题,但没有解决问题。但是,第二个 dll 使用预编译的头文件 (pch.h)。一旦我将WinSock2.h 包含在pch.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 对这个库的描述如下:

为方便起见,Microsoft Windows 软件开发工具包 (SDK) 中提供了一个名为 OneCore.lib 的总括库,该工具包提供所有 Windows 10 设备通用的 Win32 API 子集的导出。将您的经典桌面应用程序或驱动程序与 OneCore.lib(而不是其他库)链接以访问这些 API。如果您将应用或驱动程序链接到 OneCore.lib,并且只调用该库中的 Win32 API,那么您的应用或驱动程序将在所有 Windows 10 设备上成功加载。

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

【讨论】:

    【解决方案2】:

    下面是有效的代码。我确实运行它并获取了结果。

    #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

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多