【问题标题】:C++ unmanaged function callback char to C# charC++ 非托管函数回调 char 到 C# char
【发布时间】:2020-05-09 00:35:32
【问题描述】:

我有一个 C# 表单。我正在从 C++ dll 调用非托管函数。

我有一个名为 FUNDownDevCBEx 的回调,它返回变量 int nType, IntPtr pData

所以指向结构体_tagGPSMDVRInfo p = (_tagGPSMDVRInfo)Marshal.PtrToStructure(pData, typeof(_tagGPSMDVRInfo));我可以得到指针变量。

但是,当我指向 szIDNO 时,我只得到字符串的最后一个字符,我不知道为什么..

我希望 szIDNO 以增量方式显示名称,但我只得到最后一个字符。

预期:

00091
00001
01211
01222
01504

我得到了什么:

4
2
1
1
1

4

void FUNDownDevCBEx(int nType, IntPtr pData, Form1 form1)是回调循环返回szIDNO

任何建议将不胜感激。

C++ 类型定义:

 typedef struct _tagGPSInfo
 {
     int nID;
     char szIDNO[32];               
     char szName[32];               
     char szSIMCard[16];                
     union
     {
         GPSInfo_S gDVRInfo;
         GPSMobileInfo_S gMobileInfo;
         GPSDVSInfo_S DVSInfo;
     };
 }GPSInfo_S, *LPGPSInfo_S;

C++ 回调如下所示:

void (CALLBACK * FUNDownDevCBEx)(int nType, void* pData, void * pUsr)

我的 C# 转换代码:

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct _tagGPSInfo
    {
        [MarshalAs(UnmanagedType.I4)]
        public int nID;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string szIDNO;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string szName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
        public string szSIMCard;
    }



 private void devlist_Click(object sender, EventArgs e)
        {
            try
            {
                IntPtr _lHandle = IntPtr.Zero;
                NETClass.NETCLIENT_DEVOpenDevDownEx(ref _lHandle);
                NETClass.NETCLIENT_DEVSetCharEx( _lHandle);
                FUNDownDevCBEx _1callback = new FUNDownDevCBEx(FUNDownDevCBEx);
                NETClass.NETCLIENT_DEVRegDevDownCBEx( _lHandle, this, _1callback);
                NETClass.NETCLIENT_DEVStartDevDownEx(_lHandle, 0,0);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "List Exception");
                return;
            }
        }



      static void FUNDownDevCBEx(int nType, IntPtr pData, Form1 form1)
    {
        try
        {
            _tagGPSInfo p = (_tagGPSMDVRInfo)Marshal.PtrToStructure(pData, typeof(_tagGPSInfo));
            int nID = p.nID;
            string szIDNO = p.szIDNO;
            switch (nType)
            {
                case 0:
                    form1.Invoke((MethodInvoker)(() => form1.memoBox.AppendText(" DATA =" + pData + " nID=" + nID + " szIDNO=" + szIDNO + Environment.NewLine)));
                    break;
                case 1:
                    //MessageBox.Show("GPS_DEV_DOWN_GROUP" + Environment.NewLine + " DATA =" + pData + " nID=" + nID);
                    break;
                case 2:
                    //MessageBox.Show("GPS_DEV_DOWN_FAILED" + Environment.NewLine + " DATA =" + pData );
                    break;
                case 3:
                    //MessageBox.Show("GPS_DEV_DOWN_SUC" + Environment.NewLine + " DATA =" + pData);
                    break;
                case 4:
                    //MessageBox.Show("GPS_DEV_DOWN_RELATION" + Environment.NewLine + " DATA =" + pData + " nID=" + nID);
                    break;
                default:
                    //MessageBox.Show("DEFAULT");
                    break;
            }
            //MessageBox.Show("nType= " + nType + " pData= " + pData);
            NETClass.NETCLIENT_DEVStopDevDownEx(IntPtr.Zero);
            NETClass.NETCLIENT_DEVCloseDevDownEx(IntPtr.Zero);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString(), "FUNDownDevCBEx Exception");
            return;
        }
    }

我的 C# NETClass:

namespace ConversionTest
{

    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate void FUNDownDevCBEx(int nType, IntPtr data, Form1 form1);


    class NETClass
    {       
        [DllImport("libnetclient.dll", CallingConvention = CallingConvention.StdCall,SetLastError = true)]
        public static extern int NETCLIENT_DEVOpenDevDownEx(ref IntPtr lpHandle);

