반응형


Managed C++/CLI 프로젝트 설정


공용 언어 런타임 지원




code : https://github.com/EomTaeWook/Cpp-CLI-Marshalling


Native -> Managed C++/CLI 메시지를 전파를 할시엔 OutGoingMessage를 사용


Managed C++/CLI -> C# event 로 Notify가 나감


C# -> Managed C++/CLI -> Native InCommingMessage으로 사용


Dll Loading을 통한 Runtime Class 생성 예제


Managed C++/CLI 


CppInterface.h


#pragma once

namespace Cpp

{

public interface class CppInterface

{

public:

delegate void OnNotifyHandler(System::Object^ sender, System::String^ message);

public:

void Init();

void Run();

void Stop();

void InCommingMessage(System::String^ inCommingMessage);

event OnNotifyHandler^ OnNotify;

};

}

CppInterface.cpp

#include "CppInterface.h"


Dll을 뽑기위해 Cpp 파일은 헤더 추가만


Managed C++/CLI


CppWrapper.h


#pragma once
#include "../Native/Native.h"
//C++/Cli .net 환경에서 구동되는 C++
public ref class CppWrapper : Cpp::CppInterface
{
public:
//Cpp Native 단으로 함수 포인터를 넘겨주기 위한 대리자
//OutGoingMessage(std::string message);과 매칭된다.
delegate void OnNotifyCallback(std::string message);
public:
CppWrapper();
virtual ~CppWrapper();
private:
void OutGoingMessage(std::string outGoingMessage);
private:
Native* _native;
public:
// CppInterface을(를) 통해 상속됨
virtual event Cpp::CppInterface::OnNotifyHandler ^ OnNotify;
virtual void Init();
virtual void Run();
virtual void Stop();
virtual void InCommingMessage(System::String ^inCommingMessage);

};

inline CppWrapper::CppWrapper()
{
_native = new Native();
}
inline CppWrapper::~CppWrapper()
{
delete _native;
}

CppWrapper.cpp
#include "CppWrapper.h"
#include <msclr/marshal_cppstd.h>
void CppWrapper::Run()
{
}
void CppWrapper::Stop()
{
_native->Stop();
}
void CppWrapper::InCommingMessage(System::String ^inGoingMessage)
{
std::string message = msclr::interop::marshal_as<std::string>(inGoingMessage);
_native->InCommingMessage(message);
}
//Native 단에서 넘겨받은 Message를 구독하고 있는 모든 애들한테 Notify
void CppWrapper::OutGoingMessage(std::string outGoingMessage)
{
System::String^ message = gcnew System::String(outGoingMessage.c_str());
OnNotify(this, message);
}

void CppWrapper::Init()
{
auto cb = gcnew OnNotifyCallback(this, &CppWrapper::OutGoingMessage);
System::IntPtr ptr = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(cb);
_native->Init(static_cast<Native::OutGoingCallback>(ptr.ToPointer()));
}


C ++ Native Code

Native.h


#pragma once

#include <string>

//C++ Native 이며 Lib로 빌드가 되어야함.

class Native

{

public:

typedef void(__stdcall *OutGoingCallback)(std::string);

public:

Native();

~Native();

private:

//Wrapping 되는 C++/cli 함수 포인터 저장

OutGoingCallback _onNotify;

public:

void Init(OutGoingCallback onNotify);

void InCommingMessage(std::string inCommingMessage);

void Stop();

private:

void OutGoingMessage(std::string outGoingMessage);

};


Native.cpp


#include "Native.h"

#include <stdio.h>

Native::Native()

{

}

Native::~Native()

{

}

void Native::Init(OutGoingCallback onNotify)

{

_onNotify = onNotify;

OutGoingMessage("Cpp UnManaged class Init Complete");

}

void Native::OutGoingMessage(std::string outGoingMessage)

{

//printf("Cpp UnManaged class -> Managed class -> C# Outgoing Message\n");

_onNotify(outGoingMessage);

}

void Native::InCommingMessage(std::string inCommingMessage)

{

//printf("C#-> Managed class -> Cpp UnManaged class ->  inComming Message\n");

printf("Cpp Notify InCommingMessage:: %s\n", inCommingMessage.c_str());

}

