如何使用 p/invoke 将 C++ TCP 通信数据传递给 C#

how to passing c++ tcp communication data to c# with p/invoke

提问人:신성영 提问时间:9/22/2023 更新时间:10/12/2023 访问量:42

问:

我想继续通过TCP通信将从C++(不是C++ / CLI)接收的数据发送到C#。

我写了这样的代码:

C++

LibMain.h

    #pragma once
    #include <string.h>
    #include <oleauto.h>

    extern "C"
    {   
    __declspec(dllexport) BSTR __stdcall cpp_test(char* str);

    __declspec(dllexport) SAFEARRAY* cpp_test2(char** str_arr, int i);

    }

LibMain.cpp

    BSTR __stdcall cpp_test(char* str)
    {
        wchar_t* pStr;

        int strSize = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, NULL);

        pStr = new WCHAR[strSize];

        MultiByteToWideChar(CP_ACP, 0, str, strlen(str) + 1, pStr, strSize);

        return SysAllocString(pStr);

    }

    SAFEARRAY* cpp_test2(char** str_arr, int len)
    {

        SAFEARRAY* psa = SafeArrayCreateVector(VT_BSTR, 0, len);
        BSTR HUGEP* arr = NULL;
        SafeArrayAccessData(psa, reinterpret_cast<void HUGEP**>(&arr));

        for (int i = 0; i < len; i++) {
            char* str = str_arr[i];
            wchar_t* pStr;

            int strSize = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, NULL);

            pStr = new WCHAR[strSize];

            MultiByteToWideChar(CP_ACP, 0, str, strlen(str) + 1, pStr, strSize);

            arr[i] = SysAllocString(pStr);
        }
        SafeArrayUnaccessData(psa);

        return psa;

    }

c#

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern bool SetDllDirectory(string lpPathName);

    [DllImport(@"CPPSampleLibDll.dll", CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.BStr)]
    static extern string cpp_test(string str);

    [DllImport(@"CPPSampleLibDll.dll", CallingConvention = CallingConvention.Cdecl)]
    [return: MarshalAs(UnmanagedType.SafeArray)]
    static extern string[] cpp_test2(string[] str_arr, int iSize);

    string rootPath = Path.GetFullPath(@".");
    SetDllDirectory(rootPath);

    string[] test = new string[2];
    test[0] = "test0";
    test[1] = "test1";

    string[] test2 = cpp_test2(test, test.Length);
    cpp_test("test2");

    foreach (string s in test2)
    {
            MessageBox.Show(s);
    }

    MessageBox.Show(cpp_test("test2"));

此代码按预期传递字符串和字符串数组,并显示一个消息框。

我想继续通过TCP通信将从C++(不是C++ / CLI)接收的数据发送到C#。

C# C++ TCP pinvoke

评论

0赞 Alan Birtles 9/22/2023
你试过什么?你在哪里被卡住了?
0赞 신성영 9/22/2023
通过 tcp 通信接收的 C++ 数据,我想通过 DllImport 将此数据发送到 C#。
0赞 Alan Birtles 9/22/2023
我知道你想要什么,但你需要解释你这样做有什么问题。Stackoverflow 不是一个免费的代码编写服务
0赞 신성영 9/22/2023
我尝试通过 C++ dll 库成功引用 C# 代码中的字符串数组,并在 c++ 上接收 tcp 通信。我想知道是否可以使用 p/invoke 将通过线程连续接收的 tpc 接收数据传输到 dll 库的 C# 代码,以及大致如何做到这一点。
0赞 Alan Birtles 9/22/2023
使用回调?

答:

0赞 신성영 10/12/2023 #1

xx.h

#pragma once
#include <string.h>
#include <oleauto.h>

#include <iostream>
#include <string>
#include <Winsock2.h>

extern "C"
{

    __declspec(dllexport) int InitializeWinsock();

    __declspec(dllexport) void CleanupWinsock();

    __declspec(dllexport) int StartServer(int port);

    __declspec(dllexport) void CloseServer();

    SOCKET serverSocket;

    typedef void(__stdcall* CallbackDelegate)(const char* message);

    __declspec(dllexport) void WaitForConnections(CallbackDelegate callback);
}

