Programming Forums
User Name Password Register
 

RSS Feed
FORUM INDEX | TODAY'S POSTS | UNANSWERED THREADS | ADVANCED SEARCH

Reply
 
Thread Tools Display Modes
Old Mar 2nd, 2007, 5:05 AM   #1
sggaunt
Newbie
 
Join Date: Mar 2007
Posts: 9
Rep Power: 0 sggaunt is on a distinguished road
Componets in a dll

The following code is an attempt to put the Delphi7 demo application Delphi7/demos/Internet/netchat/netchat.dpr
into a dll

I have used the dll wizard to set up the library code and then added a data unit to it.
The data unit includes the
TTcpServer and TTcpClient components from the Internet tab.
any attempt to access the Client or Server componets via the dll just causes a hang.

I also tried it with fastmem4 removed and tried starting a timer on the datamodule all with the same result.


As you can see I have attempted to include fastmm4 (correctly?)
and the string list which is meant to replace the memo in the demo seems to work OK (i.e I can read and write to it via

the dll).



CODE
library Ethernet;
{ To avoid using sharemempass string information using PChar or ShortString parameters. }
uses
FastMM4 in 'FastMM4.pas',
SysUtils,
Classes,
dataUnit in 'dataUnit.pas' {DataModule1: TDataModule};

{$R *.res}

function Initalise: integer; stdcall;
begin
result := 0;
MemRecv := TStringList.Create; // create a string list to hold recived messages
memrecv.Add('Test line');
end;

function StopAll: integer; stdcall;
begin
result := 0;
try
Freeandnil(Memrecv);
Datamodule1.Server.Active := false;
datamodule1.Client.Active := false;
except
result := 1;
end;
end;

function ActivateServer(LocalP: shortstring;
remoteH: shortstring;
RemoteP: shortstring): integer; stdcall;
begin
result := 0;
with DataModule1 do
try
// any one of these calls cause the app to hang!!
Client.RemoteHost := RemoteH;
Client.RemotePort := RemoteP;
Server.LocalPort := LocalP;
Server.Active := True;
except
result := 1;
end;
end;

// read the first line out of the stringlist and delete it!!
// this works as expeceted.

function ReadLine: shortstring; stdcall;
begin
if MemRecv.Count > 0 then
begin
result := MemRecv.Strings[0];
Memrecv.Delete(0);
end
else
result := 'NO DATA';

end;



This is the data unit code (slightly) amended from the demo code.


CODE
unit dataUnit;

interface

uses
fastmm4, SysUtils, Sockets, ExtCtrls, Classes;

type
TDataModule1 = class(TDataModule)
Server: TTcpServer;
Client: TTcpClient;
procedure ServerAccept(Sender: TObject;
ClientSocket: TCustomIpClient);
private
{ Private declarations }
public
{ Public declarations }
end;

TClientDataThread = class(TThread)
private
public
ListBuffer :TStringList;
TargetList :TStrings;
procedure synchAddDataToControl;
constructor Create(CreateSuspended: Boolean);
procedure Execute; override;
procedure Terminate;
end;

var
DataModule1: TDataModule1;
Memrecv: Tstringlist;
implementation

{$R *.dfm}

