I'm trying to create a C++ DLL for use in a VBA program. I'm following this example and had success compiling the example code and using the resulting DLL. However, I needed to add some additional functionality to the DLL so I created a few more functions in the example code and recompiled it. I then made a test program to test my new functions. When I try to call some of the DLL functions from my test project I get linker errors similar to this:
error LNK2019: unresolved external symbol "int __stdcall PWCreateDocument(long,char *,char *)" (?PWCreateDocument##YGHJPAD0#Z) referenced in function _wmain
This error occurs when I call the functions to initialize ProjectWise, CVbaHelperApp::InitInstance(), and my custom function PWCreateDocument.
This error DOES NOT OCCUR when I call PWGetLastErrorMessage(). I am able to access this function from my test program, but not any other functions in the DLL.
I've ruled out any common linker errors such as, misspellings/incorrect types between function header and definition.
I find it strange that I can successfully call PWGetLastErrorMesssage but not any other functions.
Here is the code for my test program vbaHelperTest3.cpp:
#include "stdafx.h"
typedef long LONG;
typedef int BOOL;
int _tmain(int argc, _TCHAR* argv[])
{
char* filePath = "C:\\pwworking\\cemvn\\b2edsjga\\d0572507\\";
char* fileName = "BUMP Imagery 2009.xwms";
LONG projID = 572507;
char* errorMsg;
std::cout << "Hello World" << std::endl;
CVbaHelperApp myApp;
BOOL isInit = myApp.InitInstance();
std::cout << "Is Initialized? " << isInit << std::endl;
errorMsg = PWGetLastErrorMessage();
std::cout << "Error Message: " << errorMsg << std::endl;
BOOL results = PWCreateDocument(projID, filePath, fileName);
std::cout << "PWCreateDocument Result: " << results << std::endl;
return 0;
}
The header stdafx.h includes the header for my DLL, vbaHelper.h. This is the code for vbaHelper.h:
// vbaHelper.h : main header file for the VBAHELPER DLL
//
#include "stdafx.h"
#if !defined(AFX_VBAHELPER_H__3FBDA9E4_EE8B_4AA1_8FD0_21A0A8B6C590__INCLUDED_)
#define AFX_VBAHELPER_H__3FBDA9E4_EE8B_4AA1_8FD0_21A0A8B6C590__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif
#include "resource.h" // main symbols
/////////////////////////////////////////////////////////////////////////////
// CVbaHelperApp
// See vbaHelper.cpp for the implementation of this class
//
class CVbaHelperApp : public CWinApp
{
public:
CVbaHelperApp();
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CVbaHelperApp)
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
//}}AFX_VIRTUAL
//{{AFX_MSG(CVbaHelperApp)
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
//Function definitions added by me
typedef int BOOL;
typedef long LONG;
LONG __stdcall PWGetDocumentName( LONG , LONG , VOID **);
LONG __stdcall PWGetDocumentIDs( TCHAR **, LONG *, LONG *);
BOOL __stdcall PWCreateDocument( LONG, char*, char*);
char * __stdcall PWGetLastErrorMessage(void);
#endif // !defined(AFX_VBAHELPER_H__3FBDA9E4_EE8B_4AA1_8FD0_21A0A8B6C590__INCLUDED_)
Finaly, here is the code for vbaHelper.cpp, the DLL:
/****************************************************************************
*
* ProjectWise(TM) Software Development Kit
* Sample Application
* Copyright (C) 2003 Bentley Systems, Incorporated
* All Rights Reserved
*
****************************************************************************/
/****************************************************************************
*
* Project Name: VbaHelper
*
* Project Description: This example is used in conjunction with MicroStation's
* VBA to extract a Design file's attributes.
*
* File name: VbaHelper.cpp
*
* File description: Custom Module implementation
*
****************************************************************************/
/*---------------------------------------------------------------------------
Copyright (C) 2003 Bentley Systems, Incorporated
All Rights Reserved
THIS IS AN OPEN SOURCE CODE OF BENTLEY SYSTEMS, INCORPORATED
You have a royalty-free right to use, modify, reproduce and distribute
the Sample Applications (and/or any modified version) in any way you find
useful, provided that you agree that Bentley Systems, Incorporated has no
warranty obligations or liability for any Sample Application files which
are modified.
No guarantees of performance accompany Sample Application, nor is any
responsibility assumed on the part of the author(s). The software has
been tested extensively and every effort has been made to insure its
reliability.
---------------------------------------------------------------------------*/
/****************************************************************************
*
* Include Files
*
****************************************************************************/
#include "stdafx.h"
#include "vbaHelper.h"
#include "aaatypes.h"
#include "aadmsdef.h"
#include "aawddef.h"
#include "aawindef.h"
#include "aaodsdef.h"
#include "stdtypes.h"
#include "aadmsapi.fdf"
#include "aawinapi.fdf"
#include "aawindms.fdf"
#include "aaodsapi.fdf"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define DLLEXPORT __declspec( dllexport )
#define WINAPI __stdcall
//
// Note!
//
// If this DLL is dynamically linked against the MFC
// DLLs, any functions exported from this DLL which
// call into MFC must have the AFX_MANAGE_STATE macro
// added at the very beginning of the function.
//
// For example:
//
// extern "C" BOOL PASCAL EXPORT ExportedFunction()
// {
// AFX_MANAGE_STATE(AfxGetStaticModuleState());
// // normal function body here
// }
//
// It is very important that this macro appear in each
// function, prior to any calls into MFC. This means that
// it must appear as the first statement within the
// function, even before any object variable declarations
// as their constructors may generate calls into the MFC
// DLL.
//
// Please see MFC Technical Notes 33 and 58 for additional
// details.
//
/////////////////////////////////////////////////////////////////////////////
// CVbaHelperApp
BEGIN_MESSAGE_MAP(CVbaHelperApp, CWinApp)
//{{AFX_MSG_MAP(CVbaHelperApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CVbaHelperApp construction
CVbaHelperApp::CVbaHelperApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
/////////////////////////////////////////////////////////////////////////////
// The one and only CVbaHelperApp object
CVbaHelperApp theApp;
/*----------------------------------------------------------------------+
|
| name mcmMain_GetDocumentIdByFilePath
|
| author BSI 04/2003
|
| Description This function finds document and project
| numbers of the document specified by its path.
|
+----------------------------------------------------------------------*/
extern "C" int mcmMain_GetDocumentIdByFilePath
(
LPWSTR pchFilePath, /* i full file path to search */
long *plProNo, /* o project id */
long *plDocNo /* o document id */
);
/*----------------------------------------------------------------------+
|
| name HooksInitialize
|
| author BSI 04/2003
|
| Description Dll entry function for ProjectWise.
|
+----------------------------------------------------------------------*/
extern "C" LONG HooksInitialize
(
ULONG ulMask, // i Application Mask
LPVOID lpReserved // i Reserved (must be NULL)
)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
return IDOK;
}
/*----------------------------------------------------------------------+
|
| name PWGetDocumentName
|
| author BSI 04/2003
|
| Description A function that will populate documentName for the given
| DOCUMENT_ID.
|
| Return SUCCESS - The path and file name of the specified document
| were built successfully.
|
| -1 - Failed to build the path and file name of the
| specified document.
|
+----------------------------------------------------------------------*/
LONG WINAPI PWGetDocumentName
(
LONG PROJECT_ID, /* i Project ID*/
LONG DOCUMENT_ID, /* i Document ID */
VOID **documentName /* o Document Name*/
)
{
BOOL status = FALSE;
TCHAR tempDocName[MAX_STRING];
// Extract the document's name
status = aaApi_GetDocumentFileName (PROJECT_ID, DOCUMENT_ID, tempDocName, MAX_STRING);
_tcscpy ((TCHAR*)(*documentName), tempDocName);
return (status == TRUE ? SUCCESS : -1);
}
/*----------------------------------------------------------------------+
|
| name PWGetDocumentIDs
|
| author BSI 04/2003
|
| Description A function that will return the document's
| Project and Document IDs.
|
| Return SUCCESS or error number
|
+----------------------------------------------------------------------*/
LONG WINAPI PWGetDocumentIDs
(
TCHAR **fileName, /* i Desgin File Name */
LONG *ProjectID, /* o Project ID */
LONG *DocumentID /* o Document ID */
)
{
return mcmMain_GetDocumentIdByFilePath (*fileName, ProjectID, DocumentID);
}
/*----------------------------------------------------------------------+
|
| name convertCharArrayToLPCWSTR
|
| author MY CUSTOM FUNCTION 10/2015
|
| Description Converts regular string to LPCWSTR, which
| is required by projectwise.
|
| Return The converted string.
|
+----------------------------------------------------------------------*/
wchar_t *convertCharArrayToLPCWSTR(const char* charArray)
{
wchar_t * wString=new wchar_t[4096];
MultiByteToWideChar(CP_ACP, 0, charArray, -1, wString, 4096);
return wString;
}
/*----------------------------------------------------------------------+
|
| name PWCreateDocument
|
| author MY CUSTOM FUNCTION 10/2015
|
| Description A function that will create a new document in the
| specified PW project.
|
| Return SUCCESS or error number
|
+----------------------------------------------------------------------*/
BOOL WINAPI PWCreateDocument
(
LONG PROJECT_ID, /* i Project ID*/
char* PATH_NAME, /* path of document */
char* FILE_NAME /* name of document */
)
{
LONG docID = 0L;
//LONG lngAppID = aaApi_GetFExtensionApplication(L"xwms");
LONG lngAppID = aaApi_GetFExtensionApplication(L"pdf");
LONG lngWorkSpaceID = aaApi_GetWorkspaceProfileId(PROJECT_ID, 0);
LPCWSTR _path_name = convertCharArrayToLPCWSTR(PATH_NAME);
LPCWSTR _file_name = convertCharArrayToLPCWSTR(FILE_NAME);
WCHAR strWorkingDir[_MAX_PATH]; // for checked out file locationmemset (strWorkingDir, '\0', _MAX_PATH);
BOOL status = aaApi_CreateDocument(
&docID, //new document's ID
PROJECT_ID, //Passed in project ID
0, //default
0, //default
0, //default
lngAppID, //Applicaiton ID
0, //no department
lngWorkSpaceID, //workspace profile
_path_name, //source file
_file_name, //Name of file in PW, must be the same as Document Name
_file_name, //Document Name
NULL, //Document description
NULL, //Document Version
FALSE, //Specifies that this document is checked out to the user after it is create in PW.
AADMSDOCCREF_DEFAULT, //Checks documentaiton for flags
//_path_name, //location of the file if checked out
strWorkingDir,
_MAX_PATH - 1, //make sure the buffer is large enough
0 //New attribute ID in environment if created
);
//???
//long errorID=aaApi_GetLastErrorID();
//LPCWSTR errorStr = aaApi_GetLastErrorDetail();
return status;
}
/*----------------------------------------------------------------------+
|
| name PWGetLastErrorMessage
|
| author BSI 04/2003
|
| Description A function that will return the last ProjectWise Error
| message.
|
| Return Last Error message.
|
+----------------------------------------------------------------------*/
char * WINAPI PWGetLastErrorMessage
(
void
)
{
char *errorMsg;
TCHAR TerrorMsg [MAX_STRING];
errorMsg = (char *)malloc (sizeof (char) *MAX_STRING);
_tcscpy (TerrorMsg, aaApi_GetLastErrorMessage());
aaApi_UnicodeToAnsiStr (TerrorMsg, errorMsg,MAX_STRING);
return errorMsg;
}
/*----------------------------------------------------------------------+
|
| name PWGetDocumentAttributes
|
| author BSI 04/2003
|
| Description A function that will return the documents attributes.
|
| Return SUCCESS or -1 if error.
|
+----------------------------------------------------------------------*/
LONG WINAPI PWGetDocumentAttributes
(
LONG ProjectID, /* i Project ID */
LONG DocumentID, /* i Document ID */
void **AttributeData /* o Document attributes */
)
{
CString message;
LONG status = SUCCESS;
LONG lEnvId = aaApi_GetEnvId (0);
LONG lTabNo = aaApi_GetEnvNumericProperty (ENV_PROP_TABLEID, 0);
LONG count = -1;
int rowCount = -1;
/* Select environment for given project */
status = aaApi_SelectEnvByProjectId (ProjectID);
if (status == -1 || status == 0)
{
return -1;
}
else
{
// Select the documents Attribute Data
rowCount = aaApi_SelectLinkDataByObject (
lTabNo, /* i Table identifier (required) */
AADMSLDT_DOCUMENT, /* i Reference Item type */
ProjectID, /* i First item identifier */
DocumentID, /* i Second item identifier */
NULL, /* i Where statement (optional) */
&count, /* io Column count in lplColumnIds */
NULL, /* i Columns to fetch (NULL - all) */
0 /* i Flags (AADMSLDSF_XXX) */
);
if (rowCount <= 0)
return -1;
for (int colIndex= 0; colIndex<count; colIndex++)
{
message += aaApi_GetLinkDataColumnStringProperty (LINKDATA_PROP_COLUMN_NAME, colIndex);
message += ": ";
message += aaApi_GetLinkDataColumnValue (0, colIndex);
message +="\n";
}// end for
_tcscpy ((TCHAR*)(*AttributeData), message);
}
return SUCCESS;
}
/*----------------------------------------------------------------------+
|
| name InitInstance
|
| author BSI 04/2003
|
| Description Initialize the PW API
|
| Return Nonzero if initialization is successful; otherwise 0.
|
+----------------------------------------------------------------------*/
BOOL CVbaHelperApp::InitInstance()
{
// Initialize PW
aaApi_Initialize (AAMODULE_ALL);
return CWinApp::InitInstance();
}
/*----------------------------------------------------------------------+
|
| name ExitInstance
|
| author BSI 04/2003
|
| Description Remove the hook function on exit.
|
| Return 0 for success or > 0 for error.
|
+----------------------------------------------------------------------*/
int CVbaHelperApp::ExitInstance()
{
return CWinApp::ExitInstance();
}
Edit Update
I realized that my header file does not need function definitioins. The project uses a vbaHelper.def file to define the functions. I removed the definitions from the header and now have different errors:
error C3861: 'PWGetLastErrorMessage': identifier not found
error C3861: 'PWCreateDocument': identifier not found
Edit Number 2
I do not believe this should qualify as a duplicate of other Unresolved external symbol questions as I have vetted all the common reasons for this error. Also, I have now resolved the linker error and I'm looking for reason why I would be receiving "identifier not found" errors.
You need to append your function definitions with extern "C" if you want to be able to call them from c. You already have such an example in the code that you've posted.
extern "C" LONG HooksInitialize
BOOL WINAPI PWCreateDocument
is should be
BOOL DLLEXPORT PWCreateDocument
Related
I'm trying to write a basic plugin for gstreamer in C++. I am working from another person's examples and have followed the structure of the code (I believe) exactly. However, I've been trying to compile the code and I am seeing an error saying
unresolved external symbol gst_adder_get_type referenced in function "int __cdecl plugin_init(struct _GstPlugin *)" (?plugin_init##YAHPEAU_GstPlugin###Z)
I have declared but not implemented gst_adder_get_type. I did that because the examples I have seen have not had any implementation of the function either, they just have the declaration in the header file and somehow that works.
I have tried implementing the function myself anyway, but other errors pop up instead.
Adder.h:
#include <gst/gst.h>
#include <gst/base/gstpushsrc.h>
G_BEGIN_DECLS
#define GST_TYPE_ADDER (gst_adder_get_type())
#define GST_ADDER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ADDER,Gstadder))
#define GST_ADDER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ADDER,GstadderClass))
#define GST_IS_ADDER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ADDER))
#define GST_IS_ADDER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ADDER))
struct _Gstadder
{
GstElement element;
//pads
GstPad* srcpadadder; //publish data
// property values
gint _num_one;
gint _num_two;
gint _sum;
};
struct _GstadderClass
{
GstElementClass parent_class;
};
GType gst_adder_get_type(void);
typedef struct _Gstadder Gstadder;
typedef struct _GstadderClass GstadderClass;
GST_ELEMENT_REGISTER_DECLARE(adder);
G_END_DECLS
Adder.cpp:
// Adder.cpp : Defines the exported functions for the DLL application.
#include "stdafx.h"
#include "pch.h"
#include <gst/gst.h>
#include "Adder.h"
#include <gstreamer-1.0/gst/base/gstbasesrc.h>
#include <gstreamer-1.0/gst/base/gstpushsrc.h>
GST_DEBUG_CATEGORY(gst_adder_debug);
// This defines the properties owned by this class.
enum
{
PROP_NUMONE,
PROP_NUMTWO,
PROP_SUM
};
// Definitions of functions later in this file so that we can access them now.
static void gst_adder_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec);
static void gst_adder_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec);
static gboolean gst_adder_start(GstBaseSrc* basesrc);
static gboolean gst_adder_stop(GstBaseSrc* basesrc);
static GstCaps* gst_adder_get_caps(GstBaseSrc* bsrc, GstCaps* caps);
static GstFlowReturn gst_adder_fill(GstPushSrc* src, GstBuffer* buf);
static void gst_adder_finalize(GObject* object);
/*
* "CAPS" are literally capabilities. They seem only to be descriptions, not functions.
* I think this means that when I run an inspect on the DLL, this gets called to write out
* what the capabilities of the function actually are and what kinds of pads exist.
*/
static GstStaticPadTemplate src_det_factory = GST_STATIC_PAD_TEMPLATE(
"src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS("Integer"));
static void gst_adder_class_init(GstadderClass * klass) {
/*
Class starting initiation. Different from the object instantiation.
*/
GObjectClass* gobject_class;
gobject_class = G_OBJECT_CLASS(klass);
GstElementClass* element_class;
element_class = GST_ELEMENT_CLASS(klass);
GstBaseSrcClass* gstbasesrc_class;
gstbasesrc_class = (GstBaseSrcClass*)klass;
GstPushSrcClass* gstpushsrc_class;
gstpushsrc_class = (GstPushSrcClass*)klass;
// Metadata. Not sure if this is redundant or not. Assuming not.
const gchar* m_a = "AdditionMan";
gst_element_class_set_static_metadata(
element_class,
m_a,
"MY_ADDER",
"Add two numbers, return the sum",
"John Garst <john.garst#parsons.com>");
// Initialize pad for the source
gst_element_class_add_pad_template(element_class, gst_static_pad_template_get(&src_det_factory));
// This appears to be where we are linking the callback functions for the adder.
gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_adder_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_adder_get_property);
gstbasesrc_class->start = GST_DEBUG_FUNCPTR(gst_adder_start);
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR(gst_adder_stop);
gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR(gst_adder_get_caps);
gstpushsrc_class->fill = GST_DEBUG_FUNCPTR(gst_adder_fill);
gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_adder_finalize);
// Property Definitions. This is linked to an enum at the top of this file,
// and will be used more in the adder instantiation and getters/setters.
g_object_class_install_property(
gobject_class,
PROP_NUMONE,
g_param_spec_string(
"NumOne",
"NumOne",
"The first number to add.",
0,
G_PARAM_READWRITE
)
);
g_object_class_install_property(
gobject_class,
PROP_NUMTWO,
g_param_spec_string(
"NumTwo",
"NumTwo",
"The second number to add.",
0,
G_PARAM_READWRITE
)
);
g_object_class_install_property(
gobject_class,
PROP_SUM,
g_param_spec_string(
"Sum",
"Sum",
"The sum of NumOne and NumTwo.",
0,
G_PARAM_READWRITE
)
);
}
static void gst_adder_init(Gstadder* filter) {
/*
instantiate the element. This happens after class initiation.
*/
//initialize pads
filter->srcpadadder = gst_pad_new_from_static_template(&src_det_factory, "src");
//initialize values
filter->_num_one = 0;
filter->_num_two = 0;
filter->_sum = 0;
//configure
gst_base_src_set_format(GST_BASE_SRC(filter), GST_FORMAT_TIME);
gst_base_src_set_live(GST_BASE_SRC(filter), true);
gst_base_src_set_do_timestamp(GST_BASE_SRC(filter), true);
}
static gint gst_adder_add_two_numbers(Gstadder* filter) {
/*
This function takes two numbers and adds them together, returning the sum of those numbers.
*/
filter->_sum = filter->_num_one + filter->_num_two;
return filter->_sum;
}
void gst_adder_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) {
/*
Set properties in the adder.
We can set the two numbers to add, but not the sum.
*/
Gstadder* that = GST_ADDER(object);
switch (prop_id)
{
case PROP_NUMONE:
that->_num_one = g_value_get_int(value);
break;
case PROP_NUMTWO:
that->_num_two = g_value_get_int(value);
break;
}
}
void gst_adder_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec) {
/*
Set properties in the adder.
We can get any property, including the sum.
*/
Gstadder* that = GST_ADDER(object);
switch (prop_id)
{
case PROP_NUMONE:
g_value_set_int(value, that->_num_one);
break;
case PROP_NUMTWO:
g_value_set_int(value, that->_num_two);
break;
case PROP_SUM:
g_value_set_int(value, that->_sum);
break;
}
}
gboolean gst_adder_start(GstBaseSrc* basesrc) {
/*
Start the adder. There is nothing else necessary to initialize, so we won't do anything here.
*/
return gboolean();
}
gboolean gst_adder_stop(GstBaseSrc* basesrc) {
/*
Stop the adder. There is nothing to clean, so we won't do anything here.
*/
return gboolean();
}
GstFlowReturn gst_adder_fill(GstPushSrc* src, GstBuffer* buf) {
/*
Fill the adder from buffer. This seems to be where the magic actually happens.
*/
Gstadder* filter = GST_ADDER(src);
gst_adder_add_two_numbers(filter);
return GstFlowReturn();
}
GstCaps* gst_adder_get_caps(GstBaseSrc* bsrc, GstCaps* caps) {
/*
Return Adder capabilities
*/
#ifdef __linux__
return gst_caps_new_simple(0, NULL, NULL);
#else
return gst_caps_new_simple(0, nullptr);
#endif
}
void gst_adder_finalize(GObject* object) {
/*
Release memory and clean up the workspace.
*/
}
GType gst_adder_get_type(void)
{
static GType gst_adder_get_type = 0;
if (!gst_adder_get_type) {
static const GTypeInfo gst_adder_get_type_info = {
sizeof(GstadderClass),
NULL,
NULL,
NULL,
NULL,
NULL,
0,
0,
NULL,
};
gst_adder_get_type = g_type_register_static(G_TYPE_INTERFACE,
"GstVideoOverlay", &gst_adder_get_type_info, G_TYPE_FLAG_ABSTRACT);
}
return gst_adder_get_type;
}
gst_Plugin_Test.h
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the VIDEOSTRETCH_EXPORTS
// symbol defined on the command line. This symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// VIDEOSTRETCH_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef GST_PLUGIN_TEST_EXPORTS
#define GST_PLUGIN_TEST_API __declspec(dllexport)
#else
#define GST_PLUGIN_TEST_API __declspec(dllimport)
#endif
gst_Plugin_Test.cpp
// GST_Plugin_Test.cpp : Seems to initialize only the other classes that are involved. Doesn't seem
// like this can do it's own processing, which is a little odd to me. I don't know if that's correct.
//Includes. stdafx is required for some reason, as is pch.
#include "stdafx.h"
#include "pch.h"
#include "Adder.h"
#include <gst/gst.h>
#include "gst_Plugin_Test.h"
GST_DEBUG_CATEGORY(gst_gst_plugin_test_debug);
static gboolean plugin_init(GstPlugin* plugin)
{
/*
One of (?) the initialization functions. Seems like the main entry point.
*/
gboolean ret = TRUE;
GST_DEBUG_CATEGORY_INIT(gst_gst_plugin_test_debug, "gst_plugin_test", 0, "Test Stuff");
// The adder class is where things actually happen. We register it here.
ret = gst_element_register(plugin, "MY_ADDER", GST_RANK_NONE, GST_TYPE_ADDER);
return ret;
}
#define PACKAGE "GST_Plugin_Test"
// This is where the plugin metadata is defined. Shown on a gst-inspect-1.0
GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
GST_VERSION_MINOR,
GST_Plugin_Test,
"Just Testing Things",
plugin_init, "1.0", "LGPL",
"TEST", "http://www.polarisalpha.com");
UPDATE:
I did find a place that has another example where the get_type function is declared in the header and implemented in the C file, which makes a lot more sense. I've done it myself, again following the structure as best I can, and now can compile the DLL and put it into my plugins folder. However, I can't successfully inspect the plugin. It give me the error:
gst_element_register: assertion 'g_type_is_a (type, GST_TYPE_ELEMENT)' failed
which seems odd. I've updated the Adder file to include the implementation of the get_type function.
The structure of my Release version of my c++program is
| bin
| | game.exe
| content
| | sprites
| | | asset.png
Within the code I pass the path to the asset as "../content/sprites/asset.png", but it is not found by the program. What am I doing wrong?
Extra info: I am using SLD2 as a supporting library.
You are relying on the current directory being the directory containing your binary, but this is not always true. Instead, you can do this (error and range checking omitted for brevity):
WCHAR buf = new WCHAR [32768]; // allow for long path names
GetModuleFileNameW (NULL, buf, 32768);
WCHAR sep = wcsrchr (buf, '\\');
wcscpy (sep + 1, L"..\\content\\sprites\\asset.png");
...
delete [] buf;
Edit:
If you are calling code that depends on the current directory being the one containing your binary, then you can do this instead somewhere in your initialisation code:
WCHAR buf = new WCHAR [32768]; // allow for long path names
GetModuleFileNameW (NULL, buf, 32768);
WCHAR sep = wcsrchr (buf, '\\');
*sep = 0;
SetCurrentDirectoryW (buf);
delete [] buf;
Again, I have omitted error and range checking for brevity.
I have an application that seems on the surface to be straightforward, and is similar to scores of similar situations that have given me little or no trouble. After adapting a sample that I found on The Code Project, I set about to move the top level routine into a new console application, leaving the rest of the code to go into a conventional Win32 DLL. This is something that I've done dozens of times, and I know my way around __declspec(dllexport) and __declspec(dllimport) fairly well. I defined the usual pair of macros and preprocessor variables to tell the compiler to emit __declspec(dllimport) for the caller and __declspec(dllexport) for the callee. Every bit of this is "garden variety" Windows programming, yet the console program won't link.
Searching for answers, I used the /EP switch on the Microsoft Visual C++ compiler to obtain copies of the preprocessor output of both affected programs.
The main routine, ProcessTestCase_ELS, is defined in a separate source file, ProcessTestCase_ELS.cpp. As you can imagine, the listing is rather lengthy, even with WINDOWS_LEAN_AND_MEAN defined, but the relevant bit is only the following handful of lines.
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the EVENTLOGGINGFORALL_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// EVENTLOGGINGFORALL_API functions as being imported from a DLL, wheras this DLL sees symbols
// defined with this macro as being exported.
// ============================================================================
// Install an app as a source of events under the name pszName into the Windows
// Registry.
// ============================================================================
extern "C" __declspec(dllimport) DWORD AddEventSource
(
PCTSTR pszName , // Pointer to string containing event source ID
PCTSTR pszMessages , // Optional (default = NULL) pointer to string containing name of associated message file
PCTSTR pszLogName , // Optional (default = NULL) pointer to string containing name of event log
PCTSTR pszCategories , // Optional (default = NULL) pointer to string containing name of category message file
DWORD dwCategoryCount // Optional (default = 0) category count
) ;
The preprocessor output of the routine exported by the DLL is equally long, but the definition of the routine at issue is blessedly short. The entire routine follows.
// ============================================================================
// Install an app as a source of events under the name pszName into the Windows
// Registry.
// ============================================================================
extern "C" __declspec(dllexport) DWORD AddEventSource
(
PCTSTR pszName , // Pointer to string containing event source ID
PCTSTR pszMessages , // Optional (default = NULL) pointer to string containing name of associated message file
PCTSTR pszLogName , // Optional (default = NULL) pointer to string containing name of event log
PCTSTR pszCategories , // Optional (default = NULL) pointer to string containing name of category message file
DWORD dwCategoryCount // Optional (default = 0) category count
)
{
TCHAR szPath [ 260 ] ;
TCHAR * lpszPath = ( TCHAR * ) &szPath ;
HKEY hRegKey = 0 ;
DWORD dwError = 0L ;
sprintf ( szPath , // Output buffer
"%s\\%s\\%s" , // Format string
"SYSTEM\\CurrentControlSet\\Services\\EventLog" , // Substitute for token 1
pszLogName
? pszLogName
: "Application" , // Substitute for token 2
pszName ) ; // Substitute for token 3
// ------------------------------------------------------------------------
// Create the event source registry key.
// ------------------------------------------------------------------------
dwError = RegCreateKeyA ( (( HKEY ) (ULONG_PTR)((LONG)0x80000002) ) , // Hive Name
szPath , // Key Name
&hRegKey ) ; // Pointer to place to store handle to key
// ------------------------------------------------------------------------
// If pszMessages is NULL, assume that this module contains the messages,
// and get its absolute (fully qualfied) name.
// ------------------------------------------------------------------------
if ( !(pszMessages) )
{
if ( !(( HMODULE ) GetModuleFileNameA ( m_hinstDLL , szPath , 260 )) ) // Sze of buffer, in TCHARs.
{
return util::ReportErrorOnConsole ( ) ;
} // Unless ( ( HMODULE ) GetModuleFileName ( m_hinstDLL , szPath , MAX_PATH ) )
} // Unless ( pszMessages )
// ------------------------------------------------------------------------
// Register EventMessageFile.
// ------------------------------------------------------------------------
dwError = RegSetValueExA ( hRegKey , // Handle to key
"EventMessageFile" , // Value Name
0x00000000L , // Reserved - pass NULL
( 2 ) , // Value type
( PBYTE ) szPath , // Value data
( ( ( strlen ( ( LPCTSTR ) szPath ) + 1 ) * sizeof ( TCHAR ) ) ) ) ; // Size of value data - Macro TCharBufSizeP6C encapsulates all of this: ( _tcslen ( szPath ) + 1 ) * sizeof TCHAR )
// ------------------------------------------------------------------------
// Register supported event types.
// ------------------------------------------------------------------------
DWORD dwTypes = 0x0001
| 0x0002
| 0x0004 ;
dwError = RegSetValueExA ( hRegKey , // Handle to key
"TypesSupported" , // Value Name
0x00000000L , // Reserved - pass NULL
( 4 ) , // Value type
( LPBYTE ) &dwTypes , // Value data
sizeof dwTypes ) ; // Size of value data
if ( dwError )
{
return util::ReportErrorOnConsole ( dwError ) ;
} // if ( dwError )
// ------------------------------------------------------------------------
// If we want to support event categories, we have also to register the
// CategoryMessageFile, and set CategoryCount. Note that categories need to
// have the message ids 1 to CategoryCount!
// ------------------------------------------------------------------------
if ( dwCategoryCount > 0x00000000 )
{
if ( !(pszCategories && pszMessages) )
{
if ( !(( HMODULE ) GetModuleFileNameA ( m_hinstDLL , szPath , 260 )) )
{
return util::ReportErrorOnConsole ( ) ;
} // Unless ( ( HMODULE ) GetModuleFileName ( m_hinstDLL , szPath , MAX_PATH ) )
} // Unless ( pszCategories && pszMessages )
dwError = RegSetValueExA ( hRegKey , // Handle to key
"CategoryMessageFile" , // Value name
0x00000000L , // Reserved - pass NULL
( 2 ) , // Value type
MsgFileNameString ( pszMessages ,
pszCategories ,
lpszPath ) , // Value data
MsgFileNameLen ( pszMessages ,
pszCategories ,
lpszPath ) ) ; // Size of value data
if ( dwError )
{
return util::ReportErrorOnConsole ( dwError ) ;
} // if ( dwError )
dwError = RegSetValueExA ( hRegKey , // handle to key
"CategoryCount" , // value name
0x00000000L , // reserved
( 4 ) , // value type
( PBYTE ) &dwCategoryCount , // value data
sizeof dwCategoryCount ) ; // size of value data
if ( dwError )
{
return util::ReportErrorOnConsole ( dwError ) ;
} // if ( dwError )
} // if ( dwCategoryCount > 0 )
dwError = RegCloseKey ( hRegKey ) ;
if ( dwError )
{
return util::ReportErrorOnConsole ( dwError ) ;
} // if ( dwError )
else
{
return util::AnnounceChangeToAll ( ) ;
} // FALSE (UNexpected outcome) block, if ( lr )
} // DWORD •
The DLL project has a module definition file. Excluding the internal documentation, it is as follows.
LIBRARY EventLoggingForAll
VERSION 1, 0, 0, 1
EXPORTS
AddEventSource #2
RemoveEventSource #3
With respect to the DLL, dumpbin.exe gives the following reports on the DLL file and its import library.
Microsoft (R) COFF Binary File Dumper Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
Dump of file C:\Documents and Settings\DAG\My Documents\Programming\Visual Studio 6\DLL\EventLogging\Debug\EventLoggingForAll.lib
File Type: LIBRARY
Exports
ordinal name
2 _AddEventSource#20
3 _RemoveEventSource#8
Summary
A8 .debug$S
14 .idata$2
14 .idata$3
4 .idata$4
4 .idata$5
18 .idata$6
Microsoft (R) COFF Binary File Dumper Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
Dump of file C:\Documents and Settings\DAG\My Documents\Programming\Visual Studio 6\DLL\EventLogging\Debug\EventLoggingForAll.dll
File Type: DLL
Section contains the following exports for EventLoggingForAll.dll
0 characteristics
54BB1CE9 time date stamp Sat Jan 17 20:39:37 2015
0.00 version
2 ordinal base
2 number of functions
2 number of names
ordinal hint RVA name
2 0 00001014 AddEventSource
3 1 00001019 RemoveEventSource
Summary
1000 .data
1000 .idata
1000 .rdata
1000 .reloc
1000 .rsrc
12000 .text
Everything appears to be in order, yet the link step reports LNK2001: unresolved external symbol __imp__AddEventSource when I try to build the console program.
One final note; the error doesn't appear to be the result of the linker not seeing the import library, because it is the first library searched, and I can see it listed as such in the build log. I am also certain that there isn't an old version of the import library that could interfere, because I deleted every instance of it from the machine, verified that they were all gone, and started fresh.
LNK2001: unresolved external symbol __imp__AddEventSource
The linker error message says what you are doing wrong. It is looking for _AddEventSource but that's not the name of your exported function. It is _AddEventSource#20. Note the added "#20" name decoration. You obfuscated the problem by using a DEF file, but it is still visible from the dump of the .lib file.
This linker error is very much by design, it protects the client of your DLL from a exceedingly nasty problem. Your question contains no hints, but if the info is accurate, you changed a global compile option. Project + Properties, C/C++, Advanced, "Calling Convention" setting. The default is /Gd, you changed it to /Gz. But did not make the same change in the client project. This is so nasty because any call that the client code makes to an exported function will unbalance the stack. The runtime errors that this can cause are very hard to diagnose. The #20 postfix was designed to not let it come this far.
Change the setting back and make the calling convention explicit by adding the __stdcall attribute to the function declaration.
Other mistakes you are making:
Very important, not just for this problem, is to have only one .h file that declares the exported functions. Suitable to be #included into the client program's source code. That way there will never be a mismatch between the DLL and the client. This includes the need to put __stdcall in the declaration, now the DLL and client will always agree and a mismatch in the /G option can't hurt anybody.
The way you are using the TCHAR types (like PCTSTR in your declaration) is very, very nasty as well. You now critically depend on another global compile option. Project + Properties, General, "Character Set". You changed this one as well from the default so it is very, very easy to overlook that in the client project. This mistake is way nastier than the /Gz option since you do not get a linker error for that one when you use extern "C". The misbehavior you get a runtime is a bit easier to diagnose, you'll only lose an hour or two of your life when you discover that the strings only contain a single character. Stop using TCHAR completely, it stopped being relevant 10 years ago. Your code as written can only work with char*.
The VERSION statement in the DEF file is wrong, it can have only 2 digits that must be separated by a .. That produces a link error on later VS versions. You should omit it completely, it has been outdated for over 20 years, the version number should be set in a VERSION resource. Don't skip adding the resource to your .rc file, very easy to do in VS, versioning is import for DLLs.
Make sure you are building your project as 64-bit or 32-bit and that the library is found in the correct bin folder under x64 or x86.
From personal experience, I was trying to add an a new exported function to a dll I was maintaining and could not get the compiler to link against the new exported function. It turned out I had inadvertently changed the build target to x86 while trying to update the 64-bit version of the dll.
Im trying to call a function long *Test(int) in an empty C dll from a C++ Win32 Console app. The dll is called QSIWrapperUMeLt.dll. When I try building the console application I get the following output:
adding resource. type:MANIFEST, name:1, language:0x0409, flags:0x30, size:406
1>TestPlatform.obj : error LNK2019: unresolved external symbol _Test referenced in function _wmain
1>C:\Users\Deven Bryant\Desktop\temp version\TestPlatform\Debug\TestPlatform.exe : fatal error LNK1120: 1 unresolved externals
1>
1>Build FAILED.
Here's what I've done in order to call my QSIWrapperUMeLt dll from my console app;
in the dll i've included the extern "C" and __declspec(dllexport) attribute in the function definitions. both the dll and the console app are set to build for Win32. I've made sure the dll was set to compile as c code in the project settings. In the console app I've #included the QSIWrapperUMeLt.h file and changed __declspec(dllexport) to __declspec(dllimport) using pre processor directives. I've also linked to the dll with #pragma comment(lib, "DLLTutorial.lib"). The QSIWrapperUMeLt library file, header file, and dll are in my console app's project folder.
Bellow I've provided the dll's header file, the console apps cpp file, and symbol tables and export functions detected by dumpbin when run on QSIWrapperUMeLt.dll.
Also, I've tried the explicit linking route, however when I call load library it returns a null pointer.
Any ideas as to what I'm doing wrong would be much appreciated.
QSIWrapperUMELt.h
#include <Windows.h>
//#pragma comment(lib, "DLLTutorial.lib")
#ifdef EXPORT
#define DECLSPEC __declspec(dllexport)
#else
#define DECLSPEC __declspec(dllimport);
#endif
#define BOOL int
#define TRUE 1
#define FLSE 0
typedef enum
{
initialize = 0,
setup,
capture,
temperature,
close,
getimagedata
} CallStates;
typedef struct ReturnedValues
{
BOOL initialized, imagereadyflag, exposuredone;
long** imagedata;
double Temperature;
}ReturnedValues;
typedef struct CameraData
{
short Mode;
short Binning;
int Exposure;
int Top;
int Left;
int Height; //ROIH
int Width; //ROIW
int CoolerSetpoint;
int Temperature;
}CameraData;
typedef void (*callFunction)(CameraData camdata, ReturnedValues *retvals);
DECLSPEC ReturnedValues *retvals;
callFunction pcallFunction;
HINSTANCE hlib;
CameraData camdata;
#ifdef __cplusplus
extern "C"
{
#endif
DECLSPEC ReturnedValues entryPoint();
DECLSPEC long *Test(int length);
#ifdef __cplusplus
}
#endif
TestPlatform.cpp (console app)
Im only trying to call Test() from this
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
#include <conio.h>
#include <strsafe.h>
#define IMPORT
//#define EXPLICIT
using namespace std;
#ifndef EXPLICIT
#include "QSIWrapperUMeLt.h"
//#include "QSIAccessor.h"
#pragma comment(lib, "QSIWrapperUMeLt.lib")
//#pragma comment(lib, "QSIAccessorUMEmpty.lib")
#endif
#ifdef EXPLICIT
typedef long *(*Test)(int length);
HINSTANCE hinstlib;
Test pTest;
#endif
void ErrorExit(LPTSTR lpszFunction);
int _tmain(int argc, _TCHAR* argv[])
{
int temp = 1;
long *data;
#ifdef EXPLICIT
hinstlib = LoadLibrary((LPCWSTR)"QSIWrapperUMeLt.dll");
if(hinstlib)
{
#endif
int i;
cout << "hinstlib was successfully initialized" << endl;
#ifdef EXPLICIT
pTest = (Test)GetProcAddress(hinstlib, (LPCSTR)"Test");
data = pTest(10000);
#else
data = Test(1000);
//callFunction(CameraData *camdata, ReturnedValues *retvals);
#endif
for(i = 0; i < 20; i++)
{
cout << data[i] << endl;
}
#ifdef EXPLICIT
}
else
{
ErrorExit(TEXT("LoadLibrary"));
}
FreeLibrary(hinstlib);
#endif
getch();
return 0;
}
void ErrorExit(LPTSTR lpszFunction)
{
// Retrieve the system error message for the last-error code
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
// Display the error message and exit the process
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(dw);
}
QSIWrapperUMeLt SYMBOL TABLES
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin>dumpbin /SYMBOLS "C:\Users\...\Desktop\temp version\C_C++libs\QSIWrapperUMeLt\Debug\QSIWrapperUMeLt.li
b"
Microsoft (R) COFF/PE Dumper Version 10.00.30319.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file C:\Users\Deven Bryant\Desktop\temp version\C_C++libs\QSIWrapperUMeLt\Debug\QSIWrapperUMeLt.lib
File Type: LIBRARY
COFF SYMBOL TABLE
000 009C766F ABS notype Static | #comp.id
001 00000000 SECT2 notype External | __IMPORT_DESCRIPTOR_QSIWrapperUMeLt
002 C0000040 SECT2 notype Section | .idata$2
003 00000000 SECT3 notype Static | .idata$6
004 C0000040 UNDEF notype Section | .idata$4
005 C0000040 UNDEF notype Section | .idata$5
006 00000000 UNDEF notype External | __NULL_IMPORT_DESCRIPTOR
007 00000000 UNDEF notype External | ¦QSIWrapperUMeLt_NULL_THUNK_DATA
String Table Size = 0x62 bytes
COFF SYMBOL TABLE
000 009C766F ABS notype Static | #comp.id
001 00000000 SECT2 notype External | __NULL_IMPORT_DESCRIPTOR
String Table Size = 0x1D bytes
COFF SYMBOL TABLE
000 009C766F ABS notype Static | #comp.id
001 00000000 SECT2 notype External | ¦QSIWrapperUMeLt_NULL_THUNK_DATA
String Table Size = 0x25 bytes
Summary
DB .debug$S
14 .idata$2
14 .idata$3
4 .idata$4
4 .idata$5
14 .idata$6
QSIWrapperUMeLt EXPORTS
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin>dumpbin /EXPORTS "C:\Users\...\Desktop\temp version\C_C++libs\QSIWrapperUMeLt\Debug\QSIWrapperUMeLt.dl
l"
Microsoft (R) COFF/PE Dumper Version 10.00.30319.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file C:\Users\Deven Bryant\Desktop\temp version\C_C++libs\QSIWrapperUMeLt\Debug\QSIWrapperUMeLt.dll
File Type: DLL
Section contains the following exports for QSIWrapperUMeLt.dll
00000000 characteristics
500E089F time date stamp Mon Jul 23 19:29:51 2012
0.00 version
1 ordinal base
3 number of functions
3 number of names
ordinal hint RVA name
1 0 00011127 Test = #ILT+290(_Test)
2 1 0001102D entryPoint = #ILT+40(_entryPoint)
3 2 00017580 retvals = _retvals
Summary
1000 .data
1000 .idata
2000 .rdata
1000 .reloc
1000 .rsrc
4000 .text
10000 .textbss
I don't see the reason for your problem when EXPLICIT is not defined. However, here's your problem in the argument to LoadLibrary:
(LPCWSTR)"QSIWrapperUMeLt.dll"
ANSI string "QSIWrapperUMeLt.dll" is an array of bytes, containing a bunch of single byte characters, terminated with a single byte NUL. When used in an expression such as a function argument, the expression is converted to a pointer to the first character, a pointer of type char* or LPSTR.
You cast the pointer to type LPCWSTR. However, the pointer still points to a bunch of single byte characters, terminated with a single byte NUL.
Since you want a Unicode string, you can do this:
L"QSIWrapperUMeLt.dll"
or for compatibility with either ANSI or Unicode when your code might be needed in the future:
_T("QSIWrapperUMeLt.dll")
I have a problem I've been struggeling with for a full week now, and I'm not able to solve it by myself. I've been googeling, and searching in all kind of forums... I have found lots of "this might work", tried it, but no, no success. If anyone have any clue, please, please, help me!
I'v got, from an external source, lots of classes and functions written in VB that I need to be able to use from a C++ application. My first though was: no problem, I turn the VB code into a dll, and load it from my C++-program. This was though harder than I ever could imagine. My C++-program is not written in Visual Studio, but for simplicity I started with trying to load my VB dll (written in Visual Studio 2010) from a Visual Studio C++ application. This is my code so far:
VB-code : DllModule : Class-library project
DllModule.vb
Namespace DllModule
Public Module DllModule
Public Const DLL_PROCESS_DETACH = 0
Public Const DLL_PROCESS_ATTACH = 1
Public Const DLL_THREAD_ATTACH = 2
Public Const DLL_THREAD_DETACH = 3
Public Function DllMain(ByVal hInst As Long, ByVal fdwReason As Long,
ByVal lpvReserved As Long) As Boolean
Select Case fdwReason
Case DLL_PROCESS_DETACH
' No per-process cleanup needed
Case DLL_PROCESS_ATTACH
DllMain = True
Case DLL_THREAD_ATTACH
' No per-thread initialization needed
Case DLL_THREAD_DETACH
' No per-thread cleanup needed
End Select
Return True
End Function
'Simple function
Public Function Add(ByVal first As Integer, ByVal sec As Integer) As Integer
Dim abc As Integer
abc = first + sec
Return abc
End Function
End Module
End Namespace
DllModule.def
NAME DllModule
LIBRARY DllModule
DESCRIPTION "My dll"
EXPORTS DllMain #1
Add #2
C++-code : TryVbDllLoad : Console application
TryVbDllLoad.cpp
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <strsafe.h>
extern "C" {
__declspec(dllimport) int __stdcall Add(int, int);
}
typedef int (__stdcall *ptf_test_func_1_type)(int, int);
int __cdecl _tmain(int argc, _TCHAR* argv[])
{
HINSTANCE hdll = NULL;
hdll = LoadLibrary("DllModule.dll"); // load the dll
if(hdll) {
ptf_test_func_1_type p_func1=(ptf_test_func_1_type)GetProcAddress(hdll,"Add");
if(p_func1) {
int ret_val = (*p_func1)(1, 2);
} else {
DWORD dw = GetLastError();
}
FreeLibrary(hdll); // free the dll
} else {
DWORD dw = GetLastError();
}
return 0;
}
I can load the dll, but GetProcAddess returns NULL with error code 127 (the specified procedure could not be found).
I have tried to load the dll from a VB-application. This works (even without the .def-file). But I'm guessing there is no proper entry point created that the C++ application can use (when I open the dll in Dependency Walker I see no entry point or functions). I've tried compiling the VB-code both with and without "Register for COM interop".
1) What am I doing wrong?
2) If there isn't any nice way to solve this properly, what can I do instead of creating a dll? Is there any other way I can use the VB-classes and functions in my C++ application?
Kind Regards
Sara
Thanks for your answer Mare!
There must be some kind of error in my dll though, cause when I try to register is using regsvr32 I get: "The module C:/tmp/DllModule.dll was loaded, but the start address for DllRegisterServer was not found. Check that C:/tmp/DllModule.dll is a valid DLL- or OCX-file and try again."
Also, when I use
#import "C\tmp\DllModule.dll"
I get
fatal error C1083: Cannot open type library file: 'c:\tmp\dllmodule.dll'
I looked at the link with the tutorial, but there is a small problem: there are no such thing as "ActiveX DLL" to choose among all the project types. And yes, I do have Visual Studio 2010 Professional (a trial version, but still).
-- Sara
Thanks for all the input. I've come across another way to solve my problem, using a multifile assembly rather than my first dll approach.
I followed this HowTo-section: http://msdn.microsoft.com/en-us/library/226t7yxe.aspx#Y749
VB-code : DllModule : Class-library project
DllModule.vb
Imports System.Runtime.InteropServices
Namespace DllModuleNS
Public Class Class1
Public Function ClassAdd(ByRef first As Integer, ByRef sec As Integer) As Integer
Dim abc As Integer
abc = first + sec
Return abc
End Function
End Class
End Namespace
This file I compiled using both visual studio (to produce DllModule.dll-file) and cmd-line:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\Vbc.exe /t:module DllModule.vb
(to produce DllModule.netmodule-file).
C++-code : TryVbDllLoad : Console application
TryVbDllLoad.cpp
#using <mscorlib.dll>
#using ".\..\ClassLibrary1\DllModule.netmodule"
using namespace DllModule::DllModuleNS;
int _tmain(int argc, _TCHAR* argv[])
{
Class1^ me = gcnew Class1();
int a = 1, b = 2;
int xx = me->ClassAdd(a, b);
return 0;
}
In the TryVBDllLoad-project properties I changed:
Common Properties -> Framework and References : added DllModule-project as reference
Configuration Properties -> C/C++ -> General : /clr flag set
Configuration Properties -> Linker -> Input : Add Module To Assembly set to path to DllModule.netmodule (/ASSEMBLYMODULE:"DllModule.netmodule")
This resulted in that I could use the VB-class Class1 in VC++ code!
PROBLEM SOLVED!
I now took it one step further, and changed the TryVBDllLoad-project to a dll:
Configuration Properties -> General : Configurationtype Dynamic Library (.dll)
Configuration Properties -> Linker -> System : SubSystem Windows (/SUBSYSTEM:WINDOWS)
TryVbDllLoadClass.h
#ifndef TryVbDllLoadClass_H
#define TryVbDllLoadClass_H
class TryVbDllLoadClass
{
public:
TryVbDllLoadClass();
int Add(int a, int b);
};
#endif // TryVbDllLoadClass_H
TryVbDllLoadClass.cpp
#include "TryVbDllLoadClass.h"
#using <mscorlib.dll>
#using ".\..\ClassLibrary1\DllModule.netmodule"
using namespace DllModule::DllModuleNS;
TryVbDllLoadClass::TryVbDllLoadClass() {}
int TryVbDllLoadClass::Add(int a, int b)
{
Class1^ me = gcnew Class1();
int xx = me->ClassAdd(a, b);
return xx;
}
DllExport.h
#ifndef DLLEXPORT_H
#define DLLEXPORT_H
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#ifdef __dll__
#define IMPEXP __declspec(dllexport)
#else
#define IMPEXP __declspec(dllimport)
#endif // __dll__
extern "C" {
IMPEXP int __stdcall AddFunction(int);
}
#endif // DLLEXPORT_H
DllMain.h
#define __dll__
#include "dllExport.h"
#include " TryVbDllLoadClass.h"
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
{
return 1;
}
TryVbDllLoadClass * my;
IMPEXP int __stdcall AddFunction(int first, int second)
{
my = new TryVbDllLoadClass();
int res = my->Add(first, second);
delete my;
return res;
}
This dll I could then add to a non-visual-studio project just like a normal dll:
C++-code : LoadDll : Non-Visual-Studio-project (CodeBlocks in this case)
main.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "dllExport.h"
typedef int( * LPFNDLL_CREATE)(int, int);
HINSTANCE hDLL;
LPFNDLL_CREATE func;
using namespace std;
int main()
{
cout << "Hello world!" << endl;
int key = 35;
hDLL = LoadLibrary("TryVbDllLoadClass.dll");
if(hDLL)
{
cout << "Loaded: " << hDLL << endl;
func = (LPFNDLL_CREATE) (GetProcAddress(hDLL, "_AddFunction#4"));
if(func != NULL)
{
cout << "Connected: " << func << endl;
cout << "Function returns: " << func(key, key) << endl;
}
else cout << " ::: fail: " << GetLastError() << endl;
FreeLibrary(hDLL);
cout << "Freed" << endl;
}
else cout << " ::: fail: " << GetLastError() << endl;
printf("-> Goodbye world!\n");
return 0;
}
This way I can use the VB-classes given to me in my existing C++-project created outside Visuabl Studio. Finally...:)
With VB you do not get a "normal" DLL (at least this was the case in former times).
And you do not get Entry Points for functions.
But as i understood you, you have the VB source code and you can do with it whatever
is necessary. Here is a possible solution:
http://www.codeproject.com/Articles/21/Beginner-s-Tutorial-Calling-Visual-Basic-ActiveX-D
but try out first this less complicated way,
because i think a VB dll is always a COM dll, so you can:
register the dll using the Windows command
regsvr32 F:\proj\VBDllModule.dll
now your C++ code :
#import "F:\proj\VBDllModule.dll"
using namespace DllModule;
void CDialogTestDlg::OnButton1()
{
HRESULT hresult;
CLSID clsid;
_CTest *t; // a pointer to the CTest object
_bstr_t bstrA = L"hello";
_bstr_t bstrB = L" world";
_bstr_t bstrR;
::CoInitialize(NULL);
hresult=CLSIDFromProgID(OLESTR("VBTestLib.CTest"), &clsid);
hresult= CoCreateInstance(clsid,NULL,CLSCTX_INPROC_SERVER,
__uuidof(_CTest),(LPVOID*) &t);
if(hresult == S_OK)
{
bstrR = t->vbConcat(bstrA , bstrB);
AfxMessageBox((char*)bstrR);
}
}