Как использовать функции libcurl внутри приложения Windows Forms в Visual C++ 2010?

Вот ситуация...

Я использую Visual C++ 2010 Express.

Я пытаюсь создать приложение Windows Forms, которое может загружать исходный код любого заданного URL-адреса в RichTextBox. Я хочу сделать это с помощью библиотеки cUrl. Хорошая новость заключается в том, что после более чем 24-часовой работы над тем, как правильно связать файл libcurl.dll с моим проектом, я добился успеха. На самом деле я смог использовать библиотеку cUrl для получения источника URL-адреса в консольном приложении Windows и вывода результата в командное окно. Так что у меня нет проблем с подключением.

Для справки ниже приведен исходный код основного файла CPP для рабочего тестового консольного приложения:

// MyApplication.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include <string>
#include <curl/curl.h>

using namespace std;

int writer(char *data, size_t size, size_t nmemb, string *buffer);
string curl_httpget(const string &url);

int main(int argc, char *argv[])
{
    cout << curl_httpget("http://www.google.com/") << endl;
}

string curl_httpget(const string &url)
{
    string buffer;

    CURL *curl;
    CURLcode result;

    curl = curl_easy_init();

    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str()  );
        curl_easy_setopt(curl, CURLOPT_HEADER, 0);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);

        result = curl_easy_perform(curl);//http get performed

        curl_easy_cleanup(curl);//must cleanup

        //error codes: http://curl.haxx.se/libcurl/c/libcurl-errors.html
        if (result == CURLE_OK)
        {
            return buffer;
        }
        //curl_easy_strerror was added in libcurl 7.12.0
        //cerr << "error: " << result << " " << curl_easy_strerror(result) <<endl;
        return "";
    }

    cerr << "error: could not initalize curl" << endl;
    return "";
}

int writer(char *data, size_t size, size_t nmemb, string *buffer)
{
    int result = 0;
    if (buffer != NULL)
    {
        buffer->append(data, size * nmemb);
        result = size * nmemb;
    }
    return result;
}

Ниже приведен код моего основного файла проекта CPP для моего приложения Windows Forms «Code Viewer». Включает работает нормально здесь. Я устанавливаю все необходимые пути для include и lib и т. д. Я компилирую с помощью /CLR (не чистый):

// CodeViewer.cpp : main project file.

#include "stdafx.h"
#include "Form1.h"
#include <stdio.h>
#include <iostream>
#include <string>
#include <curl/curl.h>

using namespace CodeViewer;

[STAThreadAttribute]
int main(array<System::String ^> ^args)
{
    // Enabling Windows XP visual effects before any controls are created
    Application::EnableVisualStyles();
    Application::SetCompatibleTextRenderingDefault(false); 

    // Create the main window and run it
    Application::Run(gcnew Form1());
    return 0;
}

Ниже приведен код моего Form1.h для приложения «Code Viewer»:

#include <stdio.h>
#include <iostream>
#include <string>
#include <curl/curl.h>

#pragma once

namespace CodeViewer {

    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;
    using namespace std;

