5

我是一名 C++ (MSVC) 作家,VB 新手,试图帮助一位以前没有做过这项任务的专家 VB.net 作家。

我们希望开发 C/C++ 和 VB 应用程序以使用用 C++ 编写的 DLL 和 C extern-ed API 函数。C++ 程序运行良好。这是我们遇到困难的VB。

DLL 提供了一个extern C函数:

RegisterCallback( void* cbFuncPtr, void* dataPtr );

注意 1:请参阅下面的说明,了解设计更改以及我们做出此更改的原因。

注 2:附加更新添加为下面的答案。

其中回调函数有这个 C typedef:

typedef   (void)(* CALL_NACK)(void*);

预计将cbFuncPtr是一个函数指针,指向一些将被称为 CALL_BACK 的 VB 函数。dataPtr是指向具有此 C 定义的数据结构的指针:

typedef struct 
{
   int      retCode;
   void*    a_C_ptr;
   char     message[500];
}  cbResponse_t;

哪里a_C_ptr is an internal pointer in the DLL that the VB can cast to长`。它唯一地标识在 DLL 中进行回调的位置,并允许 VB 函数识别来自相同/不同位置的调用。

我们可以RegisterCallback()很好地从 VB 访问和运行该函数。日志显示我们到达那里并且数据被传入。实际数据似乎是问题所在。

在阅读大约一百万个论坛条目时,我们了解到 VB 不知道指针是什么,并且 VB 结构不仅仅是有组织的内存。我们很确定 VB 结构的“地址”不是 C 认为的地址。我们已经看到反复提到“编组”和“托管数据”,但缺乏足够的理解来了解这告诉我们什么。

我们应该如何编写 VB 代码来为 DLL 提供其回调函数的执行地址,以及我们如何编写一个 VB 结构,让 DLL 可以像 C++ 那样填写?

我们是否需要一个 DLL 函数,调用应用程序可以说“C”或“VB”,并让 DLL 以不同的方式处理结构指针?如果是这样,如何编写 C 代码来填充 VB 结构?

4

2 回答 2

1

这有点太大太深了,不能只是对原始帖子的编辑......

从@DaveNewman 发布的链接中,我提取了这个宝石:


这里有一点关于紧凑框架的内容,但在成人框架中也是如此:

.NET Compact Framework 支持自动封送包含简单类型的结构和类。所有字段在内存中的排列顺序与它们在结构或类定义中出现的顺序相同。类和结构都在本机代码中作为指向 C/C++ 结构的指针出现。

托管堆中的对象可以随时由垃圾收集器在内存中移动,因此它们的物理地址可能会更改,恕不另行通知。P/Invoke 在每个方法调用期间自动锁定通过引用传递的托管对象。这意味着传递给非托管代码的指针将对该调用有效。请记住,不能保证对象不会在后续调用中移动到不同的内存地址。

http://msdn.microsoft.com/en-us/library/aa446538.aspx#netcfmarshallingtypes_topic6


这是RegisterCallback( fcnPtr, dataPtr)功能的主要障碍。注册时传入的指针可能随时更改,RegisterCallback()不是当前语句。发帖作者是这样总结的

您无需执行任何操作,因为结构会在通话期间自动固定。

当然,这意味着没有被固定在通话之外。

出于这个原因,我们决定对设计进行更改,以在 DLL 的 C/C++ 世界中内置响应结构,而不是在 VB 的空间中。这样它就会原地不动。实际回调函数的签名将保持不变,因此 VB 程序可以知道 DLL 将响应放在哪里。这也允许 DLL 中的响应者为不同的需求分配不同的响应结构。

于 2013-04-11T11:41:17.053 回答
0

再一次,我的更新太大了,无法发表评论!

2013 年 4 月 18 日更新:

好吧,使用上面引用的从非托管代码调用托管代码中的代码的尝试失败了。我们最终不得不将 /clr 添加到 DLL 中,从而将 DLL 转换为托管代码,这使得它无法从 C 应用程序中使用。

我们现在正在Callback Sample中测试示例,我能够展示它制作了一个可与 VB 和 C++ 一起使用的 DLL。您需要拥有PinvokeLib.dll 源才能完成这项工作。

这是 C++(真正的 C)测试器的代码。编译为 MSVC 项目。


注意:注意这一行中的 __cdecl:

typedef bool (__cdecl *FPtr)(BOOL_FP_INT fp, int i );

这是我必须找到的秘密。DLL 和此应用程序是使用 __cdecl 链接而不是 __stdcall 编译的。它们是 VC++ 中的默认值,我只是使用了默认值。我尝试将所有内容都更改为 __stdcall ,但这没有用。必须是 __cdecl。


// PinvokeTester.cpp : Defines the entry point for the console application.
//


#include <stdio.h>
#include <cstdio>
#include <stdlib.h>
#include <cstdlib>
#include <string.h>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <Windows.h>

#define PINVOKELIB_API __declspec(dllimport)

HINSTANCE      hLib;                            // Windows DLL handle

bool     CALLBACK VBCallBack( int value );
bool     AttachLibrary( void );
void *   GetFuncAddress( HINSTANCE hLib, const char* procname );

int main(int argc, char* argv[])
{
   if ( !AttachLibrary() )
   {
      printf( "Lib did not attach.\n" );
      exit(1);
   }

   typedef bool (CALLBACK *BOOL_FP_INT)(int i );

   typedef bool (__cdecl *FPtr)(BOOL_FP_INT fp, int i );

   FPtr TestCallBack = (FPtr)GetFuncAddress( hLib, "TestCallBack" );

   TestCallBack( (BOOL_FP_INT)VBCallBack, 255 );

    return 0;
}

bool CALLBACK VBCallBack( int value )
{
    printf( "\nCallback called with param: %d", value);
    return true;
}


bool     AttachLibrary( void )
{
   // Get a var for the IPC-dll library.
   std::string    dllName;

   /*--- First, link to the IPC-dll library or report failure to do so. ---*/
   dllName = ".\\PinvokeLib";
   if ( NULL == (hLib = LoadLibraryA( dllName.c_str() )) )
   {
      printf( "\nERROR: Library \"%s\" Not Found or Failed to Load. \n\n", dllName.c_str() );
      printf( "\"%s\"\n", GetLastError() );
      return false;
   }

   return true;
}

//=====================================================================
void *  GetFuncAddress( HINSTANCE hLib, const char* procname )
{
   void *   procAddr = NULL;

   procAddr = (void *)GetProcAddress( hLib, procname );

   // If the symbol wasn't found, handle error ---------------------
   if ( NULL == procAddr )
   {
      std::cout << "ERROR: Could not get an address for the \""
                << procname  << "\" function. : "
                << GetLastError()  << std::endl;
      exit( 7 );
      procAddr = (void*)NULL;
   }

   return procAddr;
}
于 2013-04-18T12:17:38.737 回答