Как использовать функции libcurl в приложении Windows Forms в Visual C ++ 2010? - PullRequest
2 голосов
/ 23 августа 2011

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

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

Я пытаюсь создать приложение для форм Windows, которое может загрузить исходный код любого заданного 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 «Просмотр кода». Включает отлично работать здесь. Я устанавливаю все необходимые пути для 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 для приложения «Просмотр кода»:

#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

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

1 Ответ

3 голосов
/ 23 августа 2011

РЕШИТЬ !!!;))) WOOHOO !!См. Решение ниже (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.Приведенное выше решение выполняет все это и компилирует без ошибок и предупреждений.Также программа делает именно то, что я ожидал.:)

...