DELLに実行ファイルからテキストを送るには

解決


Delphi大好き  2010-10-30 21:18:03  No: 39450

DELLファイルの勉強を始めました。
DELLと実行ファイルの間でテキストメッセージのやり取りをしたいと考えています。

最終的にはアプリからメッセージをDELLに送り、
DELLに音声をしゃべらせます。
そしてDELLがInteger型の数値を貰うと、
中断したり再開したりするようにしたいと考えています。

DELLの簡単なサンプルは作れたのですが、
手持ちの参考書にはメッセージのやりとりのサンプルがなく、
質問させていただいた次第です。

どうかお知恵の拝借よろしくお願いします。m(__)m


Quest  2010-10-30 21:42:20  No: 39451

DELLって、もしかしてDLLの事でしょうか?
であれば、DLLに対してWindowメッセージを送ることもできるでしょうけど
このような場合であれば、それぞれの関数を用意すればいいんじゃないでしょうか。
例えば、しゃべらせるには"TalkText(const Message: string)"関数とか
中断するには"Pause(const Flag: integer)"で、Flagが0なら中断、1なら再開…みたいに。


Delphi大好き  2010-11-02 08:28:21  No: 39452

Questさんありがとうございます、
返信遅れました。
そうですDLLです。
間違えました^^;。

色々やってテキストのやりとりまでは成功しました。
が、コンポーネントの貼り付け(生成?)が判りません。
アクティブXコンポーネントを使ってDLLにしゃべらせたいと思ってます。

DLL側ソース
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
type
  TForm1 = class(TForm)

end;

function speak (s:string): string; stdcall;
begin
  Result := s;
end;

exports
  speak;
end.

実行側ソース

function speak (s:string): string;
  stdcall; external 'dll.dll';

procedure TForm1.Button1Click(Sender: TObject);
begin
  edit1.Text := speak('aiueo');
end;


Quest  2010-11-02 11:16:46  No: 39453

DLL内部でやった事はありませんが、通常のEXEの中では動的に
ActiveXコンポを生成して使用したことはありますので
同じようにすれば良いのではないでしょうか?
ちょっと乱暴ですが
function speak (s:string): string; stdcall;
var
  ActiveX: THogeHoge;
begin
  ActiveX := THogeHoge.Create(nil);
  try
    ActiveX.Speak(s);
  finally
    ActiveX.Free;
  end;
  Result := s;
end;
こんな感じでしょうか。
ActiveX: THogeHoge; <=この部分は、通常のフォームにActiveXを
貼り付けたときに自動的に宣言されるものをコピペします。


Delphi大好き  2010-11-08 08:17:14  No: 39454

返信遅れました。
参考書を頼りに以下まで何とか出来ました。
実行すると何とかしゃべるのですが、
その後でエラーが出ます。

「一度に多数の例外が発生しました。
'access vioation at 0x00151316a: read of address 0x01ad3fa4'」

となります。

以下がソースです。
よろしくお願いします。

DLL2.PDR
uses
  SysUtils,
  Classes,
  Unit3 in 'Unit3.pas' {Form1};

{$R *.res}

exports
  ShowForm  index 1,
  speak     index 2;
begin
end.

DLLフォーム部
unit Unit3;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, OleCtrls, HTTSLib_TLB, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    TextToSpeech1: TTextToSpeech;
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

procedure ShowForm; export; stdcall;
function speak (s:string): string; stdcall;
var
  Form1: TForm1;
  s1:string;
implementation

{$R *.dfm}

procedure ShowForm;
begin
  try
    Form1 := TForm1.Create(Application);
    Form1.Show;
  except

  end;
end;

function speak (s:string): string; stdcall;
begin
  s1 := s;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  form1.TextToSpeech1.Speak(s1);
  timer1.Enabled := false;
end;

DLL呼び出し部
unit Unit4;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

procedure ShowForm; stdcall;
function speak (s:string): string; stdcall;

const
  Dll2 = 'Dll2.Dll';

procedure ShowForm; external Dll2 index 1;
function speak; external Dll2 index 2;
type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  try
    speak(edit1.Text);
    ShowForm;    
  except

  end;
end;

end.


tor  2010-11-09 00:40:52  No: 39455

exeとDLLの間で長い文字列(String型)を受け渡しする場合は
双方でusesの先頭に'ShareMem'を入れておく必要があります。
(さらにBORLANDMM.DLLを一緒に配布しなくてはなりません)
詳しくはヘルプで「共有メモリマネージャ」を調べてください。

短い文字列かPCharを使えば、そのような制限はありません。
(ただし、PCharを返すような場合は自分で適切にメモリ管理を行なう必要があります)
function speak(s: PChar): PChar; stdcall;
...
speak(PChar(edit1.Text));

提示されたコードだとspeakは何も返していないようなので、procedureでいいのではないかと思いますが……


Delphi大好き  2010-11-09 05:03:01  No: 39456

Questさん  torさん  ありがとうございます。

ShareMemを追加ても、エラーが出ました。
「無効なポイント操作」
「Runtime error 217 at 00413710」
と出て、次に
「一度に多数の例外が発生しました。
'access vioation at 0x00151316a: read of address 0x01ad3fa4'」

となります。

