Need help sending command to system

I am trying to find the path of my file, then pass the path onto a command I am sending to system console.

My program takes a wireless profile .xml and loads it onto the computer and then tells windows to connect to that wireless network using the profile I just loaded. Earlier my way of getting the drive letter worked great, now all I am returning is 67 and not C. Not sure what happened. Right now I am getting an error once I try added drive letter to the rest of the command I am sending to system. Below is the code for the button I am using. Created the project in VS2010 and then ported it to 2012 to work with.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

private: System::Void btn_win7_connect_Click(System::Object^  sender, System::EventArgs^  e) 
				 {
					
					
					
	 TCHAR exe_path[MAX_PATH];
	 GetModuleFileName(NULL, exe_path, MAX_PATH);
	 TCHAR DriveLetter = exe_path[0];
					
					
							 
				 

	//Testing area to make sure its output is what I want.
	//MessageBox::Show("netsh wlan add profile filename =\" " + DriveLetter + ":\\Net Connect\\network.xml\""); 


        //This is where I receive the error express must have a class type.
	system(("netsh wlan add profile filename =\" " + DriveLetter + ":\\Net Connect\\network.xml\"").c_str());
				

	//system("TIMEOUT 2");
	//system("netsh wlan connect network");





	//END BUTTON LOAD NETWORK
				 }


Error in console
1>------ Build started: Project: Net Connect, Configuration: Debug Win32 ------
1> Net Connect.cpp
1>c:\users\sesa251707\documents\visual studio 2010\projects\net connect\net connect\Form1.h(117): error C2228: left of '.c_str' must have class/struct/union
1> type is 'System::String ^'
1> did you intend to use '->' instead?
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Last edited on
You#re concatenating a 3 char arrays together, whch isn't the same as concatenating 3 strings.

Also, how large is this?
 
TCHAR DriveLetter = exe_path[0];
this is not the way to concatenate strings in C++ (eventhough these strings are not actually strings, but arrays of char)

store your string in string variables (to have a string variable, you need #include <string>) and then put your pluses
Last edited on
@lwells

Out of interest, do you (already) code in other languages??

Anyway, the problem is general C++, rather than Windows specific...

1
2
char boatNum = '4';
cout << ("come in no. " + boatNum + " - you time is up").c_str() << endl;


does not make sense in C++.

The brackets force

"come in no. " + boatNum + " - you time is up"

to be evaluated first.

1. Unfortunately, "hello " + 'A' is valid C++.

cout << "hello " + 'A' << endl;

But what is is doing is:
- using the memory address where the string "hello" starts
- casting the char 'A' to an int, with the value 65 (the ascii code of 'A')
- adding this value to the start address, which moves the pointer several chars past the end of this string (this is C++ pointer arithmatic).

That is, the result is a char* pointer value which, if you're "lucky", will point at a different, later bit of you string (if it's long enough). Otherwise it might point at random chars (causing rubbish to printed out in this case) or, if you're unlucky, at invalid memory, which will cause the program to crash.

2. C++ cannot add two C strings (char* values), or any other two pointers

cout << "hello" + " world" << endl;

Visual C++ compiler error:
error C2110: '+' : cannot add two pointers


3. And of course, you can't call c_str() on anything apart from a C++ object, anyway.

Now...

You can get your code to use the std::string overloads of operator= if you make the following tweak:

cout << (string("come in no. ") + boatNum + " - you time is up") << endl;

Using the first C string to construct a temporary C++ string object means that all subsequent operations are on a C++ string object, not a C char* pointer. The result or string("come in no. ") + boatNum + " - you time is up" is a string, so you can then call its c_str() method.

But I would prob code this as:

