Solved

Creating Windows DLLs

Posted on 1997-11-19
6
232 Views
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:

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.
0
Comment
Question by:dirtdart
6 Comments
 
LVL 2

Expert Comment

by:anichini
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.

0
 
LVL 5

Author Comment

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


0
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 
LVL 5

Author Comment

by:dirtdart
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.
0
 
LVL 7

Accepted Solution

by:
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.


0
 
LVL 5

Author Comment

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

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
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 use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.

757 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

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now