speakですが、参考書を頼りに継ぎ接ぎで
自分でもよく判らず組んでるので、こんがらがってる次第です^^;

エラーは、フォームの呼び出しで起きてるみたいです。
スピーチアクティブXコンポーネントと、
テキストのやりとりをしなくても、
フォームを閉じた時に上記のエラーが出ました。

何が原因なのでしょうか??
ご指導よろしくお願いしますm(__)”m。


monaa  2010-11-09 12:05:40  No: 39457

フォーム付きDLLを作ったことが無かったので少し試してみました。
ぱっと試した感じでは、DLL側のフォームは明示的にFreeしてやる必要があるのでは?
exe側をCloseする時にDLL内の
procedure CloseForm;
begin
  Form3.Free;
end;
みたいなの書いて実行させてやってみてください。


Delphi大好き  2010-11-10 06:10:47  No: 39458

monaaさんありがとうございます。
Form.Freeを入れても同じでした。
TextToSpeech1.Freeを入れたところ、発音直後のエラーは消えました。
しかし、プログラム終了時にエラーが発生します。

色々試したら、エラーの元が特定出来ました!

function speak (s:string): string; stdcall;
begin
  s1 := s;
end;

このfunctionを実行してテキストのやりとりをすると、エラーになるようです。

試しにS1を'aaa'に変えたところ、「無効なポインタ操作」エラーも出ず、問題なくしゃべり、エラーも起きませんでした。

このfunctionを使わずに文字列のやりとりをする方法、
又はエラーをなくす方法はないでしょうか??
よろしくお願いしますm(__)”m。


tor  2010-11-10 06:57:31  No: 39459

まさに長い文字列の受け渡しによる問題に見えますが
先に提案したPCharを使う方法は試してみましたか?

> ShareMemを追加ても、エラーが出ました。
ShareMemを使う場合「DLLとそれを使うアプリケーションの両方で」「usesの一番最初に」指定しなくてはいけないとありますが
実際のコードはその通りになっていたでしょうか。


Delphi大好き  2010-11-10 08:24:00  No: 39460

torさんありがとうございます、無事解決しました。

ソースがこんがらがったのでサンプルを直接いじって色々試行錯誤してました^^;。
ShareMemは・・・、忘れてました^^;

でもShareMemを先頭に追加しても、こちらではエラーなりました。
「無効なポインタ操作」となります。
環境は、Vista、Delphi7Pro、です。

Pcherで受け渡しする方法で無事解決しました。
改めてありがとうございますm(__)m。

以下がソースです。

DLLソース:
library DllForm;

{ DLL メモリ マネージャ について:
  もしこの DLL が引数や返り値として String 型を使う関数や手続きを
  エクスポートする場合、以下の USES 節とこの DLL を使うプロジェクト ソースの
  USES 節の両方に、最初に現れるユニットとして ShareMem を指定しなけれ
  ばなりません。

  これは構造体やクラスに埋め込まれている場合も含め String 型を DLL
  とやり取りする場合に必ず必要となります。

  ShareMem は共用メモリマネージャである DELPHIMM.DLL とのインターフェース
  です。あなたの DLL と一緒に配布する必要があります。

  DELPHIMM.DLL を使うのを避けるには、PChar または ShortString 型を
  使って文字列のやり取りをおこなってください。}

uses
  //ShareMem,
  SysUtils,
  Classes,
  Dialogs,
  DLLMAIN in 'DLLMAIN.PAS' {SubForm};

exports                                     // エクスポート関数の定義
  ShowForm  index 1,                        // ShowModalメソッドでの表示
  speak     index 2;
begin
end.

DLLフォーム部:
unit DllMain;

interface

uses
  {ShareMem, }Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, OleCtrls, HTTSLib_TLB, ExtCtrls;

type
  TSubForm = class(TForm)
    Button1: TButton;
    TextToSpeech1: TTextToSpeech;
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
  public
    { Public 宣言 }
  end;

procedure ShowForm; export; stdcall;        // モード付きフォームの表示
function speak (s:string): string; stdcall;

var
  SubForm: TSubForm;
  s1:string;
implementation

{$R *.DFM}

{ フォームの表示処理 }
procedure ShowForm;
begin
   try
    Application.CreateForm(TSubForm, SubForm);  // フォームの作成
    SubForm.ShowModal;                          // モード付き表示
  finally
    SubForm.Free;                               // メモリなどの解放
  end;
end;

function speak (s:string): string; stdcall;
begin
  s1 := PChar(s);
end;

procedure TSubForm.Timer1Timer(Sender: TObject);
begin
  TextToSpeech1.Speak(s1);
  timer1.Enabled := false;
end;

procedure TSubForm.Button1Click(Sender: TObject);
begin
 TextToSpeech1.Free;
 close;
end;

end.

DLL呼び出し側プロジェクトファイル
unit DllInt;

interface

procedure ShowForm; stdcall;
function speak (s:string): string; stdcall;

implementation

const
  DllForm = 'DllForm.Dll';

procedure ShowForm;        external DllForm index 1;
function speak (s:string): string; external DllForm index 2;

end.

DLL呼び出し側フォームソースファイル
unit Main;

interface

uses
  {ShareMem, }Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls,
  DllInt;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
  speak(PChar(edit1.Text));
  ShowForm;
end;

end.


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加