使用参数从C#调用void函数(在win32中)

时间:2013-07-11 20:04:32

标签: c# .net winapi dll dllimport

我在win32中有以下代码,它在目标应用程序中设置了一个钩子。

void InstallHook(DWORD ThreadId)
{
    g_hHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, g_hInstDll, ThreadId); 
}

我希望从C#(。net)调用此函数。

到目前为止,我有这个:

[DllImport("TheHookDll.dll")]
public extern static void InstallHook(UInt32 ThreadId);

[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

我称之为:

IntPtr hWnd = FindWindow(null, "MyTargetAppWindowTitle");

UInt32 threadID = GetWindowThreadProcessId(hWnd, IntPtr.Zero);

InstallHook(threadID);

这为我提供了目标的句柄(hWnd),以及win32中InstallHook函数中使用的threadID。 (它只是小数而不是十六进制)

但我收到此错误消息:

  

检测到PInvokeStackImbalance消息:调用PInvoke函数   'TheOperator!TheOperator.Form1 :: InstallHook'使堆栈失衡。   这可能是因为托管的PInvoke签名不匹配   非托管目标签名。检查调用约定和   PInvoke签名的参数与目标非托管匹配   签名。

我试图将我的dll文件中的调用约定(配置属性 - > C / C ++ - >所有选项 - >调用约定)从__cdel更改为__stdcall,但没有任何运气。 (同样的错误)

我做错了什么?

我已将DWORD更改为UInt32,因为c#不支持DWORD等。但这是正确的方法吗?

任何提示?

1 个答案:

答案 0 :(得分:1)

像这样定义你的PInvoke:

    [DllImport("TheHookDll.dll", CallingConvention = CallingConvention.Cdecl)]
    public extern static void InstallHook(UInt32 ThreadId);

原因是当您调用此特定函数时,调用者需要清理堆栈。如果没有在PInvoke签名中明确指定它,运行时将不会清理堆栈,因此它会使堆栈混乱,从而导致您看到的错误消息。

如果未明确指定CallingConvention,则运行时假定您尝试调用的函数为StdCall,其中被调用者清理堆栈。情况并非如此,堆栈将全部搞砸并且很脏。

除此之外,您的签名看起来是正确的; DWORD确实是C#中的uintUInt32。如果那个让您遇到麻烦,可以尝试使用MarshalAs属性进行装饰,并让Marshal将其作为非托管类型U8返回。