wx-c.dll
.Allthough C# provides a very convenient way to call native implementations compiled into a DLL, the matching of the different object models and memory managements still provides several difficulties. Refer to the documentation of wx.Object on this issue.
Also please note some guide lines that the current code base unfortunately ignores sometimes.
enum AuiPaneButtonState { wxAUI_BUTTON_STATE_HOVER = 1 ... };
public enum AuiPaneButtonState { HOVER = 1, ... }
wx-c
shall always provide the same set of functions - without regard to defined or undefined symbols. If functions are only available for instance in __WXMSW__
, provide dummy implementations in wx-c
if __WXMSW__
is not defined and produce runtime errors (exceptions) in wx.NET.dll
if only dummy implementations are available. Use wx.ReflecConfig
for this purpose. wxnet_globals.h
into all C++ wrapper files. This header defines e.g. the WXNET_EXPORT()
macro. Use this macro as shown by the sample below to declare exported functions. Please note, that wx.NET uses __stdcall
calling convention on Windows (as suggested by Microsoft), whereas wxWidgets uses __cdecl
. wx
for all C# implementations in wx.NET.dll
. System.IntPtr
as argument for all inheritors of wx.Object. This is absolutely mandatory if using wx.Object.StorageMode.RegisteredObject
. wx.Object.memOwn
defines whether the C++ object will be destroyed on disposing the .NET wrapper. Define this explicitely. The current default is false
and I really don't know whether this is really necessary or not. This default changes on objects of storage mode wx.Object.StorageMode.VolatileObject
. These objects are considered as data exchange objects without registration in wxWidgets. This is usually true but on implementing virtual callbacks, objects of this kind might have been generated in local scope in the C++ side. In such cases, memory ownership has to be on the C++ side. The .NET implementation has to reflect this. Another opportunity for such cases is the implentation of a particular boxing class according to wx.DisposableStringBox. const
char*
to C-functions. Use pointers to wxString
instead. Use class wxString
on the C# side. Reason: C# natively supports UTF. wxWidgets
supports UTF if this support has been compiled in (I don't know any reason to use ANSI mode, but this is still supported). In both cases, wxString
will convert if necessary and omit any conversion if possible. So, do not use C strings. Refer also to the remarks on wxString. local_events.h
for some support on this issue. But: Initialize callbacks with NULL on constructing C++ objects and call them only if they differ from NULL. This is vital to safely avoid access violations on using instances with callbacks before registration of the callback functions. bool
and long
. The size of bool
is not defined (this datatype is missing in C) and the size of long
varies from 32 bit to 64 bit architecture. So, with long
you might have problems on wx-c
DLLs built for the 64-bit architectures. You may use int
for both (the standard marshalling of bool
treats it like an integer number). You may also define the marshalling explicitely using the MarshalAs
(UnmanagedType.U1) attribute and use char
instead of bool
. const
and mutual objects, the declarations of the DLL functions should reflect this. object.equals()
and objects.GetHashcode()
. When wrapping DTOs (like wx.wxString), use the wx.Object.StorageMode.VolatileObject
mode and copy all return values from wxWidgets into newly generated instances. region
endregion
directives for the following categories if applicable: wx-c.dll
. this
(e.g. this.wxObject
). This enhances readability and does for sake of IntelliSence not require extra effort. string
, System.DateTime
, System.Drawing.Rectangle
, System.Drawing.Point
, or System.Drawing.Size
to the wx-c.dll
directly. Use instances of the classes wx.wxString
, wx.wxDateTime
, wx.wxRect
, wx.wxPoint
, or wx.wxSize
instead. wx-c.dll
support the use of the _CRTDBG_MAP_ALLOC
debug facilites of the Microsoft Visual Studio (R) compiler. If you define symbol WXNET_LOG_MEM
when compiling wx-c.dll
, this DLL will log each allocation and deallocation of memory and wx.NET objects in a file containing comma separated lists named wx_c_mem.csv
. You may analyze this file using the MemLogDisplay.exe
application, a browser for these log files that is a wx.NET utility (also implemented using wx.NET). WXNET_LOG_MEM
can of course be defined using other compilers. However, the resulting log files will lack most information on memory deallocation. bool
as result of DLL functions or result or argument of callbacks. At least VC 8 seems to use a memory model for this data that does not comply with the PINVOKE standard serialization of the .NET type bool
. In callbacks use int
instead. You can avoid problems with bool
as the result of a native DLL function using char
instead with an explicit configuration of the marshalled data as UnmanagedType.U1
. Refer to wxStringMap_HasValueFor
in the example below.class wxStringMap { public: wxStringMap(); virtual ~wxStringMap(); bool HasValueFor(const wxString& key) const; const wxString& ValueFor(const wxString& key) const; };
wx-c.dll
. extern "C" __declspec(dllexport) wxStringMap* wxStringMap_CTor() { return new wxStringMap(); } extern "C" __declspec(dllexport) void wxStringMap_DTor(wxStringMap* self) { delete self; } extern "C" __declspec(dllexport) bool wxStringMap_HasValueFor(wxStringMap* self, const char* key) { return self->HasValueFor(key); } extern "C" __declspec(dllexport) const wxString* ValueFor(wxStringMap* self, const char* key) { return &(self->ValueFor(key)); }
wx.NET.dll
. namespace wx { class StringMap : Object { [DllImport("wx-c")] static extern IntPtr wxStringMap_CTor(); public StringMap() : base(wxStringMap_CTor()) {} // but where is the cosntructor for IntPtr [DllImport("wx-c")] static extern void wxStringMap_DTor(IntPtr self) protected virtual void Dispose(bool disposing) { bool still_there = RemoveObject(wxObject); if (!disposed) { lock (Object.DllSync) { if (wxObject != IntPtr.Zero && memOwn && still_there) { wxStringMap_DTor(wxObject); } } virtual_Dispose = null; wxObject = IntPtr.Zero; memOwn = false; --validInstancesCount; } disposed = true; } // this is a lot of copy/paste stuff that is not necessary since C++ // provides a virtual destructor. [DllImport("wx-c")] static extern bool wxStringMap_HasValueFor(IntPtr self, string key) public bool HasValueFor(string key) { return wxStringMap_HasValueFor(wxObject, key); } // no unicode support since standard marshalling for strings is: BSTR // bool will only work iff the C++ compiler uses full word width (4 bytes on 32 bit architectures) for bool. [DllImport("wx-c")] static extern IntPtr wxStringMap_ValueFor(IntPtr self, string key) public string ValueFor(string key) { return (wxString) Object.FindObject(wxStringMap_ValueFor(wxObject, key), typeof(wxString)); } // correct but useless cal to wx.Object.FindObject(). This method is appropriate to reuse // previously generated wrappers. But strings will be newly generated when requested. // no unicode support. } }
The wrapper will look something like the following when observing the guiding rules. Make up your mind on the effect!
#include "local_events.h" #include "wxnet_globals.h" class _StringMap : public wxStringMap { DECLARE_DISPOSABLE(_StringMap) public _StringMap() : wxStringMap(), m_onDispose(NULL) {} }; WXNET_EXPORT(wxStringMap*) wxStringMap_CTor() { return new _StringMap(); } // we do not need a method for destruction since the wrapped class supports virtual destruction. WXNET_EXPORT(void) wxStringMap_RegisterDispose(_StringMap* self, Virtual_Dispose onDispose) { if (self) self->RegisterDispose(onDispose); } WXNET_EXPORT(char) wxStringMap_HasValueFor(wxStringMap* self, const wxString* key) { if (self && key) return self->HasValueFor(*key); else return 0; } // this avoids access violations on NULL pointers and uses C standard type char instead of bool // unicode will be supported if wxWidgets has been compiled for unicode support WXNET_EXPORT(wxString*) ValueFor(wxStringMap* self, const wxString* key) { if (self && key) return new wxString(self->ValueFor(*key)); else return NULL; } // unicode support and return of fresh strings only.
namespace wx { class StringMap : Object { #region CTor [DllImport("wx-c")] static extern IntPtr wxStringMap_CTor(); [DllImport("wx-c")] static extern void wxStringMap_RegisterDispose(IntPtr self, Virtual_Dispose onDispose); static IntPtr LockedCTor() { // memory allocation at least on Windows is not thread safe. // so, we have to synchronize all C++-code allocating and deallocating // memory from lock (wx.Object.DllSync) { return wxStringMap_CTor(); } } public StringMap() : this(LockedCTor()) { // the wrapper shall be deactivated on deleting the C++ instance this.virtual_Dispose = new Virtual_Dispose(VirtualDispose); this.memOwn = true; wxStringMap_RegisterDispose(this.wxObject, virtual_Dispose); } public StringMap(IntPtr wxObject) : base(wxObject) {} #endregion #region Public Methods [DllImport("wx-c")] [return: MarshalAs(UnmanagedType.U1)] // redefines marshalling of result type bool static extern bool wxStringMap_HasValueFor(IntPtr self, IntPtr key) public bool HasValueFor(string key) { wxString wxKey=wxString.SafeNew(key); // support of unicode if compiled in return wxStringMap_HasValueFor(this.wxObject, Object.SafePtr(wxKey)); } [DllImport("wx-c")] static extern IntPtr wxStringMap_ValueFor(IntPtr self, IntPtr key) public string ValueFor(string key) { wxString wxKey=wxString.SafeNew(key); return new wxString(wxStringMap_ValueFor(this.wxObject, Object.SafePtr(wxKey))); } #endregion #region Public Properties, Indexers public string this[string key] { get { return this.ValueFor(key); } } #endregion } }