Non-dotnet DLLs: brick wall hit

G

Gary Jones

Hi,

I'm trying to find my feet with C# and have hit a bit of a wall.

I have lots of legacy code in a DLL written in Delphi. I don't think
the fact that it's written in Delphi should cause too much of a
problem since the code is just simple Pascal that does arithmetic and
string manipulations, and all "strings" in the structures passed to
and from the DLL are actually arrays of char.

I want to retain the DLL for use in existing applications, but also
make of it in C# applications. The routines need to be available and
maintainable for several applications, including legacy ones.

Anyway...

As a proof of concept, I want to be able to pass what should, to my
mind, be a simple structure (data to be processed) to the DLL and
return a simple structure (results) back to the calling C#
application. (I'm using the word "structure" to avoid being too
specific; I'm not even sure if I should use a struct or a class.)

I've spent ages trawling through newsgroups and tries all sorts of
examples of code but nothing seems to quite work. There are many
messages on this subject with lots of no-doubt useful advice, but I
think the advice will make more sense to me if I have a working
example to play with.

So, does anyone have a mickey-mouse example of passing and returning
"structures" (in my ill-defined sense) from a C# application to a
non-.NET DLL.

Thanks.

Gary Jones
(Please send any replies here rather than by email.)
 
C

cody

You can simply pass a pascal string as string, and pchar as StringBuilder to
the functions in your dll. Is that what you wanted to hear?
Maybe you could be a bit more specific.
 
J

Jeff Gaines

So, does anyone have a mickey-mouse example of passing and returning
"structures" (in my ill-defined sense) from a C# application to a
non-.NET DLL.

Hi Gary

I suspect a lot of my code is 'Micky Mouse' :)

Can you post the structure that the Delphi DLL expects and we can
probably explain how to re-create it in C#.
 
M

Michael Moreno

You can simply pass a pascal string as string, and pchar as StringBuilder to
the functions in your dll. Is that what you wanted to hear?

I would be very very very surprised if he could pass a Delphi string as
a string in C#. Delphi string requires the link with the Borland lib
ShareMem in both projects (dll and calling app) to work.
 
C

cody

Yes you are right, delphi strings are not nullterminated strings like in
C++, my mistake. But you can pass pchar as StringBuilder, it is marshalled
automatically. And for the pascal string, you can pass it as a simple char
array, the char at index 0 will be the lenght of the string, this should
work without any problems.
 
G

GaryJones

The structures I actually need to use are very large (around 200 data
items being passed into the DLL, and a similar number in the returned
structure), so I'm just trying to get a trivial example working in C#.
To test things, I've written a silly little Delphi DLL which works the
way I'm familiar with; it accepts two pointers: one to the data to be
processed, and the other to the results to be populated.

In Delphi, my example looks like this:

type
stringz255=array[0..255] of char;

TinData=record
i:integer;
s:stringz255;
end;

ToutData=record
i1,i2:integer;
s1,s2:stringz255;
end;

PinData=^TinData;
PoutData=^ToutData;


The declaration of the main routine in the DLL looks like this...
procedure dllCore(inData:pinData;outData:poutData);cdecl;


.... and my main DLL routine looks like this...
procedure dllCore(inData:pinData;outData:poutData);
var
workIn:TinData;
workOut:ToutData;
begin
// dereference
workIn:=inData^;
workOut:=outData^;

// do something useful here

// rereference
outData^:=workOut;
end;


My Delphi calling application looks like this...
procedure dllCore(inData:pinData;outData:poutData);cdecl; external
'DLLexample.dll';

procedure TmainForm.doitBtnClick(Sender: TObject);
var
callIn:Tindata;
callOut:ToutData;
begin
// assign values in callIn

dllCore(@callIn,@callOut);

// do whatever with values in callOut
end;


The example DLL looks like it should be straightforward to use in C#,
but I've tried several approaches and am still struggling.

I have the Delphi source code in my real-world application, so can make
changes on that side if necessary to make things easier or more
elegant.

Thanks.


Gary Jones

(Using Google Groups to post here and have just discovered that I now
have to use the new Google Groups beta.)
 
E

Eric

Hello GaryJones,
The structures I actually need to use are very large (around 200 data
items being passed into the DLL, and a similar number in the returned
structure), so I'm just trying to get a trivial example working in C#.

You might consider making this Delphi project into a COM project using a Remote Data Module. If you reference the COM object in C# it will create a proxy class that's easy to use.

P-Invoke in C# to call Win32 style DLLs is a little harder. It can be done, of course, but the RDM option is the easiest.
 
J

Jeff Gaines

The structures I actually need to use are very large (around 200 data
items being passed into the DLL, and a similar number in the returned
structure), so I'm just trying to get a trivial example working in C#.
To test things, I've written a silly little Delphi DLL which works the
way I'm familiar with; it accepts two pointers: one to the data to be
processed, and the other to the results to be populated.

I installed D7 to try and build your dll but it just reminded me of all
the reasons I gave up on Delphi :-(

The following works in a C# app:

struct TinData
{
public int i;
public StringBuilder s;
}

struct ToutData
{
public int i;
public StringBuilder s;
}

private void dllCore(ref TinData tin, ref ToutData tout)
{
tout.i = tin.i;
tout.s = tin.s;
}

private void btnTest_Click(object sender, System.EventArgs e)
{
JTest();
}

private void JTest()
{
TinData tinTemp = new TinData();
tinTemp.i = Convert.ToInt32(txti.Text);
tinTemp.s = new StringBuilder();
tinTemp.s.Append(txts.Text);

ToutData toutTemp = new ToutData();
toutTemp.s = new StringBuilder();

dllCore(ref tinTemp, ref toutTemp);

lbli.Text = toutTemp.i.ToString();
lbls.Text = toutTemp.s.ToString();
}

However it may be meaningless when it meets the dll!

I put together a test Form which has TextBoxes txti and txts, Labels
lbli and lbls and a Button btnTest.

By passing the two data structures as 'ref' they are effectively
pointers but I'm not sure how Delphi will react?

If you want to post the dll or its source I am happy to have a play, or
perhaps somebody else will pop in with some ideas.
 
C

Chris Jobson

The declaration of the main routine in the DLL looks like this...
procedure dllCore(inData:pinData;outData:poutData);cdecl;

I may well be wrong about this, but I thought that functions to be called
through PInvoke had to use the stdcall calling convention (rather than
cdecl).

Chris Jobson
 
W

Willy Denoyette [MVP]

Chris,

stdcall is the default, you can change this default by means of the
"CallingConvention" DllImportAttribute member.

Willy.
 
J

Jon Skeet [C# MVP]

Chris Jobson said:
I may well be wrong about this, but I thought that functions to be called
through PInvoke had to use the stdcall calling convention (rather than
cdecl).

You can specify either using the CallingConvention member of
DllImportAttribute. I haven't used it myself, but it looks like it
should be able to do the job.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top