1
2
3
4
string msg("come in no. ";
msg += boatNum;
msg += " your time is up"; 
cout << msg << endl;


esp. when there a lot of bits, or it's used in a loop, as operator+= is cheaper than operator+ (ever + constructs a temporary instance of your string).

Andy
Last edited on
std::cout << "come in no. " << boatNum << " your time is up" << std::endl;

No strings attached.
Last edited on
No one notices that @OP uses C++/CLI ?
Ooops...

So busy looking at the function body I missed the tell tale signs. :-(

Now, it there had been fewer blank lines... (feeble excuse!)

Andy
Last edited on
I almost got it working. In the code below what I output to the messagebox for testing purposes looks fine. It DriveLetter is actually the drive letter(C). But when I send my command to system instead of C it outputs 67 for drive letter. Which I believe 67 is the ANSI II code for C. Any ideas?


Created Win Forms App using VS 2010 Pro, Working on this Forms App in VS 2012.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36


private: System::Void btn_win7_connect_Click(System::Object^  sender, System::EventArgs^  e) 
				 {
					
					
					
	 TCHAR exe_path[MAX_PATH];
	 GetModuleFileName(NULL, exe_path, MAX_PATH);
	 WCHAR DriveLetter = exe_path[0];
	 stringstream ss(stringstream::in | stringstream::out);

					
	 //MessageBox for testing purposes. This actually outputs DriveLetter is C not 67.
	 MessageBox::Show("netsh wlan add profile filename =\"" + DriveLetter + ":\\Net Connect\\Moose.xml\"");
			    	
	 //My actual command I want, but DriveLetter is 67 and not C.
	 ss << "netsh wlan add profile filename =\"" << DriveLetter << ":\\Net Connect\\Moose.xml\"";
	
		string command = ss.str();
		system(command.c_str());
		system("read -p \"Press a key to continue...\" -n 1 -s");


			
	system("TIMEOUT 4");
	system("netsh wlan connect Moose.xml");



					system("pause");
	//END BUTTON LOAD NETWORK
				 }



You're getting 67 rather than 'W' as you are inserting a Unicode character (wchar_t) into a stream which uses the normal C++ char type. The wchar_t is being cast to an unsigned short (or WORD, in Win32 terms), which is a type of integer. The integer is being inserted into the stream.

Use wstringstream, which work with wchar_t, rather than stringstream. Or even better, wostringstream, as you only need to write out to the stringstream.

But actually, as modoran pointed out, you're using C++/CLI (going by the function signature. This means you should probably be using the .Net approach to launch your app.

See...

How to use Process Class in Managed C++
http://www.codeproject.com/Articles/5779/How-to-use-Process-Class-in-Managed-C

and

Can we execute a batch file (*.bat) using Application Domain?
http://stackoverflow.com/questions/3350923/can-we-execute-a-batch-file-bat-using-application-domain

and

How to launch Windows applications (Notepad) / Call Batch files from a C#/.NET console application.
http://www.codeproject.com/Articles/10102/How-to-launch-Windows-applications-Notepad-Call-Ba

(this last one is in C#, but it's easy enough to translate the bits you need into C++/CLI)

While this is outside my core skillset, I do know the rules for launching batch files in .Net (C#, C++/CLI, ...) are a bit different to those for the Win32 CreateProcess function. This little, clich├ęd demo does exactly what you'd expect...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// CLR_Test.cpp : main project file.
//
// Based on usual console app generated by Visual Studio
// stdafx.h is just an empty file
//
#include "stdafx.h"

using namespace System;
using namespace System::Diagnostics;

void DisplayMessage()
{
    Process^ myProcess = gcnew Process();
    myProcess->StartInfo->FileName = L"C:\\Test\\hello.bat";
    myProcess->Start();
}

int main(array<System::String ^> ^args)
{
    DisplayMessage();
    return 0;
}


where hello.bat contains

1
2
3
@echo off
echo Hello batch world
pause


Hello batch world
Press any key to continue . . .


Note that even when you use a .Net console app, the batch file is run in a new console (and process, of course), which is why the batch file uses the 'pause' command. See slashdot.com thread and MSDN for more into.

Andy
Last edited on
I thought about that, calling bat files. But I would love to shoot for a more portable program. As of right now I have to have two .xml files with the .exe that holds the wireless network profiles. Is there anyway to store and call the bat and or xml files inside of the program.

Oops - I think I had batch files on the brain from another recent post.

The same API can be used to launch regular executables, too.

Andy

PS Regarding

Is there anyway to store and call the bat and or xml files inside of the program.

I can of ways to do this, but which approach would depend on what context the app was being used in. At one extreme I'd junk the CreateProcess/Process::Start and use the Win32 Native WIFI directly. The approach you've started would be OK if you need a cheap solution for an in-house tool.

Also, this is really the subject for a different thread.
Last edited on
Well I will just use a small work around for now. Is there any way to hide a console from popping up in a windows form application? Here is a snippet of a button from form1.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private: System::Void btn_win7_connect_Click(System::Object^  sender, System::EventArgs^  e) 
				 {
					
					
					
					 TCHAR exe_path[MAX_PATH];
					 GetModuleFileName(NULL, exe_path, MAX_PATH);
					 WCHAR DriveLetter = exe_path[0];


				     //Drive Letter C
					 if (DriveLetter == 'c' || 'C')
					 {
						 
						 system("netsh wlan add profile filename = \"C:\\Net Connect\\moose.xml\"");
						
						 system("TIMEOUT 2");
					 
						 system("netsh wlan connect Moose");
					 }


					 //Drive Letter D
					 if (DriveLetter == 'd' || 'D')
					 {
						 
						 system("netsh wlan add profile filename = \"D:\\Net Connect\\moose.xml\"");
						
						 system("TIMEOUT 2");
					 
						 system("netsh wlan connect Moose");
					 }

					 else
						 MessageBox::Show("Error: Could not connect to the network. See Help.");



	//END BUTTON LOAD NETWORK
				 }
If you can call CreateProcess() API directly then sure you can spawn cmd.exe hidden.
1
2
3
4
5
6
7
8
9
10
11
             STARTUPINFO si;
	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);
	si.dwFlags = STARTF_USESHOWWINDOW;
	si.wShowWindow = SW_HIDE;

	PROCESS_INFORMATION pi;
	ZeroMemory(&pi, sizeof(pi));

	CreateProcess(lpApplicationName, Args, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi) ;
As it's a C++/CLI program, I (still) think Process::Start( ) would be a better fit.

You should really use Win32 from a C++/CLI program unless there is no .Net equivalent

Andy
Last edited on
So I made a mistake above and ment to use If, and then the rest are else if statements. Works fine on my laptop. But when I travel to a different computer it fails to find the files. My computer was seen as a H drive and the new one is E drive. Confused.
Topic archived. No new replies allowed.