PDA

View Full Version : Need help accessing a FreePascal-created library



Stoney
02-02-2010, 05:37 PM
Hi all,
I've been experimenting with creating a dynamic library from my game engine in the last couple of days. The reason is some developers are interested in my game engine, but would rather use it with C/C++ or C#.
And the more important reason is that I have the course "Game Development" this semester at school where we have to use Horde3D and C++. For our final project I want to use my own sprite and font manager since Horde3D's built-in overlays are not really satisfying (just supports stretchting; no rotating, coloring or alpha) for my purposes and I really would love to have TTF support in my game instead of just the one TGA font that comes with the default project we have to use.

My plan is to flatten down my object-orientiented design so it can be used C or C# and then create classes on the top of that for C++ and C#.
I already tried porting the classes I needed from Object Pascal to C++ which unfortunately didn't work as planned and I hope creating a dynamic library and writing a C header would be much easier and less painful.

Allright, generating the library works without any problems, but when I try to access to the library with C# or C I get some strange results.

Let's take this code for example:

library SDLTestLib;

uses
SDL;

var
MainSurface: PSDL_Surface;

procedure Initialize();
begin
SDL_Init(SDL_INIT_VIDEO);
end;

procedure Finalize();
begin
SDL_Quit;
end;

procedure CreateWindow(Width: Integer; Height: Integer; BPP: Integer);
begin
MainSurface := SDL_SetVideoMode(Width, Height, BPP, SDL_SWSURFACE);
if not Assigned(MainSurface) then WriteLn('Something isn''t right here.');
end;

procedure SetWindowCaption(Caption: PChar);
begin
SDL_WM_SetCaption(Caption, Caption);
end;

procedure Flip;
begin
SDL_Flip(MainSurface);
end;

exports
{$IFDEF DARWIN} {OS X entry points}
Initialize name '_Initialize',
Finalize name '_Finalize',
CreateWindow name '_CreateWindow',
SetWindowCaption name '_SetWindowCaption',
Flip name '_Flip',
{$ENDIF}
Initialize,
Finalize,
CreateWindow,
SetWindowCaption,
Flip;

end.


My C header:


#ifndef _SDLTESTLIB_H_
#define _SDLTESTLIB_H_

#ifdef __cplusplus
extern "C"
{
#endif

extern void Initialize();
extern void Finalize();
extern void CreateWindow(int Width, int Height, int BPP);
extern void Flip();
extern void SetWindowCaption(char* caption);

#ifdef __cplusplus
}
#endif

#endif /* _SDLTESTLIB_H_ */


My C# file:


using System;
using System.Runtime.InteropServices;

public class SDLTestLib {

const string ElysionLibName = "SDLTestLib.dll";

[DllImport(ElysionLibName, EntryPoint="Initialize",
CallingConvention=CallingConvention.StdCall)]
public static extern void Initialize();

[DllImport(ElysionLibName, EntryPoint="Finalize",
CallingConvention=CallingConvention.StdCall)]
public static extern void Finalize();

[DllImport(ElysionLibName, EntryPoint="Flip",
CallingConvention=CallingConvention.StdCall)]
public static extern void Flip();

[DllImport(ElysionLibName, EntryPoint="CreateWindow",
CallingConvention=CallingConvention.StdCall)]
public static extern void CreateWindow(int Width, int Height, int BPP);

[DllImport(ElysionLibName, EntryPoint="SetWindowCaption",
CallingConvention=CallingConvention.StdCall)]
public static extern void SetWindowCaption(string caption);

public static void Main()
{
bool done = false;

Initialize();
Console.WriteLine("SDL initialized.");

CreateWindow(640, 480, 16);
Console.WriteLine("Window created.");

while (!done)
{
Flip();
}

Finalize();
}
}


The C# version won't work and terminates with an access violation when calling the Flip-procedure. The C version works, but assumes the Bits value as the height.
Oddly enough, if I change the code to interface with GLFW instead of SDL, the C# version works, but assigns the height value to the width.

I'm using FreePascal 2.4.0/2.4.1, tested with Windows XP (virtual machine) and Windows Vista.
Anyone had similar problems? What am I doing wrong? When writing a pascal header for C-DLL, you need something like


{$MINENUMSIZE 4}
{$PACKRECORDS C}
{$H+}
{$MACRO ON}

in the Pascal header. Do I need something similar in my Pascal library or in C/C# header? Is there something so obvious, I may have overlooked it?

paul_nicholls
02-02-2010, 06:40 PM
if you want to access a pascal lib from c, you might need to use the "cdecl" calling convention for your pascal routines.

Or maybe "stdcall"

Not sure about C# though...

cheers,
Paul

Stoney
02-02-2010, 08:43 PM
Thanks Paul for the cdecl/stdcall tip.
I could have thought of that :)

Unfortunately that was not the solution, it's still not working.

As for C#, it's already called with stdcall (CallingConvention = CallingConvention.StdCall), I changed it to Cdecl, but no change there either.

JSoftware
02-02-2010, 09:12 PM
Did you add the cdecl or stdcall directive in the pascal code too?

The default is most likely fastcall which is not supported in neither C or C#

paul_nicholls
02-02-2010, 09:22 PM
Did you add the cdecl or stdcall directive in the pascal code too?

The default is most likely fastcall which is not supported in neither C or C#


Sorry, but as JSoftware said, that was what I meant when I said to add the cdecl or stdcall to your pascal libs.

Like this:

library SDLTestLib;

uses
SDL;

var
MainSurface: PSDL_Surface;

procedure Initialize(); cdecl
begin
SDL_Init(SDL_INIT_VIDEO);
end;

procedure Finalize(); cdecl
begin
SDL_Quit;
end;

procedure CreateWindow(Width: Integer; Height: Integer; BPP: Integer); cdecl
begin
MainSurface := SDL_SetVideoMode(Width, Height, BPP, SDL_SWSURFACE);
if not Assigned(MainSurface) then WriteLn('Something isn''t right here.');
end;

procedure SetWindowCaption(Caption: PChar); cdecl
begin
SDL_WM_SetCaption(Caption, Caption);
end;

procedure Flip; cdecl
begin
SDL_Flip(MainSurface);
end;

exports
{$IFDEF DARWIN} {OS X entry points}
Initialize name '_Initialize',
Finalize name '_Finalize',
CreateWindow name '_CreateWindow',
SetWindowCaption name '_SetWindowCaption',
Flip name '_Flip',
{$ENDIF}
Initialize,
Finalize,
CreateWindow,
SetWindowCaption,
Flip;

end.


Try something like above - see modified routines with cdecl added after...

If this doesn't work, try stdcall instead :)

cheers,
Paul

Stoney
02-02-2010, 09:51 PM
Thank you both, works like a charm now. :)

paul_nicholls
02-02-2010, 10:02 PM
Thank you both, works like a charm now. :)


Yay!
Just glad I could help :)

So was cdecl the 'charm', or was it 'stdcall'?

cheers,
Paul

Stoney
02-02-2010, 10:52 PM
It was cdecl in my case.
Would have probably worked with stdcall also, but since cdecl worked, I haven't tried stdcall.