#c# #c #com
#c# #c #com
Вопрос:
У меня есть следующая тестовая настройка, написанная в Visual Studio 2015 и использующая Windows SDK 7.1:
- Проект C # (TestCs) с атрибутами COM. Он предоставляет метод ‘Run’ и вызывает событие ‘NotifyEvent’
- C DirectShow filter (тестовый фильтр). Он построен на примере TransInPlace и содержит TestCs
- Консольное приложение C (TestApp). Он просто загружает тестовый фильтр
При компиляции в Debug и запуске тестового приложения функциональность, похоже, работает, и событие обрабатывается, как ожидалось. Проблема в том, что приложение вылетает при выходе, и пока мне не удалось выяснить, почему. Буду очень признателен за чью-либо помощь здесь.
А теперь подробнее: приложение выглядит следующим образом:
#include <windows.h>
#include <initguid.h>
#include <atlbase.h>
#include <streams.h>
// {E705C8D3-EE60-4012-9E83-3CFE4A11D1B5}
DEFINE_GUID(CLSID_TestFilter,
0xe705c8d3, 0xee60, 0x4012, 0x9e, 0x83, 0x3c, 0xfe, 0x4a, 0x11, 0xd1, 0xb5);
void Test()
{
CComPtr<IBaseFilter> pFilter;
HRESULT hr = CoCreateInstance(CLSID_TestFilter, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)amp;pFilter);
if (FAILED(hr))
return;
}
void main()
{
CoInitialize(NULL);
Test();
CoUninitialize();
}
Сбой происходит при выходе из области действия основной функции, и я ничего не могу получить из стека вызовов.
В тесте CS определен следующий интерфейс
using System;
using System.Runtime.InteropServices;
namespace TestCs
{
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IFireTestCsEvents
{
[DispId(1)]
void NotifyEvent(string message);
}
public delegate void NotifyEventEventHandler(string message);
public interface ITestCs : IDisposable
{
event NotifyEventEventHandler NotifyEvent;
bool Run(string fileName);
void Close();
}
}
Это реализация интерфейса
using System.Runtime.InteropServices;
namespace TestCs
{
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IFireTestCsEvents))]
public class TestCsImp : ITestCs
{
public event NotifyEventEventHandler NotifyEvent;
public TestCsImp()
{
}
public bool Run(string fileName)
{
NotifyEvent?.Invoke(fileName);
return true;
}
public void Close()
{
}
public void Dispose()
{
Close();
}
}
}
As for the TestFilter, it is based on TransInPlace class, and adding TestCsWrapper object to host TestCs
This is the header file
#pragma once
#import "TestCs.tlb"
using namespace TestCs;
class TestCsWrapper :
public IDispEventSimpleImpl<1, TestCsWrapper, amp;__uuidof(IFireTestCsEvents)>
{
public:
BEGIN_SINK_MAP(TestCsWrapper)
SINK_ENTRY_INFO(1, __uuidof(IFireTestCsEvents), 0x1, HandleEvent, amp;NotifyEvent)
END_SINK_MAP()
void __stdcall HandleEvent(BSTR message);
public:
TestCsWrapper();
virtual ~TestCsWrapper();
bool Open();
void Close();
bool IsValid();
bool Load(const wchar_t *fileName);
private:
ITestCsPtr m_pTestCs;
static _ATL_FUNC_INFO NotifyEvent;
};
И это реализация
#include "Pch.h"
#include "TestCsWrapper.h"
_ATL_FUNC_INFO TestCsWrapper::NotifyEvent = { CC_STDCALL, VT_EMPTY, 1, { VT_BSTR } };
#define CREATE_CRASH
void __stdcall TestCsWrapper::HandleEvent(BSTR message)
{
::SysFreeString(message);
}
TestCsWrapper::TestCsWrapper()
{
}
TestCsWrapper::~TestCsWrapper()
{
Close();
}
bool TestCsWrapper::Open()
{
m_pTestCs = ITestCsPtr(__uuidof(TestCsImp));
if(!IsValid())
return false;
#ifdef CREATE_CRASH
DispEventAdvise(m_pTestCs);
#endif
return true;
}
void TestCsWrapper::Close()
{
if(IsValid())
{
#ifdef CREATE_CRASH
DispEventUnadvise(m_pTestCs);
#endif
m_pTestCs->Close();
m_pTestCs->Release();
m_pTestCs.Detach();
}
}
bool TestCsWrapper::IsValid()
{
return (m_pTestCs != NULL);
}
bool TestCsWrapper::Load(const wchar_t *fileName)
{
if(!IsValid())
return false;
BSTR bstr = ::SysAllocString(fileName);
VARIANT_BOOL success = m_pTestCs->Run(bstr);
::SysFreeString(bstr);
return success == VARIANT_TRUE;
}
Когда я отключил код DispEventAdvise, приложение завершает работу без каких-либо исключений.
В вызове CreateInstance фильтра я добавил следующий код
bool TestFilter::Open()
{
if (!m_testCs.Open())
return false;
m_testCs.Load(L"some messsage");
return true;
}
Для демонстрации этого доступен полный код решения Visual Studio, который может быть предоставлен при необходимости
Надеюсь выяснить, что я делаю не так. Спасибо
Комментарии:
1. Вы должны предположить, что никому не понравится копировать / вставлять этот код. Там какой-то «явно неправильный» код. Возвращаемый тип handleEvent должен быть HRESULT, возвращать S_OK, чтобы код C # не выдавал исключения. SysFreeString() неверен, это задание вызывающего, это приведет к сбою среды CLR с помощью AccessViolationException. Наследование интерфейса в C # работает не так, как в COM, если вы «наследуете» от IDisposable, тогда вы должны явно сделать Dispose() первым элементом интерфейса. Вызов Release() неверен, тип интеллектуального указателя уже делает это.
2. Убедитесь, что вы можете видеть диагностику из кучи отладки, посмотрите в окне вывода. Код выхода программы, скорее всего, будет 0xC0000374 из-за повреждения кучи.
3. Спасибо, Ганс. После применения исправлений, как вы отметили, проблема сохраняется.
4. Что касается строки ошибки в окне вывода, то она выглядит следующим образом: Исключение, вызванное 0x00007FFF6539DA26 (clr.dll ) в TestApp.exe: 0xC0000005: нарушение доступа к местоположению чтения 0xffffffffffffffffffffffff.
5. В любом случае, полное решение VS можно загрузить по ссылке