Страницы

Поиск по вопросам

Показаны сообщения с ярлыком ipc. Показать все сообщения
Показаны сообщения с ярлыком ipc. Показать все сообщения

суббота, 11 января 2020 г.

Передать данные из c# в delphi

#c_sharp #delphi #ipc

Запущено два приложения. Одно на c#, второе на delphi
Необходимо передать число из c# в delphi. 

Как сделать даже не знаю, гуглил, говорят через dll или оперативную память, запись
и считывание файла.
    


Ответы

Ответ 1



Примерно так например (средствами SendMessage WM_COPYDATA): Отправка: Type TSendForm = Class(TForm) btnSend: TButton; edtNumber: TEdit; procedure btnSendClick(Sender: TObject); Private { Private-Deklarationen } Public { Public-Deklarationen } End; Const CMD_SETNUMBER = 1; //Задаем ID команды Var SendForm: TSendForm; Implementation {$R *.dfm} Procedure TSendForm.btnSendClick(Sender: TObject); Var CDS: TCopyDataStruct; Begin //Устанавливаем тип команды CDS.dwData := CMD_SETNUMBER; //Устанавливаем длину передаваемых данных CDS.cbData := Length(edtNumber.Text) + 1; //Выделяем память буфера для передачи данных GetMem(CDS.lpData, CDS.cbData); Try //Копируем данные в буфер StrPCopy(CDS.lpData, AnsiString(edtNumber.Text)); //Отсылаем сообщение в окно с заголовком StringReceiver SendMessage(FindWindow(Nil, 'Receiver'), WM_COPYDATA, Handle, Integer(@CDS)); Finally //Высвобождаем буфер FreeMem(CDS.lpData, CDS.cbData); End; End; End. Обработка: Type TRecvForm = Class(TForm) mLog: TMemo; Private FNumber: Integer; //Обработчик сообщения WM_COPYDATA Procedure WMCopyData(Var MessageData: TWMCopyData); Message WM_COPYDATA; Public Property Number: Integer Read FNumber Write FNumber; End; Const CMD_SETNUMBER = 1; Var RecvForm: TRecvForm; Implementation {$R *.dfm} Procedure TRecvForm.WMCopyData(Var MessageData: TWMCopyData); Begin If MessageData.CopyDataStruct.dwData = CMD_SETNUMBER Then Begin Try Number := StrToInt(String(PAnsiChar((MessageData.CopyDataStruct.lpData)))); Except On E: Exception Do Begin mLog.Lines.Append(Format('Ошибка: %s', [E.Message])); MessageData.Result := 0; Exit; End; End; mLog.Lines.Append(Format('Получено число: %d', [Number])); MessageData.Result := 1; End Else MessageData.Result := 0; End; End. Соответственно одно из них реализуете на C#

Ответ 2



