Wednesday, August 5, 2015

How to use native C++ code in Azure Cloud Service/C# application

Sometimes it becomes necessary to use native C ++ code in C# application or in the Azure Cloud Service. We will not dwell on the reasons why this should be done and how you can avoid it - sometimes it does not depend on the developer.

Unfortunately, you can’t link native code in C # project directly. But you can wrap your code in CLR project and then link wrapped project to your original application.

This way we have three parts:

1)      Project A written in native C++

2)      Wrapper for native project

3)      Azure Cloud Service (or C# application) where you would like to use code from project A.

To illustrate whole process, we will create a sample project that will have some native C++ code, and then I will show, how to use this code in Azure Cloud Service/C# Application.

You will find all source here: https://github.com/SkyRunn3r/NativeCodeWrapping

Let’s call our project NativeProjectA. The project have one class “sample_class”. This class has one method “write_to_file”– to write given string to the given file:

sample_class.h

#pragma once

#include <string>

 

class sample_class

{

public:

  void write_to_file(const std::string &filename, const std::string &message);

};

 

sample_class.cpp

#include <fstream>

#include "sample_class.h"

 

void sample_class::write_to_file(const std::string & filename, const std::string & message)

{

  std::ofstream o(filename);

  o << message << std::endl;

}

 

Native project modification

Let’s assume that you want to use “write_to_file” function in your Azure Cloud Service/C# application. Create header and source file to export this function:

sample_class_api.h

#pragma once

#define DLLEXP   __declspec( dllexport )

#include <string>

 

DLLEXP void write_to_file(const std::string &filename, const std::string &message);

 

sample_class_api.cpp

#include "sample_class.h"

#include "sample_class_api.h"

 

void write_to_file(const std::string &filename, const std::string &message)

{

  sample_class sample;

  sample.write_to_file(filename, message);

}

Build project as a static lib, Release configuration. Perhaps, for this kind of build you would like to create a new configuration. I created new configuration and called it Release-Lib. Go to project properties (I’m using Microsoft Visual Studio), then General -> Configuration Type, choose “Static Library (.lib)”. Then go to С/C++ -> Code Generation and set Runtime library as “Multi-threaded DLL (/MD)” (of course, all dependencies must be built the same way). Don’t forget to set platform as x64 if you plan to run your code in Cloud.

Wrapper-project

To make code compatible with C# code, it should be put in CLR (Common Language Runtime) project. In this project we will create class sample_class_wrapper, in which we will call imported from native code function. MarshalString function will help us to convert string arguments to std::string for native code.

Add to Azure Cloud App/C# Application solution (Add->Existing project) NativeProjectA. If you don’t want to mess your solution with extra projects, just make sure that wrapper project links necessary .lib files and include necessary headers file from native project. But for the first time I would recommend to add all 3 projects (native, wrapper, C# project) in the same solution and separate them after you will have a successful build.

Add new CLR-project (Add -> New Project, Visual C++ -> Windows -> CLR, CLR Empty Project) NativeProjectAWrapper. Then right-click on the newly created project and choose Add -> Reference. Open Projects -> Solution, then choose NativeProjectA (or specify Library Path and a list of input libraries if you decided not to include the native project in the solution).

Create wrapper-class:

sample_class_wrapper.h

#include <string>

 

namespace sample_class {

  public ref class sample_class_wrapper

  {

  public:

    sample_class_wrapper() {

    };

 

    void write_to_file_wrapper(System::String^ filename, System::String^ message);

    std::string marshal_string(System::String^ input);

  };

}

 

sample_class_wrapper.cpp

#include "sample_class_wrapper.h"

#include <sample_class_api.h>

 

 

namespace sample_class {

  void sample_class_wrapper::write_to_file_wrapper(System::String ^ filename, System::String ^ message)

  {

    write_to_file(marshal_string(filename), marshal_string(message));

  }

 

  std::string sample_class_wrapper::marshal_string(System::String^ s)

  {

    using namespace System::Runtime::InteropServices;

    const char* chars =

      (const char*)(Marshal::StringToHGlobalAnsi(s)).ToPointer();

    std::string os = chars;

    Marshal::FreeHGlobal(System::IntPtr((void*)chars));

    return os;

  }

}

 

Build project as .dll with Release configuration. Go to project properties, then for General -> Configuration Type set “Dynamic Library (.dll)”. Then go to С/C++ -> Code Generation and make sure that Runtime library set to “Multi-threaded DLL (/MD)”. Platform must be the same as in the native project.

Linking wrapper to Azure Cloud Service/C# application

We want to use native code in WorkerRole1 in our project AzureCloudSampleService.

Right-click on Reference for WorkerRole1 and then choose Add Reference. Choose Projects -> Solution, then click in checkbox for NativeProjectAWrapper.

Open Configuration Manager. Make sure all “Build” checkboxes are checked for NativeProjectA and NativeProjectAWrapper, and you have chosen the proper configuration (Release-Lib for NativeProjectA and Release for NativeProjectAWrapper. Set x64 as platform for WorkerRole1 configuration, but cloud service and solution platform must remain “Any CPU”.

Now let’s use some native code. In WorkerRole1 add “using sample_class”:

...

using Microsoft.WindowsAzure.ServiceRuntime;

using Microsoft.WindowsAzure.Storage;

using sample_class; //add code here

 

namespace WorkerRole1

{

...

}

Create instance of sample_class_wrapper and call for the desired function. I added native code to write to file to RunAsync function. I get temp file name and write “Hello Cloud” to this file.

Here is how my RunAsync function looks like:

private async Task RunAsync(CancellationToken cancellationToken)

{

    // TODO: Replace the following with your own logic.

    while (!cancellationToken.IsCancellationRequested)

    {

        sample_class_wrapper sample_class_object = new sample_class_wrapper();

        string testString = "Hello Cloud!";

        string filepath = System.IO.Path.GetTempFileName();

        sample_class_object.write_to_file_wrapper(filepath, testString);

        Trace.TraceInformation("Wrote message to file " + filepath);

        await Task.Delay(5000);

    }

}

After execution program starts to write to log:

In File Explorer we can see:

One of this tmp files:

It works!

You can find all sources for this project here: https://github.com/SkyRunn3r/NativeCodeWrapping

 

1 comment: