Работает с помощью свободного аналога .Net - Mono(http://mono-project.com).
Суть работы: вызовы PluginProcess транслируются в .Net, при этом для обработки данных используются так называемые потоки(streams).
Вызовы к CommFortGetData и CommFortProcess транслируются из .Net в С.
Последняя версия: 0.4
http://unix-net.ru/install_cf_xmono_4.zip
Плагин может загружать неограниченное число .Net-плагинов внутрь себя, за это отвечает обертка CommFortMono.dll.
Плагины на .Net должны лежать в <client/server>/Plugins/.NET
Небольшая документация по интерфейсам внутри .Net:
Код: Выделить всё
CMFData:
int GetSize() - возвращает размер данных.
byte[] GetData() - возвращает данные.
CMFStream:
Create() - создает пусть поток для дальнейшей передачи данных программе.
Destroy() - удаляет поток, освобождает все занятые ресурсы.
WriteString(string str) - записывает строку в поток.
WriteInteger(int i) - записывает число в поток.
WriteData(byte[] b) - записывает данные в поток.
string ReadString() - читает строку из потока.
int ReadInteger() - читает число из потока.
CMFData ReadData() - читает данные из потока.
Reset() - переходит к началу потока, для повторного чтения.
CMFContext:
Call(int id, CMFStream stream) - вызывает функцию CommFort(аналог CommFortPluginProcess в С)
CMFStream GetData(int id, CMFStream stream) - получает данные от CommFort(аналог CommFortGetData в С)
Код: Выделить всё
CommFortMono.Module.Load - вызывается при PluginStart, параметр: CMFContext ctx
CommFortMono.Module.Stop - вызывается при PluginStop.
CommFortMono.Module.Process - вызывается при PluginProcess, параметры: int id, CMFStream stream
Код: Выделить всё
    public interface IPlugin
    {
        // Вызывается при запуске .Net плагина
        void Main();
        // Вызывается в случае создания нового CMFContext
        void OnLoad(CMFContext ctx);
        // Вызывается при остановке xMono
        void OnStop();
        // Вызывается при PluginProcess
        void OnProcess(int id, CMFStream stream);
    }
Код: Выделить всё
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SamplePlugin
{
    public class Plugin : CommFortMono.IPlugin
    {
        public void Main()
        {
            Console.WriteLine("[Sample plugin]: Loaded");
        }
        public void OnStop()
        {
        }
        public void OnLoad(CMFContext ctx)
        {
        }
        public void OnProcess(int id, CMFStream stream)
        {
            if (id == 5)
            {
                string userName = stream.ReadString();
                string ipz = stream.ReadString();
                int userType = stream.ReadInteger();
                string chanName = stream.ReadString();
                int type = stream.ReadInteger();
                string text = stream.ReadString();
                if (chanName == "Общий")
                {
                    Console.WriteLine("message from username: " + userName);
                    stream = CMFStream.Create();
                    stream.WriteString("anshes and her friends");
                    stream.WriteInteger(0);
                    stream.WriteString("Сообщение на общем канале [" + userName + "]: " + text);
                    Module.GetContext().Call(50, stream);
                }
            }
        }
    }
}
Пример скрипта-редиректера:
Код: Выделить всё
// Firstly load Xml assembly via small preprocessor definition
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using CommFortMono;
public class Script
{
	private string _fromChannel;
	private string _toChannel;
	public void Main()
	{
	    //Module.Load += new Module.OnLoadHandler(OnLoad);
	    //Module.Stop += new Module.OnStopHandler(OnStop);
	    //Module.Process += new Module.OnProcessHandler(OnProcess);
		Module.Load += OnLoad;
		Module.Stop += OnStop;
		Module.Process += OnProcess;
		Console.WriteLine("[redirect.cs]: Loaded");
		XmlReader reader = XmlReader.Create("Plugins/.NET/Scripts/Redirect.xml");
		while(reader.Read())
		{
			if(reader.NodeType == XmlNodeType.Element && reader.Name == "Redirect")
			{
				_fromChannel = reader.GetAttribute(0);
				_toChannel = reader.GetAttribute(1);
				return;
			}
		}
	}
	
	public void Unload()
	{
		Module.Load -= OnLoad;
		Module.Stop -= OnStop;
		Module.Process -= OnProcess;
	}
	
	public void OnStop()
	{
	}
	public void OnLoad(CMFContext ctx)
	{
	}
	public void OnProcess(int id, CMFStream stream)
	{
		if (id == 5)
		{
			string userName = stream.ReadString();
			string ipz = stream.ReadString();
			int userType = stream.ReadInteger();
			string chanName = stream.ReadString();
			int type = stream.ReadInteger();
			string text = stream.ReadString();
			CMFData data = null;
			if(type == 2)
			{
				data = stream.ReadData();
			}
			
			
			if (chanName == _fromChannel)
			{
				stream = CMFStream.Create();
				stream.WriteString(_toChannel);
				stream.WriteInteger(0);
				stream.WriteString("[" + _fromChannel + "]: " + userName + ": " + text);
				Module.GetContext().Call(50, stream);
			}
			
			if(type == 2 && chanName == _fromChannel)
			{
				stream = CMFStream.Create();
				stream.WriteString(_toChannel);
				stream.WriteInteger(1);
				stream.WriteData(data.GetData());
				Module.GetContext().Call(51, stream);
				stream.Destroy();
				//File.WriteAllBytes("test.jpg", data.GetData());
			}
		}
	}
}

