If you have, for example, this unmanaged function:
bool fooFunction(int firstParameter, int secondParameter);
If you want to make it visible to managed code you have to wrap (as first, simple, step) it into a managed class:
public ref class MyClass abstract sealed { public: static bool Foo(int firstParameter, int secondParameter) { return fooFunction(firstParameter, secondParameter); } };
This is a simple exam, if you have to interop with complex type you may need to wrap them all. For example if you have to interop with a function that accepts a string you have to manage this. Usually I use a class like this:
ref class UnmanagedString sealed { public: UnmanagedString(String^ content) : _content(content) { _unicodePtr = _ansiPtr = IntPtr::Zero; } ~UnmanagedString() { Free(); } operator LPWSTR() { if (_content == nullptr) return NULL; Free(); _unicodePtr = Marshal::StringToHGlobalUni(_content); return reinterpret_cast<LPWSTR>(_unicodePtr.ToPointer()); } operator LPCSTR() { if (_content == nullptr) return NULL; Free(); _ansiPtr = Marshal::StringToHGlobalAnsi(_content); return reinterpret_cast<LPCSTR>(_ansiPtr.ToPointer()); } virtual System::String^ ToString() override { return _content; } virtual int GetHashCode() override { return _content->GetHashCode(); } virtual bool Equals(Object^ obj) override { return _content->Equals(obj); } private: IntPtr _unicodePtr, _ansiPtr; String^ _content; void Free() { if (_unicodePtr != IntPtr::Zero) { Marshal::FreeHGlobal(_unicodePtr); _unicodePtr = IntPtr::Zero; } if (_ansiPtr != ntPtr::Zero) { Marshal::FreeHGlobal(_ansiPtr); _ansiPtr = IntPtr::Zero; } } };
Using this class you can call a function like void foo(LPCSTR pszText) with foo(UnamangedString(myManagedString)). More complex are the calls you have to do and more code you have to write to interop between them.
Note: simply exposing a 1:1 managed interface to unmanaged functions will make your C# code harder to read, I suggest you write a true OO interface to hide underlying implementation.