    /// <summary>
    /// Summary for Form1
    /// </summary>
    public ref class Form1 : public System::Windows::Forms::Form
    {
    public:
        Form1(void)
        {
            InitializeComponent();
            //
            //TODO: Add the constructor code here
            //
        }

    protected:
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        ~Form1()
        {
            if (components)
            {
                delete components;
            }
        }
    private: System::Windows::Forms::RichTextBox^  OutputBox;
    protected: 
    private: System::Windows::Forms::TextBox^  AddressBar;
    private: System::Windows::Forms::Button^  btnGo;

    protected: 

    private:
        /// <summary>
        /// Required designer variable.
        /// </summary>
        System::ComponentModel::Container ^components;

        //My variables
    private:
        System::String^ iAddress;
        System::String^ iSource;

#pragma region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        void InitializeComponent(void)
        {
            this->OutputBox = (gcnew System::Windows::Forms::RichTextBox());
            this->AddressBar = (gcnew System::Windows::Forms::TextBox());
            this->btnGo = (gcnew System::Windows::Forms::Button());
            this->SuspendLayout();
            // 
            // OutputBox
            // 
            this->OutputBox->Location = System::Drawing::Point(12, 80);
            this->OutputBox->Name = L"OutputBox";
            this->OutputBox->Size = System::Drawing::Size(640, 228);
            this->OutputBox->TabIndex = 1;
            this->OutputBox->Text = L"";
            // 
            // AddressBar
            // 
            this->AddressBar->Location = System::Drawing::Point(12, 52);
            this->AddressBar->Name = L"AddressBar";
            this->AddressBar->Size = System::Drawing::Size(593, 22);
            this->AddressBar->TabIndex = 2;
            this->AddressBar->TextChanged += gcnew System::EventHandler(this, &Form1::AddressBar_TextChanged);
            // 
            // btnGo
            // 
            this->btnGo->Location = System::Drawing::Point(611, 51);
            this->btnGo->Name = L"btnGo";
            this->btnGo->Size = System::Drawing::Size(41, 23);
            this->btnGo->TabIndex = 3;
            this->btnGo->Text = L"GO";
            this->btnGo->UseVisualStyleBackColor = true;
            this->btnGo->Click += gcnew System::EventHandler(this, &Form1::btnGo_Click);
            // 
            // Form1
            // 
            this->AutoScaleDimensions = System::Drawing::SizeF(8, 16);
            this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
            this->ClientSize = System::Drawing::Size(664, 320);
            this->Controls->Add(this->btnGo);
            this->Controls->Add(this->AddressBar);
            this->Controls->Add(this->OutputBox);
            this->Name = L"Form1";
            this->Text = L"Code Viewer 0.0.0.1";
            this->Load += gcnew System::EventHandler(this, &Form1::Form1_Load);
            this->ResumeLayout(false);
            this->PerformLayout();

        }
#pragma endregion

    private: System::Void MarshalString ( System::String^ s, std::string& os )
            {
                using namespace System::Runtime::InteropServices;
                const char* chars = (const char*)(Marshal::StringToHGlobalAnsi(s)).ToPointer();
                os = chars;
                Marshal::FreeHGlobal(IntPtr((void*)chars));
            }

    private: System::String^ curl_httpget(const string &url)
            {
                System::String^ buffer;

                CURL *curl;
                CURLcode result;

                curl = curl_easy_init();

                if (curl)
                {
                    curl_easy_setopt(curl, CURLOPT_URL, url.c_str()  );
                    curl_easy_setopt(curl, CURLOPT_HEADER, 0);
                    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer);
                    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);

                    result = curl_easy_perform(curl);//http get performed

                    curl_easy_cleanup(curl);//must cleanup

                    //error codes: http://curl.haxx.se/libcurl/c/libcurl-errors.html
                    if (result == CURLE_OK)
                    {
                        return buffer;
                    }
                    //curl_easy_strerror was added in libcurl 7.12.0
                    //cerr << "error: " << result << " " << curl_easy_strerror(result) <<endl;
                    return "";
                }

                cerr << "error: could not initalize curl" << endl;
                return "";
            }

    private: int writer(char *data, size_t size, size_t nmemb, string *buffer)
            {
                int result = 0;
                if (buffer != NULL)
                {
                    buffer->append(data, size * nmemb);
                    result = size * nmemb;
                }
                return result;
            }

    private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e) {
             }
    private: System::Void btnGo_Click(System::Object^  sender, System::EventArgs^  e) {
                 std::string myAddress = "";
                 MarshalString(iAddress, myAddress);
                 iSource = curl_httpget(myAddress);
                 OutputBox->Text = iSource;
             }
    private: System::Void AddressBar_TextChanged(System::Object^  sender, System::EventArgs^  e) {
                 iAddress = AddressBar->Text;
             }
    };
}

Я новичок в C++, и я только учусь создавать приложения Windows Forms, поэтому в основном я не знаю, что, черт возьми, я делаю. Мне нужно иметь возможность вызывать эти функции cUrl из Form1.h, и я понятия не имею, как это сделать. Я хочу, чтобы кнопка "GO" выполняла вызов функции для извлечения исходного HTML-кода URL-адреса, введенного в AddressBar, с помощью cUrl. Я, вероятно, сделал более 100 поисковых запросов в Google, пытаясь понять это, и я упираюсь в стену. Я искал stackoverflow с теми же результатами. Всегда вроде близко, но не то, что я ищу. Я уверен, что должен быть способ сделать это.

