Writing an export function in ec, accepting a pointer

General help with the core C language of which eC is a superset, integrating eC with other languages such as C++ at the C level, using C libraries in eC, etc.
Post Reply
fedor_bel
Posts: 21
Joined: Sun Mar 14, 2010 4:46 pm

Writing an export function in ec, accepting a pointer

Post by fedor_bel »

Hello,

Let me start.

I need to write a dll with an export function to be called from another C/C++ program.

I write following:

Code: Select all

//library.ec
public struct MqlStr {int len; char *string;}; 
public dllexport int f2(MqlStr * pStr){ return 1; }                              
when I try to call my f2 function from another program - it crashes. Why can it be?
The crashed program report is following

Code: Select all

There has been a critical error
Time        : 2010.03.15 20:40
Program     : Client Terminal
Version     : 4.00 (build: 225, 10 Jul 2009)
OS          : Windows XP Professional 5.1 Service Pack 3 (Build 2600)
Processors  : 2 x X86 (level 15)
Memory      : 2086952/507712 kb
Exception   : C0000005
Address     : 00000000
Access Type : read
Access Addr : 00000000

Registers   : EAX=00000000 CS=001b EIP=00000000 EFLGS=00010246
            : EBX=00460044 SS=0023 ESP=022BBD48 EBP=022BBD80
            : ECX=6788125A DS=0023 ESI=012EFF40 FS=003b
            : EDX=000000FE ES=0023 EDI=012EFF48 GS=0000

Stack Trace : 00455691 FFFFFFFF 00000000 00000000
            : 00000000 00000000 00000000 00000000
            : 00000000 00000000 00000000 00000000
            : 00000000 00000000 00000000 00000000

Modules     :
          1 : 00400000 002B1000 c:\program files\metatrader - alpari\terminal.exe
          2 : 01870000 002C5000 c:\winxp\system32\xpsp2res.dll
          3 : 08000000 00008000 c:\program files\yandex\punto switcher\pshook.dll
          4 : 10000000 00004000 c:\program files\unlocker\unlockerhook.dll
          5 : 5AD70000 00038000 c:\winxp\system32\uxtheme.dll
          6 : 5ED00000 000CC000 c:\winxp\system32\opengl32.dll
          7 : 61540000 0021B000 d:\opt\ecere\bin\ecere.dll
          8 : 662B0000 00058000 c:\winxp\system32\hnetcfg.dll
          9 : 67880000 00012000 c:\program files\metatrader - alpari\experts\libraries\library.dll
         10 : 68B20000 00020000 c:\winxp\system32\glu32.dll
         11 : 71A50000 0003F000 c:\winxp\system32\mswsock.dll
         12 : 71A90000 00008000 c:\winxp\system32\wshtcpip.dll
         13 : 71AA0000 00008000 c:\winxp\system32\ws2help.dll
         14 : 71AB0000 00017000 c:\winxp\system32\ws2_32.dll
         15 : 71B20000 00012000 c:\winxp\system32\mpr.dll
         16 : 73000000 00026000 c:\winxp\system32\winspool.drv
         17 : 73760000 0004B000 c:\winxp\system32\ddraw.dll
         18 : 73BC0000 00006000 c:\winxp\system32\dciman32.dll
         19 : 73DD0000 000FE000 c:\winxp\system32\mfc42.dll
         20 : 74720000 0004C000 c:\winxp\system32\msctf.dll
         21 : 76380000 00005000 c:\winxp\system32\msimg32.dll
         22 : 76390000 0001D000 c:\winxp\system32\imm32.dll
         23 : 763B0000 00049000 c:\winxp\system32\comdlg32.dll
         24 : 76B40000 0002D000 c:\winxp\system32\winmm.dll
         25 : 76C90000 00028000 c:\winxp\system32\imagehlp.dll
         26 : 77120000 0008B000 c:\winxp\system32\oleaut32.dll
         27 : 773D0000 00103000 c:\winxp\winsxs\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.2600.5512_x-ww_35d4ce83\comctl32.dll
         28 : 774E0000 0013D000 c:\winxp\system32\ole32.dll
         29 : 77C10000 00058000 c:\winxp\system32\msvcrt.dll
         30 : 77DD0000 0009B000 c:\winxp\system32\advapi32.dll
         31 : 77E70000 00092000 c:\winxp\system32\rpcrt4.dll
         32 : 77F10000 00049000 c:\winxp\system32\gdi32.dll
         33 : 77F60000 00076000 c:\winxp\system32\shlwapi.dll
         34 : 77FE0000 00011000 c:\winxp\system32\secur32.dll
         35 : 7C800000 000F6000 c:\winxp\system32\kernel32.dll
         36 : 7C900000 000B2000 c:\winxp\system32\ntdll.dll
         37 : 7C9C0000 00817000 c:\winxp\system32\shell32.dll
         38 : 7E410000 00091000 c:\winxp\system32\user32.dll

Call stack  :
Calling a function defined as

Code: Select all

public dllexport int f1(){return 1;}     
works great.

The program documentation says that
Functions imported from DLL into the program must provide linkage convention accepted for Windows API functions. To provide such a convention, the key word __stdcall specific for compilers of Microsoft(r) company is used in the source codes of dlls written in C or C++ language.
May you could advice some debugging programs, information where I can learn how to debug the problem.
jerome
Site Admin
Posts: 608
Joined: Sat Jan 16, 2010 11:16 pm

