我遇到下面的MAPI类问题。(原始来源http://www.codeproject.com/KB/IP/SendFileToNET.aspx)
当用户尝试使用SendMailPopup方法发送电子邮件时,电子邮件程序会正确打开,但在邮件窗口关闭后,程序有时会崩溃并显示以下消息:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
我怀疑该错误是由清理方法引起的,我设法通过使用Int64存储指针而不是原始版本中使用的int来减少崩溃的频率。然而,我的.net不安全编程知识相当有限,所以有人可以帮助我找出造成其他崩溃的原因
更新:显然清理方法不会导致异常,因为程序即使被注释掉也会崩溃,因此唯一可能的原因是MAPI32.ddl sendmail方法。作为参数传递给它的指针可能有问题。
用户正在使用具有64位处理器和32位winxp的系统。
更新
此线程中提供的解决方案确实降低了崩溃的频率,但并未完全解决问题。 100%工作的唯一解决方案是使用实际进行MAPI调用的c ++编写一个小型控制台应用程序。我们的.NET应用程序通过启动控制台应用程序并使用参数将数据传递给它来与mapi连接。
public class MAPI
{
public bool AddRecipientTo(string email)
{
return AddRecipient(email, HowTo.MAPI_TO);
}
public bool AddRecipientCC(string email)
{
return AddRecipient(email, HowTo.MAPI_CC);
}
public bool AddRecipientBCC(string email)
{
return AddRecipient(email, HowTo.MAPI_BCC);
}
public void AddAttachment(string strAttachmentFileName)
{
m_attachments.Add(strAttachmentFileName);
}
public int SendMailPopup(string strSubject, string strBody)
{
return SendMail(strSubject, strBody, MAPI_LOGON_UI | MAPI_DIALOG);
}
public int SendMailDirect(string strSubject, string strBody)
{
return SendMail(strSubject, strBody, MAPI_LOGON_UI);
}
[DllImport("MAPI32.DLL")]
static extern int MAPISendMail(IntPtr sess, IntPtr hwnd, MapiMessage message, int flg, int rsv);
int SendMail(string strSubject, string strBody, int how)
{
MapiMessage msg = new MapiMessage();
msg.subject = strSubject;
msg.noteText = strBody;
msg.recips = GetRecipients(out msg.recipCount);
msg.files = GetAttachments(out msg.fileCount);
m_lastError = MAPISendMail(new IntPtr(0L), new IntPtr(0L), msg, how, 0);
if (m_lastError > 1)
MessageBox.Show("MAPISendMail failed! " + GetLastError(), "MAPISendMail");
Cleanup(ref msg);
return m_lastError;
}
bool AddRecipient(string email, HowTo howTo)
{
if (!String.IsNullOrEmpty(email))
{
MapiRecipDesc recipient = new MapiRecipDesc();
recipient.recipClass = (int)howTo;
recipient.name = email;
m_recipients.Add(recipient);
return true;
}
else
{
return false;
}
}
IntPtr GetRecipients(out int recipCount)
{
recipCount = 0;
if (m_recipients.Count == 0)
return IntPtr.Zero;
int size = Marshal.SizeOf(typeof(MapiRecipDesc));
IntPtr intPtr = Marshal.AllocHGlobal(m_recipients.Count * size);
int ptr = (int)intPtr;
foreach (MapiRecipDesc mapiDesc in m_recipients)
{
Marshal.StructureToPtr(mapiDesc, (IntPtr)ptr, false);
ptr += size;
}
recipCount = m_recipients.Count;
return intPtr;
}
IntPtr GetAttachments(out int fileCount)
{
fileCount = 0;
if (m_attachments == null)
return IntPtr.Zero;
if ((m_attachments.Count <= 0) || (m_attachments.Count > maxAttachments))
return IntPtr.Zero;
int size = Marshal.SizeOf(typeof(MapiFileDesc));
IntPtr intPtr = Marshal.AllocHGlobal(m_attachments.Count * size);
MapiFileDesc mapiFileDesc = new MapiFileDesc();
mapiFileDesc.position = -1;
int ptr = (int)intPtr;
foreach (string strAttachment in m_attachments)
{
mapiFileDesc.name = Path.GetFileName(strAttachment);
mapiFileDesc.path = strAttachment;
Marshal.StructureToPtr(mapiFileDesc, (IntPtr)ptr, false);
ptr += size;
}
fileCount = m_attachments.Count;
return intPtr;
}
void Cleanup(ref MapiMessage msg)
{
try
{
int size = Marshal.SizeOf(typeof(MapiRecipDesc));
Int64 ptr = 0;
if (msg.recips != IntPtr.Zero)
{
ptr = msg.recips.ToInt64();
for (int i = 0; i < msg.recipCount; i++)
{
Marshal.DestroyStructure((IntPtr)ptr, typeof(MapiRecipDesc));
ptr += size;
}
Marshal.FreeHGlobal(msg.recips);
}
if (msg.files != IntPtr.Zero)
{
size = Marshal.SizeOf(typeof(MapiFileDesc));
ptr = msg.files.ToInt64();
for (int i = 0; i < msg.fileCount; i++)
{
Marshal.DestroyStructure((IntPtr)ptr, typeof(MapiFileDesc));
ptr += size;
}
Marshal.FreeHGlobal(msg.files);
}
m_recipients.Clear();
m_attachments.Clear();
}
catch (Exception e)
{
SmtpSender errorSender = new SmtpSender();
errorSender.SendAutomaticError(e.StackTrace + e.Message, "Virhe mapi sähköpostin lähetyksessä" + MySession.ProjectName + " Käyttäjä:" + MySession.LoginName);
}
}
public string GetLastError()
{
if (m_lastError <= 26)
return errors[m_lastError];
return "MAPI error [" + m_lastError.ToString() + "]";
}
readonly string[] errors = new string[] {
"OK [0]", "User abort [1]", "Yleinen virhe sähköpostin lähettämisessä [2]", "MAPI login failure [3]",
"Disk full [4]", "Insufficient memory [5]", "Access denied [6]", "-unknown- [7]",
"Too many sessions [8]", "Too many files were specified [9]", "Too many recipients were specified [10]", "A specified attachment was not found [11]",
"Attachment open failure [12]", "Attachment write failure [13]", "Unknown recipient [14]", "Bad recipient type [15]",
"No messages [16]", "Invalid message [17]", "Text too large [18]", "Invalid session [19]",
"Type not supported [20]", "A recipient was specified ambiguously [21]", "Message in use [22]", "Network failure [23]",
"Invalid edit fields [24]", "Asiakkaalle ei ole määritetty sähköpostiosoitetta.", "Not supported [26]"
};
List<MapiRecipDesc> m_recipients = new List<MapiRecipDesc>();
List<string> m_attachments = new List<string>();
int m_lastError = 0;
const int MAPI_LOGON_UI = 0x00000001;
const int MAPI_DIALOG = 0x00000008;
const int maxAttachments = 20;
enum HowTo { MAPI_ORIG = 0, MAPI_TO, MAPI_CC, MAPI_BCC };
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MapiMessage
{
public int reserved;
public string subject;
public string noteText;
public string messageType;
public string dateReceived;
public string conversationID;
public int flags;
public IntPtr originator;
public int recipCount;
public IntPtr recips;
public int fileCount;
public IntPtr files;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MapiFileDesc
{
public int reserved;
public int flags;
public int position;
public string path;
public string name;
public IntPtr type;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MapiRecipDesc
{
public int reserved;
public int recipClass;
public string name;
public string address;
public int eIDSize;
public IntPtr entryID;
}
以下是异常的堆栈跟踪。它部分是芬兰语,但您仍然可以从中看到方法名称。
kohteessa System.Windows.Forms.UnsafeNativeMethods.DispatchMessageA(MSG& msg)
kohteessa
System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32
dwComponentID, Int32 reason, Int32 pvLoopData)
kohteessa
System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason,
ApplicationContext context)
kohteessa System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32
reason, ApplicationContext context)
kohteessa System.Windows.Forms.Application.Run(Form mainForm)
答案 0 :(得分:2)
您没有一直使用Int64(GetRecipients和GetAttachments也是如此)。我怀疑是否存在问题,但我没有详细介绍。以下是所需的更改。请注意,我使用了一种稍微不同的递增IntPtr的方法,这种方法不易出错。
改为使用Joerg的建议来增加文件附件路径的缓冲区大小。
public class MAPI
{
public bool AddRecipientTo(string email)
{
return AddRecipient(email, HowTo.MAPI_TO);
}
public bool AddRecipientCC(string email)
{
return AddRecipient(email, HowTo.MAPI_CC);
}
public bool AddRecipientBCC(string email)
{
return AddRecipient(email, HowTo.MAPI_BCC);
}
public void AddAttachment(string strAttachmentFileName)
{
m_attachments.Add(strAttachmentFileName);
}
public int SendMailPopup(string strSubject, string strBody)
{
return SendMail(strSubject, strBody, MAPI_LOGON_UI | MAPI_DIALOG);
}
public int SendMailDirect(string strSubject, string strBody)
{
return SendMail(strSubject, strBody, MAPI_LOGON_UI);
}
int SendMail(string strSubject, string strBody, int how)
{
MapiMessage msg = new MapiMessage();
msg.subject = strSubject;
msg.noteText = strBody;
msg.recips = GetRecipients(out msg.recipCount);
msg.files = GetAttachments(out msg.fileCount);
m_lastError = MAPISendMail(new IntPtr(0L), new IntPtr(0L), msg, how, 0);
if (m_lastError > 1)
MessageBox.Show("MAPISendMail failed! " + GetLastError(), "MAPISendMail");
Cleanup(ref msg);
return m_lastError;
}
bool AddRecipient(string email, HowTo howTo)
{
if (!String.IsNullOrEmpty(email))
{
MapiRecipDesc recipient = new MapiRecipDesc();
recipient.recipClass = (int)howTo;
recipient.name = email;
m_recipients.Add(recipient);
return true;
}
else
{
return false;
}
}
IntPtr GetRecipients(out int recipCount)
{
recipCount = 0;
if (m_recipients.Count == 0)
return IntPtr.Zero;
int size = Marshal.SizeOf(typeof(MapiRecipDesc));
IntPtr blockPtr = Marshal.AllocHGlobal(m_recipients.Count * size);
IntPtr currentPtr = blockPtr;
foreach (MapiRecipDesc mapiDesc in m_recipients)
{
Marshal.StructureToPtr(mapiDesc, currentPtr, false);
currentPtr = (IntPtr)((long)currentPtr + size);
}
recipCount = m_recipients.Count;
return blockPtr;
}
IntPtr GetAttachments(out int fileCount)
{
fileCount = 0;
if (m_attachments == null)
return IntPtr.Zero;
if ((m_attachments.Count <= 0) || (m_attachments.Count > maxAttachments))
return IntPtr.Zero;
int size = Marshal.SizeOf(typeof(MapiFileDesc));
IntPtr blockPtr = Marshal.AllocHGlobal(m_attachments.Count * size);
IntPtr currentPtr = blockPtr;
MapiFileDesc mapiFileDesc = new MapiFileDesc();
mapiFileDesc.position = -1;
foreach (string strAttachment in m_attachments)
{
mapiFileDesc.name = Path.GetFileName(strAttachment);
mapiFileDesc.path = Marshal.AllocHGlobal(MAX_PATH);
CopyStringAnsi(mapiFileDesc.path, strAttachment);
Marshal.StructureToPtr(mapiFileDesc, currentPtr, false);
currentPtr = (IntPtr)((long)currentPtr + size);
}
fileCount = m_attachments.Count;
return blockPtr;
}
void Cleanup(ref MapiMessage msg)
{
try
{
if (msg.recips != IntPtr.Zero)
{
IntPtr currentPtr = msg.recips;
int size = Marshal.SizeOf(typeof(MapiRecipDesc));
for (int i = 0; i < msg.recipCount; i++)
{
Marshal.DestroyStructure(currentPtr, typeof(MapiRecipDesc));
currentPtr = (IntPtr)((long)currentPtr + size);
}
Marshal.FreeHGlobal(msg.recips);
}
if (msg.files != IntPtr.Zero)
{
IntPtr currentPtr = msg.files;
int size = Marshal.SizeOf(typeof(MapiFileDesc));
for (int i = 0; i < msg.fileCount; i++)
{
Marshal.DestroyStructure(currentPtr, typeof(MapiFileDesc));
currentPtr = (IntPtr)((long)currentPtr + size);
}
Marshal.FreeHGlobal(msg.files);
}
m_recipients.Clear();
m_attachments.Clear();
}
catch (Exception e)
{
SmtpSender errorSender = new SmtpSender();
errorSender.SendAutomaticError(e.StackTrace + e.Message, "Virhe mapi sähköpostin lähetyksessä" + MySession.ProjectName + " Käyttäjä:" + MySession.LoginName);
}
}
public string GetLastError()
{
if (m_lastError <= 26)
return errors[m_lastError];
return "MAPI error [" + m_lastError.ToString() + "]";
}
readonly string[] errors = new string[] {
"OK [0]", "User abort [1]", "Yleinen virhe sähköpostin lähettämisessä [2]", "MAPI login failure [3]",
"Disk full [4]", "Insufficient memory [5]", "Access denied [6]", "-unknown- [7]",
"Too many sessions [8]", "Too many files were specified [9]", "Too many recipients were specified [10]", "A specified attachment was not found [11]",
"Attachment open failure [12]", "Attachment write failure [13]", "Unknown recipient [14]", "Bad recipient type [15]",
"No messages [16]", "Invalid message [17]", "Text too large [18]", "Invalid session [19]",
"Type not supported [20]", "A recipient was specified ambiguously [21]", "Message in use [22]", "Network failure [23]",
"Invalid edit fields [24]", "Asiakkaalle ei ole määritetty sähköpostiosoitetta.", "Not supported [26]"
};
List<MapiRecipDesc> m_recipients = new List<MapiRecipDesc>();
List<string> m_attachments = new List<string>();
int m_lastError = 0;
const int MAPI_LOGON_UI = 0x00000001;
const int MAPI_DIALOG = 0x00000008;
const int maxAttachments = 20;
const int MAX_PATH = 256;
enum HowTo { MAPI_ORIG = 0, MAPI_TO, MAPI_CC, MAPI_BCC };
[DllImport("MAPI32.DLL")]
static extern int MAPISendMail(IntPtr sess, IntPtr hwnd, MapiMessage message, int flg, int rsv);
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", CharSet = CharSet.Ansi)]
static extern void RtlMoveStringAnsi(IntPtr pdst, string psrc, IntPtr sizetcb);
private void CopyStringAnsi(IntPtr intPtr, string str)
{
int length = (str.Length + 1) * Marshal.SystemMaxDBCSCharSize;
RtlMoveStringAnsi(intPtr, str, (IntPtr)length);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
class MapiMessage
{
public int reserved;
public string subject;
public string noteText;
public string messageType;
public string dateReceived;
public string conversationID;
public int flags;
public IntPtr originator;
public int recipCount;
public IntPtr recips;
public int fileCount;
public IntPtr files;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
class MapiFileDesc
{
public int reserved;
public int flags;
public int position;
public IntPtr path;
public string name;
public IntPtr type;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
class MapiRecipDesc
{
public int reserved;
public int recipClass;
public string name;
public string address;
public int eIDSize;
public IntPtr entryID;
}
}
答案 1 :(得分:-1)
我注意到,调用MAPISendMail包含msg.files中附件的完整文件名的缓冲区将被mapi服务提供程序(?)覆盖。 提供更长的缓冲区已经解决了这些问题。