memorystreamを文字列として変数等に入れたい


ごんすけ  2025-06-02 16:58:52  No: 152038  IP: [192.*.*.*]

画像でもTreeviewでもオブジェクトデーターでもコンポーネントによくsavetosteam等の項目があり、stream経由でファイル保存できる機能がありますが、それをファイルと書き出すのではなく、文字列や変数として書き出すことはできませんでしょうか?

イメージとしてはxrayさまの「04_コンポーネントをテキスト形式で保存」
http://mrxray.on.coocan.jp/Delphi/plSamples/130_SaveComponent.htm

の書き出された内容のようなものをファイル経由ではなく値としてtmemo等に取得し
tmemoに入れた値からloadfromstreamで復元するというようなイメージをしています。

テキストデータ文字列はXMLでもJSONでもbase64でも文字列としてだし文字列として読み込みloadstreamで復元できれば
こだわりません

編集    削除
ごんすけ  2025-06-02 17:16:44  No: 152039  IP: [192.*.*.*]

用途としてはini等にコントロールの内容を保存することを想定しています。

stringgridなどの場合はループで回し文字列にし、それを文字列から復元というような事をしていましたが
その部分をコントロールの仕組みを理解せずにini等にstreamをテキスト書き出し、それを元に復元という感じのイメージをしています。

編集    削除
AAAAA  2025-06-02 20:03:25  No: 152040  IP: [192.*.*.*]

>画像でもTreeviewでもオブジェクトデーターでもコンポーネントによくsavetosteam等の項目があり、stream経由でファイル保存できる機能がありま
>すが、それをファイルと書き出すのではなく、文字列や変数として書き出すことはできませんでしょうか?



>用途としてはini等にコントロールの内容を保存することを想定しています。

矛盾してないか?


編集    削除
ごんすけ  2025-06-02 20:49:39  No: 152041  IP: [192.*.*.*]

>矛盾してないか?

お番様です。コメントありがとうございます。

ストリーム形式で書き出したいという目的ではなくコンポーネントの機能としての書き出しメソッドが
savetostreamとsavetofileの2種類しかない場合にINIなりデーターベースの普通のフィールドに項目内容を保存し
たいかんじです。

DBの場合はstreamTOBLOB等ありフィールドを保存先にでき、画像の場合はBASE64等で保存を見かけますが、そのように内容を1つのファイルに複数のものをコントロールのアペンドルールとかを気にせずにストリームを利用すれば、loadstreamでループ代入とか整形しループデーター作成を掛けなくても元の様に復元できるのではないか思った次第です。



編集    削除
AAAAA  2025-06-02 21:33:16  No: 152042  IP: [192.*.*.*]

http://mrxray.on.coocan.jp/Delphi/plSamples/130_SaveComponent.htm
で十分じゃないのか?

編集    削除
ごんすけ  2025-06-02 22:39:34  No: 152043  IP: [192.*.*.*]

そこの   ObjectBinaryToTextが、おっ。とおもったんですが
ストリームで受けて、ストリームで出して保存をしているので
iniに代入できない感じなのです。

なので、それを使うとしたら一度ファイルとして保存をかけてからloadstreamで読み込んで再度savestreamをかける感じになり数メガ程度ならいいですが数百メガになると実用的にならないなとおもいました。

単行で文字をstreamを保存するものは見かけたんですが

Treeview等で下記のようなループオブジェクトでストリームに入る場合に変数に入れ、それを展開するさばき方が
イメージできない感じになっています。
---------------
  type
PMyRec=^TMyRec;
TMyRec=record
  Text:string;
  age:string;
end;
---------------
このような場合は、単純にスプリッタで区切りループで書き出して結合して文字列として変数に保存をかけ
その値をスプリッタで戻し、AddChildとかで内容を再生成するしかないんでしょうかね?

streamにこだわっているわけではなく、生成が省ければいいんですが
有料なので確認はしていないんですが、こんな感じのイメージをしています。
http://www.deepsoftware.ru/rsllib/index.html

編集    削除
AAAAA  2025-06-03 05:24:31  No: 152044  IP: [192.*.*.*]

>用途としてはini等にコントロールの内容を保存することを想定しています。

>数百メガになると実用的にならないなとおもいました。
とは?

編集    削除
AAAAA  2025-06-03 05:28:34  No: 152045  IP: [192.*.*.*]

これかな
https://www.migaro.co.jp/tips/4440/

編集    削除
ごんすけ  2025-06-03 09:15:32  No: 152046  IP: [192.*.*.*]

おはようございます。

>これかな

それはTOOLSAPIを調査している時に試したことがあるんですが、コントロール内容の復元ではなくプロパティーの取得や代入のようなコントロールの状態の操作だった気がします。結局はusesしないとコントロールのプロパティーが参照できないので、その手法ではなくクリップボードのコントロール名をDFMファイルとマッチングさせ、それを正規表現でさばきプロパティーを得る感じにした気がします。

下記で言うitem3とか、item3.1.1や右の画像や内容をテキストで保存して復元したいのです。
https://raw.githubusercontent.com/DenisAnisimov/decTreeView/master/Img/decTreeView.png

な、感じだと、探してみて今控えに見当たらなかったんですが、XRAYさんのページで前にlistboxでもtreeviewでもなんでもバイナリファイルで簡単に保存できるというのがあったと言う話になると思いますが、状態を戻す部分が趣旨ではなくiniのような1つのファイルで複数保持し復元したいのです。

Treeviewで例えるとオブジェクトとして画像も保存するとTreeデータが数百メガになりますし、現実的な設計だとindexだけ保持し、それを元に画像の参照をかける感じになると思いますが、そうすると管理ファイルがTreeviewのデーターと画像データの2つになってしまいます。

もし、treeviewのデーターをバイナリではなく、テキストで保存できれば、暗号化もできますし、iNIでも、DBでも、別フィールドに記憶しそれに対し参照や代入をかける感じで1つのファイルで管理ができるのではと思いました。

編集    削除
vram  2025-06-03 09:27:29  No: 152047  IP: [192.*.*.*]

まったく性質が異なる2つの要望を同時にしてませんか?

まずは iniに格納するの前提の話
published定義の変数なら他の人も言われているようにObjectBinaryToTextを使えばいい
プロパティの有無でエラーが出るのが嫌なら自作すればよい

このへんで少し説明してます
https://qiita.com/vram/items/5cf926ca4160c4855e7a

> 数百メガになると実用的にならない

それはアプリが自分専用のファイルを管理して
その1つのファイルの中で自分のプロジェクトに必要なものを全て書き込んでいる
という形式ですかね?

一番わかりやすい形式だとPSDとかですが
後半に連続するバイナリ領域を前半にそのバイナリ領域へのインデックス値とサイズが管理されています
ただし数百メガになるとそれでも役に立ちません

数百メガで対応しようとするとそれは仮想ストレージ形式になります
テキストやバイナリなど様々な形式が扱えて
追加した場合は、その追加したバイナリが追加されます
削除はインデックスからの削除なので最適化しないと容量は変わらないですね
それはどこにあるのか?

そんなものは無いので自分で作るしか無いもしくは市販ライブラリを使うのでしょうけどやったこはありません
なぜならちょっとミスするだけで数百のファイルが読めなくなったりバイナリが崩れるからです

それぐらいならストリームの読み書きが出来るように見せかけておいて
裏でフォルダ作ってそこにファイルを作って読み書きする方が楽じゃないですかね?

編集    削除
ごんすけ  2025-06-03 10:36:28  No: 152048  IP: [192.*.*.*]

ObjectBinaryToTextで下記で文字列化できるのではと思いやってみたところ
invalid stream formatとなり参照できない感じでした。
//-----------------------------------------------
  type
PMyRec=^TMyRec;
TMyRec=record
  Text:string;
 age:string;
end;

procedure TForm1.Button7Click(Sender: TObject);
var
ms,  LStrStream : TMemoryStream;
begin
ms := TMemoryStream.Create;

VirtualStringTree1.SaveToStream(ms);

    LStrStream := TMemoryStream.Create;
    try
      ms.Position := 0;
      ObjectBinaryToText(ms, LStrStream);
      LStrStream.Position := 0;
    Memo1.Lines.LoadFromStream(LStrStream);
    finally
      FreeAndNil(LStrStream);
    end;

LStrStream.Free;
ms.free;
end;
//-----------------------------------------------
>一番わかりやすい形式だとPSDとかですが

それはフォトショのファイルでしょうかね?
あの様にバイナリで独自ファイルとして完全に管理したい感じではなく
savestreamとloadstreamで内容を1発で戻せるのなら、streamを文字列にできれば、何のファイルにでもテキストとして記憶でき、それを元に復元できるんじゃない?という感じです。

>裏でフォルダ作ってそこにファイルを作って読み書きする方が楽じゃないですかね?

楽さをとるのならそうなんですが元のデータの保存と管理データの保存で2重処理になるので倍の時間が
掛かる気がするので、そこをmemorystreamを文字列に変換し、1度の保存で済ませられればと思った感じです。

編集    削除
vram  2025-06-03 11:15:11  No: 152049  IP: [192.*.*.*]

使われている用語が微妙に違うので混乱しているのかも

> streamを文字列にできれば

TStringStreamクラスがあるぐらいなので ストリームは文字列対応です
もしかして「シリアライズ化」ではないでしょうか?

> invalid stream formatとなり参照できない感じでした。

ObjectBinaryToTextは基本 TPersistentやTComponentを継承したクラスの published に定義されたプロパティを保存読み込むものです

昔書いた参考プログラムを書きますが結構古いです

クラス定義
type

  TTestComp = class(TComponent)
  private
    { Private 宣言 }
    FVInt: Integer;
    FVStr: string;
    FVBool: Boolean;
    FVDouble: Double;
    FVInt64: Int64;
    FVSet: TTestSet;
    FVColor: TColor;
    FVFontStyle: TFontStyles;
    FVDate: TDateTime;
    FVComp: TTestCompSub;
    FVFont: TFont;
    FVValiant: Variant;
    FVType: TTestType;
  public
    { Public 宣言 }
  published

    property VInt : Integer read FVInt write FVInt default 1;
    property VStr : string read FVStr write FVStr;
    property VBool : Boolean read FVBool write FVBool;
    property VDouble : Double read FVDouble write FVDouble;
    property VInt64 : Int64 read FVInt64 write FVInt64 default 123456789;
    property VType : TTestType read FVType write FVType;
    property VSet : TTestSet read FVSet write FVSet;
    property VColor : TColor read FVColor write FVColor;
    property VDate : TDateTime read FVDate write FVDate;
    property VFont : TFont read FVFont write FVFont;
    property VComp : TTestCompSub read FVComp write FVComp;
    property VValiant :  Variant read FVValiant write FVValiant;
  end;

// フォームの宣言部に定義
  private
    { Private 宣言 }
    FTestComp : TTestComp;
    FSaveObj : TComponent;
    function BinToTextOut(m : TMemoryStream) : string;

// フォームの生成イベントで生成
procedure TForm1.FormCreate(Sender: TObject);
begin
  FTestComp := TTestComp.Create(Self);
end;

// フォームの破棄イベントで破棄
procedure TForm1.FormDestroy(Sender: TObject);
begin
  FTestComp.Free;
end;

// ObjectBinaryToTextを使いやすくする関数 引数ストリームクラス 返り値テキスト化されたデータ
function TForm1.BinToTextOut(m: TMemoryStream): string;
var
  StrStream: TStringStream;
  s: string;
begin
  StrStream := TStringStream.Create(s);
  try
    m.Seek(0, soFromBeginning);
    ObjectBinaryToText(m, StrStream);
    StrStream.Seek(0, soFromBeginning);
    Result:= StrStream.DataString;
  finally
    StrStream.Free;

  end;
end;

// ボタンクリックイベント
procedure TForm1.Button1Click(Sender: TObject);
var
  e : TMemoryStream;
  s : string;
begin
  // ここで保存/読込で試すオブジェクトを指定 フォームに色々置いて試す
  case  of
    0 : FSaveObj := Form1;
    1 : FSaveObj := Form1.Button1;
    2 : FSaveObj := Form1.Memo1;
    3 : FSaveObj := Form1.Panel1;
    4 : FSaveObj := Form1.Label1;
    5 : FSaveObj := FTestComp;
  end;
  e := TMemoryStream.Create;
  try
    e.WriteComponent(FSaveObj);   // ストリームにクラスの要素(実行時型情報)が送られる(バイナリ)
    s := BinToTextOut(e);         // ストリームに書き込まれた値(実行時型情報)をテキスト化(シリアライズ)
  finally
    e.Free;
  end;
end;

編集    削除
ごんすけ  2025-06-03 11:31:35  No: 152050  IP: [192.*.*.*]

>使われている用語が微妙に違うので混乱しているのかも

あっ。もしかしたら私の表現が間違っていた(認識)のでaaaaaさんやvramさん
のアドバイスと食い違っていたり性質が異なる2つの要望になっているのかもしれません。

オブジェクトと言っていたのはレコード型の意味で
レコード型はオブジェクトなんだと私が思い込んでいたのが原因かもしれません。

よく、listboxやcomboboxで項目のほかにOBJECT定義で別の値を保持できますよね。
なので、複数値=オブジェクトだと思っていました。

 ようはstreamに入っていた内容をstring化して保存&読み出ししたいだけの感じです。

編集    削除
ごんすけ  2025-06-03 11:36:11  No: 152051  IP: [192.*.*.*]

シリアライズ化というのが文字置換な認識しかなかったので調べてみましたら
「複数の要素を一列に並べる操作や処理のこと。」と出てきました。

たぶん、其れです。入力内容をstringとして1つにし、それを読み込みstreamに代入し
savestreamしloadstreamした状態に持っていきたいのです。

編集    削除
vram  2025-06-03 11:53:42  No: 152052  IP: [192.*.*.*]

やりたいことがわかったのでここからが本題ですね
Delphiにはstreamに様々なオブジェクトを読み込み、書き込みする機能が用意されています

TComponentを継承したクラスに複数の要素を定義して
そのクラスをstreamに書き込むと定義した要素がシリアライズ化されて書き込まれます
この段階ではバイナリー形式になっているので
ObjectBinaryToTextを使ってバイナリーをテキストにしています
得られるテキストは iniファイルに書き込むような形式です
もちろん iniファイルから読み込んで テキストをバイナリにして
TComponentを継承したクラスの要素として復元する機能もあります

