如何传递一个指针从C#到DLL中的本机函数? [英] How to pass a pointer from C# to native function in DLL?

查看:225
本文介绍了如何传递一个指针从C#到DLL中的本机函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

This is the signature of my function in DLL:

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

所有参数均为[in]。用户应该通过最后一个参数传递一个 WAVEFORMATEX 结构的指针。返回时,它将被填充。所有的工作非常好在C + +。

All parameters are [in]. The user should pass a pointer to a WAVEFORMATEX struct through the last parameter. Upon return, it will be filled. All that works very well in C++.

现在,我试图天使用同一个DLL从C#,它根本不工作。问题出在最后一个参数。因为我不知道C#,我想问一个人,如果这是可行的。

Now, I'm trying for days to use the same DLL from C# and it simply doesn't work. 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. If it is, I would appreciate an example.

我最后的尝试之一是:

[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;
}



注意:我也用C + +编写了我的DLL,也许我很蠢,但我认为上面的 Pack = 1 与C ++中的相关,但我不知道是否是...

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...

[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));

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

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. Is C# capable of working with pointers at all?

感谢您提供的任何反馈。

Thanks for any feedback you provide.

C ++代码):

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

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);


推荐答案

好吧,根据您提供的信息,说你需要这样声明C#函数:

Well, based on the information you have provided I would say that you need to declare the C# function like this:

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

并像这样调用函数:

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

真的没有必要手动编组这个结构体。这是一个非常简单的blittable结构,它是更干净,让框架处理编组。

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.

我假设你是准确的,当你说最终的struct参数不需要初始化,其成员可以通过函数填写。

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.

包装看起来很合理。我不认为你需要建立你的DLL以任何特殊的方式。我希望你从Windows头文件中选择 WAVEFORMATEX ,并且已经为该结构指定了打包。

Packing looks reasonable. I don't think you need to build your DLL in any special way. I hope that you are picking up WAVEFORMATEX from the Windows header files and they already specify packing for that struct.

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

If you are still stuck then you should show the successful C++ calling code.

从评论判断, bug在代码中的某处。在这种情况下,特别是当你怀疑互操作,它付出做一个简单的复制,以确定interop是否是问题。这是我的:

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

#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应用程式
$ b

C# console app

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();
        }
    }
}

strong>

Output


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

这篇关于如何传递一个指针从C#到DLL中的本机函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