Can someone show me a complete sample of using a simple type library in a C++ application? - com

I have an old application written in C++ using Visual Studio 2008 that uses COM to use a DLL written in C# using Visual Studio 2012 to display Crystal Reports reports. The VS2012 library is much more complicated than it needs to be, and the method used to access it from the C++ application is also overly complicated. So, as an exercise, I want to write a much simpler VS2012 library to display the reports and then use that library from a C++ application.
I am beginning with a trivial library and trying to make sure I can use it. I am not succeeding. Here is the library's code:
namespace ComVisibleSample
{
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("83F56EB3-086E-4F81-9FD4-A89ECB708F3E")]
[ProgId("ComVisibleSample.ComVisibleSample")]
[ComDefaultInterface(typeof(IComVisibleSample))]
public class ComVisibleSample : IComVisibleSample
{
public ComVisibleSample() {}
public int DoSomething()
{
return 42;
}
}
}
I created a C++ console application in VS2012 and added a #import directive to import this library. That compiled successfully. Here is the .tlh file that it generated:
// Created by Microsoft (R) C/C++ Compiler Version 11.00.50727.1 (3903bee7).
//
// c:\rad-con tools\trunk\crystalreportssample\consoleapplication1\debug\comvisiblesample.tlh
//
// C++ source equivalent of Win32 type library C:\Rad-Con Tools\trunk\ComVisibleSample\bin\Debug\ComVisibleSample.tlb
// compiler-generated file created 04/23/19 at 12:48:46 - DO NOT EDIT!
#pragma once
#pragma pack(push, 8)
#include <comdef.h>
namespace ComVisibleSample {
//
// Forward references and typedefs
//
struct __declspec(uuid("adffb523-f1ef-4967-9e96-c3ad943d5d97"))
/* LIBID */ __ComVisibleSample;
struct /* coclass */ ComVisibleSample;
//
// Type library items
//
struct __declspec(uuid("83f56eb3-086e-4f81-9fd4-a89ecb708f3e"))
ComVisibleSample;
// interface _Object
} // namespace ComVisibleSample
#pragma pack(pop)
No .tli file was generated.
Here's the program where I'm trying to use the type library:
#include "stdafx.h"
#import "C:\Rad-Con Tools\trunk\ComVisibleSample\bin\Debug\ComVisibleSample.tlb"
int _tmain(int argc, _TCHAR* argv[])
{
ComVisibleSample::ComVisibleSample sampleObject();
int result = sampleObject.DoSomething();
return 0;
}
If I comment out the call to DoSomething(), the program compiles. But with that line in, the compiler complains that left of .DoSomething() must be a class/struct/union.
What do I have to do to get this program and library to play happily together?

Related

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.

Using Directx members between c++\CLI assemblies C3767

