Delay Loading Dlls

Delay Loading DLLs

Your project can specify that a dll it depends upon should but be loaded when needed, by specifying it in the Linker/Input/Delay Loaded DLLs field. This setting can be different for different build configurations.

Are delay-loaded DLLs useful to avoid linking specific functions?

Would this avoid loading that specific function under XP so that I don't need to call GetModuleHandle and GetProcAddress?

Yes. This is exactly the type of situation that delay-loading was invented for. your code can call a DLL function as if it were statically linked, but the executable will not load the DLL function pointer at runtime until the function is actually called for the first time. Internally, the delay-load mechanism uses LoadLibrary() and GetProcAddress() for you.

Is it recommended to delay-load a DLL when only a few functions need to be delay-loaded?

If a DLL is delay-loaded, ALL of its functions are delay-loaded, you cannot pick and choose which ones you want. So, if your app needs to use a lot of functions from the same DLL, like user32.dll, then static linking is usually more efficient, and then you can use GetProcAddress() manually for the few functions you really need to handle differently.

In this case, I would suggest getting rid of the OS check altogether and rely only on whether the DLL function actually exists or not, eg:

typedef BOOL (WINAPI *LPFN_ACFL)(HWND);

LPFN_ACFL lpAddClipboardFormatListener = (LPFN_ACFL) GetProcAddress(GetModuleHandle(TEXT("user32")), "AddClipboardFormatListener");

if( lpAddClipboardFormatListener != NULL )
lpAddClipboardFormatListener(hWnd);
else
SetClipboardViewer(hWnd);

Where delay-loading really shines is in its hooks. For instance, on earlier systems, you can use a delay-load failure hook to implement your own version of AddClipboardFormatListener(), and then your main code can just call AddClipboardFormatListener() unconditionally on all systems and it won't know the difference. For example (just a demo, not actually tested):

LRESULT CALLBACK ClipboardSubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &ClipboardSubClassProc, uIdSubclass);
break;

case WM_CHANGECBCHAIN:
{
if (wParam == dwRefData)
SetWindowSubclass(hWnd, &ClipboardSubClassProc, uIdSubclass, lParam);

else if (dwRefData != 0)
SendMessage((HWND)dwRefData, uMsg, wParam, lParam);

break;
}

case WM_DRAWCLIPBOARD:
{
SendMessage(hWnd, WM_CLIPBOARDUPDATE, 0, 0);

if (dwRefData != 0)
SendMessage((HWND)dwRefData, uMsg, wParam, lParam);

break;
}
}

return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

BOOL WINAPI My_AddClipboardFormatListener(HWND hWnd)
{
HWND hWndNext = SetClipboardViewer(hWnd);
if ((!hWndNext) && (GetLastError() != 0))
return FALSE;

if (!SetWindowSubclass(hWnd, &ClipboardSubClassProc, 1, (DWORD_PTR)hWndNext))
{
DWORD dwErr = GetLastError();
ChangeClipboardChain(hWnd, hwndNext);
SetLastError(dwErr);
return FALSE;
}

return TRUE;
}

BOOL WINAPI My_RemoveClipboardFormatListener(HWND hWnd)
{
DWORD_PTR dwRefData;
if (!GetWindowSubclass(hWnd, &ClipboardSubClassProc, 1, &dwRefData))
{
SetLastError(ERROR_NOT_FOUND);
return FALSE;
}

RemoveWindowSubclass(hWnd, &ClipboardSubClassProc, 1);

return ChangeClipboardChain(hWnd, (HWND)dwRefData);
}

FARPROC WINAPI MyDliFailureHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
if ((dliNotify == dliFailGetProc) && (pdli->dlp.fImportByName))
{
if (strcmp(pdli->dlp.szProcName, "AddClipboardFormatListener") == 0)
return (FARPROC) &My_AddClipboardFormatListener;

if (strcmp(pdli->dlp.szProcName, "RemoveClipboardFormatListener") == 0)
return (FARPROC) &My_RemoveClipboardFormatListener;
}

return NULL;
}

__pfnDliFailureHook2 = &MyDliFailureHook;

...

LRESULT CALLBACK MyWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
AddClipboardFormatListener(hWnd);
break;

case WM_DESTROY:
RemoveClipboardFormatListener(hWnd);
break;

case WM_CLIPBOARDUPDATE:
// do all of your clipboard processing here...
break;

...
}

return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

Delay loading dll in release mode

The more I think about this, the more it looks like a [Wikipedia]: XY problem.

1. The X (running the MEX file on a machine with no MATLAB libraries)

According to [MathWorks]: Run MEX File You Receive from Someone Else (emphases are mine):

On Windows® platforms, install the C++ compiler run-time libraries used to create the MEX file.

...

