Trying to use C# DLL with ComVisible attribute set from unmanaged C++ - com

TL;DR: I'm trying to use a C# library in C++. Why am I getting an undeclared identifier error when trying to use an identifier from my .tlh file? There must be tons of examples out there, but I haven't been able to find any that include both the C# and C++ code, and that work. Links to such examples would be greatly appreciated.
I have the following classes defined in C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using CrystalDecisions.CrystalReports.Engine;
namespace CapsCrystalReportLib
{
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
[Guid("B4E5F784-12E6-4311-9BB9-D5B3252F20A3")]
public interface ICapsCrystalReport
{
[DispId(1)]
void DisplayReport(string fileName);
[DispId(2)]
void PrintReport(string fileName);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("89402DE5-BA26-4AC0-AB40-00ADD2876FF4")]
[ProgId("CAPSCrystalReport.Report")]
[ComDefaultInterface(typeof(ICapsCrystalReport))]
public class CapsCrystalReport : ICapsCrystalReport
{
public void DisplayReport(string fileName)
{
MessageBox.Show("Displaying report " + fileName);
}
public void PrintReport(string fileName)
{
MessageBox.Show("Printing report " + fileName);
}
}
}
I have the following C++ program attempting to use this class:
#include "stdafx.h"
#import "W:\\CAPS Builds\\trunk\\CapsCrystalReportLib\\bin\\Debug\\CapsCrystalReportLib.tlb" no_namespace
int _tmain(int argc, _TCHAR* argv[])
{
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
// Create the interface pointer.
CapsCrystalReport CRPtr(__uuidof(CapsCrystalReport));
long lResult = 0;
// Call the Add method.
CRPtr->DisplayReport("SomeReport.rpt");
// Uninitialize COM.
CoUninitialize();
return 0;
}
I am getting an undeclared identifier error. The compiler doesn't know what a CapsCrystalReport is. What am I doing wrong?
P.S. I took another look at the sample I copied this from. One of the comments asks the same question, and it was never answered.

You were very close, but CRPtr is a COM interface reference (=pointer) so it must be declared like this:
ICapsCrystalReportPtr CRPtr(__uuidof(CapsCrystalReport));
The IxxxPtr class was generated for you by #import in a .tlh file. What you can do when you have issues with #import, is just open the generated .tlh file and look at it.
Note you don't have to declare a default interface in C#, you can just declare the class like this:
[ComVisible(true)]
[Guid("89402DE5-BA26-4AC0-AB40-00ADD2876FF4")]
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("CAPSCrystalReport.Report")]
public class CapsCrystalReport
{
... same ...
}
And in C++, you would have to adapt your imports like this:
#import "C:\WINDOWS\Microsoft.NET\Framework64\v4.0.30319\mscorlib.tlb" auto_rename
#import "W:\\CAPS Builds\\trunk\\CapsCrystalReportLib\\bin\\Debug\\CapsCrystalReportLib.tlb" no_namespace
and you would use it like that (the interface was implicitely created by .NET and wrapped by the #import):
_CapsCrystalReportPtr CRPtr(__uuidof(CapsCrystalReport));
PS: I would recommend you to keep the namespace, avoid no_namespace because it can cause problems with collisions especially in C++.

Related

What is the C++ code to switch from a Main XAML form to a second XAML form

I am using visual studio 2015.
I am creating an app that has several forms, but I am stuck because I don't know the code to switch pages.
The MainPage.cpp file code is =
//
// MainPage.xaml.cpp
// Implementation of the MainPage class.
//
#include "pch.h"
#include "MainPage.xaml.h"
#include "IncomeForm.xaml.h"
using namespace pman_project2;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
MainPage::MainPage()
{
InitializeComponent();
}
void pman_project2::MainPage::Income_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
??????????????????????????????????????
}
The row of question marks is where i am stuck. I have looked online and it tells me how to do it in c# but not c++.
apparently the code in c# is
this.Frame.navigate(typeof(page.IncomeForm));
can anyone help?
Also use this method, but in C++/CX way:
Tip If you are programming using a .NET language (C# or Microsoft Visual Basic), the TypeName type projects as System.Type. When programming using C#, it is common to use the typeof operator to get references to the System.Type of a type. In Visual Basic, use GetType. If you're using C++/CX, where you'll need to create a TypeName helper struct, you can use the typeid component extension.
void pman_project2::MainPage::Income_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(IncomeForm::typeid));
}
For advanced usage, please see this sample
Share my sample, please check: Link

Unity importing DLL causes parser error?

I have a DLL which I am trying to import into my Unity project. I am following this tutorial on their website example. However, this line of code causes a parser error saying that "An extern alias declaration must precede all other elements."
private static extern float FooPluginFunction ();
I have tried searching online for the solution to this error but I do not get any results that are related to Unity. I have the DLL in my assets/plugins folder. Does anyone know what is causing this error and how I would fix it?
Edit: Script containing DLL import.
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
public class VR : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
[DllImport ("myplugin")]
private static extern TextFunc ();
myplugin.dll is in my Unity assets/plugins folder.
This error says that you have to put your dll import above that line where you use TextFunc() and inside a class.
Edit: Check this.

EntryPointNotFoundException occurred while calling C++ function from C#

