1

I'm trying to make a small program that will create a Batch file, do something in it and then return a string from it, and after it delete the Batch.

I would like to store the output of the batch file in the variable line.

I tried using getline() but I think it work with .txt files only. I can be wrong.

#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <string>
using namespace std;

int main(int argc, char *argv[]) {
    ofstream batch;
    string line;

    batch.open("temp.bat", ios::out);
    batch <<"@echo OFF\nwmic os get caption /value\nwmic path win32_videocontroller get description /value\npause\nexit";
    batch.close();

    system("temp.bat");
    remove("temp.bat");
};

In my code I simply using system with my Batch file. I'd like to use cout<<line.

I expect string called line would be equal to the output of the Batch file.

Dave Rager
  • 8,002
  • 3
  • 33
  • 52
younyokel
  • 327
  • 2
  • 15
  • By the way, I'm using main int arguments for future use. – younyokel Aug 02 '19 at 13:58
  • 1
    "I expect string called `line` would be equal to everything inside the Batch file" - I don't understand what that would be the case, given that this variable isn't used anywhere in your code... – ForceBru Aug 02 '19 at 13:59
  • 1
    This doesn't address the question, but get in the habit of initializing objects with meaningful values rather than default-initializing them and immediately overwriting the value. That is, change `ofstream batch; ... batch.open("temp.bat", ios::out);` to `ofstream batch("temp.bat");` (the `ios::out` is redundant; `ofstream` is an output file. Also, if you put this code in a function (for example, "create_batch_file(const char*name)` then you don't have to call `close()` on the stream; the destructor will do that for you. – Pete Becker Aug 02 '19 at 14:03
  • 1
    Side note: Prefer ``. – Ted Lyngmo Aug 02 '19 at 14:04
  • @TedLyngmo I'm getting an error using `#include ` as "no such file or directory". – younyokel Aug 02 '19 at 14:13
  • @EadwineYoun That's odd [cstdlib](https://en.cppreference.com/w/cpp/header/cstdlib) is a standard C++ header. What compiler are you using? – Ted Lyngmo Aug 02 '19 at 14:15
  • @TedLyngmo I'm using Dev-C++ version 5.11 and Compiler TDM-GCC 4.9.2 64-bit Release (the default one). – younyokel Aug 02 '19 at 14:19
  • @ForceBru I didn't use `line` string because I don't know how to convert everything from batch file to this variable. This is why I'm asking you people. – younyokel Aug 02 '19 at 14:36
  • 2
    `wmic.exe` is a standard Windows executable file, so I'm not sure why you need to write, run and delete a [tag:batch-file] for this task. Can you not simply run `"wmic.exe"` twice, each with your required arguments. – Compo Aug 02 '19 at 14:59
  • If I'm understanding correctly, you want to run the batch file and capture the text output and save it into `line`? – Dave Rager Aug 02 '19 at 15:06
  • @DaveRager yes, I just need to store what batch file returns in `line`. – younyokel Aug 02 '19 at 15:15
  • 1
    The simplest solution might be to redirect the output to a file and then read that file. – Phil1970 Aug 02 '19 at 15:45
  • @Phil1970 could you please tell me how to implement it? I think I didn't get it. – younyokel Aug 02 '19 at 15:56
  • 2
    Open a Command Prompt window and enter `wmic /?` paying particular attention to the `/output` and `/append` options. – Compo Aug 02 '19 at 16:09
  • Something like `command > log_file.txt`. Then you read the file. You might also need to convert the encoding. – Phil1970 Aug 02 '19 at 16:10
  • @Compo I don't understand how this can help me. – younyokel Aug 02 '19 at 16:24
  • I found a way simplier: I could create a new BAT that will do stuff like this: `temp.bat > log.txt` into new text file and C++ console will read it and turn into a string... I guess? – younyokel Aug 02 '19 at 16:33
  • 1
    @EadwineYoun, did you read the `wmic` usage information? It seems very clear to me, and will also work directly instead of writing a [tag:batch-file], _as mentioned in my previous comment_. – Compo Aug 02 '19 at 16:44
  • @Compo Well I did, but I have no idea what all this mean. I'm very new at BATCH and don't understand some stuff. – younyokel Aug 02 '19 at 16:55
  • You might Investigate 'popen'. When 'input' mode, all output of what-popen-runs is streamed back to the thread that invoked popen (through a pipe). There are C++ examples on SO. – 2785528 Aug 02 '19 at 17:57
  • @EadwineYoun A C++ application can use directly the Windows classes to get the information you need in your application. There is absolutely no need to run `wmic.exe` and process the UTF-16 LE encoded output of it. See Microsoft documentation for [Win32_OperatingSystem class](https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-operatingsystem) and [Win32_VideoController class](https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-videocontroller). – Mofi Aug 02 '19 at 18:21
  • @EadwineYoun For C++ code examples using these two WMI classes look on the Stack Overflow search results [\[c++\] Win32_OperatingSystem](https://stackoverflow.com/search?q=%5Bc%2B%2B%5D+Win32_OperatingSystem) and [\[c++\] Win32_VideoController](https://stackoverflow.com/search?q=%5Bc%2B%2B%5D+Win32_VideoController). Both searches can be limited even more on using additionally `class` as word to find in a question or answer. – Mofi Aug 02 '19 at 18:26
  • @Mofi Oh, so you mean I can use all these functions without accessing the custom batch files? – younyokel Aug 02 '19 at 20:07
  • [popen(3) - Linux manual page](http://man7.org/linux/man-pages/man3/popen.3.html) – David C. Rankin Aug 02 '19 at 21:40
  • @EadwineYoun Yes, you can write your C++ application with using these classes in Windows libraries. There is no need to run `wmic.exe` directly as process from within your C++ application or indirectly via a command process and a batch file. `wmic.exe` uses also just Windows libraries to get the data and output them. See also [WMI Reference](https://learn.microsoft.com/en-us/windows/win32/wmisdk/wmi-reference) linking to __COM API for WMI__ linking to [Creating a WMI Application Using C++](https://learn.microsoft.com/en-us/windows/win32/wmisdk/creating-a-wmi-application-using-c-). – Mofi Aug 03 '19 at 13:24

2 Answers2

1

A possible, though admittedly not ideal solution would be to have the batch file write it's output to a .txt file and then read in that file into your program. Look at this SO thread to see how to do this.

arc-menace
  • 435
  • 6
  • 19
1

You need to redirect output when using system():

#include <cstdio>   // std::remove(const char*)
#include <cstdlib>  // std::system(const char*)
#include <fstream>
#include <iostream>
#include <string>
#include <unordered_map>

int main()
{
  std::string foo_bat = "foo.bat";
  std::string foo_out = "foo.out";

  // Write the batch file
  {
    std::ofstream f( foo_bat );
    f << R"z(
      @echo off
      wmic os get caption /value
      wmic path win32_videocontroller get description /value
    )z";
  }

  // Execute the batch file, redirecting output using the current (narrow) code page
  if (!!std::system( (foo_bat + " | find /v \"\" > " + foo_out + " 2> NUL").c_str() ))
  {
    // (Clean up and complain)
    std::remove( foo_bat.c_str() );
    std::remove( foo_out.c_str() );
    std::cout << "fooey!\n";
    return 1;
  }

  // Read the redirected output file
  std::unordered_map <std::string, std::string> env;
  {
    std::ifstream f( foo_out );
    std::string s;
    while (getline( f >> std::ws, s ))
    {
      auto n = s.find( '=' );
      if (n != s.npos)
        env[ s.substr( 0, n ) ] = s.substr( n+1 );
    }
  }

  // Clean up
  std::remove( foo_bat.c_str() );
  std::remove( foo_out.c_str() );

  // Show the user what we got
  for (auto p : env)
    std::cout << p.first << " : " << p.second << "\n";
}

WMIC is a problematic program when it comes to controlling the output code page, hence the weird pipe trick we use with system().

But, after all that, you should use the WMI API directly to get this kind of information.

Dúthomhas
  • 8,200
  • 2
  • 17
  • 39
  • Oh, nevermind. I have now other problem with it. Looks like your code returns only one videocard. I have NVIDIA GeForce and Intel HD Graphics on my laptop and it couts only the Intel one. – younyokel Aug 03 '19 at 07:29
  • @EadwineYoun The code that returns the (primary) video card is _yours_, from your question. How to get a list of all attached video cards in C or C++ would be a separate question. – Dúthomhas Aug 03 '19 at 10:22