   C++ DLL with a C# consumer.
  What I first thought to be a pretty trivial idea has somewhat turned into a nightmare for me this afternoon.I have a DLL written in C++. It is written in C++ because I have used lex and bison to generate a parser that I am using in my project. This builds up a list of structures (objects) into a std::vector. Because I hate doing windows GUI stuff in C++, I'm wanting to do the GUI in C#. So obviously the plan is to grab that list of objects from the DLL and use it within my C# app.I realize theres some marshaling when taking types from different languages etc, but I just don't seem to be able to get anything out of the DLL (OK I lied, I managed to write a test function to return the int 6 from the DLL). I've tried just a vector of strings, and I have read that vectors can be automatically marshaled to ArrayLists and strings to String or StringBuilder, but I can't seem to get anything.So for one, I'm asking, is this possible? Actually I'm pretty certain it's possible, just is it possible without writing a million different workarounds and hacks? If so, I'd love to hear how. Secondly, am I not seeing the forrest for the trees? Ie. is there a better way to go about having data ops in C++ and GUI stuff in C#?If this doesn't make sense then please forgive me. I've never worked with DLLs before and I think the state of my brain now suggests that I hate them.   
  There is the option that you can adjust your C++ DLL to compile into a C++/CLI class library DLL.  These are much easier to use from C# once you've written them, but a little freaky to learn the extra C++ syntax rules that are involved.  In my opinion this is the superior choice over DllImporting if there are complex data structures or numerous functions involved.The idea is that you can write some 'native' C++ code and some managed C++ code in the same DLL, have them interface with each other very cleanly, then expose the managed side of it to .Net DLL consumers like C#.Here is a complete example of a C++/CLI Win32 debugger library for x86 that I wrote from scratch rather than DllImporting and manually marshalling structures in C#:(Start:  Project wizard -> [Visual C++] -> [CLR] -> [Class Library])(Modify Wizard File: Stdafx.h)#pragma once#define _WIN32_WINNT 0x0501#include <windows.h>#include <psapi.h>(File: Stdafx.cpp is default from wizard)(File: AssemblyInfo.cpp is default from wizard)(Add New File: DebugLibrary.h)#pragma onceusing namespace System;using namespace System::Windows::Forms;using namespace System::Collections::Generic;#include "stdafx.h"namespace DebugLibrary{[Flags]public enum class Flags86 : Int32{CF    = 0x00000001,// bit 1 must be 1PF    = 0x00000004,// bit 3 must be 0AF    = 0x00000010,// bit 5 must be 0ZF    = 0x00000040,SF    = 0x00000080,TF    = 0x00000100,IF    = 0x00000200,DF    = 0x00000400,OF    = 0x00000800,IOPL1 = 0x00001000,IOPL2 = 0x00002000,IOPL3 = 0x00003000,NT    = 0x00004000,// bit 15 must be 0RF    = 0x00010000,VM    = 0x00020000,AC    = 0x00040000,VIF   = 0x00080000,VIP   = 0x00100000,ID    = 0x00200000// bits 22-31 must be 0};public ref class ThreadContext{public:    unsigned int Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;    unsigned int Cs, Ds, Es, Fs, Gs, Ss;    unsigned int Eax, Ecx, Edx, Ebx, Esi, Edi, Ebp, Esp;    unsigned int Eip;    Flags86 EFlags;};public ref class LDTEntry{public:    unsigned short LimitLow;    unsigned short BaseLow;    Byte BaseMid;    Byte Type;    Byte Dpl;    Byte Pres;    Byte LimitHi;    Byte Sys;    Byte Default_Big;    Byte Granularity;    Byte BaseHi;};public enum class ExceptionType{AccessViolation,ArrayBoundsExceeded,Breakpoint,DataTypeMisalignment,FltDenormalOperand,FltDivideByZero,FltInexactResult,FltInvalidOperation,FltOverflow,FltStackCheck,FltUnderflow,IllegalInstruction,InPageError,IntDivideByZero,IntOverflow,InvalidDisposition,NoncontinuableException,PriviligedInstruction,SingleStep,StackOverflow,CtrlC};public ref class ExceptionRecord{public:ExceptionType type;bool noncontinuable;ExceptionRecord ^nestedRecord;unsigned int address;};public ref class DebugEvent{public:int processId;int threadId;};public ref class DebugEvent_CreateProcess : DebugEvent{public:IntPtr file;IntPtr process;IntPtr thread;unsigned int imageBase;unsigned int debugInfoFileOffset;unsigned int debugInfoSize;unsigned int threadLocalBase;unsigned int startAddress;// no image name, it's a pain.};public ref class DebugEvent_CreateThread : DebugEvent{public:IntPtr thread;unsigned int threadLocalBase;unsigned int startAddress;};public ref class DebugEvent_Exception : DebugEvent{public:ExceptionRecord ^exceptionRecord;bool firstChance;};public ref class DebugEvent_ExitProcess : DebugEvent{public:unsigned int exitCode;};public ref class DebugEvent_ExitThread : DebugEvent{public:unsigned int exitCode;};public ref class DebugEvent_LoadDll : DebugEvent{public:IntPtr file;unsigned int baseOfDll;unsigned int debugInfoFileOffset;unsigned int debugInfoSize;//String ^imageName; // annoying};public ref class DebugEvent_OutputDebugString : DebugEvent{public:String ^debugString;};public ref class DebugEvent_RIP : DebugEvent{public:unsigned int error;unsigned int type;};public ref class DebugEvent_UnloadDll : DebugEvent{public:unsigned int baseOfDll;};public enum class PageState{Commit  = 0x1000,Reserve = 0x2000,Free    = 0x10000};[Flags]public enum class PageProtection{NoAccess    = 0x001,ReadOnly    = 0x002,ReadWrite   = 0x004,WriteCopy   = 0x008,Execute= 0x010,ExecuteRead = 0x020,ExecuteReadWrite = 0x040,ExecuteWriteCopy = 0x080,Guard  = 0x100,NoCache= 0x200,WriteCombine= 0x400};public enum class PageType{Private = 0x20000,Mapped  = 0x40000,Image   = 0x1000000};public ref class MemoryInfo{public:unsigned int baseAddress;unsigned int allocationBase;PageProtection allocationProtection;unsigned int regionSize;PageState state;PageProtection protection;PageType pageType;MemoryInfo(const MEMORY_BASIC_INFORMATION &memInfo);};public ref class ModuleInfo{public:String ^baseName;String ^fileName;unsigned int baseAddress;unsigned int imageSize;unsigned int entryPoint;ModuleInfo(IntPtr process, HMODULE module);};public ref class DebugUtil{public:static unsigned int SuspendThread(int threadId);static unsigned int ResumeThread(int threadId);// Debugging-class functionsstatic bool CheckRemoteDebuggerPresent(IntPtr process);static void ContinueDebugEvent(int processId, int threadId, bool ignoreException);static void DebugActiveProcess(int  processId);static void DebugActiveProcessStop(int  processId);static void DebugBreakProcess(IntPtr process);static void FlushInstructionCache(IntPtr process, IntPtr address, long count);static ThreadContext ^GetThreadContext(int threadId);static LDTEntry ^GetThreadSelectorEntry(int threadId, unsigned int selector);static void SetThreadContext(int threadId, ThreadContext ^context);static DebugEvent ^WaitForDebugEvent(unsigned int milliseconds);static List<MemoryInfo^> ^GetMemoryInfo(IntPtr process);static List<ModuleInfo^> ^DebugUtil::GetModuleInfo(IntPtr process);static void VirtualProtectEx(IntPtr process, IntPtr address, unsigned int bytes, PageProtection protection);};}(Add New File: DebugLibrary.cpp)#include "stdafx.h"#include "DebugLibrary.h"using namespace System::Security;namespace DebugLibrary{MemoryInfo::MemoryInfo(const MEMORY_BASIC_INFORMATION &memInfo){baseAddress= (unsigned int)memInfo.BaseAddress;allocationBase  = (unsigned int)memInfo.AllocationBase;allocationProtection = (PageProtection)memInfo.AllocationProtect;regionSize = memInfo.RegionSize;state = (PageState)memInfo.State;protection = (PageProtection)memInfo.Protect;pageType   = (PageType)memInfo.Type;}// ---------------ModuleInfo::ModuleInfo(IntPtr process, HMODULE module){static __wchar_t buffer[512];GetModuleBaseName(process.ToPointer(), module, buffer, 512);baseName = gcnew String(buffer);GetModuleFileNameEx(process.ToPointer(), module, buffer, 512);fileName = gcnew String(buffer);MODULEINFO moduleInfo;GetModuleInformation(process.ToPointer(), module, &moduleInfo, sizeof(MODULEINFO));baseAddress = (unsigned int)moduleInfo.lpBaseOfDll;imageSize = moduleInfo.SizeOfImage;entryPoint = (unsigned int)moduleInfo.EntryPoint;}// ---------------[SuppressUnmanagedCodeSecurity]unsigned int DebugUtil::SuspendThread(int threadId){HANDLE thread = OpenThread(THREAD_ALL_ACCESS, false, threadId);unsigned int toret = ::SuspendThread(thread);CloseHandle(thread);return toret;}[SuppressUnmanagedCodeSecurity]unsigned int DebugUtil::ResumeThread(int threadId){HANDLE thread = OpenThread(THREAD_ALL_ACCESS, false, threadId);unsigned int toret = ::ResumeThread(thread);CloseHandle(thread);return toret;}[SuppressUnmanagedCodeSecurity]bool DebugUtil::CheckRemoteDebuggerPresent(IntPtr process){int toret;if (!::CheckRemoteDebuggerPresent(process.ToPointer(), &toret))throw gcnew Exception();return toret != 0;}[SuppressUnmanagedCodeSecurity]void DebugUtil::ContinueDebugEvent(int processId, int threadId, bool ignoreException){if (!::ContinueDebugEvent(processId, threadId, ignoreException ? DBG_CONTINUE : DBG_EXCEPTION_NOT_HANDLED))throw gcnew Exception();}[SuppressUnmanagedCodeSecurity]void DebugUtil::DebugActiveProcess(int processId){if (!::DebugActiveProcess(processId) || !::DebugSetProcessKillOnExit(false))throw gcnew Exception();}[SuppressUnmanagedCodeSecurity]void DebugUtil::DebugActiveProcessStop(int processId){if (!::DebugActiveProcessStop(processId))throw gcnew Exception();}[SuppressUnmanagedCodeSecurity]void DebugUtil::DebugBreakProcess(IntPtr process){if (!::DebugBreakProcess(process.ToPointer()))throw gcnew Exception();}[SuppressUnmanagedCodeSecurity]void DebugUtil::FlushInstructionCache(IntPtr process, IntPtr address, long count){if (!::FlushInstructionCache(process.ToPointer(), address.ToPointer(), count))throw gcnew Exception();}[SuppressUnmanagedCodeSecurity]ThreadContext ^DebugUtil::GetThreadContext(int threadId){HANDLE thread = OpenThread(THREAD_ALL_ACCESS, false, threadId);CONTEXT context;memset(&context, 0, sizeof(context));context.ContextFlags = CONTEXT_ALL;try{if (!::GetThreadContext(thread, &context))throw gcnew Exception();}finally{CloseHandle(thread);}ThreadContext ^toret = gcnew ThreadContext;toret->Dr0 = context.Dr0;toret->Dr1 = context.Dr1;toret->Dr2 = context.Dr2;toret->Dr3 = context.Dr3;toret->Dr6 = context.Dr6;toret->Dr7 = context.Dr7;toret->Cs = context.SegCs;toret->Ds = context.SegDs;toret->Es = context.SegEs;toret->Fs = context.SegFs;toret->Gs = context.SegGs;toret->Ss = context.SegSs;toret->Eax = context.Eax;toret->Ecx = context.Ecx;toret->Edx = context.Edx;toret->Ebx = context.Ebx;toret->Esi = context.Esi;toret->Edi = context.Edi;toret->Ebp = context.Ebp;toret->Esp = context.Esp;toret->Eip = context.Eip;toret->EFlags = (Flags86)context.EFlags;return toret;}[SuppressUnmanagedCodeSecurity]LDTEntry ^DebugUtil::GetThreadSelectorEntry(int threadId, unsigned int selector){LDT_ENTRY selectorEntry;HANDLE thread = OpenThread(THREAD_ALL_ACCESS, false, threadId);try{if (!::GetThreadSelectorEntry(thread, selector, &selectorEntry))throw gcnew Exception();}finally{CloseHandle(thread);}LDTEntry ^toret = gcnew LDTEntry;toret->LimitLow = selectorEntry.LimitLow;toret->BaseLow = selectorEntry.BaseLow;toret->BaseMid = selectorEntry.HighWord.Bits.BaseMid;toret->Type = selectorEntry.HighWord.Bits.Type;toret->Dpl = selectorEntry.HighWord.Bits.Dpl;toret->Pres = selectorEntry.HighWord.Bits.Pres;toret->LimitHi = selectorEntry.HighWord.Bits.LimitHi;toret->Sys = selectorEntry.HighWord.Bits.Sys;toret->Default_Big = selectorEntry.HighWord.Bits.Default_Big;toret->Granularity = selectorEntry.HighWord.Bits.Granularity;toret->BaseHi = selectorEntry.HighWord.Bits.BaseHi;return toret;}[SuppressUnmanagedCodeSecurity]void DebugUtil::SetThreadContext(int threadId, ThreadContext ^context){CONTEXT x86context;memset(&x86context, 0, sizeof(context));x86context.ContextFlags = CONTEXT_ALL;HANDLE thread = OpenThread(THREAD_ALL_ACCESS, false, threadId);try{if (!::GetThreadContext(thread, &x86context))throw gcnew Exception();x86context.Dr0 = context->Dr0;x86context.Dr1 = context->Dr1;x86context.Dr2 = context->Dr2;x86context.Dr3 = context->Dr3;x86context.Dr6 = context->Dr6;x86context.Dr7 = context->Dr7;x86context.SegCs = context->Cs;x86context.SegDs = context->Ds;x86context.SegEs = context->Es;x86context.SegFs = context->Fs;x86context.SegGs = context->Gs;x86context.SegSs = context->Ss;x86context.Eax = context->Eax;x86context.Ecx = context->Ecx;x86context.Edx = context->Edx;x86context.Ebx = context->Ebx;x86context.Esi = context->Esi;x86context.Edi = context->Edi;x86context.Ebp = context->Ebp;x86context.Esp = context->Esp;x86context.Eip = context->Eip;x86context.EFlags = (DWORD)context->EFlags;if (!::SetThreadContext(thread, &x86context))throw gcnew Exception();}finally{CloseHandle(thread);}}[SuppressUnmanagedCodeSecurity]DebugEvent ^DebugUtil::WaitForDebugEvent(unsigned int milliseconds){DEBUG_EVENT debugEvent;if (!::WaitForDebugEvent(&debugEvent, milliseconds))return nullptr;DebugEvent ^toret;switch (debugEvent.dwDebugEventCode){case CREATE_PROCESS_DEBUG_EVENT:{DebugEvent_CreateProcess ^de = gcnew DebugEvent_CreateProcess;toret = de;de->debugInfoFileOffset = debugEvent.u.CreateProcessInfo.dwDebugInfoFileOffset;de->debugInfoSize = debugEvent.u.CreateProcessInfo.nDebugInfoSize;de->file = IntPtr(debugEvent.u.CreateProcessInfo.hFile);de->imageBase = (unsigned int)debugEvent.u.CreateProcessInfo.lpBaseOfImage;de->process = IntPtr(debugEvent.u.CreateProcessInfo.hProcess);de->startAddress = (unsigned int)debugEvent.u.CreateProcessInfo.lpStartAddress;de->thread = IntPtr(debugEvent.u.CreateProcessInfo.hThread);de->threadLocalBase = (unsigned int)debugEvent.u.CreateProcessInfo.lpThreadLocalBase;}break;case CREATE_THREAD_DEBUG_EVENT:{DebugEvent_CreateThread ^de = gcnew DebugEvent_CreateThread;toret = de;de->thread = IntPtr(debugEvent.u.CreateThread.hThread);de->startAddress = (unsigned int)debugEvent.u.CreateThread.lpStartAddress;de->threadLocalBase = (unsigned int)debugEvent.u.CreateThread.lpThreadLocalBase;}break;case EXCEPTION_DEBUG_EVENT:{DebugEvent_Exception ^de = gcnew DebugEvent_Exception;toret = de;EXCEPTION_RECORD *current = &debugEvent.u.Exception.ExceptionRecord;ExceptionRecord ^er = de->exceptionRecord = gcnew ExceptionRecord;do{er->address = (unsigned int)current->ExceptionAddress;er->noncontinuable = current->ExceptionFlags == EXCEPTION_NONCONTINUABLE;switch (current->ExceptionCode){case EXCEPTION_ACCESS_VIOLATION:    er->type = ExceptionType::AccessViolation; break;case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:    er->type = ExceptionType::ArrayBoundsExceeded; break;case EXCEPTION_BREAKPOINT:er->type = ExceptionType::Breakpoint; break;case EXCEPTION_DATATYPE_MISALIGNMENT:    er->type = ExceptionType::DataTypeMisalignment; break;case EXCEPTION_FLT_DENORMAL_OPERAND:er->type = ExceptionType::FltDenormalOperand; break;case EXCEPTION_FLT_DIVIDE_BY_ZERO:  er->type = ExceptionType::FltDivideByZero; break;case EXCEPTION_FLT_INEXACT_RESULT:  er->type = ExceptionType::FltInexactResult; break;case EXCEPTION_FLT_INVALID_OPERATION:    er->type = ExceptionType::FltInvalidOperation; break;case EXCEPTION_FLT_OVERFLOW:   er->type = ExceptionType::FltOverflow; break;case EXCEPTION_FLT_STACK_CHECK:er->type = ExceptionType::FltStackCheck; break;case EXCEPTION_FLT_UNDERFLOW:  er->type = ExceptionType::FltUnderflow; break;case EXCEPTION_ILLEGAL_INSTRUCTION: er->type = ExceptionType::IllegalInstruction; break;case EXCEPTION_IN_PAGE_ERROR:  er->type = ExceptionType::InPageError; break;case EXCEPTION_INT_DIVIDE_BY_ZERO:  er->type = ExceptionType::IntDivideByZero; break;case EXCEPTION_INT_OVERFLOW:   er->type = ExceptionType::IntOverflow; break;case EXCEPTION_INVALID_DISPOSITION: er->type = ExceptionType::InvalidDisposition; break;case EXCEPTION_NONCONTINUABLE_EXCEPTION: er->type = ExceptionType::NoncontinuableException; break;case EXCEPTION_PRIV_INSTRUCTION:    er->type = ExceptionType::PriviligedInstruction; break;case EXCEPTION_SINGLE_STEP:    er->type = ExceptionType::SingleStep; break;case EXCEPTION_STACK_OVERFLOW: er->type = ExceptionType::StackOverflow; break;case DBG_CONTROL_C:  er->type = ExceptionType::CtrlC; break;}current = current->ExceptionRecord;if (current != NULL){ExceptionRecord ^nested = gcnew ExceptionRecord;er->nestedRecord = nested;}}while (current != NULL);de->firstChance = debugEvent.u.Exception.dwFirstChance != 0;}break;case EXIT_PROCESS_DEBUG_EVENT:{DebugEvent_ExitProcess ^de = gcnew DebugEvent_ExitProcess;toret = de;de->exitCode = debugEvent.u.ExitProcess.dwExitCode;}break;case EXIT_THREAD_DEBUG_EVENT:{DebugEvent_ExitThread ^de = gcnew DebugEvent_ExitThread;toret = de;de->exitCode = debugEvent.u.ExitThread.dwExitCode;}break;case LOAD_DLL_DEBUG_EVENT:{DebugEvent_LoadDll ^de = gcnew DebugEvent_LoadDll;toret = de;de->baseOfDll = (unsigned int)debugEvent.u.LoadDll.lpBaseOfDll;de->debugInfoFileOffset = debugEvent.u.LoadDll.dwDebugInfoFileOffset;de->debugInfoSize = debugEvent.u.LoadDll.nDebugInfoSize;de->file = IntPtr(debugEvent.u.LoadDll.hFile);}break;case OUTPUT_DEBUG_STRING_EVENT:{// Slacker HackDebugEvent ^de = gcnew DebugEvent;toret = de;}break;case RIP_EVENT:{// Slacker HackDebugEvent ^de = gcnew DebugEvent;toret = de;}break;case UNLOAD_DLL_DEBUG_EVENT:{// Slacker HackDebugEvent ^de = gcnew DebugEvent;toret = de;}break;}toret->processId = debugEvent.dwProcessId;toret->threadId = debugEvent.dwThreadId;return toret;}[SuppressUnmanagedCodeSecurity]List<MemoryInfo^> ^DebugUtil::GetMemoryInfo(IntPtr process){SYSTEM_INFO si;MEMORY_BASIC_INFORMATION mbi;GetSystemInfo(&si);List<MemoryInfo^> ^toret = gcnew List<MemoryInfo^>();for (char *addr = (char*)si.lpMinimumApplicationAddress; addr < si.lpMaximumApplicationAddress;){if (VirtualQueryEx(process.ToPointer(), addr, &mbi, sizeof(mbi))){if (mbi.RegionSize != 0 && mbi.Protect != 0 && mbi.AllocationBase != 0){toret->Add(gcnew MemoryInfo(mbi));}addr = (char*)((DWORD)mbi.BaseAddress + (DWORD)mbi.RegionSize);}else{addr += si.dwPageSize;}}return toret;}[SuppressUnmanagedCodeSecurity]List<ModuleInfo^> ^DebugUtil::GetModuleInfo(IntPtr process){HMODULE *hmodules = 0;DWORD needed = 0;EnumProcessModules(process.ToPointer(), hmodules, 0, &needed);hmodules = new HMODULE[needed/sizeof(HMODULE)];EnumProcessModules(process.ToPointer(), hmodules, needed, &needed);List<ModuleInfo^> ^modules = gcnew List<ModuleInfo^>();for (unsigned int i=0; i<needed/4; ++i){modules->Add(gcnew ModuleInfo(process, hmodules));}delete[] hmodules;return modules;}[SuppressUnmanagedCodeSecurity]void DebugUtil::VirtualProtectEx(IntPtr process, IntPtr address, unsigned int bytes, PageProtection protection){DWORD oldProtection;if (!::VirtualProtectEx(process.ToPointer(), address.ToPointer(), bytes, (DWORD)protection, &oldProtection))throw gcnew Exception("Failed to set page protection.");}}After you've got a C++/CLI Class Library project created, you can add it as an assembly reference to C# projects rather than doing any DllImporting at all.  It will automagically allow you to use anything that's public from the DLL as easily as framework classes.As you can see above there are just a bunch of places where a function will intermix Win32 calls and structures with some look-alike managed functions and structures.  In this case the code is mostly just calling Win32 functions, copying over struct members and redefining constants/enumerations, and translating error codes to exceptions.[Edited by - Nypyren on September 7, 2007 2:43:46 AM];  Thanks a lot for that. I don't have time currently to go over it all just yet, but no doubt this is exactly what I'm looking for. Thanks.   