6

I have a VSIX extension which depends on code deployed from an unmanaged DLL. I've included the DLL with the VSIX, and I cracked open the VSIX with a zip program to confirm that it is deployed correctly. However, when I use the DllImport attribute, the .NET Framework claims that it can't find it. How can I import functions from a DLL packaged inside my VSIX?

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 1
    Maybe missing paths? Does this one help? http://stackoverflow.com/a/10800260/71312 – Paul Chen Aug 07 '13 at 12:48
  • Have you confirmed that the unmanaged dll is being extracted correctly to the extension's install directory? I put together a simple VSIX that deploys a shell package that calls an unmanaged dll. I added the unmanaged dll to the project with a build action of Content and included it in the VSIX. It runs correctly both in debug and as a regular deployed extension. – WarrenG Aug 08 '13 at 06:21
  • @WarrenG: I have no idea where that is. My build action for my DLL is also "Content" and I set it to include in VSIX. – Puppy Aug 08 '13 at 14:43
  • @WarrenG: I checked the 11.0exp extensions folder, and the unmanaged DLL is right there, right next to the other unpacked contents of the VSIX- including the dll. So the unmanaged DLL is in the assembly's working directory. – Puppy Aug 08 '13 at 14:48
  • Does your unmanaged dll depend on any other dlls that aren't included in the vsix? I changed my test project so that the vsix package depends on unmanaged dll #1, which in turn depends on unmanaged dll #2. If I include dll #1, but don't include dll #2 in the vsix, I get a DllNotFoundException when I execute the test code. – WarrenG Aug 09 '13 at 05:50

3 Answers3

3

I dunno what is going wrong here, but I reinstalled Windows and Visual Studio, made no changes to the project, and now everything is fine. I had some other issues with finding DLLs for other applications, and I guess they were related, I must have just screwed up some setting.

Puppy
  • 144,682
  • 38
  • 256
  • 465
2

Windows is unable to open DLL files embedded into compressed .zip, so you will have to unpack it and put in a folder where you have the access to write to.

The .NET Framework will look for the paths of your DLLs in %LocalAppData%, so it is reasonable to unpack your DLL there.

Sergey K.
  • 24,894
  • 13
  • 106
  • 174
  • Visual Studio extracts the files from the VSIX into an install directory. – Puppy Aug 08 '13 at 14:44
  • Can you open Events Log and check from what path your package tries to load DLL? https://en.wikipedia.org/wiki/Event_Viewer – Sergey K. Aug 08 '13 at 14:49
  • And this tool probably can help: http://technet.microsoft.com/en-us/sysinternals/bb896645 – Sergey K. Aug 08 '13 at 14:51
  • The Event Log does not log any events coming from Visual Studio. – Puppy Aug 08 '13 at 14:54
  • That would seem to be only if DLLMain signals an error, or perhaps for unmanaged code using LoadLibrary or __declspec(dllimport). It certainly does not list any DLL errors coming from Visual Studio or my extension. – Puppy Aug 08 '13 at 14:57
  • It is a perfect tool to debug managed assemblies. Please, try it. – Sergey K. Aug 08 '13 at 15:01
  • I'mma be honest here and say that I looked at both the event log and process monitor, and neither of them appeared to provide any relevant information whatsoever. I have no idea how to use them to debug this issue. – Puppy Aug 08 '13 at 15:19
  • You can try simpler approach now (while you are learning how to use them): put your DLL into windows/system32 folder and try running the application. – Sergey K. Aug 08 '13 at 15:32
1

I used to get spurious package load failures in seemingly random situations. The problems primarily affected extensions comprised of more than one DLL file. I finally got them resolved by applying the [ProvideBindingPath] attribute to the main Package provided in the extension.

You'll need to include the source for the attribute in your project.

/***************************************************************************

Copyright (c) Microsoft Corporation. All rights reserved.
This code is licensed under the Visual Studio SDK license terms.
THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

***************************************************************************/

using System;
using System.Text;

namespace Microsoft.VisualStudio.Shell
{
    /// <summary>
    /// This attribute registers a path that should be probed for candidate assemblies at assembly load time.
    /// 
    /// For example:
    ///   [...\VisualStudio\10.0\BindingPaths\{5C48C732-5C7F-40f0-87A7-05C4F15BC8C3}]
    ///     "$PackageFolder$"=""
    ///     
    /// This would register the "PackageFolder" (i.e. the location of the pkgdef file) as a directory to be probed
    /// for assemblies to load.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
    public sealed class ProvideBindingPathAttribute : RegistrationAttribute
    {
        /// <summary>
        /// An optional SubPath to set after $PackageFolder$. This should be used
        /// if the assemblies to be probed reside in a different directory than
        /// the pkgdef file.
        /// </summary>
        public string SubPath { get; set; }

        private static string GetPathToKey(RegistrationContext context)
        {
            return string.Concat(@"BindingPaths\", context.ComponentType.GUID.ToString("B").ToUpperInvariant());
        }

        public override void Register(RegistrationContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            using (Key childKey = context.CreateKey(GetPathToKey(context)))
            {
                StringBuilder keyName = new StringBuilder(context.ComponentPath); 
                if (!string.IsNullOrEmpty(SubPath))
                {
                    keyName.Append("\\");
                    keyName.Append(SubPath);
                }

                childKey.SetValue(keyName.ToString(), string.Empty);
            }
        }

        public override void Unregister(RegistrationContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            context.RemoveKey(GetPathToKey(context));
        }
    }
}
Sam Harwell
  • 97,721
  • 20
  • 209
  • 280
  • Yeah, but I'm not talking about loading packages here. Unmanaged DLLs and packages use quite different mechanisms. – Puppy Aug 13 '13 at 17:15