Board index » delphi » Messing about with exe's object inside called dll

Messing about with exe's object inside called dll

Hi,

OS: Windows NT & Windows 2000
D?: Delphi 5 Enterprise

Now I know... the subject seems a bit strange, I'll explain.

My application will be calling a dll-function, where it passes an
Integer(MyObj) as a var.
By using memory mapped files (Thanks to Marco Cantu in 'Mastering Delphi6',
chapter 12, page 506) I can gain access to the actual object passes to the
function. This is where the problem starts. The object contains strings x,
y, z. When passing the object, x and y are set to something... no prob. Even
inside the dll, after getting the object I can actually read the x and y
vars. Inside the dll I state that z := x + y. Nothing special I should
say???
After returning to the application, z is no longer available, and I get an
exception when I try to access it like ShowMessage(MyObj.z).

To explain a bit further:

---- The object being
passed... ------------------------------------------------------------------
-------------------------------
type
  TMyObj = class
    x,
    y,
    z : string;
  end;

---- The function inside the
dll... ---------------------------------------------------------------------
-----------------
function RUN_External(aCommand, aResult : PChar; Context : integer) :
integer; stdcall;
var
  p : string;
  hMapFile : THandle;
  ShareData: ^TMyObj;
const
  VirtualFileName = 'ShareDllData';
  DataSize = SizeOf(TMyObj);
begin
  Result := 0;

  //create memory mapped file
  hMapFile := CreateFileMapping($FFFFFFFF, nil, Page_ReadWrite, 0, DataSize,
VirtualFileName);
  if hMapFile =0 then
    raise Exception.Create('Error creating memory-mapped file');
  //get the pointer to the actual data
  ShareData := MapViewOfFile(hMapFile, File_Map_Write, 0, 0, DataSize);

  if Context <> 0 then begin
    ShareData^ := TMyObj(Context);
    ShareData^.z := ShareData^.x + ShareData^.y; <<<<<<<<<<<----------- it
should be complaining here by now... but it doesn't... and x and y are
definalty set...
  end;

  case CommandType(aCommand) of
    ctQuery : case QueryType(aCommand) of
      qtConcept : p := 'Query Concept';
      qtConvert : p := 'Query Convert';
      qtDetails : p := 'Query Concept';
      qtSummary : p := 'Query Concept';
    else begin
        p := 'Unknown request';
        Result := -1;
      end; //begin
    end; //case
    ctTrans : case TransType(aCommand) of
      ttFAX : p := 'Trans FAX';
      ttSMS : p := 'Trans SMS';
    else begin
        p := 'Unknown transaction';
        Result := -1;
      end; //begin
    end; //case
  else begin
      p := 'Unknown Command' + ShareData^.z;
      Result := -1;
    end; //begin
  end; //case

  UnmapViewOfFile(ShareData);
  CloseHandle(hMapFile);

  StrCopy(aResult, PChar(p));
end;

---- Finally, the code inside the
form... --------------------------------------------------------------------
----------
function ExecuteExternalFunction(Input : string; var Output : string;
Context : integer) : integer;
const
  Run_External_Name = 'RUN_External';
  DLLFile = 'External.dll';
var
  DLLHandle : THandle;
  RUN_External : function(aCommand, aResult : PChar; Context : integer) :
integer; stdcall;
  s : string;
begin
  DLLHandle := LoadLibrary(DLLFile);
  Assert(DLLHandle >= 32);
  @Run_External := GetProcAddress(DLLHandle, PChar(Run_External_Name));
  Assert(@Run_External <> nil);
  SetLength(s, 2048);
  Result := RUN_External(PChar(Input), PChar(s), Context);
  Output := PChar(s);
  FreeLibrary(DLLHandle);
end;

procedure TMainForm.bCallClick(Sender: TObject);
var
  s, t : string;
  i : integer;
  O : TMyObj;
