Configuring Apache Thrift for Visual Studio 2012

Summary

Yes, it’s true: you can run and compile Apache Thrift servers and clients in Visual Studio 2012! Here’s a set of steps for compiling Apache Thrift under Visual Studio 2012 and then running a C++ Server and Client pair. I’ve also included steps to build a Java client which interacts with the C++ server, to demonstrate Thrift’s cross-language capabilities.

The current version of Thrift was 0.9.1 at the time of writing.

 

Build Apache Thrift libraries for VS2012

  1. Prerequisite: Download and build the Boost libraries for VS2012: see Configuring C++ Boost Libraries for Visual Studio. Note your {boost_install_dir} for later.
  2. Download Apache Thrift AND the Thrift compiler for Windows from http://thrift.apache.org/download
  3. Download libevent, a C++ dependency for the non-blocking Thrift Server: http://libevent.org/
  4. Extract both tar.gz files once downloaded.
  5. Navigate to {thrift_install_dir}\lib\cpp and open thrift.sln with VS2012
  6. If a dialog box appears that asks to upgrade some files to VS2012, click Upgrade
  7. Open the Visual Studio 2012 Command Prompt at Start->All Programs->Microsoft Visual Studio 2012 ->Visual Studio Tools -> Visual Studio Command Prompt (2012)
  8. In the VS2012 CMD Prompt, cd into the unzipped libevent directory. You should see subdirectories like “compat”, “include”, and “test” if it is the correct directory.
  9. Run this command in the CMD prompt to build libevent for VS2012: nmake -f Makefile.nmake
  10. Note your {libevent_install_dir}.
  11. Once the command has finished, go back to VS2012 (thrift.sln), right-click libthrift’s Project Properties, and navigate to C/C++->General.
  12. Enter this information into the Additional Include Directories line:
    {boost_install_dir}\boost_1_56_0;{boost_install_dir}\boost_1_56_0\boost
  13. Right-click libthriftnb’s Project Properties, and navigate to C/C++->General.
  14. Enter this information into the Additional Include Directories line:
    {boost_install_dir}\boost_1_56_0;{libevent_install_dir};{libevent_install_dir}\include;{libevent_install_dir}\WIN32-Code;
  15. Counter-intuitive, but necessary: Remove BoostThreadFactory.cpp from the libthrift project. This file causes compilation issues and must be removed in order to continue.
  16. Trigger a Rebuild of both projects, and note the directory where libthrift.lib and libthriftnb.lib are created. This will depend on your configuration for Debug or Release.

Build your Thrift Server and Client

This section will guide you through the Apache Thrift tutorial for C++ in VS2012, found at http://thrift.apache.org/tutorial/cpp

Building and Running the C++ Thrift server

  1. Download the tutorial.thrift and shared.thrift files from Apache:
  2. Run these two Thrift compiler commands to generate C++ for both of these service definition files:
    • thrift -r --gen cpp tutorial.thrift
    • thrift -r --gen cpp shared.thrift
  3. This will generate a gen-cpp directory in the same directory that thrift.exe was run from.
  4. Copy this gen-cpp folder into a Visual Studio Project, and separate the header and source files into the appropriate folders.

    All of the gen-cpp files imported into a VC++ project.

    All of the gen-cpp files imported into a VC++ project.

  5. Next, we must fix some convenient gotchas to bring the tutorial to life.
  6. Right-click your project in the Solution Explorer and go to Properties, then C/C++->All Options, and set Additional Include Directories to:
    {boost_install_dir};{thrift_install_dir}\lib\cpp\src\thrift\windows;{thrift_install_dir}\lib\cpp\src;
  7. Then, go to Linker->All Options, and set Additional Dependencies to:
    libboost_thread-vc110-mt-gd-1_56.lib;libboost_chrono-vc110-mt-gd-1_56.lib;libthrift.lib;%(AdditionalDependencies)

    Note: the version numbers for these libraries may differ if you’ve built a different version of Boost.

  8. Click on Linker->All Options, and set Additional Library Directories to:
    {boost_install_dir}\stage\lib;{thrift_install_dir}\lib\cpp\Debug

    The second item in the path could end with \Release, if you used the Release configuration instead of Debug while compiling Thrift.

  9. Double-click Calculator_server.skeleton.cpp. We need to adjust the main() method for the Server.
  10. Place this code at the top of the int main(int argc, char **argv) method:
      	WSADATA wsaData = {};
        WORD wVersionRequested = MAKEWORD(2, 2);
        int err = WSAStartup(wVersionRequested, &wsaData);
    
  11. Trigger a rebuild. If all goes well, then your project should compile without a hitch, and then you should be able to Run the project and see your C++ Thrift server starting up!

 

