2013-04-10 64 views
5

我是一位C++(MSVC)编写者,VB新手试图帮助一位尚未完成此任务的专家VB.net编写者。在VB中传递指向extern C函数的指针

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

DLL提供一个extern C功能:

RegisterCallback(void* cbFuncPtr, void* dataPtr); 

注1:请参见下面的设计变化,我们把它的原因我的笔记。

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

在回调函数HAVS这款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 long`。它唯一标识DLL中回调的位置,并允许VB功能识别来自相同/不同位置的呼叫。

我们能够从VB访问和运行RegisterCallback()功能就好了。记录显示我们到达那里并且数据被传入。这是实际的数据,似乎是问题所在。

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

我们应该如何编写VB代码给DLL的回调函数的执行地址,以及如何编写一个VB构造,使DLL可以像C++一样填充?

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

+1

这[MS文章,了解如何马歇尔数据到一个DLL(http://msdn.microsoft.com/en-us/library/fzhhdwae.aspx)至少看起来像它可能适用。它被[这篇关于从VB调用C++ DLL的文章]链接到(http://social.msdn.microsoft.com/forums/en-US/Vsexpressvb/thread/4c486d10-fe8b-49da-a5f1-8054b82251ff/)。我不是一个VB或.NET程序员,所以我可能会脱颖而出。 – 2013-04-10 19:59:50

+0

@DaveNewman这次讨论的最后一篇文章已经很有帮助。作为一名C程序员,数据可以随时随地移动,并且指向它的指针只在查询它们的语句时才被信任,是异端。猜测我将不得不在DLL侧创建结构,并告诉应用程序在哪里放置数据。这违背了让应用程序拥有数据的想法,以便尽管DLL可能会这样做,它仍然存在。 – 2013-04-10 20:41:47

+1

据我所知,在vb.net中,选择指针的IntPtr结构是非常有用的。但是如果你已经有了一个指针定义,不确定是否真的需要。指针也可在vb.net上找到,只是有点难以找到 – Amegon 2013-04-10 20:59:11

回答

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

+0

更新。基于@DaveNewman的发布以及我的同事的出色工作,我们正在添加[从非托管代码调用托管代码,反之亦然]中提出的托管非托管类(http://www.codeproject.com/Articles/) 9903 /调用,托管代码从 - 非托管代码和副#xx3493475xx)。 – 2013-04-12 16:23:32

0

我的更新再次过大,仅仅只是一个评论!

更新2013年4月18日:

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

我们现在测试的例子是Callback Sample,我能够展示一个可以同时使用VB和C++的DLL。你需要有PinvokeLib.dll Source才能完成这项工作。

这是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; 
} 
相关问题