2

我正在尝试将 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 是正确的,因为我使用它成功登录了设备。但是当我在调用 GetDevConfig 失败后立即检查 GetLastError 时,它表明一个非法参数。那么,有人能指出上述代码中的非法参数吗?

以下是我的带有非法参数问题的 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;
}
4

2 回答 2

1

您的 extern 函数定义错误。让我们以您的 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);

如您的帖子评论中所述,Win32 中的长度LONG为 32 位,因此您必须使用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。它指示托管代码应如何考虑 pinvoke 函数的返回值。

于 2013-09-20T19:13:32.457 回答
0

以下是我可以看到的差异:

C++ 代码:

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

现在,LLONG是指针大小,x86 下为 32 位,x64 下为 64 位。这转化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++ 代码中,您将指针传递DWORDlpBytesReturned. 在您的 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.ZeroBytesReturned参数。也许它是一个可选参数。但我不能从这里说出来。但是,可以肯定的dwOutBufferSize是,错误声明是一个错误。

于 2013-09-21T08:15:50.983 回答