xx.cpp

#include "pch.h"
#include "LibMain.h"

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

int InitializeWinsock()
{
    WSADATA wsaData;
    return WSAStartup(MAKEWORD(2, 2), &wsaData);
}

void CleanupWinsock()
{
    WSACleanup();
}

int StartServer(int port)
{
    struct sockaddr_in serverAddr;

    serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (serverSocket == INVALID_SOCKET) {
        return -1;
    }

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(port);

    if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
        closesocket(serverSocket);
        return -1;
    }

    if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {
        closesocket(serverSocket);
        return -1;
    }

    return 0;
}

SOCKET clientSocket;

void WaitForConnections(CallbackDelegate callback)
{
    while (true) {
        clientSocket = accept(serverSocket, NULL, NULL);
        if (clientSocket != INVALID_SOCKET) {
            char buffer[1024];
            int recvResult = recv(clientSocket, buffer, sizeof(buffer), 0);
            if (recvResult > 0) {
                buffer[recvResult] = '\0';
                callback(buffer); // Call the provided callback with the received message
            }

        }
    }
}

void CloseServer()
{
    closesocket(clientSocket);
}

C#

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Wpf.Ui.Mvvm.Contracts;
using Wpf.Ui.Mvvm.Services;
using WpfSampleViewerApp.Commons;
using WpfSampleViewerApp.ViewModels;
using WpfSampleViewerApp.ViewModels.Pages;
using WpfSampleViewerApp.Views;
using WpfSampleViewerApp.Views.Pages;

namespace WpfSampleViewerApp
{
    public partial class App : Application
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern bool SetDllDirectory(string lpPathName);

        Process _process;

        [DllImport(@"CPPSampleLibDll.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int InitializeWinsock();

        [DllImport(@"CPPSampleLibDll.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void CleanupWinsock();

        [DllImport(@"CPPSampleLibDll.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int StartServer(int port);

        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
        public delegate void CallbackDelegate(string message);

        [DllImport(@"CPPSampleLibDll.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void WaitForConnections(CallbackDelegate callback);

        static void TCPMessageReceivedCallback(string message)
        {
            MessageBox.Show("Received message from client: " + message);
        }

        [DllImport(@"CPPSampleLibDll.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void CloseServer();

        void OnStartup(object sender, StartupEventArgs e)
        {
            try
            {
                string rootPath = Path.GetFullPath(@".");
                SetDllDirectory(rootPath);

                Thread tcpThread = new Thread(new ThreadStart(tcpRun));
                tcpThread.Start();

                _process = new Process();
                _process.StartInfo.FileName = @"WebAPISampleApp.exe";
                _process.StartInfo.Arguments = null;
                _process.Start();

                MainWindow mainWindow = new MainWindow();
                MainWindowViewModel mainWindowViewModel = new MainWindowViewModel(mainWindow);
                mainWindow.DataContext = mainWindowViewModel;
                mainWindow.ShowDialog();

            }
            catch (Exception err)
            {
                Log log = new Log();
                log.Write("err_log", err.StackTrace);
                Environment.Exit(0);
            }            
        }

        void tcpRun()
        {
            int TCP_result = InitializeWinsock();

            if (TCP_result != 0)
            {
                MessageBox.Show("Winsock initialization failed. Error code: " + TCP_result);
                return;
            }

            try
            {
                int TCP_port = 8888;
                TCP_result = StartServer(TCP_port);
                if (TCP_result != 0)
                {
                    MessageBox.Show("Failed to start server.");
                    return;
                }

                CallbackDelegate callbackDelegate = new CallbackDelegate(TCPMessageReceivedCallback);
                GCHandle.Alloc(callbackDelegate);  
                WaitForConnections(callbackDelegate);

            }
            finally
            {
                CleanupWinsock();
            }
        }

    }
}

评论

0赞 Community 10/14/2023
正如目前所写的那样,你的答案尚不清楚。请编辑以添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。您可以在帮助中心找到有关如何写出好答案的更多信息。
0赞 신성영 10/17/2023
?只是,创建 c++ dll 文件并在 c# 代码中使用 [DllImort(@{dll_file_path})]