Хочу поделиться собственным опытом разработки под VisualStudio 2010. Приложение многопоточное, т.е. в отдельных потоках происходит запуск, работа и остановка основных частей приложения.
Допустим, мы на .NET сделаем интерфейс приложения, а C++ оставим самую «грязную» работу, которая будет находиться в отдельной DLL.
Далее код C++ для DLL:
Поясню.


Моя схема выполнена не совсем по правилам, я просто хотел показать, что составление диаграммы состояний это круто.
Надеюсь, по крайней мере, начинающие мпрограммизды найдут в этом посте что-то хорошее.
Ах да… C# обложка для работы с контекстом C++
Допустим, мы на .NET сделаем интерфейс приложения, а C++ оставим самую «грязную» работу, которая будет находиться в отдельной DLL.
Далее код C++ для DLL:
#define CLIENTDLL_API __declspec(dllexport)
// тип метода для вывода строковых сообщений (лог) в .NET-приложение.
typedef void (__stdcall *LOG_CALLBACK)(const wchar_t* LogString);
// тип метода для вывода текущего состояния и причины
typedef void (__stdcall *AUTOSTATE_CHANGED_CALLBACK)(AutostateNs::Autostate state, CauseNs::Cause cause);
// создание контекста
CLIENTDLL_API ClientContext *CreateContext(LOG_CALLBACK WriteLog, AUTOSTATE_CHANGED_CALLBACK AutostateChanged);
// инициализация контекста: тут производить действия, с наиболее высокой вероятностью возникновения ошибки
CLIENTDLL_API HRESULT Init(ClientContext *clientContext);
// удаления контекста
CLIENTDLL_API void DeleteContext(ClientContext *clientContext);
// далее выполнения действий с контекстом
CLIENTDLL_API void Start(ClientContext *clientContext, PCSTR IpAddress, PCSTR TcpPort);
CLIENTDLL_API void Stop(ClientContext *clientContext);
Поясню.
Фишка №1
заключается в том что-бы работать с контекстом — неким прокси-объектом, который в себе будет инкапсулировать один или несколько объектов. Для начинающих будет не лишним знать (или напомнить) об этом моменте. Ещё одно преимущество в том что C#'у не надо ничего знать о классе ClientContext, он просто будет получать указатель IntPtr, и передавать его в экспортируемые функции из DLL при вызове.Фишка №2
при создании контекста передаём указатели на методы C# (каллбэки), что-бы C++ сам информировал C# об изменении своего текущего состояния и о причинах его изменения (вместо причины можно использовать код ошибки, но так непонятнее), а также для вывода строк текста (даже русских!!!). Ещё хочу отметить отсутствие функции запроса состояния к объекту C++, C# сам должен запоминать состояние объекта C++ и не беспокоить неуправляемый код понапрасну, тем более на C# легче организовать многопоточную работу с состояниями. Конечно если нужно получать статистику работы (количество отправленных байт) в реальном времени, то придётся C++ беспокоить часто. И ещё отмечаю, что методы Start и Stop не сколько выполняют запуск/остановку, а инициируют эти процессы (выполняются асинхронно), результаты всегда выводятся одним образом — через каллбек-функцию, переданную при создании контекста.Фишка №3
Использование состояний и причин их возникновения. Это даёт повод выучить понятие «Диаграмма состояний», что существенно облегчает проектирование программы.

Моя схема выполнена не совсем по правилам, я просто хотел показать, что составление диаграммы состояний это круто.
Надеюсь, по крайней мере, начинающие мпрограммизды найдут в этом посте что-то хорошее.
Ах да… C# обложка для работы с контекстом C++
public class Клиент : IDisposable
{
private delegate void WriteLog_Делегат([MarshalAs(UnmanagedType.LPWStr)]string Сообщение);
private WriteLog_Делегат WriteLog;
private delegate void AutostateChanged_Делегат(АвтоматизированоеСостояние СостояниеАвтоматизированогоКомпонента, Причина Причина);
private AutostateChanged_Делегат AutostateChanged;
[DllImport("Client.dll", EntryPoint = "?CreateContext@@YAPAVClientContext@@P6GXPB_W@ZP6GXW4Autostate@AutostateNs@@W4Cause@CauseNs@@@Z@Z", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr CreateContext(WriteLog_Делегат ФункцияЗаписВЛог, AutostateChanged_Делегат AutostateChanged);
[DllImport(...
private static extern int Init(IntPtr Контекст);
[DllImport(...
private static extern void DeleteContext(IntPtr Контекст);
[DllImport(...
private static extern void Start(IntPtr Контекст, [MarshalAs(UnmanagedType.LPStr)] string IpAddress, [MarshalAs(UnmanagedType.LPStr)]string TcpPort);
[DllImport(...
private static extern void Stop(IntPtr Контекст);
private IntPtr Контекст = IntPtr.Zero;
private ПотокобезопасноеСостояние Состояние_ = new ПотокобезопасноеСостояние();
public ПотокобезопасноеСостояниеДляЧтения Состояние { get { return this.Состояние_; } }
public Клиент(Двиг Двиг)
: base(Двиг.Mainform)
{
this.Двиг = Двиг;
this.WriteLog = new WriteLog_Делегат(this.ФункцияЗаписВЛог);
this.AutostateChanged = new AutostateChanged_Делегат(this.ФункцияИзмененияСостоянияАвтоматизированогоКомпонента);
this.Контекст = CreateContext(this.WriteLog, this.AutostateChanged);
if (Init(this.Контекст) != 0)
{
this.Dispose();
throw new Exception("Не удалась инициализация клиента");
}
}
public void Dispose()
{
if (this.Контекст != IntPtr.Zero)
{
DeleteContext(this.Контекст);
this.Контекст = IntPtr.Zero;
}
}
private void ФункцияЗаписВЛог(string Сообщение)
{
Лог.ДобавитьСообщение("Неуправляемый код: {0}", Сообщение);
}
private void ФункцияИзмененияСостоянияАвтоматизированогоКомпонента(АвтоматизированоеСостояние СостояниеАвтоматизированогоКомпонента, Причина Причина)
{
this.Состояние_.ИзменитьСостояние(СостояниеАвтоматизированогоКомпонента, Причина);
}
public void Старт(IPAddress АдресСервера, int ПортСервера = 27030)
{
Start(this.Контекст, АдресСервера.ToString(), ПортСервера.ToString());
}
public bool Стоп()
{
if (this.АвтоматизированоеСостояние_.Cостояние == АвтоматизированоеСостояние.Запущен)
{
Stop(this.Контекст);
return true;
}
return false;
}
}