Хочу поделиться собственным опытом разработки под VisualStudio 2010. Приложение многопоточное, т.е. в отдельных потоках происходит запуск, работа и остановка основных частей приложения.
Допустим, мы на .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

Использование состояний и причин их возникновения. Это даёт повод выучить понятие «Диаграмма состояний», что существенно облегчает проектирование программы.
Piccy.info - Free Image Hosting
Моя схема выполнена не совсем по правилам, я просто хотел показать, что составление диаграммы состояний это круто.
Надеюсь, по крайней мере, начинающие мпрограммизды найдут в этом посте что-то хорошее.
Ах да… 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;
}
}