Creating Windows DLLs

I am trying to create a dll for some of my VB programs that will act like an API call.  By that, I mean you can declare the function in VB, call it from your VB program, get the return value, and move on.  When you call the dll, it loads, and when the call is done, it unloads.  I have successfully exported the functions, and linked it to VB.  The function works wonderfully THE FIRST TIME it is called, but crashes the VB program any subsequent time.  I know what is happening, but don't know how to solve it.  First, the code:

Syncro.h

#include <windows.h>

#ifndef WINAPI
#define WINAPI      __stdcall
#endif

#ifndef DLLEXPORT
#define DLLEXPORT      __declspec(dllexport)
#endif

#define SUPERVISOR                  ((long) 0x0000001)
#define DPWRVIEW                  ((long) 0x0000002)
#define WREQVIEW                  ((long) 0x0000003)
#define DPWRPATH                  ((long) 0x0000004)
#define INIPATH                        ((long) 0x0000005)

extern "C" DLLEXPORT long WINAPI GetUserInfo(long lpTypeOfData, BSTR lpUserID, BSTR lpBuffer);
BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved );

Syncro.cpp

#include "Syncro.h"

BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved )
{
    switch( fdwReason )
    {
        case DLL_PROCESS_ATTACH:
            break;

        case DLL_THREAD_ATTACH:
            break;

        case DLL_THREAD_DETACH:
            break;

        case DLL_PROCESS_DETACH:
            break;
    }
    return FALSE;
}

extern "C" DLLEXPORT long WINAPI GetUserInfo(long lpTypeOfData, BSTR lpUserID, BSTR lpBuffer)
{
      HKEY phKey;
      LPSTR lpKey = "SOFTWARE\\Syncro\\";
      LPTSTR lpValue;
      LPDWORD lpReserved = NULL;
      DWORD lpType;
      BYTE lpData[255];
      DWORD lpcbBuffer = 0;
      lpType = REG_SZ;
      long err;
      BSTR lpRetVal;
      LPSTR UserID = (LPSTR)lpUserID;
      LPSTR lpDataType;

      switch (lpTypeOfData)
      {
      case SUPERVISOR:
            lpDataType = "Supervisor";
            break;
      case DPWRVIEW:
            lpDataType = "DpwrView";
            break;
      case WREQVIEW:
            lpDataType = "WreqView";
      case DPWRPATH:
            lpDataType = "DataPath";
      case INIPATH:
            lpDataType = "IniPath";
      };

      strcat(lpKey, (LPSTR)lpUserID);
      err = RegOpenKey(HKEY_LOCAL_MACHINE, lpKey, &phKey);

      err = RegQueryValueEx(phKey, lpDataType, lpReserved, &lpType, &lpData[0], &lpcbBuffer);
      err = RegQueryValueEx(phKey, lpDataType, lpReserved, &lpType, &lpData[0], &lpcbBuffer);

      RegCloseKey(phKey);

      SysReAllocString(&lpBuffer, (BSTR)lpData);
      lpcbBuffer -= 1;
      return lpcbBuffer;
}

The VB Project:

Declare Function GetUserInfo Lib "e:\source code\syncro40\syncro40\debug\syncro40.dll" (ByVal lpType As Long, ByVal lpDept As String, ByVal lpBuffer As String) As Long

Private Sub Command1_Click()
Dim lpDept As String
Dim lpBuffer As String
lpDept = "jap"
lpBuffer = Space(255)
Dim retval As String

Dim l As Long
l = GetUserInfo(1, lpDept, lpBuffer)
retval = Left(lpBuffer, l)
DoEvents
Form1.CurrentX = 10
Form1.CurrentY = 10
Form1.Print retval

End Sub

Now, I have traced the program execution, by stepping through it in the debugger.  What I have come up with is that the dll is loading properly through DllMain and initializing properly.  The function is then being called properly, and returning the correct values.  When it returns to VB, the message box displays the correct values, and the program moves on.  HOWEVER, the dll does not terminate and unload.  Instead, it stays linked to the VB program.  This is what is causing the subsequent calls to crash.  I know this from the second time I stepped through it.  lpKey and several other variables were still set to the old values.  Then when I used strcat, it appended the new call values onto the old ones, which of course caused the RegOpenKey and RegQueryValue functions to fail and return arbitrary values for RetVal and lpBuffer.

I have tried creating a critical section in the dll, but all this accomplishes is having something else floating around still loaded.  I have also tried both TRUE and FALSE as return values for DllMain.  Can anyone please tell me what I'm doing wrong that is making the dll stay loaded?

Thank you for your time.
LVL 5
dirtdartAsked:
Who is Participating?
 
fasterConnect With a Mentor Commented:
As the others have pointed out, the usual way VB handling DLL is never unload a dll until the VB program terminates.  This is not a waste, because of two reasons:

1. Loading and unloading for every API is expensive.
2. For more complex DLLs (most DLLs are), some variables have to be kept.  For example, when you can API1, it may set a global variable to some value which will be needed subsequently in API2.  If the DLL is unloaded, then keeping status will be impossible (well, you can use files or registry...)

When designing a DLL, it is important to make the APIs capable of handling re-entry.  For your case, I can see that lpKey is having problem.  Instead of declaring it as

LPSTR lpKey = "SOFTWARE\\Syncro\\";

You should use this:

LPSTR lpKey;
strcpy(lpKey, "SOFTWARE\\Syncro\\");

This simple change makes it init for every call.


0
 
anichiniCommented:
This is normal behavior. DLLs do not unload after each call to them. The unload when you either call FreeLibrary or the process that loaded them terminates.

You should reimpliment the DLL so that you can call the GetUserInfo function repreatedly.

0
 
dirtdartAuthor Commented:
So how do the API calls work?  Do they stay loaded for the entire program execution after the first call? If so, then it seems that dll's are a complete waste, as they only save overhead when the program is first loaded, after that, all their overhead is added to the program for the rest of the execution.

If this is the case, how can I reimpliment the call so that the old values aren't inherited?  Obviously, redeclaring them doesn't work.
0
Cloud Class® Course: Microsoft Office 2010

This course will introduce you to the interfaces and features of Microsoft Office 2010 Word, Excel, PowerPoint, Outlook, and Access. You will learn about the features that are shared between all products in the Office suite, as well as the new features that are product specific.

 
Tommy HuiEngineerCommented:
In C, you can load and unload DLLs dynamically. That is controlled by the calling application and not the DLLs themselves. I'm not sure if you can do this with VB or not. But ultimately, the lifetime of DLLs can be controlled by using LoadLibrary and FreeLibrary.


0
 
dirtdartAuthor Commented:
Unfortunatly, that's not exactly what I'm looking for.  I honestly don't know how VB handles dlls, but as I mentioned before, it seems that API calls load and unload without the help of LoadLibrary and FreeLibrary.  Is there anywhere I could find some source code for the Win32API?  I know this is probably ludicrous, because Microsoft isn't in the habit of giving their source code away, but I'm thinking if I can see some example of how that works,  I might get a better idea of what I need to do, or at least what my restrictions are.  And if this is an impossible task, I might consider showing me how to clear the call for future use as an acceptable answer.
0
 
dirtdartAuthor Commented:
Well, it's certainly not what I was hoping for, but I guess I have learned something about dlls.  Wish I'd know this 10 hours ago.  One other question, though, if you don't mind.  Would it be best to free the BSTRs before returning the function?  I'm just thinking that this could prevent recurring values in subsequent calls.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.