How to integrate IM application with Office without Lync installed?

Manoj 86 Reputation points
2022-01-19T11:01:31.647+00:00

I am trying to integrate an IM application with Office 2019 where Lync/Skype for Business is not installed. The official Microsoft doc says :

If you are integrating using Office Standard, you need to extract the type library and install it on the target machine.

Following this, I have extracted the Unified Collaboration API 1.0 Type Library using OLE/COM Object Viewer. Then I compiled the IDL file using MIDL compiler and created a TLB file. Then I created an assembly called UCCollaborationLib.dll using tlbimp and referenced this dll inside the IM client application to implement all the required interfaces like IUCOfficeIntegration.

When Lync/Skype for Business is installed, the integration works. When Outlook starts, it looks for the keys from the registry and calls the GetAuthenticationInfo method of the IUCOfficeIntegration interface which is implemented by our class PresenceProvider.

After uninstalling Lync/Skype for Business, the type library Unified Collaboration API 1.0 Type Library is unregistered along with it.

When the IM application starts, it registers the tlb file using OleAut32.LoadTypeLibEx. After this, the type library is visible in the OLE/COM Object Viewer but now it is referencing our tlb file instead of lync.exe which was being referenced earlier when Lync/Skype for Business was installed.

Now when Outlook starts, it is fetching the keys from the registry(observed through Procmon) but it is not calling the GetAuthenticationInfo method.
Does anybody know what can we do to debug and solve this issue?

C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,001 questions
Office Development
Office Development
Office: A suite of Microsoft productivity software that supports common business tasks, including word processing, email, presentations, and data management and analysis.Development: The process of researching, productizing, and refining new or existing technologies.
3,971 questions
{count} votes

Accepted answer
  1. RLWA32 45,691 Reputation points
    2022-01-21T15:25:43.36+00:00

    I observed two issues with respect to the new OLPresenceProvider sample solution.

    The code is attempting to register type libraries per-machine (HKLM). Per-machine registration requires that the code is running with elevated privileges. I would expect the sample code to return a COM error (hr < 0) but it does not. I don't know why but I have made a change in the type library registration function that properly indicates failure when an un-elevated process attempts per-machine registration.

    After implementing this change and running the COM Server with elevated privileges type library registration succeeded for interopExtension.tlb and lyncEndorser.tlb. However, registration failed for lync.tlb and this is the reason why the IUCOfficeIntegration interface is not in the registry.

    It is my belief that something is wrong with the lync.tlb type library created from the IDL shown by OleView when browsing the real type library. See if you can extract the lync type library as a binary file from the Office executable.

    Register a type library -

        public static void Register(string tlbPath)
        {
            Trace.WriteLine($"Registering type library:");
            Trace.Indent();
            Trace.WriteLine(tlbPath);
            Trace.Unindent();
            ComTypes.ITypeLib typeLib;
    
            int hr = OleAut32.LoadTypeLibEx(tlbPath, OleAut32.REGKIND.REGKIND_NONE, out typeLib);
            if (hr < 0)
            {
                Trace.WriteLine($"Registering type library failed: 0x{hr:x}");
                return;
            }
    
            hr = OleAut32.RegisterTypeLib(typeLib, tlbPath, string.Empty);
            if(hr < 0)
            {
                Trace.WriteLine($"Registering type library failed: 0x{hr:x}");
            }
    
        }
    

    Add to Ole32 class -

                [DllImport(nameof(OleAut32), CharSet = CharSet.Unicode, ExactSpelling = true)]
                public static extern int RegisterTypeLib(
                    [In] ComTypes.ITypeLib typeLib,
                    [In, MarshalAs(UnmanagedType.LPWStr)] string fileName,
                    [In, MarshalAs(UnmanagedType.LPWStr)] string helpDir);
    

1 additional answer

Sort by: Most helpful
  1. RLWA32 45,691 Reputation points
    2022-01-27T10:21:23.627+00:00

    Following is the minimal C++ client code. I had to post it as an Answer due to the 1600 character limit for comments.

    #include <Windows.h>
    
    #include <stdio.h>
    #include <tchar.h>
    #include <atlbase.h>
    
    #import "C:\Users\RLWA32\source\repos\Rlwa32\OLPresenceProvider\DLL\lync.tlb" raw_interfaces_only raw_native_types \
      named_guids no_namespace auto_rename
    
    int main()
    {
        WCHAR szCLSid[] = L"{A8570DCA-CD23-413C-A8E1-53039C66302A}"; // Guid from PresenceProvider.cs source code
        CLSID clsid;
    
        HRESULT hr = CoInitialize(NULL);
        if (SUCCEEDED(hr))
        {
            CComPtr<IUnknown> pUnk;
    
            CLSIDFromString(szCLSid, &clsid);
    
            hr = CoCreateInstance(clsid, nullptr, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&pUnk));
            if (SUCCEEDED(hr))
            {
                CComBSTR bstrInfo;
                OIFeature oiFeatures{};
                CComPtr<IUCOfficeIntegration> piuc;
    
                hr = pUnk->QueryInterface(&piuc);
                if (SUCCEEDED(hr))
                {
                    CComBSTR version(L"15.0.0.0");
    
                    hr = piuc->GetAuthenticationInfo(version, &bstrInfo);
                    if (SUCCEEDED(hr))
                        wprintf_s(L"GetAuthenticationInfo returned %s\n", (LPCWSTR)bstrInfo);
                    else
                        wprintf_s(L"GetAuthenticationInfo failed with 0x%X\n", hr);
    
                    hr = piuc->GetSupportedFeatures(version, &oiFeatures);
                    if (SUCCEEDED(hr))
                        wprintf_s(L"GetSupportedFeatures returned %d\n", oiFeatures);
                    else
                        wprintf_s(L"GetSupportedFeatures failed with 0x%X\n", hr);
                }
                else
                {
                    CComDispatchDriver pDisp;
                    hr = pUnk->QueryInterface(&pDisp);
                    if (SUCCEEDED(hr))
                    {
                        CComVariant version(L"15.0.0.0"), vResult, vResult2;
    
                        hr = pDisp.Invoke1(L"GetAuthenticationInfo", &version, &vResult);
    
                        if (SUCCEEDED(hr))
                            wprintf_s(L"GetAuthenticationInfo returned %s\n", (LPCWSTR)V_BSTR(&vResult));
                        else
                            wprintf_s(L"IDispatch::Invoke failed with 0x%X\n", hr);
    
                        hr = pDisp.Invoke1(L"GetSupportedFeatures", &version, &vResult2);
    
                        if(SUCCEEDED(hr))
                            wprintf_s(L"GetSupportedFeatures returned %d\n", V_I4(&vResult2));
                        else
                            wprintf_s(L"IDispatch::Invoke failed with 0x%X\n", hr);
                    }
                }
            }
            else
                wprintf_s(L"CoCreateInstance faied with 0x%X\n", hr);
        }
    
        CoUninitialize();
    
        return 0;
    }
    

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.