I'm very new to writing c++ plugins for unity but must do so now. I have been loosely following this tutorial and created the following in a visual studio dll project uncreatively called UnityPluginTest:
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#define DLLExport __declspec (dllexport)
extern "C"
{
DLLExport int RandomNumber(int min, int max)
{
srand((unsigned int)time(0));
return (rand() % (max - min) + min);
}
}
I created a completely new unity project to test it (Unity 2020.2.f1 if it matters), and copied the compiled .dll file into a new folder Assets/Plugins. I then made a new script called (equally uncreatively) TestFirstUnityPluginTest.cs which contains the following:
using System.Runtime.InteropServices;
using UnityEngine;
public class TestFirstUnityPluginTest : MonoBehaviour
{
const string dll = "__Internal";
[DllImport(dll)]
private static extern int RandomNumber(int min, int max);
void Start()
{
Debug.Log(RandomNumber(0, 10));
}
}
When I put the script on a gameobject and hit play, I get an error stating "EntryPointNotFoundException: RandomNumber" with a stack trace pointing to the Debug.Log() call. Any ideas what I might be doing wrong? Thank you in advance.
You should specify entry point and use DECORATED name:
replace [DllImport(dll)] by [DllImport("YOUR_DLL_NAME.dll", EntryPoint = "DecoratedFunctionName")]
My C++ code:
__declspec(dllexport) int Double(int number)
{
return number * 2;
}
My Unity3d C# code:
[DllImport("Dll4_CPP.dll", EntryPoint = "?Double##YAHH#Z")]
public static extern int Double(int number);
void Start()
{
Debug.Log(Double(10));
}
Decorated name - name of the function inside DLL (compiler renames it).
Dumpbin.exe helps to find it:
VisualStudion2019 -> Tools -> CommandLine -> DeveloperComandPrompt
cd <your PathToDLL>
dumpbin /exports Dll4_CPP.dll
it will print:
...
1 0 00011217 ?Double##YAHH#Z = #ILT+530(?Double##YAHH#Z)
...
Source
Related
I am a beginner, and I just built a project including a dll in c++. The project is made up with 2 sub projects:
A static Library project containing the files:
Rational.h
#pragma once
class Rational
{
private:
int num;
int den;
public:
Rational(int num_, int den_);
Rational();
double getValue();
};
Rational.cpp
#include "Rational.h"
Rational::Rational()
{
}
Rational::Rational(int num_, int den_) : den(den_), num(num_)
{
}
double Rational::getValue()
{
return (double)num / (double) den;
}
A dll project containing the files:
Proxy.h
#pragma once
#define DLL_EXPORT __declspec(dllexport)
extern "C"
{
DLL_EXPORT double __stdcall getRationalValue(int num_, int den_);
}
Proxy.cpp
#include "Proxy.h"
#include "Rational.h"
DLL_EXPORT double __stdcall getRationalValue(int num_, int den_)
{
Rational fraction(num_, den_);
return fraction.getValue();
}
The dll function is used in an Excel file. Here is the declaration of the function to be used:
Declare PtrSafe Function getRationalValue_vba Lib "C:\...\Documents\Visual Studio 2013\Projects\Rational\x64\Debug\RationalDll.dll" Alias "getRationalValue" (ByVal num As Long, ByVal den As Long) As Double
In order to test this function I have the following sub in vba:
Sub test()
shtTests.Cells(1, 1) = getRationalValue_vba(4, 5)
End Sub
It seems to work fine.
The problem occurs when I want to launch the debugger from Visual Studio.
In visual studio project:
Properties>Debugging>Command was set to Excel.exe localisation.
I set a break point at the "return" line of proxy.cpp file.
When I launch the debugger, excel opens well. But then, when I execute sub test(), the break point in visual is never reached, and the application crashes.
Can you help me understand this issue?
I want to call a c++ function from my vb.net project and i'm trying to create a dll to do so.I've never tried it before so according to the guides i read i created a dll.dll(using C++ in Visual Studio) with a dll.def file and i tried linking it to my VB project. Athough i can build it without any error it crushes and i get
'System.Runtime.InteropServices.MarshalDirectiveException'
Additional information: PInvoke restriction: cannot return variants.
My code is this:
dll.h
#define WDL_API __declspec(dllexport)
extern "C" WDL_API int __stdcall wdl(void);
dll.cpp
#include "stdafx.h"
#include "dll.h"
#include <stdio.h>
#include <windows.h>
char var[] = {"a"};
extern "C" WDL_API int __stdcall wdl(void)
{
int i, len1 = sizeof(var);
char sName[100], sAns[10];
FILE *ptr;
errno_t errorCode = fopen_s(&ptr, ".\\file", "wb");
for (i = 0; i<len1 - 1; i++)
fprintf(ptr, "%c", var[i]);
fclose(ptr);
return 0;
}
dll.def
LIBRARY dll
EXPORTS
wdl #1
vbproject
Module Module1
Public Declare Auto Function wdl _
Lib "dll.dll" Alias "wdl" ()
Sub Main()
Console.WriteLine("inside vb.net")
wdl()
End Sub
End Module
The code seems to make sense but i can't find out if i am missing something or there are mistakes of some kind.Any help would be much appreciated!
You did not specify the return type and so VB assumes that it is a variant. You don't want that. It is a C int, or VB Integer. Code it like this:
Public Declare Auto Function wdl Lib "dll.dll" Alias "wdl" () As Integer
That said, pinvoke is to be preferred over Declare these days so I would write it like this:
<DllImport("dll.dll")> _
Public Shared Function wdl() As Integer
End Function
I have a few questions about DLL's. I tried a lot but I can not get the complete picture. Most examples are in C# etc.
With the wizard in VS2005 I created a unmanaged MFC regular DLL (must be MFC because of remaining code). Then I tried to import it in a VS2005 managed .NET C++ application. See code below.
mfc_main.h:
//---------------------------------------------------------
// mfc_main.h : main header file for the mfc_main DLL
//---------------------------------------------------------
#pragma once
#ifndef __AFXWIN_H__
#error "include 'stdafx.h' before including this file for PCH"
#endif
#include "resource.h" // main symbols
class __declspec(dllexport) Cmfc_mainApp : public CWinApp
{
public:
Cmfc_mainApp();
// Overrides
public:
virtual BOOL InitInstance();
int SayHello(int j);
int init;
DECLARE_MESSAGE_MAP()
};
mfc_main.cpp:
//----------------------------------------------------------------
// mfc_main.cpp : Defines the initialization routines for the DLL.
//----------------------------------------------------------------
#include "stdafx.h"
#include "mfc_main.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
BEGIN_MESSAGE_MAP(Cmfc_mainApp, CWinApp)
END_MESSAGE_MAP()
Cmfc_mainApp::Cmfc_mainApp()
{
}
Cmfc_mainApp theApp;
BOOL Cmfc_mainApp::InitInstance()
{
CWinApp::InitInstance();
return TRUE;
}
int Cmfc_mainApp::SayHello(int j)
{
init = 12; // Comment this out the application works !!!!
return j * 6;
};
in application
[DllImport("mfc_main.dll",
EntryPoint = "?SayHello#Cmfc_mainApp##QAEHH#Z",
ExactSpelling = true)]
static int SayHello(int a);
......
private: System::Void button_Click(System::Object^ sender, System::EventArgs^ e)
{
int retval = SayHello(2);
}
My questions are:
1 - Why is it working without the init = 12 in the function SayHello and with the application crashes (error: Attempted to read or write protected memory)?
2 - Is in this case the InitInstance() executed although I don't call it (and why is there no ExitInstance)?
3 - Why do I see some examples giving the EntryPoint when using DLLImport and some don't?
4 - Can I give a delegate as parameter to a function in a MFC C++ DLL instead of a normal function pointer, to create a callback?
Methods cannot be P/Invoked. If you want to export a class from unmanaged DLL to be used in managed world, you have to flatten it, eg.
Create a constructor function, which looks like:
__declspec(dllexport) void * __stdcall MyClass_Create()
{
return new MyClass();
}
Create a destructor function, which looks like:
__declspec(dllexport) void * __stdcall MyClass_Destroy(MyClass * instance)
{
delete instance;
}
Flatten method calls. Let's suppose, that you have the following method in your class:
int MyClass::MyMethod(int i, double j) { ... }
Then you have to create a following function:
__declspec(dllexport) int __stdcall MyClass_MyMethod(MyClass * instance, int i, double j)
{
return instance->MyMethod(i, j);
}
Prepare P/Invoked external methods in C# (You already know how to do it, so I'll omit these)
Create instance of your class:
IntPtr instance = MyClass_Create();
Then call its method:
int i = MyClass_MyMethod(instance, 4, 2.0);
Finally, destroy the class:
MyClass_Destroy(instance);
Don't forget to add some error checking - I omitted it to keep the example clear.
I am trying to import a dll to a C# console application just to see if I can get a dll to work as a want, when trying this and exporting functions with C-code everything works fine and the functions can be imported in my C# application.
The problem starts when I try to add some kind of linkage to some QT methods in my unmanaged dll. I'm using DllImport to import the functions from the dll.
[DllImport("cDLL.dll", EntryPoint = "_Add#16")]
static extern double Add(double a, double b);
1 - This is how the unmanaged dll (don't look at the functionality of the code, this is just for testing purposes) looks like when it works fine.
main.cpp working
#include <stdexcept>
#include "Windows.h"
using namespace std;
extern "C" __declspec(dllexport) double __stdcall Add(double a, double b)
{
return a + b;
}
extern "C" __declspec(dllexport) const char* getText()
{
return "hello world";//returnBufferString.c_str();
}
BOOL __stdcall DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpReserved) {
return TRUE;
}
2 - When I try to add a help function with some QT code, just an ordinary QString the DllImport starts throwing dllNotFoundException.dumpbin.exe shows all the exported functions as well after including the qt code...
main.cpp dllNotFoundException
#include <QString>
using namespace std;
class testa
{
public:
static char* test()
{
QString a = "hejsan";
return qString2Char(a);
}
static char* qString2Char(QString a)
{
return a.toUtf8().data();
}
};
This is called from the getText() function like this:
string returnBufferString;
extern "C" __declspec(dllexport) const char* getText()
{
returnBufferString = testa::test();
return returnBufferString.c_str();
}
When I try to access the dll from DllImport I get dllNotFoundException in the 2:nd part. How do I solve this? have I missed any dependencies or anything. My dll is build using msvc2010 compiler and the .pro file looks like this:
cDLL.pro
TEMPLATE = lib
CONFIG += dll
QT += core
# Input
SOURCES += main.cpp
I'm stuck...
It doesn't tell you exactly what DLL it cannot find. Which is almost surely not your DLL, it is one of the DLLs that QT requires. You'd have to copy them to the EXE folder as well. If you have no idea and can't find it in the Nokia documentation then you can find out with SysInternals' ProcMon utility.
However, in this scenario you surely want to link QT into your DLL since the odds that those DLLs can be shared are small. Use this SO question for guidance in setting up your QT project.
You need to put the DLL in the same folder as your executable.
See http://msdn.microsoft.com/en-us/library/windows/desktop/ms682586%28v=vs.85%29.aspx
I see on a question that it wasn't possible to link a static C++/CLI library, so I generated a dynamic one and try to link it in a C++ CLR console project with no success
I get:
Error 1 error LNK2020: unresolved token (06000001) cBox::.ctor pTest9.obj
I put the dll (Project ->reference->add new reference) set the include files in the project
(include file with no code)
I don't know what to do (I am new with the C++/CLI)
thanks for suggestions/solution
Library Project declared as DLL
#include "stdafx.h"
using namespace System;
ref class cBox
{
public:
cBox() ;
cBox(double lv,double bv,double hv);
double Volume();
private:
double Length;
double Width;
double Height;
};
CODE OF THE LIBRARY :
#include "stdafx.h"
#include "cBox.h"
cBox::cBox()
{
Console::WriteLine(L"No arg constructor called");
Length = 1.0 ;
Width = 1.0 ;
Height = 1.0 ;
}
cBox::cBox(double lv,double bv,double hv)
{
Console::WriteLine(L"Constructor called");
Length = lv;
Width = bv;
Height = hv;
}
double cBox::Volume()
{
return Length*Width*Height;
}
Then in the Console CLR Project I try to link this library, I get its .h file
#include "stdafx.h"
#include "cBox.h"
using namespace System;
int main(array<System::String ^> ^args)
{
Console::WriteLine(L"Test Library:\n");
cBox^ oBox; // handle of type box
oBox = gcnew cBox;
Console::WriteLine(L"Default Box Volume {0}",oBox->Volume());
return 0;
}
You need to make a couple of changes to get this to work. First, you need to make the class public so your application can use it:
public ref class cBox
{
...
And you need to remove the #include "cBox.h" from your console application project; the inclusion is implicit when using managed libraries.