Пожалуйста, будьте подробнее в ответе. Я, вероятно, не пойму техническое объяснение, которое не включает пример исходного кода.

Большое спасибо заранее!


ОБНОВЛЕНИЕ: после еще нескольких доработок и настроек этого кода по совету Сета (см. комментарии ниже) я смог сделать свой код почти функциональным. См. приведенную выше отредактированную версию Form1.h. У меня все еще есть одна оставшаяся ошибка компилятора, но я думаю, что я близок к пониманию, почему у меня есть эта ошибка. Ниже приведен этот код ошибки:

c:\project\libcurl\visualstudio\codeviewer\codeviewer\Form1.h(137): error C3867: 'CodeViewer::Form1::writer': function call missing argument list; use '&CodeViewer::Form1::writer' to create a pointer to member

Хотя у моего консольного приложения не было проблем с этим кодом, похоже, что вызов функции write() без параметров здесь проблема. Прямо сейчас я предполагаю, что решение состоит в том, чтобы передать ему нужные параметры, но пока я не попробую, я не узнаю. Уже поздно, поэтому я иду спать. Завтра я изучу параметры, необходимые для CURLOPT_WRITEFUNCTION...


person Jason    schedule 23.08.2011    source источник
comment
Разве Visual Studio для C++/CLI не поставляется с конструктором форм? Если это так, и я думаю, что это так, вы можете просто перетащить кнопку в свою форму и дважды щелкнуть кнопку, которая приведет вас к событию OnClick для кнопки, и вы можете ввести там код.   -  person Seth Carnegie    schedule 23.08.2011
comment
Также, если вы используете .NET, я бы воспользовался этим и не использовал CURL. Вероятно, есть более простые способы сделать то, что вы пытаетесь сделать с .NET.   -  person Seth Carnegie    schedule 23.08.2011
comment
Я понимаю часть о добавлении события OnClick (это довольно просто). Но я не понимаю, как вставить функции cUrl в этот OnClick.   -  person Jason    schedule 23.08.2011
comment
Я действительно хочу, чтобы это работало с cUrl.   -  person Jason    schedule 23.08.2011
comment
просто объявите свои функции где-нибудь, где их может увидеть обработчик OnClick, а затем вызовите их в обработчике, как обычную функцию. Я не уверен, что понимаю, какие трудности у вас возникли. Кроме того, почему вы настроены на CURL?   -  person Seth Carnegie    schedule 23.08.2011
comment
просто объявите свои функции где-нибудь, где их может увидеть обработчик OnClick, а затем вызовите их в обработчике, как обычную функцию. Можете ли вы предоставить пример кода этого   -  person Jason    schedule 23.08.2011
comment
Джейсон, скопируйте и вставьте тело своих функций CURL над функцией обработчика кликов, а затем вызовите его в функции обработчика кликов, как вы вызвали его в своей функции main вашего примера кода. Вы, очевидно, не хотите cout это, а просто установите текст вашего расширенного текстового поля в результат.   -  person Seth Carnegie    schedule 23.08.2011
comment
Кроме того, почему вы настроены на CURL? Потому что у него есть все функции, которые мне нужны, включая передачу заголовков и создание сообщений. В конце концов я хочу создать приложение, которое может отправлять сообщения в безопасную онлайн-форму с использованием SSL. Это приложение является учебником для этого. Кроме того, я ничего не знаю об этом с .NET.   -  person Jason    schedule 23.08.2011
comment
Джейсон, скопируйте и вставьте тело своих функций CURL над функцией обработчика кликов, а затем вызовите его в функции обработчика кликов, как вы вызвали его в своей основной функции вашего примера кода. Вопросы: нужно ли добавлять вложения для CURL в Form1.h? Нужно ли мне модифицировать функции, чтобы они там работали?   -  person Jason    schedule 23.08.2011
comment
вам нужно просто включить заголовки CURL, а не stdafx.h или stdio.h или что-то еще. Вам также не нужно изменять функции для работы там.   -  person Seth Carnegie    schedule 23.08.2011
comment
Когда я это делаю, я получаю множество сообщений об ошибках. Должно быть что-то, чего я не понимаю.   -  person Jason    schedule 23.08.2011
comment
CURL не написан для C++/CLI, поэтому он может даже не работать для этого языка. Помните, что C++/CLI — это совершенно другой язык, чем C++. Вероятно, вам придется научиться жить с .NET.   -  person Seth Carnegie    schedule 23.08.2011
comment
CURL — это библиотека C, но вы можете вызвать ее из C++. Я сделал это довольно успешно с моим консольным приложением (см. Код выше), это приложение форм Windows, которое оказалось более сложным.   -  person Jason    schedule 23.08.2011
comment
Да, я знаю, я использовал CURL раньше, но, как я уже сказал, C++/CLI — это НЕ C++. То, что работает в одном, иногда не работает в другом.   -  person Seth Carnegie    schedule 23.08.2011
comment
Тем не менее, попробуйте включить stdio.h и прочее (не stdafx.h), как вы это делали в другом проекте, и посмотрите, устранит ли это какие-либо ошибки.   -  person Seth Carnegie    schedule 23.08.2011
comment
А как насчет этой строки? использование пространства имен std;   -  person Jason    schedule 23.08.2011
comment
Я продолжаю получать ошибки. Возможно, вы могли бы передать мне какой-нибудь пример исходного кода?   -  person Jason    schedule 23.08.2011
comment
вставьте ошибки, которые вы получаете на pastebin или другом подобном сайте. Я никогда раньше не использовал CURL с C++/CLI, поэтому я не могу помочь вам с каким-либо примером кода.   -  person Seth Carnegie    schedule 23.08.2011
comment
Сет, см. мою правку выше. Кажется, я близок к функциональному состоянию. Завтра проверю снова.   -  person Jason    schedule 23.08.2011
comment
Джейсон, твоя проблема в том, что ты сделал функцию writer функцией-членом класса. Это означает, что вы не можете вызвать его без указателя на класс, который будет указателем this. Возьмите функцию вне класса, чтобы она была нормальной функцией, затем измените curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer); на curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writer); (обратите внимание на добавленный &)   -  person Seth Carnegie    schedule 23.08.2011
comment
Спасибо, Сет. Ваши предложения были очень полезны. На самом деле я понял, что мне нужно переместить функции за пределы класса CodeViewer незадолго до того, как я прочитал ваш комментарий, но я не был уверен, что эти функции вообще могут сосуществовать, пока вы не подтвердили, что они могут.   -  person Jason    schedule 23.08.2011


