4

I am making a Winamp plugin with the single function of sending the details of the song being played over HTTP to a webpage/webserver. I couldn't find anything like this that actually works, so decided to dive in for the first time into C++ and just do it myself.

The plugin is a C++ DLL file. I have got to the point where I can output the song title to a windows message box every time a new song is played (not bad for a C++ first-timer! ;) )

Where am I stuck:

I couldn't find anything to get me in the push notification on C++ direction AT ALL.

I tried, without any success, embedding/including HTTP libraries into my DLL to try post requests instead. libcurl, this library here, and this library too - but couldn't get any of them to work! I just kept getting linking errors and then some. After a few hours I just gave up.

I am a very skilled JavaScript programmer so I thought perhaps using JS to connect into a push notification service can work out but running JS inside C++ looks too complicated to me. Perhaps I'm wrong?

Bottom line, I don't know which solution is better (read: easier) to implement - push notifications or post requests?

I appreciate any solutions/input/directions/information or whatever you got. I can post code but the only code I have is the part getting the song information from Winamp and that part works.

Thanks in advance.

EDIT: I guess it's worth noting I'm using the new VS2012?

pilau
  • 6,635
  • 4
  • 56
  • 69
  • Libcurl seems a nice HTTP implementation. What are the errors you get when trying to use it? – lvella Sep 14 '12 at 15:48
  • Can you describe how you tried to include the HTTP libraries? Those are your best hope of a simple solution and linking libraries can be tricky, if you don't know exactly what you are doing. – Wutz Sep 14 '12 at 15:48
  • Thanks for the super prompt reply guys. In the meanwhile I have moved on from C++ to C# using a library called Sharpamp. I still want to bring a solution to this problem though, so I'll free up some time ASAP and get back to you. Cheers. – pilau Sep 15 '12 at 09:21
  • sorry to bump a 2-year old thread, I am looking to do something similar -- to log info of songs played. But I am stuck on how to get notified on song change. could you please share some info on how you managed to 'output the song title to a windows message box every time a new song is played'. TIA – mzzzzb Jul 17 '14 at 17:32
  • @mzzzzb Unfortunately I don't have that code anymore - that hard drive is long lost :( Eventually I used Sharpamp which allows you to create Winamp plugins with C#/.NET, which is a lot easier than C++ (for me, at least). Hope it helps – pilau Jul 17 '14 at 18:36
  • @pilau ouch, sorry to hear about your disk, thanks for your lead on Sharpamp will dig a bit deeper on that. thanks again – mzzzzb Jul 17 '14 at 19:09
  • @mzzzzb Sure bro. Hope it works out for you – pilau Jul 17 '14 at 19:18

1 Answers1

3

That's OK to use these kind of libraries, but when speaking of C++, I mainly think of "pure code", so I like the native style to do something, and do it without the help of libraries.

Thinking of that, I can provide you an example on how to send a HTTP's POST request, using Microsoft's winsock2 library.

First, I'd like to point out that I used this link to have a base on how to use winsock2.h.

Ok, now going on to the code, you need these includes:

#include <string>
#include <sstream>
#include <winsock2.h>

You'll also need to link winsock2 library (or specify it in your project settings, as you use VS2012):

#pragma comment(lib, "ws2_32.lib")

Now, the function I edited from the link (I've just edited it to make it look simpler, and also to do some error check and proper cleanup):

int http_post(char *hostname, char *api, char *parameters, std::string& message)
{
    int result;

    WSADATA wsaData;
    result = WSAStartup(MAKEWORD(1, 1), &wsaData);

    if(result != NO_ERROR)
    {
        //printf("WSAStartup failed: %d\n", result);
        return 0;
    }

    sockaddr_in sin;

    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if(sock == INVALID_SOCKET)
    {
        //printf("Error at socket(): %ld\n", WSAGetLastError());
        WSACleanup();
        return 0;
    }

    sin.sin_family = AF_INET;
    sin.sin_port = htons(80);

    struct hostent *host_addr = gethostbyname(hostname);
    if(host_addr == NULL)
    {
        //printf("Unable to locate host\n");
        closesocket(sock);
        WSACleanup();
        return 0;
    }

    sin.sin_addr.s_addr = *((int *)*host_addr->h_addr_list);

    if(connect(sock, (const struct sockaddr *)&sin, sizeof(sockaddr_in)) == SOCKET_ERROR)
    {
        //printf("Unable to connect to server: %ld\n", WSAGetLastError());
        closesocket(sock);
        WSACleanup();
        return 0;
    }

    std::stringstream stream;
    stream << "POST " << api << " HTTP/1.0\r\n"
           << "User-Agent: Mozilla/5.0\r\n"
           << "Host: " << hostname << "\r\n"
           << "Content-Type: application/x-www-form-urlencoded;charset=utf-8\r\n"
           << "Content-Length: " << strlen(parameters) << "\r\n"
           << "Accept-Language: en-US;q=0.5\r\n"
           << "Accept-Encoding: gzip, deflate\r\n"
           << "Accept: */*\r\n"
           << "\r\n" << parameters
    ;

    if(send(sock, stream.str().c_str(), stream.str().length(), 0) == SOCKET_ERROR)
    {
        //printf("send failed: %d\n", WSAGetLastError());
        closesocket(sock);
        WSACleanup();
        return 0;
    }

    if(shutdown(sock, SD_SEND) == SOCKET_ERROR)
    {
        //printf("shutdown failed: %d\n", WSAGetLastError());
        closesocket(sock);
        WSACleanup();
        return 0;
    }

    char buffer[1];

    do
    {
        result = recv(sock, buffer, 1, 0);
        if(result > 0)
        {
            //printf("Bytes received: %d\n", result);
            message += buffer[0];
        }
        else if(result == 0)
        {
            //printf("Connection closed\n");
        }
        else
        {
            //printf("recv failed: %d\n", WSAGetLastError());
        }
    }
    while(result > 0);

    closesocket(sock);
    WSACleanup();

    return 1;
}

As you are using a DLL, I commented out the printfs, so you can use your proper output function according to the Winamp plugin API.

Now, to use the function, it's self-explanatory:

std::string post;
http_post("www.htmlcodetutorial.com", "/cgi-bin/mycgi.pl", "user=example123&artist=The Beatles&music=Eleanor Rigby", post);

By doing this, and checking the function return number (either 1 for success or 0 for failure), you can parse the result string returned from the site, and check if everything went OK.

About your last question, using POST request is OK, but you, as a webmaster, know that it's possible to "abuse" this option, such as request flooding, etc. But it really is the simplest way.

If you, by the server side part, parse the data correctly, and check for any improper use of the request, then you can use this method without any problems.

And last, as you are a C++ beginner, you'll need to know how to manipulate std::strings for parsing the song name and artist to the post POST message string safely, if you don't know yet, I recommend this link.

Toribio
  • 3,963
  • 3
  • 34
  • 48
  • 1
    Fantastic answer! I always think doing things your own make for the best solutions however as I have virtually no clue of C++ I unfaithfully resorted into libraries.. Ironically I couldn't get them to work as well, LOL. I still have so much to learn! I eventually used a library called Sharpamp that enables programming Winamp plugins with C#, and PubNub as a push notification service. Since I have to use PNs in my solution anyway, I decided to cut out the webserver from the process, sending the notifications directly from the DLL itself. Cheers! – pilau Sep 17 '12 at 10:46