それと最近?のDelphiではiniファイル以外の形式としてJSON形式にも対応しています

JSON オブジェクト フレームワーク
https://docwiki.embarcadero.com/RADStudio/Rio/ja/JSON_%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88_%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF

JSON リーダー/ライタ フレームワーク
https://docwiki.embarcadero.com/RADStudio/Rio/ja/JSON_%E3%83%AA%E3%83%BC%E3%83%80%E3%83%BC/%E3%83%A9%E3%82%A4%E3%82%BF_%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF

編集    削除
ごんすけ  2025-06-03 15:24:09  No: 152053  IP: [192.*.*.*]

>やりたいことがわかったのでここからが本題ですね

(>_<)やっぱり私の勘違いが原因だったんですね。
そう思うと言わんとしていたことがつたわりました。

あれ?「この段階ではバイナリー形式になっているので」という事は、バイナリを文字化する関数が
あったような気がするので、それでString形にデコードとエンコードすればいいだけなのかな?

「ObjectBinaryToTextのバイナリーをテキストにしています。というのが
認識できる文字列に直すという意味の文字列化なら、そうではなく中身はなんで、どんな管理をして、見て認識できなくてもいいんです。

>それと最近?のDelphiではiniファイル以外の形式としてJSON形式にも対応しています

JSONの方がなじみがあります♪が、JSONの話しが出てきたという事は、キーに各項目内容を関連づけて処理しましょうと言う話しだと思うのですが、それだと、ループで展開代入と同じになるとおもうので、それをおこなわずに
バイナリ状態をSTRING型に入れ、String型をバイナリに戻したいという感じです。

編集    削除
ごんすけ  2025-06-03 15:32:51  No: 152054  IP: [192.*.*.*]

例えば、画像をbase64に変換するとSTRING型になり、その文字列をINIにいれれますよね?
そして、それをbase64から画像に戻せますよね?

それと同じように、memorystreamを意味が分からない表記でいいのでbase64のようにString化し
というような事を思っていたんですが、もしかしたら、画像とか文章とかしかBASE64化したことが無かったので
その為のものだと思っていましたが、memorystreamもbase64化して、それを戻せばいけるのかもと思い始めてきました。

ですが、確かbase64化すると普通の文字列でも容量が増えるので、もしかしたら他の手法でSTRING化がありそうな気がするので調べてみようと思います

編集    削除
AAAAA  2025-06-03 15:48:09  No: 152055  IP: [192.*.*.*]

>認識できる文字列に直すという意味の文字列化なら、そうではなく中身はなんで、どんな管理をして、見て認識できなくてもいいんです。
バイナリイーでよくないか?
テキストにしたい理由は?

編集    削除
vram  2025-06-03 15:55:12  No: 152056  IP: [192.*.*.*]

ストリームクラスのメソッド WriteComponentに引数として TComponentを継承したクラスを渡すとストリームに書き込まれます。なのでそのストリームを保存すればバイナリですが保存出来ます
「ループ」という言葉が出てきているのでリストのような構造と思われますが
残念なことにリストには直接対応していません

なのでストリームの先頭にリストの個数を書き込み
その後ろにループ文で WriteComponent(Items[i]) // Items[i] は TComponent型から継承したクラス
と書き込み

読み込む時は個数を読み込んでから読み込むことになります。
EOFまで読み込むように作れば個数を書き込まなくても良いと思います

ただ重要な問題点として 要素を増やすとシリアライズ化したときデータの長さが崩れる
また要素を減らすとReadComponentで存在しない要素を読み込もうとしてエラーになるなどがあります

ではどうすればいいのか?
バイナリでも出来なくはないですが説明用にObjectBinaryToTextでテキスト化したものを
データ数分の文字列リストとして書き込むのです