Building and running the C++ Thrift client

  1. Create another VS2012 project for the Thrift C++ client.
  2. Download the CppClient.cpp file and copy its contents to a new Source file in your new Client project:
    https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob;f=tutorial/cpp/CppClient.cpp;hb=HEAD
  3. Copy the gen-cpp folder from the Server application into your Client project, and add all of the files to the project. You need two separate copies of these files, or there will be two int main() functions defined in both your server and client, which is not fun to resolve.
  4. Erase or remove the files Calculator_server.skeleton.cpp and SharedService_server.skeleton.cpp from your project.
  5. Right-click your project in the Solution Explorer and go to Properties, then C/C++->All Options, and set Additional Include Directories to:
    {boost_install_dir};{thrift_install_dir}\lib\cpp\src\thrift\windows;{thrift_install_dir}\lib\cpp\src;
  6. Then, go to Linker->All Options, and set Additional Dependencies to:
    libboost_thread-vc110-mt-gd-1_56.lib;libboost_chrono-vc110-mt-gd-1_56.lib;libthrift.lib;%(AdditionalDependencies)

     Note: the version numbers for these libraries may differ if you’ve built a different version of Boost.

  7. Then, go to Linker->All Options, and set Additional Library Directories to:
    {boost_install_dir}\stage\lib;{thrift_install_dir}\lib\cpp\Debug

    The second item in the path could end with \Release, if you used the Release configuration instead of Debug while compiling Thrift.

  8. Trigger a rebuild. If all is well, it should succeed.
  9. Ensure that your Server is running, and run your Client. Thrift lives!
Eureka! The Thrift server accepts commands and responds to them.

Eureka! The Thrift server accepts commands and responds to them.

Addendum: Building and Running a Java Client for your C++ Service

Let’s explore the cross-language power of Thrift by creating a Java client which can talk to the C++ service.

Create Thrift files, and import them into Eclipse

  1. Create a new Java Project in Eclipse.
  2. Run these two Thrift compiler commands to generate Java code for both of these service definition files:
    • thrift -r --gen java tutorial.thrift
    • thrift -r --gen java shared.thrift
  3. This will create a gen-java folder wherever thrift.exe is located. Import all of these files into your Java Project, and sort them into the correct packages: some files will go into a package named ‘tutorial’ and others will go into a package named ‘shared’.
  4. Create a Java file named JavaClient.java, and paste the contents of the sample JavaClient.java into it
    https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob_plain;f=tutorial/java/src/JavaClient.java;hb=HEAD
  5. So many red marks! Let’s work on fixing those…

 

Build all Java libraries and compile the client

