Nelek Ответов: 2

Структура / архитектура приложения, использующего различные языки программирования


У меня есть рабочий проект на c++, использующий библиотеку, выполненную на c++ и частично на c.

Теперь у меня есть прецедент, когда мне нужно взаимодействовать с (исправлено после обнаружения недоразумения) мой проект на c++ использует эта библиотека из c#.
Я просто добавил файлы и необходимые #includes в свой первый проект, а затем написал свой код, используя непосредственно функциональность lib, вот почему я выбрал C++ в первую очередь, чтобы облегчить взаимодействие с ним.

Я немного исследовал и, основываясь на нескольких вещах, которые я нашел, думаю, что самый простой способ сделать это с наименьшим количеством изменений в исходной библиотеке-это использовать оболочку в CLI.

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

Я использую Visual Studio 2017 professional

Что я уже пробовал:

Я думаю об одном VS-решении с 3 проектами.

Проект 1: C++
Библиотечные файлы (2x .cpp, 8x .h и 1x .c)
MyAlreadyWorkingCode (1x .cpp, 1x .h)
MyNewConnectingCodeForCli (1x .cpp, 1x .h)

Проект 2: CLI
CallerForCpp (1x .cpp, 1x .h)

Проект 3: WPF
Приложение myapp.XAML-код, приложение.в CS
ExternalTriggersManagement.в CS
CallerForCLI.в CS


###############################################

- Приложение будет в основном реагировать на внешние триггеры (начало процесса и конец процесса), а графический интерфейс будет в основном использоваться для отображения информации о текущем и предыдущем процессе.
- В случае завершения процесса внешнего триггера (или ручного запроса / щелчка от пользователя при определенных обстоятельствах) будет вызвана соответствующая функция в CallerForCLI.cs
- CallerForCPP получит вызов из c# и переадресует его в MyNewConnectingCodeForCLI
- MyNewConnectingCodeForCLI затем будет использовать мои уже работающие функции c++, чтобы использовать библиотеку и генерировать желаемый результат.


Вещи, в которых я еще не уверен на 100% :
- Я должен объявить функции в MyNewConnectingCodeForCLI.h с помощью __declspec чтобы быть видимым снаружи но если я это сделаю #include MyAlreadyWorkingCode.h в самом деле MyNewConnectingCodeForCLI.cpp я должен иметь возможность использовать свою существующую функциональность как обычно, не меняя уже работающий код. Или я ошибаюсь?
- Проект CLI должен быть настроен как динамическая библиотека и с поддержкой CLR. И я полагаю, что проект CPP тоже
- Я должен скомпилировать, чтобы быть "загруженным с задержкой", используя флаг компоновщика от CLI до CPP, чтобы избежать неразрешенных зависимостей, а затем загрузить / инициализировать cpp.dll внутри CLI перед использованием его функциональности
[ДОБАВЛЕН]
- Одна из немногих вещей, которые мне нужно переместить с c# на c++, - это строка, поэтому мне нужно будет ее Маршалировать. В настоящее время я использую CString в c++, я мог бы изменить его, чтобы повысить совместимость. Как вы думаете, что было бы наиболее совместимым использованием для снижения сложности в маршале?

Как вы думаете, это хороший подход?
Комментарии и исправления приветствуются.

---------
Добавление после решения № 1 и №2:
Ссылка, которая заставила меня спросить о вышеупомянутой структуре, такова: Использование C++ в C# на примере « блог программирования[^]. Похоже, что он не использует "стандартные" способы. О чем вы думаете?

РЕДАКТИРОВАТЬ:
Я думаю, что этот метод может сработать, если все исходные коды находятся в одном решении и могут быть скомпилированы вместе. Но я никогда его не видел. Поэтому я и спросил.

Я нашел еще одну ссылку здесь, в CP, говорящую о обертывании c++ с cli: Быстрый C++/CLI - изучите C++/CLI менее чем за 10 минут[^]

--------
2-е дополнение после комментариев и редактирования Ричарда
Мое решение на языке C++ генерирует файл и заполняет его данными, полученными в ходе процесса. Он имеет свою собственную систему регистрации и другие функции.
Теперь я должен использовать часть его функциональности в другом месте, где имя файла зависит от другой системы. Я хочу, чтобы мое новое приложение на C# получало необходимую информацию из другой системы (программное обеспечение поставщика, отсутствие доступа к коду и очень ограниченные возможные изменения), составляло имя файла и отправляло его в проект C++ (один из немногих данных, подлежащих обмену), где он будет использоваться для создания соответствующего файла с необходимым содержимым.

По мере того как я продолжаю читать об этой теме и из-за некоторых комментариев ниже, я начинаю думать, что переписывание моего проекта C++ и полное преобразование его в DLL могут иметь свои преимущества в отношении переносимости и удобства использования другими созвездиями. Если нет, то мне, возможно, придется поддерживать параллельные проекты с одними и теми же источниками, если это будет успешно и использование распространится

2 Ответов

Рейтинг:
15

Richard MacCutchan

Взгляните на это Вызов собственных функций из управляемого кода | Microsoft Docs[^].


Очень простой пример:

UserDll.h

#pragma once

#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
#include <stdio.h>

// The following ifdef block is the standard way of creating macros which make exporting 
// from a DLL simpler. All files within this DLL are compiled with the USERDLL_EXPORTS
// symbol defined on the command line. This symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see 
// USERDLL_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef USERDLL_EXPORTS
#define USERDLL_API __declspec(dllexport)
#else
#define USERDLL_API __declspec(dllimport)
#endif

extern "C" USERDLL_API char* __stdcall UserTest(char* s);


UserDll.cpp

// UserDll.cpp : Defines the exported functions for the DLL application.
//

#include "UserDll.h"
#include <string>


// This is an example of an exported function.
char* __stdcall UserTest(char* s)
{
    printf("received from C#: %s\n", s);
    
    char* cp = new char[64];
    strcpy(cp, "The rain in Spain falls mainly in the plain");
    
    return cp;
    }



UserDll.в CS

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace PInvoke
{
    class UserDll
    {
        [DllImport("UserDll")]
        static extern IntPtr UserTest(string s);
        
        public static void Test()
        {
            IntPtr ip = UserTest("A message from C#");
            string ss = Marshal.PtrToStringAnsi(ip);
            Console.WriteLine("returned from C++: {0}", ss);
        }
    }
}


Nelek

Я уже некоторое время был в этой ссылке, но она только объясняет, как вызвать native из управляемого c++, имея функциональность .Net.

Но я хочу взаимодействовать с нативным из WPF, и CLI будет только "переадресатором вызовов"

Richard MacCutchan

Pinvoke можно использовать от любого .Чистый код для вызова собственных библиотек. Существует даже веб-сайт Pinvoke, который предоставляет последовательности вызовов для всех стандартных библиотечных функций Windows. Я сделал это сам в качестве упражнения по вызову функций Windows и моей собственной библиотеки C/C++.

Nelek

Тогда я понял все неправильно. Я посмотрю поближе. Спасибо

Nelek

Чтобы не повторяться я добавил информацию к этому вопросу

Richard MacCutchan

Эта связь, по-видимому, использует более комплексный подход, выстраивая две половины вместе. Но если у вас уже есть библиотека dll C++, вам просто нужно добавить код в свой модуль C# для выполнения вызовов P/Invoke. Смотрите мое обновленное решение для моего самого простого примера.

Nelek

У меня нет библиотеки Dll C++... У меня есть консольный проект VC++, который уже работает как автономное решение (MyAlreadyWorkingCode.exe), функциональность которого должна быть использована в новом месте с графическим интерфейсом c#

Вот почему я хотел бы коснуться как можно меньше из рабочего кода. Таким образом, легче проверить наличие различий при обновлении детали. Но я начинаю думать о том, чтобы сделать его автономной библиотекой DLL (так, как вы мне сказали), чтобы я был более гибким для будущих изменений в том или ином месте.

Я редактирую этот вопрос, чтобы дать еще немного информации

Richard MacCutchan

"У меня есть рабочий проект на c++, использующий библиотеку, выполненную на c++ и частично c.

Теперь у меня есть вариант использования, когда мне нужно взаимодействовать с этой библиотекой из c#."


Я предположил (возможно, ошибочно), что библиотека-это dll.

Nelek

Если вы видите первую часть "что вы пробовали", то в библиотеке находятся исходные файлы (8x .h, 2x .cpp и 1x .c). Код C используется внутренне в C++ библиотеки.
Я просто добавил файлы и необходимые #includes в свой первый проект, а затем написал свой код, используя непосредственно функциональность lib, вот почему я выбрал C++ в первую очередь, чтобы облегчить взаимодействие с ним.

Это то, что я пытался описать выше с помощью этой информации о "проекте X".

Но я признаю... есть необходимость в исправлении (добавлено также выше):
У меня есть рабочий проект на c++, использующий библиотеку, выполненную на c++ и частично на c.

Теперь у меня есть прецедент, когда мне нужно взаимодействовать с мой проект на c++ использует эта библиотека из c#."

Richard MacCutchan

Извините, но теперь я еще больше запутался. Вы не можете использовать код C# для доступа к функциям внутри исполняемого файла C/C++, если только он не предоставляет все через COM.

Если вы просто пытаетесь отправить данные в другую программу, то вы можете использовать сокеты/IPC/общую память и т. д.

Nelek

Я не знаю, где я потерял тебя, и у меня такое впечатление, что с каждой попыткой объяснить это, ты теряешься все больше.


Я не хочу получать доступ к exe-файлу. У меня нет никакой внешней библиотеки DLL или отдельных бывших. У меня есть все исходные коды рабочего проекта на C++.
С другой стороны, мне нужно запрограммировать приложение C#, где я должен был бы сделать то же самое, что и в моем проекте C++, и библиотека для этого находится в c++ (интегрирована в мой исходный код c++, где я могу использовать #include для прямого вызова методов).

Я просто ищу самый простой / менее ненужный метод повторной работы, чтобы иметь возможность запускать функциональность, содержащуюся в моем проекте C++, из проекта C#. Вот почему я подумал, что трюк с упаковкой может это сделать.

Пожалуйста, посмотрите на "что я пробовал". 1 решение с 3 проектами (каждый на своем языке), все в исходных кодах, без библиотек DLL и без exes, все скомпилировано вместе.

Richard MacCutchan

"Я просто ищу самый простой / менее ненужный метод повторной работы, чтобы иметь возможность запускать функциональность, содержащуюся в моем проекте C++, из проекта C#. Вот почему я подумал, что трюк с упаковкой может это сделать."

Тогда все, что вам нужно, это то, что я предложил вначале. Создайте библиотеку C++ в библиотеку DLL и используйте P/Invoke для доступа к ней из кода C#. Если предположить, что все необходимое для приложения C# уже находится в коде библиотеки, то преобразование его в DLL (предположительно, в настоящее время это статическая библиотека) не потребует никаких изменений в приложении C++.

Nelek

Я попробую это сделать.

Спасибо за ваше время

Richard MacCutchan

Нет проблем, я надеюсь, что вы можете решить это так или иначе.

Nelek

Я тоже :)

Nelek

Кстати... спасибо за то время, которое вы тратите на то, чтобы попытаться мне помочь :) И извините, если я не могу объяснить вам то, что вам нужно.

Это немного расстраивает, но я оцениваю ваши усилия. Пожалуйста, не думай, что я злюсь.

Рейтинг:
11

RickZeeland

Возможно, эта статья покажется вам интересной: Совместимость C++/C# [^]
Кроме того, последние версии Visual Studio обрабатывают библиотеки DLL C++ намного лучше, чем раньше, и могут автоматически генерировать библиотеки DLL "Interop".
То, что Visual Studio использует под капотом, - это Tlbimp.exe (Импортер Библиотеки Типов)[^]
Другим вариантом может быть создание REST API, см.: Гитхаб - Майкрософт/cpprestsdk[^]
И это CodeProject статьи: Отдохните : библиотека Windows C++ для быстрого взаимодействия с веб-интерфейсами[^]
Но это, вероятно, будет еще сложнее и не стоит усилий, если вам не нужна сетевая связь.


Nelek

Спасибо за ссылку.

По крайней мере, мне немного повезло, так как мне не нужно делиться никакими данными, мне достаточно дать некоторые вещи в качестве параметров в функциях. Остальное не зависит друг от друга.

Nelek

Ссылка, которая заставила меня спросить о вышеупомянутой структуре, такова: Использование C++ в C# на примере « блог программирования[^]. Похоже, что он не использует "стандартные" способы. О чем вы думаете?

RickZeeland

Интересная статья, но так как я никогда не использовал C++ CLR, я не могу много сказать об этом. Обычно мы получаем сторонние библиотеки dll C++, которые являются частью какого-то SDK, а затем должны написать обертку вокруг этого.

Nelek

Чтобы не повторяться в комментариях, я просто добавил информацию к вопросу. Таким образом, другие читатели получат все "биты"

Nelek

Я видел новые ссылки (правка 4). Я не думаю, что отдых будет полезен для меня в этом случае. Если бы у меня была потребность в сетевом общении, я думаю, что вместо этого я бы выбрал сокеты (у нас уже есть приложение "сервер", управляющее другими вещами)

RickZeeland

Ну, мы не можем быть все модными и модными ;)