全局variables是空的,如果2个相同的xls文件具有相同的macros打开

Excel中有macros已经被写入,并有错误报告,我必须解决这个问题。 初步调查如下…有macro ABC.xls文件。

现在,这个macros有一个名为changeTheCodesub changeTheCode ,当我按下Ctrl + M时,它将被调用。

这个小组将打开一个Open File Dialog ,用户可以select一个CSV文件。 我存储在全局variables中的CSV文件的path声明在所有函数之外

 Public txtFileNameAndPath As String 

当用户closuresexcel时,将使用此全局variables将更改保存到CSV文件中。

 Private Sub Workbook_BeforeClose(Cancel As Boolean) Call saveUnicodeCSV Call deleteXLS End Sub 

我使用这个ABC.xls文件来打开一个ABC123.CSV文件。

我使用这个DEF.xlsDEF.xls的副本)文件来打开DEF123.CSV文件。 但是当我用Ctrl + M打开DEF123.CSV时, DEF123.CSV的sub changeTheCode被调用, txtFileNameAndPath的全局variablesDEF.xls为空,当我closuresExcel时,事情并没有得到保存,因为这个。

在这里输入图像说明

全局variables正在被设置的代码。

 Public txtFileNameAndPath As String Sub CodePageChange() Dim SheetName As Worksheet Dim fd As Office.FileDialog Dim sheetName1 As String Dim tabSheetName As String Set fd = Application.FileDialog(msoFileDialogFilePicker) With fd '.... '.... '.... If .Show = True Then txtFileNameAndPath = .SelectedItems(1) Else MsgBox "Please start over. You must select a csv file." Exit Sub End If End With 

关于如何处理这个问题的input将会帮助我很多。

注意:包含macros的Excel将被提供给客户。 因此,我不能要求客户做一些registry调整,以单独的实例打开Excel。

谢谢。

我认为的问题是,当您将相同的macros名称绑定到快捷键时,打开的第一本书将被分配到该快捷键。

解决方法是使另一个macros根据工作簿名称调用正确的macros。 这可能需要您将一些工作簿和工作表replace为ActiveWorkbook或ActiveWorksheet。 但请先尝试一下。

这只是我试用的示例代码,但请编辑它以满足您的需求。 我试了两个文件a.xlsm和b.xlsm。 差异是a.xlsm在第一行有msgbox“a”,而b.xlsm在第一行有msbox“b”。 您先打开a.xlsm然后b.xls。 然后分配相同的快捷键。 你将会看到,当你在b.xlsm中运行Ctrl + M时,将运行的macros将在msgsbox为“A”的a.xlsm中,然后在b.xlsm中调用正确的macros。

简答

此代码使用从特定工作簿运行macros的Application.Run 。 创build一个帮助macros将检查文件名,然后附加macros名称。

所以,当你按Ctrl + M,无论哪个工作簿run_code运行,它将返回到activeworkbook,并从该工作簿运行macros,在这种情况下,石膏。 此外,它将使用activeworkbook值填充公共variables。

https://www.rondebruin.nl/win/s9/win001.htm application.run的一些示例

到目前为止,最简单的解决scheme只是添加来电macros

来电者macros:

 Sub call_changeTheCode() ' add to all workbooks, that have changeTheCode macro then assign to Ctrl + m Application.Run ActiveWorkbook.Name & "!changeTheCode" End Sub 

概念certificate,而不是你的实际代码,使用上面的调用macros:

 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 

如果我理解正确,用户将DEF.xls作为顶级工作簿,因此等于ActiveWorkbook 。 然后,我的想法是添加新工作表,以非常独特的名称,如“zZzVBAdatazZz”,并使其非常隐藏(设置Sheets("zZzVBAdatazZz").Visible = xlVeryHidden ),所以用户不能用菜单命令取消隐藏。 当macros运行时,你可以在这张表中DEF123.CSV的path,例如在单元格A1ActiveWorkbook.Sheets("zZzVBAdatazZz").Range(A1) = .SelectedItems(1) )中。 在您的Private Sub Workbook_BeforeClose(Cancel As Boolean)检查Sheets("zZzVBAdatazZz").Range(A1)是否有值Sheets("zZzVBAdatazZz").Range(A1) 。 如果是这样,把它存储在variables(现在可以是本地),删除A1值,并通过path来saveUnicodeCSV

有趣的问题,我认为问题的核心在于,工作簿的全局对象在整个应用程序空间中并不是真正的全局对象。 此后我写全球为“全球”来突出这个错误。

我相信你的macros将始终从一个地方运行,所以如何为每个工作簿确定一个“全局”variables的范围,但仍然从另一个工作簿的代码库中获取。

我的解决scheme是使用ThisWorkbook模块作为放置“全局”variables的地方。 所以在ThisWorkbook模块中放置下面的代码

 Option Explicit Public NJMRGlobalVar As Variant 

那么在macros代码模块中,您将需要一个函数来testing给定的工作簿是否支持/导出这个新的“全局”variables。 所以像这样(与unit testing一起给出)

 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 

所以,现在每个工作簿都有一个“全局”variables,只需通过获取对Excel.Workbook的引用即可从一个代码自由访问。 这是有效的,因为Excel.Workbook不会在接口中禁止扩展性,这意味着可以添加额外的方法和属性(但并不是所有的工作簿都会支持它们,因此您需要检测一个函数,例如上面的WorkbookHasNJMRGlobalVar )。

请发表反馈,如果不够,我会修改,我有VBA问题99/100我想获得我的青铜VBA徽章:)

编辑:阅读你的个人资料,你有C / C + +技能,所以我可以进一步在低层次的解释,如何工作。 在Excel的types库中,“nonextensible”IDL关键字装饰了大多数接口,禁止使用额外的方法和属性,但是interface _Workbook这样的interface _Workbook所以你可以。 这里是OleView.exe的截图

在这里输入图像说明

唯一的快捷方式只能分配给工作簿中的单个过程。 再次设置相同的快捷方式会覆盖以前的任务。

要在不同的工作簿中处理相同的快捷方式,请在工作簿激活时指定快捷方式:

 ' ThisWorkbook ' Private Sub Workbook_Activate() Application.OnKey "^m", "CodePageChange" End Sub ' Module ' Public Sub CodePageChange() MsgBox ThisWorkbook.Name End Sub 

或者在接收到的工作簿中处理callback,然后调用目标工作簿上的过程:

 ' 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 

使用自定义属性作为全局variables

 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 

我同意S Meaden,你不能在几个工作簿中有一个全局variables,而没有指定存储“全局”variables的工作簿。

另一种方法:在带有macros的Excel文件中,“全局”variables的设置完成:将设置replace为

 Shell ("Cmd.Exe /C SetX txtFileNameAndPath thePath") 

将pathreplace为实际path或值。 运行这个macros之后,您可以运行您的Excel文件并通过检索真正的全局值

 txtFileNameAndPath = Environ("txtFileNameAndPath") 

将全局variables放入ThisWorkbook类模块。

 Public txtFileNameAndPath As String Private Sub Workbook_BeforeClose(Cancel As Boolean) Debug.Print Me.Name & ":" & txtFileNameAndPath End Sub 

然后在程序CodePageChange循环遍历所有的工作簿,并检查使用ActiveWorkbook.Name应该使用哪个全局variables。 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 

用户友好的解决方法:切断键盘快捷方式并将命令button添加到工作表或自定义function区选项卡到工作簿以调用过程。 他们保证从他们自己的工作簿中调用该过程,并且更方便用户使用。