Nano Hash - криптовалюты, майнинг, программирование

Отправка структуры из C++ в WPF с использованием WM_COPYDATA

У меня есть родное приложение C++, которому на данный момент просто нужно отправить строку командной строки и текущие координаты курсора мыши в приложение WPF. Сообщение отправляется и принимается нормально, но я не могу преобразовать экземпляр IntPtr в C# в структуру.

Когда я пытаюсь это сделать, приложение либо падает без исключения, либо строка кода, которая его преобразует, пропускается и принимается следующее сообщение в цикле. Вероятно, это означает, что возникает собственное исключение, но я не знаю, почему.

Вот программа на С++. В настоящее время я игнорирую строку командной строки и использую фальшивые координаты курсора, чтобы убедиться, что все работает.

#include "stdafx.h"
#include "StackProxy.h"
#include "string"

typedef std::basic_string<WCHAR, std::char_traits<WCHAR>> wstring;

struct StackRecord
{
    //wchar_t CommandLine[128];
    //LPTSTR CommandLine;
    //wstring CommandLine;
    __int32 CursorX;
    __int32 CursorY;
};

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    COPYDATASTRUCT data;
    ZeroMemory(&data, sizeof(COPYDATASTRUCT));

    StackRecord* record = new StackRecord();

    wstring cmdLine(lpCmdLine);
    //record.CommandLine = cmdLine;
    record->CursorX = 5;
    record->CursorY = 16;
    data.dwData = 12;
    data.cbData = sizeof(StackRecord);
    data.lpData = record;

    HWND target = FindWindow(NULL, _T("Window1"));

    if(target != NULL)
    {
        SendMessage(target, WM_COPYDATA, (WPARAM)(HWND) target, (LPARAM)(LPVOID) &data);
    }
    return 0;
}

А вот часть приложения WPF, которая получает сообщение. Вторая строка внутри оператора IF пропускается, если все это не просто падает.

    public IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == Interop.WM_COPYDATA)
        {
            var data = (Interop.CopyDataStruct)Marshal.PtrToStructure(lParam, typeof(Interop.CopyDataStruct));
            var record = (Interop.StackRecord)Marshal.PtrToStructure(data.lpData, typeof(Interop.StackRecord));
            MessageBox.Show(String.Format("X: {0}, Y: {1}", record.CursorX, record.CursorY));
        }
        return IntPtr.Zero;
    }

А вот определения C# для структур. Я бесконечно игрался с маршаллинговыми атрибутами и ничего не добился.

internal static class Interop
{
    public static readonly int WM_COPYDATA = 0x4A;

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct CopyDataStruct
    {
        public IntPtr dwData;
        public int cbData;
        public IntPtr lpData;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    public struct StackRecord
    {
        //[MarshalAs(UnmanagedType.ByValTStr)]
        //public String CommandLine;
        public Int32 CursorX;
        public Int32 CursorY;
    }
}

Любые идеи?


  • Я также пытался переопределить WndProc в окне Winforms, поведение такое же. 14.12.2009

Ответы:


1

Я не уверен, что вы ошибаетесь, обязательно без дополнительной информации о вашей настройке. Я воспроизвел код как мог (используя WndProc в приложении WPF, отправляя из моего собственного приложения win32), и он отлично работает для меня. Есть несколько ошибок, которые определенно возникнут, если вы используете 64-битные приложения, а именно Pack = 1 приведет к смещению COPYDATASTRUCT, и чтение из указателя, вероятно, закончится болью.

Он падает, передавая только целые числа? Глядя на ваш закомментированный код, передающий LPWSTR или wstring, вы можете столкнуться с серьезными проблемами, хотя это не должно быть очевидным, пока вы не рассортируете отправленные данные.

Что бы это ни стоило, это фрагменты моего кода, которые, кажется, работают для меня, включая передачу командной строки.

/* C++ code */
struct StackRecord
{
    wchar_t cmdline[128];
    int CursorX;
    int CursorY;
};

void SendCopyData(HWND hFind)
{
    COPYDATASTRUCT cp;
    StackRecord record;

    record.CursorX = 1;
    record.CursorY = -1;

    _tcscpy(record.cmdline, L"Hello World!");
    cp.cbData = sizeof(record);
    cp.lpData = &record;
    cp.dwData = 12;
    SendMessage(hFind, WM_COPYDATA, NULL, (LPARAM)&cp);
}

/* C# code */
public static readonly int WM_COPYDATA = 0x4A;

[StructLayout(LayoutKind.Sequential)]
public struct CopyDataStruct
{
    public IntPtr dwData;
    public int cbData;
    public IntPtr lpData;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct StackRecord
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)]
    public string CommandLine;
    public Int32 CursorX;
    public Int32 CursorY;
}

protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == WM_COPYDATA)
    {
        StackRecord record = new StackRecord();
        try
        {
            CopyDataStruct cp = (CopyDataStruct)Marshal.PtrToStructure(lParam, typeof(CopyDataStruct));
            if (cp.cbData == Marshal.SizeOf(record))
            {
                record = (StackRecord)Marshal.PtrToStructure(cp.lpData, typeof(StackRecord));
            }
        }
        catch (Exception e)
        {
            System.Diagnostics.Debug.WriteLine(e.ToString());
        }
        handled = true;
    }
    else
    {
        handled = false;
    }
    return IntPtr.Zero;
}
14.12.2009
  • а именно Pack = 1 приведет к смещению COPYDATASTRUCT, и чтение с указателя, вероятно, закончится болью. Ах, теперь так очевидно. Я привык читать структуры из файлов, где Pack = 1 имеет смысл. Приложение C++ является 32-разрядным, но у меня 64-разрядная система, а код .NET объединен с 64-разрядным кодом. Удаление Pack = 1 исправило это. Спасибо. Глядя на ваш прокомментированный код, передающий LPWSTR или wstring, я думал, что это может вызвать серьезные проблемы. Я также безуспешно пытался передать фиксированную строку wchar_t, как и вы, но это было связано с вышеуказанной проблемой. 15.12.2009

  • 2

    Я создал пару приложений (с VC++ и VC# соответственно), обращаясь к "уваренному" варианту проблемы (т.е. невозможности получить эту структуру), они, кажется, работают без нареканий, так что это действительно может быть что-то с вашей настройкой, как говорит tyranid.

    В любом случае, вот код (должно быть достаточно просто вставить его во вновь созданное ПРИЛОЖЕНИЕ WIN32 (для VC++) и ПРИЛОЖЕНИЕ WINDOWS FORMS для C# для запуска и тестирования):

    StackProxy.cpp

    #include "stdafx.h"
    #include "StackProxy.h"
    #include <string>
    
    
    struct StackRecord {
        __int32 CursorX;
        __int32 CursorY;
    };
    
    
    int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
        StackRecord record;
    
        record.CursorX = 5;
        record.CursorY = 16;
    
        COPYDATASTRUCT data;
    
        data.dwData = 12;
        data.cbData = sizeof(StackRecord);
        data.lpData = &record;
    
        HWND target = FindWindow(NULL, _T("Window1"));
    
        if(target != NULL)
            SendMessage(target, WM_COPYDATA, (WPARAM)(HWND) target, (LPARAM)(LPVOID) &data);
    
        return 0;
    }
    

    Form1.cs

    using System;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            public struct COPYDATASTRUCT
            {
                public System.Int32 dwData;
                public System.Int32 cbData;
                public System.IntPtr lpData;
            }
    
            int WM_COPYDATA = 0x4A;
    
            [StructLayout(LayoutKind.Sequential)]
            public struct StackRecord
            {
                public Int32 CursorX;
                public Int32 CursorY;
            }
    
            public Form1()
            {
                InitializeComponent();
                Text = "Window1";
            }
    
            protected override void WndProc(ref Message msg)
            {
                if (msg.Msg == WM_COPYDATA) {
                    COPYDATASTRUCT cp = (COPYDATASTRUCT)Marshal.PtrToStructure(msg.LParam, typeof(COPYDATASTRUCT));
                    StackRecord record = (StackRecord)Marshal.PtrToStructure(cp.lpData, typeof(StackRecord));
                    MessageBox.Show(String.Format("X: {0}, Y: {1}, Data: {2}", record.CursorX, record.CursorY, cp.dwData));
                } 
                base.WndProc(ref msg);
            }
        }
    }
    

    Надеюсь это поможет.

    P.S. У меня мало знаний о С# и (особенно) взаимодействии (в первую очередь меня интересует программирование на С++), но, не увидев ни одного ответа [несколько часов назад], я просто подумал, что было бы неплохо попробовать решить эту проблему. Не говоря уже о наградах :)

    *Блин, я опоздал :))

    15.12.2009
    Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..