1 // PInvokeCE.cs  2006-12  http://www.xmlBlaster.org/

   2 // Simple layer to delegate C# calls to Windows CE xmlBlaster client C library

   3 // Using P/Invoke of the .NET 1.0 is not tested

   4 // Using P/Invoke of the .NET 2.0 is fully supported

   5 // Using P/Invoke of the .NET Compact Framework 2.0 (CF2) is fully supported

   6 // Using P/Invoke of the .NET Compact Framework 1.0 does NOT support callbacks

   7 //

   8 // You need on CE:

   9 //    xmlBlasterClientC-Arm4.dll

  10 //    pthreads270-Arm4.dll

  11 //    zlib123-Arm4.dll

  12 // You need on Windows:

  13 //    xmlBlasterClientC.dll

  14 //    pthreads270.dll

  15 //    zlib123.dll

  16 //

  17 // Currently only tested on normal Windows (.net 2) and Windows CE 4.2 and 5.1 (CF2) with ARM processor

  18 //

  19 // o All DLL C code is 'multibyte characters' of type UTF-8 written as 'char *'

  20 // o All C# code is 'wchar_t' UTF-16

  21 // o For 'string' we do all conversion from/to UTF-8 in C# and transfer byte[] to 'char *' in C

  22 // o char * are allocated in the C-DLL and freed in the C-DLL

  23 //

  24 // Features: All features of the client C library (compression, tunnel callbacks), see

  25 //           http://www.xmlblaster.org/xmlBlaster/doc/requirements/client.c.socket.html

  26 //

  27 // IMPORTANT: The dll are assumed in the current directory or in the %PATH%

  28 //           On Linux set the LD_LIBRARY_PATH to find the .so libraries.

  29 //           The ARM compatible dll's contain an '-Arm4' postfix like xmlBlasterClientC-Arm4.dll

  30 //

  31 // @todo     publishOneway crashes

  32 //           logging with log4net

  33 //           write a testsuite

  34 //           write a requirement

  35 //           create an assembly with ant or nant

  36 //           create the same wrapper for the xmlBlaster C++ library

  37 //

  38 // @author   xmlBlaster@marcelruff.info

  39 //

  40 // @prepare  Compile the C client library first (see xmlBlaster\src\c\xmlBlasterClientC.sln)

  41 //

  42 // @see      http://www.xmlblaster.org/xmlBlaster/doc/requirements/client.csharp.html

  43 // @see      http://msdn.microsoft.com/mobility/understanding/articles/default.aspx?pull=/library/en-us/dnnetcomp/html/netcfintrointerp.asp?frame=true

  44 // @see      http://msdn.microsoft.com/mobility/understanding/articles/default.aspx?pull=/library/en-us/dnnetcomp/html/netcfadvinterop.asp

  45 //

  46 // @see Callback function pointers: http://msdn2.microsoft.com/en-us/library/5zwkzwf4.aspx

  47 // @c        http://www.xmlBlaster/org

  48 
  49 //

  50 /*
  51 Usage:
  52 XmlBlaster C SOCKET client
  53 
  54    -dispatch/connection/plugin/socket/hostname [localhost]
  55                        Where to find xmlBlaster.
  56    -dispatch/connection/plugin/socket/port [7607]
  57                        The port where xmlBlaster listens.
  58    -dispatch/connection/plugin/socket/localHostname [NULL]
  59                        Force the local IP, useful on multi homed computers.
  60    -dispatch/connection/plugin/socket/localPort [0]
  61                        Force the local port, useful to tunnel firewalls.
  62    -dispatch/connection/plugin/socket/compress/type []
  63                        Switch on compression with 'zlib:stream'.
  64    -dispatch/connection/plugin/socket/useUdpForOneway [false]
  65                        Use UDP for publishOneway() calls.
  66    -dispatch/callback/plugin/socket/hostname [localhost]
  67                        The IP where to establish the callback server.
  68                        Can be useful on multi homed hosts.
  69    -dispatch/callback/plugin/socket/port [7611]
  70                        The port of the callback server.
  71    -plugin/socket/multiThreaded  [true]
  72                        If true the update() call to your client code is a separate thread.
  73    -plugin/socket/responseTimeout  [60000 (one minute)]
  74                        The time in millis to wait on a response, 0 is forever.
  75    -logLevel            ERROR | WARN | INFO | TRACE | DUMP [WARN]
  76 
  77 Example:
  78   TestPInvoke -logLevel TRACE -dispatch/connection/plugin/socket/hostname 192.168.2.9
  79  * 
  80 Preprocessor:
  81    XMLBLASTER_MONO
  82              Forces support in a Linux mono environment
  83    WINCE || Smartphone || PocketPC || WindowsCE || FORCE_PINVOKECE
  84              Any single of the above will force Windows CE compatibility
  85    CF1       To have Windows CE compact framework .net 1.x support,
  86              no callbacks are available in this case.
  87              Please choose to install CF2 on your PDA and leave this define away.
  88    DOTNET1   To force old .net 1.x, not tested, for Windows XP etc only
  89 */
  90 
  91 // Initial defines cleanup:

  92 // In our code we only use

  93 //   XMLBLASTER_MONO

  94 //   XMLBLASTER_WINCE

  95 //   XMLBLASTER_WIN32

  96 //   CF1

  97 //   DOTNET1

  98 //   FORCE_CDELC

  99 // NOTE: mono compiler is buggy and can't handle nested #if !

 100 #if XMLBLASTER_MONO

 101 #  warning INFO: We compile on a Linux mono box

 102 #endif

 103 
 104 #if (WINCE || Smartphone || PocketPC || WindowsCE || CF1)

 105    // VC2005 automatically set 'WindowsCE' for Mobile (Windows CE 5.0)

 106    // and typically one of the other defines for Smart Devices 2003

 107 #  define XMLBLASTER_WINCE

 108 #  warning INFO: We compile for Windows CE compact framework .net

 109 #endif

 110 
 111 #if !(WINCE || Smartphone || PocketPC || WindowsCE || CF1) && !XMLBLASTER_MONO  // Assume WIN32

 112 #  define XMLBLASTER_WIN32

 113 #  warning INFO: We compile for Windows .net target

 114 #endif

 115 
 116 #if CF1

 117 #  warning We compile for Windows CE compact framework .net 1.0, no xmlBlaster callback are available!

 118 #endif

 119 
 120 #if DOTNET1

 121 #  warning We compile for Windows .net 1.x, no xmlBlaster callback are implemented!

 122 #endif

 123 
 124 // Setting local defines

 125 #if (XMLBLASTER_WIN32 || XMLBLASTER_MONO) && !DOTNET1

 126 #  define FORCE_CDELC // only supported in .net 2 (cdecl is default on WINCE)

 127 // #  warning INFO: We use UnmanagedFunctionPointer

 128 // CF1 and CF2 and .net 1.x don't support UnmanagedFunctionPointer

 129 // CallingConvention=CallingConvention.Cdecl supported in .net CF1 and CF2

 130 // UnmanagedFunctionPointer: new in .net 2.0

 131 #endif

 132 
 133 using System;
 134 using System.Text;
 135 using System.Runtime.InteropServices;
 136 using System.Collections;
 137 
 138 
 139 namespace org.xmlBlaster.client
 140 {
 141 
 142 
 143    /// Calling unmanagegd code: xmlBlasterClientC.dll

 144    public class PInvokeCE : I_XmlBlasterAccess
 145    {
 146       static XmlBlasterLogLevel localLogLevel = XmlBlasterLogLevel.INFO; // TODO: log4net

 147       private bool initializeIsCalled = false;
 148 
 149 #     if XMLBLASTER_MONO

 150          // Linux Debug libxmlBlasterClientCD.so, set LD_LIBRARY_PATH to find the shared library

 151          const string XMLBLASTER_C_LIBRARY = "xmlBlasterClientCD";
 152 #     elif XMLBLASTER_WINCE // xmlBlasterClientC-$(ARCHFAM).dll

 153       const string XMLBLASTER_C_LIBRARY = "xmlBlasterClientC-ARM.dll";
 154 #     else // XMLBLASTER_WIN32

 155          const string XMLBLASTER_C_LIBRARY = "xmlBlasterClientC.dll";
 156          //const string XMLBLASTER_C_LIBRARY = "..\\..\\lib\\xmlBlasterClientC.dll";

 157 #     endif

 158 
 159       // Helper struct for DLL calls (struct does return empty IntPtr from DLL, why?

 160       //[StructLayout(LayoutKind.Sequential/*, CharSet = CharSet.Unicode*/)]

 161       public struct MsgUnitUnmanagedCEpublish // publish() only works with a type 'struct'?!

 162       {
 163          public MsgUnitUnmanagedCEpublish(bool dummy/*if a stuct*/)
 164          {
 165             key = IntPtr.Zero;
 166             contentLen = 0;
 167             content = IntPtr.Zero;
 168             qos = IntPtr.Zero;
 169             responseQos = IntPtr.Zero;
 170          }
 171          public MsgUnitUnmanagedCEpublish(string key, byte[] content, string qos)
 172          {
 173             this.key = stringToUtf8IntPtr(key);
 174             this.contentLen = (content == null) ? 0 : content.Length;
 175             this.content = byteArrayToIntPtr(content);
 176             this.qos = stringToUtf8IntPtr(qos);
 177             responseQos = IntPtr.Zero;
 178          }
 179          public IntPtr key;
 180          public int contentLen;
 181          public IntPtr content;
 182          public IntPtr qos;
 183          public IntPtr responseQos;
 184          /* Has to be called exactly once if freeUnmanaged==true! */
 185          public byte[] getContent(bool freeUnmanaged)
 186          {
 187             if (content == IntPtr.Zero) return new byte[0];
 188             return byteArrayFromIntPtr(content, contentLen, freeUnmanaged);
 189          }
 190          /* Has to be called exactly once if freeUnmanaged==true! */
 191          public string getKey(bool freeUnmanaged)
 192          {  // If called never: memory leak, if called twice: double free

 193             if (key == IntPtr.Zero)
 194                return "";
 195             return stringFromUtf8IntPtr(key, freeUnmanaged);
 196          }
 197          /* Has to be called exactly once if freeUnmanaged==true! */
 198          public string getQos(bool freeUnmanaged)
 199          {
 200             if (qos == IntPtr.Zero) return "";
 201             return stringFromUtf8IntPtr(qos, freeUnmanaged);
 202          }
 203          /* Has to be called exactly once! */
 204          public string getResponseQos()
 205          {
 206             if (responseQos == IntPtr.Zero) return "";
 207             return stringFromUtf8IntPtr(key);
 208          }
 209       }
 210 
 211       [StructLayout(LayoutKind.Sequential/*, CharSet = CharSet.Unicode*/)]
 212       public class MsgUnitUnmanagedCEget // the get() only works with type 'class'! ?

 213       {
 214          public MsgUnitUnmanagedCEget()
 215          {
 216             key = IntPtr.Zero;
 217             contentLen = 0;
 218             content = IntPtr.Zero;
 219             qos = IntPtr.Zero;
 220             responseQos = IntPtr.Zero;
 221          }
 222          public MsgUnitUnmanagedCEget(string key, byte[] content, string qos)
 223          {
 224             this.key = stringToUtf8IntPtr(key);
 225             this.contentLen = (content == null) ? 0 : content.Length;
 226             this.content = byteArrayToIntPtr(content);
 227             this.qos = stringToUtf8IntPtr(qos);
 228             responseQos = IntPtr.Zero;
 229          }
 230          public IntPtr key;
 231          public int contentLen;
 232          public IntPtr content;
 233          public IntPtr qos;
 234          public IntPtr responseQos;
 235          /* Has to be called exactly once! */
 236          public byte[] getContent()
 237          {
 238             if (content == IntPtr.Zero) return new byte[0];
 239             return byteArrayFromIntPtr(content, contentLen, true);
 240          }
 241          /* Has to be called exactly once! */
 242          public string getKey()
 243          {  // If called never: memory leak, if called twice: double free

 244             if (key == IntPtr.Zero)
 245                return "";
 246             return stringFromUtf8IntPtr(key, true);
 247          }
 248          /* Has to be called exactly once! */
 249          public string getQos()
 250          {
 251             if (qos == IntPtr.Zero) return "";
 252             return stringFromUtf8IntPtr(qos, true);
 253          }
 254          /* Has to be called exactly once! */
 255          public string getResponseQos()
 256          {
 257             if (responseQos == IntPtr.Zero) return "";
 258             return stringFromUtf8IntPtr(responseQos, true);
 259          }
 260       }
 261 
 262       // Helper struct for DLL calls

 263       public struct XmlBlasterUnmanagedCEException
 264       {
 265          public XmlBlasterUnmanagedCEException(bool isRemote)
 266          {
 267             remote = (isRemote) ? 1 : 0;
 268             errorCode = IntPtr.Zero;
 269             message = IntPtr.Zero;
 270          }
 271          public int remote;
 272          public IntPtr errorCode;
 273          public IntPtr message;
 274          public bool CaughtException()
 275          {
 276             return errorCode != IntPtr.Zero;
 277          }
 278          public string GetErrorCode()
 279          {
 280             return stringFromUtf8IntPtr(errorCode);
 281          }
 282          public string GetMessage()
 283          {
 284             return stringFromUtf8IntPtr(message);
 285          }
 286       }
 287 
 288       //public struct QosArr

 289       //{

 290       //   public int len;  /* Number of XML QoS strings */

 291       //   public string[] qosArr;

 292       //}

 293 
 294       //public struct MsgUnitUnmanagedCEArr

 295       //{

 296       //   public string secretSessionId;

 297       //   public int len;

 298       //   public MsgUnit[] msgUnitArr;

 299       //}

 300 
 301 
 302       [StructLayout(LayoutKind.Sequential/*, CharSet = CharSet.Unicode*/)]
 303       public class StringArr
 304       {
 305          public IntPtr str;
 306       }
 307 
 308       public void log(String str)
 309       {
 310          logger(XmlBlasterLogLevel.TRACE, "", str);
 311       }
 312 
 313       public void logger(XmlBlasterLogLevel level, String location, String message)
 314       {
 315          //logLevel.ToString("d")

 316          if ((int)level <= (int)localLogLevel) { // TODO: log4net

 317             if (null != onLogging)
 318             {
 319                try
 320                {
 321                   onLogging((XmlBlasterLogLevel)level, location, message); // Dispatch to C# clients

 322                }
 323                catch (Exception e)
 324                {
 325                   System.Diagnostics.Debug.WriteLine(e.ToString());
 326                   Console.WriteLine(e.ToString());
 327                }
 328             }
 329             else {
 330                string prefix = (location != null && location.Length > 0) ? location : "[PInvoke]";
 331                     prefix += " " + level + ": ";
 332                System.Diagnostics.Debug.WriteLine(prefix + message);
 333                Console.WriteLine(prefix + message);
 334             }
 335          }
 336       }
 337 
 338 #     if FORCE_CDELC

 339       [System.Runtime.InteropServices.UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]
 340 #     endif

 341       public delegate void LoggerUnmanagedFp(int level, IntPtr location, IntPtr message);
 342 
 343       /// Callback by xmlBlaster C dll, see LoggerUnmanagedFp

 344       /// message pointer is NOT freed here, it is freed by the calling C DLL after this call

 345       void loggerUnmanaged(int level, IntPtr location, IntPtr message)
 346       {
 347          string loc = stringFromUtf8IntPtr(location, false);
 348          string msg = stringFromUtf8IntPtr(message, false);
 349          XmlBlasterLogLevel logLevel = (XmlBlasterLogLevel)level;
 350          logger(logLevel, loc, msg);
 351       }
 352 
 353 #     if FORCE_CDELC

 354       [System.Runtime.InteropServices.UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]
 355 #     endif

 356       public delegate void ProgressUnmanagedFp(int currBytesRead, int nbytes);
 357       void callbackProgressUnmanaged(int currBytesRead, int nbytes)
 358       {
 359          //Console.WriteLine("#####################" + currBytesRead + "/" + nbytes + " bytes read from socket");

 360          logger(XmlBlasterLogLevel.INFO, "", "" + currBytesRead + "/" + nbytes + " bytes read from socket");
 361       }
 362 
 363 #     if FORCE_CDELC

 364       [System.Runtime.InteropServices.UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]
 365 #     endif

 366       public delegate int FPtr(int value);
 367 
 368 #     if FORCE_CDELC

 369       [System.Runtime.InteropServices.UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]
 370 #     endif

 371       public delegate void UpdateUnmanagedFp(IntPtr cbSessionId, ref MsgUnitUnmanagedCEpublish msgUnit, int isOneway, ref XmlBlasterUnmanagedCEException exception);
 372 
 373       /// <summary>

 374       /// Helper class to set isOneway

 375       /// </summary>

 376       public class MsgUnitInternal : MsgUnitUpdate {
 377          public MsgUnitInternal(string key, byte[] content, string qos, bool isOneway_)
 378          : base(key, content, qos)
 379          {
 380             oneway = isOneway_;
 381          }
 382       }
 383 
 384       /// Callback by xmlBlaster C dll, see UpdateUnmanagedFp and XmlBlasterUnmanagedCE.h

 385       /// The cbSessionId_ and msgUnitUnmanaged is freed by the caller

 386       /// The return should be a string, but this is difficult over P/Invoke, we would

 387       /// need to solve it by an out parameter. But xmlBlaster ignores it currently so it is an open TODO

 388       /// Info about callback function pointer: http://msdn2.microsoft.com/en-us/library/5zwkzwf4.aspx

 389       /// Nice example how to pass a pointer to a managed object to DLL

 390       /// and on callback cast it again to the managed object:

 391       ///  http://msdn2.microsoft.com/en-us/library/44ey4b32.aspx

 392       /// See http://msdn2.microsoft.com/en-us/library/ss9sb93t.aspx

 393       /// Samples: http://msdn2.microsoft.com/en-us/library/fzhhdwae.aspx

 394       void updateUnmanaged(IntPtr cbSessionId_, ref MsgUnitUnmanagedCEpublish msgUnitUnmanaged, int isOneway_, ref XmlBlasterUnmanagedCEException exception)
 395       {
 396          bool isOneway = isOneway_ == 1;
 397          logger(XmlBlasterLogLevel.TRACE, "", "Entering updateUnmanaged() isOneway=" + isOneway);
 398          string cbSessionId = stringFromUtf8IntPtr(cbSessionId_, false);
 399          //fillUnmanagedException(ref exception, "user.internal", "a test exception from C#");

 400 
 401          MsgUnitUpdate msgUnit = new MsgUnitInternal(msgUnitUnmanaged.getKey(false),
 402             msgUnitUnmanaged.getContent(false), msgUnitUnmanaged.getQos(false),
 403             isOneway);
 404          if (null != onUpdate)
 405          {
 406             try
 407             {
 408                string ret = onUpdate(cbSessionId, msgUnit);
 409                logger(XmlBlasterLogLevel.TRACE, "", "Ignoring " + ret);
 410             }
 411             catch (XmlBlasterException e)
 412             {
 413                logger(XmlBlasterLogLevel.WARN, "", "onUpdate() exception: " + e.ToString());
 414                fillUnmanagedException(ref exception, e.ErrorCode, e.Message);
 415             }
 416             catch (Exception e)
 417             {
 418                logger(XmlBlasterLogLevel.ERROR, "", "onUpdate() exception: " + e.ToString());
 419                fillUnmanagedException(ref exception, "user.update.internalError", e.ToString());
 420             }
 421             return;
 422          }
 423          logger(XmlBlasterLogLevel.INFO, "", "updateUnmanaged got message " + msgUnit.GetKeyStr());
 424          logger(XmlBlasterLogLevel.TRACE, "", "updateUnmanaged invoked START ==================");
 425          logger(XmlBlasterLogLevel.TRACE, "", msgUnit.GetKeyStr());
 426          logger(XmlBlasterLogLevel.TRACE, "", msgUnit.GetContentStr());
 427          logger(XmlBlasterLogLevel.TRACE, "", msgUnit.GetQosStr());
 428          logger(XmlBlasterLogLevel.TRACE, "", "updateUnmanaged invoked DONE ===================");
 429          //string ret = "<qos><state id='OK'/></qos>";

 430          //return ret;

 431       }
 432 
 433       /**
 434        * Allocates native C memory. 
 435        * You need to call
 436        *   xmlBlasterUnmanagedCEExceptionFree(XmlBlasterUnmanagedCEException *exception)
 437        * later!
 438        */
 439       void fillUnmanagedException(ref XmlBlasterUnmanagedCEException outE, string errorCode, string message)
 440       {
 441          outE.errorCode = stringToUtf8IntPtr(errorCode);
 442          outE.message = stringToUtf8IntPtr(message);
 443          outE.remote = 1;
 444       }
 445 
 446       /**
 447        * Copy the unmanaged data to our XmlBlasterException and
 448        * frees the native memory.
 449        */
 450       XmlBlasterException fillXmlBlasterException(ref XmlBlasterUnmanagedCEException exception)
 451       {
 452          XmlBlasterException managed = new XmlBlasterException(exception.remote != 0,
 453               stringFromUtf8IntPtr(exception.errorCode, false),
 454               stringFromUtf8IntPtr(exception.message, false));
 455          xmlBlasterUnmanagedCEExceptionFree(ref exception);
 456          return managed;
 457       }
 458 
 459       /**
 460        * Convert a string to a UTF-8 encoded byte array.
 461        * @param str UTF-16 C# string
 462        * @return UTF-8 multibyte array to be passed to C dll (zero terminated)
 463        */
 464       public static byte[] StringToUtf8ByteArray(string str)
 465       {
 466          if (str == null) return new byte[0];
 467          byte[] data = System.Text.Encoding.UTF8.GetBytes(str);
 468          return data;
 469       }
 470 
 471       /**
 472        * Convert a UTF-8 byte array to a UTF-16 string
 473        * @param dBytes UTF-8 multibyte string from C (zero terminated)
 474        * @return string to be used in C#
 475        */
 476       public static string Utf8ByteArrayToString(byte[] dBytes)
 477       {
 478          string str;
 479          str = System.Text.Encoding.UTF8.GetString(dBytes, 0, dBytes.Length);
 480          return str;
 481       }
 482 
 483       /**
 484        * Allocates native memory
 485        * @param str The UTF-16 unicode string to transfer
 486        * @return Contains zero terminated UTF-8,
 487        * the pointer memory needs to be freed with xmlBlasterUnmanagedCEFree(IntPtr).
 488        * All xmlBlasterUnmanagedCEXXX() calls free the 'char *' or 'char **'
 489        * passed in the dll, so we don't have to do it here in C#
 490        */
 491       static IntPtr stringToUtf8IntPtr(string str)
 492       {
 493          byte[] bytes = StringToUtf8ByteArray(str);
 494          return byteArrayToIntPtr(bytes);
 495       }
 496 
 497       /**
 498        * Allocates native memory
 499        * @param e The byte array to transfer
 500        * @return C allocated char * of length e.Length() (+ 1 as we add a zero in case of strings)
 501        * the pointer memory needs to be freed with xmlBlasterUnmanagedCEFree(IntPtr).
 502        * All xmlBlasterUnmanagedCEXXX() calls free the 'char *' or 'char **'
 503        * passed in the dll, so we don't have to do it here in C#
 504        */
 505       unsafe static IntPtr byteArrayToIntPtr(byte[] e)
 506       {
 507          // See http://msdn2.microsoft.com/en-us/library/aa497275.aspx#Q4rlim632

 508          // "How do I convert a byte[] to an IntPtr?"

 509          IntPtr ptr = xmlBlasterUnmanagedCEMalloc(e.Length + 1); // is fixed(...) already

 510          byte* bp = (byte*)ptr.ToPointer();
 511          for (int i = 0; i < e.Length; i++)
 512          {
 513             bp[i] = e[i];
 514          }
 515          bp[e.Length] = 0;
 516          return ptr;
 517       }
 518 
 519       /**
 520        * Handling a DLL C function which returns a malloced char *
 521        * 
 522        * As the following throws NotSupportedException on Windows CE
 523        *   byte[] tmp = stringReturner();
 524        * we have a workaround and return an IntPtr.
 525        * The string which was allocated in the DLL C code is
 526        * extracted here and then freed by a call to
 527        *   xmlBlasterFree(IntPtr)
 528        * @param ptr A C malloced 'char *' containing UTF-8 text
 529        * @return    A unicode 'wchar_t *' UTF-16 string
 530        */
 531       static string stringFromUtf8IntPtr(IntPtr ptr)
 532       {
 533          return stringFromUtf8IntPtr(ptr, true);
 534       }
 535 
 536       static string stringFromUtf8IntPtr(IntPtr ptr, bool freeUnmanaged)
 537       {
 538          if (ptr == IntPtr.Zero) return "";
 539          byte[] bytes = byteArrayFromIntPtr(ptr, -1, freeUnmanaged);
 540          return Utf8ByteArrayToString(bytes);
 541       }
 542 
 543       /**
 544        * @param contentLen 
 545        * @param freeUnmanaged If true we call the native C DLL and free() the memory pointed to by ptr
 546        */
 547 
 548       /// <summary>

 549       /// Convenience method for zero terminated string in IntPtr which shall be read and free()d

 550       /// </summary>

 551       /// <param name="contentLen">If -1 we parse until we reach the first 0, else we parse the given length</param>

 552       /// <param name="freeUnmanaged">If true we call the native C DLL and free() the memory pointed to by ptr</param>

 553       /// <returns></returns>

 554       unsafe static byte[] byteArrayFromIntPtr(IntPtr ptr, int contentLen, bool freeUnmanaged)
 555       {
 556          if (ptr == IntPtr.Zero) return new byte[0];
 557          //Can't cast type 'System.IntPtr' to 'byte[]'

 558          //  byte[] tmp = (byte[])stringReturner();

 559          //so we need to copy it manually:

 560          void* vPtr = ptr.ToPointer(); // is fixed(...) already

 561          byte* tmpP = (byte*)vPtr;
 562          int len = contentLen;
 563          if (contentLen < 0)
 564             for (len = 0; tmpP[len] != 0; len++) ;
 565          byte[] tmp = new byte[len];
 566          for (int i = 0; i < tmp.Length; i++)
 567             tmp[i] = tmpP[i];
 568          // Now free() the malloc() IntPtr in the C DLL ...

 569          if (freeUnmanaged) xmlBlasterUnmanagedCEFree(ptr);
 570          return tmp;
 571       }
 572 
 573       [DllImport(XMLBLASTER_C_LIBRARY)]
 574       private extern static IntPtr xmlBlasterUnmanagedCEMalloc(int size);
 575 
 576       [DllImport(XMLBLASTER_C_LIBRARY)]
 577       private extern static void xmlBlasterUnmanagedCEFree(IntPtr p);
 578 
 579       [DllImport(XMLBLASTER_C_LIBRARY)]
 580       private extern static IntPtr getXmlBlasterEmei();
 581 
 582       [DllImport(XMLBLASTER_C_LIBRARY)]
 583       private extern static void xmlBlasterUnmanagedCERegisterLogger(IntPtr xa, IntPtr loggerUnmanaged);
 584 
 585       [DllImport(XMLBLASTER_C_LIBRARY)]
 586       private extern static void xmlBlasterUnmanagedCERegisterProgressListener(IntPtr xa, IntPtr progressUnmanaged);
 587 
 588       [DllImport(XMLBLASTER_C_LIBRARY)]
 589       private extern static void xmlBlasterUnmanagedCEFreePP(out IntPtr p);
 590 
 591       [DllImport(XMLBLASTER_C_LIBRARY)]
 592       private extern static void xmlBlasterUnmanagedCEExceptionFree(ref XmlBlasterUnmanagedCEException exception);
 593 
 594       [DllImport(XMLBLASTER_C_LIBRARY)]
 595       private extern static IntPtr getXmlBlasterAccessUnparsedUnmanagedCE(int argc, IntPtr[] argv);
 596 
 597       [DllImport(XMLBLASTER_C_LIBRARY)]
 598       private extern static void freeXmlBlasterAccessUnparsedUnmanagedCE(IntPtr xa);
 599 
 600       [DllImport(XMLBLASTER_C_LIBRARY)]
 601       private extern static IntPtr xmlBlasterUnmanagedCEConnect(IntPtr xa, IntPtr qos, IntPtr updateUnmanaged, ref XmlBlasterUnmanagedCEException exception);
 602 
 603       [DllImport(XMLBLASTER_C_LIBRARY)]
 604       private extern static bool xmlBlasterUnmanagedCEInitialize(IntPtr xa, IntPtr updateUnmanaged, ref XmlBlasterUnmanagedCEException exception);
 605 
 606       [DllImport(XMLBLASTER_C_LIBRARY)]
 607       private extern static bool xmlBlasterUnmanagedCEDisconnect(IntPtr xa, IntPtr qos, ref XmlBlasterUnmanagedCEException exception);
 608 
 609       [DllImport(XMLBLASTER_C_LIBRARY)]
 610       private extern static IntPtr xmlBlasterUnmanagedCEPublish(IntPtr xa, out MsgUnitUnmanagedCEpublish msgUnit, ref XmlBlasterUnmanagedCEException exception);
 611 
 612       //[DllImport(XMLBLASTER_C_LIBRARY )]

 613       //private extern static QosArr xmlBlasterUnmanagedCEPublishArr(IntPtr xa, MsgUnitUnmanagedCEArr msgUnitArr, ref XmlBlasterUnmanagedCEException exception);

 614 
 615       [DllImport(XMLBLASTER_C_LIBRARY)]
 616       private extern static void xmlBlasterUnmanagedCEPublishOneway(IntPtr xa, IntPtr msgUnitArr, int length, ref XmlBlasterUnmanagedCEException exception);
 617 
 618       [DllImport(XMLBLASTER_C_LIBRARY)]
 619       private extern static IntPtr xmlBlasterUnmanagedCESubscribe(IntPtr xa, IntPtr key, IntPtr qos, ref XmlBlasterUnmanagedCEException exception);
 620 
 621       [DllImport(XMLBLASTER_C_LIBRARY)]
 622       private extern static void xmlBlasterUnmanagedCEUnSubscribe(IntPtr xa, IntPtr key, IntPtr qos,
 623          ref XmlBlasterUnmanagedCEException exception, out int size, out IntPtr ptr);
 624 
 625       [DllImport(XMLBLASTER_C_LIBRARY)]
 626       private extern static void xmlBlasterUnmanagedCEErase(IntPtr xa, IntPtr key, IntPtr qos,
 627          ref XmlBlasterUnmanagedCEException exception, out int size, out IntPtr ptr);
 628 
 629       [DllImport(XMLBLASTER_C_LIBRARY)]
 630       private extern static void xmlBlasterUnmanagedCEGet(IntPtr xa, IntPtr key, IntPtr qos,
 631          ref XmlBlasterUnmanagedCEException exception, out int size, out IntPtr ptr);
 632 
 633       [DllImport(XMLBLASTER_C_LIBRARY)]
 634       private extern static IntPtr xmlBlasterUnmanagedCEPing(IntPtr xa, IntPtr qos, ref XmlBlasterUnmanagedCEException exception);
 635 
 636       [DllImport(XMLBLASTER_C_LIBRARY)]
 637       private extern static bool xmlBlasterUnmanagedCEIsConnected(IntPtr xa);
 638 
 639       [DllImport(XMLBLASTER_C_LIBRARY)]
 640       private extern static IntPtr xmlBlasterUnmanagedCEUsage();
 641 
 642       [DllImport(XMLBLASTER_C_LIBRARY)]
 643       private extern static IntPtr xmlBlasterUnmanagedCEVersion();
 644 
 645       private IntPtr xa;
 646       private UpdateUnmanagedFp updateUnmanagedFp;
 647       private LoggerUnmanagedFp loggerUnmanagedFp;
 648       private ProgressUnmanagedFp progressUnmanagedFp;
 649       private IntPtr updateFpForDelegate;
 650       private IntPtr loggerFpForDelegate;
 651       private IntPtr progressFpForDelegate;
 652       /** Is not null if the client wishes to be notified about connection state changes in fail safe operation */
 653       private I_ConnectionStateListener connectionListener;
 654 
 655       public PInvokeCE() {
 656       }
 657 
 658       /// <summary>

 659       /// Convenience method, calls initialize()

 660       /// </summary>

 661       /// <param name="argv">argv [0] contains the first argument, etc.

 662       ///  "-dispatch/connection/plugin/socket/hostname" "192.168.1.2"</param>

 663       public PInvokeCE(string[] argv)
 664       {
 665          Initialize(toHashtable(argv));
 666       }
 667 
 668       public static Hashtable toHashtable(string[] argv)
 669       {
 670          Hashtable hash = new Hashtable();
 671          //hash.Add("__exe__", "PInvokeCE");

 672          for (int i = 0; i < argv.Length; ++i)
 673          {
 674             string key = (argv[i].StartsWith("-"))?argv[i].Substring(1):argv[i];
 675             string value = "";
 676             if (i < (argv.Length -1)) {
 677                i++;
 678                value = argv[i];
 679             }
 680             hash.Add(key, value);
 681          }
 682          return hash;
 683       }
 684 
 685       /// <summary>

 686       /// Convert command line arguments: C client lib expects the executable name as first entry

 687       /// </summary>

 688       /// <param name="hash"></param>

 689       /// <returns></returns>

 690       private IntPtr[] create_C_args(Hashtable hash) {
 691          int c_argc = 2 * hash.Count + 1;
 692 
 693          IntPtr[] c_argv = new IntPtr[c_argc];
 694          c_argv[0] = stringToUtf8IntPtr("PInvokeCE"); // TODO: my executable name

 695 
 696          ArrayList aKeys = new ArrayList(hash.Keys);
 697          int i = 1;
 698          foreach (string key_ in aKeys) {
 699             string value = (string)hash[key_];
 700             if (value == null) value = "";
 701             string key = (key_.StartsWith(