• C

Resident dll?

To my understanding, if you call a dll from a program it goes out of scope immediately after the call. Is there any way to keep variables inside the dll from loosing their data? Basically what I need to do is make 2 different calls to a dll. First, I want to make one call to set up some initialization parameters. This would probably be done just once. After this, however, I would need to make multiple calls to a routine in the dll to actually perform some operations using the initialization data. Since the initialization may be time consuming, I don't want to do it on every call to the second routine. As you can see, I would need the initialization data to remain in memory. Is this possible with a dll? Maybe it already works this way if you have a global in the dll?

Also, I'd like an example of how to use a dll from within c. Any example that sends parameters and gives a return will be OK. This part should be pretty easy but I'll give a couple extra points if it's done. I've created c dlls and called them from VB before but never called them from c. I assume it's similiar with some type of Declare statement but if it's not, please let me know.

BTW, MS Visual C++ 6.0 compiler with standard c code.
LVL 2
ggilmanAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

KangaRooCommented:
You could use a memory mapped file. The win32 online gives an example:

1.      Call the CreateFileMapping function to get a handle to a file-mapping object. The first process that loads the DLL creates the file-mapping object. Subsequent processes open a handle of the existing object. For more information, see Creating a File-Mapping Object.
      2.      Call the MapViewOfFile function to map a view into the virtual address space. This enables the process to access the shared memory. For more information, see Creating a File View.

 

// File:  DLLSHMEM.C.  
// The DLL entry-point function sets up shared memory using  
// a named file-mapping object.

#include <windows.h>
#include <memory.h>
 
#define SHMEMSIZE 4096
 
static LPVOID lpvMem = NULL; // pointer to shared memory
 
BOOL DllEntryPoint(HINSTANCE hinstDLL,  // DLL module handle
    DWORD fdwReason,                    // reason called
    LPVOID lpvReserved)                 // reserved
{
    HANDLE hMapObject = NULL;  // handle to file mapping

    BOOL fInit, fIgnore;
 
    switch (fdwReason)
    {
        // The DLL is loading due to process
        // initialization or a call to LoadLibrary.
 
          case DLL_PROCESS_ATTACH:
 
            // Create a named file mapping object.
 
            hMapObject = CreateFileMapping(
                (HANDLE) 0xFFFFFFFF, // use paging file
                NULL,                // no security attributes
                PAGE_READWRITE,      // read/write access

                0,                   // size: high 32-bits
                SHMEMSIZE,           // size: low 32-bits
                "dllmemfilemap");    // name of map object
            if (hMapObject == NULL)
                return FALSE;
 
            // The first process to attach initializes memory.
 
            fInit = (GetLastError() != ERROR_ALREADY_EXISTS);
 
            // Get a pointer to the file-mapped shared memory.
 
            lpvMem = MapViewOfFile(

                hMapObject,     // object to map view of
                FILE_MAP_WRITE, // read/write access
                0,              // high offset:  map from
                0,              // low offset:   beginning
                0);             // default: map entire file
            if (lpvMem == NULL)
                return FALSE;
 
            // Initialize memory if this is the first process.
 
            if (fInit)
                memset(lpvMem, '\0', SHMEMSIZE);

 
            break;
 
        // The attached process creates a new thread.
 
        case DLL_THREAD_ATTACH:
            break;
 
        // The thread of the attached process terminates.
 
        case DLL_THREAD_DETACH:
            break;
 
        // The DLL is unloading from a process due to
        // process termination or a call to FreeLibrary.
 
        case DLL_PROCESS_DETACH:
 
            // Unmap shared memory from the process's address space.

 
            fIgnore = UnmapViewOfFile(lpvMem);
 
            // Close the process's handle to the file-mapping object.
 
            fIgnore = CloseHandle(hMapObject);
 
            break;
 
        default:
          break;
     }
 
    return TRUE;
    UNREFERENCED_PARAMETER(hinstDLL);
    UNREFERENCED_PARAMETER(lpvReserved);
}
 
// SetSharedMem sets the contents of shared memory.
 
