Question

I'm adapting a console based program to GUI.

The console program reads a text file and "compiles" it.

My GUI application reads the text file and displays in a RichTextBox.

I'm looking for a method to treat the RichTextBox as a C++ std::istream. This would allow me to use code from the console program without modifying it.

I searched the web and StackOverflow and didn't find any solutions for treating a RichTextBox as an std::istream.

Does anybody know of any Winforms library functions that would allow treating of RichTextBox as an std::istream?

My ideas:

  1. Create an adapter to treat RichTextBox as a stream.
  2. Change the console program to pass a "getline" function to the compiler portion, and have two getline functions (one as the std::getline, another to get a line from the RichTextBox).
  3. Write the RichTextBox contents to a file and feed the file to the compiler.

I'm using Visual Studio 2010 on Win 7 using ".NET" 4.0, using C++ (don't suggest any C# techniques as I'm not fluent in translating).

Était-ce utile?

La solution

In real C++, you can create a stream buffer from an RTF control like this:

class RTF_buf : public std::streambuf {
    std::vector<char> buffer;
public:
    RTF_buf(HWND ctrl) {
        DWORD len = SendMessage(ctrl, WM_GETTEXTLENGTH, 0, 0);
        buffer.resize(len+1);
        SendMessageA(ctrl, WM_GETTEXT, len+1, (LPARAM)&buffer[0]);
        setg(&buffer[0], &buffer[0], &buffer[len]);
    }
};

Note that this isn't actually restricted to an RTF control. Just for one other example, it'll also work fine with a normal EDIT control.

C++/CLI adds a few wrinkles to this. First of all, you're dealing with "wide" characters in the RichTextBox. Second, you won't (normally) start with an HWND -- you have to retrieve that from the System.Windows.Forms.RichTextBox via its Handle property. This, unfortunately, returns the HWND as an IntPtr instead of an HWND, so you have to add a cast to get it to the right type. That makes the code a little uglier, but nothing too terrible:

#include <windows.h>
#include <streambuf>
#include <iostream>
#include <vector>
#include <algorithm>

#pragma comment(lib, "user32.lib")

using namespace System;
using namespace System::Windows::Forms;

class RTF_buf : public std::wstreambuf {
    std::vector<wchar_t> buffer;
public:
    RTF_buf(RichTextBox^ control) {
        HWND ctrl = *reinterpret_cast<HWND *>(&control->Handle);
        int len = SendMessage(ctrl, WM_GETTEXTLENGTH, 0, 0);
        buffer.resize(len+1);
        SendMessage(ctrl, WM_GETTEXT, len+1, (LPARAM)&buffer[0]);
        setg(&buffer[0], &buffer[0], &buffer[len]);
    }
};

We can create a buffer and istream something like this:

RTF_buf b(this->richTextBox1);
std::wistream in(&b);

Finally, we can read data from our stream and process them like we would essentially any other (wide) stream. For example:

wchar_t ch;

while (in >> ch)
    // do something with ch

So C++/CLI does add a little complexity to the task, but ultimately only a little--mostly the one line to obtain the handle of the control, and cast it to the correct type. Other than that the code for the buffer class barely needs to change at all, and instantiating and using it changes only to the extent that we're working with wide characters instead of narrow.

Autres conseils

The proper way to create a stream plugging into the IOStreams library is to implement a stream buffer, i.e., to derive from std::streambuf or std::wstreambuf (I'm not a Windows programmer but my understanding is that most code travels in terms of wchar_t rather than char) and override the suitable virtual member functions. Assuming you can get characters in bunches (possible all in a bukl) all you'd really overload is underflow() which is called if the input buffer was exhausted. If you can get all the characters during construction you can also set up a buffer.

Once you have a stream buffer you can use a pointer to the stream buffer to initialize an std::istream. Here is a simple example which uses a memory arean passed in the constructor as its input:

#include <iostream>
#include <streambuf>

class membuf
    : std::streambuf {
public:
    membuf(char* buffer, std::size_t size) {
        this->setg(buffer, buffer, buffer + size);
    }
};

int  main() {
    char         input[] = "hello, world!\n";
    membuf       sbuf(input, sizeof(input - 1));
    std::istream in(&sbuf);

    char buffer[100];
    if (in.getline(buffer, sizeof(buffer)) {
        std::cout << "read '" << buffer << "'\n";
    }
    else {
        std::cout << "ERROR: failed to read a line but Dietmar said...!?!\n";
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top