sabato 29 novembre 2014

Utilizzo di una dll .NET da codice 'nativo' (parte 2)

Nel post precedente, abbiamo visto come realizzare una dll con tecnologia .NET, in grado di consentire ad una qualunque applicazione (sviluppata con tecnologia .NET) di usufruire di un generico servizio web. Tuttavia il problema di partenza era quello di creare un ponte tra il framework .NET ed un’applicazione Win 32.

Il wrapper C++/CLI

Passiamo quindi a descrivere i passi per lo sviluppo del nostro wrapper, ovvero della dll che renderà accessibili i servizi esportati dalla libreria dinamica implementata negli step precedenti.
● Il primo passo è quello di creare un progetto C++ per la creazione di una dll, ed abilitare il Supporto Common Language Runtime (/clr).

● Aggiungiamo un riferimento alla dll prodotta dal progetto GoogleMapsAPI

● Fatto ciò aggiungeremo due classi CGoogleMapsClientIntefacePrivate e CGoogleMapsClientInteface.
#pragma once

# include <string>

using namespace std;

class CGoogleMapsClientIntefacePrivate;

class CGoogleMapsClientInteface
{
public:
    CGoogleMapsClientInteface();
    ~CGoogleMapsClientInteface();

#pragma region Attributes
private:

    CGoogleMapsClientIntefacePrivate* _private;

#pragma endregion

#pragma region Properties
public:
    __declspec(property(get = GetLatitude)) double Latitude;
    double GetLatitude(void);

    __declspec(property(get = GetLongitude)) double Longitude;
    double GetLongitude(void);

    __declspec(property(put = SetAddress, get = GetAddress)) const char* Address;
    void SetAddress(const char*);
    const char* GetAddress(void) const;
#pragma endregion
};
● La cui implementazione è la seguente:
#include "stdafx.h"
#include "GoogleMapsClientInteface.h"

# include <msclr\auto_gcroot.h>

using namespace System::Runtime::InteropServices;
using namespace GoogleMapsAPI;

class CGoogleMapsClientIntefacePrivate
{
public:
    msclr::auto_gcroot<GoogleMaps^> googleMapsAPI;
};

CGoogleMapsClientInteface::CGoogleMapsClientInteface()
{
    this->_private = new CGoogleMapsClientIntefacePrivate();
    this->_private->googleMapsAPI = gcnew GoogleMaps();
}


CGoogleMapsClientInteface::~CGoogleMapsClientInteface()
{
    if (this->_private)
        delete this->_private;
}

double CGoogleMapsClientInteface::GetLatitude()
{
    return this->_private->googleMapsAPI->Latitude;
}

double CGoogleMapsClientInteface::GetLongitude()
{
    return this->_private->googleMapsAPI->Longitude;
}

void CGoogleMapsClientInteface::SetAddress(const char* value)
{
    this->_private->googleMapsAPI->Address = gcnew System::String(value);

    this->_private->googleMapsAPI->GetCoordinates();
}
const char* CGoogleMapsClientInteface::GetAddress(void) const
{
    return (const char*)Marshal::StringToHGlobalAnsi(this->_private->googleMapsAPI->Address).ToPointer();

}
Il punto focale dell’implementazione è dato dalla classe auto_gcroot, che, citando l’help online di Microsoft, “può essere utilizzato per incapsulare un handle virtuale in un tipo nativo”. In altre parole questa classe ci fornirà il punto d’accesso al mondo .NET, e, ad essa delegheremo gli oneri legati alle differenti modalità di gestione della memoria tra il mondo nativo ed il mondo gestito. Tuttavia, così com’è, la classe auto_gcroot, parte dell’armamentario C++/CLI, non è utilizzabile in una dll C++ nativa. In primo luogo perchè non verrebbe riconosciuta in fase di compilazione, ed anche perchè se venisse compilata altererebbe irrimediabilmente il livello di astrazione che stiamo cercando di dare all’intero progetto seguendo una delle tecniche care allo sviluppo C++ nativo descritte nel PIMPL Idiom. A tale scopo abbiamo nascosto il nostro handle nella classe CGoogleMapsClientIntefacePrivate ottenendo un’interfaccia compatibile con il mondo nativo.

Funzioni esportate

Ottenuto il punto d’accesso, o il wrapper che dir si voglia, non ci resta che implementare la nostra dll nativa. Dando per scontata la parte legata allo sviluppo della dll in sé per sé, focalizzerei l’attenzione sul fatto che non abbiamo esportato direttamente la classe CGoogleMapsClientInteface, cosa del tutto lecita e possibile, ma abbiamo creato un handle verso essa mediante il metodo CGoogleMapsClientInteface_Create(). Il motivo di tale scelta è dato dal fatto che il nostro client non verrà sviluppato in C++ nativo (in tal caso ci saremmo fermati al passo precedente), ma in Delphi 7, linguaggio che ha una gestione della memoria totalmente differente da quella del C++, e che difficilmente riuscirebbe ad utilizzare una classe esportata direttamente dal nostro wrapper.
#ifdef CLIBRIDGE_EXPORTS
#define CLIBRIDGE_API __declspec(dllexport)
#else
#define CLIBRIDGE_API __declspec(dllimport)
#endif

