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