Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations wOOdy-Soft on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Creating a DLL that VFP can access 1

Status
Not open for further replies.

chpicker

Programmer
Apr 10, 2001
1,316
Hey guys,

I decided that I wanted to code a function in C++ rather than VFP because of the speed that C++ can give me. The function runs in 1/10 the time. However, I now want to put this function into a DLL and call it from VFP just like I do the win32api functions. It's not working.

I created a DLL project in Visual C++ 6.0 and let the wizard create one that exports some symbols so I could see how it worked. I compiled it into a DLL file, using the pre-created function as-is. When I DECLARE the function in VFP, I get no error, but I get the "Cannot find the entry point" error when I try to call the function. And yes, I am absolutely sure I got the case correct in both the function declaration and the function call.

I created a simple C++ program that calls the function, and it works just fine. Is there something special you have to do to a DLL file for VFP to recognize it?

On a side note, as an experiment I also tried exporting the functions "DllRegisterServer" and "DllUnregisterServer", but regsvr32 still complains that it can't find those entry points, either. That could be because I don't know the parameters that are passed, though.

I condensed the DLL code into a single file that compiles correctly for me. Here it is:
Code:
#define WIN32_LEAN_AND_MEAN  // Exclude rarely-used stuff from Windows headers

#include <windows.h>

#define MYDLL_API __declspec(dllexport)

MYDLL_API int fnMydll(void);

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
      )
{
    return TRUE;
}

MYDLL_API int fnMydll(void)
{
 return 42;
}
I created an empty Win32 Console Application and used this code to access the DLL. It prints 42 on the screen, as you would expect:
Code:
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <iostream.h>

#define MYDLL_API __declspec(dllimport)
MYDLL_API int fnMydll(void);

int main(int argc, char* argv[])
{
 int answer=fnMydll();
 cout << answer << endl;
 return 0;
}
What changes do I need to make to my DLL project so I can call the function from VFP and get 42? I find it hard to believe that all of the standard Windows API functions are compiled with the VFP API, which is why I feel there's something simple I can do to fix this.

On another side note, I DID create an FLL using the VFP API and got the "42" function to work using SET LIBRARY. I don't want to do it this way, though. I want this DLL to be accessible to other languages.

Ian
 
It's the C++ decorations that are getting in your way most likely...

Try this line in place of your function declaration...

extern "C" MYDLL_API int fnMydll(void);


boyd.gif

 
As an alternative to this there is another way that I should probably note for clarity sake. You can use a .DEF file rather than __declspec(dllexport). An added benefit is that you'll get to define what function names are visible to VFP (or any language that is going to use the DLL) and thus your names won't be decorated/mangled. However, if you have no need to use polymorphic functions or member functions then the extern "C" clause will be the easier softer route which is why I included it up above.

OK, so let's look at the final option... which is using the decorated name in VFP. (The C++ compiler mangles the names to ensure uniqueness and the algorithms for doing this are different between programming languages and versions). You'll first need to find the decorated name that the compiler generated. I like going to the command prompt and doing a dumpbin...

DumpBin /Exports MyDll.dll

...this will give you the fully mangled name of the function that is being exported (assuming we allowed the compiler to mangle it)... let's say it is "?fnMydll@@YAHXZ"... So, in VFP in order to call it we would need to do the following:

DECLARE integer ?fnMydll@@YAHXZ in MyDll.dll as 'MyFunc'
?MyFunc()

...there's more that could be said and gone over here, but for the moment I think that will do given this is a VFP forum and not a C++ forum. Just a quick note, I went ahead and generated a quick DLL in Visual C++ using extern "C" and it worked fine... so I think you're all set.

boyd.gif

 
Hi Craig, hi chpicker,

mangling the names should be the problem, true!
I use a free compiler (Lcc compiler) ( and it's an option of the linker to "do not include underscores in dll exports", otherwise it puts an underscore before every function name...

So the linker option (in project configuration) set and this code works for me:

Code:
#include <windows.h>

/*------------------------------------------------------------------------
 Procedure:     LibMain ID:1
 Purpose:       Dll entry point.Called when a dll is loaded or
                unloaded by a process, and when new threads are
                created or destroyed.
 Input:         hDllInst: Instance handle of the dll
                fdwReason: event: attach/detach
                lpvReserved: not used
 Output:        The return value is used only when the fdwReason is
                DLL_PROCESS_ATTACH. True means that the dll has
                sucesfully loaded, False means that the dll is unable
                to initialize and should be unloaded immediately.
 Errors:
------------------------------------------------------------------------*/

BOOL WINAPI __declspec(dllexport) LibMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved)
{
    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            // The DLL is being loaded for the first time by a
            //given process. Perform per-process initialization
            // here.  If the initialization is successful, return TRUE;
            // if unsuccessful, return FALSE.

            break;
        case DLL_PROCESS_DETACH:
            // The DLL is being unloaded by a given process.
            // Do any per-process clean up here, such as undoing 
            // what was done in DLL_PROCESS_ATTACH.  The return
            // value is ignored.


            break;
        case DLL_THREAD_ATTACH:
            // A thread is being created in a process that has
            // already loaded this DLL.  Perform any per-thread
            // initialization here. The return value is ignored.

            break;
        case DLL_THREAD_DETACH:
            // A thread is exiting cleanly in a process that
            // has already loaded this DLL. Perform any per-thread clean 
            // up here. The return value is ignored.

            break;
    }
    return TRUE;
}

int __declspec(dllexport) the_answer_is()
{
	return 42;
}

then in vfp:
Code:
 declare INTEGER the_answer_is in my.dll
 ? the_answer_is()

To find out how names are mangled by the compiler/linker open the dll in a hexeditor, the exported names can normally be found at the end of the file...

Bye, Olaf.
 
Hey Craig,

It's funny, I did notice the mangled names when I used a Dependency Viewer on my DLL. I thought that might have something to do with it, but the fact that my C++ program could still access gave me pause. Maybe the .lib file that you have to link it to addresses this in VC++.

Thanks for the info, that extern "C" identifier will be all I need. I'm not exporting classes, just the single function. However, I'll keep the .DEF info in mind, as well, in case I try to really confuse myself with MSVC. *grins*

PS...I tried to give you a star, but my browser isn't cooperating. I'll try to do it again later. You've been a great help!

Ian
 
Hi,

I experimented a little further, made some string search function (boyer-moore algorithm), which in special cases did even better than AT(), but I stumbbled about a curiosity: even if I pass a string by reference as char *, the time for a call raises with the string length!?

C example:
Code:
int __declspec(dllexport) testcall(char *cString)
{
    return 0;
}
Fox test code:
Code:
declare ... in dll...

lcString = space(1000000)
to = seconds()
for i=1 to 100
   testcall(@lcString)
endfor i 
?seconds()-t0

compare this time to a loop calling a similar foxpro function:
Code:
lcString = space(1000000)
to = seconds()
for i=1 to 100
   foxtestcall(@lcString)
endfor i 
?seconds()-t0

function foxtestcall()
   lparameters tcString
   return 0
endfunc

I get about 4 seconds (dll calls) to 0 (fox calls) for the million spaces string. Time needed get's drastically reduced if repeating this with a shorter string. Why? calling by reference means passing an adress, no more no less. That shouldn't depend on the string length, should it?

Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top