94erbrom
Goto Top

C-Sharp - Umleiten des StandardOutput eines Process geht nicht

Hallo Zusammen.

Ich bin gerade am verzweifeln.

Ich habe hier ein Fremdprogramm samt seiner V-C++-Projektdateien.
Das Programm schaltet in Windows die Standart-Sound-Ausgabe auf das Gerät mit der entsprechenden, als Parameter übergebenen Nummer um.
Der Aufruf "run.exe 1" schaltet zum Beispiel auf HDMI um, "run.exe 2" auf Headset, etc.
Der Aufruf "run.exe", also ohne Parameter, erzeugt eine Liste, die alle aktiven Audio-Ausgabe-Geräte, von 0 an durchnummeriert, enthält.
Genau diese Liste möchte ich nun in die Variable DevicesList umleiten.

Beispiel für den Aufruf ohne Parameter
C:\AudioDeviceSwitcher>run.exe
Audio Device 0: Lautsprecher (6- Logitech G430 Gaming Headset)
Audio Device 1: Audials Sound Capturing (Audials Sound Capturing)
Audio Device 2: Lautsprecher (Realtek High Definition Audio)
Audio Device 3: GRUNDIG WXGA-4 (2- NVIDIA High Definition Audio)

Auf das Fremdprogramm (run.exe) greife ich aus meinem C#-Projekt mit System.Diagostics.Process zu und leite den StandardOutput in die String DevicesList um.
Im C++-Programm wird die Ausgabe mit printf erstellt, also auf die Standard-Ausgabe geschrieben, wenn ich das Programm mit CMD ausführe, erhalte ich auch eine Ausgabe.
In meinem Programm erhalte ich jedoch keine Ausgabe zurück.
Ich habe den Programmaufruf und die Umleitung auch schon mit einer test.bat-Datei des Inhalts

echo Hallo Welt
run.exe
echo ciao

getestet, das Ergebnis war dann, dass in der Variable nach >run.exe nichts mehr stand, echo ciao wurde also gar nicht ausgeführt.
In CMD funktionierte aber alles korrekt.
Habe sicherheitshalber auch mal den StandardError umgeleitet, aber auch ohne Ergebnis

Die Standard-Ausgabe fange ich mit folgendem Code-Fragment:
public static void getDevicesList()
        {
            Process p = new Process();
            p.StartInfo.FileName = exelocation;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.Arguments = "";
            Console.WriteLine(Environment.NewLine + exelocation + Environment.NewLine); //dient nur der Sicherstellung, dass der Pfad stimmt
            
            try
            {
                p.Start();
                StreamReader reader = p.StandardOutput;
                devicesList = reader.ReadToEnd();
                
                p.WaitForExit();
            }
            catch (Exception ex)
            {
                Console.WriteLine(Environment.NewLine + Environment.NewLine + ex + Environment.NewLine + Environment.NewLine);
                MessageBox.Show("Exception!!!", null);
            }

            MessageBox.Show("Inhalt von DevicesList:" + Environment.NewLine + devicesList, "DevicesList");

        }


Bei einem C++-Testprogramm, dass nur mit printf einmal "test" schreibt, konnte ich mit dieser Methode die Ausgabe umleiten.
Auch mit einer .bat-Datei ging es.
Das Umschalten auf andere Geräte geht mit dieser Methode auch. Der Pfad stimmt also.

Bitte um schnelle Hilfe, ich verzweifel hier.
Falls ihr noch weiteren Code braucht, sagt es mir.

LG 94erBrom

Content-Key: 258362

Url: https://administrator.de/contentid/258362

Printed on: April 20, 2024 at 01:04 o'clock

Mitglied: 114757
114757 Dec 23, 2014 updated at 14:23:13 (UTC)
Goto Top
Moin,
versuch mal den Output deines c++ Programms stattdessen mit
std::cout << "Hello World" << std::endl;  
ausgeben zu lassen. Das sollte in jedem c++ funktionieren.

Gruß jodel32
Member: 94erBrom
94erBrom Dec 23, 2014 at 15:31:24 (UTC)
Goto Top
Danke, werde ich mal ausprobieren.