        [DllImport("libnetclient.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern int NETCLIENT_DEVRegDevDownCBEx( IntPtr lHandle, Form1 form1, FUNDownDevCBEx _callback);

        [DllImport("libnetclient.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern int NETCLIENT_DEVStartDevDownEx( IntPtr lHandle, int nMgrType, int nDevType);

        [DllImport("libnetclient.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern int NETCLIENT_DEVStopDevDownEx( IntPtr lHandle);

        [DllImport("libnetclient.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern int NETCLIENT_DEVCloseDevDownEx( IntPtr lHandle);

        [DllImport("libnetclient.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern int NETCLIENT_DEVSetCharEx( IntPtr lHandle, bool bUtf8 = true);
    }
}

【问题讨论】:

  • 问题可能出在我们在这里看不到的代码中。创建minimal reproducible example
  • @NineBerry 我添加了我的网络类和按钮事件。
  • 如果您的应用程序是 64 位,则可能存在对齐问题,因此 c# 期望 szIDNO 从偏移量 8 开始,而 dll 将其放在偏移量 4 处。因此,使用 5 个字符的字符串您只能得到最后一个字符。
  • @AlexSkalozub 可能是,但在我测试的其他帐户中还有其他不同长度的较长字符串,结果相同。我认为这是一个字节数组或其他东西,它会覆盖到最后一个字节,有点令人沮丧.我在 32 位设置下运行它。
  • @alex 我不这么认为。我认为我们仍然对这里发生的事情一无所知。

标签: c# c++ callback pinvoke intptr


【解决方案1】:

感谢 cmets。解决办法是晚上好好休息,早上喝杯咖啡。

由于[StructLayout(LayoutKind.Sequential)] 是顺序的,因此结构中的每个变量都必须是顺序的。由于我的原始结构非常长,我在szIDNO 之前错过了一个int 变量,这导致指向""miss"" 的指针从我的字符串中有4 个字符...

这就是我的理解,欢迎评论...

[StructLayout(LayoutKind.Sequential)]
public struct _tagGPSMDVRInfo
{
    //struct now in proper sequence 
    [MarshalAs(UnmanagedType.I4)]
    public int nID;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string szName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string szIDNO;// <--- Now in correct position.... 
    [MarshalAs(UnmanagedType.I4)]
    public int nJingDu;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 7)]
    public string strReserve;
}



static void FUNDownDevCBEx(int nType, IntPtr pData, Form1 form1)
{
    try
    {
        switch (nType)
        {
            case 0:
                _tagGPSMDVRInfo p = new _tagGPSMDVRInfo(); // Create the managed struct
                p = Marshal.PtrToStructure<_tagGPSMDVRInfo>(pData);//simplify
                int nID = p.nID;
                string szIDNO = p.szIDNO;
                form1.Invoke((MethodInvoker)(() => form1.textBox4.AppendText(" DATA =" + pData + " nID=" + nID + " szIDNO=" + szIDNO + Environment.NewLine)));
                ;
                break;
            case 1:
                //MessageBox.Show("GPS_DEV_DOWN_GROUP" + Environment.NewLine + " DATA =" + pData + " nID=" + nID);
                break;
            case 2:
                //MessageBox.Show("GPS_DEV_DOWN_FAILED" + Environment.NewLine + " DATA =" + pData );
                break;
            case 3:
                //MessageBox.Show("GPS_DEV_DOWN_SUC" + Environment.NewLine + " DATA =" + pData);
                break;
            case 4:
                //MessageBox.Show("GPS_DEV_DOWN_RELATION" + Environment.NewLine + " DATA =" + pData + " nID=" + nID);
                break;
            default:
                //MessageBox.Show("DEFAULT");
                break;
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString(), "FUNDownDevCBEx Exception");
        return;
    }
}

【讨论】:

  • 不,这个结构定义不可能是正确的。肯定有问题,但这不是正确的解决方案。
  • 缺少结构字段会产生这里看到的问题是有道理的。但是您的答案中给出的代码没有意义,因为与原始问题相比,结构的相关开头没有变化。此外,指定“Size = 16”对结构没有意义,因为它要长得多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-17
  • 1970-01-01
  • 1970-01-01
  • 2011-01-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多