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 #        if DEBUG

 156             const string XMLBLASTER_C_LIBRARY = "xmlBlasterClientCD.dll";
 157 #        else

 158             const string XMLBLASTER_C_LIBRARY = "xmlBlasterClientC.dll";
 159             //const string XMLBLASTER_C_LIBRARY = "..\\..\\lib\\xmlBlasterClientC.dll";

 160 #        endif

 161 #     endif

 162 
 163       // Helper struct for DLL calls (struct does return empty IntPtr from DLL, why?

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

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

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

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

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

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

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

 293       //{

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

 295       //   public string[] qosArr;

 296       //}

 297 
 298       //public struct MsgUnitUnmanagedCEArr

 299       //{

 300       //   public string secretSessionId;

 301       //   public int len;

 302       //   public MsgUnit[] msgUnitArr;

 303       //}

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

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

 321             if (null != onLogging)
 322             {
 323                try
 324                {
 325                   onLogging((XmlBlasterLogLevel)level, location, message); // Dispatch to C# clients

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

 343       [System.Runtime.InteropServices.UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]
 344 #     endif

 345       public delegate void LoggerUnmanagedFp(int level, IntPtr location, IntPtr message);
 346 
 347       /// Callback by xmlBlaster C dll, see LoggerUnmanagedFp

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

 349       void loggerUnmanaged(int level, IntPtr location, IntPtr message)
 350       {
 351          string loc = stringFromUtf8IntPtr(location, false);
 352          string msg = stringFromUtf8IntPtr(message, false);
 353          XmlBlasterLogLevel logLevel = (XmlBlasterLogLevel)level;
 354          logger(logLevel, loc, msg);
 355       }
 356 
 357 #     if FORCE_CDELC

 358       [System.Runtime.InteropServices.UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]
 359 #     endif

 360       public delegate void ProgressUnmanagedFp(int currBytesRead, int nbytes);
 361       void callbackProgressUnmanaged(int currBytesRead, int nbytes)
 362       {
 363          //Console.WriteLine("#####################" + currBytesRead + "/" + nbytes + " bytes read from socket");

 364          logger(XmlBlasterLogLevel.INFO, "", "" + currBytesRead + "/" + nbytes + " bytes read from socket");
 365       }
 366 
 367 #     if FORCE_CDELC

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

 370       public delegate int FPtr(int value);
 371 
 372 #     if FORCE_CDELC

 373       [System.Runtime.InteropServices.UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]
 374 #     endif

 375       public delegate void UpdateUnmanagedFp(IntPtr cbSessionId, ref MsgUnitUnmanagedCEpublish msgUnit, int isOneway, ref XmlBlasterUnmanagedCEException exception);
 376 
 377       /// <summary>

 378       /// Helper class to set isOneway

 379       /// </summary>

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

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

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

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

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

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

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

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

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

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

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

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

 434          //return ret;

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

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

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

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

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

 554       /// </summary>

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

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

 557       /// <returns></returns>

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

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

 563          //so we need to copy it manually:

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

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

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

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

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

 663       /// Convenience method, calls initialize()

 664       /// </summary>

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

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

 667       public PInvokeCE(string[] argv)
 668       {
 669          Initialize(toHashtable(argv));
 670       }
 671 
 672       public static Hashtable toHashtable(string[] argv)
 673       {
 674          Hashtable hash = new Hashtable();
 675          //hash.Add("__exe__", "PInvokeCE");

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

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

 691       /// </summary>

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

 693       /// <returns></returns>

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

 699 
 700          ArrayList aKeys = new ArrayList(hash.Keys);
 701          int i = 1;
 702          foreach (string key_ in aKeys) {
 703             string value = (string)hash[key_];
 704             if (value == null) value = "";
 705             string key = (key_.StartsWith("-")) ? key_ : ("-" + key_);
 706             c_argv[i] = stringToUtf8IntPtr(key);
 707             i++;
 708             c_argv[i] = stringToUtf8IntPtr(value);
 709             i++;
 710          }
 711          return c_argv;
 712       }
 713 
 714       public void setLogLevel(string level) {
 715 #        if XMLBLASTER_WINCE   // Enum.GetValues is not supported

 716             if ("INFO".Equals(level.ToUpper()))
 717                localLogLevel = XmlBlasterLogLevel.INFO;
 718             else if ("WARN".Equals(level.ToUpper()))
 719                localLogLevel = XmlBlasterLogLevel.WARN;
 720             else if ("ERROR".Equals(level.ToUpper()))
 721                localLogLevel = XmlBlasterLogLevel.ERROR;
 722             else if ("TRACE".Equals(level.ToUpper()))
 723                localLogLevel = XmlBlasterLogLevel.TRACE;
 724             else if ("DUMP".Equals(level.ToUpper()))
 725                localLogLevel = XmlBlasterLogLevel.DUMP;
 726 #        else

 727             Array logArray = Enum.GetValues(typeof(XmlBlasterLogLevel));
 728             foreach (XmlBlasterLogLevel logLevel in logArray)
 729                if (logLevel.ToString().Equals(level)) {
 730                   localLogLevel = logLevel;
 731                   break;
 732                }
 733          #endif

 734       }
 735 
 736       public void Initialize(string[] argv) {
 737          Initialize(toHashtable(argv));
 738       }
 739 
 740       public void Initialize(Hashtable properties) {
 741          if (properties == null) properties = new Hashtable();
 742 
 743          updateUnmanagedFp = new UpdateUnmanagedFp(this.updateUnmanaged);
 744          updateFpForDelegate = Marshal.GetFunctionPointerForDelegate(updateUnmanagedFp);
 745 
 746          if (properties.Contains("-help") || properties.Contains("help")
 747                || properties.Contains("/?"))
 748          {
 749                string usage = "Usage:\nxmlBlaster C client v" + GetVersion()
 750                   + " on " + System.Environment.OSVersion.ToString() +
 751                #if XMLBLASTER_WINCE

 752                   " compact framework .net " + System.Environment.Version.ToString();
 753                #else

 754                   " .net " + System.Environment.Version.ToString();
 755                #endif

 756                usage += "\n" + GetUsage();
 757                logger(XmlBlasterLogLevel.TRACE, "", usage);
 758                throw new XmlBlasterException("user.usage", usage);//"Good bye");

 759          }
 760 
 761          if (properties.Contains("logLevel")) {
 762             setLogLevel((string)properties["logLevel"]);
 763          }
 764 
 765          IntPtr[] c_argv = create_C_args(properties);
 766          // Frees not c_argv (as it crashed)

 767          xa = getXmlBlasterAccessUnparsedUnmanagedCE(c_argv.Length, c_argv);
 768          for (int i = 0; i < c_argv.Length; ++i)
 769             xmlBlasterUnmanagedCEFree(c_argv[i]);
 770 
 771          loggerUnmanagedFp = new LoggerUnmanagedFp(this.loggerUnmanaged);
 772          loggerFpForDelegate = Marshal.GetFunctionPointerForDelegate(loggerUnmanagedFp);
 773          xmlBlasterUnmanagedCERegisterLogger(xa, loggerFpForDelegate);
 774 
 775          this.initializeIsCalled = true;
 776 
 777          // At this stage (constructor) no logListener can be here, so this

 778          // output will end up in the console

 779          logInfos(XmlBlasterLogLevel.TRACE);
 780       }
 781 
 782       /**
 783        * @see org.xmlBlaster.client.I_XmlBlasterAccess#registerConnectionListener(I_ConnectionStateListener)
 784        */
 785       public void RegisterConnectionListener(I_ConnectionStateListener connectionListener) {
 786          logger(XmlBlasterLogLevel.TRACE, "", "RegisterConnectionListener() is not implemented, TODO!!!");
 787          this.connectionListener = connectionListener;
 788       }
 789 
 790       private bool logInfoDone = false;
 791 
 792       public void logInfos(XmlBlasterLogLevel level) {
 793          if (logInfoDone) return;
 794          logInfoDone = true;
 795          // At this stage (constructor) no logListener can be here, so this

 796          // output will end up in the console

 797          logger(level, "", "xmlBlaster C client v" + GetVersion() 
 798             + " on " + System.Environment.OSVersion.ToString() +
 799 #        if XMLBLASTER_WINCE

 800             " compact framework .net " + System.Environment.Version.ToString());
 801             //" deviceId=" + getDeviceUniqueId());

 802 #        else

 803             " .net " + System.Environment.Version.ToString());
 804 #        endif

 805       }
 806 
 807 
 808       ~PInvokeCE()
 809       {
 810          if (xa != IntPtr.Zero)
 811             freeXmlBlasterAccessUnparsedUnmanagedCE(xa);
 812          logger(XmlBlasterLogLevel.TRACE, "", "~PInvokeCE() ...");
 813       }
 814 
 815       void check(string methodName)
 816       {
 817          logger(XmlBlasterLogLevel.TRACE, "", methodName + "() check ...");
 818          if (xa == IntPtr.Zero) {
 819             logger(XmlBlasterLogLevel.ERROR, "", "Can't process " + methodName + ", xmlBlaster pointer is reset, please create a new instance of PInvokeCE.cs class after a disconnect() call");
 820             throw new XmlBlasterException("internal.illegalState", "Can't process " + methodName + ", xmlBlaster pointer is reset, please create a new instance of PInvokeCE.cs class after a disconnect() call");
 821          }
 822       }
 823 
 824       private delegate void OnLogging(XmlBlasterLogLevel logLevel, string location, string message);
 825       private event OnLogging onLogging;
 826       public void AddLoggingListener(I_LoggingCallback listener)
 827       {
 828          if (listener != null)
 829          {
 830             onLogging += new OnLogging(listener.OnLogging);
 831          }
 832       }
 833       public void RemoveLoggingListener(I_LoggingCallback listener)
 834       {
 835          if (listener != null)
 836          {
 837             onLogging -= new OnLogging(listener.OnLogging);
 838          }
 839       }
 840 
 841       private delegate void OnData(bool read, int currBytesRead, int nbytes);
 842       private event OnData onData;
 843       public void AddCallbackProgressListener(I_ProgressCallback listener)
 844       {
 845          if (listener == null) return;
 846          if (this.progressUnmanagedFp == null)
 847          {
 848             this.progressUnmanagedFp = new ProgressUnmanagedFp(this.callbackProgressUnmanaged);
 849             this.progressFpForDelegate = Marshal.GetFunctionPointerForDelegate(this.progressUnmanagedFp);
 850             xmlBlasterUnmanagedCERegisterProgressListener(xa, this.progressFpForDelegate);
 851          }
 852          this.onData += new OnData(listener.OnData);
 853       }
 854 
 855       public void RemoveCallbackProgressListener(I_ProgressCallback listener)
 856       {
 857          if (listener != null)
 858          {
 859             this.onData -= new OnData(listener.OnData);
 860          }
 861       }
 862 
 863       private delegate string OnUpdate(string cbSessionId, MsgUnitUpdate msgUnit);
 864       private event OnUpdate onUpdate;
 865       public ConnectReturnQos Connect(string qos, I_Callback listener)
 866       {
 867          if (!this.initializeIsCalled)
 868             Initialize(new Hashtable());
 869 
 870          check("connect");
 871          if (listener != null)
 872          {
 873             onUpdate += new OnUpdate(listener.OnUpdate);
 874          }
 875          logInfos(XmlBlasterLogLevel.INFO);
 876          try
 877          {
 878             XmlBlasterUnmanagedCEException exception = new XmlBlasterUnmanagedCEException(false);
 879 #           if CF1 // Compact Framework .net 1.0

 880                /*bool bb = */xmlBlasterUnmanagedCEInitialize(xa, IntPtr.Zero, ref exception);
 881 #           else // Compact Framework 2

 882                /*bool bb = */xmlBlasterUnmanagedCEInitialize(xa, updateFpForDelegate, ref exception);
 883 #           endif

 884             checkAndThrow("xmlBlasterUnmanagedCEInitialize", ref exception);
 885 
 886 #           if CF1 // Compact Framework .net 1.0

 887                IntPtr retP = xmlBlasterUnmanagedCEConnect(xa, stringToUtf8IntPtr(qos), IntPtr.Zero, ref exception);
 888 #           else // Compact Framework 2

 889                IntPtr retP = xmlBlasterUnmanagedCEConnect(xa, stringToUtf8IntPtr(qos), updateFpForDelegate, ref exception);
 890 #           endif

 891             checkAndThrow("xmlBlasterUnmanagedCEConnect", ref exception);
 892             string ret = stringFromUtf8IntPtr(retP);
 893             //logger(XmlBlasterLogLevel.TRACE, "", "xmlBlasterUnmanagedCEConnect: SUCCESS '" + ret + "'");

 894 
 895             I_ConnectionStateListener li = this.connectionListener;
 896             if (li != null) {
 897                li.reachedAlive(ConnectionStateEnum.UNDEF, this);
 898             }
 899 
 900             return new ConnectReturnQos(ret);
 901          }
 902          catch (XmlBlasterException e)
 903          {
 904             throw e;
 905          }
 906          catch (Exception e)
 907          {
 908             throw new XmlBlasterException("internal.unknown", "connect failed", e);
 909          }
 910       }
 911 
 912       public void LeaveServer()
 913       {
 914          check("LeaveServer");
 915          try
 916          {
 917             IntPtr tmp = xa;
 918             xa = IntPtr.Zero;
 919             freeXmlBlasterAccessUnparsedUnmanagedCE(tmp);
 920             logger(XmlBlasterLogLevel.TRACE, "", "freeXmlBlasterAccessUnparsedUnmanagedCE: SUCCESS freed all resources");
 921          }
 922          catch (Exception e)
 923          {
 924             throw new XmlBlasterException("internal.unknown", "LeaveServer failed", e);
 925          }
 926       }
 927 
 928       /// After calling diconnect() this class is not usable anymore

 929       /// you need to create a new instance to connect again

 930       public bool Disconnect(string qos)
 931       {
 932          check("disconnect");
 933          XmlBlasterException ex = null;
 934          try
 935          {
 936             XmlBlasterUnmanagedCEException exception = new XmlBlasterUnmanagedCEException(false);
 937             bool bb = xmlBlasterUnmanagedCEDisconnect(xa, stringToUtf8IntPtr(qos), ref exception);
 938             if (exception.CaughtException())
 939             {
 940                ex = fillXmlBlasterException(ref exception);
 941                // Important logging since we don't throw the exception below

 942                logger(XmlBlasterLogLevel.TRACE, "", "xmlBlasterUnmanagedCEDisconnect: Got exception from C: " + ex.ToString());
 943             }
 944 
 945             freeXmlBlasterAccessUnparsedUnmanagedCE(xa);
 946             xa = IntPtr.Zero;
 947             logger(XmlBlasterLogLevel.TRACE, "", "xmlBlasterUnmanagedCEDisconnect: SUCCESS freed all resources");
 948 
 949             //if (ex != null) throw ex;

 950             return bb;
 951          }
 952          catch (XmlBlasterException e)
 953          {
 954             throw e;
 955          }
 956          catch (Exception e)
 957          {
 958             throw new XmlBlasterException("internal.unknown", "disconnect failed", e);
 959          }
 960          finally {
 961             I_ConnectionStateListener li = this.connectionListener;
 962             if (li != null) {
 963                li.reachedDead(ConnectionStateEnum.ALIVE, this); // TODO: previous state

 964             }
 965          }
 966       }
 967 
 968       private void checkAndThrow(string location, ref XmlBlasterUnmanagedCEException exception)
 969       {
 970          if (exception.CaughtException())
 971          {
 972             XmlBlasterException e = fillXmlBlasterException(ref exception);
 973             logger(XmlBlasterLogLevel.WARN, "", location + ": errorCode=" + e.ErrorCode);
 974             logger(XmlBlasterLogLevel.TRACE, "", location + ": Got exception from C: " + e.ToString());
 975 
 976             if (e.ErrorCode.StartsWith("communication.")) {
 977                I_ConnectionStateListener li = this.connectionListener;
 978                if (li != null) {
 979                   li.reachedDead(ConnectionStateEnum.ALIVE, this); // TODO: previous state

 980                }
 981             }
 982 
 983             throw e;
 984          }
 985          logger(XmlBlasterLogLevel.TRACE, "", location + ": SUCCESS");
 986       }
 987 
 988       public PublishReturnQos Publish(string key, string content, string qos)
 989       {
 990          return Publish(new MsgUnit(key, content, qos));
 991       }
 992 
 993       public PublishReturnQos Publish(MsgUnit msgUnit)
 994       {
 995          check("publish");
 996          try
 997          {
 998             XmlBlasterUnmanagedCEException exception = new XmlBlasterUnmanagedCEException(false);
 999             MsgUnitUnmanagedCEpublish msgUnitUnmanaged = new MsgUnitUnmanagedCEpublish(msgUnit.GetKeyStr(),
1000                msgUnit.GetContent(), msgUnit.GetQosStr());
1001 
1002             IntPtr retP = xmlBlasterUnmanagedCEPublish(xa, out msgUnitUnmanaged, ref exception);
1003             checkAndThrow("xmlBlasterUnmanagedCEPublish", ref exception);
1004 
1005             string ret = stringFromUtf8IntPtr(retP);
1006             return new PublishReturnQos(ret);
1007          }
1008          catch (XmlBlasterException e)
1009          {
1010             throw e;
1011          }
1012          catch (Exception e)
1013          {
1014             throw new XmlBlasterException("internal.unknown", "publish failed", e);
1015          }
1016       }
1017 
1018       public void PublishOneway(MsgUnit[] msgUnitArr)
1019       {
1020          check("publishOneway");
1021          if (msgUnitArr.Length < 1)
1022             return;
1023          IntPtr arrP = IntPtr.Zero;
1024          try
1025          {
1026             IntPtr current = arrP;
1027             for (int i=0; i<msgUnitArr.Length; i++) {
1028                MsgUnitUnmanagedCEpublish msgUnitUnmanaged = new MsgUnitUnmanagedCEpublish(msgUnitArr[i].GetKeyStr(),
1029                         msgUnitArr[i].GetContent(), msgUnitArr[i].GetQosStr());
1030                if (i == 0) {
1031                   int len = Marshal.SizeOf(msgUnitUnmanaged);
1032                   arrP = Marshal.AllocHGlobal(msgUnitArr.Length * len);
1033                   current = arrP;
1034                } 
1035                Marshal.StructureToPtr(msgUnitUnmanaged, current, false);
1036                current = (IntPtr)((long)current + Marshal.SizeOf(msgUnitUnmanaged));
1037             }
1038             XmlBlasterUnmanagedCEException exception = new XmlBlasterUnmanagedCEException(false);
1039             xmlBlasterUnmanagedCEPublishOneway(xa, arrP, msgUnitArr.Length, ref exception);
1040             checkAndThrow("xmlBlasterUnmanagedCEPublishOneway", ref exception);
1041             logger(XmlBlasterLogLevel.TRACE, "", "publishOneway: SUCCESS");
1042          }
1043          catch (XmlBlasterException e)
1044          {
1045             throw e;
1046          }
1047          catch (Exception e)
1048          {
1049             throw new XmlBlasterException("internal.unknown", "publishOneway failed", e);
1050          }
1051          finally {
1052             if (arrP != IntPtr.Zero) Marshal.FreeHGlobal(arrP);
1053          }
1054       }
1055 
1056       public SubscribeReturnQos Subscribe(string key, string qos)
1057       {
1058          check("subscribe");
1059          try
1060          {
1061             XmlBlasterUnmanagedCEException exception = new XmlBlasterUnmanagedCEException(false);
1062             IntPtr retP = xmlBlasterUnmanagedCESubscribe(xa, stringToUtf8IntPtr(key),
1063                                            stringToUtf8IntPtr(qos), ref exception);
1064             checkAndThrow("xmlBlasterUnmanagedCESubscribe", ref exception);
1065             string ret = stringFromUtf8IntPtr(retP);
1066             logger(XmlBlasterLogLevel.TRACE, "", ret);
1067             return new SubscribeReturnQos(ret);
1068          }
1069          catch (XmlBlasterException e)
1070          {
1071             throw e;
1072          }
1073          catch (Exception e)
1074          {
1075             throw new XmlBlasterException("internal.unknown", "subscribe failed", e);
1076          }
1077       }
1078 
1079       public UnSubscribeReturnQos[] UnSubscribe(string key, string qos)
1080       {
1081          check("unSubscribe");
1082          try
1083          {
1084             XmlBlasterUnmanagedCEException exception = new XmlBlasterUnmanagedCEException(false);
1085             int size;
1086             IntPtr outArray = new IntPtr();
1087             xmlBlasterUnmanagedCEUnSubscribe(xa, stringToUtf8IntPtr(key), stringToUtf8IntPtr(qos), ref exception, out size, out outArray);
1088             checkAndThrow("xmlBlasterUnmanagedCEUnSubcribe", ref exception);
1089             if (size == 0)
1090             {
1091                logger(XmlBlasterLogLevel.TRACE, "", "xmlBlasterUnmanagedCEUnSubcribe: Done (no topics found)");
1092                return new UnSubscribeReturnQos[0];
1093             }
1094             UnSubscribeReturnQos[] retQosArr = new UnSubscribeReturnQos[size];
1095             IntPtr current = outArray;
1096             for (int i = 0; i < size; i++)
1097             {
1098                StringArr stringArr = new StringArr();
1099                Marshal.PtrToStructure(current, stringArr);
1100                current = (IntPtr)((long)current + Marshal.SizeOf(stringArr));
1101                string ret = stringFromUtf8IntPtr(stringArr.str);
1102                retQosArr[i] = new UnSubscribeReturnQos(ret);
1103             }
1104             xmlBlasterUnmanagedCEFreePP(out outArray);
1105             return retQosArr;
1106          }
1107          catch (XmlBlasterException e)
1108          {
1109             throw e;
1110          }
1111          catch (Exception e)
1112          {
1113             throw new XmlBlasterException("internal.unknown", "unSubscribe failed", e);
1114          }
1115       }
1116 
1117       public EraseReturnQos[] Erase(string key, string qos)
1118       {
1119          check("erase");
1120          try
1121          {
1122             XmlBlasterUnmanagedCEException exception = new XmlBlasterUnmanagedCEException(false);
1123             int size;
1124             IntPtr outArray = new IntPtr();
1125             xmlBlasterUnmanagedCEErase(xa, stringToUtf8IntPtr(key), stringToUtf8IntPtr(qos), ref exception, out size, out outArray);
1126             checkAndThrow("xmlBlasterUnmanagedCEErase", ref exception);
1127             if (size == 0)
1128             {
1129                logger(XmlBlasterLogLevel.TRACE, "", "xmlBlasterUnmanagedCEErase: Done (no topics found)");
1130                return new EraseReturnQos[0];
1131             }
1132 
1133             EraseReturnQos[] retQosArr = new EraseReturnQos[size];
1134             IntPtr current = outArray;
1135             for (int i = 0; i < size; i++)
1136             {
1137                StringArr stringArr = new StringArr();
1138                Marshal.PtrToStructure(current, stringArr);
1139                current = (IntPtr)((long)current + Marshal.SizeOf(stringArr));
1140                string ret = stringFromUtf8IntPtr(stringArr.str);
1141                retQosArr[i] = new EraseReturnQos(ret);
1142             }
1143             //logger(XmlBlasterLogLevel.TRACE, "", "xmlBlasterUnmanagedCEErase: FREEING WRONG POINTER??");

1144             xmlBlasterUnmanagedCEFreePP(out outArray);
1145             return retQosArr;
1146          }
1147          catch (XmlBlasterException e)
1148          {
1149             throw e;
1150          }
1151          catch (Exception e)
1152          {
1153             throw new XmlBlasterException("internal.unknown", "erase failed", e);
1154          }
1155       }
1156 
1157       public MsgUnitGet[] Get(string key, string qos)
1158       {
1159          check("get");
1160          try
1161          {
1162             XmlBlasterUnmanagedCEException exception = new XmlBlasterUnmanagedCEException(false);
1163             int size;
1164             IntPtr outArray = new IntPtr();
1165             xmlBlasterUnmanagedCEGet(xa, stringToUtf8IntPtr(key), stringToUtf8IntPtr(qos),
1166                ref exception, out size, out outArray);
1167             checkAndThrow("xmlBlasterUnmanagedCEGet", ref exception);
1168             MsgUnitGet[] msgUnitArr = new MsgUnitGet[size];
1169             IntPtr current = outArray;
1170             for (int i = 0; i < size; i++)
1171             {
1172                logger(XmlBlasterLogLevel.TRACE, "", "xmlBlasterUnmanagedCEGet: parsing #" + i);
1173                MsgUnitUnmanagedCEget msgUnitUnmanaged = new MsgUnitUnmanagedCEget();
1174                Marshal.PtrToStructure(current, msgUnitUnmanaged);
1175                //Console.WriteLine("msgUnitUnmanaged.contentLen=" + msgUnitUnmanaged.contentLen + " sizeof="+Marshal.SizeOf(msgUnitUnmanaged));

1176                current = (IntPtr)((long)current + Marshal.SizeOf(msgUnitUnmanaged));
1177                // The getters free the memory in the C DLL:

1178                msgUnitArr[i] = new MsgUnitGet(
1179                   msgUnitUnmanaged.getKey(), msgUnitUnmanaged.getContent(), msgUnitUnmanaged.getQos());
1180                msgUnitUnmanaged.getResponseQos(); // dummy call to free memory in C DLL

1181             }
1182             //logger(XmlBlasterLogLevel.TRACE, "", "xmlBlasterUnmanagedCEGet: freeing now IntPtr SHOULD IT BE *outArray???");

1183             xmlBlasterUnmanagedCEFreePP(out outArray);
1184             return msgUnitArr;
1185          }
1186          catch (XmlBlasterException e)
1187          {
1188             throw e;
1189          }
1190          catch (Exception e)
1191          {
1192             throw new XmlBlasterException("internal.unknown", "get failed", e);
1193          }
1194       }
1195 
1196       public string Ping(string qos)
1197       {
1198          check("ping");
1199          try
1200          {
1201             XmlBlasterUnmanagedCEException exception = new XmlBlasterUnmanagedCEException(false);
1202             IntPtr retP = xmlBlasterUnmanagedCEPing(xa, stringToUtf8IntPtr(qos), ref exception);
1203             checkAndThrow("xmlBlasterUnmanagedCEPing", ref exception);
1204             string ret = stringFromUtf8IntPtr(retP);
1205             return ret;
1206          }
1207          catch (XmlBlasterException e)
1208          {
1209             throw e;
1210          }
1211          catch (Exception e)
1212          {
1213             throw new XmlBlasterException("internal.unknown", "ping failed", e);
1214          }
1215       }
1216 
1217       public bool IsConnected()
1218       {
1219          if (xa == IntPtr.Zero) return false;
1220          try
1221          {
1222             bool bb = xmlBlasterUnmanagedCEIsConnected(xa);
1223             logger(XmlBlasterLogLevel.TRACE, "", "xmlBlasterUnmanagedCEIsConnected: SUCCESS '" + bb + "'");
1224             return bb;
1225          }
1226          catch (XmlBlasterException e)
1227          {
1228             throw e;
1229          }
1230          catch (Exception e)
1231          {
1232             throw new XmlBlasterException("internal.unknown", "isConnected failed", e);
1233          }
1234       }
1235 
1236       public string GetVersion()
1237       {
1238          IntPtr retP = xmlBlasterUnmanagedCEVersion();
1239          return stringFromUtf8IntPtr(retP);
1240       }
1241 
1242       public string GetUsage()
1243       {
1244          IntPtr retP = xmlBlasterUnmanagedCEUsage();
1245          return stringFromUtf8IntPtr(retP);
1246       }
1247 
1248       public string GetEmeiId() {
1249 #        if XMLBLASTER_WINCE

1250          try {
1251             IntPtr intPtr = getXmlBlasterEmei();
1252             if (intPtr == IntPtr.Zero)
1253                return null;
1254             byte[] bytes = byteArrayFromIntPtr(intPtr, -1, true);
1255             string emeiId = toHexString(bytes);
1256             return emeiId;
1257          }
1258          catch (Exception e) {
1259             logger(XmlBlasterLogLevel.WARN, "", "getEmeiId() is not supported on this platform: " + e.ToString());
1260             return null;
1261          }
1262 #        else

1263             return null;
1264 #        endif

1265       }
1266 
1267       public string GetDeviceUniqueId() {
1268 #        if XMLBLASTER_WINCE

1269             byte[] bytes = GetDeviceID("xmlBlasterClient");
1270             if (bytes == null) return null;
1271             string deviceId = toHexString(bytes);
1272             //{// Remove again

1273             //   throw new XmlBlasterException("bla", "My deviceId=" + deviceId);

1274             //}

1275             return deviceId;
1276 #        else

1277             return null;
1278 #        endif

1279       }
1280 
1281       public static string toHexString(byte[] bytes) {
1282          if (bytes == null) return null;
1283          StringBuilder temp = new StringBuilder();
1284          for (int i=0; i<bytes.Length; i++)
1285             temp.Append(bytes[i].ToString("X2")); // hex view

1286             //temp.Append(bytes[i].ToString("D3")).Append(" "); // Decimal view

1287          return temp.ToString();
1288       }
1289 
1290 #if XMLBLASTER_WINCE

1291       /*
1292 HRESULT GetDeviceUniqueID(
1293   LPBYTE pbApplicationData,
1294   DWORD cbApplictionData,
1295   DWORD dwDeviceIDVersion,
1296   LPBYTE pbDeviceIDOutput,
1297   DWORD* pcbDeviceIDOutput
1298 );
1299       */
1300       //http://blogs.msdn.com/windowsmobile/archive/2006/01/09/510997.aspx#514959

1301       [DllImport("coredll.dll")]
1302       private extern static int GetDeviceUniqueID([In, Out] byte[] appdata,
1303                                                   int cbApplictionData,
1304                                                   int dwDeviceIDVersion,
1305                                                   [In, Out] byte[] deviceIDOuput,
1306                                                   out uint pcbDeviceIDOutput);
1307 
1308       /// Works only on Windows Mobile 5 and above

1309       private byte[] GetDeviceID(string AppString)
1310       {
1311          // Call the GetDeviceUniqueID

1312          byte[] AppData = new byte[AppString.Length];
1313          for (int count = 0; count < AppString.Length; count++)
1314             AppData[count] = (byte)AppString[count];
1315 
1316          int appDataSize = AppData.Length;
1317          byte[] DeviceOutput = new byte[20];
1318          uint SizeOut = 20;
1319 
1320          try {
1321             GetDeviceUniqueID(AppData, appDataSize, 1, DeviceOutput, out SizeOut);
1322          }
1323          catch (Exception e) {
1324             logger(XmlBlasterLogLevel.WARN, "", "GetDeviceUniqueID() is not supported on this platform");
1325             logger(XmlBlasterLogLevel.TRACE, "", "GetDeviceUniqueID() is not supported on this platform: " + e.ToString());
1326             return null;
1327          }
1328 
1329          if (SizeOut == 0)
1330             return null;
1331 
1332          return DeviceOutput;
1333       }
1334 #endif

1335 
1336 /* From news group 2007-03-01
1337 // support@NOdroopySPAMeyes.com
1338 // microsoft.public.dotnet.framework.compactframework
1339 #region DeviceId support
1340 
1341 [DllImport("coredll.dll")]
1342 
1343 private static extern bool KernelIoControl(Int32 IoControlCode, IntPtr
1344 
1345 InputBuffer, Int32 InputBufferSize, byte[] OutputBuffer, Int32
1346 
1347 OutputBufferSize, ref Int32 BytesReturned);
1348 
1349 
1350 
1351 
1352 
1353 private static Int32 FILE_DEVICE_HAL = 0x00000101;
1354 
1355 private static Int32 FILE_ANY_ACCESS = 0x0;
1356 
1357 private static Int32 METHOD_BUFFERED = 0x0;
1358 
1359 private static Int32 IOCTL_HAL_GET_DEVICEID =
1360 
1361 ((FILE_DEVICE_HAL) << 16) | ((FILE_ANY_ACCESS) << 14)
1362 
1363 | ((21) << 2) | (METHOD_BUFFERED);
1364 
1365 private static string GetDeviceID()
1366 
1367 {
1368 
1369 byte[] OutputBuffer = new byte[256];
1370 
1371 Int32 OutputBufferSize, BytesReturned;
1372 
1373 OutputBufferSize = OutputBuffer.Length;
1374 
1375 BytesReturned = 0;
1376 
1377 // Call KernelIoControl passing the previously defined
1378 
1379 // IOCTL_HAL_GET_DEVICEID parameter
1380 
1381 // We donĀ't need to pass any input buffers to this call
1382 
1383 // so InputBuffer and InputBufferSize are set to their null
1384 
1385 // values
1386 
1387 bool retVal = KernelIoControl(IOCTL_HAL_GET_DEVICEID, IntPtr.Zero, 0,
1388 
1389 OutputBuffer,
1390 
1391 OutputBufferSize,
1392 
1393 ref BytesReturned);
1394 
1395 // If the request failed, exit the method now
1396 
1397 if (retVal == false)
1398 
1399 {
1400 
1401 return null;
1402 
1403 }
1404 
1405 // Examine the OutputBuffer byte array to find the start of the
1406 
1407 // Preset ID and Platform ID, as well as the size of the
1408 
1409 // PlatformID.
1410 
1411 // PresetIDOffset - The number of bytes the preset ID is offset
1412 
1413 // from the beginning of the structure
1414 
1415 // PlatformIDOffset - The number of bytes the platform ID is
1416 
1417 // offset from the beginning of the structure
1418 
1419 // PlatformIDSize - The number of bytes used to store the
1420 
1421 // platform ID
1422 
1423 // Use BitConverter.ToInt32() to convert from byte[] to int
1424 
1425 Int32 PresetIDOffset = BitConverter.ToInt32(OutputBuffer, 4);
1426 
1427 Int32 PlatformIDOffset = BitConverter.ToInt32(OutputBuffer, 0xc);
1428 
1429 Int32 PlatformIDSize = BitConverter.ToInt32(OutputBuffer, 0x10);
1430 
1431 // Convert the Preset ID segments into a string so they can be
1432 
1433 // displayed easily.
1434 
1435 StringBuilder sb = new StringBuilder();
1436 
1437 sb.Append(String.Format("{0:X8}-{1:X4}-{2:X4}-{3:X4}-",
1438 
1439 BitConverter.ToInt32(OutputBuffer, PresetIDOffset),
1440 
1441 BitConverter.ToInt16(OutputBuffer, PresetIDOffset + 4),
1442 
1443 BitConverter.ToInt16(OutputBuffer, PresetIDOffset + 6),
1444 
1445 BitConverter.ToInt16(OutputBuffer, PresetIDOffset + 8)));
1446 
1447 // Break the Platform ID down into 2-digit hexadecimal numbers
1448 
1449 // and append them to the Preset ID. This will result in a
1450 
1451 // string-formatted Device ID
1452 
1453 for (int i = PlatformIDOffset;
1454 
1455 i < PlatformIDOffset + PlatformIDSize;
1456 
1457 i++)
1458 
1459 {
1460 
1461 sb.Append(String.Format("{0:X2}", OutputBuffer[i]));
1462 
1463 }
1464 
1465 // return the Device ID string
1466 
1467 return sb.ToString();
1468 
1469 }
1470 
1471 #endregion
1472 */
1473 
1474    } // class PInvokeCE

1475 } // namespace



syntax highlighted by Code2HTML, v. 0.9.1