Ich hatte bereits letzte Woche eine Version des C#-Projekt am laufen und im Praxiseinsatz, die funktioniert nur jetzt seltsamerweise nicht mehr(erinnere mich nicht, eine neue Version drüberinstalliert zu haben), und ich habe davon keine Datensicherung, weshalb ich alles neu programmieren muss (das ursprüngliche Projekt ist nach einigen Änderungen inzwischen unbrauchbar).
Es hat wie gesagt schon funktioniert, der Fehler dürfte also eigentlich nicht auf Seiten des C++-Programms liegen...
Member: 94erBrom
94erBrom Dec 23, 2014 updated at 16:23:39 (UTC)
Goto Top
Ich habe jetzt eine neuere Version des C++Programms gefunden und heruntergeladen.
In der neuen Version ist es möglich, sich über den Parameter --help eine Hilfeseite ausgeben zu lassen. Dies geschieht wie auch bei der Geräte-Liste und den Fehler-Hinweisen mit wprintf_s.
Wenn ich das Programm aus C# mit dem Parameter --help aufrufe, funktioniert die Umleitung, ohne Parameter, mit absichtlich falscher Geräte-Nr. oder Parameter -a (zeigt alle Geräte, auch deaktivierte) jedoch geht es nicht. Der einzige Unterschied scheint mir zu sein, dass bei diesen Variablen mit in die Ausgabe geschrieben werden.
Über cmd funktioniert alles wie es soll
Mitglied: 114757
114757 Dec 23, 2014 updated at 17:51:31 (UTC)
Goto Top
Wenn du den C++ Quellcode hast warum setzt du dir das Prog nicht direkt in c# um, ohne den Umweg des Auslesens vom Standardout ??

Kompilierst du das Programm selber oder nimmst du immer nur ein bereits fertig kompiliertes Programm ? Ich schätze mal das Prog wurde nicht mit den richtigen Compiler-Schaltern kompiliert.

Meine Glaskugel hat leider schon Schicht und hängt am Christbaum, so dass ich hier leider keinen Quellcode aus ihr herzaubern kann face-sad
Member: 94erBrom
94erBrom Dec 23, 2014 updated at 18:48:01 (UTC)
Goto Top
Ich habe vom C++ Programm sowohl die Projektdateien, sprich Quellcode, als auch eine fertig compilierte Release-Version.
Ich benutze die Version, die im Release-Verzeichnis des Projekts lag.
Die funktioniert auch auf der CMD ohne Probleme.

Was das umsetzen in C# angeht, muss ich gestehen, dass meine C++-Kenntnisse dafür nicht ausreichen. Mehr als einfache Grundlagen zu In- und Output sowie If, While, For, Arrays und Rechenoperationen kann ich in C++ nicht. Außerdem greift das Programm auf undokumentierte Systembibliotheken zu, was die Sache recht kompliziert macht.

Ich nehme fürchte, dass die Ausgabe auf den Console-Kanal (Also Kanal 3) läuft, und daher nicht umgeleitet wird.
Weiß aber nicht, ob das mit Visual C++ überhaupt geht

Der Quellcode des Hauptprogramms:

// EndPointController.cpp : Defines the entry point for the console application.
//
#include <stdio.h>
#include <wchar.h>
#include <tchar.h>
#include <string>
#include <iostream>
#include "windows.h"
#include "Mmdeviceapi.h"
#include "PolicyConfig.h"
#include "Propidl.h"
#include "Functiondiscoverykeys_devpkey.h"

// Format default string for outputing a device entry. The following parameters will be used in the following order:
// Index, Device Friendly Name
#define DEVICE_OUTPUT_FORMAT "Audio Device %d: %ws"

typedef struct TGlobalState
{
	HRESULT hr;
	int option;
	IMMDeviceEnumerator *pEnum;
	IMMDeviceCollection *pDevices;
	LPWSTR strDefaultDeviceID;
	IMMDevice *pCurrentDevice;
	LPCWSTR pDeviceFormatStr;
	int deviceStateFilter;
} TGlobalState;

void createDeviceEnumerator(TGlobalState* state);
void prepareDeviceEnumerator(TGlobalState* state);
void enumerateOutputDevices(TGlobalState* state);
HRESULT printDeviceInfo(IMMDevice* pDevice, int index, LPCWSTR outFormat, LPWSTR strDefaultDeviceID);
std::wstring getDeviceProperty(IPropertyStore* pStore, const PROPERTYKEY key);
HRESULT SetDefaultAudioPlaybackDevice(LPCWSTR devID);
void invalidParameterHandler(const wchar_t* expression, const wchar_t* function, const wchar_t* file, 
	unsigned int line, uintptr_t pReserved);

