See the question and my original answer on StackOverflow

What's important in CDec declaration is the [out] and [retval] attributes combined. Tools that understand it (like VB/VBA) will be capable of compiling calls to this method in a simplified way, masking error handling, so

HRESULT _stdcall CDec(
        [in] VARIANT* Expression,
        [out, retval] VARIANT* pvar

is equivalent to

VARIANT _stdcall CDec([in] VARIANT* Expression);

equivalent here does not mean it's equivalent in binary form, it just means the tools that understand that syntax will be ok to use (and compile in the final binary target) the first expression when they see the second. It also implies that if there is an error (HRESULT failure) then the tool should raise an error by any way it sees fit (VB/VBA will do this).

That's simply "syntactic sugar".

You can write that using MIDL, but also .NET: just create a standard Class Library using Visual Studio and add this sample c# class:

public class Class1
    public object Test(object obj)
        return obj;

Compile that and run the regasm tool to register it, with a command like this:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\regasm "C:\mypath\ClassLibrary1\bin\Debug\classlibrary1.dll" /tlb /codebase

This will register the class as a COM object, and create a C:\mypath\ClassLibrary1\bin\Debug\classlibrary1.tlb type library file.

Now, start Excel (you could use any COM automation compatible client), and add a reference to the ClassLibrary1 (developer mode, VBA editor, Tools / Reference). If you don't see it you may be running with a different bitness. It's possible to use COM for 32-64 communication, but for now, just make sure your client runs at the same bitness as how your ClassLibrary1.dll was compiled.

Once you have the reference, add some VB code, like this.

Sub Button1_Click()
    Dim c1 As New Class1
    output = c1.Test("hello from VB")
End Sub

As you will experience, VB intellisense will show the method like we expect, like in C#, and it works fine.

Now, let's try to use it from C++: create a console project (again, make sure the bitness is compatible), and add this code to it:

#include "stdafx.h" // needs Windows.h

#import "c:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\mscorlib.tlb" // adapt to your context
#import "C:\\mypath\\ClassLibrary1\\bin\\Debug\\classlibrary1.tlb" 

using namespace ClassLibrary1;

int main()

  _Class1Ptr c1(__uuidof(Class1));
  _variant_t output = c1->Test(L"hello from C++");

  wprintf(L"output: %s\n", V_BSTR(&output));

  return 0;

This will also work fine and the code looks close to VB's one. Notice I used Visual Studio magic #import directive which is super cool because it masks many details of COM Automation plumbing (just like VB/VBA does), including bstr and variant smart classes.

Let's click on the Test call and do a Goto Definition (F12), this is what we see:

inline _variant_t _Class1::Test ( const _variant_t & obj ) {
    VARIANT _result;
    HRESULT _hr = raw_Test(obj, &_result);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _variant_t(_result, false);

haha! This is basically what VB/VBA does also undercovers. We can see how exception handling is done. Again, if you do an F12 on _Class1Ptr, this is what you'll see (simplified):

_Class1 : IDispatch
    // Wrapper methods for error-handling

    _variant_t Test (
        const _variant_t & obj );

    // Raw methods provided by interface
      virtual HRESULT __stdcall raw_Test (
        /*[in]*/ VARIANT obj,
        /*[out,retval]*/ VARIANT * pRetVal ) = 0;


Here we are. As you can see, the Test method generated by C# in its binary form is of the [out, retval] form as expected. The rest is all sugar and wrappers. Most COM interfaces methods are, at binary level, designed using [out, retval] because compilers don't support a common compatible binary format for function return.

What VBE7 defines is a dispinterface, again some form of syntactic sugar for defining interfaces on top of COM raw/binary IUnknown interface. The only mystery left is why CDec is defined differently than other methods in VBE7. I don't have an answer for that.

Now, specifically about the module keyword in IDL, IDL is just an abstract definitions (functions, constants, classes, etc.) tool that optionally outputs artefacts (.H, .C, .TLB, etc.) targeted for a specific language (C/C++, etc.) or for specific clients.

It happens that VB/VBA supports TLB's constants and methods. It interprets constants as what they are, and functions in modules as DLL exports from the module's dll name.

So if you create this my.idl file somewhere on your disk:

library MyLib
    module MyModule
        const int MyConst = 1234;

        // note this is the real GetCurrentThreadId from kernel32.dll
        int GetCurrentThreadId();

You can compile a TLB from it like this:

midl c:\mypath\my.idl /out c:\mypath

It will create a my.tlb file that you can reference in VB/VBA. Now from VB/VBA, you have a new function available (intellisense will work on it) called GetCurrentThreadId. It works because Windows' kernel32.dll does export a GetCurrentThreadId function.

You can only create DLL Exports from C/C++ projects (and from other languages/tools like Delphi), but not from VB/VBA, not from .NET.

In fact there are some tricks to create exports in .NET, but it's not really standard: Is is possible to export functions from a C# DLL like in VS C++?