Сделал так: using System.Runtime.InteropServices; using System.Security; class ProgramSend { public const int WM_COPYDATA = 0x004A; [return: MarshalAs(UnmanagedType.Bool)] [DllImport("user32.dll")] public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern IntPtr SendMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam); [DllImport("user32.dll", EntryPoint = "FindWindow")] public static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct MyStruct { public double coins; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string status; } public static IntPtr IntPtrAlloc(T param) { IntPtr retval = Marshal.AllocHGlobal(Marshal.SizeOf(param)); Marshal.StructureToPtr(param, retval, false); return (retval); } public static void IntPtrFree(IntPtr preAllocated) { if (IntPtr.Zero == preAllocated) throw (new Exception("Go Home")); Marshal.FreeHGlobal(preAllocated); preAllocated = IntPtr.Zero; } [StructLayout(LayoutKind.Sequential)] public struct COPYDATASTRUCT { public IntPtr dwData; // Specifies data to be passed public int cbData; // Specifies the data size in bytes public IntPtr lpData; // Pointer to data to be passed } [SuppressUnmanagedCodeSecurity] public class NativeMethod { [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref COPYDATASTRUCT lParam); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); } } public static class MyCalss { public static System.String MyFunc(string sMsg) { IntPtr hTargetWnd = ProgramSend.NativeMethod.FindWindow(null, "CCDemo"); if (hTargetWnd == IntPtr.Zero) { MessageBox.Show("Unable to find the \"ReceiverMainForm\" window", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } try { ProgramSend.COPYDATASTRUCT cds = new ProgramSend.COPYDATASTRUCT(); cds.dwData = (IntPtr)1; //some data cds.cbData = sMsg.Length + 1; cds.lpData = Marshal.StringToCoTaskMemAnsi(sMsg); ProgramSend.NativeMethod.SendMessage(hTargetWnd, ProgramSend.WM_COPYDATA, IntPtr.Zero, ref cds); Marshal.FreeCoTaskMem(cds.lpData); int result = Marshal.GetLastWin32Error(); if (result != 0) { MessageBox.Show(String.Format( "SendMessage(WM_COPYDATA) failed w/err 0x{0:X}", result)); } } finally { //Marshal.FreeHGlobal(pMyStruct); } return "done"; } } На делфи: type TMyRecord = packed record coins: Double; status: string[255]; end; PMyRecord = ^TMyRecord; procedure TFMain.WMCOPYDATA(var msg: TWMCopyData); var coins: Double; status: String[255]; sText: String; begin status:=Pansichar(msg.CopyDataStruct.lpData); coins := msg.CopyDataStruct.dwData; ShowMessage('Msg: ' + status + ' Number: ' + FloatToStr(coins)); end;

суббота, 13 июля 2019 г.

Использование не PoD типов данных в shared memory c++

Как изменять не PoD поля в случае использования shared memory? Я так понимаю, что для PoD типов подобный подход "в лоб" работает и на выделенной в mmap памяти поле класса изменяется. Однако поле типа string в данном случае останется неизменным. Каким образом его можно изменить? P.s. Вопрос чисто учебный, так что если вы подведете под ответ некоторую теоретическую базу - будет здорово.
UPD: не PoD поле остается неизменным в parent-процессе. В child-процессе оно-таки меняется, но это порождает еще больше вопросов :)
#include #include #include #include #include #include #include #include
using namespace std;
class human { public: string name; int age; human() { name = '\0'; age = 0; } ~human() {} void set() { cout << "Enter name" << endl; cin >> name; cout << "Enter age" << endl; cin >> age; } void print() { cout << name << " " << age << endl; } };
main(int argc, char * argv[]) { pid_t cpid; char buf; human* shared; human non; shared = (human*)mmap(NULL, sizeof(*shared), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); *shared = non; cpid = fork(); // duplicate the current process if (cpid == 0) // if I am the child then { shared->set(); exit(EXIT_SUCCESS); } else // if I am the parent then { wait(NULL); // wait for the child process to exit before I do the same cout << "Human:" << endl; shared->print(); exit(EXIT_SUCCESS); } return 0; }


Ответ

Нельзя так делать. Сериализуйте данные в разделяемую память, а потом десериализуйте на выходе.
std::string может содержать указатель на внешнюю память, поэтому то, что Вы выделили память под сам объект строки в общей памяти ничего не значит — данные лежат совсем в другом месте.
Конечно, может так совпасть, что оставив код неизменным и изменив строку в одном процессе, это изменение будет видно и в другом, но это частный случай. Вызван он тем, что почти(?) все популярные реализации std::string используют т.н. SSO(small string optimization), при которой вся строка хранится в самом объекте, при её малом размере. Но если записать в строку больше, чем внутренний буфер позволяет, вся строка обязательно будет перемещена в кучу процесса.
После этого, достучаться до этой строки уже не получится так просто. И, попытавшись прочитать или записать что-либо в строку во втором процессе, Вы получите UB, т.к. адрес строки, содержащийся в std::string указывает на область памяти выделенной в первом процессе. В общем случае, во втором процессе этот адрес никем не будет занят, либо будет занят, но совсем другим объектом — в любой из этих ситуаций, обращение к этому адресу как к std::string будет UB(если не совпадёт, конечно, что там окажется другая строка, по точно такому же адресу и такого же размера, но подобные «чудеса» я в рассмотрение не беру.

Если же абстрагироваться от примера, то есть простое правило: если хочешь обменяться данными с любой внешней сущностью(будь то процесс или ещё что-то), сначала сохрани это в каком-то общем для обеих сущностей формате, чтобы принимающая сторона могла безошибочно определить, что ей пришло. Использовать простые объекты C++ языка в такой взаимодействии нельзя, т.к. слишком много мест, где можно «поскользнуться».

четверг, 20 июня 2019 г.

Как подключиться к именованному каналу без прав администратора?

Привет. Есть два приложения на C# - служба (windows service, для службы был указан Account LocalSystem) и клиентское приложение(обычное windows приложение, в данном случае консольное), которые обмениваются информацией по именованным каналам (Named Pipes). Проблема в том, что клиентское приложение может подключиться к службе по именованному каналу только с правами администратора.
Нашёл в сети решение, в котором предлагается назначить серверному именованному каналу PipeSecurity, содержащий один или более PipeAccessRule. После применения этого подхода серверное приложение падает с исключением System.UnauthorizedAccessException. Подскажите, как по именованному каналу осуществить подключение к службе клиентского приложения, которое запущено без прав админа? Делаю так:
Windows Service
protected override void OnStart(string[] args) { new Thread(() => { while (true) { using (var pipe = new NamedPipeServerStream("test", PipeDirection.InOut, 1, PipeTransmissionMode.Byte)) { var ps = new PipeSecurity(); ps.AddAccessRule(new PipeAccessRule(WindowsIdentity.GetCurrent().Name, PipeAccessRights.FullControl, AccessControlType.Allow)); pipe.SetAccessControl(ps);
pipe.WaitForConnection(); int x = pipe.ReadByte(); pipe.WriteByte((byte)(x + 1)); } } }) { IsBackground = true }.Start(); }
Клиент:
using (var pipe = new NamedPipeClientStream(".", "test", PipeDirection.InOut)) { pipe.Connect(); pipe.WriteByte(100); int x = pipe.ReadByte(); Console.WriteLine(x); }
Пробовал также в коде службы WindowsIdentity.GetCurrent().Name менять на "Everyone", но в этом случае исключение System.Security.Principal.IdentityNotMappedException.


Ответ

Попробуйте такую реализацию:
new Thread(() => { var ps = new PipeSecurity();
// себе разрешаем все ps.AddAccessRule( new PipeAccessRule( WindowsIdentity.GetCurrent().Owner, PipeAccessRights.FullControl, AccessControlType.Allow));
// остальным только чтение/запись в пайп ps.AddAccessRule( new PipeAccessRule( new SecurityIdentifier(WellKnownSidType.WorldSid, null), PipeAccessRights.ReadWrite, AccessControlType.Allow));
while (true) { // параметры безопасности передаем в конструктор, а не в SetAccessControl using (var pipe = new NamedPipeServerStream("test", PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.None, 0, 0, ps)) { pipe.WaitForConnection(); int x = pipe.ReadByte(); pipe.WriteByte((byte)(x + 1)); } } }) { IsBackground = true }.Start();