typedef void* THandle;

# include "GoogleMapsClientInteface.h"

#ifdef __cplusplus
extern "C"
{
#endif

    inline CLIBRIDGE_API THandle CGoogleMapsClientInteface_Create()
    {
        return (THandle) new CGoogleMapsClientInteface();
    }

    inline CLIBRIDGE_API void CGoogleMapsClientInteface_SetAddress(THandle handle, const char* value)
    {
        if (handle)
        {
            ((CGoogleMapsClientInteface*)handle)->Address = value;

            ((CGoogleMapsClientInteface*)handle)->GetAddress();
        }
    }
    inline CLIBRIDGE_API const char* CGoogleMapsClientInteface_GetAddress(THandle handle)
    {
        return (handle)
            ? ((CGoogleMapsClientInteface*)handle)->Address
            : "";
    }

    inline CLIBRIDGE_API double CGoogleMapsClientInteface_GetLatitude(THandle handle)
    {
        return (handle)
            ? ((CGoogleMapsClientInteface*)handle)->Latitude
            : numeric_limits<double>::signaling_NaN();
    }

    inline CLIBRIDGE_API double CGoogleMapsClientInteface_GetLongitude(THandle handle)
    {
        return (handle)
            ? ((CGoogleMapsClientInteface*)handle)->Longitude
            : numeric_limits<double>::signaling_NaN();
    }

    CLIBRIDGE_API int fnCLIBridge(void);
#ifdef __cplusplus
}
#endif

L’applicazione nativa

A questo punto non ci resta che mettere insieme i blocchi sviluppati finora per creare un programma di test scritto in idioma nativo. Si tratta di una semplice applicazione Console, scritta in C++ e non in Delphi 7 (a tutto c’è un limite ;-)).
// Win32CrashTestDummy.cpp : definisce il punto di ingresso dell'applicazione console.
//

#include "stdafx.h"

# include <iostream>
# include <conio.h>

# include "CLIBridge.h"

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    cout << " ------------------------ " << endl;
    cout << " ---------START---------- " << endl;
    cout << " ------------------------ " << endl;

    cout << endl;

    // cout << "Exported function \'fnCLIBridge()\' returned: " << fnCLIBridge() << endl;

    THandle handle = CGoogleMapsClientInteface_Create();

    const char* address = "Google Building 44, Mountain View, California, Stati Uniti";

    CGoogleMapsClientInteface_SetAddress(handle, address);

    const char* value = CGoogleMapsClientInteface_GetAddress(handle);

    cout << "Address:\t" << value << endl;

    cout << endl;

    cout << "Latitude:\t" << CGoogleMapsClientInteface_GetLatitude(handle) << endl;

    cout << endl;

    cout << "Longitude:\t" << CGoogleMapsClientInteface_GetLongitude(handle) << endl;

    cout << endl;

    cout << " ------------------------ " << endl;
    cout << " ----------END----------- " << endl;
    cout << " ------------------------ " << endl;

    std::cin.get();
    cout << " ------------------------ " << endl;

    return 0;
}
Il risultato finale è illustrato nell’immagine seguente, nella quale si mette a confronto l’output dell’applicazione console con quella WinForm sviluppata nel post precedente.

Conclusione

Lavorando nel mondo della tecnologia, in qualsiasi ambito, l’eterogeneità degli strumenti da utilizzare è la regola e non l’eccezione, e quindi può capitare che una tecnologia datata, ma ancora efficiente e funzionale, debba poter accedere a nuovi strumenti. Quello illustrato è uno dei casi che ci si può trovare a dover affrontare, ed uno dei possibili metodi per risolverlo.
Il link ai sorgenti dell’intero progetto raggiungibili dal link seguente
Git
Enjoy
Creative Commons Quality is not for sale

sabato 1 novembre 2014

Utilizzo di una dll .NET da codice 'nativo' (parte 1)