A MEX file is a dynamically linked subroutine that the MATLAB interpreter loads and executes when you call the function. Dynamic linking means that when you call the function, the program looks for dependent libraries. MEX files use MATLAB run-time libraries and language-specific libraries. A MEX file might also use specialized run-time libraries. The code for these libraries is not included in the MEX file; the libraries must be present on your computer when you run the MEX file.

[MathWorks]: MATLAB Runtime contains links for downloading many versions (yours - according to your paths - would be [MathWorks]: MCR Runtime - MCR_R2012a_win32_installer.exe), which are free (I installed 3 of those versions to test this scenario), and also states:

Run compiled MATLAB applications or components without installing MATLAB

So, it's pretty clear (to me) that whoever would like to use that file, should install the MCR.

2. The Y (using Delay Loaded DLLs)

VStudio supports this feature ([MS.Docs]: Linker Support for Delay-Loaded DLLs) for quite some time.

Never worked with MEX files, nor do I have the full problem specs, but allowing one such file to run when there are no MATLAB .dlls present, doesn't look like good design to me (meaning that it also contains other stuff - which on my opinion should be placed separately). The only scenario that makes sense is that the MEX file would be an .exe (don't know whether this is possible or it's just a dumb thing) and it would have some --help equivalent (which would be nice (but not mandatory) to run on environments without the .dll's).
But that too could be solved using other ways (e.g. a README like file)

3. The end problem

Considering that there were / are multiple (logical) errors in the question:

  • The .dlls passed to the linker
  • The .lib files located in the bin dir
  • The latest path (extern/lib/win64/microsoft) contains 64 bit .libs, while the linker is set for 32 bit output
  • [MS.Docs]: Linker Tools Error LNK1107 which is pretty clear (as the error message in the question)

I can only conclude that for Release, "C:\Program Files (x86)\MATLAB\R2012a\bin\win32\libmx.dll" was incorrectly fed to the linker (instead of the corresponding .lib).

I played a little bit with MEX:

code.c:

#include <stdio.h>
#include <conio.h>
#include <mex.h>

int main(int argc, char **argv) {
if (argc > 1) {
fprintf(stdout, "Argument passed: mexEvalString() returns\n", mexEvalString("n = 1;"));
} else {
fprintf(stdout, "Argument NOT passed: pass...\n");
}
fprintf(stdout, "Press a key to exit...\n");
_getch();
return 0;
}

Notes:

  • I used fprintf because in mex.h there is a line:

    #define printf mexPrintf
  • Didn't know what function to use from libmx.dll, to force it being added directly (not just a dependency for libmex.dll)

  • I was able to test the Delay Laded DLLs feature in Debug and Release (when passing no arg, the program ran without adding the MEX .dlls to %PATH%).
    It's true that at runtime I got Access Violation, but that's a totally different issue
  • Needless to say that adding any of the .dlls to "Linker -> Input -> Additional Dependencies", triggered the exact same error

At the end, I would like to mention that MCR R2012a (and some others that were released after it), are built with VStudio 9.0 (2008), and building your program with VStudio 10.0 (2010), will yield having both CRT Libs in loaded your process, and in some cases that might trigger some errors (especially since VStudio 9.0's comes as an assembly).
This applies to libmx.dll and libmex.dll, but not libeng.dll.

Delay-load DLL in windows: can I dynamically choose what DLL name to look for? (c++)

To tell the delay load helper to use a different DLL than what is linked to, you can use a delay load failure hook. Per MSDN:

Linker Support for Delay-Loaded DLLs: Error Handling and Notification

If your code needs to recover or provide an alternate library and/or routine on failure, a hook can be provided to the helper function that can supply or remedy the situation. The hook routine needs to return a suitable value, so that processing can continue (an HINSTANCE or FARPROC) or 0 to indicate that an exception should be thrown. It could also throw its own exception or longjmp out of the hook. There are notification hooks and failure hooks.

When a delay-loaded DLL fails to load, the hook is called with a dliFailLoadLib notification containing details about the load operation and error. The hook can recover from the error by returning a valid HMODULE for the helper to use, or return 0 to fail the load operation.

If the notification is dliFailLoadLib, the hook function can return:

  • 0, if it cannot handle the failure.

  • An HMODULE, if the failure hook fixed the problem and loaded the library itself.

So, if the error reports the failed DLL is foo.dll, your hook can load and return the HMODULE for bar.dll, and then the helper will load all of foo.dll's delay-loaded functions from bar.dll instead.

How do I stop a delay-loaded DLL from throwing a missing from your computer system error?

Don't forget to include the "*.dll" extension. In linker option you must specify the following:

libcurl.dll;libxml2.dll;zlib1.dll;iconv.dll;

If you use #pragma comment(lib, "libcurl") then you don't have to specify it a second time in linker option.

SetDllDirectory(L"./plugins/ts3websitepreview/"); should be sufficient. You don't need __HrLoadAllImportsForDll.

If you want to call DLL's DLL_PROCESS_ATTACH ahead of time, then use LoadLibrary instead.



Related Topics



Leave a reply



Submit