libCurl在Windows上编译使用遇到的坑

解决libcurl7.50.3在windows XP SP3 VC++ 6.0下编译报错 unresolved external symbol __imp__IdnToAscii@20 unresolved external symbol __imp__IdnToUnicode@20

错误重现:

复制代码
复制代码
--------------------Configuration: curl - Win32 LIB Debug DLL Windows SSPI DLL WinIDN--------------------

xilink6: executing 'D:\vc6.0\VC98\Bin\link.exe'
libcurld.lib(idn_win32.obj) : error LNK2001: unresolved external symbol __imp__IdnToAscii@20
libcurld.lib(idn_win32.obj) : error LNK2001: unresolved external symbol __imp__IdnToUnicode@20
..\..\..\..\build\Win32\VC6\LIB Debug - DLL Windows SSPI - DLL WinIDN\curld.exe : fatal error LNK1120: 2 unresolved externals
Error executing xilink6.exe.

curld.exe - 3 error(s), 2 warning(s)


--------------------Configuration: curl - Win32 LIB Release DLL Windows SSPI DLL WinIDN--------------------
Linking...
xilink6: executing 'D:\vc6.0\VC98\Bin\link.exe'
libcurl.lib(idn_win32.obj) : error LNK2001: unresolved external symbol __imp__IdnToAscii@20
libcurl.lib(idn_win32.obj) : error LNK2001: unresolved external symbol __imp__IdnToUnicode@20
..\..\..\..\build\Win32\VC6\LIB Release - DLL Windows SSPI - DLL WinIDN/curl.exe : fatal error LNK1120: 2 unresolved externals
复制代码
复制代码

根据两个API的名字google了一下

IdnToUnicode,IdnToAscii封装在normaliz.dll中,查询了本机的几个SDK(Microsoft Platform SDK February 2003,Microsoft Windows SDK v6.0A,Microsoft Windows SDK v7.0A,
Microsoft Windows SDK v7.1),只有Microsoft Platform SDK February 2003没有,心想这下坏了,不好编译了,因为Microsoft Platform SDK February 2003是Windows XP最终版SDK,翻了一下%systemroot%\system32\又可以找到normaliz.dll,查看了一下导出函数,又是有的,不懂什么软件带过来的,还是系统更新装上的。

msdn对IdnToUnicode,IdnToAscii这两个函数的使用要求如下:

Minimum supported client

Windows Vista [desktop apps | Windows Store apps]

Minimum supported server

Windows Server 2008 [desktop apps | Windows Store apps]

Redistributable

Microsoft Internationalized Domain Name (IDN) Mitigation APIs onWindows XP with SP2 and later,Windows Server 2003 with SP1

Header

Winnls.h (include Windows.h)

Library

Normaliz.lib

DLL

Normaliz.dll
  从以上表格看出,这个库一开始并不是为Windows XP准备的,只是后来在SP2以后才有,用VC++6.0编译的时候normaliz.lib指向的dll函数地址就是错误的,
于是想到用LoadLibrary和GetProcAddress函数配合,显式调用者两个函数,从这个程度上来说也可以节省内存开销,使用完立即释放掉。

解决办法:

问题的根源是idn,于是找到了CURL_FOLDER\lib\idn_win32.c进行开刀。
以下是7.50.3版本的idn_win32.c修改前的模样:
复制代码
复制代码
#include "curl_setup.h"

#ifdef USE_WIN32_IDN

#include "curl_multibyte.h"
#include "curl_memory.h"
#include "warnless.h"

  /* The last #include file should be: */
#include "memdebug.h"

#ifdef WANT_IDN_PROTOTYPES
#  if defined(_SAL_VERSION)
WINNORMALIZEAPI int WINAPI
IdnToAscii(_In_                           DWORD    dwFlags,
           _In_reads_(cchUnicodeChar)     LPCWSTR  lpUnicodeCharStr,
           _In_                           int      cchUnicodeChar,
           _Out_writes_opt_(cchASCIIChar) LPWSTR   lpASCIICharStr,
           _In_                           int      cchASCIIChar);
