CopyMemory API 的替代方法来恢复 Excel IRibbonUI 是什么?

huangapple go评论69阅读模式
英文:

What is the alternative for CopyMemory API to recover Excel IRibbonUI

问题

以下是翻译好的内容:

我使用以下代码来检索在“Template_Rib”全局变量中设置的在功能回调中设置的“IRibbonUI”,该变量随机失去其值。

但是“CopyMemory” API使Excel随机崩溃。是否有此“CopyMemory” API的替代方法?

#If VBA7 Then
  Public Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cBytes&)
#Else
  Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cBytes&)
#End If
Public Template_Rib As IRibbonUI
Public Sub CallbackOnLoad(ribbon As IRibbonUI)
#If VBA7 Then
  Dim StoreRibbonPointer As LongPtr
#Else
  Dim StoreRibbonPointer As Long
#End If
Set Template_Rib = ribbon
Template_Rib.ActivateTab "TemplateTab" '要激活的选项卡的名称
'将IRibbonUI指针存储在附加文件中的命名区域中
StoreRibbonPointer = ObjPtr(ribbon)
ThisWorkbook.Names.Add Name:="RibbonID", RefersTo:=StoreRibbonPointer
End Sub
Sub TryToRetrieveRibbon()
On Error GoTo ErrorHandler
If Template_Rib Is Nothing Then
  Set Template_Rib = GetRibbon(Replace(ThisWorkbook.Names("RibbonID").RefersTo, "=", ""))
  'Set Template_Rib = GetRibbon(Evaluate(ThisWorkbook.Names("RibbonID").Value))
End If
ErrorHandler:
Err.Clear
End Sub
#If VBA7 Then
  Function GetRibbon(ByVal lRibbonPointer As LongPtr) As Object
#Else
  Function GetRibbon(ByVal lRibbonPointer As Long) As Object
#End If
  Dim objRibbon As Object
  CopyMemory objRibbon, lRibbonPointer, LenB(lRibbonPointer)
  Set GetRibbon = objRibbon
  Set objRibbon = Nothing
End Function
英文:

I use below code to retrieve the IRibbonUI that was set on ribbon callback in Template_Rib global variable that randomly losing its value.

But the CopyMemory API makes the Excel crash randomly. Is there any alternative for this CopyMemory API?

#If VBA7 Then
  Public Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cBytes&)
#Else
  Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cBytes&)
#End If
Public Template_Rib As IRibbonUI
Public Sub CallbackOnLoad(ribbon As IRibbonUI)
#If VBA7 Then
  Dim StoreRibbonPointer As LongPtr
#Else
  Dim StoreRibbonPointer As Long
#End If
Set Template_Rib = ribbon
Template_Rib.ActivateTab "TemplateTab" 'Name of the tab to activate
'Store pointer to IRibbonUI in a Named Range within add-in file
StoreRibbonPointer = ObjPtr(ribbon)
ThisWorkbook.Names.Add Name:="RibbonID", RefersTo:=StoreRibbonPointer
End Sub
Sub TryToRetrieveRibbon()
On Error GoTo ErrorHandler
If Template_Rib Is Nothing Then
  Set Template_Rib = GetRibbon(Replace(ThisWorkbook.Names("RibbonID").RefersTo, "=", ""))
  'Set Template_Rib = GetRibbon(Evaluate(ThisWorkbook.Names("RibbonID").Value))
End If
ErrorHandler:
Err.Clear
End Sub
#If VBA7 Then
  Function GetRibbon(ByVal lRibbonPointer As LongPtr) As Object
#Else
  Function GetRibbon(ByVal lRibbonPointer As Long) As Object
#End If
  Dim objRibbon As Object
  CopyMemory objRibbon, lRibbonPointer, LenB(lRibbonPointer)
  Set GetRibbon = objRibbon
  Set objRibbon = Nothing
End Function

答案1

得分: 2

I don't think there is an alternative to CopyMemory for that, but I might have an idea why your implementation might be unstable.

First, have you made sure that CallbackOnLoad is actually called? It happened to me in the past where I would edit the custom XML for the ribbon, but forget to specify the onload property in the top tag. Eg.:

<customUI onLoad="CallbackOnLoad" xmlns="http://schemas.microsoft.com/office/2006/01/customui">

It's probably not your case since refreshing the ribbon would never work in that case, so it wouldn't be random in that sense, but I thought I'd mention it just in case.


