小编Gre*_*edo的帖子

如何在Excel UDF中强制参数

我创建了一个接受数组输入的excel UDF.我希望它只允许数组中的偶数个项目.这是代码:(它只是短的所以我会发布所有这些,你可以有一些上下文)

Function SUBSTITUTEMULTI(inputString As String, ParamArray criteria() As Variant) as String

Dim subWhat As String
Dim forWhat As String
Dim x As Single


If UBound(criteria()) Mod 2 = 0 Then
'check whether an even number of arguments is input
MsgBox "You've entered too few arguments for this function", vbExclamation

Else
    x = 0
    For Each element In criteria
        If x = 0 Then
            subWhat = element
        Else
            forWhat = element
            inputString = WorksheetFunction.Substitute(inputString, subWhat, forWhat)
        End If
        x = …
Run Code Online (Sandbox Code Playgroud)

excel vba excel-vba excel-udf

6
推荐指数
1
解决办法
189
查看次数

从用户表单监视类事件

我有一个用户窗体,它在运行时自行组装,通过查看文件夹并将其中的所有图片提取到我的窗体上的图像控件中。让这个过程稍微复杂一点的是,我还使用图像控件的事件来运行一些代码。

作为一个简化的例子 - 我有一个在运行时创建图片的表单,该图片有一个点击事件来清除其内容。为此,我有一个自定义类来表示图像对象

在名为“imgForm”的空白用户表单中

Dim oneImg As New clsImg 'our custom class

Private Sub UserForm_Initialize()
Set oneImg.myPic = Me.Controls.Add("Forms.Image.1") 'set some property of the class
oneImg.Init 'run some setup macro of the class
End Sub
Run Code Online (Sandbox Code Playgroud)

在名为“clsImg”的类模块中

Public WithEvents myPic As MSForms.Image

Public Sub Init() 'can't put in Class_Initialise as it is called before the set statement - so myPic is still empty at that point
myPic.Picture = LoadPicture(path/image)
End Sub