Re: Writing an export function in ec, accepting a pointer

Post by jerome »

Hi Fedor,

In this respect eC is no different than C.
You will need a proper 'matching' prototype to call it from the C/C++ program.
Are you linking the DLL with the linker, or are you loading the functions yourself with the Windows API?

eC has the 'stdcall' keyword to help you use stdcall convention. This will make your function use stdcall calling convention:

public dllexport int stdcall f2(MqlStr * pStr){ return 1; }

However, the only important thing is that the prototypes match those in your program, whether it's stdcall or not. Is your other program C or C++? If you want to call a function in a C or eC DLL from C++ you should declare it as such:

extern "C" __declspec(dllexport) int __stdcall f2(MqlStr * pStr);


Also the function you pasted here only returns 1 and doesn't do anything. Is that exactly the function that is crashing? You will have to be aware at one point of how eC treats diffrent types of data objects... struct and class are treated differently in eC (To my recent astonishment, much in the same way they are different in C#!). For example eC structs are always passed by references, and although you don't see the * symbol classes instances always are pointers. Furthermore, of course eC and C++ classes are different kind of objects. (We need to develop a compatibility layer.)

Best of luck!

Jerome
fedor_bel
Posts: 21
Joined: Sun Mar 14, 2010 4:46 pm

Re: Writing an export function in ec, accepting a pointer

Post by fedor_bel »

Hello Jerome,

Thank you very much for your quick, smart and informative answer!
Unfortunately, the new information, you provided, did not help me to manage to transmit a parameter into a function.

1. If I try to use

extern "C" __declspec(dllexport) int __stdcall f2(MqlStr * pStr);

the ec compiler produces an error

"Library.ec:12:8: error: syntax error"

The compiler does not seem to like extern "C" construct. Is it a bug or I am not using it right?

2. If I try to use

public dllexport int stdcall f2(MqlStr * pStr);

the calling program can not find the function in the dll. Although if I use

public dllexport int f2(MqlStr * pStr);

the calling program finds the function in the dll ok.
Why is that?

3. The example functions are empty just for the sake of simplicity, of course. What I try to achieve is to be able to receive a parameter into my export function. The calling program does not link my DLL with the linker, it loads the functions using the Windows API and late binding. Let me provide some more description regarding the requirement to the DLLs:
Functions imported from DLL into the program must provide linkage convention accepted for Windows API functions. To provide such a convention, the key word __stdcall specific for compilers of Microsoft(r) company is used in the source codes of programs written in C or C++ language. The above linkage convention is characterized by the following:
- calling function must "see" the called (imported from DLL) function prototype in order to put parameters onto the stack in a proper way;
- calling function puts parameters onto the stack in the reversed order, i.e., from right to left; it is this order in which the imported function reads parameters passed to it;
- parameters are passed by their values, except for those explicitely passed by a link;
- on reading the parameters passed to it, the imported function will flush the stack by itself.
Some more notes I made during my vane attempts:
- public int f1(); - works great!
- public int f1(int a1); - fails miserably!
WTF? sorry for bad language :-)

Or could you just advise a good book or tutorial on the subject, but not too long, because I am pretty much tired of this trial and error approach - I definitely lack knowledge in this low level stuff. I cant even confidently define the topic of my misknowledge: could it be "a linking for dummies", or maybe "dissassembling a dll or c/c++ program for dummies".
fedor_bel
Posts: 21
Joined: Sun Mar 14, 2010 4:46 pm

Re: Writing an export function in ec, accepting a pointer

Post by fedor_bel »

Sorry guys for me being so stupid!

The problem is in the decorated function names or mangled function names.
I either have to
1. use a .def file when linking my dll written in ec language.
or
2. lookup the decorated function names using for example the dumpbin.exe program with the /exports option on my dll and use the decorated names when calling my functions from dll.

This seem to work! That is why I the program could not find my functions in the dll when I used the __stdcall convention.
jerome
Site Admin
Posts: 608
Joined: Sat Jan 16, 2010 11:16 pm

Re: Writing an export function in ec, accepting a pointer

Post by jerome »

Dear fedor,

If you are loading a method from a DLL, then I assume you are using function pointers?
I am not sure if there are other ways to do it in C++.
Loading functions from DLLs is a common topic for which Google will find you tons of tutorials.
Just keep in mind eC definitions works just like C, with some convenience keyword like 'stdcall' and 'dllexport' allowing for cleaner and cross-platform syntax.

The extern "C" example was code to write in your C++ file, not in eC code.

extern "C" __declspec(dllimport) int __stdcall f2(MqlStr * pStr);

This would let you link with an eC function defined with dllexport.
But this is for doing it with the linker. To do it with the win32 loader, you would usually define it with a function pointer prototype and use GetProcAddress. stdcall functions have a declarations that varies with the number of parameters, e.g.:

Code: Select all

int (stdcall * f2)(MqlStr * pStr) = null;
f2 = GetProcAddress(library, "f2@4");
Hope this helps,

Jerome
fedor_bel
Posts: 21
Joined: Sun Mar 14, 2010 4:46 pm

Re: Writing an export function in ec, accepting a pointer

Post by fedor_bel »

Thank you very much, Jerome.
That helps! The picture becomes slowly more clear.
Post Reply