// EndPointController.exe [NewDefaultDeviceID]
int _tmain(int argc, LPCWSTR argv)
{
	TGlobalState state;

	// Process command line arguments
	state.option = 0; // 0 indicates list devices.
	state.strDefaultDeviceID = '\0';
	state.pDeviceFormatStr = _T(DEVICE_OUTPUT_FORMAT);
	state.deviceStateFilter = DEVICE_STATE_ACTIVE;

	for (int i = 1; i < argc; i++) 
	{
		if (wcscmp(argv[i], _T("--help")) == 0)
		{
			wprintf_s(_T("Lists active audio end-point playback devices or sets default audio end-point\n"));
			wprintf_s(_T("playback device.\n\n"));
			wprintf_s(_T("USAGE\n"));
			wprintf_s(_T("  EndPointController.exe [-a] [-f format_str]  Lists audio end-point playback\n"));
			wprintf_s(_T("                                               devices that are enabled.\n"));
			wprintf_s(_T("  EndPointController.exe device_index          Sets the default playvack device\n"));
			wprintf_s(_T("                                               with the given index.\n"));
			wprintf_s(_T("\n"));
			wprintf_s(_T("OPTIONS\n"));
			wprintf_s(_T("  -a             Display all devices, rather than just active devices.\n"));
			wprintf_s(_T("  -f format_str  Outputs the details of each device using the given format\n"));
			wprintf_s(_T("                 string. If this parameter is ommitted the format string\n"));
			wprintf_s(_T("                 defaults to: \"%s\"\n\n"), _T(DEVICE_OUTPUT_FORMAT));
			wprintf_s(_T("                 Parameters that are passed to the 'printf' function are\n"));
			wprintf_s(_T("                 ordered as follows:\n"));
			wprintf_s(_T("                   - Device index (int)\n"));
			wprintf_s(_T("                   - Device friendly name (wstring)\n"));
			wprintf_s(_T("                   - Device state (int)\n"));
			wprintf_s(_T("				     - Device default? (1 for true 0 for false as int)\n"));
			wprintf_s(_T("                   - Device description (wstring)\n"));
			wprintf_s(_T("                   - Device interface friendly name (wstring)\n"));
			wprintf_s(_T("                   - Device ID (wstring)\n"));
			exit(0);
		}
		else if (wcscmp(argv[i], _T("-a")) == 0)
		{
			state.deviceStateFilter = DEVICE_STATEMASK_ALL;
			continue;
		}
		else if (wcscmp(argv[i], _T("-f")) == 0)
		{
			if ((argc - i) >= 2) {
				state.pDeviceFormatStr = argv[++i];
				
				// If printf is called with an invalid format string, jump to the invalidParameterHandler function.
				_set_invalid_parameter_handler(invalidParameterHandler);
				_CrtSetReportMode(_CRT_ASSERT, 0);
				continue;
			}
			else
			{
				wprintf_s(_T("Missing format string"));
				exit(1);
			}
		}
	}
	
	if (argc == 2) state.option = _wtoi(argv[1]);

	state.hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
	if (SUCCEEDED(state.hr))
	{
		createDeviceEnumerator(&state);
	}
	return state.hr;
}

// Create a multimedia device enumerator.
void createDeviceEnumerator(TGlobalState* state)
{
	state->pEnum = NULL;
	state->hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator),
		(void**)&state->pEnum);
	if (SUCCEEDED(state->hr))
	{
		prepareDeviceEnumerator(state);
	}
}

// Prepare the device enumerator
void prepareDeviceEnumerator(TGlobalState* state)
{
	state->hr = state->pEnum->EnumAudioEndpoints(eRender, state->deviceStateFilter, &state->pDevices);
	if SUCCEEDED(state->hr)
	{
		enumerateOutputDevices(state);
	}
	state->pEnum->Release();
}

