Недопустимый параметр при конвертации C SDK в C#

Я пытаюсь преобразовать C SDK в C# и сталкиваюсь с ошибкой "Недопустимый параметр" при преобразовании функции C.

Детали функции C SDK перечислены ниже

#ifndef LLONG
#ifdef WIN32
#define LLONG LONG
#else //WIN64 
#define LLONG INT64
#endif
#endif

#ifndef CLIENT_API
#define CLIENT_API  __declspec(dllexport) 
#endif

#else

#ifndef CLIENT_API
#define CLIENT_API  __declspec(dllimport)
#endif

#endif

#define CALLBACK __stdcall
#define CALL_METHOD  __stdcall  //__cdecl


// Configuration type,corresponding to CLIENT_GetDevConfig and CLIENT_SetDevConfig
#define DH_DEV_DEVICECFG            0x0001      // Device property setup 
#define DH_DEV_NETCFG               0x0002      // Network setup 
#define DH_DEV_CHANNELCFG           0x0003      // Video channel setup
#define DH_DEV_PREVIEWCFG           0x0004      // Preview parameter setup
#define DH_DEV_RECORDCFG            0x0005      // Record setup
#define DH_DEV_COMMCFG              0x0006      // COM property setup 
#define DH_DEV_ALARMCFG             0x0007      // Alarm property setup
#define DH_DEV_TIMECFG              0x0008      // DVR time setup 
#define DH_DEV_TALKCFG              0x0009      // Audio talk parameter setup 
#define DH_DEV_AUTOMTCFG            0x000A      // Auto matrix setup
#define DH_DEV_VEDIO_MARTIX         0x000B      // Local matrix control strategy setup
#define DH_DEV_MULTI_DDNS           0x000C      //  Multiple ddns setup 
#define DH_DEV_SNAP_CFG             0x000D      // Snapshot corresponding setup 
#define DH_DEV_WEB_URL_CFG          0x000E      // HTTP path setup 
#define DH_DEV_FTP_PROTO_CFG        0x000F      // FTP upload setup 
#define DH_DEV_INTERVIDEO_CFG       0x0010      // Plaform embedded setup. Now the channel parameter represents the platform type. 



// Search configuration information 
CLIENT_API BOOL  CALL_METHOD CLIENT_GetDevConfig(LLONG lLoginID, DWORD dwCommand, LONG lChannel, LPVOID lpOutBuffer, DWORD dwOutBufferSize, LPDWORD lpBytesReturned,int waittime=500);

Информация C# выглядит следующим образом:

//  [DllImport("dhnetsdk.dll", CallingConvention = CallingConvention.StdCall)]
       [DllImport("dhnetsdk.dll", CallingConvention = CallingConvention.Cdecl)]
   //  [DllImport("dhnetsdk.dll")]

      public static extern bool CLIENT_GetDevConfig(long lLoginID, 
           uint dwCommand, 
           long lChannel, 
            IntPtr lpBuffer,
           uint dwOutBufferSize, 
           uint lpBytesReturned, 
           int waittime = 500);

И я вызываю метод следующим образом:

int t = 500;
            uint BytesReturned = 0;
            uint c = 8;

            var lpOutBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(NET_TIME)));
            if (CLIENT_GetDevConfig(lLogin, c, 0, lpOutBuffer, (uint)Marshal.SizeOf(typeof(NET_TIME)),  BytesReturned, t) == false)
            {
            Console.WriteLine("GetDevConfig FAILED");
            }


[StructLayout(LayoutKind.Sequential)]
    public  struct NET_TIME
    {
     //   [FieldOffset(0)]  
        uint  dwYear;
     //   [FieldOffset(4)]
        uint  dwMonth;
     //   [FieldOffset(4)]
        uint  dwDay;
      //  [FieldOffset(4)]
        uint  dwHour;
      //  [FieldOffset(4)]
        uint  dwMinute;
      //  [FieldOffset(4)]
        uint  dwSecond;
    }

Я уверен, что lLogin верный, так как я успешно вошел в устройство, используя его. Но когда я проверяю GetLastError сразу после сбоя вызова GetDevConfig, это указывает на недопустимый параметр. Итак, кто-нибудь может указать на недопустимый параметр в приведенном выше коде?

Ниже приведен мой код C# с недопустимыми параметрами...