void Native::Stop()

{

printf("Cpp Native Stop\n");

}


C#


Entry.cs


using System;

using System.Collections.Concurrent;

using System.Diagnostics;

using System.IO;

using System.Linq;

using System.Reflection;

using System.Threading;


namespace ProtoType

{

    public class Entry :IDisposable

    {

        private ConcurrentDictionary<string, ProcessObject> _processes;

        private bool _disposed;

        public Entry()

        {

            _processes = new ConcurrentDictionary<string, ProcessObject>();

        }

        protected void Dispose(bool isDispose)

        {

            foreach(var p in _processes)

            {

                p.Value.CancellationToken.Cancel();

            }

            _processes.Clear();

            _disposed = isDispose;

        }

        public void Dispose()

        {

            if (_disposed)

                return;

            Dispose(true);

        }

        //각 DLL로 부터 Notify가 들어오는 구간

        private void OnNotify(object sender, string message)

        {

            Console.WriteLine($"Center Notify Recive : {message}");

            //등록되어 있는 Dll 클래스에게 Notify 전달

            foreach (var item in _processes)

            {

                var method = item.Value.DllProcess.GetType().GetMethod("InCommingMessage", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);

                method?.Invoke(item.Value.DllProcess, new object[] { message });

            }

        }

        private void ProcessStart(object process)

        {

            ProcessObject state = process as ProcessObject;

            var stopMethod = state.DllProcess.GetType().GetMethod("Stop", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);

            while (!state.CancellationToken.IsCancellationRequested)

            {

                try

                {

                    if (state.Started)

                        continue;


                    var initMethod = state.DllProcess.GetType().GetMethod("Init", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);

                    initMethod?.Invoke(state.DllProcess, null);


                    var runMethod = state.DllProcess.GetType().GetMethod("Run", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);

                    runMethod?.Invoke(state.DllProcess, null);


                    state.Started = true;

                }

                catch(Exception ex)

                {

                    stopMethod?.Invoke(state.DllProcess, null);

                    state.Started = false;

                    Trace.WriteLine(ex.Message);

                }

                finally

                {

                    Thread.Sleep(1000);

                }

            }

            if (state.DllProcess is CS.CSInterface)

            {

                var p = state.DllProcess as CS.CSInterface;

                p.OnNotify -= OnNotify;

            }

            else if (state.DllProcess is Cpp.CppInterface)

            {

                var p = state.DllProcess as Cpp.CppInterface;

                p.OnNotify -= OnNotify;

            }

            stopMethod? .Invoke(state.DllProcess, null);

            Console.WriteLine($"Thread Close");

        }

        public void Start()

        {

            var files = Directory.GetFiles(Environment.CurrentDirectory).Where(r=>r.EndsWith("dll")).ToList();

            foreach(var file in files)

            {

                var assem = Assembly.LoadFile(file);

                var types = assem.GetExportedTypes().Where(r => r.IsClass &&

                                                                r.GetInterfaces().Any(i => i.Equals(typeof(Cpp.CppInterface))) ||

                                                                r.GetInterfaces().Any(i => i.Equals(typeof(CS.CSInterface)))

                                                                ).ToList();

                foreach (var type in types)

                {

                    var process = new ProcessObject();

                    process.CancellationToken = new CancellationTokenSource();


                    process.DllProcess = Activator.CreateInstance(type);

                    if (process.DllProcess is CS.CSInterface)

                    {

                        var p = process.DllProcess as CS.CSInterface;

                        p.OnNotify += OnNotify; //옵저버 패턴 생각하면 됨

                    }

                    else if (process.DllProcess is Cpp.CppInterface)

                    {

                        var p = process.DllProcess as Cpp.CppInterface;

                        p.OnNotify += OnNotify; //옵저버 패턴 생각하면 됨

                    }

                    _processes.TryAdd(type.Name, process);

                }

            }

            foreach (var process in _processes)

            {

                ThreadPool.QueueUserWorkItem(ProcessStart, process.Value);

            }

        }

    }

}


결과






반응형

+ Recent posts