// Enumerate the output devices
void enumerateOutputDevices(TGlobalState* state)
{
	UINT count;
	state->pDevices->GetCount(&count);

	// If option is less than 1, list devices
	if (state->option < 1) 
	{

		// Get default device
		IMMDevice* pDefaultDevice;
		state->hr = state->pEnum->GetDefaultAudioEndpoint(eRender, eMultimedia, &pDefaultDevice);
		if (SUCCEEDED(state->hr))
		{
			
			state->hr = pDefaultDevice->GetId(&state->strDefaultDeviceID);

			// Iterate all devices
			for (int i = 1; i <= (int)count; i++)
			{
				state->hr = state->pDevices->Item(i - 1, &state->pCurrentDevice);
				if (SUCCEEDED(state->hr))
				{
					state->hr = printDeviceInfo(state->pCurrentDevice, i, state->pDeviceFormatStr,
						state->strDefaultDeviceID);
					state->pCurrentDevice->Release();
				}
			}
		}
	}
	// If option corresponds with the index of an audio device, set it to default
	else if (state->option <= (int)count)
	{
		state->hr = state->pDevices->Item(state->option - 1, &state->pCurrentDevice);
		if (SUCCEEDED(state->hr))
		{
			LPWSTR strID = NULL;
			state->hr = state->pCurrentDevice->GetId(&strID);
			if (SUCCEEDED(state->hr))
			{
				state->hr = SetDefaultAudioPlaybackDevice(strID);
			}
			state->pCurrentDevice->Release();
		}
	}
	// Otherwise inform user than option doesn't correspond with a device
	else
	{
		wprintf_s(_T("Error: No audio end-point device with the index '%d'.\n"), state->option);
	}
	
	state->pDevices->Release();
}

HRESULT printDeviceInfo(IMMDevice* pDevice, int index, LPCWSTR outFormat, LPWSTR strDefaultDeviceID)
{
	// Device ID
	LPWSTR strID = NULL;
	HRESULT hr = pDevice->GetId(&strID);
	if (!SUCCEEDED(hr))
	{
		return hr;
	}

	int deviceDefault = (strDefaultDeviceID != '\0' && (wcscmp(strDefaultDeviceID, strID) == 0));

	// Device state
	DWORD dwState;
	hr = pDevice->GetState(&dwState);
	if (!SUCCEEDED(hr))
	{
		return hr;
	}
		
	IPropertyStore *pStore;
	hr = pDevice->OpenPropertyStore(STGM_READ, &pStore);
	if (SUCCEEDED(hr))
	{
		std::wstring friendlyName = getDeviceProperty(pStore, PKEY_Device_FriendlyName);
		std::wstring Desc = getDeviceProperty(pStore, PKEY_Device_DeviceDesc);
		std::wstring interfaceFriendlyName = getDeviceProperty(pStore, PKEY_DeviceInterface_FriendlyName);
		
		if (SUCCEEDED(hr))
		{
			wprintf_s(outFormat,
				index,
				friendlyName.c_str(),
				dwState,
				deviceDefault,
				Desc.c_str(),
				interfaceFriendlyName.c_str(),
				strID
			);
			wprintf_s(_T("\n"));
		}

		pStore->Release();
	}
	return hr;
}

std::wstring getDeviceProperty(IPropertyStore* pStore, const PROPERTYKEY key)
{
	PROPVARIANT prop;
	PropVariantInit(&prop);
	HRESULT hr = pStore->GetValue(key, &prop);
	if (SUCCEEDED(hr))
	{
		std::wstring result (prop.pwszVal);
		PropVariantClear(&prop);
		return result;
	}
	else
	{
		return std::wstring (L"");
	}
}

HRESULT SetDefaultAudioPlaybackDevice(LPCWSTR devID)
{	
	IPolicyConfigVista *pPolicyConfig;
	ERole reserved = eConsole;

    HRESULT hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient), 
		NULL, CLSCTX_ALL, __uuidof(IPolicyConfigVista), (LPVOID *)&pPolicyConfig);
	if (SUCCEEDED(hr))
	{
		hr = pPolicyConfig->SetDefaultEndpoint(devID, reserved);
		pPolicyConfig->Release();
	}
	return hr;
}

void invalidParameterHandler(const wchar_t* expression,
   const wchar_t* function, 
   const wchar_t* file, 
   unsigned int line, 
   uintptr_t pReserved)
{
   wprintf_s(_T("\nError: Invalid format_str.\n"));
   exit(1);
}