using System;
using System.Runtime.InteropServices;

class PlatformInvokeTest
{
    static public int lLogin;

    public delegate void fDisConnect(long lLoginID, IntPtr pchDVRIP, long nDVRPort, uint dwUser);
    public delegate void fHaveReConnect(long lLoginID, IntPtr pchDVRIP, long nDVRPort, uint dwUser);

    [DllImport("dhnetsdk.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern bool CLIENT_Init(fDisConnect cbDisConnect, uint dwUser);

    [DllImport("dhnetsdk.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void CLIENT_SetAutoReconnect(fHaveReConnect cbHaveReconnt, uint dwUser);

    [DllImport("dhnetsdk.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int CLIENT_Login(string pchDVRIP, ushort wDVRPort, string pchUserName, string pchPassword, NET_DEVICEINFO lpDeviceInfo, IntPtr error);

    [DllImport("dhnetsdk.dll", CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CLIENT_GetDevConfig(
    int loginId,
    uint command,
    int channel,
    out NET_TIME buffer,
    out uint bufferSize,
    IntPtr lpBytesReturned,
    int waittime = 500);

    [DllImport("dhnetsdk.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern bool CLIENT_Logout(long lID);

    [DllImport("dhnetsdk.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern void CLIENT_Cleanup();

    [DllImport("dhnetsdk.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern uint CLIENT_GetLastError();


    public static void fDisConnectMethod(long lLoginID, IntPtr pchDVRIP, long nDVRPort, uint dwUser)
    {
        System.Console.WriteLine("Disconnect");
        return;
    }

    public static void fHaveReConnectMethod(long lLoginID, IntPtr pchDVRIP, long nDVRPort, uint dwUser)
    {
        System.Console.WriteLine("Reconnect success");
        return;
    }

    [StructLayout(LayoutKind.Sequential)]
    public class NET_DEVICEINFO
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 48)]
        public byte[] sSerialNumber;
        public byte byAlarmInPortNum;
        public byte byAlarmOutPortNum;
        public byte byDiskNum;
        public byte byDVRType;
        public byte byChanNum;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct NET_TIME
    {
        uint dwYear;
        uint dwMonth;
        uint dwDay;
        uint dwHour;
        uint dwMinute;
        uint dwSecond;
    }

    public static void Main()
    {
        fDisConnect fDisConnecthandler = fDisConnectMethod;
        fHaveReConnect fHaveReConnecthandler = fHaveReConnectMethod;
        NET_DEVICEINFO deviceinfo = new NET_DEVICEINFO();
        IntPtr iRet = new IntPtr(0);
        CLIENT_Init(fDisConnectMethod, 0);
        CLIENT_SetAutoReconnect(fHaveReConnecthandler, 0);
        lLogin = CLIENT_Login("192.168.1.198", 31111, "user", "password", deviceinfo, iRet);

        if (lLogin <= 0)
            Console.WriteLine("Login device failed");
        else
        {
            Console.WriteLine("Login device successful");
            byte[] byteout = new byte[20];
            const int t = 500;
            IntPtr BytesReturned;
            BytesReturned = IntPtr.Zero;
            const uint c = 8;
            NET_TIME nt;
            uint sizeofnt = (uint)Marshal.SizeOf(typeof(NET_TIME));
            if (CLIENT_GetDevConfig(lLogin, c, 0, out  nt, out sizeofnt, BytesReturned, t) == false)
            {
                uint gle = CLIENT_GetLastError();
                Console.WriteLine("getDevConfig failed");
            }
            CLIENT_Logout(lLogin);
            CLIENT_Cleanup();
        }
    }
}

А вот мой код на C, который я пытаюсь перенести на C#. Работает без проблем..

#pragma comment(lib,"dhnetsdk.lib")

#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include "dhnetsdk.h"

void CALLBACK DisConnectFunc(LONG lLoginID, char *pchDVRIP, LONG nDVRPort, DWORD dwUser)
{
    printf("Disconnect.");
    return;
}

void CALLBACK AutoConnectFunc(LONG lLoginID,char *pchDVRIP,LONG nDVRPort,DWORD dwUser)
{
    printf("Reconnect success.");
    return;
}

int main(void)
{
    NET_TIME nt = {0};
    NET_DEVICEINFO deviceInfo = {0};
    unsigned long lLogin = 0;
    LPVOID OutBuffer;
    int iRet = 0;
    DWORD dwRet = 0;

    //Initialize the SDK, set the disconnection callback functions
    CLIENT_Init(DisConnectFunc,0);     
    //Setting disconnection reconnection success of callback functions. If don't call this interface, the SDK will not break reconnection.                                  
    CLIENT_SetAutoReconnect(AutoConnectFunc,0);                      
    lLogin = CLIENT_Login("192.168.1.108",31111,"user","password",&deviceInfo, &iRet);
    if(lLogin <= 0)
    {
        printf("Login device failed");
    }
    else
    {
        OutBuffer = (LPVOID)malloc(sizeof(NET_TIME));
        memset(OutBuffer, 0, sizeof(NET_TIME));
        if(CLIENT_GetDevConfig( lLogin, 8 /* DH_DEV_TIMECFG */, 0, OutBuffer, sizeof(NET_TIME), &dwRet, 500) == FALSE)
        {
            printf("Failed\n");
        }
        else
        {
            memcpy(&nt, OutBuffer, sizeof(nt));




    printf("Time %d %d %d %d %d %d\n", nt.dwYear,nt.dwMonth,nt.dwDay, nt.dwHour,nt.dwMinute, nt.dwSecond);
        }
        _getch();
    }
    CLIENT_Logout(lLogin);
    CLIENT_Cleanup();
    return 0;
}

2 ответа

Решение

Вот различия, которые я вижу:

Код C++:

CLIENT_API BOOL CALL_METHOD CLIENT_GetDevConfig(
    LLONG lLoginID, 
    DWORD dwCommand, 
    LONG lChannel, 
    LPVOID lpOutBuffer, 
    DWORD dwOutBufferSize, 
    LPDWORD lpBytesReturned,
    int waittime
);

Сейчас, LLONG размер указателя, 32-разрядный для x86, 64-разрядный для x64. Это переводится как IntPtr для C#. Я бы объявил p/invoke следующим образом:

[DllImport("dhnetsdk.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CLIENT_GetDevConfig(
    IntPtr lLoginID,
    uint dwCommand,
    int lChannel,
    out NET_TIME lpOutBuffer,
    uint dwOutBufferSize,
    out uint lpBytesReturned,
    int waittime
);

Основные проблемы, которые я вижу, это:

  • Вы переведены dwOutBufferSize неправильно. Это IN параметр, но вы передаете его по ссылке. Это наиболее вероятное объяснение неудачи.
  • В коде C++ вы передаете указатель на DWORD переменная для lpBytesReturned, В вашем коде C# вы передаете IntPtr.Zero который является нулевым указателем.

Итак, я бы позвонил CLIENT_GetDevConfig выглядит так:

NET_TIME nt;
uint sizeofnt = (uint)Marshal.SizeOf(typeof(NET_TIME));
uint BytesReturned;
if (!CLIENT_GetDevConfig(lLogin, 8, 0, out nt, sizeofnt, out BytesReturned, 500))
    ....

Может быть, вы действительно можете пройти IntPtr.Zero к BytesReturned параметр. Возможно, это необязательный параметр. Но я не могу сказать это отсюда. Однако наверняка dwOutBufferSize неправильное объявление является ошибкой.

Ваша внешняя функция неправильно определена. Давайте возьмем ваш пример вызова C.

// Search configuration information 
CLIENT_API BOOL  CALL_METHOD CLIENT_GetDevConfig(LLONG lLoginID, DWORD dwCommand, LONG lChannel, LPVOID lpOutBuffer, DWORD dwOutBufferSize, LPDWORD lpBytesReturned,int waittime=500);

Как указано в комментарии вашего поста, длина LONG 32-битный в Win32, поэтому вы должны использовать int, Вы также можете использовать ключевое слово out чтобы получить свою структуру без использования вручную Mashaller. Я бы определил вашу функцию как таковую.

[DllImport("dhnetsdk.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CLIENT_GetDevConfig(int loginId, uint command, int channel, out NET_TIME buffer, out uint bufferSize, IntPtr lpBytesReturned, int waittime = 500);

Обратите внимание на наличие дополнительного атрибута MarshalAs, Он указывает, как управляемый код должен учитывать возвращаемое значение функции pin voke'd.

Другие вопросы по тегам