?
Solved

LNK 2005 when compiling dll

Posted on 2005-03-30
22
Medium Priority
?
339 Views
Last Modified: 2013-12-14
error LNK2005: _DllMain@12 already defined in ShacoZip.obj

I get this error when I compile a dll I am trying to create using VS.NET 2003.

I added this DllMain() to the main.cpp because when I tried to regsvr32 the dll I got a message telling me that there was no entry point for the dll.

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

I have been doing some research and am seeing things relating to .def files.  I am not very familiar with creating dll's but this needs to be done so a vb application can use it.  Any help on how to get this to compile would be really appreciated.

PT
0
Comment
Question by:ptrennum
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 11
  • 11
22 Comments
 
LVL 86

Expert Comment

by:jkr
ID: 13662352
'DllMain()' is not needed for 'regsvr32', the latter app needs 'DllRegisterServer'()' and/or 'DllUnregisterServer()' - you can safely remove your 'DllMain()' code. See also http://support.microsoft.com/default.aspx?scid=kb;en-us;207132 ("INFO: How Regsvr32.exe Registers and Unregisters COM DLLs")
0
 

Author Comment

by:ptrennum
ID: 13662385
I just added:
   /FORCE:MULTIPLE
to my command line options under linker in project properties.

This allowed me to compile however I can still not register the dll successfully??
0
 
LVL 86

Expert Comment

by:jkr
ID: 13662425
>>This allowed me to compile however I can still not register the dll successfully?

As I wrote, you'll need to provide 'DllRegisterServer'()' and/or 'DllUnregisterServer()', but _not_ 'DllMain()'.
0
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!

 

Author Comment

by:ptrennum
ID: 13662461
Sorry, I wrote that before I noticed your post.  It looks like there is source there to create my own register function, however I would really just like to add the register and un register to my existing project.  Are there any good examples of how that can be done??
0
 
LVL 86

Expert Comment

by:jkr
ID: 13662474
BTW, about how to implement the functionality of the aforementioned functions, see http://msdn.microsoft.com/library/en-us/dnesscom/html/classesservers.asp ("Classes and Servers (Essential COM)"):

All well-implemented COM servers support self-registration. For an in-process server, this means that the DLL must export the well-known functions

STDAPI DllRegisterServer(void);
STDAPI DllUnregisterServer(void);

Note that STDAPI is simply a macro that indicates that the function returns an HRESULT and uses COM's standard calling convention for global functions. These routines must be explicitly exported, using either a module definition file, linker switches, or compiler directives. These routines are used by the Class Store to configure the local cache after downloading the file to the client machine. In addition to the Class Store, these well-known routines are used by various environments (e.g., Microsoft Transaction Server, ActiveX Code Download, miscellaneous setup utilities) to install or uninstall servers on host machines. The Win32 SDK includes a utility, REGSVR32.EXE, that will install or uninstall a COM in-process server using these well-known exported functions.
0
 
LVL 86

Expert Comment

by:jkr
ID: 13662492
>>I would really just like to add the register and un register to my existing project

We're kinda out-of-sync :o)

Check the above article and it's code samples, the main responsibility of the registration code is to cerate registry entries, e.g.

const char *g_RegTable[][3] = {
// format is { key, value name, value }
{ "CLSID\\{571F1680-CC83-11d0-8C48-0080C73925BA}",  0, "Gorilla" },
{ "CLSID\\{571F1680-CC83-11d0-8C48-0080C73925BA}\\InprocServer32",
  0, (const char*)-1 // rogue value indicating file name
},
{ "CLSID\\{571F1680-CC83-11d0-8C48-0080C73925BA}\\ProgID",
  0, "Apes.Gorilla.1"
},
{ "Apes.Gorilla.1", 0,  "Gorilla" },
{ "Apes.Gorilla.1\\CLSID",
   0,  "{571F1680-CC83-11d0-8C48-0080C73925BA}" },
};