Secondly, in theory the pointer for the ribbon is a 64-bit integer (assuming 64-bit OS) and it could be bigger than what Excel Names can store. (I'm saying in theory because while I was testing it, I wasn't able to get anything bigger than a 42-bit integer.)

For instance, you could not store the upper limit of an unsigned 64-bit integer (9,223,372,036,854,775,807) inside a name because it will only store the first 15 significant digits, which limits you to a 999,999,999,999,999 which is a 50-bit integer).

To prevent this from happening randomly, you could store the pointer as a string like this:

ThisWorkbook.Names.Add Name:="RibbonID", RefersTo:=Chr(34) & StoreRibbonPointer & Chr(34)

Then, you can retrieve and use the pointer with something like this:

Dim StringPtr As String
StringPtr = Replace(Replace(ThisWorkbook.Names("RibbonID").RefersTo, "=", ""), Chr(34), "")
#If VBA7 Then
    Set ObjRibbon = GetRibbon(CLngPtr(StringPtr))
#Else
    Set ObjRibbon = GetRibbon(CLng(StringPtr))
#End If

Thirdly, you've defined the 3rd parameter for RtlMoveMemory as a Long using the & type declaration suffix. However, when you are passing LenB(lRibbonPointer), you are actually passing an Integer. You can convince yourself of that by putting a breakpoint on the line with CopyMemory and then run the following in the Immediate Window:

?TypeName(LenB(lRibbonPointer)) '-> Integer

To make sure that you pass a Long variable, you can use a Long to make sure that the conversion occurs in your code instead of inside the Windows API function which might lead to an unstable outcome.

Dim Length As Long
Length = LenB(lRibbonPointer)
CopyMemory objRibbon, lRibbonPointer, Length 

Related answers:

英文:

I don't think there is an alternative to CopyMemory for that, but I might have an idea why your implementation might be unstable.

First, have you made sure that CallbackOnLoad is actually called? It happened to me in the past where I would edit the custom XML for the ribbon, but forget to specify the onload property in the top tag. Eg.:

&lt;customUI onLoad=&quot;CallbackOnLoad&quot; xmlns=&quot;http://schemas.microsoft.com/office/2006/01/customui&quot; &gt;

It's probably not your case since refreshing the ribbon would never work in that case, so it wouldn't be random in that sense, but I thought I'd mention it just in case.


Secondly, in theory the pointer for the ribbon is a 64-bit integer (assuming 64-bit OS) and it could be bigger than what Excel Names can store. (I'm saying in theory because while I was testing it, I wasn't able to get anything bigger than a 42-bit integer.)

For instance, you could not store the upper limit of an unsigned 64-bit integer (9,223,372,036,854,775,807) inside a name because it will only store the first 15 significant digits, which limits you to a 999,999,999,999,999 which is a 50-bit integer).

CopyMemory API 的替代方法来恢复 Excel IRibbonUI 是什么?

To prevent this from happening randomly, you could store the pointer as a string like this:

ThisWorkbook.Names.Add Name:=&quot;RibbonID&quot;, RefersTo:=Chr(34) &amp; StoreRibbonPointer &amp; Chr(34)

Then, you can retrieve and use the pointer with something like this:

Dim StringPtr As String
StringPtr = Replace(Replace(ThisWorkbook.Names(&quot;RibbonID&quot;).RefersTo, &quot;=&quot;, &quot;&quot;), Chr(34), &quot;&quot;)
#If VBA7 Then
    Set ObjRibbon = GetRibbon(CLngPtr(StringPtr))
#Else
    Set ObjRibbon = GetRibbon(CLng(StringPtr))
#End If

Thirdly, you've defined the 3rd parameter for RtlMoveMemory as a Long using the &amp; type declaration suffix. However, when you are passing LenB(lRibbonPointer), you are actually passing an
Integer. You can convince yourself of that by putting a breakpoint on the line with CopyMemory and then run the following in the Immediate Window:

?TypeName(LenB(lRibbonPointer)) &#39;-&gt; Integer

To make sure that you pass a Long variable, you can use a Long to make sure that the conversion occurs in your code instead of inside the Windows API function which might lead to an unstable outcome.

Dim Length As Long
Length = LenB(lRibbonPointer)
CopyMemory objRibbon, lRibbonPointer, Length 

Related answers:

huangapple
  • 本文由 发表于 2023年7月18日 01:16:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/76706734.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定