Converting System::String^ to LPWSTR

I'm just getting into C++ and have mostly a C# background, so I'm re-writing little code snippets I've got in C# into C++ just for the sake of learning/practice.

I am passing a System::String into a function that calls CreateProcess, so I need that System::String to become a LPWSTR.

I have no problem using System::Runtime::InteropServices::Marshal::StringToHGlobalUni(); or using a pin_ptr<const wchar_t> if I use the snippet below:
pin_ptr<const wchar_t> lpcwstrFile = PtrToStringChars(Filename);
LPCWSTR lpcwstrOtherFile = lpcwstrFile;
success = CreateProcess(lpcwstrOtherFile,		// lpApplicationName
			NULL,			// lpCommandLine
			NULL,			// lpProcessAttributes
			NULL,			// lpThreadAttributes
			FALSE,			// bInheritHandles
			CREATE_SUSPENDED,	// dwCreationFlags
			NULL,			// lpEnvironment
			NULL,			// lpCurrentDirectory
			&sInfo,			// lpStartupInfo
			&pInfo);			// lpProcessInformation

Open in new window


But I get an access violation when I try this (the difference being line 2 from above is not present here):
pin_ptr<const wchar_t> lpcwstrFile = PtrToStringChars(Filename);

success = CreateProcess(lpcwstrFile,			// lpApplicationName
			NULL,			// lpCommandLine
			NULL,			// lpProcessAttributes
			NULL,			// lpThreadAttributes
			FALSE,			// bInheritHandles
			CREATE_SUSPENDED,	// dwCreationFlags
			NULL,			// lpEnvironment
			NULL,			// lpCurrentDirectory
			&sInfo,			// lpStartupInfo
			&pInfo);			// lpProcessInformation

Open in new window


Interestingly (interesting to me, anyway), if I wprintf lpcwstrFile in the second snippet I see the correct path on the console, but using it with CreateProcess throws an AccessViolation every time.

So my question is: Why does assigning lpcwstrFile to lpcwstrOtherFile make a difference in behavior, given that the values of lpcwstrFile and lpcwstrOtherFile are the same?
LVL 33
Todd GerbertIT ConsultantAsked:
Who is Participating?
 
Todd GerbertIT ConsultantAuthor Commented:
Ahh...I'm a doofus!  The "cb" member of the STARTUPINFO struct needs to be set - probably just coincidence it seemed to work with one pointer vs. the other. ;)

This code works as expected:
bool CUACChecker::RequiresElevation(String^ Filename)
{
	BOOL success;
	bool requiresElevation;
	int win32error;

	PROCESS_INFORMATION pInfo;
	STARTUPINFO sInfo;

	ZeroMemory(&sInfo, sizeof(sInfo));
	sInfo.cb = sizeof(sInfo);
	
	pin_ptr<const wchar_t> lpcwstrFile = PtrToStringChars(Filename);
	
	// wchar_t* file = (wchar_t*)(Marshal::StringToHGlobalUni(Filename).ToPointer());
	
	success = CreateProcess(lpcwstrFile,	// lpApplicationName
			NULL,			// lpCommandLine
			NULL,			// lpProcessAttributes
			NULL,			// lpThreadAttributes
			FALSE			// bInheritHandles
			CREATE_SUSPENDED,		// dwCreationFlags
			NULL,			// lpEnvironment
			NULL,			// lpCurrentDirectory
			&sInfo,			// lpStartupInfo
			&pInfo);			// lpProcessInformation

	if (!success)
		win32error = GetLastError();


	// Marshal::FreeHGlobal(IntPtr((void*)file));
	lpcwstrFile = nullptr;

	if(success)
	{
		requiresElevation = false;

		TerminateProcess(pInfo.hProcess, 0);
		CloseHandle(pInfo.hThread);
		CloseHandle(pInfo.hProcess);
	}
	else
	{
		if (win32error == ERROR_ELEVATION_REQUIRED)
			requiresElevation = true;
		else
			throw gcnew Win32Exception(win32error);
	}

	return requiresElevation;
}

Open in new window

0
 
trinitrotolueneDirector - Software EngineeringCommented:
pin_ptr is the cause for your problem. The access violation is due to that
0
 
trinitrotolueneDirector - Software EngineeringCommented:
essentially by pinning you are preventing the object from moving in memory. This is something you want to take care of only when you are mixing managed and unmanaged code. In your case all you are doing is a complete port.

So I wouldn't still use the pinning here.

0
Cloud Class® Course: Ruby Fundamentals

This course will introduce you to Ruby, as well as teach you about classes, methods, variables, data structures, loops, enumerable methods, and finishing touches.

 
trinitrotolueneDirector - Software EngineeringCommented:
You might find it useful to have a look at this article and this description from MSDN
0
 
Todd GerbertIT ConsultantAuthor Commented:
Ahh, sorry - I should have posted more code. ;)

This function determines if a given executable file would require UAC elevation by using CreateProcess() to start it suspended and test for ERROR_ELEVATION_REQUIRED.  It takes a managed System::String^ and needs to use it with the native CreateProcess(), hence the pinning.  I discovered pin_ptr in a Microsoft Knowledgebase article: http://msdn.microsoft.com/en-us/library/d1ae6tz5(VS.80).aspx