Circa un anno fa, un mio collega si è trovato nella condizione di dover utilizzare una dll sviluppata con tecnologia .NET, all’interno del software di controllo di un sistema di assemblaggio e test di componenti per il mercato automotive, sviluppato con un linguaggio nativo. Ovviamente la dll in questione non era COM-aware, ovvero non poteva essere utilizzata come un ActiveX (troppo facile). Poiché in azienda sono tra i pochi ad avere una certa esperienza sia con la tecnologia .NET che con linguaggi nativi, sono stato coinvolto nella ricerca di una soluzione al problema, e, dopo essermi documentato un po’, tra le varie soluzioni possibili, ho deciso di implementare quella illustrata nell’immagine che segue:
In pratica, si tratta di sfruttare la possibilità di incorporare codice gestito in una dll C++, e creare quindi un wrapper che sia in grado di rendere fruibili metodi e proprietà esposti dalla dll .NET, ad un software legacy
Al fine di illustrare tale concetto, il resto del memorandum, che dividerò in due parti, si svilupperà nella scrittura di quanto segue: 
1. una dll C# che che esporti metodi e/o proprietà; 
2. un wrapper C++/CLI che renda visibili le interfacce della dll del punto precedente ad un software nativo; 
3. programma di test, scritto in codice nativo che importi la dll, prodotta allo step 2 e ne usi metodi e/o proprietà. 
Ho già condiviso l’intero progetto su GitHub ed è possibile raggiungerlo dal link che segue CLIWrapperDemo.



La libreria .NET

Il primo passo sarà quello di implementare una libreria di classi .NET attorno alla quale costruiremo il nostro wrapper. Chiameremo la libreria GoogleMapsAPI che, com’è facile capire dal nome, sfrutterà le API di Google Maps, al fine di restituire le coordinate (longitudine e latitudine) dato un indirizzo. Per ulteriori informazioni sul servizio di geolocalizzazione ho fatto riferimento alla documentazione ufficiale Google Maps API Web Services. Il risultato è una classe con tre proprietà ed un metodo.
/// <summary>
/// Returns the location latitude
/// </summary>
public double Latitude { get; private set; }

/// <summary>
/// Returns the location longitude
/// </summary>
public double Longitude { get; private set; }


/// <summary>
/// Set the desired location address
/// </summary>
public string Address { get; set; }

/// <summary>
/// Gets the location coordinates from the Google Maps API Web Service
/// https://developers.google.com/maps/documentation/geocoding/
/// </summary>
/// <returns>true on success, false otherwise</returns>
public bool GetCoordinates()
{
     ...
}

Per testare immediatamente la dll prodotta ho implementato una semplice applicazione WinForm. Il risultato è illustrato nell’immagine che segue:
dotNETCrashTestDummy
Ok, abbiamo una dll .NET e non ci resta che creare il wrapper e provare ad utilizzarlo nel nostro vecchio software legacy. We’ll find out in the next episode!
Enjoy.
Creative Commons Quality is not for sale

mercoledì 15 ottobre 2014

Markdown? What's it?

Logo markdown

Con l’intento di scrivere la documentazione di un nuovo progetto che sto sviluppando, e di sfruttare, a tal fine, l’opzione di associare ad un progetto ‘hostato’ su Bitbucket un Wiki che, per definizione si presta a scrivere/modificare un documento condiviso in un gruppo di lavoro. Una breve lettura delle caratteristiche dello strumento e…

Wiki features:
This wiki uses the Markdown syntax.

MARKDOWN?

Ok,…
1. Cos’è?
2. Qual’è la sintassi?
3. Quali sono gli editor che posso utilizzare?
Ovviamente Google knows e quindi ecco un pò di fonti utili per la sintassi:

Per gli strumenti, non c’è che l’imbarazzo della scelta, e, primo tra tutti StackEdit, ovvero l’editor on-line con il quale sto scrivendo questo appunto, che tra le varie features ha:
- l’integrazione in Chrome
- in Google Drive
- Varie possibilità di pubblicazione dei documenti prodotti, su diversi supporti, tra i quali, Blogger appunto.

Enjoy

Aggiornamento:

Continuando nella ricerca di strumenti per l’editing con sintassi Markdown, tra gli strumenti desktop, ci sono una serie di plugin per Sublime Text. Un’utile guida su come installare ed usare tali plugins è riportata in:

giovedì 1 maggio 2014

Verified Certificate record successfully matched!!!

E anche questa è fatta! Il fatto di esser stato un pò lontano dal blog non significa che, dall'ultimo post, la mia curiosità per tutto ciò che riguarda la tecnologia, sia andata in vacanza. E infatti, tra le altre cose, delle quali prima o poi troverò il modo di scrivere, ho avuto il piacere di seguire un corso online sulla programmazione di applicazioni mobile su dispositivi android, tenuto dall'ottimo prof. Adam Porter. Devo ammettere che tener il passo per le otto settimane, tra lezioni, quiz e esercizi di programmazione, non è stato semplice, ma alla fine sono arrivato alla meta.  L'opportunità di avere una panoramica su questo mondo con una guida esperta e competente era irrinunciabile e valeva lo sforzo fatto per raggiungere lo scopo. E a coronamento dell'esperienza c'è stato anche un certificato di completamento del corso.



Enjoy