将指针传递给VB中的DLL中的extern C函数

时间:2013-04-10 19:48:50

标签: c++ vb.net dll

我是一名C ++(MSVC)作家,VB新手试图协助以前没有完成此任务的专家VB.net作家。

我们希望开发C / C ++和VB应用程序,以使用C ++编写的带有C外部API函数的DLL。 C ++程序运行得很好。这是我们遇到困难的VB。

DLL提供extern C功能:

RegisterCallback( void* cbFuncPtr, void* dataPtr );

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

注意2:在下面添加了附加更新作为答案。

回调函数有这个C typedef:

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

cbFuncPtr应该是指向某个VB函数的函数指针,该函数将被调用为CALL_BACK。 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函数识别来自相同/不同位置的调用。

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

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

我们应该如何编写VB来为DLL提供其回调函数的执行地址?我们如何编写DLL可以填充的VB结构,就像它对C ++一样?

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

2 个答案:

答案 0 :(得分:1)

这有点太大而且只是对原始帖子进行编辑......

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


  

这里有一些关于紧凑框架的内容,但它的内容相同   成年人框架:

     

.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中的响应者为不同的需求分配单独的响应结构。

答案 1 :(得分:0)

我的更新再次太大而不仅仅是评论!

2013年4月18日更新:

好吧,尝试使用上面引用的Calling Managed Code from Unmanaged Code代码是一个破产。我们最终不得不向DLL添加/ clr使得将DLL转换为托管代码,这使得它无法从C应用程序中使用。

我们现在正在测试Callback Sample上的示例,我能够显示它创建了一个适用于VB和C ++的DLL。您需要PinvokeLib.dll Source来完成这项工作。

这是C ++(C really)测试人员的代码。编译为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;
}