WINNORMALIZEAPI int WINAPI
IdnToUnicode(_In_                             DWORD   dwFlags,
             _In_reads_(cchASCIIChar)         LPCWSTR lpASCIICharStr,
             _In_                             int     cchASCIIChar,
             _Out_writes_opt_(cchUnicodeChar) LPWSTR  lpUnicodeCharStr,
             _In_                             int     cchUnicodeChar);
#  else
WINBASEAPI int WINAPI IdnToAscii(DWORD dwFlags,
                                 const WCHAR *lpUnicodeCharStr,
                                 int cchUnicodeChar,
                                 WCHAR *lpASCIICharStr,
                                 int cchASCIIChar);
WINBASEAPI int WINAPI IdnToUnicode(DWORD dwFlags,
                                   const WCHAR *lpASCIICharStr,
                                   int cchASCIIChar,
                                   WCHAR *lpUnicodeCharStr,
                                   int cchUnicodeChar);
#  endif
#endif

#define IDN_MAX_LENGTH 255

bool curl_win32_idn_to_ascii(const char *in, char **out);
bool curl_win32_ascii_to_idn(const char *in, char **out);

bool curl_win32_idn_to_ascii(const char *in, char **out)
{
  bool success = FALSE;

  wchar_t *in_w = Curl_convert_UTF8_to_wchar(in);
  if(in_w) {
    wchar_t punycode[IDN_MAX_LENGTH];
    int chars = IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH);
    free(in_w);
    if(chars) {
      *out = Curl_convert_wchar_to_UTF8(punycode);
      if(*out)
        success = TRUE;
    }
  }

  return success;
}

bool curl_win32_ascii_to_idn(const char *in, char **out)
{
  bool success = FALSE;

  wchar_t *in_w = Curl_convert_UTF8_to_wchar(in);
  if(in_w) {
    size_t in_len = wcslen(in_w) + 1;
    wchar_t unicode[IDN_MAX_LENGTH];
    int chars = IdnToUnicode(0, in_w, curlx_uztosi(in_len),
                             unicode, IDN_MAX_LENGTH);
    free(in_w);
    if(chars) {
      *out = Curl_convert_wchar_to_UTF8(unicode);
      if(*out)
        success = TRUE;
    }
  }

  return success;
}

#endif /* USE_WIN32_IDN */
复制代码
复制代码
idn_win32.c修改后的模样:
复制代码
复制代码
#include "curl_setup.h"

#ifdef USE_WIN32_IDN

#include "curl_multibyte.h"
#include "curl_memory.h"
#include "warnless.h"

  /* The last #include file should be: */
#include "memdebug.h"

#ifdef WANT_IDN_PROTOTYPES
//#  if defined(_SAL_VERSION)
typedef int (*fnIdnToAscii)(DWORD,const WCHAR *,int,WCHAR *,int);
typedef int (*fnIdnToUnicode)(DWORD,const WCHAR *,int,WCHAR *, int);
//#  endif
#endif

#define IDN_MAX_LENGTH 255

bool curl_win32_idn_to_ascii(const char *in, char **out);
bool curl_win32_ascii_to_idn(const char *in, char **out);

bool curl_win32_idn_to_ascii(const char *in, char **out)
{
  bool success = FALSE;

  wchar_t *in_w = Curl_convert_UTF8_to_wchar(in);
 
  if(in_w) {
    wchar_t punycode[IDN_MAX_LENGTH];
    int chars = -1;
    fnIdnToAscii IdnToAscii;
    HINSTANCE hNormalizDLL = LoadLibrary("normaliz.dll");
    if (!hNormalizDLL) {
        FreeLibrary(hNormalizDLL);
        assert(hNormalizDLL);
        return FALSE;
    }
    IdnToAscii = (fnIdnToAscii)GetProcAddress(hNormalizDLL, "IdnToAscii");
    if (!IdnToAscii) {
        assert(IdnToAscii);
        return TRUE;
    }
    chars = IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH);
    
    free(IdnToAscii);
    FreeLibrary(hNormalizDLL);

    free(in_w);
    if(chars) {
      *out = Curl_convert_wchar_to_UTF8(punycode);
      if(*out)
        success = TRUE;
    }
  }

  return success;
}