//------------- TClientDataThread impl -----------------------------------------
constructor TClientDataThread.Create(CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
FreeOnTerminate := true;
ListBuffer := TStringList.Create;
end;

procedure TClientDataThread.Terminate;
begin
ListBuffer.Free;
inherited;
end;

procedure TClientDataThread.Execute;
begin
Synchronize(synchAddDataToControl);
end;

procedure TClientDataThread.synchAddDataToControl;
begin
TargetList.AddStrings(ListBuffer);
end;

//------------- end TClientDataThread impl -------------------------------------


procedure TDataModule1.ServerAccept(Sender: TObject;
ClientSocket: TCustomIpClient);
var
s: string;
DataThread: TClientDataThread;
begin
// create thread
DataThread:= TClientDataThread.Create(true);
// set the TagetList to the output string list.
DataThread.TargetList := memRecv;

// Load the Threads ListBuffer
DataThread.ListBuffer.Add('*** Connection Accepted ***');
DataThread.ListBuffer.Add('Remote Host: ' + ClientSocket.LookupHostName(ClientSocket.RemoteHost) +
' (' + ClientSocket.RemoteHost + ')');
DataThread.ListBuffer.Add('===== Begin message =====');
s := ClientSocket.Receiveln;
while s <> '' do
begin
DataThread.ListBuffer.Add(s);
s := ClientSocket.Receiveln;
end;
DataThread.ListBuffer.Add('===== End of message =====');

// Call Resume which will execute and synch the
// ListBuffer with the TargetList
DataThread.Resume;
end;

end.
sggaunt is offline   Reply With Quote
Old Mar 2nd, 2007, 9:15 AM   #2
DaWei
Resident Grouch
 
DaWei's Avatar
 
Join Date: Jun 2005
Posts: 6,453
Rep Power: 10 DaWei is on a distinguished road
As a new member, it would be polite and sociable for you to read the rules/FAQ, particularly with regard to the use of code tags.
__________________
Abstraction doesn't make it impossible to write bad code; it makes it possible to write superior code.
Contributor's Corner: Grumpy on C++ Exceptions DaWei on Pointers
DaWei is offline   Reply With Quote
Old Mar 3rd, 2007, 6:00 AM   #3
sggaunt
Newbie
 
Join Date: Mar 2007
Posts: 9
Rep Power: 0 sggaunt is on a distinguished road
Sorry about that: The original location of this post did indeed have [code[/code] tags, but they didnt come across in the paste.
Shortage of time to completion of this project meant I needed help with this as quickly as possible. Hence the lack of time to Read the [rules] as I should and now have. Once again sorry.

BTW the code doesnt explicitly create the datamodule in the DLL which has now been sorted out but there are still problems with the operation

Steve
sggaunt is offline   Reply With Quote
Old Mar 3rd, 2007, 9:47 AM   #4
Ooble
I eat cake for breakfast.
 
Ooble's Avatar
 
Join Date: Jul 2004
Location: In my box.
Posts: 4,434
Rep Power: 9 Ooble is on a distinguished road
Could you please repost the code with the [code] tags? It's very difficult to read without proper formatting.
__________________
Me :: You :: Them
Ooble is offline   Reply With Quote
Old Mar 5th, 2007, 4:47 AM   #5
sggaunt
Newbie
 
Join Date: Mar 2007
Posts: 9
Rep Power: 0 sggaunt is on a distinguished road
Here is the code in tags,
Please notice the ammended part (lack of implict creation of the data module)which was causing the hang.
However there are still problems in that although my fire wall detectes activity, the dll still dosent seem to be able to transmitt or recieve messages.


library Ethernet;
{ To avoid using sharemempass string information using PChar or ShortString parameters. }
uses
FastMM4 in 'FastMM4.pas',
SysUtils,
Classes,
dataUnit in 'dataUnit.pas' {DataModule1: TDataModule};

{$R *.res}

function Initalise: integer; stdcall;
begin
result := 0;
// The following line was missing from the original code and this caused most of the 
//problem I belive it moust also be implictly freed.

DataModule1 := TDataModule1.Create(nil); // creates the data module
 
MemRecv := TStringList.Create; // create a string list to hold recived messages
memrecv.Add('Test line');
end;

function StopAll: integer; stdcall;
begin
result := 0;
try
Freeandnil(Memrecv);
Datamodule1.Server.Active := false;
datamodule1.Client.Active := false;
except
result := 1;
end;
end;

function ActivateServer(LocalP: shortstring;
remoteH: shortstring;
RemoteP: shortstring): integer; stdcall;
begin
result := 0;
with DataModule1 do
try
// any one of these calls cause the app to hang!!
Client.RemoteHost := RemoteH;
Client.RemotePort := RemoteP;
Server.LocalPort := LocalP;
Server.Active := True;
except
result := 1;
end;
end;

// read the first line out of the stringlist and delete it!!
// this works as expeceted.

function ReadLine: shortstring; stdcall;
begin
if MemRecv.Count > 0 then
begin
result := MemRecv.Strings[0];
Memrecv.Delete(0);
end
else
result := 'NO DATA';

end;

// new fuction retives the values from the components this works ok.
function RetrieveSettings(Code: integer): shortstring; stdcall;
begin
   with datamodule1 do
   case code of
   0: result := Server.LocalHost;
   1: result := Server.LocalPort;
   2: result := Client.RemotePort;
   3: result := Client.RemoteHost;
   else result := 'Illegal Code';
   end;
end;


exports
 Initalise, StopAll, ActivateServer, Send, ReadLine,
 Minimum, Maximum, RetrieveSettings, ReadCount;

begin
end;


This is the data unit code (slightly) amended from the demo code.


unit dataUnit;

interface

uses
fastmm4, SysUtils, Sockets, ExtCtrls, Classes;

type
TDataModule1 = class(TDataModule)
Server: TTcpServer;
Client: TTcpClient;
procedure ServerAccept(Sender: TObject;
ClientSocket: TCustomIpClient);
private
{ Private declarations }
public
{ Public declarations }
end;

TClientDataThread = class(TThread)
private
public
ListBuffer :TStringList;
TargetList :TStrings;
procedure synchAddDataToControl;
constructor Create(CreateSuspended: Boolean);
procedure Execute; override;
procedure Terminate;
end;

var
DataModule1: TDataModule1;
Memrecv: Tstringlist;
implementation

{$R *.dfm}

//------------- TClientDataThread impl -----------------------------------------
constructor TClientDataThread.Create(CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
FreeOnTerminate := true;
ListBuffer := TStringList.Create;
end;

procedure TClientDataThread.Terminate;
begin
ListBuffer.Free;
inherited;
end;

procedure TClientDataThread.Execute;
begin
Synchronize(synchAddDataToControl);
end;

procedure TClientDataThread.synchAddDataToControl;
begin
TargetList.AddStrings(ListBuffer);
end;

//------------- end TClientDataThread impl -------------------------------------


procedure TDataModule1.ServerAccept(Sender: TObject;
ClientSocket: TCustomIpClient);
var
s: string;
DataThread: TClientDataThread;
begin
// create thread
DataThread:= TClientDataThread.Create(true);
// set the TagetList to the output string list.
DataThread.TargetList := memRecv;

// Load the Threads ListBuffer
DataThread.ListBuffer.Add('*** Connection Accepted ***');
DataThread.ListBuffer.Add('Remote Host: ' + ClientSocket.LookupHostName(ClientSocket.RemoteHos t) +
' (' + ClientSocket.RemoteHost + ')');
DataThread.ListBuffer.Add('===== Begin message =====');
s := ClientSocket.Receiveln;
while s <> '' do
begin
DataThread.ListBuffer.Add(s);
s := ClientSocket.Receiveln;
end;
DataThread.ListBuffer.Add('===== End of message =====');

// Call Resume which will execute and synch the
// ListBuffer with the TargetList
DataThread.Resume;
end;

end.
sggaunt is offline   Reply With Quote
Old Mar 8th, 2007, 9:58 AM   #6
Jimmy Bazooka
Newbie
 
Join Date: Mar 2007
Location: at this heap offset AF3A: 66C3
Posts: 10
Rep Power: 0 Jimmy Bazooka is an unknown quantity at this point
I cant see any Client or Server var declarations?! Well even if they are there, then you make it all wrong. You need to supply your dll host app with pointer to class instance variable. for example

some code in DLL:

...
 var 
  SomeForm: TForm;

function CreateSomeForm: Pointer;
begin
 Result := SomeForm.Create(nil);
 ...
 // some other code
end;

procedure DestroySomeForm;
begin
 If Assigned(SomeForm) then SomeForm.Free;
 ...
 // some other code
end;
Jimmy Bazooka is offline   Reply With Quote
Old Mar 8th, 2007, 12:11 PM   #7
sggaunt
Newbie
 
Join Date: Mar 2007
Posts: 9
Rep Power: 0 sggaunt is on a distinguished road
Thanks for your reply, not sure I fully understand, heres are some of the calling functions maybe you can put it more in context?

procedure TForm1.Button1Click(Sender: TObject);
begin
  try
     if Initalise > 0 then
       showmessage('Initalisation Error')
     else
       MessageBox.Text := 'Init OK';
  except
     showmessage('Error in Dll');
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
   try
      MessageBox.Text := inttostr(Maximum(10,20));
   except
       showmessage('Error in Dll');
   end;
end;

procedure TForm1.SetupBtnClick(Sender: TObject);
begin
  if ActivateServer(strtoint(LocalPort.Text), Address.Text, strtoint(RemotePort.Text)) = 0 then
      MessageBox.Text := 'Server active';
  IpCheck.Enabled := true;
end;

procedure TForm1.ClosebtnClick(Sender: TObject);
begin
  ipcheck.Enabled := false;
  close
end;

procedure TForm1.ReadBtnClick(Sender: TObject);
begin
  MessageBox.Text := ReadLine;
  RemCount.Caption := inttostr(ReadCount);
end;

procedure TForm1.SendBtnClick(Sender: TObject);
begin
   if send(OutputBox.Text, trim(address.Text), strtoint(trim(remotePort.Text))) = 0 then
       messagebox.Text := 'Client connected';
   BSent.Caption := inttostr(ClientBytesSent);
end;
sggaunt is offline   Reply With Quote
Old Mar 12th, 2007, 3:15 PM   #8
Jimmy Bazooka
Newbie
 
Join Date: Mar 2007
Location: at this heap offset AF3A: 66C3
Posts: 10
Rep Power: 0 Jimmy Bazooka is an unknown quantity at this point
Lightbulb

procedure TForm1.Button1Click(Sender: TObject);
begin
  try
     if Initalise > 0 then
       showmessage('Initalisation Error')
     else
       MessageBox.Text := 'Init OK';
  except
     showmessage('Error in Dll');
  end;
end;

firstly, i dont see the reason why you use try except block?
you have no crash unsafe calls there so you can easly remove it.

secondly, could you supply more info what components you want to implement in your DLL?

and what is MessageBox in your case? Is it the component created by DLL?
Jimmy Bazooka is offline   Reply With Quote
Old Mar 13th, 2007, 7:14 AM   #9
sggaunt
Newbie
 
Join Date: Mar 2007
Posts: 9
Rep Power: 0 sggaunt is on a distinguished road
Thanks for the Reply Jimmy

Yes not done (much/anything) with DLL's untill now so I wasnt sure if errors would propergate out of the DLL or how they should be handled!

We were going to put the Client server demo from the Delphi7 Intenet tab into the DLL , but now this dosnt seems to be the correct thing to do.
So what I now propose to use is the Indy IDTCPServer component, and the functionality will be based around the Demo code for this component.
in Indy/demos/IIdTCPDemo/Server.

We are starting to make some progress getting the application version of this talk to to our hardware (PIC Micro) server.
But our costomer wants a DLL so he can do something with VB.

Steve
sggaunt is offline   Reply With Quote
Old Mar 14th, 2007, 2:05 PM   #10
Jimmy Bazooka
Newbie
 
Join Date: Mar 2007
Location: at this heap offset AF3A: 66C3
Posts: 10
Rep Power: 0 Jimmy Bazooka is an unknown quantity at this point
Exclamation One more thing to know...

OK, Steve. Also I'd like to aware you of problem of calling conventions missmatch, that is, you need to specify the StdCall convention to make sure that your exported function will work in VB. By default, Delphi uses Register calling convention, so the parameters are passed from left to right, and if possible, through CPU registers instead of creating new stack frame. But compilers (intepreters) from Microsoft and many other developers use StdCall convention by default (as all functions of WinAPI). Under StdCall calling convention parameters are passed from right to left and always on the stack.
Jimmy Bazooka is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread in Forum | Next Thread in Forum »

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump




DaniWeb IT Discussion Community
All times are GMT -5. The time now is 9:39 PM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC