Как се извиква управлявана (C#) функция от C++?

Имам C# DLL файлов проект (my_cs_dll.dll), който дефинира статичен клас със статична членска функция.

namespace Foo
{
    public static class Bar
    {
        public static double GetNumber() { return 1.0; }
    }
}

Имам и C++ DLL проект, който използва /clr.

#using <my_cs_dll.dll>

double get_number_from_cs() { return Foo::Bar::GetNumber(); }

Добавих препратка към 'my_cs_dll.dll' в раздела за препратки към общите свойства на проекта C++ (зависимостите копиране локално/копиране са И двете True).

И също така добавих пътя към 'my_cs_dll.dll' в раздела „Разрешаване#използване на препратки“ на проекта C++ Configuration Properties C/C++ General.

Всичко се изгражда без грешка, но по време на изпълнение продължавам да получавам изключение „System.IO.FileNotFound“ от системата, твърдейки, че не може да намери сборката my_cs_dll.dll.

И двата DLL файла определено присъстват в същата директория, от която стартирам.

Опитах всякакви вариации на настройките, споменати по-горе, и прочетох всичко, което можах да намеря за manged/unmanaged interop, но изглежда не мога да разбера какво не е наред...

Използвам Visual Studio 2008 и .NET 3.5.


person mark    schedule 24.09.2009    source източник


Отговори (1)


Изглежда, че вашият C# асембли не се разрешава по време на изпълнение. Вашият C# dll в същата директория ли е (или поддиректория на) вашия изпълним файл? Мина известно време, откакто направих това, но си спомням, че освен ако асемблито ви не е инсталирано в GAC, то трябва да е в директорията (или поддиректория), където се намира вашият изпълним файл, за разлика от местоположението на dll, който използва то. Това е свързано с функциите за сигурност на .NET.

Ако все още имате проблеми, можете да опитате сами да използвате разрешаването на сглобката. Във вашия C++ проект с активиран clr опитайте да добавите следното:

using namespace System;
using namespace System.Reflection;
void Resolve()
{
    AppDomain::CurrentDomain->AssemblyResolve +=
        gcnew ResolveEventHandler(OnAssemblyResolve);
}
Assembly ^OnAssemblyResolve(Object ^obj, ResolveEventArgs ^args)
{
#ifdef _DEBUG
    String ^path = gcnew String(_T("<path to your debug directory>"));
#else
    String ^path = gcnew String(_T("<path to your release directory>"));
#endif
    array<String^>^ assemblies =
        System::IO::Directory::GetFiles(path, _T("*.dll"));
    for (long ii = 0; ii < assemblies->Length; ii++) {
        AssemblyName ^name = AssemblyName::GetAssemblyName(assemblies[ii]);
        if (AssemblyName::ReferenceMatchesDefinition(gcnew AssemblyName(args->Name), name)) {
            return Assembly::Load(name);
        }
    }
    return nullptr;
}

Може да се наложи да промените малко кода, за да го накарате да се компилира във вашия проект. В моя случай направих двете функции статични методи на клас в моя clr-активиран проект. Просто се уверете, че сте извикали функцията Resolve() в началото на кода си, т.е. преди да опитате да извикате get_number_from_cs().

Докато използването на COM е опция, не е необходимо. Вие сте на прав път с настоящия си подход. Ако искате малко държане на ръка, погледнете този пример за CodeProject. Това е този, който следвам, за да накарам неуправляваното си приложение да използва моите управлявани сборки.

person Matt Davis    schedule 24.09.2009
comment
Здравей Мат, трябва да е в директорията (или поддиректорията), където се намира вашият изпълним файл, за разлика от местоположението на dll, който го използва. Dll файловете са вложени в няколко поддиректории под exe. Преместването на управляваната dll в същата директория като изпълним изчисти проблема веднага. Благодаря ти!! - person mark; 24.09.2009
comment
ОК страхотно. Останах с впечатлението, че докато dll е в поддиректория на изпълнимия файл, независимо колко дълбоко, асемблито ще бъде разрешено. Но както казах, мина известно време, откакто го гледах. - person Matt Davis; 24.09.2009
comment
Използвах подобен код при зареждане на C# DLL от вграден ресурс. Номерът беше да поставите този код във файл, който не препраща към C# DLL. Изглежда, че забавянето на зареждането на .Net асембли се случва при влизане в компилираща единица (.cpp файл), която съдържа препратка към въпросната асембли, а не когато извикването в референтната асембли е действително направено, ако изобщо се случи. - person mheyman; 07.02.2013