在Excel中有一个已经写好的宏,并且有关于此的报告错误,我必须解决这个问题。初步调查如下......
有ABC.xls
个文件,其中包含macro
。
现在,该宏有一个名为sub
的{{1}},当我按下changeTheCode
时,它会被调用。
此子将打开Ctrl + M
,用户可以在其中选择CSV文件。我存储在全局变量中的CSV文件的路径声明在所有函数之外...
Open File Dialog
当用户关闭excel时,此全局变量将用于将更改保存到CSV文件中。
Public txtFileNameAndPath As String
我使用此Private Sub Workbook_BeforeClose(Cancel As Boolean)
Call saveUnicodeCSV
Call deleteXLS
End Sub
文件打开ABC.xls
文件。
我使用此ABC123.CSV
(ABC.xls的副本)文件打开DEF.xls
文件。但是,当我使用DEF123.CSV
打开DEF123.CSV
时,Ctrl + M
的子changeTheCode
被调用,ABC.xls
的全局变量txtFileNameAndPath
为空的,当我关闭Excel时,由于这个原因,事情没有得到保存。
设置全局变量的代码。
DEF.xls
如何处理这个问题的输入将对我有所帮助。
注意:包含宏的Excel将提供给客户。因此,我不能要求客户进行一些注册表调整,以便在单独的实例中打开Excel。
感谢。
答案 0 :(得分:5)
我认为问题是当您将相同的宏名称绑定到快捷键时,打开的第一本书将被分配给该快捷键。
解决方法是使另一个宏根据工作簿名称调用正确的宏。这可能需要您将某些工作簿和工作表替换为ActiveWorkbook或ActiveWorksheet。但请先试试。
这只是我尝试的示例代码,但请根据您的需要进行编辑。我通过制作两个文件a.xlsm和b.xlsm来尝试它。区别是a.xlsm有msgbox" a"在第一行和b.xlsm有msbox" b"在第一行。首先打开a.xlsm然后再打开b.xls进行检查。然后分配相同的快捷键。您将看到,当您在b.xlsm中运行Ctrl + M时,将运行的宏将位于带有msgbox" A"的a.xlsm中。但是它会在b.xlsm中调用正确的宏。
简答
此代码使用Application.Run
来运行特定工作簿中的宏。创建一个帮助宏,它将检查文件名,然后附加宏名称。
因此,当您按ctrl + m时,无论运行哪个工作簿run_code,它都将返回到activeworkbook并从该工作簿运行宏(在本例中为plaster)。此外,它将使用activeworkbook值填充公共变量。
https://www.rondebruin.nl/win/s9/win001.htm application.run的一些示例
到目前为止,最简单的解决方案只需添加来电宏
来电宏:
Sub call_changeTheCode() ' add to all workbooks, that have changeTheCode macro then assign to Ctrl + m
Application.Run ActiveWorkbook.Name & "!changeTheCode"
End Sub
概念证明,而不是您的实际代码,使用上面的来电宏:
Public varvar As String
Sub run_code() 'assignt to shortcut key CTRL+M both macros in a.xlsm and b.xlsm
MsgBox "a" ' to test create another workbook and change this to b
file_path = ActiveWorkbook.Path 'just to check path
file_name = ActiveWorkbook.Name 'gets the file name
MsgBox file_path 'msgbox the file_path
MsgBox file_name 'msgbox the file_name
MsgBox file_name & "!plaster" 'msgbox the file name plus macro name, in your instance it would be "ABC.xlsm'!macro_name" please note the format
Application.Run file_name & "!plaster"
End Sub
Sub plaster() 'this is the test macro that will show correct macro in workbook is called
varvar = ActiveWorkbook.Name
MsgBox "hi this is workbook " & varvar
End Sub
答案 1 :(得分:3)
如果我理解正确,则用户将DEF.xls
作为顶级工作簿,因此等于ActiveWorkbook
。然后我的想法是为这个工作簿添加一个非常独特的名称,例如" zZzVBAdatazZz"并使其非常隐藏(设置Sheets("zZzVBAdatazZz").Visible = xlVeryHidden
),因此用户无法通过菜单命令取消隐藏它。运行宏时,您可以在此工作表中存储DEF123.CSV
的路径,让我们在单元格A1
(ActiveWorkbook.Sheets("zZzVBAdatazZz").Range(A1) = .SelectedItems(1)
)中说明。在Private Sub Workbook_BeforeClose(Cancel As Boolean)
检查Sheets("zZzVBAdatazZz").Range(A1)
中是否有值。如果是这样,请将其存储在变量中(现在可以是本地的),删除A1
中的值并将路径传递给saveUnicodeCSV
。
答案 2 :(得分:2)
有趣的问题,我认为问题的核心是工作簿的全局对象在整个应用程序空间中并不是真正的全局对象。此后,我将全球写为“全球”,以突出这种误解。
我相信你的宏总是从一个地方运行,所以如何为每个工作簿设置一个“全局”变量的范围,但仍然可以从另一个工作簿的代码库中获取它。
我的解决方案是使用ThisWorkbook
模块作为容纳“全局”变量的位置。所以在ThisWorkbook
模块中放置以下代码
Option Explicit
Public NJMRGlobalVar As Variant
然后在宏代码的模块中,您将需要一个函数来测试给定的工作簿是否支持/导出这个新的“全局”变量。所以这样的事情(与单元测试一起给出)
Private Function WorkbookHasNJMRGlobalVar(ByVal wb As Excel.Workbook) As Boolean
If wb Is Nothing Then
WorkbookHasNJMRGlobalVar = False
Else
Dim vTest As Variant
vTest = CVErr(xlErrName)
'Requires VBA IDE->Tools->Options->General->Error Trapping->Break on Unhandled Errors
On Error Resume Next
vTest = CallByName(wb, "NJMRGlobalVar", VbGet)
Dim lSaveError As Long
lSaveError = Err.Number
On Error GoTo 0
WorkbookHasNJMRGlobalVar = (lSaveError = 0)
End If
End Function
Private Sub TestWorkbookHasNJMRGlobalVar()
Debug.Assert WorkbookHasNJMRGlobalVar(ThisWorkbook) = True
Dim wbUnsuitable As Excel.Workbook
Set wbUnsuitable = Workbooks.Item("VBA Fileshare.xlsm") '*<---- different for you!
Debug.Assert WorkbookHasNJMRGlobalVar(wbUnsuitable) = False
End Sub
最后一步是重写您的代码,而不是使用ThisWorkbook,而是重写ActiveWorkbook或使用Application.Workbooks.Item(“foo.xlsm”)获取目标工作簿。所以这里有一些代码(适用于ActiveWorkbook)。
Sub CodePageChange()
Dim SheetName As Worksheet
Dim fd As Office.FileDialog
Dim sheetName1 As String
Dim tabSheetName As String
If Not WorkbookHasNJMRGlobalVar(ActiveWorkbook) Then
MsgBox "Currently Active Workbook not a suitable candidate for that macro."
Else
Dim wbGlobalVarEnabled As Excel.Workbook
Set wbGlobalVarEnabled = ActiveWorkbook
Set fd = Application.FileDialog(msoFileDialogFilePicker)
With fd
'....
'....
'....
If .Show = True Then
wbGlobalVarEnabled.NJMRGlobalVar = .SelectedItems(1)
Else
MsgBox "Please start over. You must select a csv file."
Exit Sub
End If
End With
End If
End Sub
所以现在每个工作簿都有一个“全局”变量,可以通过获取对Excel.Workbook的引用从一个代码中自由访问。这是有效的,因为Excel.Workbook不禁止接口中的可扩展性,这意味着可以添加额外的方法和属性(但并非所有工作簿都支持它们,因此您可以检测其功能,例如上面的WorkbookHasNJMRGlobalVar
。
请发布反馈,如果还不够,我会修改,我有99/100的VBA问题我想获得我的青铜VBA徽章:)
编辑:阅读你的个人资料,你有C / C ++技能,所以我可以进一步解释这是如何工作的。在Excel的类型库中,“不可扩展”的IDL关键字装饰了大多数接口,禁止使用额外的方法和属性,但interface _Workbook
中缺少这些,所以你可以这样做。以下是OleView.exe的截图
答案 3 :(得分:2)
唯一的快捷方式只能分配给工作簿中的单个过程。 再次设置相同的快捷方式会覆盖先前的分配。
要在不同工作簿中处理相同的快捷方式,请在激活工作簿时指定快捷方式:
' ThisWorkbook '
Private Sub Workbook_Activate()
Application.OnKey "^m", "CodePageChange"
End Sub
' Module '
Public Sub CodePageChange()
MsgBox ThisWorkbook.Name
End Sub
或者处理接收它的工作簿中的回调,然后调用目标工作簿上的过程:
' ThisWorkbook '
Private Sub Workbook_Open()
Application.OnKey "^m", "OnHotkeyCtrlM"
End Sub
' Module '
Public Sub OnHotkeyCtrlM()
Application.Run "'" & ActiveWorkbook.Name & "'!CodePageChange"
End Sub
Public Sub CodePageChange()
MsgBox ThisWorkbook.Name
End Sub
答案 4 :(得分:1)
将自定义属性用作全局变量
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Debug.Print ActiveWorkbook.CustomDocumentProperties("xyz")
End Sub
Sub changeTheCode()
On Error Resume Next
ActiveWorkbook.CustomDocumentProperties("xyz").Delete
ActiveWorkbook.CustomDocumentProperties.Add Name:="xyz", LinkToContent:=False, Type:=msoPropertyTypeString, Value:=ActiveWorkbook.Name
Debug.Print ActiveWorkbook.CustomDocumentProperties("xyz")
End Sub
答案 5 :(得分:1)
我同意S Meaden的说法,如果没有指定存储“全局”变量的工作簿,则不能在多个工作簿中使用全局变量。
另一种方法: 在带有宏的Excel文件中,完成“全局”变量的设置: 用
替换设置Shell ("Cmd.Exe /C SetX txtFileNameAndPath thePath")
将thePath替换为实际路径或值。 运行此宏后,您可以运行Excel文件并通过
检索真正的全局值txtFileNameAndPath = Environ("txtFileNameAndPath")
答案 6 :(得分:1)
将全局变量放入ThisWorkbook
类模块。
Public txtFileNameAndPath As String
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Debug.Print Me.Name & ":" & txtFileNameAndPath
End Sub
然后在过程CodePageChange
中遍历所有工作簿并使用ActiveWorkbook.Name
检查应使用哪个全局变量。 HTH
If .Show = True Then
Dim w As Workbook
On Error Resume Next
For Each w In Workbooks
If ActiveWorkbook.Name = w.Name Then
w.txtFileNameAndPath = .SelectedItems(1)
Exit For
End If
Next w
On Error GoTo 0
Else
MsgBox "Please start over. You must select a csv file."
Exit Sub
End If
答案 7 :(得分:0)
用户友好的解决方法:抛弃键盘快捷方式并将CommandButton添加到工作表或将自定义功能区选项卡添加到工作簿以调用该过程。他们保证可以从他们自己的工作簿中调用该过程,并且对用户来说更容易。