我的Office Outlook插件开发之旅(二)
下面将完成的展示,使用MAPI接口操作Outlook完成通讯录更新。
using Microsoft.Office.Interop.Outlook;
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AddressBookTool2
{
class Program
{
static void Main(string[] args)
{
Process current = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName(current.ProcessName);
bool isAlreadyRunning = false;
foreach (Process process in processes)
{
if (process.Id != current.Id)
{
// 检查路径以确保是同一个可执行文件的另一个实例
if (process.MainModule.FileName == current.MainModule.FileName)
{
isAlreadyRunning = true;
break;
}
}
}
if (isAlreadyRunning)
{
}
else
{
while (true)
{
Action();
Thread.Sleep(1000 * 60 * 15);
}
}
}
const string conn = "Server=192.168.100.99;Database=帆软报表;uid=sa;pwd=dsc";
/// <summary>
/// 动作
/// </summary>
public static void Action()
{
string ipAddress = "192.168.100.99";
Ping ping = new Ping();
PingReply pingReply = ping.Send(ipAddress);
if (pingReply.Status != IPStatus.Success) return;
UpdateClient();
const string sql = "SELECT Guid,应用名称,版本,备注 FROM 帆软报表.dbo.版本控制 WHERE 应用名称 = N'集团通讯录'";
List<Dictionary<string, object>> list = SelectList(sql, conn);
if (list.Count > 0)
{
string version = list[0]["版本"].ToString();
if (string.IsNullOrEmpty(version)) return;
string path = $"{Environment.CurrentDirectory}\\Version.conf";
if (!File.Exists(path))
{
UpdateAddressBook();
File.WriteAllText(path, version.Trim());
}
else
{
string content = "";
foreach (string item in File.ReadLines(path))
{
content += item;
}
if (content.Trim() != version.Trim())
{
UpdateAddressBook();
File.WriteAllText(path, version.Trim());
}
}
}
}
/// <summary>
/// 更新客户端
/// </summary>
public static void UpdateClient()
{
string ipAddress = "192.168.100.18";
Ping ping = new Ping();
PingReply pingReply = ping.Send(ipAddress);
if (pingReply.Status != IPStatus.Success) return;
const string sql = "SELECT Guid,应用名称,版本,备注 FROM 帆软报表.dbo.版本控制 WHERE 应用名称 = N'集团通讯录客户端'";
List<Dictionary<string, object>> list = SelectList(sql, conn);
if (list.Count > 0)
{
string version = list[0]["版本"].ToString();
string path = $"{Environment.CurrentDirectory}\\ClientVersion.conf";
if (!File.Exists(path))
{
File.WriteAllText(path, version.Trim());
UpdateClientAction();
Environment.Exit(0);
}
else
{
string content = "";
foreach (string item in File.ReadLines(path))
{
content += item;
}
if (content.Trim() != version.Trim())
{
File.WriteAllText(path, version.Trim());
UpdateClientAction();
Environment.Exit(0);
}
}
}
}
/// <summary>
/// 更新客户端行动
/// </summary>
public static void UpdateClientAction()
{
string batPath = $"{Environment.CurrentDirectory}\\temp.bat";
string cmdText = $"@echo off{Environment.NewLine}timeout /t 3 /nobreak >nul{Environment.NewLine}start {Environment.CurrentDirectory}\\updater.bat \"{Environment.CurrentDirectory}\" \"{Environment.CurrentDirectory}\\AddressBookTool2.exe\"{Environment.NewLine}eixt";
File.WriteAllText(batPath, cmdText, Encoding.UTF8);
ProcessStartInfo psi = new ProcessStartInfo()
{
FileName = batPath,
CreateNoWindow = true,
RedirectStandardError = true,
UseShellExecute = false
};
using (Process process = new Process())
{
process.StartInfo = psi;
process.Start();
}
}
/// <summary>
/// 更新通讯录
/// </summary>
public static void UpdateAddressBook()
{
const string sql = "SELECT 邮箱地址 AS 'emailAddress',姓氏 AS 'firstName',姓名 AS 'lastName', 群组 AS 'group' FROM [帆软报表].[dbo].[集团邮箱通讯录] WHERE 邮箱地址 IS NOT NULL AND (姓氏 IS NOT NULL OR 姓名 IS NOT NULL)";
List<Dictionary<string, object>> list = SelectList(sql, conn);
List<Contact> contacts = new List<Contact>();
list.ForEach(it =>
{
string firstName = it["firstName"].ToString()?.Trim();
string lastName = it["lastName"].ToString()?.Trim();
string emailAddress = it["emailAddress"].ToString()?.Trim();
string group = it["group"].ToString()?.Trim();
int index = group.IndexOf(",");
if (index >= 0)
{
string[] groupList = group.Split(',');
groupList.ToList().ForEach(g =>
{
Contact contact = new Contact()
{
FirstName = firstName,
LastName = lastName,
EmailAddress = emailAddress,
Group = g
};
contacts.Add(contact);
});
}
else
{
Contact contact = new Contact()
{
FirstName = firstName,
LastName = lastName,
EmailAddress = emailAddress,
Group = group
};
contacts.Add(contact);
}
});
if (contacts.Count == 0) return;
GeneractionStroe(contacts, false, "通讯录", true, "汤石集团通讯录");
GeneractionStroe(contacts, false, "通讯录", false, "湯石集團通訊錄");
}
public static void GeneractionStroe(List<Contact> contacts, bool order, string fileName, bool CH_zh, string addressBookName)
{
// 繁简转换
contacts = ContactsLang(contacts, CH_zh);
// 创建Store,也就是PST档
Microsoft.Office.Interop.Outlook.Application outlookApp = new Microsoft.Office.Interop.Outlook.Application();
NameSpace session = outlookApp.GetNamespace("MAPI");
string path = $"{Environment.CurrentDirectory}\\{fileName}.pst";
Store store = GetStore(session, path, 5);
MAPIFolder contactFolder = GetAddressBookFolder(store, addressBookName);
// 移除就的联系人
int count = contactFolder.Items.Count;
for (int i = 0; i < count; i++)
{
contactFolder.Items.Remove(1);
}
MAPIFolder deteledItems = store.GetDefaultFolder(OlDefaultFolders.olFolderDeletedItems);
count = deteledItems.Items.Count;
for (int i = 0; i < count; i++)
{
deteledItems.Items.Remove(1);
}
// 新增联系人
contacts.Distinct(new ContactEqualityComparer()).ToList().ForEach(contact =>
{
if (!string.IsNullOrEmpty(contact.FirstName) && !string.IsNullOrEmpty(contact.LastName))
{
ContactItem newContact = contactFolder.Items.Add(OlItemType.olContactItem);
OrderContactItem(contact.FirstName, contact.LastName, order, newContact);
newContact.Email1Address = contact.EmailAddress;
newContact.Save();
System.Runtime.InteropServices.Marshal.ReleaseComObject(newContact);
newContact = null;
}
});
// 新增群组
contacts.GroupBy(it => it.Group).Select(it => new
ContactGroup()
{
Group = it.Key,
Contacts = it.ToList()
}).ToList().ForEach(it =>
{
if (!string.IsNullOrEmpty(it.Group))
{
DistListItem dist = contactFolder.Items.Add(OlItemType.olDistributionListItem);
it.Contacts.ForEach(contact =>
{
Recipient recipient = outlookApp.Session.CreateRecipient(contact.FirstName + contact.LastName + "(" + contact.EmailAddress + ")");
if (recipient.Resolve())
{
dist.AddMember(recipient);
}
});
dist.DLName = it.Group;
dist.Save();
// 确保所有项已从新创建的PST中释放
System.Runtime.InteropServices.Marshal.ReleaseComObject(dist);
dist = null;
}
});
// 清理Outlook对象,避免内存泄漏
System.Runtime.InteropServices.Marshal.ReleaseComObject(session);
System.Runtime.InteropServices.Marshal.ReleaseComObject(outlookApp);
outlookApp = null;
session = null;
}
/// <summary>
/// 获取Store
/// </summary>
/// <param name="session">命名控件</param>
/// <param name="target">目标pst档</param>
/// <param name="count">最大递归次数</param>
/// <returns></returns>
public static Store GetStore(NameSpace session, string target, int count)
{
Store store = null;
foreach (Store it in session.Stores)
{
if (it.FilePath == target)
{
store = it;
break;
}
}
if (store == null && count > 0)
{
session.AddStore(target);
return GetStore(session, target, count--);
}
return store;
}
/// <summary>
/// 获取联系人文件夹
/// </summary>
/// <param name="store">Store实例</param>
/// <param name="name">文件夹名称</param>
/// <returns></returns>
public static MAPIFolder GetAddressBookFolder(Store store, string name)
{
MAPIFolder folder = null;
Folders folders = store.GetDefaultFolder(OlDefaultFolders.olFolderContacts).Folders;
foreach (MAPIFolder item in folders)
{
if (item.Name == name)
{
folder = item;
break;
}
}
if (folder == null)
{
folder = store.GetDefaultFolder(OlDefaultFolders.olFolderContacts).Folders.Add(name);
folder.ShowAsOutlookAB = true;// 设置成联系人
}
return folder;
}
/// <summary>
/// 转换语言,简体转繁体
/// </summary>
/// <param name="contacts">联系人信息集合</param>
/// <param name="lang">是否转繁体</param>
/// <returns></returns>
public static List<Contact> ContactsLang(List<Contact> contacts, bool lang)
{
List<Contact> result = new List<Contact>();
if (lang)
{
return contacts;
}
else
{
contacts.ForEach(it =>
{
it.FirstName = Strings.StrConv(it.FirstName, VbStrConv.TraditionalChinese);
it.LastName = Strings.StrConv(it.LastName, VbStrConv.TraditionalChinese);
it.Group = Strings.StrConv(it.Group, VbStrConv.TraditionalChinese);
result.Add(it);
});
}
return result;
}
/// <summary>
/// 转换联系人的姓与名位置
/// </summary>
/// <param name="firstName">姓氏</param>
/// <param name="lastName">名字</param>
/// <param name="order">顺序</param>
/// <param name="contact">联系人实例</param>
public static void OrderContactItem(string firstName, string lastName, bool order, ContactItem contact)
{
if (order)
{
contact.FirstName = firstName;
contact.LastName = lastName;
}
else
{
contact.LastName = firstName;
contact.FirstName = lastName;
}
}
public struct Contact
{
public string FirstName;
public string LastName;
public string EmailAddress;
public string Group;
}
public struct ContactGroup
{
public string Group;
public List<Contact> Contacts;
}
public class ContactEqualityComparer : IEqualityComparer<Contact>
{
public bool Equals(Contact x, Contact y)
{
return x.FirstName == y.FirstName && x.LastName == y.LastName && x.EmailAddress == y.EmailAddress;
}
public int GetHashCode(Contact obj)
{
return obj.FirstName.GetHashCode() ^ obj.LastName.GetHashCode() ^ obj.EmailAddress.GetHashCode();
}
}
/// <summary>
/// 执行select语句
/// </summary>
/// <param name="sql">select语句</param>
/// <param name="connection">数据库链接语句</param>
/// <returns>List的结果</returns>
/// <exception cref="System.Exception"></exception>
public static List<Dictionary<string, object>> SelectList(string sql, string connection)
{
if (sql == null || connection == null || sql == "" || connection == "")
throw new System.Exception("未传入SQL语句或者Connection链接语句");
List<Dictionary<string, object>> list = new List<Dictionary<string, object>>();
SqlConnection conn = new SqlConnection(connection);
SqlCommand cmd = new SqlCommand(sql, conn);
try
{
conn.Open();
SqlDataReader sqlDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (sqlDataReader.Read())
{
int count = sqlDataReader.FieldCount;
if (count <= 0) continue;
Dictionary<string, object> map = new Dictionary<string, object>();
for (int i = 0; i < count; i++)
{
string name = sqlDataReader.GetName(i);
object value = sqlDataReader.GetValue(i);
map.Add(name, value);
}
list.Add(map);
}
conn.Close();
return list;
}
catch (System.Exception)
{
conn.Close();
return null;
}
}
}
}
updater.bat 更新用的
@echo off
set "source=\\192.168.100.18\mis\21.email-plugin\AddressBookTool"
:: 使用Robocopy复制除了version.conf之外的所有文件和目录
robocopy "%source%" "%1%" /E /XD Version.conf ClientVersion.conf
:: 检查并有条件地复制version.conf
if not exist "%1%\Version.conf" (
copy /Y "%source%\Version.conf" "%1%"
)
:: 启动程序
cd /d "%1%"
start "" "%2%"
exit
setup.bat 安装用
@echo off
set "REG_PATH=HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\AddressBookTool"
set "client_bat=C:\TONS\AddressBookTool\start.bat"
set "folder=C:\TONS\AddressBookTool"
set "source=\\192.168.100.18\mis\21.email-plugin\AddressBookTool"
:: 使用Robocopy复制除了version.conf之外的所有文件和目录
robocopy "%source%" "%folder%" /E /XD version.conf
:: 检查并有条件地复制version.conf
if not exist "%folder%\version.conf" (
copy /Y "%source%\version.conf" "%folder%"
)
:: 配置环境变量
reg query %REG_PATH% >nul 2>nul&&goto A||goto B
:B
reg add "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /v AddressBookTool /t REG_SZ /d %client_bat% /f
:A
start %client_bat%
exit
start.bat 启动用
@echo off
set "environment=C:\TONS\AddressBookTool"
set "client=C:\TONS\AddressBookTool\AddressBookTool2.exe"
:: 启动程序
cd /d "%environment%"
start "" "%client%"
exit