Public Sub myPic_MouseDown(ByVal Button As Integer, ByVal Shift As …
Run Code Online (Sandbox Code Playgroud)

excel events vba class userform

6
推荐指数
1
解决办法
845
查看次数

检查值是否可迭代vba

在许多语言中,可以检查对象是否可迭代,如何为VBA执行此操作?

我可以试试:

Function isIterable(obj As Variant) As Boolean
    On Error Resume Next
    Dim iterator As Variant
    For Each iterator In obj
        Exit For
    Next
    isIterable = Err.Number = 0
End Function
Run Code Online (Sandbox Code Playgroud)

但我想知道是否有内置或更好的方法?

excel vba excel-vba

6
推荐指数
1
解决办法
190
查看次数

在VBA中使用自定义枚举器实现类似Python的生成器

在VBA中,如果你想要一个迭代Range像Python对象,你做的东西像这样.然而,这种方法涉及一次性构建整个范围:

Set mCollection = New Collection
Dim i As Long
For i = startValue To endValue
    mCollection.Add i
Next
Run Code Online (Sandbox Code Playgroud)

...如果你想要制作一个非常大的范围,这是很糟糕的,因为它需要很长时间和很多内存来构建该集合.这就是发电机的用途; 它们在循环时生成序列中的下一个项目.

现在,如果您希望一个类可以迭代,它必须返回一个[_NewEnum],这是通过Set关键字完成的.这告诉我一个For...Each循环只需要引用一个Enum,因为Set关键字只分配指向返回变量的指针,而不是实际值.

这为一些杂耍提供了空间:

  • For...Each(此后称为"迭代器")需要一些内存来指示所提供的内容[_NewEnum]; 枚举对象指针的引用
  • 自定义类可以随时[_NewEnum]从封装的集合生成指针
  • 也许因此,如果类知道Iterator在内存中寻找枚举指针的位置,它可以用指向不同枚举对象的指针覆盖该位内存.

换一种说法:

  • For...Each循环的第一次迭代中,我的类返回一个变量,其值是指向一个枚举的指针.变量驻留在内存中给定的位置VarPtr(theVariable)
  • 下一次迭代,我手动调用我的类的方法,生成第二个枚举
  • 之后,该方法继续通过在变量指针给出的地址处覆盖第一个枚举对象的指针,并将其替换ObjPtr()为第二个枚举的地址.

如果这个理论是正确的,那么For Each循环现在将持有对不同值的引用[_NewEnum],因此会做出不同的事情.


这是我试图这样做的方式:

发电机:NumberRange类模块

注意:必须导入以保留属性.

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True …
Run Code Online (Sandbox Code Playgroud)

excel foreach enums vba excel-vba

6
推荐指数
1
解决办法
199
查看次数

VBA.CBlah和CBlah之间的区别

奇怪的观察:

通常,当我想将地址保存到变量中的函数时,我会执行以下操作:

Function getAddress(ByVal func As LongPtr) As LongPtr
    getAddress = func
End Function

Sub printAddress()
    Dim functionPointer As LongPtr
    functionPointer = getAddress(AddressOf myFunc)
    Debug.Print functionPointer
End Sub
Run Code Online (Sandbox Code Playgroud)

但是我发现我可以使用1缸套

functionPointer = VBA.CLngPtr(AddressOf myFunc)
Run Code Online (Sandbox Code Playgroud)

functionPointer = CLngPtr(AddressOf myFunc)
Run Code Online (Sandbox Code Playgroud)

...不起作用并加薪

编译错误:

预期:表达

这是怎么回事?据我所知,唯一的区别CLngPtr是在全局变量(class?)中声明了,而VBA.CLngPtr显式限定了声明,但我不知道为什么这会导致观察到的行为(它们都指向同一个函数,但没有他们?)

vba addressof

6
推荐指数
1
解决办法
78
查看次数

没有循环的整数到布尔/位数组

我有一个数字(比如5),我首先要将其转换为二进制(101),然后在VBA中拆分成一个位数组{1,0,1}或布尔值{True,False,True}

有没有办法在没有循环的情况下做到这一点?

我可以转换为二进制文件而无需使用工作表公式在我的代码中循环,如下所示

myBinaryNum = [DEC2BIN(myDecInteger,[places])]
Run Code Online (Sandbox Code Playgroud)

但是我被告知工作表功能非常低效,而且这个功能特别有限.

我不知道如何在没有循环数字的情况下拆分成数组MID.有没有像strConv数字一样的东西?

arrays excel vba excel-vba

5
推荐指数
1
解决办法
1109
查看次数

过滤列表的算法

我已经实现了我认为在 VBA 中过滤 a 的相当垃圾的方法System.Collections.ArrayList。该代码采用一个列表和一个项目/比较值来过滤掉。它循环遍历列表并删除匹配的项目。然后它重新启动循环(因为你不能For Each同时.Remove

Public Sub Filter(ByVal testValue As Object, ByVal dataSet As ArrayList)
'testValue and the items in `dataSet` all Implement IComparable from mscorlib.dll
'This allows comparing objects for equality
'i.e. obj1.CompareTo(obj2) = 0 is equivalent to obj1 = obj2
    Dim item As IComparable
    Dim repeat As Boolean
    repeat = False
    For Each item In dataSet
        If item.CompareTo(testValue) = 0 Then   'or equiv; If item = testValue
            dataSet.Remove item
            repeat = …
Run Code Online (Sandbox Code Playgroud)

algorithm vba arraylist filter time-complexity

5
推荐指数
1
解决办法
1032
查看次数

NOT的怪异行为

我遇到了一些布尔变量的怪异行为;下面的代码同时打印“ Hello”和“ There”,意思是resultNOT result都计算为True

Dim result As Boolean
result = PostMessage(Application.hWnd, 275, 0, 0)
Debug.Print "Post message: "; result
If result Then Debug.Print "Hello"
If Not result Then Debug.Print "There"
Run Code Online (Sandbox Code Playgroud)

产出

Post message: True
Hello
There
Run Code Online (Sandbox Code Playgroud)

根据文档,PostMessage的声明如下:

Dim result As Boolean
result = PostMessage(Application.hWnd, 275, 0, 0)
Debug.Print "Post message: "; result
If result Then Debug.Print "Hello"
If Not result Then Debug.Print "There"
Run Code Online (Sandbox Code Playgroud)

带有评论:

如果函数成功,则返回值为非零。

这是我在VBA中的方法:

Public Declare Function PostMessage Lib "user32" Alias "PostMessageA" …
Run Code Online (Sandbox Code Playgroud)

winapi vba boolean

5
推荐指数
1
解决办法
139
查看次数

VBA 如何确定非 IUnknown 引用类型的生命周期?

我试图理解是什么决定了不同数据位的生命周期;VBA 解释器如何知道释放给定变量的关联内存是安全的。这是我到目前为止发现的:

值类型

简单值类型使用作用域来确定生命周期;例如,函数内的 med 将一直保留,直到命中一条Long语句。目前,我还不清楚确切的机制,但我想象 VBA 解释器会保留给定范围内所有变量的列表,并使用和释放与它们关联的所有内存。DimExit/End *VarPtrLenB

对象类型

同时对象都派生于IDispatch基于COMIUnknown接口的接口。因此,对象使用引用计数来确定生命周期。因此,保存对对象(基本上是 a LongPtr)的引用的变量在超出范围时将被覆盖(类似于值类型),但就在发生这种情况之前,VBA 解释器会调用IUnknown_Release对象IUnknown的接口(或更准确地说,变量/块中保存的任何接口With)。

因此,每当引用计数降至零时,COM 对象就有责任清理并释放自己的实例内存

其他参考类型

不过,还有其他引用类型(即,类型不存储为像 Double 和 Long 那样的原始数据,而是作为指向其他地方的完整数据的指针)。例如:

  • 弦乐
  • 数组
  • UDT
  • 任何通过 ByRef 传递的内容

现在,由于这些不是严格的值类型,VBA 不能仅使用范围来确定生命周期;让我们假设某个函数Dim是一个数组,当函数 s 时,数组就超出了范围End。如果 VBA 的默认行为是调用SafeArrayDestroy数组指针,那么一旦变量超出范围,内存就会被释放。但是,如果数组是函数的返回值,会发生什么情况 - 现在,当变量超出范围或事情会中断时,VBA 无法释放基础数据。如果数组不使用引用计数,那么VBA如何知道是否释放它?

类似地,对于其他类型 - 任何线索到底决定了这些半值半引用类型的生命周期(它们都是在没有运算符的情况下分配的,Set所以我猜它们不是严格的引用类型)

com excel vba safearray lifetime

5
推荐指数
0
解决办法
157
查看次数

Excel 函数无需 VBA 从值构造数组

注意:这个问题涉及一些功能,例如可选的 LAMBDA 参数和 ISOMITTED 函数,这些功能仅在 Beta 通道中可用(在撰写本文时)(更多信息请参见此处


我试图在 Excel LAMBDA 函数中模仿 VBA 的参数数组,所以想要一个函数:

=ARRAY(arg_1, [arg_2], [arg_3], ...)
Run Code Online (Sandbox Code Playgroud)

{arg_1, arg_2, arg_3, ...}...根据传递的参数数量返回动态调整大小的数组。

如果我知道参数的数量,我可以像这样使用选择函数:

=CHOOSE(SEQUENCE(number_of_args), arg_1, arg_2, arg_3, ...))
Run Code Online (Sandbox Code Playgroud)

但我不想将参数数量作为参数传递,我希望它是动态的。一个想法是进行二分搜索ISOMITTED(arg_n)以找到第一个丢失的参数。但这仍然硬编码了我的 ARRAY 函数的参数数量上限,更不用说创建一个讨厌的硬编码二叉树了。


使用 vba 这当然很容易:

Public Function ARRAYFROMARGS(ParamArray args()) As Variant
    ARRAYFROMARGS = args
End Function
Run Code Online (Sandbox Code Playgroud)

调用 ARRAYFROMARGS 的演示

尽管这只能接受可以强制转换为变体的值,因此不能接受 lambda 或链接数据类型等。这使得非 VBA 版本更加灵活。

excel lambda vba excel-formula dynamic-arrays

5
推荐指数
1
解决办法
1012
查看次数