绑在功能区的PowerPoint加载项的持续存储位置是什么?

发布于 2025-01-23 05:03:51 字数 3982 浏览 1 评论 0原文

冗长的背景故事: 我正在VBA创建PowerPoint加载项。我的加载项在功能区上创建一个自定义选项卡,然后我使用getEnabled回调来确定是否应根据选择启用或禁用某些按钮,就像PowerPoint一样,PowerPoint与BOLD或BOLD一样根据您是否选择文本的内容,下划线按钮。

为此,您使用onload回调:

<customUI xmlns="..." onLoad="initRibbon">

将指向功能区的变量传递。您可以将其另存为这样的全局变量:

Global myRibbon As IRibbonUI

Sub initRibbon(ribbon As IRibbonUI)
    Set myRibbon = ribbon

然后我可以在PowerPoint中设置WindowsElectionChange事件,以使按钮控件无效以驱动getEnabled呼叫并设置按钮状态。

Private Sub App_WindowSelectionChange(ByVal Sel As Selection)
'Enable or disable buttons depending on selection

...
    
    'By invalidating the control, the rbnGetEnabled sub in the Ribbon module will run
    myRibbon.InvalidateControl ("AddLinkToOneNote")

End Sub

这是按钮XML:

<button id="AddLinkToOneNote" visible="true" size="large" 
        label="Link to OneNote" 
        screentip="Add a link to OneNote to the selected node" 
        onAction="rbnAddLinkToOneNote" imageMso="LinkedNotes"
        getEnabled="rbnGetEnabled" />

初始问题和解决方案: 问题在于,有很多情况会导致功能区全局变量丢失并重置为“无”。它可能会在VBA执行时停止代码。它可能正在更改事件代码所在的类模块(在开发代码时发生了很多事情!!!)。我认为还有更多原因,但是当发生这种情况时,功能区回到不再运行,您的色带被困在该州。解决方案?重新启动您的办公应用程序。

Interwebs的出色人员为这个问题设计了一个巧妙的解决方案。您可以将功能带指针保存在比全局变量更安全的地方,并在丢失的情况下重新加载。这是该部分的常见代码模式:

Public Declare Sub CopyMemory Lib "kernel32" Alias _
    "RtlMoveMemory" (destination As Any, source As Any, _
    ByVal length As Long)

Public Sub ribbonLoaded(ribbon As IRibbonUI)
   ' Store pointer to IRibbonUI
   Dim lngRibPtr As Long
' Store the custom ribbon UI Id in a static variable.
' This is done once during load of UI. I.e. during workbook open.
    Set guiRibbon = ribbon
    lngRibPtr = ObjPtr(ribbon)
    ' Write pointer to worksheet for safe keeping
    Tabelle2.Range("A1").Value = lngRibPtr
End Sub

Function GetRibbon(lngRibPtr as Long) As Object
   Dim objRibbon As Object
   CopyMemory objRibbon, lngRibPtr, 4
   Set GetRibbon = objRibbon
   ' clean up invalid object
   CopyMemory objRibbon, 0&, 4
End Function

最后,我的问题!!! 如果您使用的是Excel,例如工作表或名称中的加载项文件,Executeexcel4macro隐藏名称空间,甚至是VBA模块,则有很多很酷的地方可以存储地址! PowerPoint附加组件没有任何可用的地方。

