mirror of
https://github.com/cyanfish/naps2.git
synced 2024-10-05 11:57:32 +03:00
Added helper classes for working with unmanaged structures (which are used to interface with native code, e.g. MAPI).
This commit is contained in:
parent
2ff243c9e1
commit
54ee56b639
@ -49,87 +49,73 @@ namespace NAPS2.Email.Mapi
|
||||
/// Sends an email described by the given message object.
|
||||
/// </summary>
|
||||
/// <param name="message">The object describing the email message.</param>
|
||||
/// <exception cref="EmailException">Throws an EmailException if an error occurred.</exception>
|
||||
/// <returns>Returns true if the message was sent, false if the user aborted.</returns>
|
||||
public bool SendEmail(EmailMessage message)
|
||||
{
|
||||
// Translate files & recipients to unmanaged MAPI structures
|
||||
var files = message.Attachments.Select(attachment => new MapiFileDesc
|
||||
using (var files = Unmanaged.CopyOf(GetFiles(message)))
|
||||
using (var recips = Unmanaged.CopyOf(GetRecips(message)))
|
||||
{
|
||||
// Create a MAPI structure for the entirety of the message
|
||||
var mapiMessage = new MapiMessage
|
||||
{
|
||||
subject = message.Subject,
|
||||
noteText = message.BodyText,
|
||||
recips = recips,
|
||||
recipCount = recips.Length,
|
||||
files = files,
|
||||
fileCount = files.Length
|
||||
};
|
||||
|
||||
// Determine the flags used to send the message
|
||||
var flags = MapiSendMailFlags.None;
|
||||
if (!message.AutoSend)
|
||||
{
|
||||
flags |= MapiSendMailFlags.Dialog;
|
||||
}
|
||||
if (!message.AutoSend || !message.SilentSend)
|
||||
{
|
||||
flags |= MapiSendMailFlags.LogonUI;
|
||||
}
|
||||
|
||||
// Send the message
|
||||
var returnCode = MAPISendMail(IntPtr.Zero, IntPtr.Zero, mapiMessage, flags, 0);
|
||||
|
||||
// Process the result
|
||||
if (returnCode == MapiSendMailReturnCode.UserAbort)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (returnCode != MapiSendMailReturnCode.Success)
|
||||
{
|
||||
logger.Error("Error sending email. MAPI error code: {0}", returnCode);
|
||||
errorOutput.DisplayError(MiscResources.EmailError);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static MapiRecipDesc[] GetRecips(EmailMessage message)
|
||||
{
|
||||
return message.Recipients.Select(recipient => new MapiRecipDesc
|
||||
{
|
||||
name = recipient.Name,
|
||||
address = "SMTP:" + recipient.Address,
|
||||
recipClass = recipient.Type == EmailRecipientType.Cc ? MapiRecipClass.Cc
|
||||
: recipient.Type == EmailRecipientType.Bcc ? MapiRecipClass.Bcc
|
||||
: MapiRecipClass.To
|
||||
}).ToArray();
|
||||
}
|
||||
|
||||
private static MapiFileDesc[] GetFiles(EmailMessage message)
|
||||
{
|
||||
return message.Attachments.Select(attachment => new MapiFileDesc
|
||||
{
|
||||
position = -1,
|
||||
path = attachment.FilePath,
|
||||
name = attachment.AttachmentName
|
||||
}).ToArray();
|
||||
var recips = message.Recipients.Select(recipient => new MapiRecipDesc
|
||||
{
|
||||
name = recipient.Name,
|
||||
address = "SMTP:" + recipient.Address,
|
||||
recipClass = recipient.Type == EmailRecipientType.Cc ? MapiRecipClass.Cc
|
||||
: recipient.Type == EmailRecipientType.Bcc ? MapiRecipClass.Bcc
|
||||
: MapiRecipClass.To
|
||||
}).ToArray();
|
||||
|
||||
// Create a MAPI structure for the entirety of the message
|
||||
var mapiMessage = new MapiMessage
|
||||
{
|
||||
subject = message.Subject,
|
||||
noteText = message.BodyText,
|
||||
recips = ToUnmanagedArray(recips),
|
||||
recipCount = recips.Length,
|
||||
files = ToUnmanagedArray(files),
|
||||
fileCount = files.Length
|
||||
};
|
||||
|
||||
// Determine the flags used to send the message
|
||||
var flags = MapiSendMailFlags.None;
|
||||
if (!message.AutoSend)
|
||||
{
|
||||
flags |= MapiSendMailFlags.Dialog;
|
||||
}
|
||||
if (!message.AutoSend || !message.SilentSend)
|
||||
{
|
||||
flags |= MapiSendMailFlags.LogonUI;
|
||||
}
|
||||
|
||||
// Send the message
|
||||
var returnCode = MAPISendMail(IntPtr.Zero, IntPtr.Zero, mapiMessage, flags, 0);
|
||||
|
||||
// Process the result
|
||||
if (returnCode == MapiSendMailReturnCode.UserAbort)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (returnCode != MapiSendMailReturnCode.Success)
|
||||
{
|
||||
logger.Error("Error sending email. MAPI error code: {0}", returnCode);
|
||||
errorOutput.DisplayError(MiscResources.EmailError);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates an unmanaged array and populates it with the content of the given managed array.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the array's elements.</typeparam>
|
||||
/// <param name="managedArray">The array from which to copy the content.</param>
|
||||
/// <returns>A pointer to the start of the unmanaaged array.</returns>
|
||||
private IntPtr ToUnmanagedArray<T>(IList<T> managedArray)
|
||||
{
|
||||
int elementSize = Marshal.SizeOf(typeof(T));
|
||||
|
||||
// Allocate the unmanaged array
|
||||
int arraySize = managedArray.Count * elementSize;
|
||||
IntPtr unmanagedArray = Marshal.AllocHGlobal(arraySize);
|
||||
|
||||
// Populate it from the content of the managed array
|
||||
for (int i = 0; i < managedArray.Count; ++i)
|
||||
{
|
||||
int ptrOffset = i * elementSize;
|
||||
Marshal.StructureToPtr(managedArray[i], unmanagedArray + ptrOffset, false);
|
||||
}
|
||||
|
||||
return unmanagedArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -152,9 +152,12 @@
|
||||
<Compile Include="Scan\Exceptions\ScanDriverUnknownException.cs" />
|
||||
<Compile Include="Scan\LocalizedDescriptionAttribute.cs" />
|
||||
<Compile Include="Scan\Stub\StubScanDriver.cs" />
|
||||
<Compile Include="Unmanaged.cs" />
|
||||
<Compile Include="Scan\Twain\DibUtils.cs" />
|
||||
<Compile Include="Scan\Twain\TwainApi.cs" />
|
||||
<Compile Include="StringWrapper.cs" />
|
||||
<Compile Include="UnmanagedArray.cs" />
|
||||
<Compile Include="UnmanagedBase.cs" />
|
||||
<Compile Include="WinForms\FTwainGui.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
|
60
NAPS2/Unmanaged.cs
Normal file
60
NAPS2/Unmanaged.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace NAPS2
|
||||
{
|
||||
public static class Unmanaged
|
||||
{
|
||||
public static Unmanaged<T> CopyOf<T>(T value)
|
||||
{
|
||||
return new Unmanaged<T>(value);
|
||||
}
|
||||
|
||||
public static UnmanagedArray<T> CopyOf<T>(T[] value)
|
||||
{
|
||||
return new UnmanagedArray<T>(value);
|
||||
}
|
||||
}
|
||||
|
||||
public class Unmanaged<T> : UnmanagedBase<T>
|
||||
{
|
||||
public Unmanaged()
|
||||
: this(default(T))
|
||||
{
|
||||
}
|
||||
|
||||
public Unmanaged(T value)
|
||||
{
|
||||
if (!ReferenceEquals(value, null))
|
||||
{
|
||||
Size = Marshal.SizeOf(typeof(T));
|
||||
Pointer = Marshal.AllocHGlobal(Size);
|
||||
Marshal.StructureToPtr(value, Pointer, false);
|
||||
}
|
||||
}
|
||||
|
||||
~Unmanaged()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
protected override T GetValue()
|
||||
{
|
||||
if (Pointer == IntPtr.Zero)
|
||||
{
|
||||
// T must be a reference type, so this returns null
|
||||
return default(T);
|
||||
}
|
||||
return (T)Marshal.PtrToStructure(Pointer, typeof(T));
|
||||
}
|
||||
|
||||
protected override void DestroyStructures()
|
||||
{
|
||||
Marshal.DestroyStructure(Pointer, typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
99
NAPS2/UnmanagedArray.cs
Normal file
99
NAPS2/UnmanagedArray.cs
Normal file
@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace NAPS2
|
||||
{
|
||||
public class UnmanagedArray<T> : UnmanagedBase<T[]>
|
||||
{
|
||||
public UnmanagedArray(IEnumerable<T> array)
|
||||
{
|
||||
ElementSize = Marshal.SizeOf(typeof(T));
|
||||
if (array != null)
|
||||
{
|
||||
T[] arrayVal = array as T[] ?? array.ToArray();
|
||||
|
||||
Length = arrayVal.Length;
|
||||
Size = ElementSize * Length;
|
||||
Pointer = Marshal.AllocHGlobal(Size);
|
||||
|
||||
// Populate the contents of the unmanaged array
|
||||
for (int i = 0; i < Length; ++i)
|
||||
{
|
||||
Marshal.StructureToPtr(arrayVal[i], this[i], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~UnmanagedArray()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of each element in the unmanaged array in bytes.
|
||||
/// </summary>
|
||||
public int ElementSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of elements in the unmanaged array.
|
||||
/// </summary>
|
||||
public int Length { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer offset to access the element of the unmanaged array at the given index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the element to access.</param>
|
||||
/// <returns>The offset, relative to the Pointer.</returns>
|
||||
public int Offset(int index)
|
||||
{
|
||||
if (index < 0 || index >= Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
}
|
||||
return index * ElementSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the element of the unmanaged array at the given index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the item to access.</param>
|
||||
/// <returns>The pointer to the element.</returns>
|
||||
public IntPtr PointerWithOffset(int index)
|
||||
{
|
||||
return Pointer + Offset(index);
|
||||
}
|
||||
|
||||
protected override T[] GetValue()
|
||||
{
|
||||
var result = new T[Length];
|
||||
for (int i = 0; i < Length; ++i)
|
||||
{
|
||||
result[i] = (T)Marshal.PtrToStructure(this[i], typeof(T));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override void DestroyStructures()
|
||||
{
|
||||
for (int i = 0; i < Length; ++i)
|
||||
{
|
||||
Marshal.DestroyStructure(this[i], typeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the element of the unmanaged array at the given index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the item to access.</param>
|
||||
/// <returns>The pointer to the element.</returns>
|
||||
public IntPtr this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return PointerWithOffset(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
67
NAPS2/UnmanagedBase.cs
Normal file
67
NAPS2/UnmanagedBase.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace NAPS2
|
||||
{
|
||||
public abstract class UnmanagedBase<T> : IDisposable
|
||||
{
|
||||
private bool disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of the unmanaged structure in bytes. If the structure is null, this is zero.
|
||||
/// </summary>
|
||||
public int Size { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicated whether the unmanaged structure is null.
|
||||
/// </summary>
|
||||
public bool IsNull
|
||||
{
|
||||
get
|
||||
{
|
||||
return Pointer == IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer to the unmanaged structure. If the provided value was null, this is IntPtr.Zero.
|
||||
/// </summary>
|
||||
public IntPtr Pointer { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a managed copy of the unmanaged structure.
|
||||
/// </summary>
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
throw new ObjectDisposedException("unmanaged");
|
||||
}
|
||||
return GetValue();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Pointer != IntPtr.Zero)
|
||||
{
|
||||
DestroyStructures();
|
||||
Marshal.FreeHGlobal(Pointer);
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
protected abstract T GetValue();
|
||||
|
||||
protected abstract void DestroyStructures();
|
||||
|
||||
public static implicit operator IntPtr(UnmanagedBase<T> unmanaged)
|
||||
{
|
||||
return unmanaged.Pointer;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user