如何在 C# 中以编程方式保存用户使用 CTRL-C 复制到剪贴板的 Outlook 电子邮件附件(例如 PDF)
How to programmatically save an outlook email attachment (e.g. PDF) that has been copied to clipboard by the user using CTRL-C, in C#
上下文:
当复制到剪贴板的文件例如是桌面上的文件时,我能够从剪贴板复制(使用 CTRL-C)和粘贴文件。使用以下语法这很简单:
File.Copy(Clipboard.GetFileDropList()[0], savePath)
其中Clipboard.GetFileDropList()[0] returns复制文件的路径,savePath为粘贴位置
但是,我发现如果复制的文件(使用 CTRL-C)是 Outlook 电子邮件中的文件附件,则上述语法不起作用。在这种情况下,Clipboard.ContainsFileDropList() returns false 和 Clipboard.GetFileDropList()[0] 导致以下错误消息:
“ArgumentOutOfRangeException:索引超出范围。必须为非负且小于集合的大小。参数名称:索引”
尽管按下 CTRL-V 确实成功粘贴了文件,确认文件最初已成功复制到剪贴板。
问题:
对不起,如果我错过了一些非常基本的东西。我的问题是,当从 Outlook 中使用 CTRL-C 将电子邮件附件复制到剪贴板时,如何以编程方式 paste/save 将电子邮件附件(PDF、Word 等)从剪贴板复制到文件位置。
请注意,我知道我正在尝试做的事情可以通过跳过剪贴板并以编程方式与 Outlook 交互以访问选定的电子邮件附件来解决。然而,我的目标是学习如何在不同场景下以编程方式与剪贴板交互。
你用错了DataFormat
。您始终可以通过调用 Clipboard.GetDataObject().GetFormats()
.
获取当前存在的数据格式列表
您需要使用:
"FileGroupDescriptor"
检索文件名
private static async Task<List<string>> GetAttachedFileNamesFromClipboardAsync(IDataObject clipboardData)
{
if (!clipboardData.GetDataPresent("FileGroupDescriptor"))
{
return new List<string>();
}
using (var descriptorStream = clipboardData.GetData("FileGroupDescriptor", true) as MemoryStream)
{
using (var streamReader = new StreamReader(descriptorStream))
{
var streamContent = await streamReader.ReadToEndAsync();
string[] fileNames = streamContent.Split(new[] { '[=10=]' }, StringSplitOptions.RemoveEmptyEntries);
return new List<string>(fileNames.Skip(1));
}
}
}
"FileContents"
检索原始文件内容
// Returns the attachment file content as string
private static async Task<string> GetAttachmentFromClipboardAsync(IDataObject clipboardData)
{
if (!clipboardData.GetDataPresent("FileContents"))
{
return string.Empty;
}
using (var fileContentStream = clipboardData.GetData("FileContents", true) as MemoryStream)
{
using (var streamReader = new StreamReader(fileContentStream))
{
return await streamReader.ReadToEndAsync();
}
}
}
// Returns the attachment file content as MemoryStream
private static MemoryStream GetAttachmentFromClipboard(IDataObject clipboardData)
{
if (!clipboardData.GetDataPresent("FileContents"))
{
return null;
}
return clipboardData.GetData("FileContents", true) as MemoryStream;
}
将附件保存到磁盘
由于Windows只将第一个选择的附件添加到系统剪贴板,这个解决方案只能保存一个附件。显然无法访问办公室剪贴板。
private static async Task SaveAttachmentFromClipboardToFileAsync(IDataObject clipboardData, string destinationFilePath)
{
if (!clipboardData.GetDataPresent("FileContents"))
{
return;
}
using (var attachedFileStream = clipboardData.GetData("FileContents", true) as MemoryStream)
{
using (var destinationFileStream = File.Open(destinationFilePath, FileMode.OpenOrCreate))
{
await attachedFileStream.CopyToAsync(destinationFileStream);
}
}
}
使用 Office 将附件保存到磁盘API
需要参考 Microsoft.Office.Interop.Outlook.dll。此解决方案不依赖于系统剪贴板。它只是从 Outlook 资源管理器中当前打开的邮件项目中读取选定的附件。
您仍然可以监视系统剪贴板以触发保存附件的过程。
private static void SaveSelectedAttachementsToFolder(string destinationFolderPath)
{
var outlookApplication = new Microsoft.Office.Interop.Outlook.Application();
Explorer activeOutlookExplorer = outlookApplication.ActiveExplorer();
AttachmentSelection selectedAttachments = activeOutlookExplorer.AttachmentSelection;
foreach(Attachment attachment in selectedAttachments)
{
attachment.SaveAsFile(Path.Combine(destinationFolderPath, attachment.FileName));
}
}
上下文:
当复制到剪贴板的文件例如是桌面上的文件时,我能够从剪贴板复制(使用 CTRL-C)和粘贴文件。使用以下语法这很简单:
File.Copy(Clipboard.GetFileDropList()[0], savePath)
其中Clipboard.GetFileDropList()[0] returns复制文件的路径,savePath为粘贴位置
但是,我发现如果复制的文件(使用 CTRL-C)是 Outlook 电子邮件中的文件附件,则上述语法不起作用。在这种情况下,Clipboard.ContainsFileDropList() returns false 和 Clipboard.GetFileDropList()[0] 导致以下错误消息:
“ArgumentOutOfRangeException:索引超出范围。必须为非负且小于集合的大小。参数名称:索引”
尽管按下 CTRL-V 确实成功粘贴了文件,确认文件最初已成功复制到剪贴板。
问题:
对不起,如果我错过了一些非常基本的东西。我的问题是,当从 Outlook 中使用 CTRL-C 将电子邮件附件复制到剪贴板时,如何以编程方式 paste/save 将电子邮件附件(PDF、Word 等)从剪贴板复制到文件位置。
请注意,我知道我正在尝试做的事情可以通过跳过剪贴板并以编程方式与 Outlook 交互以访问选定的电子邮件附件来解决。然而,我的目标是学习如何在不同场景下以编程方式与剪贴板交互。
你用错了DataFormat
。您始终可以通过调用 Clipboard.GetDataObject().GetFormats()
.
您需要使用:
"FileGroupDescriptor"
检索文件名
private static async Task<List<string>> GetAttachedFileNamesFromClipboardAsync(IDataObject clipboardData)
{
if (!clipboardData.GetDataPresent("FileGroupDescriptor"))
{
return new List<string>();
}
using (var descriptorStream = clipboardData.GetData("FileGroupDescriptor", true) as MemoryStream)
{
using (var streamReader = new StreamReader(descriptorStream))
{
var streamContent = await streamReader.ReadToEndAsync();
string[] fileNames = streamContent.Split(new[] { '[=10=]' }, StringSplitOptions.RemoveEmptyEntries);
return new List<string>(fileNames.Skip(1));
}
}
}
"FileContents"
检索原始文件内容
// Returns the attachment file content as string
private static async Task<string> GetAttachmentFromClipboardAsync(IDataObject clipboardData)
{
if (!clipboardData.GetDataPresent("FileContents"))
{
return string.Empty;
}
using (var fileContentStream = clipboardData.GetData("FileContents", true) as MemoryStream)
{
using (var streamReader = new StreamReader(fileContentStream))
{
return await streamReader.ReadToEndAsync();
}
}
}
// Returns the attachment file content as MemoryStream
private static MemoryStream GetAttachmentFromClipboard(IDataObject clipboardData)
{
if (!clipboardData.GetDataPresent("FileContents"))
{
return null;
}
return clipboardData.GetData("FileContents", true) as MemoryStream;
}
将附件保存到磁盘
由于Windows只将第一个选择的附件添加到系统剪贴板,这个解决方案只能保存一个附件。显然无法访问办公室剪贴板。
private static async Task SaveAttachmentFromClipboardToFileAsync(IDataObject clipboardData, string destinationFilePath)
{
if (!clipboardData.GetDataPresent("FileContents"))
{
return;
}
using (var attachedFileStream = clipboardData.GetData("FileContents", true) as MemoryStream)
{
using (var destinationFileStream = File.Open(destinationFilePath, FileMode.OpenOrCreate))
{
await attachedFileStream.CopyToAsync(destinationFileStream);
}
}
}
使用 Office 将附件保存到磁盘API
需要参考 Microsoft.Office.Interop.Outlook.dll。此解决方案不依赖于系统剪贴板。它只是从 Outlook 资源管理器中当前打开的邮件项目中读取选定的附件。
您仍然可以监视系统剪贴板以触发保存附件的过程。
private static void SaveSelectedAttachementsToFolder(string destinationFolderPath)
{
var outlookApplication = new Microsoft.Office.Interop.Outlook.Application();
Explorer activeOutlookExplorer = outlookApplication.ActiveExplorer();
AttachmentSelection selectedAttachments = activeOutlookExplorer.AttachmentSelection;
foreach(Attachment attachment in selectedAttachments)
{
attachment.SaveAsFile(Path.Combine(destinationFolderPath, attachment.FileName));
}
}