bool curl_win32_ascii_to_idn(const char *in, char **out)
{
  bool success = FALSE;

  wchar_t *in_w = Curl_convert_UTF8_to_wchar(in);
  if(in_w) {
    size_t in_len = wcslen(in_w) + 1;
    wchar_t unicode[IDN_MAX_LENGTH];
    int chars = -1;
    fnIdnToUnicode IdnToUnicode;
    
    HINSTANCE hNormalizDLL = LoadLibrary("normaliz.dll");
    if (!hNormalizDLL) {
        FreeLibrary(hNormalizDLL);
        assert(hNormalizDLL);
        return FALSE;
    }
    IdnToUnicode = (fnIdnToUnicode)GetProcAddress(hNormalizDLL, "IdnToUnicode");
    if (!IdnToUnicode) {
        assert(IdnToUnicode);
        return FALSE;
    }
    chars = IdnToUnicode(0, in_w, curlx_uztosi(in_len),
                             unicode, IDN_MAX_LENGTH);
    
    free(IdnToUnicode);
    FreeLibrary(hNormalizDLL);
    free(in_w);
    if(chars) {
      *out = Curl_convert_wchar_to_UTF8(unicode);
      if(*out)
        success = TRUE;
    }
  }

  return success;
}

#endif /* USE_WIN32_IDN */
复制代码
复制代码
 经过以上修改就可以编译成功了。

 

