简体   繁体   English

如何将指针从C#传递到DLL中的本机函数?

[英]How to pass a pointer from C# to native function in DLL?

This is the signature of my function in DLL: 这是我在DLL中的函数的签名:

int __stdcall myFun( void * const context, const char * const pszFileName, const unsigned int buffSize, void * const pWaveFormatex );

All parameters are [in]. 所有参数均为[in]。 The user should pass a pointer to a WAVEFORMATEX struct through the last parameter. 用户应该通过最后一个参数将指针传递给WAVEFORMATEX结构。 Upon return, it will be filled. 返回后,它将被填补。 All that works very well in C++. 所有这些在C ++中都很有效。

Now, I'm trying for days to use the same DLL from C# and it simply doesn't work. 现在,我正在尝试使用C#中的相同DLL,但它根本不起作用。 The problem is in the last parameter. 问题出在最后一个参数中。 Since I do not know C# at all, I would like to ask somebody if this is doable at all. 由于我根本不了解C#,我想问一下这是否可行。 If it is, I would appreciate an example. 如果是的话,我会很感激一个例子。

One of my last attempts was this: 我最后一次尝试是这样的:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct WAVEFORMATEX
{
    public ushort wFormatTag;
    public ushort nChannels;
    public uint nSamplesPerSec;
    public uint nAvgBytesPerSec;
    public ushort nBlockAlign;
    public ushort wBitsPerSample;
    public ushort cbSize;
}

Note: I also built my DLL written in C++ with the Struct Member Alignment = 1. Maybe I'm stupid, but I thought that Pack = 1 above is related with that in C++, but I have no idea if it is... 注意:我还使用Struct Member Alignment = 1构建了用C ++编写的DLL。也许我很愚蠢,但我认为上面的Pack = 1与C ++中的相关,但我不知道它是否......

[DllImport("myLib.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
        public static extern int myFun( IntPtr context,
                                        [MarshalAs( UnmanagedType.LPStr )]
                                        string pszFileName,
                                        int bufferSize,
                                        ref IntPtr pWfx );

IntPtr unmanaged_pWfx = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WAVEFORMATEX)));

result = DLLWrapper.myFun(context,
                          "C:\\video.wmv",
                          176400,
                          ref unmanaged_pWfx );

WAVEFORMATEX wfxFormat = (WAVEFORMATEX)Marshal.PtrToStructure( unmanaged_pWfx, typeof(WAVEFORMATEX));

The behavior is undefined. 行为未定义。 Sometimes it hangs, sometimes it terminates... Something is very wrong. 有时它会挂起,有时它会终止......有些事情是非常错误的。 However, the problem is not in the DLL, the problem is on the C# side. 但是,问题不在DLL中,问题出在C#端。 Is C# capable of working with pointers at all? C#是否能够使用指针?

Thanks for any feedback you provide. 感谢您提供的任何反馈。

EDIT (working C++ code): 编辑(工作C ++代码):

void * context;
WAVEFORMATEX wfx;
int success = getContext( &context );
success = myFun( context, "C:\\video.wmv", 176400, &wfx );

The equivalent in C# is: C#中的等价物是:

IntPtr context;
WAVEFORMATEX wfx;

int success = getContext( out context );
success = myFun( context, "C:\\video.wmv", 176400, out wfx );


extern "C" __stdcall int getContext( void ** pContext );

