Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > Как правильно импортировать строку в C#


Автор: Competitor 8.9.2007, 14:49
Привет всем.
Сделал dll'ку на С++, которая работает с WMI и выдает на выход какую-то строку типа BSTR. Я импортирую эту дллку с свой основной проект на C#. Как мне передать значение из С++ дллки в C#, чтобы там оно нормально прочиталось.
Допустим метод в С++ на экспорт выглядит так:
Выделить всёкод C++
Код

private:
    BSTR bstrData;
extern "C" __declspec(dllexport) BSTR GetData()
{
    return bstrData
}

А в C# метод импорта выглядит так:
Выделить всёкод C#
Код

        [DllImport("Test1.dll")]
        public static extern string GetData();

При таком написании вываливается ошибка записи в память. Подскажите плиз как решить эту проблему? 

Автор: Stepan_Lameroff 8.9.2007, 15:47
А так не пробовали?

Код

[DllImport("Test1.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string GetData();

Автор: snav 9.9.2007, 09:21
Competitor,
Скорее всего, ошибка в неправильном декларировании возвращаемого значение на C#. Это связано с особенностями управления памятью при маршалинге. Дело в том, что существует несколько стратегий работы памятью при возврате указателя на строку. Различие состоит в том, кто отвечает за освобождение выделенной памяти и как должно осуществляться освобождение памяти. В твоем примере импортируемая функция объявлена следующим образом:

[DllImport("Test1.dll")]
public static extern string GetData();

При таком объявлении маршалер .NET использует стратегию, принятую в COM. Т.е. считается, что импортируемая функция DLL выделяет память, а приложение .NET должно ее освободить. Поэтому маршалер, получив указатель на строку BSTR, копирует строку в string, а затем пытается освободить память указателя, вызвав функцию CoTaskMemFree(). Видимо, из-за этого и происходит ошибка.

Скорее всего, в твоей DLL применяется подход, подразумевающий, что приложение .NET не должно освобождать память, захваченную для указателя. В этом случае объявление функции должно быть следующим:

[DllImport("Test1.dll")]
private static extern IntPtr GetData(); 

Полученный таким образом указатель нужно вручную преобразовать в string, например:

public static string GetDataEx() 
{ 
    IntPtr ptr = GetData(); 

    if ( ptr == IntPtr.Zero ) 
        return string.Empty; 

    return Marshal.PtrToStringBSTR(ptr); 
} 

Возможно, что вместо метода Marshal.PtrToStringBSTR() тебе нужен другой метод типа Marshal.PtrToStringXXXX() в зависимости от того, какой указатель ты возвращаешь.

Более подробно можно почитать в MSDN: http://msdn.microsoft.com/library/rus/default.asp?url=/library/rus/cpguide/html/cpconmemorymanagement.asp

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)