Ответы (1)


РЕШЕНО!!! ;))) УУУУУ!! См. решение ниже (Form1.h):

#include <stdio.h>
#include <iostream>
#include <string>
#include <curl/curl.h>

using namespace std;
int writer(char *data, size_t size, size_t nmemb, string *buffer);
string curl_httpget(const string &url);
string iAddress;
string iSource;

string curl_httpget(const string &url)
{
    string buffer;

    CURL *curl;
    CURLcode result;

    curl = curl_easy_init();

    if (curl)
    {
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str()  );
        curl_easy_setopt(curl, CURLOPT_HEADER, 0);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);

        result = curl_easy_perform(curl);//http get performed

        curl_easy_cleanup(curl);//must cleanup

        //error codes: http://curl.haxx.se/libcurl/c/libcurl-errors.html
        if (result == CURLE_OK)
        {
            return buffer;
        }
        //curl_easy_strerror was added in libcurl 7.12.0
        //cerr << "error: " << result << " " << curl_easy_strerror(result) <<endl;
        return "";
    }

    cerr << "error: could not initalize curl" << endl;
    return "";
}

int writer(char *data, size_t size, size_t nmemb, string *buffer)
{
    int result = 0;
    if (buffer != NULL)
    {
        buffer->append(data, size * nmemb);
        result = size * nmemb;
    }
    return result;
}

#pragma once

namespace CodeViewer {

    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;