begin
  s := rgCommand.Items[rgCommand.ItemIndex] + #20;
  case rgCommand.ItemIndex of
    0 : s := s + rgQuery.Items[rgQuery.ItemIndex] + #20;
    1 : s := s + rgTrans.Items[rgTrans.ItemIndex] + #20;
  end;
  O := TMyObj.Create;
  try
    O.x := 'X';
    O.y := 'Y';
    i := ExecuteExternalFunction(s, t, Integer(O));
    ShowMessage(IntToStr(i) + #13 + t + #13 +
                O.x + #13 + O.y + #13 + O.z);
<<<<<<<------------ this is where I get an exception... z is no longer
available here...
  finally
    O.Free;
  end;
end;

----------------------------------------------------------------------------
----------------------------------------
What I do know, is when I remove all stuff for the object, I have the
correct code to pass STRINGS from EXE to DLL and BACK!!!
This took a while to figure out. I don't want to rely on borlandmm.dll hehe

In the end the question comes to this:
How do I get ANY object from exe to dll, modify it, and pass it back to exe?

Thanks,

Rory Vieira
GIOS Voice Professionals
Network Manager / Developer

rory dot vieira at gios dot nl

 

Re:Messing about with exe's object inside called dll


"DJ Bo" <d...@gios.nl> wrote in news:3cf37cd7_2@dnews:

Quote
> I don't want to rely on borlandmm.dll

When passing Object Pascal string types across process boundaries
(i.e. between an EXE and DLL), you may have no choice but to rely
on  BorlandMM.DLL.  The "MM" in the DLL's filename stands for
"memory manager".  Delphi has to do some special memory management
tricks to allow strings to be passed between your EXE and DLL.

Does the access violation still occur if you use BorlandMM.DLL?

Also, if the DLL you are writing is only going to be used by a
Delphi EXE, you can write a package instead of a DLL and not have
to mess around with BorlandMM.DLL or file mapping.

HTH,

Chris.
-------------
C.R. Timmons Consulting, Inc.
http://www.crtimmonsinc.com/

Re:Messing about with exe's object inside called dll


Quote
> This took a while to figure out. I don't want to rely on borlandmm.dll

hehe

hehe?  There's a reason that Borland included the memory manager...

Quote
> How do I get ANY object from exe to dll, modify it, and pass it back to

exe?

What you're running into is the fact that strings are managed by the
compiler so that they dynamically allocate memory as needed.  The problem is
that your DLL has it's own memory manager, so if you modify a string, you've
allocated new memory from your DLL's memory manager, and deallocated a bad
pointer (because the memory was allocated from somewhere else).  If your
object used PChar's, you could /READ/ the contents of the object, but
passing data back takes more work because you need to pass data into memory
blocks OWNED by the EXE, not the DLL.

Eric

Re:Messing about with exe's object inside called dll


Quote
"Eric Hill" <e...@ijack.net> wrote in message news:3cf386c8$1_2@dnews...
> What you're running into is the fact that strings are managed by the
> compiler so that they dynamically allocate memory as needed.  The problem
is
> that your DLL has it's own memory manager, so if you modify a string,
you've
> allocated new memory from your DLL's memory manager, and deallocated a bad
> pointer (because the memory was allocated from somewhere else).  If your
> object used PChar's, you could /READ/ the contents of the object, but
> passing data back takes more work because you need to pass data into
memory
> blocks OWNED by the EXE, not the DLL.

Now this is my point! How would I go about and write the changed data back
to the EXE's memoryspace.
That's where I thought I had to use those memory mapped files...
I thought that THAT'S what they do ?!?

Hmmm.
I'll keep on trying...

Re:Messing about with exe's object inside called dll


"Chris R. Timmons" <crtimmons@X_NOSPAM_Xcrtimmonsinc.com> wrote in message
news:Xns921C5710445B6crtimmonscrtimmonsin@207.105.83.65...

Quote
> "DJ Bo" <d...@gios.nl> wrote in news:3cf37cd7_2@dnews:

> > I don't want to rely on borlandmm.dll

> When passing Object Pascal string types across process boundaries
> (i.e. between an EXE and DLL), you may have no choice but to rely
> on  BorlandMM.DLL.  The "MM" in the DLL's filename stands for
> "memory manager".  Delphi has to do some special memory management
> tricks to allow strings to be passed between your EXE and DLL.

That's why it took some time before the aCommand and aResult vars of the
RUN_External funtion had to be PChar.
I also use the ONLY correct method to CHANGE aResult inside the DLL. (This
came from some website)

Quote
> Does the access violation still occur if you use BorlandMM.DLL?
Elas, it does...

> Also, if the DLL you are writing is only going to be used by a
> Delphi EXE, you can write a package instead of a DLL and not have
> to mess around with BorlandMM.DLL or file mapping.

You are certainly right there, BUT...
Other ppl might want to write there dll for us. Maybe even using Visual C...
I cannot remember if C can make these packages...
I'll gice it a looksie though.

Re:Messing about with exe's object inside called dll


Quote
> Now this is my point! How would I go about and write the changed data back
> to the EXE's memoryspace.
> That's where I thought I had to use those memory mapped files...
> I thought that THAT'S what they do ?!?

Memory mapped files allow two PROCESSES to share some (fixed size) data.

An EXE and DLL share the same memory space, so they can exchange data freely
without the use of memory mapped files.

Strings are special cases of variables in Delphi because they are managed
internally.  Delphi creates, reallocates, and destroys memory associated
with the string throughout its use.  Since an EXE and DLL use two different
memory managers (that have no knowledge of each other), Delphi winds up
allocating and reallocating memory for the other memory manager and things
get all out of whack.

If you do not want to use the shared memory manager, then you need to do
things the way the Windows API does.

function GetAString(Buffer: PChar; BufferSize: Integer): Integer;
var
    AString: string;
begin
    Result := 0;
    AString := 'Some string from somewhere.';
    if BufferSize = 0 then
        Result := Length(AString)
    else
        Move(PChar(AString), Buffer, BufferSize);
end;

In your code do this:
var
    AString: string;
begin
    SetLength(AString, GetAString(nil, 0));
    GetAString(PChar(AString), Length(AString));
    // Use AString as needed...
end;

Notice how we simply copy bytes from one memory location to another.

You /do/ have a potential bug here as the passed buffer size parameter isn't
checked against the length of the string being copied...

Eric

Re:Messing about with exe's object inside called dll


"DJ Bo" <d...@gios.nl> wrote in news:3cf39454_2@dnews:

Quote
>> Also, if the DLL you are writing is only going to be used by a
>> Delphi EXE, you can write a package instead of a DLL and not
>> have to mess around with BorlandMM.DLL or file mapping.
> You are certainly right there, BUT...
> Other ppl might want to write there dll for us. Maybe even using
> Visual C... I cannot remember if C can make these packages...
> I'll gice it a looksie though.

If your Delphi EXE might call a DLL written in a non-Delphi
language, then you cannot safely pass a Delphi object to that
non-Delphi DLL routine.  For example, if a Delphi EXE calls a
Visual C++ DLL routine, and gives it a TMyObj as a parameter, the
Visual C++ DLL will not be able to manipulate the TMyObj instance
at all.  (Visual C++ code has no idea what a Delphi object is).

When an EXE and the DLL it uses are written in different
languages, the only types that can be passed as parameters are
those primitive types found in Windows.pas (PCHAR, BOOL, DWORD,
etc).

HTH,

Chris.
-------------
C.R. Timmons Consulting, Inc.
http://www.crtimmonsinc.com/

Other Threads