I wish to call C++ function (here Score()) which is present in Score_Update1.dll.
Both C# & C++ files get compiled successfully. I have also put above dll into the Debug/bin of C# project. But when I run C# code it gives EntryPointNotFoundException.
What could be the reason behind this Exception?
I tried dependency walker for Score_Update1.dll. But it doesn't show any Entry Point
I wish to use PInvoke technique for calling C++ function from C#
// Score_Update1.h
#pragma once
#include <iostream>
using namespace std;
using namespace System;
extern "C"{
#define MYAPI __declspec(dllexport)
namespace Score_Update1 {
public class MYAPI UpdateScore
{
// TODO: Add your methods for this class here.
public:
void Score();
};
}
}
// This is the main Score_Updat1.dll DLL file.
#include "stdafx.h"
#include "Score_Update1.h"
using namespace Score_Update1;
void UpdateScore::Score()
{
cout<<"Score has been updated";
}
C# code is as follows:
using Score_Update1;
using System.Runtime.InteropServices;
namespace GameTesting
{
class Game
{
[DllImport("Score_Update1.dll")]
internal extern static void Score();
static void Main(string[] args)
{
try
{
Game.Score();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
}
The reason for EntryPointNotFoundException is that the DLL does not contain an entry point named Score. If you look at the exported names using dumpbin or some similar tool you will see mangled names.
However, using the mangled name isn't going to help you here. You've exported a class and the function you want to call is a member function. You cannot directly instantiate a C++ class from pinvoke. And you cannot call member functions. If you wish to use pinvoke you would need to flatten the class to a C style interface. Another route would be to compile the C++ code to a mixed mode C++/CLI assembly and consume that.

Use library in windows form application

I need to use WinSparkle library in my Windows Form Application. I have include library header - <winsparkle.h> and have placed DLL import code. I suppose Dll import code is C# style. How to convert it to C++ .Net style?
// AutoUpdate.cpp : main project file.
#include "stdafx.h"
#include "Form1.h"
#include <winsparkle.h>
using System;
using System::Runtime::InteropServices;
using namespace AutoUpdate;
namespace AutoUpdate // YOUR NAMESPACE CAN GO HERE
{
**// C# lines**
class WinSparkle
{
// Note that some of these functions are not implemented by WinSparkle YET.
[DllImport("WinSparkle.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern void win_sparkle_init();
[DllImport("WinSparkle.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern void win_sparkle_cleanup();
[DllImport("WinSparkle.dll", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)]
public static extern void win_sparkle_set_appcast_url(String url);
[DllImport("WinSparkle.dll", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.Cdecl)]
public static extern void win_sparkle_set_app_details(String company_name,
String app_name,
String app_version);
[DllImport("WinSparkle.dll", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.Cdecl)]
public static extern void win_sparkle_set_registry_path(String path);
[DllImport("WinSparkle.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern void win_sparkle_check_update_with_ui();
}
}
[STAThreadAttribute]
int main(array<System::String ^> ^args)
{
// Enabling Windows XP visual effects before any controls are created
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
// Create the main window and run it
Application::Run(gcnew Form1());
return 0;
}
You seem to be getting a little confused here.
You certainly could do this with a mixed mode C++/CLI assembly. You'd include the header file and pass the lib file to the linker. Then you could call the functions directly since they are declared in the header file. You'd need to call the functions from the C++/CLI assembly, or expose whatever is needed through a managed ref class for consumption by the C# code.
However, the document you link to does not suggest this route. It suggests no C++/CLI at all and a pure C# p/invoke solution. That would appear to be the simplest approach.
I recommend that you remove the C++/CLI layer and instead use pure C# p/invoke. Follow precisely the instructions to which you linked. Start from the section titled Managed code / .NET / C# applications.

VS C++ Issue with InvokeRequired / delegate void

I am new to C++ and Im using VS2010.
Could someone check the code below and help to solve it? Everytime the function UpdateDataGrid(unsigned char CANPacket[15])
is called the following message is shown in a new window and the application closes.
An unhandled exception of type 'System.ArgumentException' occurred in System.Windows.Forms.dll
Additional information: Object of type 'System.Byte' cannot be converted to type 'System.Byte*'.
I have to use unsinged char data type and not String^ in this project.
Is there any way to correct my code?
//Piece of my code
namespace VCCDC {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
using namespace System::Threading;
public ref class Form1 : public System::Windows::Forms::Form
{
delegate void UpdateDataGridCallback(unsigned char CanPacket[15]);
private: void UpdateDataGrid(unsigned char CANPacket[15]) {
if (this->dataGridView1->InvokeRequired) {
UpdateDataGridCallback^ d = gcnew UpdateDataGridCallback(this,&VCCDC::Form1::UpdateDataGrid);
this->Invoke(d,gcnew unsigned char(CANPacket[15]));
}
else {
//Update dataGridView1 with new data
}
}
}
}]
Change the line
this->Invoke(d,gcnew unsigned char(CANPacket[15]));
to
this->Invoke(d,CANPacket));
You already have an unsigned char pointer, pass it through. With gcnew you intent to create another one, which is unnecessary.
Also error caused by this gcnew line. You have to construct the Byte* with a Byte parameter. Yours is Byte* too.