Thrift has two dependencies in Java: ant and slf4j.

  1. Download the latest Apache Ant binary from here: http://ant.apache.org/bindownload.cgi
  2. Download the latest SLF4J binary from here: http://www.slf4j.org/download.html
  3. Unzip both downloads and note their locations.
  4. We won’t be able to build Ant files until we configure Ant correctly.
    Follow Steps 1-5 here: http://ant.apache.org/manual/install.html#getting
  5. Next, download and unzip this maven-ant-tasks library, which we will need for the Thrift compilation:
    http://maven.apache.org/ant-tasks/download.cgi
  6. Copy the unzipped maven-ant-tasks-2.1.3.jar into {ant_install_directory}\lib
  7. Time to build Thrift’s Java library.
  8. Navigate to {thrift_install_dir}\lib\java
  9. Open a Command Prompt, and type ant
    If you’ve set up Ant and the maven-ant-tasks JAR correctly, you will see BUILD SUCCEEDED
  10. Copy the path for the newly-built Thrift Java library, located at “{thrift_install_dir}\lib\java\build\libthrift-0.9.1.jar”
  11. In Eclipse, right-click your project’s JRE System Library in the project explorer, and choose Build Path->Configure Build Path
  12. Click on the Libraries tab and Add External JAR, then add the Thrift Java library.
  13. Finish up by adding the SLF4J dependency to Eclipse.
  14. In Eclipse, right-click your project’s *JRE System Library in the project explorer, and choose Build Path->Configure Build Path
  15. Click on the Libraries tab and Add External JAR, then add the SLF4J jar, located at “{slf4j_install_directory}\slf4j-api-1.7.7.jar”
  16. Ensure that your Thrift server is up, and run your Java project as a Java Application through Eclipse. It works!
  17. If the program exits with a message like “Please enter ‘simple’ or ‘secure'”, then you are missing a program argument. Right-click your project, click Run->Run Configurations, and click on the Arguments tab. Add the word simple or secure to the Program Arguments dialog box, then click Run.

 

Related Links

  1. The Apache/Facebook developers were very Thrifty with their documentation, and produced a one-page tutorial: http://thrift.apache.org/tutorial/cpp
  2. A blog post with instructions for Visual Studio 2010 can be found here: http://technicaltidbit.blogspot.com/2012/12/apache-thrift-in-windows.html
Advertisements

Configuring C++ Boost Libraries for Visual Studio

Summary

Here is a basic guide to getting C++ Boost libraries to work within Visual Studio.

More information about these handy libraries is available here: http://www.boost.org/

 

Installation

 

Download the Boost libraries

  1.  Determine the version of Visual Studio that you will be using for your project. For example, Visual Studio C++ 2012 is also known as version 11. You can find this out by clicking HELP > About Visual Studio and looking for the “Version ##” line on the screen.
  2. Determine whether you will be building a 32-bit or 64-bit version of the Boost libraries.
  3. Now that you have determined these two pieces of information, navigate to http://sourceforge.net/projects/boost/files/boost-binaries/, click on the folder for the latest version, and download the correct .exe installer for your Visual Studio version and architecture.
  4. Once the download is finished, run the installer as normal and note the installation directory.

Build the libraries for your machine

  1. Open the Developer Command Prompt for VS20xx under Start > Microsoft Visual Studio 20xx > Visual Studio Tools

    vs_dev

    The location of the Developer Command Prompt for VS2012.

  2. Once the Developer Command Prompt is open, type in cd <your Boost install directory>
  3. Next, type these commands to build the Boost libraries for your machine: bootstrap and then b2. This last command will run for about ten minutes, so take a break!
  4. Once b2 has finished, you should see it print two lines noting “compiler include paths” and “linker library paths”. Write these paths down or take a screenshot.
boost

The Boost build has finished, and Boost tells us where to find the new libraries.

Link the libraries through Visual Studio

  1. Open your C++ project in Visual Studio 20xx. Right click its name in the Solution Explorer and go to Properties.
  2. Click on Configuration Properties, then C/C++, and then All Options.
  3. Modify Additional Include Directories: append the “compiler include paths” string to the end of it.
  4. Click on Configuration Properties, then Linker, and then All Options.
  5. Modify Additional Library Directories: append the “linker library paths” string to the end of it.
  6. Click OK to save your changes. Ensure that you are including the specific Boost libraries like this at the top of your source files: #include <boost/foreach.hpp>
  7. Try to Rebuild your project. Hopefully, all of your library issues will now be resolved! Happy coding!

 