STDAPI DllRegisterServer(void) {
  HRESULT hr = S_OK;
// look up server's file name
  char szFileName[MAX_PATH];
  GetModuleFileNameA(g_hinstDll, szFileName, MAX_PATH);
// register entries from table
  int nEntries = sizeof(g_RegTable)/sizeof(*g_RegTable);
  for (int i = 0; SUCCEEDED(hr) && i < nEntries; i++) {
    const char *pszKeyName   = g_RegTable[i][0];
    const char *pszValueName = g_RegTable[i][1];
    const char *pszValue     = g_RegTable[i][2];
// map rogue value to module file name
    if (pszValue == (const char*)-1)
      pszValue = szFileName;
    HKEY hkey;
// create the key
    long err = RegCreateKeyA(HKEY_CLASSES_ROOT,
                          pszKeyName, &hkey);
    if (err == ERROR_SUCCESS) {
// set the value
      err = RegSetValueExA(hkey, pszValueName, 0,
                           REG_SZ, (const BYTE*)pszValue,
                           (strlen(pszValue) + 1));
      RegCloseKey(hkey);
    }
    if (err != ERROR_SUCCESS) {
  // if cannot add key or value, back out and fail
      DllUnregisterServer();
      hr = SELFREG_E_CLASS;
    }
  }
  return hr;
}


0
 

Author Comment

by:ptrennum
ID: 13662657
Sorry I'm not exactly following here, how are we out of sync?
I do not need to implement these functions in my code?
Is there anything I need to add to my .def file?
0
 
LVL 86

Expert Comment

by:jkr
ID: 13663020
>>how are we out of sync?

Never mind, that was about when we're posting comments :o)

>>I do not need to implement these functions in my code?

Yes, you do - unless you already have - are they declared like

extern "C"  STDAPI  DllRegisterServer   (   void);
extern "C"  STDAPI  DllUnregisterServer   (   void);

?

If so, you won't have to add them to your .def file.
0
 

Author Comment

by:ptrennum
ID: 13663165
No I don't have them anywhere in my code
0
 
LVL 86

Expert Comment

by:jkr
ID: 13663209
Well, then it's not surprising that regsvr32.exe complains about them as 'missing'. Are you creating a COM DLL at all?
0
 

Author Comment

by:ptrennum
ID: 13663214
So to summarize here:
I need to add ->
  extern "C"  STDAPI  DllRegisterServer   (   void);
  extern "C"  STDAPI  DllUnregisterServer   (   void);

to my main.h, then implement them in my main.cpp.
0
 

Author Comment

by:ptrennum
ID: 13663219
I am creating an MFC DLL
0
 
LVL 86

Expert Comment

by:jkr
ID: 13663252
>>I need to add ->
>>  extern "C"  STDAPI  DllRegisterServer   (   void);
>> extern "C"  STDAPI  DllUnregisterServer   (   void);
>>to my main.h, then implement them in my main.cpp.

If your DLL implements a COM/OLE object that should be exposed: Yes. If you're building a standard DLL (i.e. without COM/OLE support), there's no need to use 'regsvr32.exe' or to provide these functions.
0
 

Author Comment

by:ptrennum
ID: 13663271
I need to use this dll in a vb application, I am assuming to do this it will need to be a com dll
0
 
LVL 86

Expert Comment

by:jkr
ID: 13663296
That depends - a C++ DLL can be used from VB without COM. But, in general, you are right, mostly COM is used. In this case, the registration code is supposed to create the required registry entries, as described in the article I linked above.
0
 

Author Comment

by:ptrennum
ID: 13663331
In dll unregister what on earth are these supposed to represent??
for (int i = nEntries - 1; i >= 0; i—–)
0
 

Author Comment

by:ptrennum
ID: 13663363
Also in dllregister:

GetModuleFileNameA(g_hinstDll, szFileName, MAX_PATH);
g_hinstDll is undefined.
0
 
LVL 86

Expert Comment

by:jkr
ID: 13663367
LOL, missed that - well, it seems that the whole code should read

STDAPI DllUnregisterServer(void) {
  HRESULT hr = S_OK;
  int nEntries = sizeof(g_RegTable)/sizeof(*g_RegTable);
  for (int i = nEntries - 1; i >= 0; i--){
    const char *pszKeyName = g_RegTable[i][0];

    long err = RegDeleteKeyA(HKEY_CLASSES_ROOT, pszKeyName);
    if (err != ERROR_SUCCESS)
      hr = S_FALSE;
  }
  return hr;
}

MS seems to have had a 'lil 'character set accident' there :o)

BTW, don't forget to make them 'extern "C"', or regsvr32.exe won't find them either.
0
 

Author Comment

by:ptrennum
ID: 13663479
Ok so I can basically copy those functions into my source,

I still am not able to register my dll.

I don't need to add anything to my .def file?


Here is my source code in my main.cpp file, I know it isn't the prettiest but like I said I'm not exactly experienced with this kind of stuff.