読み込む時はその文字列リストの行数=データ数になります
ただしバイナリでもObjectBinaryToTextでもその変換で特殊文字コードが含まれていても
1行分の普通の文字列として管理できるようにするためのエンコード、デコードが必要です
(文字列中に改行や「"」「,」などがあると代入処理で誤動作する)

奥が深いというかめんどくさいですね

もしTComponentを継承したクラスに要素を保存してそのクラスをTListクラスで管理している
という形式で設計していくというのであれば
クラスの要素丸ごと iniに保存、読込するクラスを Qiitaに投稿しておきますよ
(要望が無くても近日中に公開を予定しています)

編集    削除
ごんすけ  2025-06-03 16:49:49  No: 152057  IP: [192.*.*.*]

バイナリのままだと、BLOB以外のフィールドやINIやテキストファイルに直接入れられない事と他のデーターと結合する際に対象のフォーマットを解析しバイナリベースでファイルに情報の追加が必要になる為になります。

>WriteComponentに引数として TComponentを継承したクラスを渡すと

それがXrayさんのところで見たlistboxやCBやSG等を復元する仕組みだったと思いますが、それだとコントロールに最初からあるバイナリセーブと同じになり、無関係のデーターと結合できなくなってしまうんですよね。で結局は、各設定ファイルとデーターファイルの2つを利用しなくてはいけない感じになってしまいます。

>読み込む時はその文字列リストの行数=データ数になります

それだとCSVで書き出しループで戻すのとにたような感じですよね?
stringgrid等はcol,rowだけなので、その感じでやっているんですが、Treeviewや色々なものに対応するとなると
ちぇくを入れる処理=フラグとしてcellを用意し、その値を見てSGに戻すという感じになりますよね?

でもstreamなら書き出し読み込みだけで、その処理もなく復元がされる。ならstreamを文字列にし復元すればいいのではないか?と思った感じです。

>(文字列中に改行や「"」「,」などがあると代入処理で誤動作する)

改行の処理も必要になり、streamをString化できれば、そのような処理も不要だとにらんだ感じです。

>TListクラスで管理しているという形式で設計していくというのであれば

TLISTというのはあまり使ったことが無いのでどのような感じになるかイメージできませんが
tintlistはソートが簡単に出来なかったりで使いにくかったイメージがあります。たぶん、イメージと違うものになる気がするので、stream2stringのようなものをもう少し調べてみたいと思います。

編集    削除
AAAAA  2025-06-03 18:23:13  No: 152058  IP: [192.*.*.*]

同じこと繰り返すけど
http://mrxray.on.coocan.jp/Delphi/plSamples/130_SaveComponent.htm
だよね やりたいこと?

テキスト作成されるし,
TFileStream を TMemoryStream にすれば ファイル作成されないし

INI形式にこだわる理由がわからないけど

編集    削除
ごんすけ  2025-06-04 01:04:05  No: 152059  IP: [192.*.*.*]

>INI形式にこだわる理由がわからないけど

INIというかStringを重視している感じです。
なので、stringしかいれられないINIを例にした感じで、そこはtmemoでもstringridでもなんでもいい感じです。

>同じこと繰り返すけど

色々書いてあるのでどの部分の事かが私が判っていないのかも・・・・

下記のものを上記のページで例えるとどのような感じになるでしょうか?
///////////////////////////////////////////////////

// ----- こうして保存したものを -----
procedure TForm1.Button10Click(Sender: TObject);
var
ms:TMemoryStream;
s:string;
begin
ListBox1.items.add('aaa');
ListBox1.items.add('bbb');
ListBox1.items.add('ccc');

ms := TMemoryStream.Create;
ListBox1.items.SaveToStream(ms);

//ココが不明
s:=       msを代入

Memo1.Text:=s;
ms.Free;
end;


// ----- こうして復元 -----

procedure TForm1.Button11Click(Sender: TObject);
var
ms:TMemoryStream;
s:string;
begin
ListBox1.Clear;

ms := TMemoryStream.Create;
s:=Memo1.Text;

//ココが不明
Streamに代入←  s;

ms.position:=0;
ListBox1.items.LoadFromStream(ms);
ms.Free;
end;

編集    削除
AAAAA  2025-06-04 04:39:04  No: 152060  IP: [192.*.*.*]


-------------------------------------------
var
ms:TStringStream;
s:string;
begin
ListBox1.items.add('aaa');
ListBox1.items.add('bbb');
ListBox1.items.add('ccc');

ms := TStringStream.Create;
ListBox1.items.SaveToStream(ms);

MS.Position := 0;
Memo1.Text:=MS.ReadString(MS.Size);
ms.Free;

--------------------------------------

TStrings なら 
Memo1.Lines.Text := ListBox1.Items.Text
Stream経由にする必要ないし

StringGrid なんて Save ないよ?


編集    削除
vram  2025-06-04 08:21:13  No: 152061  IP: [192.*.*.*]

フォームにListBox1とMemo1 Button1 Button2を置いてボタン1と2のクリックイベントの中身と関数2つのソースを書きます
ボタン1がListBox1をシリアライズ化してテキスト化してメモに表示
ボタン2がメモの内容をシリアライズ化された文字列として受け取りListBox1に復元します
ボタン2のイベントの先頭でクリアしているのに復元されると思います
ボタン1を押してからMemo1の内容を手動で修正してボタン2を押すとその内容がListBox1に反映されます

途中テキスト化していますがテキスト化せずに保存読込でもOKです。
リソース形式 拡張子 res 形式で保存されます。

function BinToText(m: TMemoryStream): string;
var
  StrStream: TStringStream;
  s: string;
begin
  StrStream := TStringStream.Create(s);
  try
    m.Seek(0, soFromBeginning);
    ObjectBinaryToText(m, StrStream);
    StrStream.Seek(0, soFromBeginning);
    Result:= StrStream.DataString;
  finally
    StrStream.Free;
  end;
end;

procedure TextToBin(m: TMemoryStream;str : string);
var
  StrStream: TStringStream;
begin
  StrStream := TStringStream.Create(str);
  try
    m.Seek(0, soFromBeginning);
    StrStream.Seek(0, soFromBeginning);
    ObjectTextToBinary(StrStream, m);
    m.Seek(0, soFromBeginning);
  finally
    StrStream.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  e : TMemoryStream;
  s : string;
begin
  ListBox1.items.add('aaa');
  ListBox1.items.add('bbb');
  ListBox1.items.add('ccc');
  e := TMemoryStream.Create;
  try
    e.WriteComponent(ListBox1);
    s := BinToText(e);
    Memo1.Lines.Text := s;
  finally
    e.Free;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  e : TMemoryStream;
  s : string;
begin
  ListBox1.Clear;
  e := TMemoryStream.Create;
  try
    s :=  Memo1.Lines.Text;
    TextToBin(e,s);
    e.ReadComponent(ListBox1);
  finally
    e.Free;
  end;
end;

編集    削除
ごんすけ  2025-06-04 11:53:15  No: 152062  IP: [192.*.*.*]

そうです。そのような感じの事をやりたかったんです。
意味が伝わったようで安心しました。

ただ、下記のようにWriteComponentで行うとrootの項目情報しか文字列化されていないのとコントロール限定になってしまうようなので少しイメージが違う感じでWriteComponentせずにSaveToStreamのものを入れるとエラーになってしまいました(多分、streamの中には下記代入したTOBJECTの内容も入っていますよね?)

procedure TForm1.Button16Click(Sender: TObject);
var
  e : TMemoryStream;
  s : string;
begin
 ListBox1.Clear;
Listbox1.items.addObject('aaa',TObject(1111));
Listbox1.items.addObject('bbb',TObject(2222));
Listbox1.items.addObject('ccc',TObject(3333));
  e := TMemoryStream.Create;
  try
    e.WriteComponent(ListBox1);

//    ListBox1.items.SaveToStream(e); 

    s := BinToText(e);
    Memo1.Lines.Text := s;
  finally
    e.Free;
  end;
end;
//----- 結果 ------
object ListBox1: TListBox
  Left = 776
  Top = 64
  Width = 121
  Height = 97
  ItemHeight = 12
  Items.Strings = (
    'aaa'
    'bbb'
    'ccc')
  TabOrder = 11
end

>TStrings なら Stream経由にする必要ないし
>StringGrid なんて Save ないよ?

項目を戻したいというより、streamをstringにしたかったのです。
普通のStringGridはsaveはなかったんですね。というセーブではなく

例えばstringgridのセルにbase64の画像の文字列をいれ、セル指定でbase64の画像を指定コントロールに表示させられますよね?

0001|画像1の文字列|画像2の文字列|画像3の文字列|・・・・・・・

のようにstreamの内容をSTRING化できれば各cellに入れ、それの参照をかける事により復元できるのではとおもいました。

コントロール1の内容でコントロール2の内容に複写みたい場合は下記のようなものだと、
savestreamしloadstreamすればコントロールの複写ができます。

https://imgur.com/syRO0gR

SGにTBITMAPのSTREAMをいれたいときも下記のようにすることによりsavestreamとloadstreamをするだけでTBITMAPのストリームを代入できます。

AdvStringGrid1.CreateBitmap(1,AdvStringGrid1.RowCount-1,true,haCenter, vaAboveText).LoadFromStream(stream);

のような感じで項目の復元な感じではなくstream情報の文字列化と復元がしたい感じで
WriteComponent経由だと、なんでもかんでもstreamの感じにはならない感じで思念しています。

編集    削除
vram  2025-06-04 14:11:01  No: 152063  IP: [192.*.*.*]

WriteComponentで書き込まれる要素は
publishedで一般的な型として定義されている要素です。

「StringGridに文字列や画像が入っていてそれを直接保存読込したい」
というのは気持ちはわかりますがおすすめしません。

データとGUIがありGUIだけでしようとすると無理が来ます
今のところは

読込
ファイルのデータを読込→SringGridの表示に必要なデータを要素に持ったクラス→SringGridを表示する

書き込み
StringGirdの編集イベントまたは保存のタイミング→編集されたデータを内部のクラスの要素に反映→ファイルに保存

こうなります。

さっき折角説明したやり方がまったく使えないかというとそうではありません

TOrign = class(TComponent)
  private
    FBmp : TBitmap;  // このビットマップをBase64の文字列で保存復元したい
    
  published
    BaseStr : string read GetBaseStr write SetBaseStr;  // こうやって要素は文字列ですよと定義する

function TOrign.GetBaseStr();
begin
  result := BmpToBase64Str(FBmp); // ビットマップをBase64化した文字列で返す関数を作る
end;

procedure TOrign.SetBaseStr(Value : string);
begin
  Base64StrToBmp(FBmp,Value);  // Base64の文字列をビットマップに復元する関数を作る
end;

これでTOrignをさっきのシリアライズ化で保存復元すればiniやresのBase64文字列からビットマップも保存復元できるのです
ただしStringGrid直接では無く、その保存復元に必要な要素を持ったクラスに対して行い、StringGirdとDataの関係を自分で作る必要があります

編集    削除
ごんすけ  2025-06-04 16:02:58  No: 152064  IP: [192.*.*.*]

bitmapを処理したい感じではなく

bitmap→base64で文字列化→それをcell入れる
cellからbase64を読み込む→bitmapに戻す。

という流れができるように

memorystreamの内容→文字列化→cellにいれる
cellの文字列→memorystream化。

という感じでbainary2string、string2binaryのような感じができないかと思っています。

なので、listbox2:=listbox1;やsavestream、loadstreamで複製が出来るのと同じように
streamの中には複製のための情報が入っている(たぶん)、それをstring化し、またstreamに戻せば
各種手続きをなく同じものが生成できるのではと思いました。

編集    削除
AAAAA  2025-06-04 16:51:14  No: 152065  IP: [192.*.*.*]

例が毎回ばらばなのよ
ListView だったり StringGrid だったり Bitmapだったり
なにがしたいの?

コンポーネント関係なく汎用的にセーブするのが
http://mrxray.on.coocan.jp/Delphi/plSamples/130_SaveComponent.htm

これは設計時フォーム上で「エディタ上で表示」で表示されるやつな感じ

TreeView とか ListView は 000EF123 みたいになるので

>バイナリのままだと、BLOB以外のフィールドやINIやテキストファイルに直接入れられない事と他のデーターと結合する際に対象のフォーマットを>解析しバイナリベースでファイルに情報の追加が必要になる為になります。
はできないと思う

なにが したいのか が わからない

編集    削除
ごんすけ  2025-06-04 17:07:00  No: 152066  IP: [192.*.*.*]

>なにが したいのか が わからない

memorystreamの情報→文字列化
文字列→memorystreamという感じです。

メモリーストリームの内容は対象にかかわらずに
色々な環境でロードストリームできますよね?

ということは、ストリーム自体に変動可能な情報を持っていてまるまる文字化すれば。
そのまま復元できると踏んだ次第です。

listviewでも画像でもstringgridでもストリームに入れれば
目的に合わせてストリームロードで代入できますよね?そこがコントロール依存の話しで進んでいるので色々な例が出てしまう感じになっています。

編集    削除
ごんすけ  2025-06-04 17:12:29  No: 152067  IP: [192.*.*.*]

そこで、まえに出したURLのように
http://www.deepsoftware.ru/rsllib/index.html

なんでもかんでもXMLやSTRING等の文字列に変換し
それを相互に戻せるという仕組みを作りたかっのです。


編集    削除
Mr.XRAY  2025-06-04 18:22:05  No: 152068  IP: [192.*.*.*]

ほとんど何をどうしたいのか理解ができていませんが,

> cellからbase64を読み込む→bitmapに戻す。

以下のようなことをしたいのでしょうか ?
説明には「変数として」とありますが,
変数として扱うのあれば TMemoryStream のままでいいような気がします.
( Objects プロパティ使用 ).


const 
  // 16x16, 16色のビットマップ画像の例
  Base64_Text : AnsiString = 
     'Qk32AAAAAAAAAHYAAAAoAAAAEAAAABAAAAABAAQAAAAAAIAAAAAAAAAAAAAAABAAAAAAAAAA////'
  + 'AN7e3gCzs7MA8rwAAD3I/wBwu9EAZ6zAABho4gAkEugAVlZOABQZSwAkMDMAHhcAAAAAAAD//wAA'
  + '////AF3ZVVW1VVVVXXfXW91VVVVddHvdPdVVVV10mZAz1VVVVdmZAAPVVVVV2ZAAANVVVVW5AAAA'
  + 'DVVVW9MAAAAA1VVd0zAAAiAdVVXdzAACIA1VVVVV0AIgCNVVVVVdAACI1VVVVVXbCIjVVVVVVVra'
  + 'iKVVVVVVVVbd1VVVVVVVVVVV';

//=============================================================================
//  リソースから読み出すのではなく
//  コード内にオブジェクトを文字列として埋め込んで使用する 1 つの例
//  あらかじめ EncodeBase64 関数で Base64 の文字列を作成して使用する
//
//  uses に   Soap.EncdDecd が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LMemStream : TMemoryStream;
  LByteArray : TBytes;
  LBitmap    : TBitmap;
begin
  LMemStream := TMemoryStream.Create;
  LBitmap    := TBitmap.Create;
  try
    LByteArray := DecodeBase64(Base64_Text);
    LMemStream.Write(LByteArray[0], Length(LByteArray));
    LMemStream.Position := 0;
    LBitmap.LoadFromStream(LMemStream);
    Image1.Canvas.StretchDraw(Image1.ClientRect, LBitmap);
  finally
    FreeAndNil(LMemStream);
    FreeAndNil(LBitmap);
  end;
end; 
  

編集    削除
Mr.XRAY  2025-06-04 18:38:11  No: 152069  IP: [192.*.*.*]

> 変数として扱うのあれば TMemoryStream のままでいいような気がします.
> ( Objects プロパティ使用 ).

TBitmap のままでもいいような気がします.
  

編集    削除
ごんすけ  2025-06-04 19:08:20  No: 152070  IP: [192.*.*.*]

XRAY様、こんばんはです。
オブジェクト用語でXRAY様に警告されるかと思ってドキドキしとりました(>_<)

>以下のようなことをしたいのでしょうか ?
>変数として扱うのあれば TMemoryStream のままでいいような気がします.

画像の件はできていますのでその部分ではなく、色々なコントロールでsavestreamし、違うコントロールでloadstreamして、必要な部分をチョイス利用できますよね。その際にメモリーストリームの状態では文字列として扱えないので、streamの情報を画像処理の様にエンコードし文字列化して利用や保存し、それをデコードしストリーム情報として利用したいのです。

Streamのバイナリ情報をINIやSGのCELLに文字として代入し、それを参照しStreamの値として利用したいのです。
たとえば、5種類のTreeviewの内容(savestreamで出したもの)を各CELLに文字列として代入し
各CELLの指定した物の内容を読み込むことによりTreeviewの表示を各STREAM LOADの形でコントロールに戻したいのです。

編集    削除
AAAAA  2025-06-04 19:12:38  No: 152071  IP: [192.*.*.*]

>memorystreamの情報→文字列化
>文字列→memorystreamという感じです。

>メモリーストリームの内容は対象にかかわらずに
>色々な環境でロードストリームできますよね?

>ということは、ストリーム自体に変動可能な情報を持っていてまるまる文字化すれば。
>そのまま復元できると踏んだ次第です。

l>istviewでも画像でもstringgridでもストリームに入れれば
>目的に合わせてストリームロードで代入できますよね?そこがコントロール依存の話しで進んでいるので色々な例が出てしまう感じになっています。

文字列化が必要な理由は?

>バイナリのままだと、BLOB以外のフィールドやINIやテキストファイルに直接入れられない事と他のデーターと結合する際に対象のフォーマットを>解析しバイナリベースでファイルに情報の追加が必要になる為になります。

これ???
Base64にしたテキストを編集するの???

編集    削除
ごんすけ  2025-06-04 19:26:26  No: 152072  IP: [192.*.*.*]

>文字列化が必要な理由は?

1つのファイルで管理するためになります。

listboxやSGをsavebynaryで書き出すと5つの内容を扱いたい時に5つのファイルができますよね?
Streamを文字列化できればINIやSGに文字列代入し、書き出しもテキストやバイナリ1本で設定から内容から管理できると思いました。

もちろん、内容復元ならループで回し、整形テキストで単行化し、それをループで回して各コントロールのメソッドで
状態の復元はできますが、savestreamしloadstreamすれば1ぱつで復元しささるという事は、streamを文字化できれば、整形処理をなくし1発で戻せ、複数ファイルの管理もINIやCELLで行いファイルの1本化が行えるのではと思った感じです。

編集    削除
ごんすけ  2025-06-04 19:33:39  No: 152073  IP: [192.*.*.*]

>Base64にしたテキストを編集するの???

編集という意味ではなく、バイナリを文字化しエンコード、デコードで復元できるという意味で使わせてもらいました。
なので、streamをエンコードし文字化し、デコードしstream化したいという感じで、画像かどうかの部分ではなく処理状態の意味合いで書いた感じです。

編集    削除
AAAAA  2025-06-04 19:53:58  No: 152074  IP: [192.*.*.*]

>listboxやSGをsavebynaryで書き出すと5つの内容を扱いたい時に5つのファイルができますよね?

ListBox が 5個あり 5個で ListBox1.Items.SaveToFile すると ファイルが5個できる って事かな?

これを1ファイルにしたいって事かな?



var
    FileStream: TFileStream;
begin
    ListBox1.Items.Add('AAA');
    ListBox2.Items.Add('BBB');
    FileStream := TFileStream.Create('A.TXT',fmCreate);
    ListBox1.Items.SaveToStream(FileStream);
    ListBox2.Items.SaveToStream(FileStream);
    FileStream.Free;

読み込むときに ListBox1 と ListBox2 の区別できるように しないとだめだけど

編集    削除
ごんすけ  2025-06-04 20:10:52  No: 152075  IP: [192.*.*.*]

やってみました。

その時に下記のような感じでだとTOBJECTはデータとして出ていないので復元されませんよね

procedure TForm1.Button18Click(Sender: TObject);
var
    FileStream: TFileStream;
begin
    Listbox1.items.addObject('aaa',TObject(1111));
    Listbox1.items.addObject('bbb',TObject(2222));
    ListBox2.Items.Add('BBB');
    FileStream := TFileStream.Create('d:\A.TXT',fmCreate);
    ListBox1.Items.SaveToStream(FileStream);
    ListBox2.Items.SaveToStream(FileStream);
    FileStream.Free;
end;

たぶん、memorystreamの中にはTOBJECTの内容も入っていると思うんですよ。

それを下記のXRAYさんのやつのように

  Base64_Text : AnsiString = 
     'Qk32AAAAAAAAAHYAAAAoAAAAEAAAABAAAAABAAQAAAAAAIAAAAAAAAAAAAAAABAAAAAAAAAA////'
  + 'AN7e3gCzs7MA8rwAAD3I/wBwu9EAZ6zAABho4gAkEugAVlZOABQZSwAkMDMAHhcAAAAAAAD//wAA'
  + '////AF3ZVVW1VVVVXXfXW91VVVVddHvdPdVVVV10mZAz1VVVVdmZAAPVVVVV2ZAAANVVVVW5AAAA'
  + 'DVVVW9MAAAAA1VVd0zAAAiAdVVXdzAACIA1VVVVV0AIgCNVVVVVdAACI1VVVVVXbCIjVVVVVVVra'
  + 'iKVVVVVVVVbd1VVVVVVVVVVV';

上記のような感じで、何が書いてあるかわからなくても&編集できなくてもいいので文字化し
streamとして書き戻したい感じなのです。

編集    削除
ごんすけ  2025-06-04 20:25:02  No: 152076  IP: [192.*.*.*]

その様な感じでTreeviewとかだと階層情報やcheckboxやicon情報や画像情報などもStreamに入って(いるであろう)物を
上記のような一括文字列に変換し、それをstreamに書きもとして、複数のtreeviewの内容を復元したい感じなのです。

編集    削除
ごんすけ  2025-06-04 20:27:18  No: 152077  IP: [192.*.*.*]

一括というのは全部まとめてではなく、各文字列化した場所から、文字列streamを指定して戻す感じを想定しています。

編集    削除
vram  2025-06-05 08:15:04  No: 152078  IP: [192.*.*.*]

> 1つのファイルで管理するためになります。
> listboxやSGをsavebynaryで書き出すと5つの内容を扱いたい時に5つのファイルができますよね?

1つのコンポーネントの要素を文字列化してすべてを書き出せば1つのファイルになります
ストリームにこだわっていますが、可変幅のデータをストリームで管理するのは
インデックスリストも必要なので余計に複雑です
ここでは文字列で説明します

ListBox1とStringGrid1があったとしてWriteComponentとそのテキスト化を行えば

ListBox1=(ListBox1を構成する要素)・・・ #$0d#$0a
StringGrid1=(StringGrid1を構成する要素)・・・#$0d#$0a

こうできますよね?
画像をどう管理しているのかこれまでの情報にない気がするので想像ですが
イメージリストを使っているならそれを文字列化とその文字列から復元する処理を書けばいけるでしょ

画像データをbase64などで文字列化してそれを1行で管理する理論
イメージリストの要素数分こうなる
Base64='Qk01AAAAAAAAAHYAAAAo・・・
Base64='Qk02AAAAAAAAAHYAAAAo・・・

これを1つの文字列にしてしまう
str=Base64='Qk01AAAAAAAAAHYAAAAo・・・,Base64='Qk02AAAAAAAAAHYAAAAo・・・
文字列中の' や = や , は別の文字にエンコードデコードする

この文字列をさきほどのようにコンポーネントごとまとめて管理
ListBox1=(ListBox1を構成する要素)・・・ #$0d#$0a
StringGrid1=(StringGrid1を構成する要素)・・・#$0d#$0a
ImageList1=Base64='Qk01AAAAAAAAAHYAAAAo・・・,Base64='Qk02AAAAAAAAAHYAAAAo・・・#$0d#$0a

1ファイルで1コンポーネント1行の iniファイルが出来上がり

それかTSrtingListを少し改造すれば
[ListBox1]
ListBox1を構成する要素の文字列リスト)


[StringGrid1]
(StringGrid1を構成する文字列リスト)


[ImageList1]
Base64='Qk01AAAAAAAAAHYAAAAo・・・
Base64='Qk02AAAAAAAAAHYAAAAo・・・



こうすれば1ファイルになる

編集    削除
オごんすけ  2025-06-05 13:35:03  No: 152079  IP: [192.*.*.*]

>ListBox1とStringGrid1があったとしてWriteComponentとそのテキスト化を行えば

そこがなかなかいい感じなのですが    Listbox1.items.addObject('aaa',TObject(1111)); の様にした場合に
TObject(1111)の部分が文字列化しささらない or 参照の仕方が判っておらず、TOBJECTにしてもTreeviewのchildノードにしてもルートの値だけで参照&復元できない状態なのです。

readComponentでchildノードも復元できるという事は元のデーターには情報があるので
何か参照の仕方があるとは思うんですが、そこが把握していない感じになります。

>ストリームにこだわっていますが

自身の環境だと大半のコントロールへの代入手段がloadfileかloadstreamしかないことが多いので
例えば下記のようなものを抜き出してBMP代入する場合にストリームでないと、DLLから抜き出してsaveファイルloadファイルをコントロールだと何千個もファイル生成を繰り返す感じになり高負荷になってしまいます。

https://imgur.com/hpIQctH

>画像をどう管理しているのかこれまでの情報にない気がするので想像ですが

どのように管理されているかはストリーム経由でしかやりとりがないので
まったく把握はしていないんですが、下記の様にストリームに入っているBMPを投げるとSGに代入されます。
なので、そこを把握せずに行うためにStreamを文字列化し復元したい感じになります。

AdvStringGrid1.CreateBitmap(1,AdvStringGrid1.RowCount-1,true,haCenter, vaAboveText).LoadFromStream(stream);

これを
AdvStringGrid1.savestream(sm);
AdvStringGrid1.loadstream(sm);

すると同じものが複製されます。

なので、可変幅のデータをストリームで管理するのはインデックスリストも必要なので余計に複雑です。というのは
ストリームに複数画像を入れた場合の区切りの開始と終了地点の意味ですよね?

ですがPictureContainer等だと、ストリームに入れ、ストリームから読みそのまま利用できるので
そこの部分は、利用側ではなくコントロールの作成側で全部済ませてくれていると思うんですよ。

imagelistも自動で処理される感じになっていると思っているんですが、そのようなブラックボックス的な
部分もstreamを仲介すれば定義なしで使えるのではと思い、注視している感じになります。

編集    削除
vram  2025-06-05 15:59:28  No: 152080  IP: [192.*.*.*]

やりたいことがせっかくわかり書けていたのにまたわからなくなってきました

サンプル画像がTStringGridということですが
そのTStringGridにImagesはありますが これを保存復元したいということでしょうか?

それなら普通は画像のキャッシュファイルを作るだけです

> コントロールの作成側で全部済ませてくれていると思うんですよ
Delphiの内部構造の知識はある方ですけど たぶんコントロール側ではやってくれないと思います
DelphiIDEはリソース形式で読込、保存できるのでそれをユーザーがマネできるかというとうまく動きません

編集    削除
ごんすけ  2025-06-05 17:31:26  No: 152081  IP: [192.*.*.*]

>サンプル画像がTStringGridということですが

サンプル画像はDELPHIのIDEのプロパティー一覧になります。

IDEのiconを取得するには、DLL等をループで処理しますよね。

それをSGに代入しようと思ったときにIOでの読み込み手段がStreamとFILEの2つしかない場合、streamならメモリ処理できますが使わない場合は全部ファイルとして書き出しファイルとして読み込む処理が必要になるのでそのオーバーヘッドをなくすためにStreamを使いたいという感じです。

>Delphiの内部構造の知識はある方ですけど たぶんコントロール側ではやってくれないと思います

重大な思い違いが発覚しました!!

streamやWriteComponentに全情報が入っていると思っていましたが
入っていませんでした(たぶん)

これがvram さんの言っていたインデックス管理の難しさとか言う部分かもしれません。

もしかして、streamもwritecomponentsも復元できるだけの情報を保持していないのですか?

stringlistで試してみたら、streamにはいっているであろうOBJECTが参照できませんでした。
// ------------------------------------
procedure TForm1.Button21Click(Sender: TObject);
var
sl1,sl2:TStringList;
ms:TMemoryStream;
i:integer;
begin
ms:= TMemoryStream.Create;
sl1:=TStringList.create;
sl1.AddObject('ddd', TObject(11));
sl1.AddObject('aaaccc', TObject(22));
sl1.SaveToStream(ms);
sl1.Free;

sl2:=TStringList.create;
ms.position:=0;
sl2.LoadFromStream(ms);
   for i := 0 to sl2.count -1 do
    begin
//Memo1.lines.add(sl2[i]);
Memo1.lines.add(inttostr(integer(sl2.Objects[i])));
    end;
sl2.Free;
end;
// ------------------------------------

という事は、memorystreamって何?ってところから調べ始める必要が出てきました。

そもそもがms.position:=0;のポジションて、いつも0にしているけど、内容の区別処理に使うものだと思いますが
何で定義して、何のためにあるの?な基本を調べる事から始める感じになりそうです(>_<)

編集    削除
AAAAAA  2025-06-05 18:58:12  No: 152082  IP: [192.*.*.*]

IDEのForm 上で【エディタで表示】で表示されるやつ
それ以外の保存は自力

編集    削除
AAAAA  2025-06-05 19:00:32  No: 152083  IP: [192.*.*.*]

AdvStringGrid1.savestream(sm);

savestream のなかで自力で Stream に吐き出しているの
 

編集    削除
ごんすけ  2025-06-05 21:01:50  No: 152084  IP: [192.*.*.*]

なんてことでしょう。

AAAAAさんに説明するのに試してみたら、やはりストーリームで管理されているっぽいです。

stringlistで出来なかったのはvram さんのいうようにstringlistで管理する部分が無かっただけで
streamに入れるように作ってあれば使えるのかもしれない気がしてきました。

>それ以外の保存は自力

そこをDLLから自力で処理する際にファイル生成をせずにstreamで行えば
はやくなりそうですね。という感じでした。

>savestream のなかで自力で Stream に吐き出しているの

自力というか他力というか、下記の様にstreamにsaveしloadするだけで
子ノードも画像もイメージコントロールも復元できてしまうのです。

https://imgur.com/LlzEuIN

procedure TForm1.Button22Click(Sender: TObject);
var
ms1,ms2:TMemoryStream;
Bmp :TBitmap;
begin
// ----- SG1の生成処理 -----
AdvStringGrid1.RemoveRows(0,AdvStringGrid1.RowCount);
AdvStringGrid2.RemoveRows(0,AdvStringGrid2.RowCount);
advstringgrid1.RowCount:=4;
advstringgrid2.RowCount:=4;

ms1 := TMemoryStream.Create;
ImageEnView1.IO.LoadFromFile('C:\555\torisu.bmp');
ImageEnView1.IO.SaveToStreamBMP(ms1);
ms1.Position := 0;

advstringgrid1.Rows[0].DelimitedText:='aa,bb,cc,dd';
advstringgrid1.Rows[1].DelimitedText:='あ,bb,う,え';
advstringgrid1.Rows[2].DelimitedText:='11,22,33,44';

Bmp := TBitmap.Create;
bmp.LoadFromStream(ms1);
Bmp.Width := 60;
Bmp.Height :=60;

AdvStringGrid1.AddBitmap(0,2,bmp,True,haCenter,vaCenter);
advstringgrid1.AddRadioButton(4,3,true);
AdvStringGrid1.AddRating(3,2,3,clLime,clSilver);
advstringgrid1.AddCheckBox(2,2,true,false);
advstringgrid1.group(1);

// ----- SG2に複写処理 -----
ms2 := TMemoryStream.Create;
advstringgrid1.SaveToBinStream(ms2);
//advstringgrid1.SaveToStream(ms2);

ms2.Position := 0;

advstringgrid2.LoadFromBinStream(ms2);
//advstringgrid2.LoadFromStream(ms2);

//image 2に複写処理
ms1.Position := 0;
ImageEnView2.IO.LoadFromStreamBMP(ms1);
end;

編集    削除
ごんすけ  2025-06-05 21:03:44  No: 152085  IP: [192.*.*.*]

なのでstreamの情報が文字にできstreamに戻せれば
その文字を3つ用意すれば用途に合わせてSGの内容生成ができるはず(出来てほしい)のです。

編集    削除
ごんすけ  2025-06-05 21:07:28  No: 152086  IP: [192.*.*.*]

グループ無し
https://imgur.com/HhC3nXV

編集    削除
AAAAA  2025-06-05 21:37:34  No: 152087  IP: [192.*.*.*]

>savestream のなかで自力で Stream に吐き出しているの
というのは

procedure TAdvStringGrid.SaveToStream(Stream: TSttraam)
var
     C,R: Integer;
     Cell: TCellItem;
begin
    for C:=0 to ColCount -1 do 
    begin
      for R:=0 to RowCount -1 do
      begin
        Cell := Cells[C,R];
        Stream.WriteComponent(Cell); 
      end;  
    end; 
end;

こんなことやってるの

編集    削除
ごんすけ  2025-06-05 22:10:09  No: 152088  IP: [192.*.*.*]

>こんなことやってるの

おっ。新情報や。
Streamって、そのような感じでしょりしているんですね。

stringgridのはコントロールのデフォルトのstreamメソッドなので何をしているか把握していませんが
素のstreamのメソッド見ても、元データーをどうさばいてるのか把握できなさそうなので
活用できなさそうです。

イメージコントロールもSGコントロールも入れて呼べば利用できることが多い感じです。

編集    削除
ごんすけ  2025-06-05 22:11:50  No: 152089  IP: [192.*.*.*]

自前で文字列化するときは上記の様にループで区切り文字を生成し、分離しループで通常のメソッドで追加してました。

編集    削除
vram  2025-06-06 08:16:31  No: 152090  IP: [192.*.*.*]

話が全然通じないと思って用語を調べたら
TMSソフトが作っている TAdvStringGridコンポーネントの話ですよね?

さすがに自分の環境や説明に使ってるもののことぐらい最低限書いてください

そのコンポーネントが LoadFromBinStream でストリームに書きだしているのでそれを文字列化したいということ?

そもそもLoadFromBinStreamがTAdvStringGridコンポーネント独自の処理なのでどんな内容をやりとりしているのか知らない
それが独自規格ならここで聞いても誰もわからない、作ったメーカーに問い合わせてください

もしそのメソッドがDelphiのとある仕様通りに出力しているのであれば

自分がサンプルを書いた
function TForm1.BinToTextOut(m: TMemoryStream): string;
で文字列に出来ます

同じくサンプルに書いた
  e := TMemoryStream.Create;
  try
    e.WriteComponent(advstringgrid1);   // ストリームにクラスの要素(実行時型情報)が送られる(バイナリ)
    s := BinToTextOut(e);               // ストリームに書き込まれた値(実行時型情報)をテキスト化(シリアライズ)
  finally
    e.Free;
  end;

これで文字列に出来るなら対応しているしエラーが出るなら対応していない

対応しているかどうかの目安は
DelphiのIDEで出来る操作かどうかということ
IDEでの操作はresファイルに保存されるのでやりとりの仕組みが用意されている
そうではなくて実行時に手動で操作する必要がある場合は resファイルに保存する仕組みが無いので
自動で保存復元する機能はない

編集    削除
ごんすけ  2025-06-06 08:58:17  No: 152091  IP: [192.*.*.*]

>さすがに自分の環境や説明に使ってるもののことぐらい最低限書いてください

質問の趣旨がコントロールをどうにかしたいという意味合いではなく
streamの内容を文字化したいということで、そこは環境に依存しないと思った次第です。
(コントロールがどうではなく、BMPでもなんでも対象なので、環境を限定すると、また都度例が違うという流れになる&環境固定の回答になると思い省略しました。)

>それが独自規格ならここで聞いても誰もわからない、作ったメーカーに問い合わせてください

LoadFromBinStreamかどうかにかかわらず入れている場所はtmemorystreamなので、その規格が判らなくても処理できるようにmemorystreamを文字化したいという趣旨になります。base64が画像フォーマットにかかわらず&画像を作るコントロールに限らずに文字列化でき復元できますよね?そのようにSTREAMのバイナリ情報を文字化し同じものを復元したいのです。

>もしそのメソッドがDelphiのとある仕様通りに出力しているのであれば自分がサンプルを書いた

プロパティー等の型情報は出るんですが項目内容等が出ません。

ですがstreamのloadで複写できるという事はstream自体には入っているという事になります。
その内容の参照部が無いのでコントロール自体のプロパティーは復元できても同じものとして復元できない感じになっています。

編集    削除
ごんすけ  2025-06-06 09:10:25  No: 152092  IP: [192.*.*.*]

BinToTextOut。これいいですね。DFMと同じ情報が参照できるんですね。
今まではDFMファイルを読み込んでマッチングさせプロパティーを参照していましたが
これにコントロールネームをstringで与えられたら、色々使えそうなので、あとで色々いじってみたいと思います。

編集    削除
vram  2025-06-06 09:42:50  No: 152093  IP: [192.*.*.*]

さきほども伝えましたがTAdvStringGridのLoadFromBinStream は独自の仕様です
なので質問されてもわかりません

TStringGridをStreamに書き込む、読み込む機能はDelphiの仕様です。
なので質問には答えられます

TStringGridやTListBoxを例に出されていたのでみなさんそれに合わせて回答しています
でも実際にやりたいのはTAdvStringGridのLoadFromBinStream で出力された形式だ
と言われたら今まで回答したことは何だったんだ?という話になります

質問のタイトルに戻ってTMemoryStreamをBase64を使って文字列にする
またその逆はこんな感じでは?

コンパイラはDelphi10を想定

uses
  Classes, SysUtils, EncdDecd;

function MemoryStreamToBase64(MS: TMemoryStream): string;
var
  Bytes: TBytes;
begin
  SetLength(Bytes, MS.Size);
  MS.Position := 0;
  MS.ReadBuffer(Bytes[0], MS.Size);
  Result := EncodeBase64(Bytes, Length(Bytes));
end;

procedure Base64ToMemoryStream(const Base64Str: string; MS: TMemoryStream);
var
  Bytes: TBytes;
  Len: Integer;
begin
  Bytes := DecodeBase64(Base64Str);
  MS.Clear;
  Len := Length(Bytes);
  if Len > 0 then
    MS.WriteBuffer(Bytes[0], Len);
  MS.Position := 0;
end;

編集    削除
AAAAA  2025-06-06 17:47:43  No: 152094  IP: [192.*.*.*]

TAdvStringGrid も例の1つだと思う・・・・

編集    削除