3

I recently got a problem in a program that used to work fine. I tracked it down to the following code:

using System.Drawing;
using System.Runtime.InteropServices;

namespace Foo
{
    static class CProgram
    {
        [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetDefaultDllDirectories(int directoryFlags);

        public const int LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x000001000;

        private static void Main()
        {
            SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
            Font font = SystemFonts.DefaultFont;
        }
    }
}

Once I use SetDefaultDllDirectories with anything but zero as a parameter the program crashes. I traced it to SafeNativeMethods.Gdip.GdipGetGenericFontFamilySansSerif(out fontfamily); which simply calls "GdipGetGenericFontFamilySansSerif". But this call fails with an FontFamilyNotFound error.

It works without the SetDefaultDllDirectories. And it even works, if I place the font assignment before AND after that call.

Is there anything on my system that causes this or was it an update from MS that causes this bug?

System: Win7 x64, fully updated, AMD Radeon HD with latest beta drivers used

Background: I need that function to use AddDllDirectory to add a subdirectory of my executables path (something like C:/MyProgram/myLibrariesX)

kjbartel
  • 10,381
  • 7
  • 45
  • 66
Flamefire
  • 5,313
  • 3
  • 35
  • 70
  • 2
    Hmya, this is the worst kind of global variable. There are two versions of Gdiplus.dll and you basically *never* want the compat version in system32, that ways lies dragons. This is an XY question, it surely has a better answer than *that*. Not limited to avoiding giving Windows a hard time intentionally, just copy the DLL in the right place. – Hans Passant Sep 12 '14 at 23:14
  • 1
    I agree with Hans. Put another way, and AFAIK, the problem is simply that .NET doesn't support calls to SetDefaultDllDirectories. – Harry Johnston Sep 12 '14 at 23:20
  • Agreed. You need to tackle the root of the problem. – David Heffernan Sep 13 '14 at 06:06
  • Looking into the loaded libraries with the debugger shows, that in both cases the same dll from WinSxS folder is loaded. Background on the directory is, that one contains x86 and one x64 dlls. On startup (compiled with "any" architecture) it chooses the right folder to load from. Also there are lots of plugins for gstreamer that better are in a separate folder to not have like 100 files in one folder. Following up on ".NET doesn't support calls to SetDefaultDllDirectories": Why? The call itself works... Current workaround: Access SystemFonts.DefaultFont before using that function... – Flamefire Sep 13 '14 at 10:04
  • 1
    MS has recently changed the way font loading works because of [MS14-045/CVE-2014-1819](https://technet.microsoft.com/library/security/MS14-045#ID0E1LAG). I don't know if this relates to your problem specifically, but if you want more insight into exactly why your code is failing try running [Process Monitor](http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx). – arx Sep 13 '14 at 23:04
  • @Flamefire: first let me make it clear that I was speculating, I don't know for certain that Microsoft don't support calling SetDefaultDllDirectories from .NET code. That said, it seems likely to me that they either never considered that someone might want to do so, or that it would be too difficult for them to cope with; the number of things it might potentially break is probably huge, and testing would be a nightmare. – Harry Johnston Sep 13 '14 at 23:45

2 Answers2

1

I ran into the same problem as I needed native libraries to be found within an architecture folder under the base folder, i.e. x86 and x64. The work around I used was to instead append the architecture specific folder to the PATH environment variable.

var archPath = $"{AppDomain.CurrentDomain.BaseDirectory}{Environment.Is64BitProcess ? "x64" : "x86"}\\";
var pathVariable = Environment.GetEnvironmentVariable("PATH");
pathVariable = $"{archPath};{pathVariable}";
Environment.SetEnvironmentVariable("PATH", pathVariable);

The PATH isn't used for assembly probing but is used for native DLL probing. For assemblies I used the AppDomain.CurrentDomain.AssemblyResolve event.

kjbartel
  • 10,381
  • 7
  • 45
  • 66
0

I can not reproduce the problem with Visual Studio 2013 in Windows 8.1 x64, while I could reproduce it on Windows 7. So it seems to be fixed now.

Ecki
  • 1