Creating Windows DLLs

Posted on 1997-11-19
Last Modified: 2009-12-16
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:


#include <windows.h>

#ifndef WINAPI
#define WINAPI      __stdcall

#define DLLEXPORT      __declspec(dllexport)

#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 );


#include "Syncro.h"

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

        case DLL_THREAD_ATTACH:

        case DLL_THREAD_DETACH:

        case DLL_PROCESS_DETACH:
    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";
      case DPWRVIEW:
            lpDataType = "DpwrView";
      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);


      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)
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.
Question by:dirtdart

Expert Comment

ID: 1173669
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.


Author Comment

ID: 1173670
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.
LVL 15

Expert Comment

by:Tommy Hui
ID: 1173671
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.

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!


Author Comment

ID: 1173672
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.

Accepted Solution

faster earned 100 total points
ID: 1173673
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.


Author Comment

ID: 1173674
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.

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

730 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question