VOID SetSharedMem(LPTSTR lpszBuf)
{
    LPTSTR lpszTmp;

 
    // Get the address of the shared memory block.
 
    lpszTmp = (LPTSTR) lpvMem;
 
    // Copy the null-terminated string into shared memory.
 
    while (*lpszBuf)
        *lpszTmp++ = *lpszBuf++;
    *lpszTmp = '\0';
}
 
// GetSharedMem gets the contents of shared memory.
 
VOID GetSharedMem(LPTSTR lpszBuf, DWORD cchSize)
{
    LPTSTR lpszTmp;
 
    // Get the address of the shared memory block.
 
    lpszTmp = (LPTSTR) lpvMem;
 
    // Copy from shared memory into the caller's buffer.

 
    while (*lpszTmp && --cchSize)
        *lpszBuf++ = *lpszTmp++;
    *lpszBuf = '\0';
}
 

Note that the shared memory can be mapped to a different address in each process. For this reason, each process has its own instance of the lpvMem parameter, which is declared as a global variable so that it is available to all DLL functions. The example assumes that the DLL global data is not shared, so each process that loads the DLL has its own instance of lpvMem.
In this example, the shared memory is released when the last handle of the file-mapping object is closed. To create persistent shared memory, a DLL can create a detached process (see CreateProcess) when the DLL is first loaded. If this detached process uses the DLL and does not terminate, it has a handle of the file-mapping object that prevents the shared memory from being released. 1.      Call the CreateFileMapping function to get a handle to a file-mapping object. The first process that loads the DLL creates the file-mapping object. Subsequent processes open a handle of the existing object. For more information, see Creating a File-Mapping Object.
      2.      Call the MapViewOfFile function to map a view into the virtual address space. This enables the process to access the shared memory. For more information, see Creating a File View.

 

// File:  DLLSHMEM.C.  
// The DLL entry-point function sets up shared memory using  
// a named file-mapping object.

#include <windows.h>
#include <memory.h>
 
#define SHMEMSIZE 4096
 
static LPVOID lpvMem = NULL; // pointer to shared memory
 
BOOL DllEntryPoint(HINSTANCE hinstDLL,  // DLL module handle
    DWORD fdwReason,                    // reason called
    LPVOID lpvReserved)                 // reserved
{
    HANDLE hMapObject = NULL;  // handle to file mapping

    BOOL fInit, fIgnore;
 
    switch (fdwReason)
    {
        // The DLL is loading due to process
        // initialization or a call to LoadLibrary.
 
          case DLL_PROCESS_ATTACH:
 
            // Create a named file mapping object.
 
            hMapObject = CreateFileMapping(
                (HANDLE) 0xFFFFFFFF, // use paging file
                NULL,                // no security attributes
                PAGE_READWRITE,      // read/write access

                0,                   // size: high 32-bits
                SHMEMSIZE,           // size: low 32-bits
                "dllmemfilemap");    // name of map object
            if (hMapObject == NULL)
                return FALSE;
 
            // The first process to attach initializes memory.
 
            fInit = (GetLastError() != ERROR_ALREADY_EXISTS);
 
            // Get a pointer to the file-mapped shared memory.
 
            lpvMem = MapViewOfFile(

                hMapObject,     // object to map view of
                FILE_MAP_WRITE, // read/write access
                0,              // high offset:  map from
                0,              // low offset:   beginning
                0);             // default: map entire file
            if (lpvMem == NULL)
                return FALSE;
 
            // Initialize memory if this is the first process.
 
            if (fInit)
                memset(lpvMem, '\0', SHMEMSIZE);

 
            break;
 
        // The attached process creates a new thread.
 
        case DLL_THREAD_ATTACH:
            break;
 
        // The thread of the attached process terminates.
 
        case DLL_THREAD_DETACH:
            break;
 
        // The DLL is unloading from a process due to
        // process termination or a call to FreeLibrary.
 
        case DLL_PROCESS_DETACH:
 
            // Unmap shared memory from the process's address space.

 
            fIgnore = UnmapViewOfFile(lpvMem);
 
            // Close the process's handle to the file-mapping object.
 
            fIgnore = CloseHandle(hMapObject);
 
            break;
 
        default:
          break;
     }
 
    return TRUE;
    UNREFERENCED_PARAMETER(hinstDLL);
    UNREFERENCED_PARAMETER(lpvReserved);
}
 
