这是我想要做的。我没有成功地进行了广泛的搜索。
我有两个 spreadhseets 。
第一册
Book2的密码为open = "green"
第1册在单元格A1 ='C:\[Book2.xlsm]Sheet1'!A1
我希望我的宏将Book1单元格中的公式 A1复制到A2 ,以便从受密码保护的Book2中获取ref A2。问题是它不断弹出来询问密码。我想在不打开Book2的情况下自动输入该密码。粘贴新公式后,我无法立即通过密码弹出。
任何帮助非常感谢。代码I到目前为止使用:
Sub Macro1()
Range("A1").Select
Selection.Copy
Range("A2").Select
ActiveSheet.Paste
UpDateLinks
End Sub
Sub UpDateLinks()
Const PWord As String = "green"
Dim xlLinks
Dim i As Integer
xlLinks = ThisWorkbook.LinkSources(xlExcelLinks)
If Not IsEmpty(xlLinks) Then
For i = 1 To UBound(xlLinks)
SendKeys PWord & "{Enter}"
ThisWorkbook.UpdateLink Name:=xlLinks(i)
Next i
End If
End Sub
答案 0 :(得分:0)
您可以在单个工作簿中实现此目的。将数据表的可见属性设置为xlVeryHidden,然后为VBA设置密码。然后无法从用户界面取消隐藏工作表
答案 1 :(得分:0)
问题在于时间问题:在您致电UpdateLink
(或编写带有链接的公式)之前,密码对话框不会显示,到那时您的SendKeys
已经很久了,不见了。另一方面,一旦密码对话框打开,VBA就会卡在该行上,并且Excel有效挂起,直到有人或某事输入密码并单击确定(或取消,导致UpdateLink出错)。
因此,在调用UpdateLink(或编写公式)之前,需要在Excel线程外部运行某些东西(vbscript,exe或其他)。您将希望传递Excel实例句柄的“某些内容”以及所需的密码。我写了一个可执行文件,导致这个VBA调用exe然后调用UpdateLink:
Public Sub UpdateLinksIntoPasswordProtectedWorkbook()
Const pwd As String = "WhateverThePwdIs"
'call the EXE passing the hWnd and the pwd
Shell """c:\SomeFolders\SpyUpdateLinkPwdDialog.exe"" " & Application.hWnd & " " & pwd
'set the new formula
Range("A2").Formula ="'C:\[Book2.xlsm]Sheet1'!A1"
'or alternatively in other situations call UpdateLink
ThisWorkbook.UpdateLink "c:\Whatever\PasswordProtectedWorkbook.xlsx"
End Sub
可执行文件监视该Excel实例的子窗口的下一个30秒,该子窗口具有类'bosa_sdm_XL9'(使用Spy ++发现此名称),然后将密码填充到该对话框中并单击其OK按钮。链接已更新,然后VBA可以从那里继续。
可执行文件是使用UIAutomation(更具体地说,Windows Kit中的UIAComWrapper)编写的,首先立即将焦点设置回Excel实例,然后在接下来的30秒左右每秒查询该Excel实例的子节点:
/* this is critical: switch focus back to the Excel instance that called me */
AutomationElement aeExcel = AutomationElement.FromHandle(Program.xlwHnd);
然后用定时方法:
/* the password dialog class name is bosa_sdm_XL9 */
AutomationElementCollection aeXLPwdWindow = aeExcel.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "bosa_sdm_XL9"));
if (aeXLPwdWindow.Count > 0)
{
AutomationElementCollection aePwdBox = aeXLPwdWindow[0].FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ClassNameProperty, "EDTBX"));
AutomationElementCollection aeOkButton = aeXLPwdWindow[0].FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.NameProperty, "OK"));
if (aePwdBox.Count > 0 && aeOkButton.Count > 0)
{
/* UIAutomation? couldn't get it to work */
SendMessage(new IntPtr(aePwdBox [0].Current.NativeWindowHandle), WM_SETTEXT, 0, new StringBuilder(Program.pwd));
object vp;
/* UIAutomation click the button */
if (aeOkButton[0].TryGetCurrentPattern(InvokePatternIdentifiers.Pattern, out vp))
{
((InvokePattern)vp).Invoke();
attempts = 30; //stop trying after 30 seconds
return;
}
}
}