/***********************************************************************\
* Virtuoso.Miranda.Plugins (Hyphen) *
* Provides a managed wrapper for API of IM client Miranda. *
* Copyright (C) 2006-2009 virtuoso *
* deml.tomas@seznam.cz *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
\***********************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using Virtuoso.Miranda.Plugins.Native;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Virtuoso.Miranda.Plugins.Resources;
namespace Virtuoso.Miranda.Plugins.Infrastructure
{
///
/// Represents database event information.
///
public class DatabaseEventInfo : IMirandaObject
{
#region Constants
private const string MS_DB_EVENT_GETBLOBSIZE = "DB/Event/GetBlobSize";
///
/// DB/Event/Get
/// Retrieves all the information stored in hDbEvent
/// wParam=(WPARAM)(HANDLE)hDbEvent
/// lParam=(LPARAM)(DBEVENTINFO*)&dbe
/// hDbEvent should have been returned by db/event/add or db/event/find*event
/// Returns 0 on success or nonzero if hDbEvent is invalid
/// Don't forget to set dbe.cbSize, dbe.pBlob and dbe.cbBlob before calling this
/// service
/// The correct value dbe.cbBlob can be got using db/event/getblobsize
/// If successful, all the fields of dbe are filled. dbe.cbBlob is set to the
/// actual number of bytes retrieved and put in dbe.pBlob
/// If dbe.cbBlob is too small, dbe.pBlob is filled up to the size of dbe.cbBlob
/// and then dbe.cbBlob is set to the required size of data to go in dbe.pBlob
/// On return, dbe.szModule is a pointer to the database module's own internal list
/// of modules. Look but don't touch.
///
private const string MS_DB_EVENT_GET = "DB/Event/Get";
private const string MS_DB_TIME_TIMESTAMPTOSTRING = "DB/Time/TimestampToString";
///
/// DB/Event/GetContact
/// Retrieves a handle to the contact that owns hDbEvent.
/// wParam=(WPARAM)(HANDLE)hDbEvent
/// lParam=0
/// hDbEvent should have been returned by db/event/add or db/event/find*event
/// NULL is a valid return value, meaning, as usual, the user.
/// Returns (HANDLE)(-1) if hDbEvent is invalid, or the handle to the contact on
/// success
/// This service is exceptionally slow. Use only when you have no other choice at
/// all.
///
private const string MS_DB_EVENT_GETCONTACT = "DB/Event/GetContact";
///
/// DB/Event/GetText (0.7.0+)
/// Retrieves the event's text
/// wParam=(WPARAM)0 (unused)
/// lParam=(LPARAM)(DBEVENTGETTEXT*)egt - pointer to structure with parameters
/// egt->dbei should be the valid database event read via MS_DB_EVENT_GET
/// egt->datatype = DBVT_WCHAR or DBVT_ASCIIZ or DBVT_TCHAR. If a caller wants to
/// suppress Unicode part of event in answer, add DBVTF_DENYUNICODE to this field.
/// egt->codepage is any valid codepage, CP_ACP by default.
/// Function returns a pointer to a string in the required format.
/// This string should be freed by a call of mir_free
///
private const string MS_DB_EVENT_GETTEXT = "DB/Event/GetText";
#endregion
#region .ctors
///
/// Initializes a new instance of the class.
///
protected DatabaseEventInfo() { }
///
/// Initializes a new instance of the class.
///
/// Event handle.
protected DatabaseEventInfo(IntPtr eventHandle)
{
if (eventHandle == IntPtr.Zero)
throw new ArgumentNullException("eventHandle");
this.mirandaHandle = eventHandle;
FromHandle(eventHandle, out type, out flags, out data, out owningModule, out timestamp);
}
///
/// Creates a new instance of the from an event handle.
///
/// Event handle.
/// Database event info.
public static DatabaseEventInfo FromHandle(IntPtr eventHandle)
{
return new DatabaseEventInfo(eventHandle);
}
#endregion
#region Initializers
///
/// Gets the event information based on its handle.
///
/// Event handle.
/// [OUT] Event type.
/// [OUT] Event flags.
/// [OUT] Event data.
/// [OUT] Event related module.
/// [OUT] Event timestamp.
public static void FromHandle(IntPtr eventHandle, out DatabaseEventType type, out DatabaseEventProperties flags, out string data, out Protocol owningModule, out DateTime timestamp)
{
InteropBuffer buffer = null;
try
{
unsafe
{
DBEVENTINFO dbEventInfo;
PrepareDbEventInfo(eventHandle, out dbEventInfo, out buffer);
GetEventInfo(ref dbEventInfo, eventHandle, buffer, out type, out flags, out data, out owningModule, out timestamp);
}
}
catch (MirandaException)
{
throw;
}
catch (Exception e)
{
throw new InvalidOperationException(TextResources.ExceptionMsg_CannotFinishMarshaling, e);
}
finally
{
if (buffer != null)
{
buffer.Unlock();
InteropBufferPool.ReleaseBuffer(buffer);
}
}
}
///
/// Prepares the for information extraction and the blob buffer.
///
/// Event handle.
/// [OUT] DB event info to marshal data into.
/// [OUT] Locked Blob buffer.
private unsafe static void PrepareDbEventInfo(IntPtr eventHandle, out DBEVENTINFO dbEventInfo, out InteropBuffer buffer)
{
int blobSize = MirandaContext.Current.CallServiceUnsafe(MS_DB_EVENT_GETBLOBSIZE, eventHandle.ToPointer(), null);
if (blobSize == -1)
throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, MS_DB_EVENT_GETBLOBSIZE, blobSize.ToString()));
// Acquire a buffer for the blob
buffer = InteropBufferPool.AcquireBuffer(blobSize);
buffer.Lock();
dbEventInfo = new DBEVENTINFO(blobSize, buffer.IntPtr);
}
///
/// Initializes the instance by marshaling data from a pointer.
///
/// pointer.
private void MarshalEventInfo(IntPtr pDbEventInfo)
{
DBEVENTINFO info = (DBEVENTINFO)Marshal.PtrToStructure(pDbEventInfo, typeof(DBEVENTINFO));
// Get an appropriately size buffer for the blob
InteropBuffer buffer = InteropBufferPool.AcquireBuffer((int)info.BlobSize);
try
{
buffer.Lock();
GetEventInfo(ref info, IntPtr.Zero, buffer, out type, out flags, out data, out owningModule, out timestamp);
}
finally
{
buffer.Unlock();
InteropBufferPool.ReleaseBuffer(buffer);
}
}
///
/// Get the event information from a struct.
///
/// [REF] struct.
/// Event handle (the blob buffer will be populated if not null).
/// Buffer to use for blob marshaling.
/// [OUT] Event type.
/// [OUT] Event flags.
/// [OUT] Event data.
/// [OUT] Event related module.
/// [OUT] Event timestamp.
private static void GetEventInfo(ref DBEVENTINFO dbEventInfo, IntPtr eventHandle, InteropBuffer blobBuffer, out DatabaseEventType type, out DatabaseEventProperties flags, out string data, out Protocol owningModule, out DateTime timestamp)
{
MirandaContext context = MirandaContext.Current;
unsafe
{
// If the event handle is set, we probably want to populate the blob buffer...
if (eventHandle != IntPtr.Zero)
PopulateBlobBuffer(ref dbEventInfo, eventHandle);
type = (DatabaseEventType)dbEventInfo.EventType;
flags = (DatabaseEventProperties)dbEventInfo.Flags;
data = GetEventData(ref dbEventInfo);
}
owningModule = GetEventModule(ref dbEventInfo);
GetEventTimestamp(ref dbEventInfo, blobBuffer, out timestamp);
}
///
/// Populates the blob buffer set by the parameter.
///
/// [REF] struct identifiing the buffer.
/// Event handle.
/// Buffer could bet populated.
private unsafe static void PopulateBlobBuffer(ref DBEVENTINFO dbEventInfo, IntPtr eventHandle)
{
int result;
fixed (void* pDbEventInfo = &dbEventInfo)
result = MirandaContext.Current.CallServiceUnsafe(MS_DB_EVENT_GET, eventHandle.ToPointer(), pDbEventInfo);
if (result != 0)
throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, MS_DB_EVENT_GET, result.ToString()));
}
///
/// Gets the event timestamp.
///
/// [REF] struct.
/// Buffer to reuse.
/// [OUT] Timestamp.
private static void GetEventTimestamp(ref DBEVENTINFO dbEventInfo, InteropBuffer blobBuffer, out DateTime timestamp)
{
try
{
DBTIMETOSTRING timeToString = new DBTIMETOSTRING("s D");
timeToString.MaxBytes = blobBuffer.Size;
timeToString.Output = blobBuffer.IntPtr;
unsafe
{
MirandaContext.Current.CallServiceUnsafe(MS_DB_TIME_TIMESTAMPTOSTRING, (void*)dbEventInfo.Timestamp, &timeToString);
}
timestamp = DateTime.Parse(Translate.ToString(timeToString.Output, StringEncoding.Ansi));
}
catch (FormatException)
{
timestamp = DateTime.MinValue;
}
}
///
/// Gets the event module.
///
/// [REF] struct.
/// Event module.
private static Protocol GetEventModule(ref DBEVENTINFO dbEventInfo)
{
Protocol owningModule;
bool moduleFound = false;
if (dbEventInfo.Module != IntPtr.Zero)
moduleFound = MirandaContext.Current.Protocols.TryGetValue(Translate.ToString(dbEventInfo.Module, StringEncoding.Ansi), out owningModule);
else
owningModule = Protocol.UnknownProtocol;
if (!moduleFound)
owningModule = Protocol.UnknownProtocol;
return owningModule;
}
///
/// Gets the event data.
///
/// [REF] struct.
/// Event data.
private unsafe static string GetEventData(ref DBEVENTINFO dbEventInfo)
{
string data;
DBEVENTGETTEXT dbGetText = new DBEVENTGETTEXT();
dbGetText.Codepage = 0;
dbGetText.DataType = (int)DatabaseSettingType.UnicodeString;
IntPtr pText;
fixed (void* pDbEventInfo = &dbEventInfo)
{
dbGetText.DbEventInfoPtr = new IntPtr(pDbEventInfo);
pText = (IntPtr)MirandaContext.Current.CallServiceUnsafe(MS_DB_EVENT_GETTEXT, null, &dbGetText);
}
if (pText != IntPtr.Zero)
{
data = Translate.ToString(pText, StringEncoding.Unicode);
MirandaContext.Current.MirandaMemoryManager.Free(pText);
}
else
throw new MirandaException(String.Format(TextResources.ExceptionMsg_Formatable2_MirandaServiceReturnedFailure, MS_DB_EVENT_GETTEXT, "null"));
return data;
}
#endregion
#region Properties
private IntPtr mirandaHandle;
public IntPtr MirandaHandle
{
get { return mirandaHandle; }
}
private Protocol owningModule;
public Protocol OwningModule
{
get { return owningModule; }
}
private DateTime timestamp;
public DateTime Timestamp
{
get { return timestamp; }
}
private DatabaseEventProperties flags;
public DatabaseEventProperties Flags
{
get { return flags; }
}
private DatabaseEventType type;
public DatabaseEventType Type
{
get { return type; }
}
private string data;
public string Data
{
get { return data; }
}
#endregion
#region Methods
///
/// Marshals the from a struct pointer.
///
/// struct pointer.
/// Event info.
internal static DatabaseEventInfo FromPointer(IntPtr pDbEventInfo)
{
DatabaseEventInfo info = new DatabaseEventInfo();
info.MarshalEventInfo(pDbEventInfo);
return info;
}
///
/// Gets the handle of the contact owning this event. This method is very slow, use wisely.
///
/// Associated contact handle.
public IntPtr GetContactHandle()
{
return GetContactHandle(mirandaHandle);
}
///
/// Gets the handle of the contact owning this event. This method is very slow, use wisely.
///
/// Event handle to get the contact handle for.
/// Associated contact handle.
public static IntPtr GetContactHandle(IntPtr eventHandle)
{
if (eventHandle == IntPtr.Zero)
throw new ArgumentNullException("eventHandle");
int contactHandle = MirandaContext.Current.CallService(MS_DB_EVENT_GETCONTACT, eventHandle, IntPtr.Zero);
if (contactHandle == -1)
throw new ArgumentException(TextResources.ExceptionMsg_InvalidHandle, "eventHandle");
return (IntPtr)contactHandle;
}
#endregion
}
}