Overview

The sections below descibe how to configure a Visual Studio .NET project for use with STEP Tools® libraries.

When using STEP Tools® software in .NET applications, you will typically wrap a block of C++ code in a CLI wrapper to perform a high-level operation when called from Visual Basic or C# code. Passing numeric parameters between C++ and .NET code is straightforward, but string data takes some extra handling.

Project Settings for .NET

To build a .NET DLL that uses STEP Tools® software, first set the project to use Common Language Runtime Support (/clr) as shown below. By default, the project may be set to /clr:pure, which will cause errors when trying to link with native C++ libraries.

Finally configure your project as recommended for your version of Visual Studio

Passing Strings between .NET and C++

The following code shows how to pass a string from .NET into a ROSE C++ function. This example converts the .NET filename string into a null-terminated UTF8 string, pins the result in memory so that it can be safely passed to unmanaged code, and then passes the pinned pointer to read a STEP file.

void SomeDotNETClass::LoadSTEPFile (String ^filename_wide) 
{
    // Marshal the string to UTF8 for roselib usage. 
    array<unsigned char>^filename_as_utf8 = MakeNullTermUTF8 (filename_wide);
    pin_ptr<unsigned char> fn = &filename_as_utf8[0];

    RoseDesign * des = ROSE.findDesign(reinterpret_cast<char*>(fn));
    if (!des) {
	throw gcnew Exception(LoadSTEPFile: Could not read STEP file.);
    }
}

The following code shows how to pass a string from a ROSE C++ function back into .NET code. In this case, the function finds a RoseObject using some unspecified means and then returns the EXPRESS type name of the objectas a string value. You can replace that with whatever your code needs to do.

String^ SomeDotNETClass::GetEntityStringAtt (Int64 some_param)
{
    RoseObject * obj = get_object_somehow(some_param);
    if (!obj) return String::Empty;

    return MakeStringFromUTF8(obj-> domain()-> name());
}

The MakeNullTermUTF8() and MakeStringFromUTF8() functions used by the previous code fragments are shown below. You can cut and paste these into your code to help convert between the managed UTF16 strings used by .NET and the UTF8 null-terminated strings used by C++.

#include <tchar.h>

using namespace System::Text;
using namespace System::Runtime::InteropServices;


// Convert a managed UTF16 .NET string into an unmanaged null-terminated
// UTF8 byte array.  When used with unmanaged code, you can then just
// pin the pointer to the byte array and use it as an unmanaged char*
//
array<unsigned char> ^ MakeNullTermUTF8 (String ^dotnet_str) 
{
    if (!dotnet_str) return nullptr;
    
    // Since we need to add a trailing null character, allocate the
    // character array ourself and then populate it with a variation
    // of the GetBytes call.

    // GetMaxByteCount is faster
    size_t bytesz = Encoding::UTF8->GetByteCount(dotnet_str);
    array<unsigned char> ^bytes = gcnew array<unsigned char>((int)bytesz + 1);
	
    // Copy to array, returns the number of bytes copies, use to null
    // terminate the array.
    size_t strlen = Encoding::UTF8->GetBytes(
	dotnet_str, 0, dotnet_str->Length, bytes, 0
	);
    bytes[(int)strlen] = 0;
    return bytes;
}


// Copy and convert an unmanaged null-terminated UTF8 string into a
// new managed UTF16 .NET String.
//
String ^ MakeStringFromUTF8 (const char * utf8_str)
{
    // Never return a null string from this interface
    // if (!utf8_str) return nullptr;
    if (!utf8_str) return String::Empty;

    size_t bytelen = strlen(utf8_str);
    if (!bytelen) return String::Empty;

    array<unsigned char> ^bytes = gcnew array<unsigned char>((int)bytelen);
    { 	
	pin_ptr<unsigned char> pinnedBytes = &bytes[0];
	memcpy(pinnedBytes, utf8_str, bytelen);
    }
    return Encoding::UTF8->GetString(bytes);
}