// SetSharedMem sets the contents of shared memory.
 
VOID SetSharedMem(LPTSTR lpszBuf)
{
    LPTSTR lpszTmp;

 
    // Get the address of the shared memory block.
 
    lpszTmp = (LPTSTR) lpvMem;
 
    // Copy the null-terminated string into shared memory.
 
    while (*lpszBuf)
        *lpszTmp++ = *lpszBuf++;
    *lpszTmp = '\0';
}
 
// GetSharedMem gets the contents of shared memory.
 
VOID GetSharedMem(LPTSTR lpszBuf, DWORD cchSize)
{
    LPTSTR lpszTmp;
 
    // Get the address of the shared memory block.
 
    lpszTmp = (LPTSTR) lpvMem;
 
    // Copy from shared memory into the caller's buffer.

 
    while (*lpszTmp && --cchSize)
        *lpszBuf++ = *lpszTmp++;
    *lpszBuf = '\0';
}
 

Note that the shared memory can be mapped to a different address in each process. For this reason, each process has its own instance of the lpvMem parameter, which is declared as a global variable so that it is available to all DLL functions. The example assumes that the DLL global data is not shared, so each process that loads the DLL has its own instance of lpvMem.
In this example, the shared memory is released when the last handle of the file-mapping object is closed. To create persistent shared memory, a DLL can create a detached process (see CreateProcess) when the DLL is first loaded. If this detached process uses the DLL and does not terminate, it has a handle of the file-mapping object that prevents the shared memory from being released.
0
jkrCommented:
This can be done *MUCH* easier - simply call 'LoadLibrary( "yourdll.dll");' from your app to increase the library lock count. This will ensure that your DLL won't be unloaded before you call 'FreeLibrary()' again when you're finished... your DLL will stay in memory as long as you need it.

An 'elaborating' quote from the docs:

"Each process maintains a reference count for each loaded library module. This reference count is incremented
each time LoadLibrary is called, and decremented each time FreeLibrary is called. A DLL library module
loaded at process initialization due to load-time dynamic linking has a reference count of one. This count
incremented if the same module is loaded by a call to LoadLibrary. " 
0
ggilmanAuthor Commented:
KangaRoo, thanks but I already know how to do this. I actually use this elsewhere in my program to communicate between different processes. I thought about doing it like that but seems like there should be an easier way.

Jkr, this is more on track with what I'm looking for. Let me see if this works. As long as globals and statics within the dll remain active, it should be fine.

What about the example of how to declare the routine as being in a dll??
0
The Ultimate Tool Kit for Technolgy Solution Provi

Broken down into practical pointers and step-by-step instructions, the IT Service Excellence Tool Kit delivers expert advice for technology solution providers. Get your free copy for valuable how-to assets including sample agreements, checklists, flowcharts, and more!

jkrCommented:
An example on this is quite easy:

#ifdef _DLL // define this when building the DLL
#define __DYNLINK __declspec( dllexport)
#else
#define __DYNLINK __declspec( dllimport)
#endif

// the above #define will make
// sure that the appropriate
// import/export modifier is used
// when either building the DLL
// that exports the functions
// or the app that imports them

#ifdef __cplusplus // no C++ name mangling
extern "C"
{
#endif
__DYNLINK
int Myfunc( char* psz);
#ifdef __cplusplus
}
#endif

BTW: To import functions from a DLL, you'll have to use the import library (<dllname>.lib). Whe using VC++, you can make life easier by using

