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?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

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
trinitrotolueneDirector - Software EngineeringCommented:
You might find it useful to have a look at this article and this description from MSDN
0
PMI ACP® Project Management

Prepare for the PMI Agile Certified Practitioner (PMI-ACP)® exam, which formally recognizes your knowledge of agile principles and your skill with agile techniques.

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:
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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Todd GerbertIT ConsultantAuthor Commented:
Discovered error in my code, this solution accurately corrects the problem.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.