我是一名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结构?
答案 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;
}