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

3 c# c++ dll pinvoke

这是我在DLL中的函数的签名:

int __stdcall myFun( void * const context, const char * const pszFileName, const unsigned int buffSize, void * const pWaveFormatex );
Run Code Online (Sandbox Code Playgroud)

所有参数均为[in].用户应该通过WAVEFORMATEX最后一个参数传递指向结构的指针.返回后,它将被填补.所有这些在C++中都很有效.

现在,我正在尝试使用C#中的相同DLL,但它根本不起作用.问题出在最后一个参数中.由于我根本不了解C#,我想问一下这是否可行.如果是的话,我会很感激一个例子.

我最后一次尝试是这样的:

[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;
}
Run Code Online (Sandbox Code Playgroud)

注意:我还使用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));
Run Code Online (Sandbox Code Playgroud)

行为未定义.有时它会挂起,有时它会终止......有些事情是非常错误的.但是,问题不在DLL中,问题出在C#端.C#是否能够使用指针?

感谢您提供的任何反馈.

编辑(工作C++代码):

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

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);
Run Code Online (Sandbox Code Playgroud)

Dav*_*nan 5

那么,根据你提供的信息,我会说你需要像这样声明C#函数:

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

并调用这样的函数:

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

实际上不需要手动编组此结构.这是一个非常简单的blittable结构,让框架处理编组更加清晰.

当您声明最终的struct参数不需要初始化并且其成员由函数填写时,我假设您是准确的.

包装看起来合理.我认为你不需要以任何特殊的方式构建你的DLL.我希望您WAVEFORMATEX从Windows头文件中获取并且他们已经为该结构指定了包装.

如果您仍然卡住,那么您应该显示成功的C++调用代码.


从评论来看,你的代码中仍然存在一个错误.在这种情况下,特别是当您怀疑互操作时,进行简单的再现以确定互操作是否是问题是值得的.这是我的:

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;
}
Run Code Online (Sandbox Code Playgroud)

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

产量

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