Debug
statements are indeed different from everything else. If you look for a Debug
module in the Object Browser, you won't find it, even with hidden classes and members shown.
Debug.Print
and Debug.Assert
statements are literally baked into the language [parser] itself - the comma here doesn't mean "argument separator", instead it's a special-form syntax that is [apparently] reserved for Print
methods (note: VBA user code cannot use Print
for a method name, it's reserved).
So Debug
statements are basically special kinds of keywords. IntelliSense / parameter quick-info is shown for argument list syntax elements, but syntactically, the "arguments" of a Debug.Print
are an output list, exactly like the Print statement does.
Note that the VBE automatically turns a ?
token into a Print
statement:
Debug.? --> Debug.Print
There's quite a bit of historical baggage with Print
: the keyword/command (and its ?
shorthand) was used in old BASIC dialects to output things to a screen... or an actual [dot matrix!] printer.
So the short answer is, Debug
statements are made with keywords, not member calls - and that's why IntelliSense isn't of any use with them, since there aren't any arguments.
The Rubberduck project has a fun story with these statements... because it isn't really possible to parse a typical Debug.Print
statement any differently than any other implicit callStmt
(i.e. it looks and parses like any other procedure call), we had to give the statement its own dedicated parser rule, and "declare" a fake DebugClass
module and make Print
a "method" of that "class" in order to be able to track uses like we do with other early-bound identifier references:
But there really isn't such a thing: statements with an output list are baked into the language at the parser & compiler level, whereas literally every other member call you ever made in VBA was a member of some module - hit F2 and browse the members of the VBA standard library: you'll find CLng
type conversion, Now
and DateAdd
date/time functions, MsgBox
, DoEvents
, and so many others - all belong to some module. But Debug
statements are closer to a Stop
or Resume
keyword, handled at a lower level.
Further proof that there's more than meets the eyes - other than the simple fact that default syntax highlighting in the VBE will highlight both Debug
and Print
in bright keyword-blue, if you compile a COM-visible class written in C#:
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("6F25002E-2C9F-4D77-8CCB-A9C0E4FB2EF1")]
public interface ITestClass
{
[DispId(1)]
void Print(string value);
[DispId(2)]
void DoSomething();
}
[ComVisible(true)]
[ComDefaultInterface(typeof(ITestClass))]
[ClassInterface(ClassInterfaceType.None)]
[Guid("6F25002E-2C9F-4D77-9624-6CA79D4F088A")]
[ProgId("PrintTest.Class1")]
[EditorBrowsable(EditorBrowsableState.Always)]
public class Class1 : ITestClass
{
public void Print(string value)
{
/* no-op */
}
public void DoSomething()
{
/* no-op */
}
}
..and then invoke it from VBA code...
The DoSomething
method can be invoked, but the Print
method will throw error 438 - just like it would if you tried to qualify it with anything other than Debug
. So how come Print
works in an Access report's code-behind then?
The interface isn't documented so this is pure speculation, but there's an IVBPrint
interface that looks very much like something VBA would be looking for:
[
odl,
uuid(000204F0-0000-0000-C000-000000000046),
nonextensible
]
interface IVBPrint : IUnknown {
HRESULT _stdcall WriteText([in] BSTR strText);
[propput]
HRESULT _stdcall Column([in] long retVal);
[propget]
HRESULT _stdcall Column([out, retval] long* retVal);
};
If that's the case, then error 438 is just VBA's way to say "IVBPrint implementation not found"