[DllImport("myLib.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int getContext(out IntPtr context);

Well, based on the information you have provided I would say that you need to declare the C# function like this: 那么,根据你提供的信息,我会说你需要像这样声明C#函数:

[DllImport("myLib.dll")]
public static extern int myFun(
    IntPtr context,
    string fileName,
    uint bufferSize,
    out WAVEFORMATEX wfx
);

And call the function like this: 并调用这样的函数:

WAVEFORMATEX wfx;
int result = DLLWrapper.myFun(context, @"C:\video.wmv", 176400, out wfx);

There's really no need for manual marshalling of this struct. 实际上不需要手动编组此结构。 It's a very simple blittable struct and it is much cleaner to let the framework handle the marshalling. 这是一个非常简单的blittable结构,让框架处理编组更加清晰。

I am assuming that you are accurate when you state that the final struct parameter does not need to be initialised and its members are filled out by the function. 当您声明最终的struct参数不需要初始化并且其成员由函数填写时,我假设您是准确的。

Packing looks reasonable. 包装看起来合理。 I don't think you need to build your DLL in any special way. 我认为你不需要以任何特殊的方式构建你的DLL。 I hope that you are picking up WAVEFORMATEX from the Windows header files and they already specify packing for that struct. 我希望您从Windows头文件中获取WAVEFORMATEX ,并且他们已经为该结构指定了打包。

If you are still stuck then you should show the successful C++ calling code. 如果您仍然卡住,那么您应该显示成功的C ++调用代码。


Judging from the comments, you still have a bug somewhere in your code. 从评论来看,你的代码中仍然存在一个错误。 In such a situation, especially when you doubt the interop, it pays to make a simple reproduction to determine whether the interop is the problem, or not. 在这种情况下,特别是当您怀疑互操作时,进行简单的再现以确定互操作是否是问题是值得的。 Here is mine: 这是我的:

C++ DLL C ++ DLL

#include <Windows.h>
#include <mmsystem.h>
#include <iostream>

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

__declspec(dllexport) int __stdcall myFun(void * const context, 
    const char * const pszFileName, const unsigned int buffSize, 
    void * const pWaveFormatex)
{
    std::cout << context << std::endl
        << pszFileName << std::endl 
        << buffSize << std::endl;

    WAVEFORMATEX wfx;
    wfx.cbSize = 1;
    wfx.nAvgBytesPerSec = 2;
    wfx.nBlockAlign = 3;
    wfx.nChannels = 4;
    wfx.nSamplesPerSec = 5;
    wfx.wBitsPerSample = 6;
    wfx.wFormatTag = 7;
    CopyMemory(pWaveFormatex, &wfx, sizeof(wfx));

    return 666;
}

C# console app C#控制台应用

using System;
using System.Runtime.InteropServices;

namespace ConsoleApplication13
{
    class Program
    {
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct WAVEFORMATEX
        {
            public ushort wFormatTag;
            public ushort nChannels;
            public uint nSamplesPerSec;
            public uint nAvgBytesPerSec;
            public ushort nBlockAlign;
            public ushort wBitsPerSample;
            public ushort cbSize;
        }

        [DllImport(@"Win32Project1.dll", EntryPoint = "?myFun@@YGHQAXQBDI0@Z")]
        public static extern int myFun(
            IntPtr context,
            string fileName,
            uint bufferSize,
            out WAVEFORMATEX wfx
        );

        static void Main(string[] args)
        {
            WAVEFORMATEX wfx;
            int result = myFun((IntPtr)42, @"C:\video.wmv", 176400, out wfx);
            Console.WriteLine(result);
            Console.WriteLine(wfx.cbSize);
            Console.WriteLine(wfx.nAvgBytesPerSec);
            Console.WriteLine(wfx.nBlockAlign);
            Console.WriteLine(wfx.nChannels);
            Console.WriteLine(wfx.nSamplesPerSec);
            Console.WriteLine(wfx.wBitsPerSample);
            Console.WriteLine(wfx.wFormatTag);

            Console.ReadLine();
        }
    }
}

Output 产量

0000002A
C:\video.wmv
176400
666
1
2
3
4
5
6
7

The problem is passing the IntPtr. 问题是通过IntPtr。 You're passing stack allocated variable(the one that holds actual pointer) to your code, but you want to pass the pointer. 您将堆栈分配的变量(保存实际指针的变量)传递给您的代码,但是您想要传递指针。 Just remove the "ref" keyword from your code. 只需从代码中删除“ref”关键字即可。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM