Subject: using a Cygnus dll with VC++ : impossible ?
From: [email-masked] (Jerome GRANDJEAN)

6 Jun 1998 07:27:21 -0700
Message-ID: <[email-masked]>
Mime-Version: 1.0
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
To: "Cygnus List" <[email-masked]>

Hi,

  I have some questions and observations about using a dll made by using
Cygnus with MS VC++.

1.
First, I never manage to use the functions malloc, free, realloc in my dll.
I had to use pointer instead, and Initialized them in my VC progam with
functions malloc, realloc and free of VC.
 This prevent me from using system functions which use malloc, realloc or
free, like fdopen, sscanf...
I don't know if this is a normal behaviour or not.

2.
The struct FILE are totally different, so you can't call a function using
you VC FILE* or VC std[in/out].

3.
Moreover, Cygnus doesn't seem to call functions the same way as VC does :

For instance, with the declarations :

typedef struct
{
     int n1;
     int n2;
     int n3;
     int n4;
} quad;

quad Associe(int n1, int n2, int n3, int n4) // function with complex result
{
     quad pRes;
     pRes.n1=n1;
     pRes.n2=n2;
     pRes.n3=n3;
     pRes.n4=n4;
     return(pRes);
}
int nRendPreums(int,int); // function with simple argument

In my program, I get :

for the simple function :
-------
with Cygnus
  46  nI=nRendPreums(1,2);
      0x4011ef <main+35>:     pushl  $0x2
      0x4011f1 <main+37>:     pushl  $0x1
      0x4011f3 <main+39>:     call   0x40159c <nRendPreums>
      0x4011f8 <main+44>:     addl   $0x8,%esp
      0x4011fb <main+47>:     movl   %eax,%eax
      0x4011fd <main+49>:     movl   %eax,0xfffffff4(%ebp)
--
with VC++
74:       nTest=nRendPreums(1,2);
004012A4   push        2
004012A6   push        1
004012A8   call        dword ptr [_nRendPreums(0x00416fdc)]
004012AE   add         esp,8
004012B1   mov         dword ptr [nTest],eax
-------

and for the complex function :
-------
with Cygnus :
 52  MonQuad=Associe(1,2,3,4);
      0x4011f7 <main+19>:     leal   0xfffffff0(%ebp),%eax
      0x4011fa <main+22>:     pushl  $0x4
      0x4011fc <main+24>:     pushl  $0x3
      0x4011fe <main+26>:     pushl  $0x2
      0x401200 <main+28>:     pushl  $0x1
      0x401202 <main+30>:     pushl  %eax    // the address of MonQuad is
stored
                                                 on the stack before calling
Associe
      0x401203 <main+31>:     call   0x401040 <Associe>
      0x401208 <main+36>:     addl   $0x10,%esp
--
with VC++

78:       MonQuad=Associe(1,2,3,4);
004012A9   push        4
004012AB   push        3
004012AD   push        2
004012AF   push        1
004012B1   lea         ecx,dword ptr [ebp-54h]
004012B4   push        ecx
004012B5   call        @ILT+25(?Associe@@YA?AUquad@@HHHH@Z)(0x00401019) //
call to Associe
004012BA   add         esp,14h
004012BD   mov         edx,dword ptr [eax]      // the 1st result value is
stored in edx
004012BF   mov         dword ptr [ebp-44h],edx  // then put on the stack
004012C2   mov         ecx,dword ptr [eax+4]
004012C5   mov         dword ptr [ebp-40h],ecx  // idem for 2nd result with
ecx
004012C8   mov         edx,dword ptr [eax+8]
004012CB   mov         dword ptr [ebp-3Ch],edx  // idem for 3rd, edx
004012CE   mov         eax,dword ptr [eax+0Ch]
004012D1   mov         dword ptr [ebp-38h],eax  // and the 4th, eax
004012D4   mov         ecx,dword ptr [ebp-44h]
004012D7   mov         dword ptr [MonQuad],ecx  // MonQuad.n1 is set
004012DA   mov         edx,dword ptr [ebp-40h]
004012DD   mov         dword ptr [ebp-1Ch],edx  // MonQuad.n2 too
004012E0   mov         eax,dword ptr [ebp-3Ch]
004012E3   mov         dword ptr [ebp-18h],eax  // MonQuad.n3
004012E6   mov         ecx,dword ptr [ebp-38h]
004012E9   mov         dword ptr [ebp-14h],ecx  // MonQuad.n4
---------
Here, functions are _cdecl, but the result is the same with __stdcall
functions (exect for the "add xxx, esp")
So, for function whose result is not longer than 32 bits (the length of a
register), wa can see than the call is done the same way :
 - The arguments are put on the stack from right to left
 - The result is store in eax

However, if the result is larger than 32 bits, Cygnus and VC++ don't behave
the same way :
 - after the arguments, Cygnus put on the stack the address
   of the object modified by the function.
 - VC doesn't. Instead, the address of the result is stored in eax.
   After the call, the result is store in the object.

This means that a VC function can't call a Cygnus function whose result is
larger than a register (larger than an int or a pointer).

Moreover, this test was made in debug mode for both sides. I don't know what
the optimisations could change in the way arguments are stored on the stack.

As I'm not a crack about C, compilers and Windows, I still ahve a few
questions :
  - Is there any standard for calling a function in a Windows'dll ?
  - Is there another keyword which could make one of the compiler behave
like the other ?

  Today, it seems to me that I must have in my dll functions that don't use
malloc directly or inderectly but import such functions (for instance, I
import within a dll malloc, realloc free and sscanf, with other names of
course), and that returns only pointer on struct and not struct themselves.
Of course, you can also rewrite a function in VC++ with an _asm section
calling your function the good way, it seems to work fine.

  Well, that's all, I hope that someone could say me if I really missed
something, for the memory functions and for functions call, and then explain
how to make VC and Cygnus work one with the other simply.

Bye

--------------------------------------------------------------
Jerome GRANDJEAN                                       SUPELEC
Email : [email-masked]        51 bis Bd de Sevigne
Tel : 02.99.38.20.77                              35700 RENNES
TAM TAM : 06.01.43.57.60                                FRANCE

-
For help on using this list (especially unsubscribing), send a message to
"[email-masked]" with one line of text: "help".