ClientDataSet1変数内に新しいインスタンスを作成していますが、フォーム上の他のコンポーネントはどれもそれを参照していません。
これは、「データプロバイダーまたはデータパッケージがありません」というエラーメッセージの原因ではありません。それを見つけるには、再現可能なケースを投稿する必要があります。
その再現可能なケースを実行する最も簡単な方法は、次のとおりです。
- サーバーに2つのTClientDataSetを配置します(ClientDataSet1とClientDataSet2)
- プロバイダーなどを使用して、設計時にそのClientDataSet1にデータをロードします。
- そのデータをClientDataSet1から.CDSファイルに保存します
- 設計時に.CDSファイルをClientDataSet2にロードします
- ClientDataSet2をクライアントに送信します
それでも再現される場合は、その.pas/.dfmをどこかに投稿してください。再現されない場合は、再現されるまで部分を切り取り始めます。
幸運を!
--jeroen
編集:私はあなたのソースをダウンロードし、Delphi2009で動作させました。
いくつかの問題が含まれており、Synapseライブラリにも問題が含まれています。
あなたの問題の大部分は正確ではないことに帰着します。ソースコードのフォーマット、使用している命名規則の組み合わせ、割り当てられたオブジェクトの解放の欠如に基づいて、私はすでにそれについて漠然とした感覚を持っていました。
忘れる前に:私が最初にしたことは、debug dcusの使用を有効にし、コンパイラオプションの最適化を無効にすることでした。これらは、問題をより深く掘り下げるための非常に貴重な設定です。できるだけ近くにしたかったので、既存のコードにクリーンアップを追加しませんでした。しかし、少なくともthenブロックが別々の行にあるように、ソースコードフォーマッターを実行しました。
送信コードから始めましょう。
データベース接続を削除し、ClientDataSet1をクライアントからサーバーにコピーし、 SServerユニットをリファクタリングして、 TTCPSocketDaemonとTTCPSocketThrdの両方がFClientDataSetにアクセスできるようにしました。TClientDataSet
それは私が以前に求めた一種の再現可能なケースを私に与えました。
次に、クライアントの実行を開始しました。LogThis(strmReply.Size);が表示されました。0(ゼロ!)を出力していたので、何も受信していませんでした。
それで私はサーバーをもう一度調べました。区切りテキストを使用して着信引数を分割しているようですが、その後は、ifsl[0]ではなくsl.Names[0 ]を参照していました。これはDelphi2009で失敗し、おそらく他のDelphiバージョンでも失敗します。それに加えて、空のsをチェックしていなかったため、タイムアウトするたびにリストインデックスが範囲外で失敗していました。
次に、デバッグコードを追加しました。実際に送信されているものを確認したかったのです。これにより、S_CLIENT.SaveToStream(strm、dfXMLUTF8);を使用して、バイナリではなくXMLを使用してストリーミングしました。バッファをローカルのファイルにコピーして、そのファイルを監視できるようにします。ファイルはOKでした:ClientDataSetの有効なXML表現であるように見えました。
最後に、サーバーでSendBufferを使用し、クライアントでRecvStreamを使用していることに気付きました。それらは一致しません。選択する必要があるため、接続の両側でバッファーまたはストリームを使用してください。私はストリームを選びました。
したがって、送信コードは次のようになります。
procedure TTCPSocketThrd.Execute;
var
s: string;
strm: TMemoryStream;
ADO_QUERY: TADOQuery;
DS_PROV: TDataSetProvider;
DS_CLIENT: TClientDataSet;
sl: TStringList;
adoconnstr: string;
CDSStream: TFileStream;
begin
CoInitialize(nil);
Sock := TTCPBlockSocket.Create;
adoconnstr := 'POST YOUR CONNECTION STRING HERE, IF TOO LONG adoconnstr := adoconnstr + ''nwestring''';
try
Sock.Socket := CSock;
sl := TStringList.Create;
Sock.GetSins;
with Sock do
begin
repeat
if terminated then
break;
s := RecvTerminated(60000, '|');
if s = '' then
Exit;
//Den Text mit Leerzeichen splitten zur besseren Verarbeitung
sl.Delimiter := ' ';
sl.DelimitedText := s;
LogThis(sl.Names[0]);
if sl[0] = 'getBENUds' then // jpl: not sl.Names[0] !!!!
begin
// //ini ADO_QUERY
// ADO_QUERY := TADOQuery.Create(Form1);
// ADO_QUERY.ConnectionString := adoconnstr;
// ADO_QUERY.SQL.Clear;
// ADO_QUERY.SQL.Add('SELECT * FROM BENU');
// ADO_QUERY.Open;
// LogThis('ADO_QUERY fertig');
// //ini DS_PROV
// DS_PROV := TDataSetProvider.Create(ADO_QUERY);
// DS_PROV.DataSet := ADO_QUERY;
// LogThis('DS_DSPROV fertig');
// //ini DS_CLIENT
// DS_CLIENT := TClientDataSet.Create(ADO_QUERY);
// DS_CLIENT.ProviderName := 'DS_PROV';
// DS_CLIENT.SetProvider(DS_PROV);
DS_CLIENT := FClientDataSet;
LogThis('DS_CLIENT fertig');
//DSCLIENTDATASET bauen
strm := TMemoryStream.Create;
DS_CLIENT.Open;
//DS_CLIENT.Open;
DS_CLIENT.SaveToStream(strm, dfXMLUTF8); // jpl: easier debugging than binary
strm.Seek(0, soBeginning);
SendStream(strm); // jpl: not SendBuffer(strm.Memory, strm.Size); !!!
strm.Seek(0, soBeginning);
CDSStream := TFileStream.Create('Server.cds', fmCreate);
CDSStream.CopyFrom(strm, strm.Size);
CDSStream.Free();
LogThis('gesendet');
end
else if sl[0] = 'validuser' then // jpl: not sl.Names[0] !!!!
begin
//do stuff
end;
if lastError <> 0 then
break;
until false;
Form1.Memo1.Lines.Add('down');
end;
finally
Sock.Free;
end;
end;
そこで、受信コードを再検討しました。すでにstrmReply.Sizeをログに記録しているので、0(ゼロ)の特殊なケースに反応しなかったのはなぜだろうと思ったので、次のように追加しました。LogThis('空のストリームを受信しました')
次に、デバッグを開始しましたが、strmReply.Sizeが毎回0(ゼロ)のままであることがわかりました。そこで、最近使用しているSynapseコード( Habariコンポーネントに含まれているすでに持っているコピーを取得しました)を掘り下げ始めました。そこで私は2つのことを発見しました:
- ストリームを送信すると、最初の4バイトでストリームの長さをエンコードしていました
- エンコードは、デコードとは異なるバイト順序で実行されました
これは間違いなくSynapseのバグですので、遠慮なく報告してください。その結果、RecvStreamはSendStreamに対応しませんが、 RecvStreamIndyは対応します。
これらすべての変更の後、それはまだ機能しませんでした。その理由は、ClientDataSet1.LoadFromStream(strmReply); はストリーム内の現在の位置から読み取りを開始しています(自分で確認できます。コアの読み込みロジックはTCustomClientDataSet.ReadDataPacketにあります)。したがって、strmReply.Seek(0、soBeginning);を追加するのを忘れたようです。、そしてそれを追加した後、それは突然機能しました。
したがって、受信コードは次のとおりです。
procedure TForm1.btnConnectClick(Sender: TObject);
var
CDSStream: TFileStream;
begin
CSocket := TTCPBlockSocket.Create;
strmReply := TMemoryStream.Create;
ClientDataSet1 := TClientDataSet.Create(Form1);
try
// CSocket.Connect('10.100.105.174', '12345');
CSocket.Connect('127.0.0.1', '12345');
if CSocket.LastError = 0 then
begin
LogThis('verbunden');
CSocket.SendString('getBENUds|');
LogThis('''getBENUds|'' gesendet');
if CSocket.LastError = 0 then
begin
CSocket.RecvStreamIndy(strmReply, 50000); // jpl: this fails, because SendStream defaults to Indy: CSocket.RecvStream(strmReply, 50000);
LogThis(strmReply.Size);
if strmReply.Size = 0 then
LogThis('empty stream received')
else
begin
strmReply.Seek(0, soBeginning);
CDSStream := TFileStream.Create('Client.cds', fmCreate);
CDSStream.CopyFrom(strmReply, strmReply.Size);
CDSStream.Free();
strmReply.Seek(0, soBeginning); // jpl: always make sure that the stream position is right!
ClientDataSet1.LoadFromStream(strmReply);
ClientDataSet1.Open;
end;
end;
end;
except
on E: Exception do
ShowMessage(E.Message);
end;
CSocket.Free;
end;
結局、あなたの問題を解決することは難しくありませんでした。最も難しかったのはRecvStreamIndyについて知ることでした。残りはコードの単なるクリーンアップであり、いつ何が起こるかを正確に把握することでした。これにはほとんどの時間がかかりました。この場合、ストリーム処理では、ストリームの位置を注意深く監視する必要があります。
よろしく、
--jeroen