c#如何编写非托管代码的回调函数(非stdcall调用转换)

eDRIVE 2004-06-15 03:40:15
我看了文档,delegate无法做到,似乎意味着delegate仅仅支持stdcall调用转换。

可是现在需要写一个cdecl调用转换的过程来响应外部代码的回调,不知C#可否做到? 请给与建议与事例,全分立刻送上。
...全文
774 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
eDRIVE 2004-06-16
  • 打赏
  • 举报
回复
谢谢Lostinet,回复很详尽,虽然我似乎想获得非中介的实现,但目前为止唯有如此能够现实地完成我的需要,全分送上。以后望请多赐教。 :)
Lostinet 2004-06-15
  • 打赏
  • 举报
回复
不好意思.没有看清你的问题.

DotNet 为 Delegate 在内存中创建一个临时的函数,用于与Delegate关联起来.
这个设计使得不同的Delegate事例和它的函数一一对应.

你可以用C写个dll,用于cdecl与stdcall的互换. 过程如下:

目标dll的函数(A) -> 用于转换的cdecl函数(B) -> 用于转换的stdcall函数(C) -> DotNet Delegate(D)

当B被调用时,它负责把参数转化,然后调用C ( C可以调用D,或者C根本就是某个Delegate动态生成的函数地址)

不过这里有一个很大的问题,B 的地址是固定的.

也就是B被调用时,你必须以某种方法来知道,你需要调用哪个C.

如果全局就只有一个C,那么这个问题也不存在了。

详细的情况如下,(C语法我基本忘了.用c:代表是c的语法)

你现在已经有了目标的 dll - A.dll , 里面有一个函数
c:
void DoAction(DoActionCallBack callback [,parameterlist]);//cdcel
void DoActionCallBack([parameterlist]);//cdcel

你现在希望能调用DoAction,那么要做一个中间的dll - B.dll
c:
void WrapperCallBack(int z);//stdcall
[win dll 的线程变量定义] WrapperCallBack _thread_callback;
void DoActionCallBackWrapper([parameterlist])//cdcel
{
_ASSERT(_thread_callback!=0);
获取参数
_thread_callback(参数Z);
}
void DoActionWrapper(WrapperCallBack callback,int x,int y)//stdcall
{
WrapperCallBack _back=_thread_callback;
_thread_callback=callback;

DoAction(DoActionCallBackWrapper,x,y);

callback=_back;
}

当然,最后你DotNet可以不知道A.dll了.你只认B.dll,
定义与WrapperCallBack相符合的Delegate,并且调用DoActionWrapper就可以了
eDRIVE 2004-06-15
  • 打赏
  • 举报
回复
谢谢各位的回复

Lostinet,我明白你的意思,但是WindowsAPI的回调都是stdcall约定的,delegate默认实现是stdcall,所以API的回调可以完成,但是我要编写cdecl约定的回调过程,请问该怎么实现?

9sun888,我现在想做的是在c#程序中装载一个私有的DLL,DLL中的一个函数需要一个回调函数指针,这个回调函数是按照cdecl调用约定的,我在尝试用c#写一个满足条件的回调函数,即我必须遵守相同的调用约定。c#发起的对DLL函数的主动调用我已经实现,现在就是实现这个反向调用很没有心得。


Lostinet 2004-06-15
  • 打赏
  • 举报
回复
这个用delegate就可以了.


例如如果你使用EnumWindows,那么定义可以是:
[DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool EnumWindows(EnumThreadWindowsCallback callback, IntPtr extraData);

public delegate bool EnumThreadWindowsCallback(IntPtr hWnd, IntPtr lParam) ;

你自己试一下吧.
9sun888 2004-06-15
  • 打赏
  • 举报
回复
我查了一下MSDN,这段代码:

CallingConvention 枚举
指定调用在非托管代码中实现的方法所需的调用约定。
[C#]
[Serializable]
public enum CallingConvention
请始终使用 CallingConvention 枚举,而不是 CALLCONV 枚举在托管代码中指定一个调用约定。后者仅作 COM 定义之用。DllImportAttribute 和 System.Reflection.Emit 中的若干类使用 CallingConvention 枚举,以动态发出平台调用签名。
成员
成员名称说明
Cdecl调用方清理堆栈。这使您能够调用具有 varargs 的函数(如 Printf),使之可用于接受可变数目的参数的方法。
FastCall不支持此调用约定。
StdCall被调用方清理堆栈。这是使用平台 invoke 调用非托管函数的默认约定。
ThisCall第一个参数是 this 指针,它存储在寄存器 ECX 中。其他参数被推送到堆栈上。此调用约定用于对从非托管 DLL 导出的类调用方法。
Winapi
受 .NET Framework 精简版的支持。此成员实际上不是调用约定,而是使用了默认平台调用约定。例如,在 Windows 上默认为 StdCall,在 Windows CE .NET 上默认为 Cdecl。

示例
[Visual Basic, C#, C++] 下面的示例演示了如何应用 Cdecl 调用约定,因为由调用方负责清理堆栈,所以必须使用此调用约定。
[C#]
using System;
using System.Runtime.InteropServices;

public class LibWrap
{
// C# doesn't support varargs so all arguments must be explicitly defined.
// CallingConvention.Cdecl must be used since the stack is
// cleaned up by the caller.

// int printf( const char *format [, argument]... )

[DllImport("msvcrt.dll", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)]
public static extern int printf(String format, int i, double d);

[DllImport("msvcrt.dll", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)]
public static extern int printf(String format, int i, String s);
}

public class App
{
public static void Main()
{
LibWrap.printf("\nPrint params: %i %f", 99, 99.99);
LibWrap.printf("\nPrint params: %i %s", 99, "abcd");
}
}
13880079673 2004-06-15
  • 打赏
  • 举报
回复
C#里面的回调函数的定义就是:

在托管应用程序中帮助非托管 DLL 函数完成任务的代码。

你的第二句俺没大看懂
elite2018 2004-06-15
  • 打赏
  • 举报
回复
try to use [unsafe] to directly call unmanged code
turnmissile 2004-06-15
  • 打赏
  • 举报
回复
关注...

110,565

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