1. 遇到如下问题
1>libcurl.lib(easy.obj) : error LNK2001: 无法解析的外部符号 __imp_WSAStartup
1>libcurl.lib(telnet.obj) : error LNK2001: 无法解析的外部符号 __imp_WSAStartup
1>libcurl.lib(easy.obj) : error LNK2001: 无法解析的外部符号 __imp_WSACleanup
1>libcurl.lib(telnet.obj) : error LNK2001: 无法解析的外部符号 __imp_WSACleanup
1>libcurl.lib(ftp.obj) : error LNK2001: 无法解析的外部符号 __imp_WSAGetLastError
1>libcurl.lib(telnet.obj) : error LNK2001: 无法解析的外部符号 __imp_WSAGetLastError
1>libcurl.lib(tftp.obj) : error LNK2001: 无法解析的外部符号 __imp_WSAGetLastError
1>libcurl.lib(asyn-thread.obj) : error LNK2001: 无法解析的外部符号 __imp_WSAGetLastError
1>libcurl.lib(select.obj) : error LNK2001: 无法解析的外部符号 __imp_WSAGetLastError
1>libcurl.lib(sendf.obj) : error LNK2001: 无法解析的外部符号 __imp_WSAGetLastError
1>libcurl.lib(connect.obj) : error LNK2001: 无法解析的外部符号 __imp_WSAGetLastError
1>libcurl.lib(select.obj) : error LNK2001: 无法解析的外部符号 __WSAFDIsSet
1>libcurl.lib(select.obj) : error LNK2001: 无法解析的外部符号 __imp_select
1>libcurl.lib(select.obj) : error LNK2001: 无法解析的外部符号 __imp_WSASetLastError
1>libcurl.lib(connect.obj) : error LNK2001: 无法解析的外部符号 __imp_WSASetLastError
1>libcurl.lib(curl_addrinfo.obj) : error LNK2001: 无法解析的外部符号 __imp_WSASetLastError
1>libcurl.lib(sendf.obj) : error LNK2001: 无法解析的外部符号 __imp_recv
1>libcurl.lib(connect.obj) : error LNK2001: 无法解析的外部符号 __imp_recv
1>libcurl.lib(sendf.obj) : error LNK2001: 无法解析的外部符号 __imp_send
1>libcurl.lib(telnet.obj) : error LNK2001: 无法解析的外部符号 __imp_send
1>libcurl.lib(connect.obj) : error LNK2001: 无法解析的外部符号 __imp_bind
1>libcurl.lib(ftp.obj) : error LNK2001: 无法解析的外部符号 __imp_bind
1>libcurl.lib(tftp.obj) : error LNK2001: 无法解析的外部符号 __imp_bind
1>libcurl.lib(connect.obj) : error LNK2001: 无法解析的外部符号 __imp_closesocket
1>libcurl.lib(connect.obj) : error LNK2001: 无法解析的外部符号 __imp_connect
1>libcurl.lib(connect.obj) : error LNK2001: 无法解析的外部符号 __imp_getpeername
1>libcurl.lib(connect.obj) : error LNK2001: 无法解析的外部符号 __imp_getsockname
1>libcurl.lib(ftp.obj) : error LNK2001: 无法解析的外部符号 __imp_getsockname
1>libcurl.lib(connect.obj) : error LNK2001: 无法解析的外部符号 __imp_getsockopt
1>libcurl.lib(smb.obj) : error LNK2001: 无法解析的外部符号 __imp_htons
1>libcurl.lib(connect.obj) : error LNK2001: 无法解析的外部符号 __imp_htons
1>libcurl.lib(curl_addrinfo.obj) : error LNK2001: 无法解析的外部符号 __imp_htons
1>libcurl.lib(ftp.obj) : error LNK2001: 无法解析的外部符号 __imp_htons
1>libcurl.lib(telnet.obj) : error LNK2001: 无法解析的外部符号 __imp_htons
1>libcurl.lib(connect.obj) : error LNK2001: 无法解析的外部符号 __imp_ntohs
1>libcurl.lib(ftp.obj) : error LNK2001: 无法解析的外部符号 __imp_ntohs
1>libcurl.lib(connect.obj) : error LNK2001: 无法解析的外部符号 __imp_setsockopt
1>libcurl.lib(connect.obj) : error LNK2001: 无法解析的外部符号 __imp_socket
1>libcurl.lib(connect.obj) : error LNK2001: 无法解析的外部符号 __imp_WSAIoctl
1>libcurl.lib(curl_addrinfo.obj) : error LNK2001: 无法解析的外部符号 __imp_getaddrinfo
1>libcurl.lib(curl_addrinfo.obj) : error LNK2001: 无法解析的外部符号 __imp_freeaddrinfo
1>libcurl.lib(ftp.obj) : error LNK2001: 无法解析的外部符号 __imp_accept
1>libcurl.lib(ftp.obj) : error LNK2001: 无法解析的外部符号 __imp_listen
1>libcurl.lib(tftp.obj) : error LNK2001: 无法解析的外部符号 __imp_recvfrom
1>libcurl.lib(tftp.obj) : error LNK2001: 无法解析的外部符号 __imp_sendto
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_init
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_unbind_s
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_set_option
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_simple_bind_s
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_search_s
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_msgfree
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_err2string
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_first_entry
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_next_entry
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_first_attribute
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_next_attribute
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_get_values_len
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_value_free_len
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_get_dn
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ldap_memfree
1>libcurl.lib(ldap.obj) : error LNK2001: 无法解析的外部符号 __imp_ber_free
1>libcurl.lib(nonblock.obj) : error LNK2001: 无法解析的外部符号 __imp_ioctlsocket

具体步骤就是:

给工程添加依赖的库:项目->属性->链接器->输入->附加依赖项,把 ws2_32.lib、 winmm.lib、 wldap32.lib添加进去

注意,debug配置用libcurld.lib

2.如出现
 error LNK2001: 无法解析的外部符号 __imp_curl_easy_perform
