![]() |
|
![]() |
|
|
Thread Tools | Display Modes |
|
|
#1 |
|
Newbie
Join Date: Mar 2007
Posts: 9
Rep Power: 0
![]() |
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. |
|
|
|
|
|
#2 |
|
Resident Grouch
![]() ![]() ![]() ![]() ![]() ![]() Join Date: Jun 2005
Posts: 6,453
Rep Power: 10
![]() |
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 |
|
|
|
|
|
#3 |
|
Newbie
Join Date: Mar 2007
Posts: 9
Rep Power: 0
![]() |
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 |
|
|
|
|
|
#4 |
|
I eat cake for breakfast.
![]() ![]() ![]() ![]() Join Date: Jul 2004
Location: In my box.
Posts: 4,434
Rep Power: 9
![]() |
Could you please repost the code with the [code] tags? It's very difficult to read without proper formatting.
|
|
|
|
|
|
#5 |
|
Newbie
Join Date: Mar 2007
Posts: 9
Rep Power: 0
![]() |
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. |
|
|
|
|
|
#6 |
|
Newbie
Join Date: Mar 2007
Location: at this heap offset AF3A: 66C3
Posts: 10
Rep Power: 0
![]() |
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; |
|
|
|
|
|
#7 |
|
Newbie
Join Date: Mar 2007
Posts: 9
Rep Power: 0
![]() |
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; |
|
|
|
|
|
#8 |
|
Newbie
Join Date: Mar 2007
Location: at this heap offset AF3A: 66C3
Posts: 10
Rep Power: 0
![]() |
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? |
|
|
|
|
|
#9 |
|
Newbie
Join Date: Mar 2007
Posts: 9
Rep Power: 0
![]() |
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 |
|
|
|
|
|
#10 |
|
Newbie
Join Date: Mar 2007
Location: at this heap offset AF3A: 66C3
Posts: 10
Rep Power: 0
![]() |
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.
![]() |
|
|
|
![]() |
| Bookmarks |
| Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
| Thread Tools | |
| Display Modes | |
|
|