从c++ dll中获取结构体到c#应用程序中

Get struct from C++ dll into a C# application

本文关键字:应用程序 结构体 获取 c++ dll      更新时间:2023-10-16

我正在学习c#,并有一些经验。对于一个小项目,我需要在我的c#应用程序中实现一个c++ dll。它是一个车牌识别sdk。我可以初始化它,所以调用这个c++代码是有效的。但是我有一个问题,从c++代码中接收一个结构体,其中包含字符串。我试了很多次,在这里读了很多书,但我没有成功。这是c#端:

[DllImport("D:\processor.dll", EntryPoint = "StartALPR",   CallingConvention   = CallingConvention.Cdecl)]
[return:MarshalAs(UnmanagedType.LPStruct)]
 public static extern TMyData StartALPR(TImageSource tImageSource);
c#结构体TMyData:
[StructLayout(LayoutKind.Sequential)]  
public  struct  TMyData
{
    [MarshalAs(UnmanagedType.LPTStr)]        
    public  string PlateString;

   [MarshalAs(UnmanagedType.LPTStr)]
   public string PlateXML;

   [MarshalAs(UnmanagedType.LPTStr)]
   public  string LastError;
};

这是我们调用来发送tImageSource的方法它包含一个字符串和一个要分析的图像的文件路径。

alprResult = StartALPR(tImageSource);

文件被分析了,所以这是有效的。我可以在VS2015的输出中看到板串。但我期待一个结构体"alprResult"回到TMydata中定义的,但我得到一个异常,方法符号与pinvoke符号不兼容。我所拥有的唯一信息是如何在c++中使用此dll/代码的示例。下面是c++代码:

TImageSource SImageSource;
TMyData *pd;
/* Set input image */
SImageSource.MyImageFile = AnsiString(CarImage).c_str();
Memo1->Lines->Clear();
/* Starting plate detect and recognition */
pd = StartALPR(&SImageSource);
/* Standard XML result, the plate datas with numbers and positions */
Memo1->Lines->Add(pd->PlateXML);
/* last error message */
Memo2->Lines->Add(pd->LastError);
/* Best characters on plate */
Edit1->Text = pd->PlateString;

这是同一个例子中的c++结构体:

   struct TMyData;
   typedef TMyData *PMyData;
      struct TMyData
      {
      /**
      PlateString: Best license plate number of the plate group
      */
   const char * PlateString;        
   /**
  PlateXML: Plate group data in standard XML string
  */
  const char * PlateXML;        
  /**
  LastError: Laast config error ex: bad file extensions         ..                 Default: empty
   */
   const char * LastError;      
        };

如何在c#中使用它?

实际上我最近不得不解决一个类似的问题。我只是将数据序列化到一个结构体中,并通过本地zeromq套接字发送。

//
//  Weather update server in C++
//  Binds PUB socket to tcp://*:5556
//  Publishes random weather updates
//
//  Olivier Chamoux <olivier.chamoux@fr.thalesgroup.com>
//
#include <zmq.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#if (defined (WIN32))
#include <zhelpers.hpp>
#endif
#define within(num) (int) ((float) num * random () / (RAND_MAX + 1.0))
int main () {
    //  Prepare our context and publisher
    zmq::context_t context (1);
    zmq::socket_t publisher (context, ZMQ_PUB);
    publisher.bind("tcp://*:5556");
    publisher.bind("ipc://weather.ipc");                // Not usable on Windows.
    //  Initialize random number generator
    srandom ((unsigned) time (NULL));
    while (1) {
        int zipcode, temperature, relhumidity;
        //  Get values that will fool the boss
        zipcode     = within (100000);
        temperature = within (215) - 80;
        relhumidity = within (50) + 10;
        //  Send message to all subscribers
        zmq::message_t message(20);
        snprintf ((char *) message.data(), 20 ,
            "%05d %d %d", zipcode, temperature, relhumidity);
        publisher.send(message);
    }
    return 0;
}

和现在的客户端

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using ZeroMQ;
namespace Examples
{
    static partial class Program
    {
        public static void WUClient(string[] args)
        {
            //
            // Weather update client
            // Connects SUB socket to tcp://127.0.0.1:5556
            // Collects weather updates and finds avg temp in zipcode
            //
            // Author: metadings
            //
            if (args == null || args.Length < 2)
            {
                Console.WriteLine();
                Console.WriteLine("Usage: ./{0} WUClient [ZipCode] [Endpoint]", AppDomain.CurrentDomain.FriendlyName);
                Console.WriteLine();
                Console.WriteLine("    ZipCode   The zip code to subscribe. Default is 72622 Nürtingen");
                Console.WriteLine("    Endpoint  Where WUClient should connect to.");
                Console.WriteLine("              Default is tcp://127.0.0.1:5556");
                Console.WriteLine();
                if (args.Length < 1)
                    args = new string[] { "72622", "tcp://127.0.0.1:5556" };
                else
                    args = new string[] { args[0], "tcp://127.0.0.1:5556" };
            }
            string endpoint = args[1];
            // Socket to talk to server
            using (var context = new ZContext())
            using (var subscriber = new ZSocket(context, ZSocketType.SUB))
            {
                string connect_to = args[1];
                Console.WriteLine("I: Connecting to {0}…", connect_to);
                subscriber.Connect(connect_to);
                /* foreach (IPAddress address in WUProxy_GetPublicIPs())
                    {
                        var epgmAddress = string.Format("epgm://{0};239.192.1.1:8100", address);
                        Console.WriteLine("I: Connecting to {0}…", epgmAddress);
                        subscriber.Connect(epgmAddress);
                    }
                } */
                // Subscribe to zipcode
                string zipCode = args[0];
                Console.WriteLine("I: Subscribing to zip code {0}…", zipCode);
                subscriber.Subscribe(zipCode);
                // Process 10 updates
                int i = 0;
                long total_temperature = 0;
                for (; i < 20; ++i)
                {
                    using (var replyFrame = subscriber.ReceiveFrame())
                    {
                        string reply = replyFrame.ReadString();
                        Console.WriteLine(reply);
                        total_temperature += Convert.ToInt64(reply.Split(' ')[1]);
                    }
                }
                Console.WriteLine("Average temperature for zipcode '{0}' was {1}°", zipCode, (total_temperature / i));
            }
        }
    }
}