Search code examples
phpstormtypelibphpstorm-2021.1

IntelliSense support for COM interfaces contained in a type library


Is there a way get normal IntelliSense functionality in PhpStorm for types contained in a COM type library?

In FoxPro I get full IntelliSense as soon as a variable is declared as a COM interface (it pulls the info from the registry); in Delphi and Visual Studio I can have the IDE create some import artifacts for the type libraries (import unit, interop assembly) to make IntelliSense work. However, so far I couldn't find a way to make IntelliSense work with type libraries in PhpStorm.

All mentions of COM type libraries that I found referred only to PHP's ability to load constants (com_load_typelib()), but I want method and parameter info. I wouldn't mind generating files for PhpStorm from my type libraries or even hand-crafting some definitions for the COM interfaces that I use most often. However, I really need normal IntelliSense for the COM interfaces to keep from going crazy.

Is there a way to peel this cat?


Solution

  • As suggested by LazyOne, the manual approach can be as simple as writing a stub declaration and dumping the php file somewhere in the project tree or search path (like an apposite stubs directory):

    <?php
    /** allows observing the server lock/object counts and unloadability state of a COM module that uses the
     * Zrbj.COM.ComServerLocking infrastructure
     * @property-read int $Revision revision of the observer module implementation
     * @property-read string $ServerDLL executable which houses the code for this observer object
     */
    interface ISrvDllObserver
    {
        /** server lock count information (ICounterInfo) for this object's own server module */
        public function GetCountsForOwnModule (): ICounterInfo;
    
        /** server lock count information (ICounterInfo) for a loaded COM module that uses Zrbj.COM.ComServerLocking.
         * @param string $module_name basename without file extension is sufficient unless there are multiple loaded
         * modules with the same basename; use path and/or file extension to disambiguate
         */
        public function GetCountsForLoadedModule (string $module_name): ICounterInfo;
    }
    

    From that point on IntelliSense works perfectly.

    The manual process may give perfect results, but it is exceedingly laborious. I remembered a scriptable object for processing type libraries that shipped with FoxPro and earlier versions of Visual Studio, tlbinf32.dll (+ tlbinf32.chm). The ProgID to start with is TLI.TLIApplication.

    The only extant reference to this tool that I could find at microsoft.com is this old article:

    Visual Basic: Inspect COM Components Using the TypeLib Information Object Library

    The tool still ships with Visual Studio, but it is now called vstlbinf.dll and no longer documented. vstlbinf.dll still refers to the old tlbinf32.chm as its help file but that doesn't get shipped anymore. The old documentation is still useful for understanding the object model, even though it refers to a version that is two decades older. (Note: it may be necessary to register the DLL manually.)

    So I took Delphi and set about taming TLI.TLIApplication. It turned out not to be a whole lot easier than working with ITypeLib, ITypeInfo etc. directly, but the advantage is that TLI.TLIApplication can be used from any scripting language, including PHP itself.

    Here`s a sample of the information that can be ripped from a COM type library for use in a PHP stub:

    <?php
    // 'k:\VS2019\Community\Common7\IDE\vstlbinf.dll' (2021-04-21 10:05:35)
    // processed 2021-05-02 22:39:10 by Zrbj.COM.PhpStubs.pas rev. 2021-05-02
    //
    // Library: TLI
    // Version: 1.0
    // LIBID  : {8B217740-717D-11CE-AB5B-D41203C10000}
    // Comment: TypeLib Information
    // 32 interface(s) and 3 coclass(es)
    //
    // coclasses:
    // * {8B217752-717D-11CE-AB5B-D41203C10000} -> _SearchHelper (ProgID TLI.SearchHelper)
    //   'Helper object for GetMembersWithSubString and multiple TypeLibs'
    // * {8B217746-717D-11CE-AB5B-D41203C10000} -> _TypeLibInfo (ProgID TLI.TypeLibInfo)
    //   'TypeLib information'
    // * {8B21775E-717D-11CE-AB5B-D41203C10000} -> _TLIApplication (ProgID TLI.TLIApplication)
    //   'TLIApplication object'
    
    /// {8B21774B-717D-11CE-AB5B-D41203C10000} dual nonextensible dispatchable
    /** VarType information for parameters and return types
     * @property-read TypeInfo $TypeInfo Type information for VT_PTR VarType
     * @property-read int $TypeInfoNumber TypeInfo number for 0 VarType (Cheaper than TypeInfo property)
     * @property-read variant $TypedVariant Get a variant with this VarType
     * @property-read bool $IsExternalType Is TypeInfo external to this library
     * @property-read TypeLibInfo $TypeLibInfoExternal External typelib. Same as TypeInfo.Parent.
     * @property-read int $PointerLevel Dereferencing level of type
     * @property-read int $VarType VarType of Parameter
     * @property-read int $ElementPointerLevel Dereferencing level for type of an array element
     */
    interface VarTypeInfo
    {
        /** Get bounds for VT_VECTOR array. LBound in column 1, UBound in column 2. */
        public function ArrayBounds (int $Bounds): int;
    }
    
    /// {8B217749-717D-11CE-AB5B-D41203C10000} dual nonextensible dispatchable
    /** Parameter Information
     * @property-read string $Name Name of the object
     * @property-read bool $Optional Optional Parameter
     * @property-read VarTypeInfo $VarTypeInfo VarTypeInfo object for this parameter
     * @property-read bool $Default Default Parameter
     * @property-read variant $DefaultValue Default value
     * @property-read bool $HasCustomData Check if custom data is available
     * @property-read CustomDataCollection $CustomDataCollection Custom data GUIDs and Values
     * @property-read int $Flags Parameter Flags
     */
    interface ParameterInfo
    {
    }
    
    ...
    

    Conspicuously absent are parameter comments, because there is no such thing in Microsoft IDL or type libraries. But on the whole the result of processing type libraries into PHP stubs seems quite satisfactory, and it certainly makes working with COM objects in PHP a lot easier.