Can a dll file be initialized - A Dll constructor - c++

I am currently importing a function out of my dll in the following way in a C# application.
[DllImport("C:\\File.dll")]
public static extern int SomeMethod(int A);
My dll file is written in C++. This method is also defined in the .def file of the dll.
Currently whenever this method is called in the first ever attempt a couple of other methods are called , these methods set up the grounds for this method. However these methods only need to be called once. These methods are never called again/ I wanted to know is there a way to call a group of methods (initializers) as soon as a dll file is loaded. Is there anything as a dll constructor ?
I have something like this in my dllmain.cpp could entering something in there accomplish this task
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE 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;
}

DllMain is the closest to a DLL "constructor" (and the correct place to do initialization is in the case label for DLL_PROCESS_ATTACH). However a more realistic constructor would be that of a COM class instantiated by the C# code. In that case the constructor would be called for every instance of the class that you create (just like for a C# class). Create an ATL project in Visual C++ and it will generate code like this for you:
class ATL_NO_VTABLE CMyClass :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CMyClass, &CLSID_MyClass>,
public IMyClass
{
public:
CMyClass()
{
}
DECLARE_REGISTRY_RESOURCEID(IDR_MYCLASS1)
BEGIN_COM_MAP(CMyClass)
COM_INTERFACE_ENTRY(IMyClass)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
...
In the CMyClass constructor or in the FinalConstruct method you can perform the initialization code for the object. Also add your SomeMethod method to this class (and to the IDL interface in the IDL file).
Then you would use that class in C# like this:
MyClass mc = new MyClass(); // CMyClass::CMyClass and CMyClass::FinalConstruct are called in C++
mc.SomeMethod(); // CMyClass::SomeMethod is called in C++

Related

Shared global variable with DLL not working

I'm trying to get some code that works on the Mac to work on Windows. The code involves sharing data between a DLL, a static library and the main program. I suspect the problem arises because of the differences in the way that Unix and Windows handle global variables (see, for example, the answers here). However, I haven't figured out how to fix it. Here's a minimal example:
My Visual Studio 2019 solution contains three projects.
Project 1 makes the static library MarinaLib.lib
Header is MarinaLib.h
#pragma once
#include "Marina.h"
Class header is Marina.h
#pragma once
class Marina
{
public:
static Marina* get_marina();
protected:
static Marina* marina_instance;
};
Source file is Marina.cpp
#include "Marina.h"
Marina* Marina::marina_instance { nullptr };
Marina* Marina::get_marina()
{
if( !marina_instance )
marina_instance = new Marina();
return marina_instance;
}
Project 2 makes the DLL MarinaDLL.dll . It #defines MARINADLL_EXPORTS
First source file dllmain.cpp
// dllmain.cpp : Defines the entry point for the DLL application.
#include "framework.h"
BOOL APIENTRY DllMain( HMODULE 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;
}
Header file MarinaDLL.h is
#pragma once
#ifdef MARINADLL_EXPORTS
#define QUERY_DECLSPEC __declspec(dllexport)
#else
#define QUERY_DECLSPEC __declspec(dllimport)
#endif
QUERY_DECLSPEC void query_marina();
Second source file is marinaDLL.cpp
#include "MarinaDLL.h"
#include "..\MarinaLib\Marina.h"
void query_marina()
{
auto inst = Marina::get_marina();
}
Project 3 makes the executable MarinaExample.exe and does not #define MARINADLL_EXPORTS. It links in MarinaLib.lib and MarinaDLL.lib
The source file MarinaExample.cpp is
#include "MarinaLib.h"
#include "MarinaDLL.h"
int main()
{
auto instance = Marina::get_marina();
query_marina();
}
In the first line of main(), the code enters Marina::get_marina(). marina_instance is nullptr so the code creates a new Marina and makes marina_instance point to it. This is fine.
In the second line of main(), the code enters query_marina() and from there goes into Marina::get_marina(). At this point marina_instance is nullptr which is not what I want. I would like it to maintain its previous non-null value.
I've seen some examples of solutions to problems in similar code but they don't seem to work in my situation. Any suggestions on how to fix this?
Thanks.
add export modifier to class definition and export the whole class.
In dll and lib build define QUERY_DECLSPEC=export , in exe build as import :
class QUERY_DECLSPEC Marina
{
public:
static Marina* get_marina();
protected:
static Marina* marina_instance;
};

lpvoid to interface reference invalid cast exception

I have problems casting a class to LPVOID and than recasting it to interface class. Here is the simplified code:
public interface class IEventRaiser
{
void fireAppDisconnect()
// some other methods
}
interface class ISpecificEventRaiser : IEventRaiser
{
// some specific methods
}
public ref class ManagedItem
{
ManagedItem()
{
eventRaiser = gcnew EventRaiser();
LPVOID lP = reinterpret_cast<LPVOID>(GCHandle::ToIntPtr(GCHandle::Alloc(eventRaiser)).ToPointer();
item = new UnmanagedItem(lP);
}
// some implementation
ref class EventRaiser : public ISpecificEventRaiser
{
virtual void fireAppDisconnect();
// other methods
};
EventRaiser^ eventRaiser;
UnmanagedItem* item;
};
public class UnmanagedItem
{
UnmanagedItem(LPVOID eventRaiser)
{
IEventRaiser^ r;
IntPtr pointer(eventRaiser);
handle = GCHandle::FromIntPtr(pointer);
r = safe_cast<IEventRaiser^>(handle.Target); // InvalidCastException : Unable to cast object of type 'EventRaiser' to type 'IEventRaiser'.
}
};
There should be no problem with casting to EventRaiser^ to IEventRaiser^, because i tried it before. Before trying to LPVOID conversations, it was working fine. But when i cast it into LPVOID and recast it to IEventRaiser, it throws InvalidCastException. How can i do the castings via LPVOID properly?
Reason to have a LPVOID pointer to my unmanaged class was to get rid of duplicate header imports. So, although i pay attention to included header files, IEventRaiser's header file was imported to inhereted project in some way. So, as Hans Passant indicated on comments, it causes a cast problem because of different assembly names. What i did to solve the problem is adding #ifdef statements to base project's IEventRaiser header file includes. Like the following:
#ifdef BASE_DLL
#include "IEventRaiser.h"
#endif
Then, i added to "BASE_DLL" to preprocessor definitions. It guarantees that the event raiser header will be included once. The other alternative was using forward declarations for not to add "IEventRaiser.h" header file to unmanaged file header. It's also tried and working.

In MFC, how can WinMain function find user application object's address value?

Greeting, I'm novice in MFC area.
I have a question about process of starting MFC application.
I learned that unlike SDK program, I don't have to write WinMain. Because It is supplied by the class library and is called when the application starts up.*
(See reference here : https://msdn.microsoft.com/en-us/library/akdx0603.aspx)
And my curious part is here: *Then CWinApp calls member functions of the application object to initialize and run the application.
That sentence indicates that CWinApp already know address value of application object which is made by a programmer.
However, even though application object is defined as global variable, how can WinMain function find application object's address value?
I couldn't find any connection or declaration in my sample MFC code which brings address value to the WinMain function.
#include <afxwin.h>
class CHelloApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
class CMainFrame : public CFrameWnd
{
public:
CMainFrame();
protected:
afx_msg void OnPaint();
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP()
};
CHelloApp theApp;
BOOL CHelloApp::InitInstance()
{
m_pMainWnd = new CMainFrame;
m_pMainWnd->ShowWindow(m_nCmdShow);
return TRUE;
}
CMainFrame::CMainFrame()
{
Create(NULL, "HelloMFC Application");
}
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
Good question, but you could answer it yourself when running your exe under a debugger, setting breakpoints at the right places and stepping into MFC source code.
The CRT provides a function mainCRTStartup. This function is the entry point that gets called when your program starts. mainCRTStartup calls __tmainCRTStartup. This function first calls _initterm to call the constructors for all global objects - like your CWinApp theApp. That constructor also calls CWinApp::CWinApp which stores the this pointer in a global state variable. When that is done __tmainCRTStartup calls WinMain which calls AfxWinMain. AfxWinMain is reading out the pointer to CWinApp theApp from the global state variable and calls the CWinApp's virtual member functions.
This only works because...
the constructor stored the this pointer
only one CWinApp object exists
CWinApp is used as an interface and thus the MFC does not need to know how exactly your CWinApp derived class looks like

How can i set an entrypoint for a dll

First i thought entry point in dlls DLLMain but then when i try to import it in C# i get an error that entrypoint wasn't found Here is my code:
#include <Windows.h>
int Test(int x,int y)
{
return x+y;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBox(0,L"Test",L"From unmanaged dll",0);
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
How can i set an entry point for my dll? And if you dont mind can you give me little explanation about entry point?
Like do i have to set import the same dll again and changing the entry point so i can use other functions in same dll? thanks in advance.
In your example, it seems you intend Test() to be an entry point however you aren't exporting it. Even if you begin exporting it, it might not work properly with C++ name "decoration" (mangling). I'd suggest redefining your function as:
extern "C" __declspec(dllexport) int Test(int x,int y)
The extern "C" component will remove C++ name mangling. The __declspec(dllexport) component exports the symbol.
See http://zone.ni.com/devzone/cda/tut/p/id/3056 for more detail.
Edit: You can add as many entry points as you like in this manner. Calling code merely must know the name of the symbol to retrieve (and if you're creating a static .lib, that takes care of it for you).

Automatic initialization routine in C++ library?

If i have a header file foo.h and a source file foo.cpp, and foo.cpp contains something along the lines of:
#ifdef WIN32
class asdf {
asdf() { startup_code(); }
~asdf() { cleanup_code(); }
};
asdf __STARTUP_HANDLE__
#else
//unix does not require startup or cleanup code in this case
#endif
but foo.h does not define class asdf, say i have an application bar.cpp:
#include "foo.h"
//link in foo.lib, foo.dll, foo.so, etc
int main() {
//do stuff
return 0;
}
If bar.cpp is compiled on a WIN32 platform, will the asdf() and ~asdf() be called at the appropriate times (before main() and at program exit, respectively) even though class asdf is not defined in foo.h, but is linked in through foo.cpp?
Yes -- but be very careful. The order in which static objects (like your asdf object) are initialized is undefined. So it is undefined behavior if any other object tries to reference your object before main().
Also, __STARTUP_HANDLE__ is not a valid identifier. Double-underscores are not allowed in any identifier (even macros), and a single-underscore followed by a capital letter is also not allowed.
If it ain't broke, don't fix it. Look out for the static initialization order issue, as the other answers say. But you really should fix that reserved identifier.
__STARTUP_HANDLE__ does work similar to a runtime library, and runtime libraries use names like that, but it's not part of the runtime library so that name isn't allowed.
#ifdef WIN32
namespace { // anonymous namespace - no outside access
class asdf {
asdf() { startup_code(); }
~asdf() { cleanup_code(); }
} x; // to create an instance
}
As long as the objects (foo.o) themselves are linked into the executable, this will work (assuming the static initialization order fiasco doesn't hit you). If you're pulling it in from a library (foo.lib), though, it won't unless you explicitly mark the object as an "active object," but I don't remember how to do that in MSVC.
On Windows and with DLL's you have the possibility to use the function DllMain that will be called at load etc
BOOL WINAPI DllMain(HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpvReserved) {
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
default:
break;
}
return TRUE;
}