This code works as it currently is (the Marshal::StringToHGlobalUni method works fine for me too). Note, though, that PtrToStringChars() is giving me a const wchar_t* on line 15 named lpcwstrFile, and the next line I'm just assigning lpcwstrFile to another const wchar_t* named lpcwstrOtherFile.  If I stick a wprintf(L"%s", lpcwstrFile) in there I see the correct string printed on the console.  If I pass lpcwstrFile to CreateProcess I get an access violation - but if I pass lpcwstrOtherFile it works fine.

I'm a little befuddled because, so far as I can tell anyway, lpcwstrFile and lpcwstrOtherFile both point to the same location and CreateProcess() is expecting a LPCWSTR so it shouldn't be trying to modify it.

bool CUACChecker::RequiresElevation(String^ Filename)
{
	BOOL success;
	bool requiresElevation;
	int win32error;

	PROCESS_INFORMATION pInfo;
	STARTUPINFO sInfo;
	SECURITY_ATTRIBUTES	pSec;
	SECURITY_ATTRIBUTES tSec;

	pSec.nLength = sizeof(SECURITY_ATTRIBUTES);
	tSec.nLength = sizeof(SECURITY_ATTRIBUTES);
	
	pin_ptr<const wchar_t> lpcwstrFile = PtrToStringChars(Filename);
	LPCWSTR lpcwstrOtherFile = lpcwstrFile;
	
	// wchar_t* file = (wchar_t*)(Marshal::StringToHGlobalUni(Filename).ToPointer());
	
	success = CreateProcess(lpcwstrOtherFile,	// lpApplicationName
			NULL,			// lpCommandLine
			NULL,			// lpProcessAttributes
			NULL,			// lpThreadAttributes
			FALSE			// bInheritHandles
			CREATE_SUSPENDED,		// dwCreationFlags
			NULL,			// lpEnvironment
			NULL,			// lpCurrentDirectory
			&sInfo,			// lpStartupInfo
			&pInfo);			// lpProcessInformation

	if (!success)
		win32error = GetLastError();


	// Marshal::FreeHGlobal(IntPtr((void*)file));
	lpcwstrFile = nullptr;

	if(success)
	{
		requiresElevation = false;

		TerminateProcess(pInfo.hProcess, 0);
		CloseHandle(pInfo.hThread);
		CloseHandle(pInfo.hProcess);
	}
	else
	{
		if (win32error == ERROR_ELEVATION_REQUIRED)
			requiresElevation = true;
		else
			throw gcnew Win32Exception(win32error);
	}

	return requiresElevation;
}

Open in new window

0
 
MedievalWarriorCommented:
0
 
Todd GerbertIT ConsultantAuthor Commented:
I believe c_str() is for std::string, but the string in this case is a .Net System::String.
0
 
trinitrotolueneDirector - Software EngineeringCommented:
>>>>"If I pass lpcwstrFile to CreateProcess I get an access violation - but if I pass lpcwstrOtherFile it works fine."

that's because lpcwstrOtherFile is not pinned. CreateProcess() cannot accept pinned pointers.
0
 
trinitrotolueneDirector - Software EngineeringCommented:
you really need to read those articles I pointed you to. Its not that CreateProcess() is modifying the pointer. Why should it? An access violation happens because it is accessing something it should not and in this case it should not be accessing a pinned pointer.

First of all this is not a proper way to use pinned pointers. Use pinned pointers only if there is need to do it.
0
 
Todd GerbertIT ConsultantAuthor Commented:
I had already been over the MSDN article, and did read the other one you mentioned (which I thought was very good, by the way, thanks for pointing it out) - so either I'm just missing some of the finer points or subconsciously superimposing my C# experience with this new information, likely a combination of the two. ;)

>> CreateProcess() cannot accept pinned pointers
No, but CreateProcess() will accept a wchar_t*, and pinned pointers are implicitly convertable to native pointers, so doesn't that mean I can pass a pin_ptr<wchar_t> where a whcar_t* is expected?

I understand that an interior_ptr points to an object on the managed heap, and "follows" that object if it's location in the heap happens to change (i.e. the CLR updates the interior_ptr in the event a managed object is relocated as a result of GC activity).

I also understand that a pin_ptr is (perhaps to oversimplify a bit) essentially an interior_ptr that doesn't move and is assignable to native pointer types whose primary purpose is allow the passing of a pointer to a managed object to a native function.

Is that not the case here? Isn't pinning required so that the Garbage Collector doesn't move the string while CreateProcess() is running?

Both the CodeProject article you referred to and the Microsoft KB I had found earlier both use this example:
String ^s = "Hello World";
pin_ptr<const wchar_t> pinnedPtrToS = PtrToStringChars(s);
wprintf("%s", pinnedPtrToS);

Open in new window

Isn't that same as what I've got, except I'm calling CreateProcess instead of wprintf - but what's the difference between CreateProcess and wprintf, aside from compile-time/run-time linking (as far as usage of a pin_ptr is concerned)? Is that linkage significant to CLI?
0
 
Todd GerbertIT ConsultantAuthor Commented:
Discovered error in my code, this solution accurately corrects the problem.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.