Geeks With Blogs
Jeff Ferguson Irritating other people since 1967

I am currently working with a client to bring .NET support to the FLEXnet Publisher Licensing Toolkit version 11.6. The toolkit, as shipped, is very tightly tied to the C language, and it is designed for use with client applications written in C. My client, however, wants to use the toolkit from VB.NET 2.0 applications. I set out, therefore, with a vision that stated "make FLEXnet look like .NET".

My first approach to the problem was to use P/Invoke to write a wrapper in VB.NET 2.0. This wrapper would sit just above the FLEXnet Publisher API and would wrap the function calls within a set of .NET classes that would hide the complexities of the API. With this architecture, .NET client applications would reference the .NET 2.0 wrapper, and the client calls to the wrapper would delegate the client’s method and property calls down to one or more calls to the FLEXnet Publisher API.

This initial effort was moderately, but not completely, successful. Some of the main problems with this approach had to do with the FLEXnet Publisher API’s design which mandated:

  • Explicit Pointer Casting: Calls to lc_get_attr() and lc_set_attr() require, in some cases, that the pointer used to reference the value (or its placeholder) be cast to a pointer of a specific type, which is sometimes further obscured by a typedef, such as LM_A_VAL_TYPE or LM_SHORT_PTR. Pointer casts to different target types in a single VB.NET P/Invoke scenario are problematic.
  • Macro-Based Variable Declaration and Initialization: One of the datatypes used in the call to lc_cryptstr() must be declared by a C macro called LM_CODE() and must be initialized by a C macro called LM_CODE_GEN_INIT(). These macros are found in C header files that ship with the FLEXnet Publisher API and their translation into a VB.NET environment for use with a .NET layer proved to be very difficult.

Given the discovery of the FLEXnet Publisher API’s tight coupling to C, I devised a new approach. The approach shared the vision of the first approach – namely, "to use a .NET wrapper to wrap the FLEXnet API calls into a .NET environment" – but was built using both native (unmanaged) C++ and C++/CLI (formerly known as Managed C++). The new approach involved two steps:

  1. Wrap the FLEXnet API calls into a native, unmanaged, C++ class. The methods exposed by this class would be implemented via calls into the FLEXnet API.
  2. Build a C++/CLI class that delegates its calls to an obect of the native, unmanaged C++ class.


The main advantage to this approach is that the native C++ class can make use of all of the C header files, data types, libraries and macros that ship with the FLEXnet API, thereby providing a level of programmability not possible with a 100% .NET language solution and P/Invoke. The disadvantage to this approach is one of maintenance, as it requires someone with knowledge of C++. However, the vision statement of “make FLEXnet look like .NET” can best be realized using C++, and I am a firm believer in using the right tool for the right job.

This approach proved to be much more successful, and, thanks to Visual C++’s ability to include unmanaged and managed code in the same assembly – a feature unavailable to other .NET languages – both the native C++ class and the C++/CLI class can be included in a single assembly. The assembly exposes the C++/CLI class as a standard .NET class which can be referenced from the client’s VB.NET applications. Problem solved.

Here is an example of that approach. In this example, we will wrap the FLEXnet API lc_cryptstr() with a .NET class that can be used by .NET applications. The unmanaged, native C++ class, which is the class that will call the FLEXnet API, is declared in a header file as follows:

class UnmanagedWrapper
{
public:
    UnmanagedWrapper(void);
    static int EncryptLicenseString(char * InputString, char ** OutputString);
    ~UnmanagedWrapper(void);
};

The implementation of the class performs the work that needs to take place to wrap the FLEXnet API call:

UnmanagedWrapper::UnmanagedWrapper(void)
{
}
int UnmanagedWrapper::EncryptLicenseString(char * InputString, char ** OutputString)
{
    LM_HANDLE * lm_job;
    char * errors;
    LM_CODE(code, ENCRYPTION_SEED1, ENCRYPTION_SEED2, VENDOR_KEY1, VENDOR_KEY2, VENDOR_KEY3, VENDOR_KEY4, VENDOR_KEY5);
    LM_CODE_GEN_INIT(&code);
    lc_init(NULL, VENDOR_NAME, &code, &lm_job);
    return lc_cryptstr(lm_job, InputString, OutputString, &code, LM_CRYPT_FORCE, NULL, &errors);
}
UnmanagedWrapper::~UnmanagedWrapper(void)
{
}

Now that the native C++ class is built, the C++/CLI class can be built. Its job is to simply hold a pointer to the native C++ class and delegate calls to the native C++ class:

public ref class OwnerLicensingEngine
{
private:
    UnmanagedWrapper * thisUnmanagedWrapper;
public:
    OwnerLicensingEngine()
    {
        thisUnmanagedWrapper = new UnmanagedWrapper();
    }
    String^ EncryptLicenseString(String^ Input)
    {
        IntPtr InputAsIntPtr = Marshal::StringToHGlobalAnsi(Input);
        char * InputAsString = (char *)(void *)InputAsIntPtr;
        char * OutputString;
        thisUnmanagedWrapper->EncryptLicenseString(InputAsString, &OutputString);
        String ^ StringToReturn = gcnew String(OutputString);
        Marshal::FreeHGlobal(InputAsIntPtr);
        return StringToReturn;
    }
    ~OwnerLicensingEngine()
    {
        delete thisUnmanagedWrapper;
    }
};