如果我的代码仅在一个PowerPoint .pptm文件中,我会没事的。我可以在演示文稿上使用标签。我有那个代码工作。但是我需要加载程序中的代码,该代码在打开任何演示文稿之前加载。当功能区第一次加载时,我会得到色带指针,并且需要将其用于与该色带绑定的每个呈现的演示文稿。如果有人打开了一个单独的PowerPoint实例,则需要为该应用程序实例提供单独的功能区指针。 (上面链接的文章通过保存应用程序指针和功能区指针来确保您使用正确的指针,以确保使用它之前,

您可以尝试将其存储在注册表或文件中,但是如果PowerPoint崩溃或这些条目不会清理,那将是一团糟。

另一个选项是使用存储的窗口句柄 PowerPoint很烂,我不确定它能正确处理多个PowerPoint实例:

Select Case VersionNo
   Case "8"  ' For PPT97:
      hWnd = FindWindow("PP97FrameClass")
   Case "9"  ' For PPT2K:
      hWnd = FindWindow("PP9FrameClass")
   Case "10" ' For XP:
      hWnd = FindWindow("PP10FrameClass")
   Case "11" ' For 2003:
      hWnd = FindWindow("PP11FrameClass")
   Case "12" ' For 2007:
      hWnd = FindWindow("PP12FrameClass")
   Case "14" ' For 2010:
      hWnd = FindWindow("PPTFrameClass")
   Case "15" ' For 2013:
      hWnd = FindWindow("PPTFrameClass")
   Case "16" ' For 2016:
      hWnd = FindWindow("PPTFrameClass")
   Case Else
      Err.Raise number:=vbObjectError + ERR_VERSION_NOT_SUPPORTED, _
            Description:="Newer version: " & VersionNo
      Exit Property
End Select

因此,我可以在哪里存储此值的任何想法?另外,是否有更好的方法将不需要指针指向功能区指针的事件代码中的控件无效?

Lengthy backstory:
I am creating a PowerPoint add-in in VBA. My add-in creates a custom tab on the ribbon, and then I am using the getEnabled callback to determine whether certain buttons should be enabled or disabled depending on the selection, much like PowerPoint does with the Bold or Underline buttons based on whether you've selected something with text or not.

To do this, you use the onLoad callback:

<customUI xmlns="..." onLoad="initRibbon">

which passes a variable pointing to the ribbon. You save it as a global variable like this:

Global myRibbon As IRibbonUI

Sub initRibbon(ribbon As IRibbonUI)
    Set myRibbon = ribbon

Then I can set up the WindowSelectionChange event in PowerPoint to invalidate the button controls to drive the getEnabled callback and set the button state.

Private Sub App_WindowSelectionChange(ByVal Sel As Selection)
'Enable or disable buttons depending on selection

...
    
    'By invalidating the control, the rbnGetEnabled sub in the Ribbon module will run
    myRibbon.InvalidateControl ("AddLinkToOneNote")

End Sub

Here's the button XML:

<button id="AddLinkToOneNote" visible="true" size="large" 
        label="Link to OneNote" 
        screentip="Add a link to OneNote to the selected node" 
        onAction="rbnAddLinkToOneNote" imageMso="LinkedNotes"
        getEnabled="rbnGetEnabled" />

Initial problem and solution:
The problem is that there are a lot of situations that cause the ribbon global variable to be lost and reset to "Nothing". It could be stopping code as it executes in VBA. It could be changing the Class module where the Event code is (which happens a LOT when you are developing code!!!). I think there are more reasons, but when this happens, the ribbon callbacks no longer run and your ribbon is trapped in the state it was. Solution? Restart your Office app.

The brilliant people of the interwebs have devised a clever solution to this problem. You save the ribbon pointer somewhere safer than a global variable and reload it if it gets lost. Here's a common code pattern for that part:

Public Declare Sub CopyMemory Lib "kernel32" Alias _
    "RtlMoveMemory" (destination As Any, source As Any, _
    ByVal length As Long)

Public Sub ribbonLoaded(ribbon As IRibbonUI)
   ' Store pointer to IRibbonUI
   Dim lngRibPtr As Long
' Store the custom ribbon UI Id in a static variable.
' This is done once during load of UI. I.e. during workbook open.
    Set guiRibbon = ribbon
    lngRibPtr = ObjPtr(ribbon)
    ' Write pointer to worksheet for safe keeping
    Tabelle2.Range("A1").Value = lngRibPtr
End Sub

Function GetRibbon(lngRibPtr as Long) As Object
   Dim objRibbon As Object
   CopyMemory objRibbon, lngRibPtr, 4
   Set GetRibbon = objRibbon
   ' clean up invalid object
   CopyMemory objRibbon, 0&, 4
End Function

Finally, my question!!!
There are lots of cool places to store the address if you are using Excel such as a worksheet or name within the Add-in file, the ExecuteExcel4Macro hidden name space, or even a VBA module! PowerPoint add-ins don't have any of these places available.

If my code only was in a single PowerPoint .pptm file, I would be fine. I could just use Tags on the Presentation. I have that code working. But I need my code in an Add-in, which loads before any Presentations are open. I get the ribbon pointer when the ribbon loads the first time, and I need to use it for every presentation that is opened tied to that ribbon. If someone opens a separate instance of PowerPoint, it needs to have a separate ribbon pointer for that application instance. (One sample on the article previously linked above handled that nicely by saving both the Application pointer and the Ribbon pointer to make sure you had the right pointer before using it!)

You could try to store it in the registry or in a file, but it's going to be a mess if PowerPoint crashes or these entries don't get cleaned up.

Another option is using the Window handle for storage, but the method to finding the hWnd for PowerPoint sucks and I'm not sure it could handle multiple PowerPoint instances properly:

Select Case VersionNo
   Case "8"  ' For PPT97:
      hWnd = FindWindow("PP97FrameClass")
   Case "9"  ' For PPT2K:
      hWnd = FindWindow("PP9FrameClass")
   Case "10" ' For XP:
      hWnd = FindWindow("PP10FrameClass")
   Case "11" ' For 2003:
      hWnd = FindWindow("PP11FrameClass")
   Case "12" ' For 2007:
      hWnd = FindWindow("PP12FrameClass")
   Case "14" ' For 2010:
      hWnd = FindWindow("PPTFrameClass")
   Case "15" ' For 2013:
      hWnd = FindWindow("PPTFrameClass")
   Case "16" ' For 2016:
      hWnd = FindWindow("PPTFrameClass")
   Case Else
      Err.Raise number:=vbObjectError + ERR_VERSION_NOT_SUPPORTED, _
            Description:="Newer version: " & VersionNo
      Exit Property
End Select

So any ideas on where I could store this value? Alternatively, is there a better way to invalidate the controls from the Event code that doesn't require a pointer to the ribbon?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文