Calling C++ function with LPCSTR* argument from C#

Comanche

Member
Joined
May 26, 2006
Messages
6
Hi,
I got a function with the following signature:

Code:
SCCRTN SccHistory(
  LPVOID pvContext,
  HWND hWnd,
  LONG nFiles,
  LPCSTR* lpFileNames,
  LONG fOptions,
  LPCMDOPTS pvOptions
);

(this function resides in SSSCC.DLL)

I cant 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.
 
UnmanagedType.LPArray and UnmanagedType.LPStr

For the string array, try:

C#:
[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPStr)] String[] lpFileNames

Good luck :cool:
 
Thanks, it works!

Meanwhile, I found out another solution - but its 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!
 
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:

C#:
[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 youve already got that sorted.

Good luck :cool:
 
Last edited by a moderator:
Well, I would be happy not to use unsafe code, but I couldnt declare the function
Code:
===== C++ Declaration: =====
SCCRTN SccInitialize(
  LPVOID* ppvContext,
  HWND hWnd,
  LPCSTR lpCallerName,
  LPSTR lpSccName,
  LPLONG lpSccCaps,
  LPSTR lpAuxPathLabel,
  LPLONG pnCheckoutCommentLen,
  LPLONG pnCommentLen
);
other than
Code:
[[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 theres a following definition:
Code:
typedef LPVOID LPCMDOPTS;
It doesnt 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.
 
Safe function declarations

An equivalent declare might be

C#:
[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 were 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 youre 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 :)
 
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 thats the access to MS SourceSafe thru MSSCCI interface) - plz review the C# solution at this link.
 
Back
Top