none
Освободить память в проекте Win32, С++ от объектов WPF (C#) из DLL RRS feed

  • Вопрос

  • Проект Win32, С++. Выполнен в среде VS 2010. Содержимое WPF из своей DLL размещаю в окне Win32. Технология описана на сайте Майкрософт https://msdn.microsoft.com/ru-ru/library/ms744829(v=vs.110).aspx  Проблема с освобождением памяти. Открытие и закрытие окна съедает за раз по 8 Мбайт. Все, что связано с ресурсами окна  на С++ освобождаю при обработке события WM_CLOSE  окна. А вот UserControl из своей DLL на C# видимо выгружается не полностью. В DLL используются  переменные различного типа, статические классы, пользовательские контролы.   Объект HwndSource (который необходим для переноса содержимого WPF и связанное с ним содержимое WPF в окно Win32) остается в памяти до тех пор, пока  не будет уничтожен дескриптор окна.   Дискриптор окна обнуляю там же, при обработке события WM_CLOSE   окна, а память не вся выгружается наверное. Хотя, по  идее, там должен сборщик мусора эту работу выполнить. Как решить эту проблему?

    27 июля 2015 г. 20:24

Все ответы

  • Добрый день.

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

    28 июля 2015 г. 5:36
    Отвечающий
  • Да, при выгрузке пользовательского контрола из DLL (по технологии WPF) два раза запускаю сборщик мусора (пытаюсь):

    private void UserControl_Unloaded(object sender, RoutedEventArgs e)

    {

        //обнуляю все, что было и

         GC.Collect();

         GC.Collect();

     }

    Такое впечатление, что без результата. Может зря два раза? Это от отчаяния. Я еще и при закрытии окна в процессе Win32 тоже пытаюсь (из неуправляемого кода управлять очистить управляемый):

    System::GC::Collect();

    Может скажете, что это глупо, а что делать? Вообще у меня в этой DLL есть и статические классы на С# на Framework, которые активно использую и в этом контроле и из кода самого приложения, на С++.

    Да был такой эксперимент, когда памяти не хватает, окно с контролом из DLL вылетает. Приходит сообщение что видеодрайвер был заново восстановлен и запущен. После этого открыть окно уже не удается. Только после остановки приложения. Посмотрю, что там с объемом памяти на этот процесс происходит. Напишу.

    28 июля 2015 г. 7:06
  • При открытии-закрытии этого окна объем памяти, занятый процессом сначала (1-й раз) был больше, чем при загрузке окна во второй и третий разы, а потом постепенно увеличивался где-то по 8 Мбайт. Последний раз перед вылетом вырос сразу на 50 Мбайт. При вылете - объем, занятый этим процессом не уменьшился.

    После 7-8 переоткрытий (если можно так сказать) окна, увеличилось и количество потоков почему-то. Хотя в коде никаких потоков специально сам не создаю.

    28 июля 2015 г. 8:39
  • Запустите профайлер памяти и посмотрите кто держит память:

    http://blogs.msdn.com/b/dotnet/archive/2013/04/04/net-memory-allocation-profiling-with-visual-studio-2012.aspx

    Помните что все объекты ссылки на которые находятся в статических классах и все на что они держат ссылки, прямо или косвенно, никогда не будут собраны GC и останутся в памяти навсегда.

    Так же, подумайте об изменении работы проекта. Сделайте его управляемым приложением WPF с вызовом кода C++ через P/Invoke или COM интероп.


    This posting is provided "AS IS" with no warranties, and confers no rights.

    28 июля 2015 г. 16:09
    Модератор
  • Все конечно, запутано. Упрощаем задачу.  В проекте С++ Имеется класс:

    public ref class WPFPageHostCay

    {

    public:

           WPFPageHostCay(){};

           static wpfcay::UserCay^ hostedPage;

           static System::Windows::Interop::HwndSource^ hwndSource;

           static HWND hWnd;

    };

    В отдельной функции его поля заполняются данными:

    void GetHwnd()

    {

    System::Windows::Interop::HwndSourceParameters^ sourceParams =

    gcnew System::Windows::Interop::HwndSourceParameters("hi");

    WPFPageHostCay::hwndSource = gcnew System::Windows::Interop::HwndSource(*sourceParams);

    WPFPageHostCay::hostedPage = gcnew wpfcay::UserCay();  

    WPFPageHostCay::hostedPage->OnButtonLamp +=

    gcnew wpfcay::UserCay::ButtonLampHandler(WPFButtonLampCay);

    WPFPageHostCay::hostedPage->OnTaskAction +=

    gcnew wpfcay::UserCay::TaskActionHandler(OnTaskEventResult);

    WPFPageHostCay::hwndSource->RootVisual = WPFPageHostCay::hostedPage;

    WPFPageHostCay::hWnd = (HWND)WPFPageHostCay::hwndSource->Handle.ToPointer();

    }

    Класс должен удаляться сборщиком мусора. А как очистить его поля от данных? Вариант не проходит:

    WPFPageHostCay::hwndSource = NULL;

    WPFPageHostCay::hostedPage = NULL; //Невозможно преобразовать ... в "int".

     
    30 июля 2015 г. 7:12
  • Итак, сам себе отвечаю: все нормально разрешилось. ссылки на объект WPF в ref class в моем проекте надо закрывать nullptr. И пришлось, конечно, убрать всю функциональность из  этого объекта WPF в отдельный статический класс на C#, который не требуется выгружать из памяти при закрытии окна с WPF. На всякий случай пришлось разобраться и с деструкторами C#.

    Мне необходим был проект на С++ для работы с 3D-графикой (DirectX), составляющей половину проекта. Хотя я большой поклонник WPF, к сожалению эта технология напрямую с DirectX не работает.  А так получилось, как говорят, "два в одном флаконе".  При этом делать весь проект на функциях Win32 очень трудоемко, да и лень, после того, как "полюбил" WPF.  Взаимодействие через статические события, того же статического класса и его методы работает прекрасно.

    Единственное, что осталось непонятным: почему код C# назвали управляемым? С точки зрения разработчика именно он -  "неуправляемый". Выгрузка его и освобождение памяти в проекте никак мной не контролируется. А C++ на Win32 действительно  полностью управляемый разработчиком.

    10 августа 2015 г. 6:46