Оболочка C # для неуправляемой DLL - PullRequest
2 голосов
/ 19 августа 2011

Прежде всего - я знаю, как вызывать неуправляемые функции.Я застрял с методами интерфейса в неуправляемых DLL.Я думаю, написание оболочки - это то, что мне нужно.

Пожалуйста, помогите мне с реальным кодом.

У меня есть:

  • старая DLL (C);
  • 2 заголовочных файла;
  • lib файлов (зачем?).
  • ! НЕТ ИСТОЧНИКОВ!

Вот некоторый код в .h файле(достопримечательность - ОБЪЯВИТЬ ИНТЕРФЕЙС):

#ifdef  __BUILD_DLL
#define BLNETPASS_EXPORT __declspec(dllexport)
#else
#define BLNETPASS_EXPORT
#endif

#include "BlNetpassUid.h"
extern "C"
{
 BLNETPASS_EXPORT HRESULT WINAPI BlNetPassCreateA
                 (LPCSTR pHostName, const GUID *, VOID **);

 BLNETPASS_EXPORT HRESULT WINAPI BlNetPassCreateW
                 (LPCWSTR pHostName, const GUID *, VOID **);

 BLNETPASS_EXPORT HRESULT WINAPI CanBeUnload (DWORD dTimeout );


...

#ifdef  UNICODE
#define BlNetPassCreate BlNetPassCreateW
#else
#define BlNetPassCreate BlNetPassCreateA
#endif

#undef INTERFACE
#define INTERFACE INetPass
DECLARE_INTERFACE_ (INetPass, IUnknown)
{
// IUnknown methods

 STDMETHOD  (    QueryInterface)(REFIID, VOID **)   PURE;
 STDMETHOD_ (ULONG,      AddRef)()          PURE;
 STDMETHOD_ (ULONG,     Release)()          PURE;

// INetPass methods

 STDMETHOD  ( CreateNetPassEnum)(VOID **, REFIID cid,
                         REFIID iid)    PURE;
};

Это не COM-объект, я думаю, я не могу использовать импорт библиотеки типов для этой библиотеки DLL

Что мне нужно:

  • call BlNetPassCreateA;
  • получить указатель на интерфейс INetPass;
  • метод интерфейса вызова CreateNetPassEnum.

Вот как я это делал раньше (это PInvoke):

[DllImport("BlNetpassApi")] 
public static extern int BlNetPassCreateA(string pHostName, ref Guid GUID, out IntPtr rINetPass);

...
...

 IntPtr something; 
 Guid temp_guid = Guid.Empty;

 int temp_int = BlNetPassCreateA(null, ref temp_guid, out something);

Хорошо!IntPtr содержит некоторые данные, но что дальше - как описать интерфейс, который находится позади, и вызвать его метод?

Ответы [ 2 ]

1 голос
/ 19 августа 2011

Вы должны написать управляемую оболочку C ++ для вызова функций c ++ и затем использовать ее в своем .Net проекте.Потому что нет нормального способа вызова функций на экземплярах неуправляемых объектов из .Net.

1 голос
/ 19 августа 2011

Я бы создал C # -эквивалент C-интерфейса с dll-import для всех соответствующих функций. Вы можете даже определить структуры, которые соответствуют тем на стороне C. Пример того, как я бы это сделал:

    // This defines a custom datatype present in our dll.
    [StructLayout(LayoutKind.Sequential)]
    public struct MyDllStruct
    {
        int aValue;

        [MarshalAs(UnmanagedType.Bool)]
        bool anotherValue;
    }

    public static class MyDllInterface
    {
        // The function in the interface can be customized by instead             
        // specifying the EntryPoint in the DllImport-attribute.
        [DllImport("MyDll.dll", EntryPoint = "mydll_function")]
        public static extern int MyDllFunction(
                int param1,
            // We want this to become a C-style boolean 
            // (false == 0, true != 0)
                [MarshalAs(UnmanagedType.Bool)]
                bool param2,
            // We want the dll to write to our struct, and to be  
            // able to get the data written to it back we need to 
            // specify the Out-attribute. If we were just feeding the 
            // parameter to the dll we could just use the In-attribute
            // instead, or combine them if we want to achieve both.
                [Out, MarshalAs(UnmanagedType.LPStruct)]
                MyDllStruct resultStruct
            );

        [DllImport("MyDll.dll")]
        // Sometime we need to do a little magic on our own so we let 
        // this function return an IntPtr, and not the struct.
        public static extern IntPtr get_a_pointer_struct();

        // By not defining the EntryPoint-parameter the application 
        // expects that the dll function is named just as the
        // C#-function declaration.
        [DllImport("MyDll.dll")]
        // Here we specify that we are expecting a char-array 
        // back from the function, so the application should
        // marshal it as such.
        [return: MarshalAs(UnmanagedType.LPStr)]
        public static extern string mydll_get_errormsg();
    }

    // This class "converts" the C-style functions
    // into something more similar to the common
    // way C# is usually written.
    public class MyDllWrapper
    {
        // Calls the MyDllInterface.MyDllFunction and returns
        // the resulting struct. If an error occured, an
        // exception is thrown.
        public MyDllStruct CallFunction(int param1, bool param2)
        {
            MyDllStruct result = new MyDllStruct();

            int errorCode = MyDllInterface.MyDllFunction(
                                param1, param2, result);

            if (errorCode < 0)
                throw new Exception(String.Format(
                    "We got an error with code {0}. Message: ", 
                    errorCode, MyDllInterface.mydll_get_errormsg()));

            return result;
        }

        // Gets a pointer to a struct, and converts it 
        // into a structure.
        public MyDllStruct GetStruct()
        {
            IntPtr structPtr = MyDllInterface.get_a_pointer_struct();

            return (MyDllStruct)Marshal.PtrToStructure(
                structPtr, typeof(MyDllStruct));
        }
    }

В приведенном выше коде, вероятно, есть куча ошибок, и маршалинг, скорее всего, неверен. Целью было в основном показать шаблон для того, как я обычно реализую C-интерфейсы в C #.

РЕДАКТИРОВАТЬ: Я не совсем уверен, как работает ваш C-код, но, глядя на заголовок, я бы сказал, что IntPtr, который вы получили от вызова BlNetPassCreateA, является вашим указателем this при последующих вызовах интерфейса. Вызов CreateNetPassEnum, вероятно, будет выполнен следующим образом:

    [DllImport("BlNetPassApi")]
    public static extern void CreateNetPassEnum(IntPtr this, int cid, int reffid);

    ...
    IntPtr something; 
    Guid temp_guid = Guid.Empty;

    int temp_int = BlNetPassCreateA(null, ref temp_guid, out something);

    CreateNetPassEnum(in something, 10, 10);

Я на самом деле не знаю, какого типа REFIID, как мы получаем соответствующие значения для них, но я надеюсь, что это поможет вам по крайней мере приступить к решению.

...