views:

40

answers:

2

BOOL PathFindOnPath( LPTSTR pszFile, LPCTSTR *ppszOtherDirs );

I am calling this API from managed C++. My pszFile is in a System.String.

What is the easiest way to pass this as LPTSTR? (Considering its an inout parameter)

I have tried pin_ptr and interior_ptr but neither seems to be accepted by the compiler.

+3  A: 

You need to marshal across a (pre-allocated) StringBuilder instead of a String reference. For details, see this MSDN article on Marshaling.

Reed Copsey
Thanks.. // shellName IS A String WITH THE INPUT PATH StringBuilder ^ shellFileName = gcnew StringBuilder(MAX_PATH); shellFileName->Append(shellName); BOOL findResult = PathFindOnPath(shellFileName, NULL); gives: Error 1 error C2664: 'PathFindOnPathW' : cannot convert parameter 1 from 'System::Text::StringBuilder ^' to 'LPWSTR'
BillyG
The MSDN article seems to be C#. I can't seem to pass StringBuilder to API needing LPWSTR in C++, either declared as a StringBuilder ^ with gcnew, or just declared on the stack.
BillyG
+1  A: 

Strings are immutable, you cannot pass it directly, even if you pin it. More seriously, you will have to deal with the possibility that the function returns a larger string. The function is unsafe by design since you cannot prevent it from returning a path string that's too large. Not much you can do about that I suppose, but you will have to use a buffer that's at least large enough for common path strings. This code will get the job done:

#include <vcclr.h>
...
    String^ file = "blah.txt";
    wchar_t path[_MAX_PATH];
    {
        pin_ptr<const wchar_t> wch = PtrToStringChars(file);
        wcscpy_s(path, _MAX_PATH, wch);
    }
    BOOL ok = PathFindOnPath(path, something);

The curly braces look odd perhaps, it ensures that the managed string doesn't stay pinned for too long.

Hans Passant
BillyG
LPTSTR path = (LPTSTR)Marshal::StringToCoTaskMemUni(path).ToInt32(); // less code, and only one line
BillyG
And a glaring memory leak. And you'll corrupt the heap, you haven't dealt with the string size problem. If you *really* want to marshal it yourself then use Marshal::Copy().
Hans Passant
Whoops :) So basically there's no way that C++ gives us to marshall strings. It must be done manually. I'm trying to figure out what is meant by THE CLR MARSHALLER at start of this paragraph http://msdn.microsoft.com/en-us/magazine/cc164193.aspx#S4 whether they mean CLR or just C# can marshall StringBuilder
BillyG
The P/Invoke marshaller needs to be used by languages like C# and VB.NET. The point of writing code in C++/CLI is that you *don't* have to use it. You still can use [DllImport] if you really want to but you'll have to move your unmanaged code into a separate DLL. There are few conceivable advantages of doing so.
Hans Passant
Thanks. Basically there isnt any marshalling going on with C++/CLI. That clarifies it. I wasn't sure if it was available or not (the marshalling features.)
BillyG