System::String and unmanaged Function aus c++ Dll übergeben [Gelöst]



  • Hallo Zusammen...

    resultierend aus diesem Thread möchte ich mit C++/CLI einen Wrapper Basteln, der mir eine Klasse aus einer C++ unmanaged Dll in C# zugänglich macht.

    Die erstellte CLI dll gibt mir meine Klasse schon wunderbar an C# weiter jedoch gibt es bei den Methodenaufrufen noch ein Problem.

    Die Launch() Methode funktioniert, da gibt es keine Probleme was jedoch nicht funktioniert ist die übergabe eines char*/wchar* pointers aus der ShowMessage Methodean an die unmanaged dll...hier bekomme ich eine Access Violation...

    Ich habe schon diverse Methoden ausprobiert wie ich den System::String für unmanaged umformen kann...

    z.B. wie hier beschrieben oder hier (um mal nur zwei Quellen zu nennen)

    Die Zielmethode treibt keine komischen Sachen mit dem String, ich denke also, irgend etwas mache ich noch beim umformen/übergeben falsch..

    Vorab, ich habe vorher noch nie etwas mit CLI gemacht von daher habt etwas Nachsehen mit mir 🙂

    Hier der code:

    public interface TestClassUnmanaged
    {
        virtual int GimmeTheValue();
        virtual void ShowMessage(const wchar_t* msg);
    };
    
    public ref class wrapper
    {
    private:
        TestClassUnmanaged* m_pVal;
    public:
        wrapper()
        {
            m_pVal = GetFunc();
        }
    
        [DllImportAttribute("c:\\asd.dll", EntryPoint = "?GetClass@@YAPAVTestClass@@XZ")]
        static TestClassUnmanaged* GetFunc ();
    
        Int32 Launch()
        {
            return m_pVal->GimmeTheValue();
        }
    
        void ShowMessage(String^ s)
        {
            std::wstring us2 = marshal_as<std::wstring>(s);
            m_pVal->ShowMessage(us2.c_str());
        }
    };
    

    Die TestKlasse in der unmanaged Dll sieht übrigens so aus

    class TestClass
    {
    public:
        int m_nValue;
        TestClass(){m_nValue = 42;}
        virtual int GimmeTheValue(){return m_nValue;}
        virtual void ShowMessage(const wchar_t* msg)
        {
            MessageBox(NULL,msg,L"test Box",MB_OK);
        }
    };
    
    __declspec(dllexport) TestClass* __cdecl GetClass()
    {
        return new TestClass();
    }
    

    Habt ihr einen Tipp für mich was ich da falsch mache?

    Gruß
    _Sascha_



  • In C++/CLI musst Du eben NICHT P/Invoke (=DllImport) benutzen, sondern die DLL direkt (=wie bei einem "normalen" C++ Projekt) einbinden (=Headers zur Kompilierzeit, Import Library zur Linkzeit und DLL zur Laufzeit).

    Edit:
    Ausserdem sollten new / delete in derselben DLL stattfinden.



  • LoadLibrary zur Laufzeit habe ich versucht...

    Ich hatte etwas herum expereimentiert und das CLI Projekt zuerst als exe erstellt...da hat das mit dem LoadLibrary geklappt aber als ich eine Dll daraus gemacht habe und die Klasse mit ref gekennzeichnet habe, sodass ich sie in C# benutzen kann, ist das LoadLibray fehl geschlagen...



  • Ich meine nicht LoadLibrary(..) sondern einfach *.lib angeben beim Linken. Das Programm lädt dann automatisch die DLL.



  • Ich verstehe nicht so recht... 😕

    das hier ist nur ein Testbeispiel...die eigentliche Dll die ich wrappen möchte liegt nur als Dll vor, nicht als lib...

    sprich...die asd.dll bekomme ich zugeliefert...ich habe nur eine Beschreibung des Interfaces und die Dll



  • Ok. Dann ist LoadLibrary(..) / GetProcAddress(..) angesagt.
    Mische nicht P/Invoke (GetFunc) und "normale" Aufrufe (m_pVal->GimmeTheValue()).

    Entscheide dich - entweder C Interface für die DLL oder C++ Interface (Klasse mit nur pure virt. Funktionen).

    Edit:
    Hier mal ein kleines Bsp. welches ev. dein Problem löst:

    C++ DLL Code
    Lib.hpp

    #pragma once
    
    class Lib
    {
    public:
        virtual void display(const wchar_t* message) = 0;
        virtual void destroy() = 0;
    
    protected:
        ~Lib() {};
    };
    

    LibImpl.hpp

    #pragma once
    
    #include "Lib.hpp"
    
    extern "C"
    {
        __declspec(dllexport) Lib* CreateLib();
    }
    
    class LibImpl
        : public Lib
    {
    public:
        // Lib interface
        virtual void display(const wchar_t* message);
        virtual void destroy();
    };
    

    LibImpl.cpp

    #include "LibImpl.hpp"
    
    #include <iostream>
    
    Lib* CreateLib()
    {
        return new LibImpl();
    }
    
    void LibImpl::display(const wchar_t* message)
    {
        std::wcout << message << std::endl;
    }
    
    void LibImpl::destroy()
    {
        delete this;
    }
    

    C++/CLI Applikations Code
    LibWrapper.h

    #pragma once
    
    #define WIN32_LEAN_AND_MEAN
    #include <Windows.h>
    
    using namespace System;
    
    class Lib;
    
    ref class LibWrapperException
        : public Exception
    {
    };
    
    ref class LibWrapper
    {
    public:
        LibWrapper();
        ~LibWrapper();
    
        void display(String^ message);
    
    private:
        HMODULE module_;
        Lib* lib_;
    };
    

    LibWrapper.cpp

    #include "stdafx.h"
    #include "LibWrapper.h"
    
    #include <Lib.hpp>
    
    #include <string>
    #include <msclr/marshal.h> 
    #include <msclr/marshal_cppstd.h>
    
    LibWrapper::LibWrapper()
    {
        module_ = LoadLibrary(L"Lib.dll");
    
        if (!module_)
        {
            throw gcnew LibWrapperException();
        }
    
        typedef Lib* (*CreateLib)();
        CreateLib func = reinterpret_cast<CreateLib>(GetProcAddress(module_, "CreateLib"));
        if (!func)
        {
            FreeLibrary(module_);
            throw gcnew LibWrapperException();
        }
    
        lib_ = func();
    }
    
    LibWrapper::~LibWrapper()
    {
        lib_->destroy();
        FreeLibrary(module_);
    }
    
    void LibWrapper::display(String^ message)
    {
        std::wstring s = msclr::interop::marshal_as<std::wstring>(message);
        lib_->display(s.c_str());
    }
    

    Main.cpp

    #include "stdafx.h"
    #include "LibWrapper.h"
    
    using namespace System;
    
    int main(array<System::String ^> ^args)
    {
        LibWrapper wrapper;
    
        wrapper.display(L"Test");
    
        return 0;
    }
    

    Bemerkung: Ich wusste grad nicht wie man in C++/CLI eine Klasse Non-Copyable macht - man sollte aber.

    Unterschiede zu deinem Code:
    - Benutzung von LoadLibrary(..) / GetProcAddress(..) anstelle von DllImport
    - Export der Factory Methode als extern "C" was das Namemangeling ausschaltet
    - Löschen der Lib Instanz in der Lib DLL durch Lib::destroy()
    - Bei meinem Code haben Interface und Implementation was miteinander zu tun, bei dir nicht (TestClassUnmanaged und TestClass) - zudem ist bei Dir das Interface managed (?), die Implementation aber nicht.



  • Hi,

    danke für dein Ausführliches Beispiel. Echt klasse!
    Ich werde es mir nachher gleich mal zu Gemüte führen und mein Testprojekt anpassen.

    Bemerkung: Ich wusste grad nicht wie man in C++/CLI eine Klasse Non-Copyable macht - man sollte aber.

    Ich würde es wie bei normalem c++ gemacht...Operator= und Copy-Constructor private machen...aber wenn es da ein Schlüsselwort dafür gibt würde mich das auch interessieren 🙂

    Gruß
    _Sascha_



  • Hi...
    funktioniert jetzt prima...auch die char* Übergabe 🙂

    ➡ Jetzt werde ich das mal auf die eigentliche Dll übertragen die ich wrappen möchte, falls ich da nochmal fragen habe, werde ich mich vertrauensvoll an dich wenden 🙂

    Also Danke nochmal. 👍

    Gruß
    _Sascha_


Anmelden zum Antworten