之前写了个简单的批量处理word、excel文档内容替换的程序给朋友用,在这里。
后来朋友反映说是当碰到加密了的word时会跳出输入密码的对话框,因为程序是用单线程写的,所以在这种情况下就会卡在那里不处理接下去的事务了。
一开始我也觉得这个应该挺简单的,事先判断一下有没有密码就行了。而且的确有Word.Document.HasPassword属性。不幸的是这个document一定要打开后才能获取到这个属性。
之后想着,在打开word文档的时候有个变量就是传密码的“PasswordDocument”,没密码的文档应该不会去用到这个变量,有密码的文档在尝试用这个变量之后,应该会报错然后被try...catch块捕捉到,然后返回就可以了。
Word.Application _myWord = new Word.Application();
object missing = Type.Missing;
object filepath = filePath as object;
object testPassword = "123";
object visible = false;
Word.Document myDoc = null;
try
{
myDoc = _myWord.Documents.Open(ref filepath, ref missing, ref missing, ref missing, ref testPassword , ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing,
ref visible, ref missing, ref missing, ref missing, ref missing);
}
catch (COMException ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine();
return;
}
但是不幸的是那个要求输密码的对话框还是跳了出来,只有在点了取消之后才会抛出密码错误异常然后被捕捉到。
(之后在修改excel部分程序的时候,发现这招对excel有用)。
在网上搜了些文章,好像VB中这招是可行的,可我只会c#。之后又看到一篇文章(现在找不到在哪了,要不也不会找了那么久),说是可以把那个密码框关掉。
于是我尝试了一下,在弹出密码框时候关闭密码框也是会抛出密码错误的异常的。接下去目标就明确了,调用API获取密码框的句柄,然后尝试关闭。
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll ", EntryPoint = "FindWindowEx", SetLastError = true)]
internal static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName);
[DllImport("User32.dll ", EntryPoint = "SendMessage", SetLastError = true)]
internal static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, string lParam);
先加几个API方法,第一、第二个可以通过类名和标题来获取所需窗口的句柄,第三个方法可以通过发送消息来关闭窗口。
之后需要的是加个:
private System.Windows.Forms.Timer timer1;
然后在替换开始的时候设置:
timer1.Enabled = true;
并且在Tick事件中进行捕捉:
private void timer1_Tick(object sender, EventArgs e)
{
IntPtr passworHwnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, null, "密码");
if (passworHwnd != IntPtr.Zero)
{
//0x0010是 WM_CLOSE的值
SendMessage(passworHwnd, 0x0010, IntPtr.Zero, "0");
}
}
对于中文版的office,那个窗口的标题就是“密码”,要是有别的窗口也叫“密码”的话,那就不幸了。那就再增加个类名也可以,这里有个小工具来获取类名(网上也很多)。至于具体想怎么找就根据自己不同的需要扩展吧。反正思路就是这样,在成功获取句柄之后我们就能通过发送关闭窗口指令的消息来关闭窗口了。
测试证明成功关闭并且捕获到异常,这样就可以顺利的跳过有密码的word文档处理接下去的事务了。有个不足之处就是窗口会闪一下。
如果各位有更好的办法的话请大家指教。源码全部在这里。
对于office开发VB的确是好用多了,因为可以录制宏,当然c#也能照着写。