#ifdef _DLL // define this when building the DLL
#define __DYNLINK __declspec( dllexport)
#else
#define __DYNLINK __declspec( dllimport)
#pragma comment ( lib, "mydll.lib")
#endif
0
abancroftCommented:
If you statically link to the DLL, it will remain in the programs address space for the lifetime of the program (assuming you aren't using delay loading).
0
jkrCommented:
ggilman, are you still with us?
0
ggilmanAuthor Commented:
Still here. Just now getting a chance to try it out. I'll let you know.
0
ggilmanAuthor Commented:
jkr,
Tests seem to be fine. Actually the globals stay even if I don't use the "LoadLibrary". If I don't however, do you know if they will actually leave before my program exits? I guess before I was always assuming that the globals would always leave scope, that's why I posted this question.
At any rate, you have certainly been helpful. However, can you help with your example just a little more? I use the __DYNLINK in a header file to declare the functions. My code works fine as long as I don't need a return a variable. ( data = myFunc() ). I can send/recieve parameters just fine but I get compile errors if I set a variable equal to the return value of a function call. Don't remember the exact error message but I think it was "different levels of indirection."
Online help seems to be of little help in this area. I've seen __declspec( dllexport) in the help a million times but not once do they actually use it in an example other than the declaration itself.
0
jkrCommented:
>>If I don't however, do you know if they will actually leave
>>before my program exits?

If you use an import library, the answer is 'no'. The image loader will perform the calls to 'Load/FreeLibrary()' for you, so that you don't have to care.

'Different levels of indirection' usually means that you're trying to assign a the contents of a pointer of the same type to a pointer or vice versa.

The reason why you haven't seen '__declspec ( dlledxport)' in an actual example is probably because it's mostly 'heidden' by using such macros as in my example, as otherwise you'd have to use 2 different versions of the header file.

 __declspec( dllexport)
int MyFunc (int);

causes a function to be declared as 'exported' (e.g. when building a DLL), whereas

 __declspec( dllimport)
int MyFunc (int);

declares a function to be 'imported'. ( It's important for the linker to know about that). So using a macro as I described above just makes life easier ;-)

0
ggilmanAuthor Commented:
I understand the dllimport/export and agree that your macro certainly makes things easier but when I was testing, I basically had

__declspec( dllimport)
int MyFunc (int);

and then used MyFunc as such:

int i;
i = myFunc(1);

and got a compile error. By itself, myFunc(1) would work just fine and I could tell the dll was running.

I did what I was supposed to, right??
0
ggilmanAuthor Commented:
jkr, I'll give you a few extra points like I promised since you've been helping me but please check my last comment to see if I was messing anything up.
0
ggilmanAuthor Commented:
By the way I did add the points. That was my way of saying "I'm adding points" but looking at it now it looks more like I'm saying "I'm really going to do what I told you". Whatta moron. If you want, you can just post that last response as an answer and they're yours. Thanks again.
0
ggilmanAuthor Commented:
Actually looking back at your last comment, too, what do you mean by an import library? As in how would you define it as an import library rather than some other dll?
0
jkrCommented:
An import library is a small lib taht holds the 'call stubs' to the referenced DLL.

__declspec( dllimport)
int MyFunc (int);

//...

int i;
i = myFunc(1);

Should definitely work - what compile error did you get?

BTW - here's an 'real life' example:

#ifndef __H
#define __H


#ifdef __DLL
#define __DYNLINK   __declspec ( dllexport)
#else
#define __DYNLINK   __declspec ( dllimport)
#ifndef __DLL_NO_IMPLICIT_IMPORT
#pragma comment ( lib, "mydll.lib")
#endif  //  __DLL_NO_IMPLICIT_IMPORT
#endif  //  __DLL

#include <tchar.h>

#ifdef  __cplusplus
extern "C"
{
#endif

__DYNLINK
void    __cdecl DbgReportW          (   wchar_t*    pszFormat,  ...);

__DYNLINK
void    __cdecl DbgReportA          (   char*       pszFormat,  ...);

//...

#ifdef  __cplusplus
}
#endif


#endif
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
ggilmanAuthor Commented:
With:

__declspec( dllimport)
int MyFunc (int);

//...

int i;
i = myFunc(1);


my error was the "different levels of indirection" as I mentioned earlier. I thought it should have worked too but it didn't. I don't really have time today but will certainly try it again tomorrow.
0
ggilmanAuthor Commented:
jkr, thanks again. I'm going to accept your comment since you have certainly helped. I'm guessing that my compile error was something silly and I'll look at it later.
0
jkrCommented:
Aaah - if it wasn't a typo, see the functions' names (uppercase/lowercase ;-)

__declspec( dllimport)
int MyFunc (int);

//...

int i;
i = myFunc(1); // <-- 'MyFunc()'?

0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C

From novice to tech pro — start learning today.