- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Currently I have a few fortran functions which I call from C++ and a few callback functions which call C++ funtions from Fortran. Let's say I communicate both ways. I use function pointers, pointers to structs of array and so on. Practically every combination.
Under 32-bit every function is declared as "Stdcall" so both sides know what to do since the calling convention is the same. Everything works fine.
Now I want everything to work under 64-bit as well. But I wonder if that is possible easily. Has anyone done such a thing before?
I use the Intel Visual Fortran Compiler 12.1 along with the Microsoft Visual C++ 2010. As you can read on Wikipedia (http://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions) the calling conventions between Intel and Microsoft are a little bit different. So does this has an impact on my application?
The first problem I encountered had to do with struct by value parameters. Under the debugger I saw that the pointer needed to be dereferenced to be valid. The Fortran variable just contained the address to the variable instead of the variable itself.
Since I couldn't solve this problem with C_F_POINTER I had to pass every variable seperatly. (If you are interested, I can also post a code sample.)
So the main question is: What other troubles can/will I run into?
Is it generally not recommended to do Interop between Microsoft and Intel? Should I use the Intel C++ compiler instead?
Under 32-bit every function is declared as "Stdcall" so both sides know what to do since the calling convention is the same. Everything works fine.
Now I want everything to work under 64-bit as well. But I wonder if that is possible easily. Has anyone done such a thing before?
I use the Intel Visual Fortran Compiler 12.1 along with the Microsoft Visual C++ 2010. As you can read on Wikipedia (http://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions) the calling conventions between Intel and Microsoft are a little bit different. So does this has an impact on my application?
The first problem I encountered had to do with struct by value parameters. Under the debugger I saw that the pointer needed to be dereferenced to be valid. The Fortran variable just contained the address to the variable instead of the variable itself.
Since I couldn't solve this problem with C_F_POINTER I had to pass every variable seperatly. (If you are interested, I can also post a code sample.)
So the main question is: What other troubles can/will I run into?
Is it generally not recommended to do Interop between Microsoft and Intel? Should I use the Intel C++ compiler instead?
Link Copied
15 Replies
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
We consider Microsoft Visual C++ to be the "companion C processor" for Intel Visual Fortran. The C interoperability features are intended to match what MSVC does. (And Intel C++ is supposed to be compatible as well.)
Your use of STDCALL takes you out of the world of the Fortran C interoperability features and may have side effects you have not considered. Is there a reason not to use the default calling conventions?
Please show a small example where you think the Fortran compiler is not behaving properly.
Your use of STDCALL takes you out of the world of the Fortran C interoperability features and may have side effects you have not considered. Is there a reason not to use the default calling conventions?
Please show a small example where you think the Fortran compiler is not behaving properly.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
It's amusing that you have problems porting to 64-bit Windows because all compilers use the same calling convention on that platform. Usually going from 64- to 32-bit is hard; the reverse should be easy. Are you perhaps forgetting to put the VALUE attribute in the Fortran side of the interface? If a C compiler is told to pass a struct by value that doesn't obey the calling convention rules for passing by value, it creates a call by reference. A Fortraninterface that specifies a call by reference would work correctly in that case. A Fortran interface that specifies call by value might, one hopes, even so call by reference because it is trying to be compatible withits C compiler. When the platform is upgraded to 64-bit the rules allow more structs to be passed by value so Fortran code that specifies call by reference will break on structs that are permitted under the more inclusive rules. It looks like VALUE is the default for STDCALL but perhaps ifort ignores the STDCALL attribute on 64-bit platforms in this instance. Try giving the struct argument the VALUE attribute and check via the debugger that ifort does the right thing on both 32- and 64-bit Windows.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
On 64-bit, ifort accepts the STDCALL attribute and applies the value and lowercase name aspects, and ignores the rest.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Quoting stst
...So the main question is: What other troubles can/will I run into?...
Do you want to calla function in a32-bit DLL from a 64-bit application,or opposite?
If you considerboth modulesas 64-bit than it should work.
A call from a 32-bit address space to a 64-bitaddress space is not allowed on Windows platformsand
special techniques,for examplea middle level component, have to be used. The same apples to calls from
a 64-bit address space to a 32-bit address space.
For information on calling conventions for x64 processors please take a look at a"Calling Convention" article on MSDN.
Best regards,
Sergey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Your use of STDCALL takes you out of the world of the Fortran C interoperability features and may have side effects you have not considered. Is there a reason not to use the default calling conventions?
No, there is no real reason to not use the Default conventions (which is CDECL i think). We had to use the CVF calling convention, because of legacy modules, but this reason is now gone (I think).
But why is it "bad" to use STDCALL? What side effects do you mean?
As far as I know the major difference is "Who does the stack cleanup". So as far as both side use the same calling convention it should be OK, or not?
all compilers use the same calling convention on that platform.
Are you sure?
It looks like VALUE is the default for STDCALL but perhaps ifort ignores the STDCALL attribute on 64-bit platforms in this instance.
I generally use the calling convention which always passes parameters by reference. But I also suspect that under 64-bit the STDCALL attribute is ignored. Because under 64-bit there is just one calling convention. The question is: How do I get the compiler to behave the same way under 64 and 32-bit?
Do you want to calla function in a32-bit DLL from a 64-bit application,or opposite?
No, that's not possible anyway. You can't even load the DLL. Everything is in 64-bit.
Please show a small example where you think the Fortran compiler is not behaving properly.
I really can't say if the misbehaviour happens on the C++ or on the Fortran side. The only thing you will see is that it works under 32-bit but not under 64-bit.
The calling convention I used for this is example is "Default".
As far as I know the alignment should be OK, because the variables scale with the platform.
Here is a sample code:
[bash] subroutine InitLibrary( strAppPath ) !DEC$ ATTRIBUTES DLLEXPORT :: INITLIBRARY use, intrinsic :: ISO_C_BINDING character(*, C_CHAR), intent(in) :: strAppPath print *, strAppPath end subroutine InitLibrary
------------ C++ ---------------------- struct fstring { char* str; size_t len; }; extern "C" void INITLIBRARY( fstring strAppPath ); int _tmain(int argc, _TCHAR* argv[]) { fstring path; path.str = strdup("Hello World!"); path.len = strlen(path.str); INITLIBRARY( path ); return 0; } [/bash]
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
It is quite easy to see what is happening by looking at the disassembly of the .OBJ files. Here are the relevant lines:
[bash] 000000000000006B: 48 89 11 mov qword ptr [rcx],rdx 000000000000006E: 48 89 41 08 mov qword ptr [rcx+8],rax 0000000000000072: E8 00 00 00 00 call INITLIBRARY[/bash]
The string pointer is inserted into the memory addressed by RCX, and the string length (in RAX) is inserted into memory 8 bytes higher. The subroutine is called with a single argument, namely a pointer in RCX to the structure, and this is inconsistent with the calling convention for Fortran strings.
You are assuming that a structure argument will be passed by value, even when the structure is longer than the CPU register. On top of that, your application involves mixing languages and calling conventions.
Here is my C version of your main program; it works and prints the string, when I use the 64-bit versions of IFort 12.1 and either MSC 16.0 or ICl 12.1.
[cpp]#include
extern void INITLIBRARY( char *str, size_t len );
int main()
{
char *path; int len;
path = strdup("Hello World!");
len = strlen(path);
INITLIBRARY( path, len );
return 0;
}
[/cpp]
If you are going to be a heavy user of mixed C and Fortran programming, I recommend that you use the C-interoperability features of Fortran 2003, which are well supported by VC/IFort and Icl/IFort.
[bash] 000000000000006B: 48 89 11 mov qword ptr [rcx],rdx 000000000000006E: 48 89 41 08 mov qword ptr [rcx+8],rax 0000000000000072: E8 00 00 00 00 call INITLIBRARY[/bash]
The string pointer is inserted into the memory addressed by RCX, and the string length (in RAX) is inserted into memory 8 bytes higher. The subroutine is called with a single argument, namely a pointer in RCX to the structure, and this is inconsistent with the calling convention for Fortran strings.
You are assuming that a structure argument will be passed by value, even when the structure is longer than the CPU register. On top of that, your application involves mixing languages and calling conventions.
Here is my C version of your main program; it works and prints the string, when I use the 64-bit versions of IFort 12.1 and either MSC 16.0 or ICl 12.1.
[cpp]#include
If you are going to be a heavy user of mixed C and Fortran programming, I recommend that you use the C-interoperability features of Fortran 2003, which are well supported by VC/IFort and Icl/IFort.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you, your post was very helpful.
The solution you came up with is basically the same as mine. That's what I meant when I wrote "I had to pass every variable seperatly". So I think I am on the right path.
I understand what you wrote about the registers. But how do I ensure that the registers are matching between C++ and Fortran. For example: What is the register for the fifth real (double) parameter? Shouldn't the calling convention say which parameter should go in which register?
This further leads me to the conclusion that the calling convention between Microsoft and Intel are different under 64-bit, which is an unsolvable problem.
I recommend that you use the C-interoperability features of Fortran 2003
What features do you mean in particular? Can you provide an example how to properly do C-Interop the F2003 way?
The solution you came up with is basically the same as mine. That's what I meant when I wrote "I had to pass every variable seperatly". So I think I am on the right path.
I understand what you wrote about the registers. But how do I ensure that the registers are matching between C++ and Fortran. For example: What is the register for the fifth real (double) parameter? Shouldn't the calling convention say which parameter should go in which register?
This further leads me to the conclusion that the calling convention between Microsoft and Intel are different under 64-bit, which is an unsolvable problem.
I recommend that you use the C-interoperability features of Fortran 2003
What features do you mean in particular? Can you provide an example how to properly do C-Interop the F2003 way?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You do not need to be concerned about what goes in each register - the compilers will handle that for you. Just make sure that you are properly indicating things that are passed by value or by reference. Using the C interoperability features is the best way to ensure that Fortran treats arguments the same way that C does.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Here is a link to a description of the Microsoft X64 ABI. However, you do not need to descend to the machine instruction level if you use the F2003 C-interoperability route.
The C-interoperability features consist of derived types, intrinsic subroutines, functions and modules to facilitate mixing C and Fortran 2003, and are described in the Fortran documentation. On every platform, a Fortran compiler that is equipped with C-Interoperability specifies a "companion" C compiler.
Therefore, writing code to the C-interoperability specifications facilitates portability, and only the compiler writers have to be concerned with machine level conventions and ABIs.
The C-interoperability features consist of derived types, intrinsic subroutines, functions and modules to facilitate mixing C and Fortran 2003, and are described in the Fortran documentation. On every platform, a Fortran compiler that is equipped with C-Interoperability specifies a "companion" C compiler.
Therefore, writing code to the C-interoperability specifications facilitates portability, and only the compiler writers have to be concerned with machine level conventions and ABIs.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
It seems like you were thinking about the low-level details of how that path struct would be passed by your C program and got them wrong. In addition to the official Microsoft documentation Agner Fog has some very useful resources on his web page. As can be seen, the C program should pass the struct by reference in64-bit Windows, while in 32-bit Windows a copy of the struct was placed on the stack.
C interoperability doesn't work if any of the dummy arguments is of type CHARACTER with LEN > 1 because Fortran needs the hidden LEN argument passed in case the dummy argument was declared with LEN = * and Fortran compilers don't always agree with each other about how that should be done. If you want to pass a character string to Fortran from C using C interoperability features you have to do a little song and dance to make it go.
[bash]! sub.f90 subroutine sub(string, length) bind(C,name='Sub') use, intrinsic :: ISO_C_BINDING implicit none character(1,C_CHAR), target :: string(*) integer(C_SIZE_T), value :: length character(length,C_CHAR), pointer :: fptr type(C_PTR) cptr cptr = C_LOC(string(1)) call C_F_POINTER(cptr,fptr) print *, fptr end subroutine sub [/bash]
[bash]// main.c #include
extern void Sub(char *str, size_t len);
int main()
{
char *str;
size_t len;
str = strdup("Hello World!");
len = strlen(str);
Sub(str, len);
return 0;
}
[/bash]
C interoperability doesn't work if any of the dummy arguments is of type CHARACTER with LEN > 1 because Fortran needs the hidden LEN argument passed in case the dummy argument was declared with LEN = * and Fortran compilers don't always agree with each other about how that should be done. If you want to pass a character string to Fortran from C using C interoperability features you have to do a little song and dance to make it go.
[bash]! sub.f90 subroutine sub(string, length) bind(C,name='Sub') use, intrinsic :: ISO_C_BINDING implicit none character(1,C_CHAR), target :: string(*) integer(C_SIZE_T), value :: length character(length,C_CHAR), pointer :: fptr type(C_PTR) cptr cptr = C_LOC(string(1)) call C_F_POINTER(cptr,fptr) print *, fptr end subroutine sub [/bash]
[bash]// main.c #include
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You do not need to be concerned about what goes in each register - the
compilers will handle that for you. Just make sure that you are properly
indicating things that are passed by value or by reference.
So you are saying that the problem I encountered has nothing to do with registers. It's a matter of passing the parameters by value or by reference.
If that's true my biggest worry is resolved.
If you want to pass a character string to Fortran from C using C interoperability features you have to do a little song and dance to make it go.
Ok, now I got really curious on how to pass a string the right way. At the moment I do it the same way as mecej4 suggested it.
[bash] subroutine InitLibrary( strAppPath ) !DEC$ ATTRIBUTES DLLEXPORT :: INITLIBRARY use, intrinsic :: ISO_C_BINDING character(*, C_CHAR), intent(in) :: strAppPath print *, strAppPath end subroutine InitLibrary ------------------- C++ -------------------------- extern "C" void INITLIBRARY( char* strAppPath, size_t len ); int _tmain(int argc, _TCHAR* argv[]) { char* str = strdup("Hello World!"); size_t len = strlen(str); INITLIBRARY( str, len ); return 0; }[/bash]
What is the disadvantage with my method?
Is it really necessary to do it the same way as you did?
Or would something like this also be okay?
[bash] subroutine InitLibrary( cptr, length ) !DEC$ ATTRIBUTES DLLEXPORT :: INITLIBRARY use, intrinsic :: ISO_C_BINDING type(C_PTR) :: cptr integer(C_SIZE_T), value :: length character(length, C_CHAR) :: strAppPath call C_F_POINTER(cptr, strAppPath) print *, strAppPath end subroutine InitLibrary --------------- C++ ------------------- extern "C" void INITLIBRARY( char** strAppPath, size_t len ); int _tmain(int argc, _TCHAR* argv[]) { char* str = strdup("Hello World!"); size_t len = strlen(str); INITLIBRARY( &str, len ); return 0; } [/bash]
Altough it's a dumb question, but what does Bind(C)? I know it should specify that a procedure is interoperable, but if I add this to my subroutine it doesn't get exported anymore, so it has quite the opposite effect.
So you are saying that the problem I encountered has nothing to do with registers. It's a matter of passing the parameters by value or by reference.
If that's true my biggest worry is resolved.
If you want to pass a character string to Fortran from C using C interoperability features you have to do a little song and dance to make it go.
Ok, now I got really curious on how to pass a string the right way. At the moment I do it the same way as mecej4 suggested it.
[bash] subroutine InitLibrary( strAppPath ) !DEC$ ATTRIBUTES DLLEXPORT :: INITLIBRARY use, intrinsic :: ISO_C_BINDING character(*, C_CHAR), intent(in) :: strAppPath print *, strAppPath end subroutine InitLibrary ------------------- C++ -------------------------- extern "C" void INITLIBRARY( char* strAppPath, size_t len ); int _tmain(int argc, _TCHAR* argv[]) { char* str = strdup("Hello World!"); size_t len = strlen(str); INITLIBRARY( str, len ); return 0; }[/bash]
What is the disadvantage with my method?
Is it really necessary to do it the same way as you did?
Or would something like this also be okay?
[bash] subroutine InitLibrary( cptr, length ) !DEC$ ATTRIBUTES DLLEXPORT :: INITLIBRARY use, intrinsic :: ISO_C_BINDING type(C_PTR) :: cptr integer(C_SIZE_T), value :: length character(length, C_CHAR) :: strAppPath call C_F_POINTER(cptr, strAppPath) print *, strAppPath end subroutine InitLibrary --------------- C++ ------------------- extern "C" void INITLIBRARY( char** strAppPath, size_t len ); int _tmain(int argc, _TCHAR* argv[]) { char* str = strdup("Hello World!"); size_t len = strlen(str); INITLIBRARY( &str, len ); return 0; } [/bash]
Altough it's a dumb question, but what does Bind(C)? I know it should specify that a procedure is interoperable, but if I add this to my subroutine it doesn't get exported anymore, so it has quite the opposite effect.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
BIND(C) specifies that the procedure is "interoperable", in the words of the standard. This then requires that all of the arguments be interoperable. As was noted, character arguments with length greater than 1 are not interoperable - the standard says that an interoperable procedure has no hidden arguments. BIND(C) also downcases the routine name (you can override this with BIND(C,NAME='xxxxx'), so that may explain your belief that the routine was not exported.
The advantage of using BIND(C) is that it forces the Fortran compiler to play by the same rules the C compiler does. This has special signfiicance when passing structures by value and in how functions return values.
The advantage of using BIND(C) is that it forces the Fortran compiler to play by the same rules the C compiler does. This has special signfiicance when passing structures by value and in how functions return values.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
> What is the disadvantage with my method?
That is easily answered (it is not portable), but keeping in mind a little bit of the historical perspective may help more.
In 16-bit MSDOS, and Win32 later, there were many calling conventions, sometimes with names such as "fastcall" which had more than one definition. Among these, the STDCALL used by MS Fortran, Digital/Compaq Visual Fortran allowed one type of mismatch between caller and callee to be detected at link time: a mismatch caused by missing/extra arguments, or wrong types, or both. If the mismatch caused the combined byte count of the argument list to disagree, the mismatch was caught at link time.
The issue of whether popping arguments off the stack is done by the caller or callee is another matter and is not relevant here.
When AMD designed the X64 architecture, a standard for subprogram linkage was developed: the AMD/AT&T ABI . Later, Microsoft developed its own variant. Neither of these includes STDCALL. Therefore, STDCALL becomes an issue in 64-bit code when older source code containing STDCALL as an attribute is ported to a 64 bit platform. A better solution is to port the code thoroughly by removing all vestiges of STDCALL in the sources.
It is possible to have a match of the byte count and still have a mismatched call. For example, arguments could be in improper order. Therefore, while STDCALL may have been better than no argument checking, it is limited in its usefulness. The transition from Fortran 77 to 9X and 200X made much better means of checking available. Declaring called procedures in modules and inserging module USE statements in callers enables complete checking to be done at compile time.
The C-interoperability features of Fortran 2003 enable most but not all interlanguage calls to be made in a portable way. However, as Repeat Offender pointed out, Fortran CHARACTER variables of length different from 1 are not covered by interoperability. There are other features in Fortran 2003 (e.g., subroutines with OPTIONAL arguments) which are also not covered by the interoperability features.
That is easily answered (it is not portable), but keeping in mind a little bit of the historical perspective may help more.
In 16-bit MSDOS, and Win32 later, there were many calling conventions, sometimes with names such as "fastcall" which had more than one definition. Among these, the STDCALL used by MS Fortran, Digital/Compaq Visual Fortran allowed one type of mismatch between caller and callee to be detected at link time: a mismatch caused by missing/extra arguments, or wrong types, or both. If the mismatch caused the combined byte count of the argument list to disagree, the mismatch was caught at link time.
The issue of whether popping arguments off the stack is done by the caller or callee is another matter and is not relevant here.
When AMD designed the X64 architecture, a standard for subprogram linkage was developed: the AMD/AT&T ABI . Later, Microsoft developed its own variant. Neither of these includes STDCALL. Therefore, STDCALL becomes an issue in 64-bit code when older source code containing STDCALL as an attribute is ported to a 64 bit platform. A better solution is to port the code thoroughly by removing all vestiges of STDCALL in the sources.
It is possible to have a match of the byte count and still have a mismatched call. For example, arguments could be in improper order. Therefore, while STDCALL may have been better than no argument checking, it is limited in its usefulness. The transition from Fortran 77 to 9X and 200X made much better means of checking available. Declaring called procedures in modules and inserging module USE statements in callers enables complete checking to be done at compile time.
The C-interoperability features of Fortran 2003 enable most but not all interlanguage calls to be made in a portable way. However, as Repeat Offender pointed out, Fortran CHARACTER variables of length different from 1 are not covered by interoperability. There are other features in Fortran 2003 (e.g., subroutines with OPTIONAL arguments) which are also not covered by the interoperability features.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
You might want to consider taking more control of the interface issues on the Fortran side. In the first of your two examples above, you have character dummy argument with len /= 1, so you can't use C interoperability. You could still adjust the name of the function with !DEC$ ATTRIBUTES ALIAS: 'InitLibrary' :: InitLibrary.
Also you could change the interface to something a little more transparent like:
subroutine InitLibrary(strAppPath, length)
!DEC$ ATTRIBUTES REFERENCE :: strAppPath
integer(C_SIZE_T), value :: length
character(length,C_CHAR) strAppPath
That way you have forced the (no longer) hidden length parameter to be in the order you want so that if you compiled with the switch /iface:cvf, for example, such parameters would no longer jump around in the argument list.
In your second example, you could use the BIND attribute torename your subroutine in a standard way:
subroutine InitLibrary(cptr, length) bind(C,name='InitLibrary')
You could also set one less level of indirection by giving cptr the VALUE attribute:
type(C_PTR), value :: cptr
I think you also need to declare strAppPath as a Fortran pointer:
character(length, C_CHAR), pointer :: strAppPath
In either case a couple of modifications would need to be made to the C program, but when you are done I think in both cases your code doesn't rely on low-level arcana about how the Fortran compiler interfaces with other languagesand so is clearer and a bit more robust.
Also you could change the interface to something a little more transparent like:
subroutine InitLibrary(strAppPath, length)
!DEC$ ATTRIBUTES REFERENCE :: strAppPath
integer(C_SIZE_T), value :: length
character(length,C_CHAR) strAppPath
That way you have forced the (no longer) hidden length parameter to be in the order you want so that if you compiled with the switch /iface:cvf, for example, such parameters would no longer jump around in the argument list.
In your second example, you could use the BIND attribute torename your subroutine in a standard way:
subroutine InitLibrary(cptr, length) bind(C,name='InitLibrary')
You could also set one less level of indirection by giving cptr the VALUE attribute:
type(C_PTR), value :: cptr
I think you also need to declare strAppPath as a Fortran pointer:
character(length, C_CHAR), pointer :: strAppPath
In either case a couple of modifications would need to be made to the C program, but when you are done I think in both cases your code doesn't rely on low-level arcana about how the Fortran compiler interfaces with other languagesand so is clearer and a bit more robust.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Ok, so here are the points I will change:
- Use CDECL on 32-bit instead of STDCALL
- Use bind(C)
- Don't use struct by value parameters
- Change the character dummy arguments with length /= 1 (like in my 2nd example with the two things pointed out by Repeat Offender)

Reply
Topic Options
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page