// CShacoZipApp
// The module attribute causes DllMain, DllRegisterServer and DllUnregisterServer to be automatically implemented for you

BEGIN_MESSAGE_MAP(CShacoZipApp, CWinApp)
END_MESSAGE_MAP()

extern "C"  STDAPI  DllRegisterServer(void);
extern "C"  STDAPI  DllUnregisterServer(void);

// CShacoZipApp construction

CShacoZipApp::CShacoZipApp()
{
      // TODO: add construction code here,
      // Place all significant initialization in InitInstance
}


// The one and only CShacoZipApp object

CShacoZipApp theApp;


// CShacoZipApp initialization

BOOL CShacoZipApp::InitInstance()
{
      CWinApp::InitInstance();

      return TRUE;
}

const char *g_RegTable[][3] = {
// format is { key, value name, value }
{ "CLSID\\{571F1680-CC83-11d0-8C48-0080C73925BA}",  0, "Gorilla" },
{ "CLSID\\{571F1680-CC83-11d0-8C48-0080C73925BA}\\InprocServer32",
  0, (const char*)-1 // rogue value indicating file name
},
{ "CLSID\\{571F1680-CC83-11d0-8C48-0080C73925BA}\\ProgID",
  0, "Apes.Gorilla.1"
},
{ "Apes.Gorilla.1", 0,  "Gorilla" },
{ "Apes.Gorilla.1\\CLSID",
   0,  "{571F1680-CC83-11d0-8C48-0080C73925BA}" },
};

extern "C" STDAPI DllRegisterServer(void) {
  HRESULT hr = S_OK;
// look up server's file name
  char szFileName[MAX_PATH];
  //GetModuleFileNameA(g_hinstDll, szFileName, MAX_PATH);
  GetModuleFileNameA(NULL, szFileName, MAX_PATH);
// register entries from table
  int nEntries = sizeof(g_RegTable)/sizeof(*g_RegTable);
  for (int i = 0; SUCCEEDED(hr) && i < nEntries; i++) {
    const char *pszKeyName   = g_RegTable[i][0];
    const char *pszValueName = g_RegTable[i][1];
    const char *pszValue     = g_RegTable[i][2];
// map rogue value to module file name
    if (pszValue == (const char*)-1)
      pszValue = szFileName;
    HKEY hkey;
// create the key
    long err = RegCreateKeyA(HKEY_CLASSES_ROOT,
                          pszKeyName, &hkey);
    if (err == ERROR_SUCCESS) {
// set the value
      err = RegSetValueExA(hkey, pszValueName, 0,
                           REG_SZ, (const BYTE*)pszValue,
                           (strlen(pszValue) + 1));
      RegCloseKey(hkey);
    }
    if (err != ERROR_SUCCESS) {
  // if cannot add key or value, back out and fail
      DllUnregisterServer();
      hr = SELFREG_E_CLASS;
    }
  }
  return hr;
}

extern "C" STDAPI DllUnregisterServer(void) {
  HRESULT hr = S_OK;
  int nEntries = sizeof(g_RegTable)/sizeof(*g_RegTable);
  for (int i = nEntries - 1; i >= 0; i--){
    const char *pszKeyName = g_RegTable[i][0];

    long err = RegDeleteKeyA(HKEY_CLASSES_ROOT, pszKeyName);
    if (err != ERROR_SUCCESS)
      hr = S_FALSE;
  }
  return hr;
}
0
 
LVL 86

Accepted Solution

by:
jkr earned 2000 total points
ID: 13663566
>>I still am not able to register my dll.

U, make the declaration read

extern "C"  STDAPI  __declspec(dllexport) DllRegisterServer(void);
extern "C"  STDAPI  __declspec(dllexport) DllUnregisterServer(void);

If you want to put them in your .def file, you could use

LIBRARY      "ShacoZip"

EXPORTS
    DllRegisterServer           @4 PRIVATE
    DllUnregisterServer         @5 PRIVATE
0
 

Author Comment

by:ptrennum
ID: 13663640
JKR - You are fantastic!!

Thank you very much for your patience and knowledge!

PT
0
 
LVL 86

Expert Comment

by:jkr
ID: 13663710
You're most welcome :o)
0

Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say 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

Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The viewer will learn how to synchronize PHP projects with a remote server in NetBeans IDE 8.0 for Windows.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.
Suggested Courses

801 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