Other Resources

Parts of this write-up were figured out through Boost’s Visual Studio page.

Saxon XML Parser: XSD Validation

Here’s how to get Saxon XML parser (http://www.saxonica.com/documentation/about/intro.xml) to perform XSD validation on an XML file of your choice in C#:

static void Main(string[] args)
    {
        try
        {
            errors = new ArrayList();
            Saxon.Api.Processor proc = new Processor(true);
            proc.SetProperty(
            net.sf.saxon.lib.FeatureKeys.
            VALIDATION_WARNINGS,"true");

            //this is the property to set!

            SchemaManager schemaManager = proc.SchemaManager;

            FileStream xsdFs = new FileStream(@"C:\path\to.xsd", FileMode.Open);

            schemaManager.Compile(XmlReader.Create(xsdFs));
            SchemaValidator schemaValidator = schemaManager.NewSchemaValidator();

            FileStream xmlFs = new FileStream(@"C:\path\to.xml", FileMode.Open);

            schemaValidator.SetSource(XmlReader.Create(xmlFs));
            schemaValidator.ErrorList = errors;
            schemaValidator.Run();
        }
        catch(net.sf.saxon.type.ValidationException e)
        {
            foreach(StaticError error in errors)
            {
                Console.WriteLine(error.ToString());
            }
            Console.ReadKey(true);
            Environment.Exit(0);

        }

        foreach (StaticError error in errors)
        {
            Console.WriteLine(error.ToString());
        }
        Console.ReadKey(true);

If you don’t set FeatureKeys.VALIDATION_WARNINGS, Saxonica will throw a ValidationException after catching the first validation warning.

Source: Taken from my answer on StackOverflow: http://stackoverflow.com/questions/7351478/xsd-validation-using-saxon-api/7905598#7905598

System.Net.Mail: “File is being used by another process.”

Problem: You’re using System.Net.Mail to send an email which has an attachment; you send the email with an attachment and then try to delete the file that you had attached to that email:

The process cannot access the file ‘[your attachment file]’ because it is being used by another process.

Potential Solution: Make sure to call MailMessage.Dispose() after sending the email. This will free all holds on the attachment file and allow you to delete it.

MailMessage mail = new MailMessage();

mail.From = new MailAddress("email_address_from@gmail.com");
mail.To.Add("email_address_to@gmail.com");
mail.Subject = "the email subject";
mail.Body = "the email body";

FileInfo attachment = new FileInfo("path/to/attachment.file");

attachmentItem = new Attachment(attachment);
mail.Attachments.Add(attachmentItem);

SmtpClient smtp = new SmtpClient("smtp.gmail.com",587);

smtp.EnableSsl = true;

smtp.Credentials = new System.Net.NetworkCredential("email_address_from@gmail.com", "email_password");

smtp.Send(mail);

mail.Dispose(); //<-- this did the trick!

if (attachment != null)
   File.Delete(attachment.FullName);

This bug took me an hour to sort out >_< . In general, step through your code slowly in order to find the root cause of a file lock.

C# Excel automation: creating a macro-enabled Excel spreadsheet via a C# Application

Just last week, I needed to write an application that could create an Excel spreadsheet and fill it with data automatically. I surveyed the possible solutions and eventually settled upon the Microsoft Excel Component Object Model (COM) library.

Below is a step-by-step example which 1. creates an Excel spreadsheet, 2. fills it with relevant data, 3. adds a VBA macro to the spreadsheet, and 4. saves the file so that others can edit it.

Another good example can be found here: http://support.microsoft.com/kb/302084

*Because my application needed a GUI, I created my project as a Windows Forms project. You can easily adapt this example to any other project type (including a Console Application) if needed.*

Before you begin, you will need to have a copy of Microsoft Office 2003 (or above) installed on your computer.

Create a new C# Windows Forms project in Visual Studio and give it a name. Then, add some References to your project:

Click on the COM tab and add: Microsoft Visual Basic for Applications Extensibility and Microsoft Excel Object Library

Adding the VBIDE library reference to the project.

Adding the VBIDE library reference to the project.

Adding the Excel COM library reference to the project.

Adding the Excel COM library reference to the project.

Here’s my project with the references (labeled as VBIDE and Microsoft.Office.Interop.Excel) added:

The project with the references added.

The project with the references added.

I wrote two utility methods to help with graphics: encloseCellWithBorder will enclose a range of cells with a single-line border and setInterior will set the interior color of a range of cells.

In order to allow Excel to create the macro, you will need to change some security settings in Microsoft Excel: http://support.microsoft.com/kb/282830

Here’s the code that goes within the Form1.cs file:

using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
using System.Configuration;
using VBIDE = Microsoft.Vbe.Interop;
using System.Runtime.InteropServices;

namespace ExcelApp
{
   public partial class Form1 : Form
   {
      Excel.Application xlApp;
      Excel.Workbook xlWorkBook;
      Excel.Worksheet xlWorkSheet;
      int excelActiveRow;

      /// <summary>
      /// Sets border of cells from beginCell to endCell to a single-line width border all around.
      /// </summary>
      /// <param name="ws">The worksheet to modify cells in</param>
      /// <param name="beginCell">The cell to begin the border change at, e.g. "A1"</param>
      /// <param name="endCell">The cell to end the border change at, e.g. "C2"</param>
      private void encloseCellWithBorder(Excel.Worksheet ws, string beginCell, string endCell)
      {
         Excel.Range oResizeRange = ws.get_Range(beginCell, endCell);
         oResizeRange.Borders.LineStyle = Excel.XlLineStyle.xlContinuous;
         oResizeRange.Borders.Weight = Excel.XlBorderWeight.xlThin;
      }

      /// <summary>
      /// Sets color of cells from beginCell to endCell to the specified color.
      /// </summary>
      /// <param name="ws">The worksheet to modify cells in</param>
      /// <param name="beginCell">The cell to begin the color change at, e.g. "A1"</param>
      /// <param name="endCell">The cell to end the color change at, e.g. "C2"</param>
      /// <param name="color">The System.Drawing.Color color to give the cell interior</param>
      private void setInterior(Excel.Worksheet ws, string beginCell, string endCell, System.Drawing.Color color)
      {
         Excel.Range oResizeRange = ws.get_Range(beginCell, endCell);
         oResizeRange.Interior.Color = System.Drawing.ColorTranslator.ToOle(color);
      }

      /// <summary>
      /// The "main" method of the GUI - creates an Excel spreadsheet, fills it with some data, attaches a macro, and saves the spreadsheet to the C:/ drive.
      /// </summary>
      private void button1_Click(object sender, EventArgs e)
      {
         //the first row of the excel spreadsheet is row 1. We maintain the current active row of the spreadsheet as a variable for easy tracking.
         excelActiveRow = 1;

         //initialize the Excel application and make it invisible to the user.
         xlApp = new Excel.Application();
         //This is a must for lengthy spreadsheets - the Excel COM library does not like it if a user clicks around in the spreadsheet while it is being created.
         xlApp.UserControl = false;
         xlApp.Visible = false;

         //Create the Excel workbook and worksheet - and give the worksheet a name.
         xlWorkBook = (Excel.Workbook)(xlApp.Workbooks.Add(Missing.Value));
         xlWorkSheet = (Excel.Worksheet)xlWorkBook.ActiveSheet;
         xlWorkSheet.Name = "worksheet_name";

         /*insert some values into the Excel spreadsheet here.
         This code will create a spreadsheet like this:
         A       |        B        | C
         Value 1 | Another Value 1 | 4
         Value 2 | Another Value 2 | 2
                 |                 | 6
         */

         //to insert a value into an Excel cell, use the .Cells[Row, column] property of the Excel.WorkSheet class.

         //first row: Value 1 | Another Value 1 | 4
         xlWorkSheet.Cells[excelActiveRow, 1] = "Value 1";
         //enclose the cell in column A with a single-line border.
         encloseCellWithBorder(xlWorkSheet, "A" + excelActiveRow, "A" + excelActiveRow);
         xlWorkSheet.Cells[excelActiveRow, 2] = "Another Value 1";
         xlWorkSheet.Cells[excelActiveRow, 3] = 4;
         excelActiveRow++; //advance to the next row.

         //second row: Value 2 | Another Value 2 | 2
         xlWorkSheet.Cells[excelActiveRow, 1] = "Value 2";
         //fill the cell in column A with red color.
         setInterior(xlWorkSheet,"A" + excelActiveRow,"A" + excelActiveRow, System.Drawing.Color.FromArgb(255, 0, 0));
         xlWorkSheet.Cells[excelActiveRow, 2] = "Another Value 2";
         xlWorkSheet.Cells[excelActiveRow, 3] = 2;
         excelActiveRow++; //advance to the next row.

         //third row:         |                 | 6
         //attach a formula to this cell: SUM(C1,C<excelActiveRow>-1)
         xlWorkSheet.get_Range("C" + excelActiveRow, "C" + excelActiveRow).Formula = "=SUM(C1:C" + (excelActiveRow-1)+")";

         //modify the font styling of the entire workbook to Calibri, size 10.
         xlWorkSheet.get_Range("A1", "B" + excelActiveRow).Font.Name = "Calibri";
         xlWorkSheet.get_Range("A1", "B" + excelActiveRow).Font.Size = "10";

         //add the macro to the excel workbook here.
         Microsoft.Vbe.Interop.VBComponent xlMod;
         xlMod = xlWorkBook.VBProject.VBComponents.Add(VBIDE.vbext_ComponentType.vbext_ct_StdModule);
         xlMod.Name = "Module1";

         /*place the VB macro code within a string first.
         Make sure that you maintain spacing levels for code that goes inside Subs.
         End each line with \r\n to tell VBA to start a new line.
         */
         string macroCode = "Sub main()\r\n" +
         "   MsgBox \"Hello world\"\r\n" +
         "end Sub";

         xlMod.CodeModule.AddFromString(macroCode);

         //save the workbook to "C:\workbook.xlsm";
         string fullPath = "C:\\workbook.xlsm" ;

         try
         {//save the workbook.
            xlWorkBook.SaveAs(fullPath, Excel.XlFileFormat.xlOpenXMLWorkbookMacroEnabled,
            null, null, false, false, Excel.XlSaveAsAccessMode.xlNoChange, false, false, null, null, null);
         }
         catch (Exception ex)
         {
            //release all memory - stop EXCEL.exe from hanging around.
            if (xlMod != null) { Marshal.ReleaseComObject(xlMod); }
            if (xlWorkBook != null) { Marshal.ReleaseComObject(xlWorkBook); }
            if (xlWorkSheet != null) { Marshal.ReleaseComObject(xlWorkSheet); }
            if (xlApp != null) { Marshal.ReleaseComObject(xlApp); }
            xlMod = null;
            xlWorkBook = null;
            xlWorkSheet = null;
            xlApp = null;
            GC.Collect();

            Environment.Exit(0);
         }
         xlApp.Quit();

         //release all memory - stop EXCEL.exe from hanging around.
         if (xlMod != null) { Marshal.ReleaseComObject(xlMod); }
         if (xlWorkBook != null) { Marshal.ReleaseComObject(xlWorkBook); }
         if (xlWorkSheet != null) { Marshal.ReleaseComObject(xlWorkSheet); }
         if (xlApp != null) { Marshal.ReleaseComObject(xlApp); }
         xlMod = null;
         xlWorkBook = null;
         xlWorkSheet = null;
         xlApp = null;
         GC.Collect();
      }

   }
}
The finished Excel spreadsheet and attached macro.

The finished Excel spreadsheet and attached macro.

So there you have it! This code will create and save a macro-enabled spreadsheet to the C:/ drive. This solution worked well for my application!

Don’t forget to change your security settings for Excel: http://support.microsoft.com/kb/282830

I’ve attached the code for Form1.cs as a PDF: Form1

ZeepMobile: Sending and Receiving Messages in ASP.NET / C#

Last week, I started working with the ZeepMobile API, which lets anyone send SMS messages to subscribed users for free (with ads) or for a monthly fee: See the ZeepMobile site for more details.

My application needed to be created in ASP.NET / C#, and while the ZeepMobile site provided me with a good example on how to send messages correctly via these technologies, I did not find any examples on how to receive and respond to messages sent by subscribers. Here’s how I set up both ends for ZeepMobile’s service in C#/ASP.NET.


Sending Messages

http://aboutdev.wordpress.com/2009/02/09/zeep-mobile-sms-with-ads/  talks about sending messages to your subscribed users. I made some modifications to the code given there. Since this is originally AboutDev’s code, you’ll have to head over to that site and follow my modifications below.

1. Create a new .cs Class within your Website project in Visual Studio. Name it sendMessage.cs. If Visual Studio asks, be sure to place the file in the “App_Code” folder.

Adding a new .cs Class to the Website.

Adding the sendMessage.cs Class to the Website.

2. Copy the code from http://aboutdev.wordpress.com/2009/02/09/zeep-mobile-sms-with-ads/ and paste it into your new sendMessage.cs file.

3. Now we’re going to modify the code to make it more general-purpose: we want to be able to send messages as needed from within our website code.

4. Remove the main() method completely.

5. Paste in your API and Secret keys from the ZeepMobile site.

public static string API_KEY = "Your API Key here";
public static string SECRET_ACCESS_KEY = "Your Secret Key here";

6.  I added “message” and “uid” parameters to the SendTcpPost() method. Change the SendTcpPost() method to:

public static void SendTcpPost(String uid,String message)
 {

 SendSMS(
 "https://api.zeepmobile.com/messaging/2008-07-14/send_message", message, uid);
//URL for Send_Message, message to send, User ID in system.

 }

7. Add this function in: this is useful for converting received special characters into a more-readable format.


public static String convertSpecialChars(String inn){

 inn = inn.Replace("%3A", ":");
 inn = inn.Replace("%2F", "/");
 inn = inn.Replace("+", " ");
 inn = inn.Replace("%2B", "+");
 return inn;
 }

8. Last but not least, we want to be able to send a message from anywhere in our code:

Change:


class Program

to


 public class sendMessage

Once you’ve followed these steps, the sending end of your ZeepMobile service should be successfully set up! Thanks again to AboutDev for this code!

Receiving Messages

Now comes the second part of this process: setting up the receiving end of the connection so that we can respond to messages sent by users.

1. Add a new “Generic Handler” to your Website and call it “PostHandler.ashx”.

Adding the Generic Handler to the Website.

Adding PostHandler.ashx to the website: this will act as the receiving end for ZeepMobile messages.

2. Here’s the code that goes into PostHandler.ashx. Please remove any <pre> tags that you see here, they’re not meant to be in this code segment.

<%@ WebHandler Language="C#" %>
using System;
using System.Web;
using System.Data.SqlClient;
using System.Text.RegularExpressions;

/// <summary>
/// This page handles all incoming POST data from ZeepMobile and responds accordingly.
/// </summary>
public class PostHandler : IHttpHandler
{

 public bool IsReusable
 {
 get
 {
 return false;
 }
 }

/// <summary>
/// This method receives incoming messages, separates them into their parts, and responds accordingly.
/// </summary>
/// <param name="context"></param>
 public void ProcessRequest(HttpContext context)
 {

 //First, read in the message information.
 context.Response.ContentType = "text/plain";
 System.IO.StreamReader sr = new System.IO.StreamReader(context.Request.InputStream);
 string ret = "";
 ret = ret + sr.ReadToEnd().ToString();

 sr.Close();

 //Now, split the message into its parts:
 //min: the originating Mobile number.
 //event: The Event-type that this message falls under.
 //uid: The ID of the sender.
 //body: The actual message.

 int minInd = ret.IndexOf("&min=");
 //originating mobile number.
 String minText = ret.Substring(minInd + 5, ret.Length - (minInd + 5));

 int eventInd = ret.IndexOf("&event=");
 //event-type of message.
 String eventText = ret.Substring(eventInd + 7, ret.IndexOf("&", eventInd + 7) - (eventInd + 7));

 int uidInd = ret.IndexOf("&uid=");
 //UID of sender.
 String uidText = ret.Substring(uidInd + 5, ret.IndexOf("&", uidInd + 5) - (uidInd + 5));

 int bodyInd = ret.IndexOf("&body=");
 //body of message.
 String bodyText = ret.Substring(bodyInd + 6, ret.IndexOf("&", bodyInd + 6) - (bodyInd + 6));

 //Now we have the actual parts of the message.

 //Here, use the special function to make special characters appear as they should.
 minText = ConsoleApplication1.sendMessage.convertSpecialChars(minText);
 eventText = ConsoleApplication1.sendMessage.convertSpecialChars(eventText);
 uidText = ConsoleApplication1.sendMessage.convertSpecialChars(uidText);
 bodyText = ConsoleApplication1.sendMessage.convertSpecialChars(bodyText);

 //now that we have obtained the relevant fields, we need to understand the event-type that
 //we just received.

 //If the uid is blank, then we have a non-subscribed user. Tell this user how to subscribe.

 if ((uidText.Equals("")))
 {
 if (eventText.Equals("MO"))
 {
 //we have a non-subscribed user. Send instructions on how to subscribe.
 context.Response.Write("Hello! To subscribe, text '<yourkeywordhere> JOIN' to 88147");
 }
 }
 else
 {   //there was a UID passed to us. Either this is a subscribed user, or a new user that has just joined.

 if(eventText.Equals("MO")){ //MO Event: Respond to your subscribed user.
 context.Response.Write("Here's my response to your message!");
 }
 else if (eventText.Equals("SUBSCRIPTION_UPDATE"))
 { //This is a new user. Add this user to your database here, and send back a Welcome message!

 context.Response.Write("Welcome to my application! Here's how you can get started!");
 }
 else{
 //unknown event. Respond with a statement on how to subscribe.
 context.Response.Write("Hello!!! To subscribe, text '<yourkeywordhere> JOIN' to 88147");
 }
 }

 }

}

3. Make sure to go into your ZeepMobile panel and change your Incoming URL to wherever you’ll be hosting this PostHandler.ashx file:

Changing ZeepMobile Incoming URL.

Make sure to change your ZeepMobile Incoming URL!

PostHandler.ashx will only run code when events are being sent to it via HTTP POST. You will want to put in extra measures to make sure that no other site can POST data to this page.
To respond to an incoming event, simply place your response in context.Response.Write(“Message here”) – this message will be sent back as a text to the original sender of your message.

Also, keep in mind that ZeepMobile’s ad is always 40 characters long. Try to limit your response to a message to

[160 – (40 chars ad) – (x chars your_keyword_length) – (1 char semicolon)] characters

to avoid sending split text messages with multiple ads (ugh).

Download files

Here is the complete source code for this example. Since I can’t upload .zip files…I’ve attached them as PDFs. Download away!

PostHandler

So there you have it! A further tip: To test your application, use the Virtual Phone feature that ZeepMobile provides on its site. Very useful!