Search code examples
.netmsscci

Implementing MSSCCI providers in .Net


I would like to implement a MSSCCI provider, however if at all possible I would like to implement it in .Net (so my MSSCCI provider is in fact a thin wrapper around a .Net implementation)

  • Is this possible?
  • Is this a good idea?

I know that implementing it in .Net would mean that anyone using my MSSCCI provider would be forced to host the .Net framework inside their process - is this an unreasonable request? Also what other limitations would I need to consider if I were to implement it in .Net?


Solution

  • It's possible, and relatively easy. I developed one some time ago and it works fine. I used COM interoperability from C++ to C#.

    So, you will create two dlls. The C++ one is only a wrapper that implements the API pass the calls to a COM in C#. The C# one must be registered as a COM component with regasm /codebase mycomlibrary.dll

    Here are some guidelines to implement it. In the code sample I only implement the SccInitialize function as an example. Hope it helps.

    This is the C++ component:

    #include <comutil.h>
    
    /**********************************************************************************************************/
    // Imports the COM object that implements the SCC API in .NET
    /**********************************************************************************************************/
    #import "SccCOMServer.tlb" no_namespace named_guids
    
    static int s_nInitializedCount = 0;
    
    /**********************************************************************************************************/
    // Starting point of the dll
    /**********************************************************************************************************/
    BOOL APIENTRY DllMain( 
                          HANDLE hModule, 
                          DWORD  ul_reason_for_call, 
                          LPVOID lpReserved
                          )
    {
        switch (ul_reason_for_call)
        {
            case DLL_PROCESS_ATTACH:
            case DLL_THREAD_ATTACH:
            case DLL_THREAD_DETACH:
            case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }
    
    /**********************************************************************************************************/
    // Variable with a instance of the COM object
    /**********************************************************************************************************/
    
    ISccCOMServer *mCpi = NULL;
    
    
    /**********************************************************************************************************/
    // Utility functions
    /**********************************************************************************************************/
    
    void BSTR2T(BSTR s1, LPSTR s2)
    {
        _bstr_t s(s1, false);
        strcpy(s2, s);
    }
    
    char* ConvertBSTRToLPSTR (BSTR bstrIn)
    {
        LPSTR pszOut = NULL;
        if (bstrIn != NULL)
        {
            int nInputStrLen = SysStringLen (bstrIn);
    
            // Double NULL Termination
            int nOutputStrLen = WideCharToMultiByte(CP_ACP, 0, bstrIn, nInputStrLen, NULL, 0, 0, 0) + 2; 
            pszOut = new char [nOutputStrLen];
    
            if (pszOut)
            {
                memset (pszOut, 0x00, sizeof (char)*nOutputStrLen);
                WideCharToMultiByte (CP_ACP, 0, bstrIn, nInputStrLen, pszOut, nOutputStrLen, 0, 0);
            }
        }
        return pszOut;
    }
    
     /**********************************************************************************************************/
    //                                      IMPLEMENTATION OF THE FUNCTIONS
    /**********************************************************************************************************/
    
    
    /**********************************************************************************************************/
    // Initialization and Housekeepeng Functions
    /**********************************************************************************************************/
    
    SCCEXTERNC SCCRTN EXTFUN __cdecl SccInitialize(
        LPVOID * ppContext, 
        HWND hWnd, 
        LPCSTR lpCallerName,
        LPSTR lpSccName, // [In, out]
        LPLONG lpSccCaps, // [Out]
        LPSTR lpAuxPathLabel, // [In, out]
        LPLONG pnCheckoutCommentLen, // [Out]
        LPLONG pnCommentLen //[Out]
        )
    {
    
        // Initialize COM the first time the function is called
        CoInitialize(0);
        s_nInitializedCount++;
        HRESULT hr = CoCreateInstance(CLSID_ISccCOMServerImpl,
            NULL, CLSCTX_INPROC_SERVER,
            IID_ISccCOMServer, reinterpret_cast<void**>(&mCpi));
    
        long response;
    
        // We need auxiliar strings because out string in COM are BSTR *  
        BSTR bstrSccName;
        BSTR bstrAuxPathLabel;
    
        bstrSccName = T2BSTR(lpSccName);
        bstrAuxPathLabel = T2BSTR(lpAuxPathLabel);
    
        Context *CC = new Context;
        // Calling to the COM equivalent Function
    
        response = mCpi->Initialize(CC, (long) hWnd, lpCallerName, &bstrSccName, lpSccCaps, &bstrAuxPathLabel, 
            pnCheckoutCommentLen, pnCommentLen);
    
        *ppContext = (void *)CC;
    
        // Converting the strings
        BSTR2T(bstrSccName, lpSccName);
        BSTR2T(bstrAuxPathLabel, lpAuxPathLabel);
        return response;
    
    }
    

    And then the C# part is simpler:

    [Guid("C6659361-1625-4746-931C-36014B146679")]
    public class ISccCOMServerImpl : ISccCOMServer
    {
        public int Initialize(
            out Context ppContext,
            IntPtr hWnd,
            string lpCallerName,
            ref string lpSccName, // out
            out int lpSccCaps, // out
            ref string lpAuxPathLabel, // out
            out int pnCheckoutCommentLen, // out
            out int pnCommentLen //out
            )
        {
           //your managed code here!
        }
    

    }