error LNK2001: 无法解析的外部符号 __imp_curl_easy_init
 error LNK2001: 无法解析的外部符号 __imp_curl_slist_append
 error LNK2001: 无法解析的外部符号 __imp_curl_slist_free_all
 error LNK2001: 无法解析的外部符号 __imp_curl_easy_cleanup
 error LNK2001: 无法解析的外部符号 __imp_curl_easy_setopt

加入预编译选项:项目->属性->c/c++ ->预处理器->预处理器,把 BUILDING_LIBCURL;HTTP_ONLY复制进去(注意不要丢了";")

3.如出现
1>libcurl.lib(cookie.obj) : error LNK2001: 无法解析的外部符号 __imp_fgets
1>libcurl.lib(netrc.obj) : error LNK2001: 无法解析的外部符号 __imp_fgets
1>libcurl.lib(mime.obj) : error LNK2001: 无法解析的外部符号 __imp_access
1>OLDNAMES.lib(access.obi) : error LNK2001: 无法解析的外部符号 __imp_access
1>libcurl.lib(tftp.obj) : error LNK2001: 无法解析的外部符号 __imp_strstr
1>libcurl.lib(digest.obj) : error LNK2001: 无法解析的外部符号 __imp_strstr
1>libcurl.lib(ftplistparser.obj) : error LNK2001: 无法解析的外部符号 __imp_strstr
1>libcurl.lib(url.obj) : error LNK2001: 无法解析的外部符号 __imp_strstr
1>libcurl.lib(http.obj) : error LNK2001: 无法解析的外部符号 __imp_strstr
1>libcurl.lib(transfer.obj) : error LNK2001: 无法解析的外部符号 __imp_strstr
1>libcurl.lib(ftp.obj) : error LNK2001: 无法解析的外部符号 __imp_strstr
1>libcurl.lib(warnless.obj) : error LNK2001: 无法解析的外部符号 __imp_read
1>OLDNAMES.lib(read.obi) : error LNK2001: 无法解析的外部符号 __imp_read
1>libcurl.lib(warnless.obj) : error LNK2001: 无法解析的外部符号 __imp_write
1>OLDNAMES.lib(write.obi) : error LNK2001: 无法解析的外部符号 __imp_write
1>libcurl.lib(parsedate.obj) : error LNK2001: 无法解析的外部符号 __imp__gmtime64
1>libcurl.lib(file.obj) : error LNK2001: 无法解析的外部符号 __imp_open
1>OLDNAMES.lib(open.obi) : error LNK2001: 无法解析的外部符号 __imp_open
1>libcurl.lib(smb.obj) : error LNK2001: 无法解析的外部符号 __imp__getpid
1>libcurl.lib(system_win32.obj) : error LNK2001: 无法解析的外部符号 __imp__mbspbrk

这个问题 :要 统一代码生成方式,如果是 Debug 就是多线程调试 /MTd,如果是 Release 就是多线程 /MT
C/C++ -> 代码生成 -> 运行库

curl 工程默认是MD ,改成MT就可以了。

 

 4.libcurl出现CURLcode 23 CURLE_WRITE_ERROR错误
百度了下是 write_func 回调函数有问题 ,

因为我编译的是全平台的,windows、linux 系统接口是不同的,回调函数需要定义成不现的函数
 

#if defined(WIN32) || defined(_WIN32) || defined(WINDOWS)
size_t write_func(char *ptr, size_t size, size_t nmemb, void *userdata) //回调函数
{
std::string &buffer = *(std::string *) userdata;
size_t len = size * nmemb;
for (size_t i = 0; i < len; ++i) {
buffer += *ptr;
++ptr;
}
// LOGE(buffer.c_str());
return len;
}
#else

void write_func(char *ptr, size_t size, size_t nmemb, void *userdata) //回调函数
{
std::string &buffer = *(std::string *) userdata;
size_t len = size * nmemb;
for (size_t i = 0; i < len; ++i) {
buffer += *ptr;
++ptr;
}
// LOGE(buffer.c_str());
}
#endif
 

原文链接:https://blog.csdn.net/q610098308/java/article/details/88569866

posted @   unicornsir  阅读(2879)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示