The private variable thisUnmanagedWrapper points to an object of the native C++ class. The object is created in the C++/CLI class constructor and destroyed in the C++/CLI class destructor. The C++/CLI’s method implementation translates data types as necessary (in the example above, .NET String objects need to be available as character pointers usable by the native C++ class), calls the appropriate native C++ method, performs any necessary cleanup, and returns. At this point, the C++/CLI class is ready to go and can be used by .NET clients, such as in this unit test:

<TestMethod()> Public Sub TestEncryptLicenseString()
    Dim TestFLEXnetObject As New OwnerLicensingEngine
    Dim Input As String
    Dim Signed As String
    Input = "FEATURE f1 JEFF 1.000 permanent uncounted HOSTID=0003ffb91c18 SIGN=""0"""
    Signed = Nothing
    Signed = TestFLEXnetObject.EncryptLicenseString(Input)
    Assert.IsNotNull(Signed)
    Assert.AreNotEqual(Input, Signed)
End Sub

It took me a while to figure out all of the compiler and linker options to get a successful build, given the mix of .NET, FLEXnet libraries, and the Visual C++ runtime. The command line for the debug builds are shown below.

The compiler command line for the debug builds is as follows:

/Od
/I "[path to FLEXnet install]\machind"
/D "WIN32"
/D "_DEBUG"
/D "_WINDLL"
/D "_UNICODE"
/D "UNICODE"
/FD
/EHa
/MDd
/Yu"stdafx.h"
/Fp"Debug\ClientLicensingEngine.pch"
/Fo"Debug\\"
/Fd"Debug\vc90.pdb"
/W3
/nologo
/c
/Zi
/clr
/TP
/errorReport:prompt
/FU "c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll"
/FU "c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Data.dll"
/FU "c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.XML.dll"

The linker command line is as follows:

/OUT:"C:\Projects\…\ClientLicensingEngine.dll"
/INCREMENTAL
/NOLOGO
/LIBPATH:"[path to FLEXnet install]\i86_n3"
/DLL
/MANIFEST
/MANIFESTFILE:"Debug\ClientLicensingEngine.dll.intermediate.manifest"
/MANIFESTUAC:"level='asInvoker' uiAccess='false'"
/NODEFAULTLIB
/DEBUG
/ASSEMBLYDEBUG
/PDB:"c:\Projects\…\ClientLicensingEngine.pdb"
/DYNAMICBASE
/FIXED:No
/NXCOMPAT
/MACHINE:X86
/ERRORREPORT:PROMPT
lm_new_md.obj
lmgr_md.lib
libcrvs_md.lib
libsb_md.lib
[path to FLEXnet install]\i86_n3\activation\lib\libnoact_md.lib
msvcrtd.lib
msvcmrtd.lib
mscoree.lib
oldnames.lib
kernel32.lib
user32.lib
netapi32.lib
advapi32.lib
gdi32.lib
comdlg32.lib
comctl32.lib
wsock32.lib

The compiler and linker options shown above will need to be adjusted for release builds so that the release-mode libraries are included.

If you find this solution to be elegant, don’t thank me. Thank Visual C++, which combines its support for native C with its support for .NET to provide a simple language solution to difficult problems whose problem domains are firmly rooted in a C-based API. I worked in C and C++ for many years before switching to C# and VB.NET in the summer of 2000. I didn’t look back at C++, until now, but, given the problem I faced with this project, I am grateful for its continued presence in the Visual Studio family of languages.

Use the right tool for the right job. C++ still has its place.

Posted on Tuesday, September 23, 2008 6:14 AM | Back to top


Comments on this post: The Case for C++ In A .NET World

# re: The Case for C++ In A .NET World
Requesting Gravatar...
Hi Ferguson,

I have a typical requirement of using Flexlm licensing model in vb.net2005 application. Can you please guide me how can i build a wrapper that talks between flexlm dlls and vb.net code. Any help on this is really appreciable!

Thanks
Left by anand on Nov 25, 2008 3:55 AM

# re: The Case for C++ In A .NET World
Requesting Gravatar...
@anand:

I think that the approach I mention in this blog would help your situation as well. Once the .NET wrapper is built, then the wrapper can be used with any .NET application from any .NET language, including VB.NET. The architecture proposed in this blog post is essentially this:

your application -> FLEXnet .NET wrapper -> FLEXnet unmanaged wrapper -> FLEXnet API

Though the unmanaged wrapper must be written in C++ for the reasons explained above, the wrapper is pure .NET when all is said and done and you would be able to use it from your VB.NET application. In fact, this blog post mentions that I wrote the unit tests in VB.NET, which proves that VB.NET clients can be written against this wrapper.

Thanks for reading!
Left by Jeff Ferguson on Nov 25, 2008 8:06 AM

# re: The Case for C++ In A .NET World
Requesting Gravatar...
interessting method, will try it.
Left by forum paris sportifs on Nov 09, 2010 5:59 AM

Your comment:
 (will show your gravatar)


Copyright © Jeff Ferguson | Powered by: GeeksWithBlogs.net