Comanche Posted January 9, 2007 Posted January 9, 2007 Hi, I got a function with the following signature: SCCRTN SccHistory( LPVOID pvContext, HWND hWnd, LONG nFiles, LPCSTR* lpFileNames, LONG fOptions, LPCMDOPTS pvOptions ); (this function resides in SSSCC.DLL) I can't figure out how should I pass the required array of file names from C# to the lpFileNames argument. Any help will be appreciated. Googling for "LPCSTR* C#" gives 1 or 2 meaningfull rezults, but no one of them helps :( Thank you. Quote
MrPaul Posted January 9, 2007 Posted January 9, 2007 UnmanagedType.LPArray and UnmanagedType.LPStr For the string array, try: [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)] String[] lpFileNames Good luck :cool: Quote Never trouble another for what you can do for yourself.
Comanche Posted January 9, 2007 Author Posted January 9, 2007 Thanks, it works! Meanwhile, I found out another solution - but it's much more heavy: IntPtr[] lpFileNamesEx = new IntPtr[fileLocalFullNames.Length]; for (int i = 0; i < fileLocalFullNames.Length; i++) { lpFileNamesEx = Marshal.StringToCoTaskMemAnsi(fileLocalFullNames); } fixed (IntPtr* p = &lpFileNamesEx[0]) { int nRes = SccHistory(pContext, ownerHwnd, lpFileNamesEx.Length, p, iOptions, &lOptions); if (nRes != 0) { throw new Exception("SccHistory() call failed with error code " + nRes.ToString()); } } Here fileLocalFullNames is the string array of file names. And the function should be declared like this: [DllImport(@"C:\Program Files\Microsoft Visual Studio\COMMON\VSS\win32\ssscc.dll")] private static extern int SccHistory(IntPtr ppContext, IntPtr hWnd, int nFiles, IntPtr* lpFileNames, int fOptions, long* pvOptions); So thanks once more! Quote
MrPaul Posted January 9, 2007 Posted January 9, 2007 (edited) Function declaration Personally, I think you should avoid using pointers and other unsafe code in C# whenever possible. In this case the function could be declared as: [DllImport(@"C:\Program Files\Microsoft Visual Studio\COMMON\VSS\win32\ssscc.dll")] private static extern int SccHistory( IntPtr ppContext, IntPtr hWnd, int nFiles, [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)] String[] lpFileNames, int fOptions, ref CmdOpts pvOptions ); The declaration of the last parameter depends on whether the CmdOpts type is declared as a value type (struct) or reference type (class) in C#. If it is a struct then you need the ref modifier as shown, so that a pointer is passed. If it is a class, then you must omit the ref as classes are always passed by reference. If it is a struct, you may have to specify MarshalAs for some of the members, though I assume you've already got that sorted. Good luck :cool: Edited January 9, 2007 by MrPaul Quote Never trouble another for what you can do for yourself.
Comanche Posted January 10, 2007 Author Posted January 10, 2007 Well, I would be happy not to use unsafe code, but I couldn't declare the function ===== C++ Declaration: ===== SCCRTN SccInitialize( LPVOID* ppvContext, HWND hWnd, LPCSTR lpCallerName, LPSTR lpSccName, LPLONG lpSccCaps, LPSTR lpAuxPathLabel, LPLONG pnCheckoutCommentLen, LPLONG pnCommentLen ); other than [[color=teal]DllImport[/color]([color=darkred]@"C:\Program Files\Microsoft Visual Studio\COMMON\VSS\win32\ssscc.dll"[/color])] [color=blue]private static extern int[/color] SccInitialize( [color=teal]IntPtr*[/color] ppContext, [color=teal]IntPtr[/color] hWnd, [color=teal]String[/color] lpCallerName, [color=blue]byte[/color][] lpSccName, [color=blue]long[/color]* lpSccCaps, [color=blue]byte[/color][] lpAuxPathLabel, [color=blue]long[/color]* CheckoutCommentLen, [color=blue]long[/color]* CommentLen ); I tried other ways but failed. So I had to start using unsafe code and pointers. Besides, I have no idea what should LPCMDOPTS look like in C#. I only know that I will never pass anything to the pvOptions argument and that in SSC.H there's a following definition: typedef LPVOID LPCMDOPTS; It doesn't really matter how this will be declared in C# - I only need to have a possibility to supply null or zero for the pvOptions argument. Quote
MrPaul Posted January 10, 2007 Posted January 10, 2007 Safe function declarations An equivalent declare might be [DllImport(@"C:\Program Files\Microsoft Visual Studio\COMMON\VSS\win32\ssscc.dll")] private static extern int SccInitialize( ref IntPtr ppContext, IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] String lpCallerName, byte[] lpSccName, /* Could also use StringBuilder */ ref int lpSccCaps, /* C++ LONG == C# int */ byte[] lpAuxPathLabel, /* Could also use StringBuilder */ ref int CheckoutCommentLen, /* C++ LONG == C# int */ ref int CommentLen /* C++ LONG == C# int */ ); In this case we're just replacing the pointers with ref parameters, as it achieves the same thing. If you need to use the returned ppContext structure, you can use Marshal.PtrToStructure. There are very few functions which cannot be declared in safe code. Of course if you're happy using unsafe code blocks then there is no problem, but you are very rarely forced to. As for the lpCmdOpts parameter in the previous post, if you do not intend to use it then you may be able to get away with declaring it as IntPtr pvOptions and then passing IntPtr.Zero when you call it. Good luck :) Quote Never trouble another for what you can do for yourself.
Comanche Posted January 10, 2007 Author Posted January 10, 2007 Thanks a lot! At last I managed to call several dialogs from the SSSCC.DLL. And all code is unsafe now. Anyone who is interested in working with this library (and that's the access to MS SourceSafe thru MSSCCI interface) - plz review the C# solution at this link. Quote
Recommended Posts