0

I am trying to follow this tutorial to build a C++/CLI wrapper for my .NET assembly and I am having issues.

I managed to get it to compile by retyping some of the text but now I have this problem when building:

Severity    Code    Description Project File    Line    Suppression State
Error   LNK1146 no argument specified with option '/EXPORT' MSAToolsLibraryWrapper  D:\My Programs\2017\MSAToolsLibraryWrapper\MSAToolsLibraryWrapper\LINK  1   

Update

If I remove /EXPORT from my Properties -> Linker -> Command Line -> Additional Options then it compiles. But, in the tutorial it said:

Step3. Configure the C++/CLI class library to export symbols. The symbols can be imported and called by native C++ applications.

Ah, I get it, the instructions that followed were the steps to take. I did not need to modify the build.

Update 2

OK, so take for example this method in my C# library:

Method

What is the right syntax for exposing that in my wrapper class?

Update 3:

I got a bit further:

HRESULT CSimpleMSAToolsLibraryWrapper::GetPersonnelNames(bool bSort)
{
    // Get the pinned CSSimpleObject object from its memory address. 
    GCHandle h = GCHandle::FromIntPtr(IntPtr(m_impl));
    MSAToolsLibraryClass^ obj = safe_cast<MSAToolsLibraryClass^>(h.Target);

    HRESULT hr = S_FALSE; // Fallback
    try
    {
        // Redirect the call to the corresponding method of the wrapped  
        // MSAToolsLibraryClass object.
        array<System::String^>^ listNames = obj->GetPersonnelNames(bSort);
        hr = S_OK;
    }
    catch (Exception ^ e)
    {
        hr = Marshal::GetHRForException(e);
        // Do we do anything else here?
    }

    if (SUCCEEDED(hr))
    {
        // What exactly do we do with "listNames"?
        // Ideally I want to return the array rather than HRESULT.

        // Convert System::String to PCWSTR. 
        //marshal_context ^ context  = gcnew marshal_context();
        //PCWSTR pszStr  = context->marshal_as<const wchar_t*>(str);
        //hr  = StringCchCopy(pszBuffer, dwSize, pszStr  == NULL  ? L"" : pszStr);
        //delete context; // This will also free the memory pointed by pszStr 
    }

    return hr;
}

But as you can see, I have a couple of questions there still.

Update 4

I ended up doing this:

std::vector<std::string> CSimpleMSAToolsLibraryWrapper::GetPersonnelNames(bool bSort)
{
    // Get the pinned CSSimpleObject object from its memory address. 
    GCHandle h = GCHandle::FromIntPtr(IntPtr(m_impl));
    MSAToolsLibraryClass^ obj = safe_cast<MSAToolsLibraryClass^>(h.Target);
    std::vector<std::string> vectorListNames;

    HRESULT hr;
    try
    {
        // Redirect the call to the corresponding method of the wrapped  
        // MSAToolsLibraryClass object.
        array<String^>^ listNames = obj->GetPersonnelNames(bSort);
        vectorListNames = ConvertToUnManaged(listNames);
    }
    catch (Exception ^ e)
    {
        hr = Marshal::GetHRForException(e);
        // Do we do anything else here?
    }

    return vectorListNames;
}

std::vector<std::string> CSimpleMSAToolsLibraryWrapper::ConvertToUnManaged(array<String^>^ ar)
{
    std::vector<std::string> vector(ar->Length);
    for (int i = 0; i < ar->Length; ++i) {
        auto s = ar[i];
        vector[i] = msclr::interop::marshal_as<std::string>(s);
    }
    return vector;
}

But now I have another problem:

Severity    Code    Description Project File    Line    Suppression State
Error   C3395   'CSimpleMSAToolsLibraryWrapper::ConvertToUnManaged': __declspec(dllexport) cannot be applied to a function with the __clrcall calling convention
MSAToolsLibraryWrapper  d:\my programs\2017\msatoolslibrarywrapper\msatoolslibrarywrapper\MSAToolsLibraryWrapper.h  26  

I got around that by moving the method out of the class.

Another related issue I can't get my head around is the tutorials they said to add a reference to your DLL, which I did. But I have one DLL for x86 and one DLL for x64 and I don't know how to reference each DLL based on the build being made. Separate question?

Update 5

Is it supposed to be:

#ifdef MSATOOLSLIBRARYWRAPPER_EXPORTS
#define SYMBOL_DECLSPEC __declspec(dllexport)
#else
#define SYMBOL_DECLSPEC __declspec(dllimport)
#endif

using namespace System;


// This native C++ class wraps the C# class CSSimpleObject defined in the  
// .NET class library CSClassLibrary.  
class SYMBOL_DECLSPEC CSimpleMSAToolsLibraryWrapper

Or just:

public ref class CSimpleMSAToolsLibraryWrapper
Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
  • 1
    Is the "consumer" of your wrapper a C++ or a C program? because if it is a C++ program, then you would return a `std::vector>`, otherwise you would "return" (I'll use quotes here, because you'll pass it as a parameter) for example a `LPTSTR[]` plus a `int` with the length (or you'll have a zero-terminated array, an array where the last element is NULL) – xanatos Mar 13 '17 at 10:38
  • Ah... And I don't know where you found `PCWSTR`... it si many years I've seen it :-) I'll quote [Hans Passant](http://stackoverflow.com/questions/21659751/pcwstr-vs-lpwstr): *PCWSTR is a time anachronism, dinosaur-and-humans movie style*. You probably want `LPCWSTR` – xanatos Mar 13 '17 at 10:44
  • @xanatos That is just the code from the tutorial I linked too. I wasn't using that myself. My consumer is MFC / C++ abnd I am using vectors so I will try that. Thanks. – Andrew Truckle Mar 13 '17 at 10:46
  • @xanatos OK, sorry, but this is my first time. How do I iterate the `array` object to build the `vector` object? – Andrew Truckle Mar 13 '17 at 10:48
  • 1
    Ok then you can return a `std::vector` or a `std::vector`... About how to convert strings, you can look at http://stackoverflow.com/questions/14047414/convert-a-string-to-wstring-c or http://stackoverflow.com/a/1405251/613130 – xanatos Mar 13 '17 at 10:48
  • `for (int i = 0; i < listNames->Length ; i++) { // and then listNames[i] is the string you want` – xanatos Mar 13 '17 at 10:51
  • Full example http://stackoverflow.com/a/25243648/613130 . Note the comment: pinning is NOT necessary – xanatos Mar 13 '17 at 10:51
  • 1
    You have a managed class `CSimpleMSAToolsLibraryWrapper` (a `ref class`): you need to make it unmanaged to export methods to the unmanaged world... – xanatos Mar 13 '17 at 12:07
  • 1
    The first one... The second one is a managed class (see the `ref` word?) – xanatos Mar 13 '17 at 12:52
  • @xanatos ok. But that brings me to related comment in my other question. I can't work out how to call this wrapper from the mfc project. And, my wrapper references to x86 c# dll and also have a x64 bit build of it. – Andrew Truckle Mar 13 '17 at 12:55
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/137939/discussion-between-xanatos-and-andrew-truckle). – xanatos Mar 13 '17 at 12:55

0 Answers0