I am trying to call a member function from a C++/CLI assembly from another one, but when I start using DirectX struct I get C3767 error : candidate function not accessib
from Utilities.dll
#pragma once
#include "define.h"
namespace Utilities
{
public ref class Data
{
public:
BOOL CreateBuffer( LPDIRECT3DDEVICE9 dev)
{
...
return TRUE;
}
{
}
And using it from a renderer
#include "Renderer.h"
namespace SomeNamespace
{
SceneRenderer::SceneRenderer(void)
{
}
void SceneRenderer::Render(LPDIRECT3DDEVICE9 dev)
{
...
m_vbo->CreateBuffer(dev); //error C3767: 'Utilities::Data::CreateBuffer': candidate function(s) not accessible
...
}
}
I know that using the address of the device int* (&dev) I can cast back to a LPDIRECT3DDEVICE9, but im looking for a better solution
A managed C++ assembly will not export unmanaged types in its public interface by default. LPDIRECT3DDEVICE9 is an unmanaged type, so your CreateBuffer method will be marked private, regardless of the access specifier provided (kind of stupid that the compiller isn't even generating a warning about this).
Use #pragma make_public or, better yet, do not use unmanaged types in managed interfaces.
Suggestion: Use slimDx or Xna if you want to use DirectX in managed code. These libraries already provide managed wrappers for everything.

C++/CLI wrapper not working (LoaderLock exception)

I've made very simple wrapper for unmanaged C++ library (to be used with C#). Wrapper has one unmanaged class and one managed class. Managed class has private member that is of unmanaged class and uses it like that.
I've done very simple setup - I've created wrapper for only one function to see if everything is working. But as soon as I create an instance of wrapper, my application get's exception "LoaderLock was detected" with following message:
DLL 'c:\path\CPPWrapper.dll' is
attempting managed execution inside OS Loader lock. Do not attempt to
run managed code inside a DllMain or image initialization function
since doing so can cause the application to hang.
If I turn off breaking for "LoaderLock" exception, I get "FileLoadException was unhandled":
Could not load file or assembly 'CPPWrapper.dll' or one of its dependencies. Exception from HRESULT: 0xE0434352
Any idea what I am doing wrong and how can I fix this problem?
CPPWrapper.h
// CPPWrapper.h
#pragma once
#include "Native.h"
using namespace System;
namespace CPPWrapper {
public ref class Class1
{
public:
Class1() : mnt(new Native)
{
}
~Class1(void)
{
// call the finalize method
this->!Class1();
}
// Finalize (for garbage collection)
!Class1(void)
{
// Remove unmanaged class
delete mnt;
mnt = NULL;
}
void Napravi()
{
mnt->CreatePK();
}
private:
Native *mnt;
};
}
I found that the correct way to fix this is by adding #pragma unmanaged in the dllmain.c Don't turn off breaking for "LoaderLock" exception.
See Initialization of Mixed Assemblies and scroll down to the DllMain section for details. Basically what is happening is that the project is compiling the DllMain function as managed (MSIL) but it only runs in unmanaged code. This #pragma unmanaged forces the function to be compiled as an unmanaged function.
So my dllmain.c is now:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#pragma unmanaged
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Another potential cause for LoaderLock is initialization of global static objects. In my case, I was compiling a boost::xpressive regex globally and it didn't like doing that when my C++/CLI wrapper was initializing DllMain. I'm not sure why, because it shouldn't have been managed code, but moving it to a function static object fixed it.

C++ CLI error C3767: candidate function(s) not accessible

I'm new to C++ CLI coming from unmanaged C++ world.
I'm getting this error:
candidate function(s) not accessible
when I pass a std::string as part of the method argument.
Here's the exact code:
Lib Project (compiled as .dll project)
//Lib.h
#pragma once
public ref class Lib
{
public:
Lib(void);
public:
void Extract( std::string& data_ );
};
//Lib.cpp
#include "Lib.h"
Lib::Lib(void)
{
}
void Lib::Extract( std::string& data_ )
{
data_.empty();
}
LibTest Project (compiled as application.exe)
// LibTest.h
#pragma once
ref class LibTest
{
public:
LibTest(void);
};
// LibTest.cpp
#include "LibTest.h"
LibTest::LibTest(void)
{
Lib^ lib = gcnew Lib;
lib->Extract( std::string("test") );
}
int main()
{
return 0;
}
Compiler Error:
1>------ Build started: Project: LibTest, Configuration: Debug Win32 ------
1>Compiling...
1>LibTest.cpp
1>.\LibTest.cpp(7) : error C3767: 'Lib::Extract': candidate function(s) not accessible
The problem is that std::string will compile as a internal (non public) type. This is actually a change in VS 2005+:
http://msdn.microsoft.com/en-us/library/ms177253(VS.80).aspx:
Native types are private by default outside the assembly
Native types now will not be visible outside the assembly by default. For more information on type visibility outside the assembly, see Type Visibility. This change was primarily driven by the needs of developers using other, case-insensitive languages, when referencing metadata authored in Visual C++.
You can confirm this using Ildasm or reflector, you will see that your extract method is compiled as:
public unsafe void Extract(basic_string<char,std::char_traits<char>,std::allocator<char> >* modopt(IsImplicitlyDereferenced) data_)
with basic_string being compiled as:
[StructLayout(LayoutKind.Sequential, Size=0x20), NativeCppClass, MiscellaneousBits(0x40), DebugInfoInPDB, UnsafeValueType]
internal struct basic_string<char,std::char_traits<char>,std::allocator<char> >
Note the internal.
Unfortunately you are then unable to call a such a method from a different assembly.
There is a workaround available in some cases: You can force the native type to be compiled as public using the make_public pragma.
e.g. if you have a method Extract2 such as:
void Extract2( std::exception& data_ );
you can force std::exception to be compiled as public by including this pragma statement beforehand:
#pragma make_public(std::exception)
this method is now callable across assemblies.
Unfortunately make_public does not work for templated types (std::string just being a typedef for basic_string<>)
I don't think there is anything you can do to make it work. I recommend using the managed type System::String^ instead in all your public API. This also ensures that your library is easily callable from other CLR languages such as c#
if you simply must access the internal methods another work around would be making the projects as Friend Assemblies like that:
//Lib Project
#pragma once
//define LibTest as friend assembly which will allow access to internal members
using namespace System;
using namespace System::Runtime::CompilerServices;
[assembly:InternalsVisibleTo("LibTest")];
public ref class Lib
{
public:
Lib(void);
public:
void Extract( std::string& data_ );
};
//LibTest Project
#pragma once
#using <Lib.dll> as_friend
ref class LibTest
{
public:
LibTest(void);
};
In addition to the solutions described above, one can subclass the templated type to obtain a non-templated type, and include its definition in both projects, thus overcoming some of the problems mentioned above.