【问题标题】:How to call a x64 Assembly procedure in C#如何在 C# 中调用 x64 汇编程序
【发布时间】:2021-12-05 14:48:59
【问题描述】:

我正在做一个项目,目前有以下结构:

  1. C# WPF 项目包含用户界面以及对外部方法的调用。
  2. 包含算法的 C++ DLL 项目。
  3. 包含算法的 ASM DLL 项目。

为简单起见,我们假设该算法不带参数并返回两个预定义数字的总和。

C++(二)项目中的函数签名和实现如下:

int Add(int x, int y)
{
    return x + y;
}

extern "C" __declspec(dllexport) int RunCpp()
{
    int x = 1, y = 2;

    int z = Add(x, y);

    return z;
}

下面是我在 C# 中调用函数的方式:

[DllImport("Algorithm.Cpp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int RunCpp();

这很好用——在 C# 中调用函数返回值 3,一切正常,没有抛出异常。

但是,我现在正在努力在 C# 代码中调用 ASM 过程。 我已经看到(并在一定程度上测试了自己)不可能直接在 C# 代码中调用 MASM DLL。但是,我听说可以在 C++ 中调用 ASM,然后在 C# 中调用该函数。

1. 我的第一个问题是——实际上可以直接在 C# 中调用 ASM 代码吗?当我尝试这样做时,我得到一个基本上说二进制代码不兼容的异常。
2.我曾尝试使用 C++ 间接调用 ASM DLL,虽然我没有遇到异常,返回值是“随机的”,就像在内存中留下的余数一样,例如:-7514271。这是我做错了什么,还是有其他方法可以做到这一点?

这是在 C++ 中调用 ASM 的代码:

typedef int(__stdcall* f_MyProc1)(DWORD, DWORD);

extern "C" __declspec(dllexport) int RunAsm()
{
    HINSTANCE hGetProcIDDLL = LoadLibrary(L"Algorithm.Asm.dll");

    if (hGetProcIDDLL == NULL)
    {
        return 0;
    }

    f_MyProc1 MyProc1 = (f_MyProc1)GetProcAddress(hGetProcIDDLL, "MyProc1");

    if (!MyProc1)
    {
        return 0;
    }

    int x = 1, y = 2;

    int z = MyProc1(x, y);

    FreeLibrary(hGetProcIDDLL);

    return z;
}

这里是用C#调用C++的代码:

[DllImport("Algorithm.Cpp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int RunAsm();

如果需要,这是MyProc1 的 ASM 代码:
Main.asm:

MyProc1 proc x: DWORD, y: DWORD

mov EAX, x
mov ECX, y
add EAX, ECX
ret

MyProc1 endp

Main.def:

LIBRARY Main
EXPORTS MyProc1

【问题讨论】:

  • 听起来好像你编译了一个 32 位 dll 并尝试使用 64 位约定调用它。
  • 事实并非如此——这三个项目都是在 64 位配置下编译的。
  • 验证反汇编,然后仔细检查 xy 解析到什么。
  • 我不是本机代码方面的专家,但据我所知,c++ 和程序集 dll 之间应该没有什么区别。你确定调用约定是正确的吗? Exporting from a dll 建议使用 stdcall。
  • @JonasH 将调用约定更改为 stdcall 并没有什么不同——算法的 C++ 版本仍然可以正常工作,而 ASM 版本仍然不能。

标签: c# c++ assembly masm masm64


【解决方案1】:

实际上可以直接在 C# 中调用 ASM 代码吗?

这个例子有两个项目,C# 和基于程序集的 DLL。看起来您已经知道如何使基于 C++ 的 DLL 工作。项目名称与目录名称相同,xcs 用于 C#,xcadll 用于 dll。我从空目录开始创建空项目,然后将源文件移动到目录中,然后将现有项目添加到每个项目中。

xcadll 属性:

Configuration Type: Dynamic Library (.dll)
Linker | Input: xcadll.def

xcadll\xcadll.def:

LIBRARY xcadll
EXPORTS DllMain
EXPORTS Example

xcadll\xa.asm 属性(对于发布版本,不需要 /Zi):

General | Excluded From Build: No
General | Item Type: Custom Build Tool
Custom Build Tool | General | Command Line: ml64 /c /Zi /Fo$(OutDir)\xa.obj xa.asm
Custom Build Tool | General | Outputs: $(OutDir)\xa.obj

xcadll\xa.asm:

        includelib      msvcrtd
        includelib      oldnames        ;optional
        .data
        .data?
        .code
        public  DllMain
        public  Example

DllMain proc                            ;return true
        mov     rax, 1
        ret     0
DllMain endp

Example proc                            ;[rcx] = 0123456789abcdefh
        mov     rax, 0123456789abcdefh
        mov     [rcx],rax
        ret     0
Example endp
        end

xcs\Program.cs:

using System;
using System.Runtime.InteropServices;
namespace xcadll
{
    class Program
    {
    [DllImport("c:\\xcadll\\x64\\release\\xcadll.dll")] 
    static extern void Example(ulong[] data);

        static void Main(string[] args)
        {
            ulong[] data = new ulong[4] {0,0,0,0};
            Console.WriteLine("{0:X16}", data[0]);
            Example(data);
            Console.WriteLine("{0:X16}", data[0]);
            return;
        }
    }
}

为了调试,使用

    [DllImport("c:\\xcadll\\x64\\debug\\xcadll.dll")] 

xcs 属性 |调试 |启用本机模式调试(选中复选框)

【讨论】:

  • 这对我来说非常有效 - 非常感谢您的时间和精力!
猜你喜欢
  • 2014-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 2014-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多