开发者

How to use libcurl functions inside a Windows Forms Application in Visual C++ 2010?

Here is the situation...

I am using Visual C++ 2010 Express.

I am trying to create a windows forms application that can load the source code of any given URL into a RichTextBox. I wish to accomplish this using the cUrl library. The good news is, after beating my brains out for more than 24 hours on how to properly link the libcurl.dll file to my project I was successful. In fact I was able to use the cUrl library to retrieve the source of a url in a Windows console application and print the result to a command window. So I do not have linking problems.

For reference, below is the source code for the main CPP file for the working test console app:

// 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;
}

Below is the code for my main project CPP file for my Windows Forms Application "Code Viewer". The includes work fine here. I setup all the necessary paths for include and lib, etc. I am compiling with /CLR (not pure):

// 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;
}

Below is the code of my Form1.h for the "Code Viewer" app:

#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;
             }
    };
}

I am brand new to C++ and I am just learning how to build Windows Forms Applications so basically I don't know what the heck I'm doing. I need to be able to call these cU开发者_如何学Pythonrl functions from inside the Form1.h and I have no idea how to do this. I want the "GO" button to execute a function call to retrieve the HTML source code of the URL typed into AddressBar using cUrl. I have probably done in excess of 100 Google searches trying to figure this one out and I am hitting the wall. I've been searching stackoverflow with the same results. Always sorta kinda close but not what I'm looking for. I'm sure there must be a way to do this.

Please be detailed in the response. I probably won't understand a technical explanation that does not include some sample source code.

Thanks very much in advance!


UPDATE: After some more tinkering and tweeking of this code on the advice of Seth (see comments below) I was able to get my code nearly functional. See the above edited version of Form1.h. I still have one remaining compiler error, but I think I am close to understanding why I have that error. Following is that error code:

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

While my console app had no problem with this code it appears that calling the writer() function without parameters is a problem here. Right now I'm guessing the solution is to feed it the parameters that it wants, but until I try that I won't know. It's late so I'm going to bed. Tomorrow I'll research the parameters needed for the CURLOPT_WRITEFUNCTION...


SOLVED!!! ;))) WOOHOO!! See solution below (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);
             }
    };
}

As Seth stated above, I needed to move the CURL functions outside of the form class. Then I had a string type problem because the CURL functions return a std::string and I needed the string to be a System::String^ for the form class. The solution there was to use the MarshalString() function to convert the string retrieved by my CURL function from std::string to System::String^ before passing the value to OutputBox->Text. The same also holds true for AddressBar->Text. The solution above accomplishes all that and compiles clean with no errors or warnings. Also the program does exactly what I expected it to do. :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