Consuming MSSCCI plugin - calling SccInitialize

Hi

I'm trying to write an editor (in C#) that would be able to connect to source control using MSSCCI, much like Visual Studio does. I've started by importing SccInitialize method from the VSS plugin C:\Program Files\Microsoft Visual SourceSafe\ssscc.dll.

I can call SccGetVersion() and it works fine, but when I call SccInitialize, it returns -15 (#define SCC_E_NONSPECIFICERROR -15). SccInitialize populates and returns lpSccName (puts "Source Safe" there) and lpAuxPathLabel (puts "Source Safe Database" there).

Is there anything special I have to do to consume VSS plugin from my custom code

Is there a requirement for a license

My code is below. Thanks for help.

[DllImport("C:\\Program Files\\Microsoft Visual SourceSafe\\ssscc.dll")]

private static extern int SccInitialize(ref IntPtr ppContext, IntPtr hWnd, String lpCallerName, byte[] lpSccName, IntPtr lpSccCaps, byte[] lpAuxPathLabel, IntPtr pnCheckoutCommentLen, IntPtr pnCommentLen );

//--------------------

IntPtr ppContext = IntPtr.Zero;
IntPtr hWnd = this.Handle; // call it from C# Window Form app
string lpCallerName = "SF Test";
byte[] lpSccName = new byte[SCC_NAME_LEN - 1];
IntPtr lpSccCaps = IntPtr.Zero;
byte[] lpAuxPathLabel = new byte[SCC_NAME_LEN - 1];
IntPtr pnCheckoutCommentLen = IntPtr.Zero;
IntPtr pnCommentLen = IntPtr.Zero;

int nRes = SccInitialize(ref ppContext, hWnd, lpCallerName, lpSccName, lpSccCaps, lpAuxPathLabel, pnCheckoutCommentLen, pnCommentLen);

// RETURNS -15; lpSccName RETURNS "Source Safe"



Answer this question

Consuming MSSCCI plugin - calling SccInitialize

  • pilarp

    Hi

    I'm also trying to write C# tools that use MSSCCI to connect to Visual Source Safe, Perforce, AlienBrain and CVS. If anyone has any code that could help me get started, I would be much apprieciated.

    I'm currently stuck at the same place as the original post, with SccInitialize. I've read all that has been said, but really don't like the unsafe or byte array ideas. There must be some pure C# way to do this better.

    Thanks
    Kevin

  • iortizvictory

    The database path is the lpAuxPath parameter to SccOpenProject(). It is the folder of the database, not the full path of the srcsafe.ini file.

    In any case, don't try to create the ProjectName and AuxPath strings yourself. Use whatever you can read from mssccprj.scc files or whatever you previously got back by calling SccGetProjPath (and possible saved it sowmewhere).

    For VSS you can recreate the 2 string (database path is know, the physical file names you can obtain them by running "ss.exe phys" or by using IVSS), but this breaks the whole idea of using MSSCCI. If you just intend to target VSS, you will be much better using IVSS interface instead of MSSCCI.

    Alin


  • vampa

    I have no idea what value you're looking at, but it's not what VSS returns for the capability. VSS returns SCC_CAP_REMOVE, SCC_CAP_REENTRANT, SCC_CAP_DIFFALWAYS, so you should have looked at an odd value much bigger than what you have. All VSS versions support history, properties and PopulateList commands. The program using unsafe code from the earlier post produces the right result.

    As I said, you're pretty much on your own from now on... What you're doing is not supported by Microsoft, and it looks like you'll need help pretty much with every function you intend to call...

    DllExport will likely cause you more trouble. You attach the attribute at compile time, hardcode the location on disk of the MSSCCI dll you'll be calling into, and expects the runtime to load the dll for you on the first MSSCCI invocation. First, the code will throw if the dll is not found in the hardcoded path (I have installed VSS on D: drive). Second, this is not MSSCCI-like (where you have to read the installed providers from registry, read the SccServerPath string from registry and use LoadLibrary() at runtime to load that specific scc provider. Even if you try code like

    [DllImport("ssscc.dll")]

    private static extern int SccInitialize(IntPtr* ppContext, IntPtr hWnd, String lpCallerName, byte[] lpSccName, long* lpSccCaps, byte[] lpAuxPathLabel, long* CheckoutCommentLen, long* CommentLen);

    [DllImport("kernel32.dll")]

    private static extern long LoadLibrary(String lpstrDllName);

    String strDllName = "blah, blah"; // construct dll name here

    int ret = LoadLibrary(strDllName);

    int nRes = SccInitialize(....);

    You have solved the problem of loading the MSSCCI dll from the current install location, but you'd still be bound of using only SourceSafe (ssscc.dll), in which case you'd be better off using IVSS instead of MSSCCI.

    I strongly suggest calling MSSCCI from a managed C++ dll if you intend to interface MSSCCI providers with a .NET application, and you won't have any of these problems (use implicit PInvoke instead of explicit PInvoke with DllImport).

    Alin

     


  • Jaw9

    I don't think we can help you with every error you'll encounter while writing your IDE; you'll be pretty much on your own using MSSCCI to connect to VSS... MSSCCI is supported only to integrate your source control provider into VisualStudio.

    If you'd pass 0 instead of SCC_OP_SILENTOPEN for flags, you'd probably see dialogs from VSS telling what's wrong. My bet is that the project name you passed in is just something you came up with, and not something returned previously by GetSccPath(), and VSS fails to find the project. VSS project names are not like $/TestFolder, they look more like "$/TestFolder", GQGBAAAA

    Alin


  • Aruna Muthyala

    Thanks, I could for now sidestep SccGetProjPath (which works but throws invalid argument exception somewhere in VSS library) by following your advice.

    I'm getting strange report in lpSccCap bits returned by SccInitialize: I've tried SccPopulateList, SccPopulateDirList and SccQueryInfo functions and none of them worked. When I looked at the bits, it turned out that all these capabilities are not set by VSS. If none of this works (at least with VSS), how can you get the list of files under VSS and properties for each file

    Thank you

    _lpSccCaps
    1833848

    _lpSccCaps & SCC_EXCAP_POPULATELIST_DIR
    0
    _lpSccCaps & SCC_CAP_POPULATELIST
    0

    _lpSccCaps & SCC_CAP_QUERYINFO
    0

    _lpSccCaps & SCC_CAP_SCCFILE
    0
    _lpSccCaps & SCC_CAP_QUERYINFO
    0
    _lpSccCaps & SCC_CAP_GETPROJPATH
    512
    _lpSccCaps & SCC_CAP_REENTRANT
    0
    _lpSccCaps & SCC_EXCAP_POPULATELIST_DIR
    0
    _lpSccCaps & SCC_CAP_HISTORY
    8
    _lpSccCaps & SCC_CAP_PROPERTIES
    16
    _lpSccCaps & SCC_CAP_DIRECTORYSTATUS
    524288


  • sapeluso

    I've noticed that if I pass 0 for the flag, it returns SCC_E_PROJSYNTAXERR, so I guess you are right - I have to run SccGetProjPath to get project name correctly.

    One quick question: I could't find in the documentation where you supply actual database name, i.e. if you have Source Safe, which parameter should accept the path to it: Program Files\VSS\srcsafe.ini

    thanks for your help


  • Vinay Ahuja

    Hello Dimmi3,

    I am currently attempting to write a consumer for the MSSCCI plugin so that I can connect to PVCS. Could you please post more sample code from your implementation

    Thanks,

    Brad



  • fmrjj

    Hi,

    First you have some bugs in declaring the array sizes. They should look like this.

             byte[] lpSccName = new byte[SCC_NAME_LEN + 1];

             byte[] lpAuxPathLabel = new byte[SCC_AUXLABEL_LEN + 1];

    Second, the ppContext, lpSccCaps, pnCheckoutCommentLen, pnCommentLen are output parameters from SccInitialize(). You cannot initialize them to InitPtr.Zero, because VSS will crash when he's going to dereference the NULL pointer trying to return the values.

    If you would enable Native debugging in Project's Properties, you'd see something like this in output window: First-chance exception at 0x5c51726d (ssscc.dll) in WindowsApplication3.exe: 0xC0000005: Access violation writing location 0x00000000.

    The AccessViolation is trapped by ssscc.dll code which returns nicely to you SCC_E_NONSPECIFICERROR, so your application doesn't crash.

    I'm not very good with C#, so I don't know if it's possible to initialize an IntPtr to point to specific valid memory address.

    An ugly solution might be  to declare your function as

    private static extern int SccInitialize(ref byte[] ppContext, IntPtr hWnd, String lpCallerName, byte[] lpSccName, byte[] lpSccCaps, byte[] lpAuxPathLabel, byte[] pnCheckoutCommentLen, byte[] pnCommentLen);

    to use byte arrays, and declare the variables like

              byte[] ppContext = new byte[4];

              byte[] pnCheckoutCommentLen = new byte[4];

              byte[] pnCommentLen = new byte[4];

              byte[] lpSccCaps = new byte[4];

    but this is getting really ugly and it will be nasty to get back the values of the returned parameters. If you find out a better solution with IntPtr, let me know ...

    Barry pointed out that you can use "Unsafe" keyword around the class then you can write code like this

        public unsafe partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }

            [DllImport("C:\\Program Files\\Microsoft Visual SourceSafe\\ssscc.dll")]
           
    private static extern int SccInitialize(IntPtr * ppContext, IntPtr hWnd, String lpCallerName, byte[] lpSccName, long * lpSccCaps, byte[] lpAuxPathLabel, long *CheckoutCommentLen, long * CommentLen);

            private const int SCC_AUXLABEL_LEN = 31;
            private const int SCC_NAME_LEN = 31;
            private const int SCC_CAP_DIFF = 4;

            private void button1_Click(object sender, EventArgs e)
            {
               
    long lSccCaps = 0;
                long lCheckoutCommentLen = 0;
                long lCommentLen = 0;

                IntPtr hWnd = this.Handle; // call it from C# Window Form app
                string lpCallerName = "SF Test";
                byte[] lpSccName = new byte[SCC_NAME_LEN + 1];
                byte[] lpAuxPathLabel = new byte[SCC_AUXLABEL_LEN + 1];
                IntPtr pContext = new IntPtr();

                int nRes = SccInitialize(&pContext, hWnd, lpCallerName, lpSccName, &lSccCaps, lpAuxPathLabel, &lCheckoutCommentLen, &lCommentLen);
                MessageBox.Show(nRes.ToString());
            }
        }

    I have no idea though how you'd use unsafe code to pass in callbacks (e.g. for functions like SccPopulateList), and for output structures. 

    My suggestion is to write a Managed C++ dll to interact with MSSCCI, and there you can use #pragma unmanaged and #pragma managed to include the scc.h file (so no need to declare more wrapping functions), etc

    Alin


  • CMP

    Alin,

    thanks for your answer. I kind of figured it out last night - I realized that I have to create all of the par's memory allocation myself. The byte array worked out fine:

    String user="Dmitri" -> byte[] lpUser = Encoding.ASCII.GetBytes(user) - would get byte array from String in C#

    and string text = enc.GetString(byteArray).Replace("\0", "") would get string out of byte array.

    For things like LPLONG lpSccCaps which are basically in/out pointers, it works if you malloc it manually before calling SCC method: IntPtr lpSccCaps = Marshal.AllocHGlobal(4);

    The only trick is to always clean it up to avoid memory leaks: Marshal.FreeHGlobal(lpSccCaps);

    So I'm past this issue and on to the second one:

    I'm trying to call SccOpenProject() after a successful call to SccInitialize;

    I pass ppContext, hWnd, user name, project name ($/TestFolder), localProjPath (c:\temp), lpAuxProjPath (c:\program files\vss\srcsafe.ini), comment (""), null for lpTextOutProc and dwFlags = SCC_OP_SILENTOPEN.

    The project $/TestFolder exists in my local database c:\program files\vss but every time SccOpenProject returns SCC_I_OPERATIONCANCELED (2) - does anyone know what is wrong here

    Thank you


  • Steven Bowman

    Please check this C# solution:

    http://formtoini.narod.ru/Download/Working_with_SSSCC_from_C-Sharp.rar

    Note that clicking on this link doesn't start download immediately: you'll need to confirm your intention by clicking the link once more on the new Web-page appeared; the page will be in Russian but the link is easily found at this page; it's a public service - so there are such silly things.

    Please report any bugs to a.yumashin@gmail.com.


  • Tim Raleigh

    Thanks again for the reply, I'm switching to C++.

    Btw, I did not use DllImport for scc library, instead I located all installed providers by looking at the registry and then just used LoadLibrary with the name retrieved from the registry. After, I've created a delegate for each Scc method and called it using GetDelegateForFunctionPointer()

    internal delegate int MySccQueryInfo(IntPtr ppContext, long nFiles, String[] lpFileNames, [In, Out] long[] lpStatus);

    .....

    IntPtr procaddr = GetProcAddress(_CCIlib, "SccQueryInfo");

    MySccQueryInfo myGetPP = (MySccQueryInfo)Marshal.GetDelegateForFunctionPointer(procaddr, typeof(MySccQueryInfo));

    int nRes = myGetPP(_ppContext, nDirs, fileNames, lpStatus);


  • Consuming MSSCCI plugin - calling SccInitialize