[Last Call] Learn how to a build a cloud-first strategyRegister Now


Creating Windows DLLs

Posted on 1997-11-19
Medium Priority
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.

Industry Leaders: 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 300 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

Technology Partners: 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!

Question has a verified solution.

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

C++ Properties One feature missing from standard C++ that you will find in many other Object Oriented Programming languages is something called a Property (http://www.experts-exchange.com/Programming/Languages/CPP/A_3912-Object-Properties-in-C.ht…
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
Suggested Courses

831 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