    /// <summary>
    /// Summary for Form1
    /// </summary>
    public ref class Form1 : public System::Windows::Forms::Form
    {
    public:
        Form1(void)
        {
            InitializeComponent();
            //
            //TODO: Add the constructor code here
            //
        }

    protected:
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        ~Form1()
        {
            if (components)
            {
                delete components;
            }
        }
    private: System::Windows::Forms::RichTextBox^  OutputBox;
    protected: 
    private: System::Windows::Forms::TextBox^  AddressBar;
    private: System::Windows::Forms::Button^  btnGo;

    protected: 

    private:
        /// <summary>
        /// Required designer variable.
        /// </summary>
        System::ComponentModel::Container ^components;

#pragma region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        void InitializeComponent(void)
        {
            this->OutputBox = (gcnew System::Windows::Forms::RichTextBox());
            this->AddressBar = (gcnew System::Windows::Forms::TextBox());
            this->btnGo = (gcnew System::Windows::Forms::Button());
            this->SuspendLayout();
            // 
            // OutputBox
            // 
            this->OutputBox->Location = System::Drawing::Point(12, 80);
            this->OutputBox->Name = L"OutputBox";
            this->OutputBox->Size = System::Drawing::Size(640, 228);
            this->OutputBox->TabIndex = 1;
            this->OutputBox->Text = L"";
            // 
            // AddressBar
            // 
            this->AddressBar->Location = System::Drawing::Point(12, 52);
            this->AddressBar->Name = L"AddressBar";
            this->AddressBar->Size = System::Drawing::Size(593, 22);
            this->AddressBar->TabIndex = 2;
            this->AddressBar->TextChanged += gcnew System::EventHandler(this, &Form1::AddressBar_TextChanged);
            // 
            // btnGo
            // 
            this->btnGo->Location = System::Drawing::Point(611, 51);
            this->btnGo->Name = L"btnGo";
            this->btnGo->Size = System::Drawing::Size(41, 23);
            this->btnGo->TabIndex = 3;
            this->btnGo->Text = L"GO";
            this->btnGo->UseVisualStyleBackColor = true;
            this->btnGo->Click += gcnew System::EventHandler(this, &Form1::btnGo_Click);
            // 
            // Form1
            // 
            this->AutoScaleDimensions = System::Drawing::SizeF(8, 16);
            this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
            this->ClientSize = System::Drawing::Size(664, 320);
            this->Controls->Add(this->btnGo);
            this->Controls->Add(this->AddressBar);
            this->Controls->Add(this->OutputBox);
            this->Name = L"Form1";
            this->Text = L"Code Viewer 0.0.0.1";
            this->Load += gcnew System::EventHandler(this, &Form1::Form1_Load);
            this->ResumeLayout(false);
            this->PerformLayout();

        }
#pragma endregion

    private: System::Void MarshalString ( System::String^ s, std::string& os )
            {
                using namespace System::Runtime::InteropServices;
                const char* chars = (const char*)(Marshal::StringToHGlobalAnsi(s)).ToPointer();
                os = chars;
                Marshal::FreeHGlobal(IntPtr((void*)chars));
            }
    private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e) {
             }
    private: System::Void btnGo_Click(System::Object^  sender, System::EventArgs^  e) {
                 iSource = curl_httpget(iAddress);
                 String^ mySource = gcnew String(iSource.c_str());
                 OutputBox->Text = mySource;
             }
    private: System::Void AddressBar_TextChanged(System::Object^  sender, System::EventArgs^  e) {
                 System::String^ myAddress = AddressBar->Text;
                 MarshalString(myAddress, iAddress);
             }
    };
}

Как сказал Сет выше, мне нужно было переместить функции CURL за пределы класса формы. Затем у меня возникла проблема со строковым типом, потому что функции CURL возвращают std::string, а мне нужно, чтобы строка была System::String^ для класса формы. Решение заключалось в использовании функции MarshalString() для преобразования строки, полученной моей функцией CURL, из std::string в System::String^ перед передачей значения в OutputBox->Text. То же самое справедливо и для AddressBar->Text. Приведенное выше решение выполняет все это и компилируется без ошибок или предупреждений. Кроме того, программа делает именно то, что я ожидал. :)

person Jason    schedule 23.08.2011