I want to call a method of .NET dll (v4.5) from C++ code. This dll uses a third party dll (SKCLNET .NET v1.0)
I build a simple C++ code for that (see below).
The problem is that GetProcAddress(...) returns NULL for some reason.
I'm not sure what is wrong with the code...
I tried to invoke the dll-function via .NET-Console-App directly this worked fine.
#include "pch.h"
#include <iostream>
#include <Windows.h>
using namespace System;
typedef std::string(*GetLicenseStatusFunction)();
int main(array<System::String ^> ^args)
{
HMODULE hDLL = LoadLibrary(L"LicenseCheck.dll");
if (hDLL == NULL) {
std::cout << "Error loading the DLL" << std::endl;
return 1;
}
GetLicenseStatusFunction pGetLicenseStatus = (GetLicenseStatusFunction)GetProcAddress(hDLL, "ValidateLicense.GetLicenseStatus");
if (pGetLicenseStatus == NULL) {
std::cout << "Error getting the function pointer" << std::endl;
return 1;
}
std::string result = pGetLicenseStatus();
std::cout << result << std::endl;
return 0;
}
Here the structure of the used dll:
Here the ValidateLicense class in the .NET dll with the function GetLicenseStatus() I would like to access.
From using namespace System; and the prototype of main,
I assume this is a C++/CLI project (and not a native C++ one).
Since using C++/CLI means it is a .NET project, you can consume other .NET assemblies similarly to the way you would do that in C#.
Just use "add reference" from the project tree and add the assembly you want to consume.
LoadLibrary() and GetProcAddress() are used only to load and call native functions, from native code. As explained above this is not the case here.
Note that in C++/CLI reference types use the handle to object operator (^). So from C++/CLI side the method ValidateLicense.GetLicenseStatus returns a String^.
Also in C++/CLI use :: instead of . for scopes of namespaces and classes:
using namespace ....
//...
String^ sts = ValidateLicense::GetLicenseStatus();
In order to print it you can use:
Console::WriteLine(sts);
You can also convert it to a native std::string:
#include <string>
#include <msclr/marshal_cppstd.h>
//...
std::string sts_cpp = msclr::interop::marshal_as<std::string>(sts);
Related
I have a simple C++ program which calls a C# DLL. It sends and receives a string.
#include "stdafx.h"
#include <string>
#include <msclr\marshal_cppstd.h>
char* callCsDLL(char* arg) {
// arg will be passed to C# DLL method
// and result will contain a string reply
// Copy a new System::String^ from char*
System::String^ clrString = msclr::interop::marshal_as<System::String^>(arg);
// Call C# function
System::String^ t = Namespace::Class::run(clrString);
// Create new std::string from System::String^
std::string cppString = msclr::interop::marshal_as<std::string>(t);
// Copy C++ string to the result
char* result = new char[cppString.length() + 1];
strcpy(result, cppString.c_str());
return result;
}
Inside Visual Studio 2017, this is quite easy to do, use C++ CLR, just add the C# DLL to the references in the C++ project, and build. It's almost magic.
How would I link these together outside of Visual Studio, that is, using the command line? Could I avoid MSBUILD altogether? The resulting executable should simply use the DLL.
I work on a simple console application in C# 4.6.1.
I can make cobol program using Micro Focus environnent.
My goal is to call, from console application, cobol program which have calls to another cobol program.
Today I can call with success cobol methods in cobol dll file, using DllImport or LoadLibrary.
In my example, my cobol method named Hello, in dll file hello.dll, print "Hello :D" and in the same way, my another cobol program named Itsme, in dll file itsme.dll, print "It's me".
Inside Cobol program, we can call another program with his name. In my example, I add the the following line in my Hello program :
CALL "ITSME"
In my console application, I load both dll file (hello.dll and itsme.dll).
When I call my method Hello, it's supposed to print "Hello It's me".
But it won't, because of :
Load error : file 'Itsme'
error code: 173, pc=0, call=1, seg=0 173
Called program file not found in drive/directory
I know it work on Unix system, using dlopen instead of LoadLibrary.
LoadLibrary isn't the equivalent of dlopen ?
EDIT : There is a little sample of code after looking to Simon Sobisch answer :
#include "stdafx.h"
#include "stdio.h"
#include "windows.h"
#include "cobtypes.h"
typedef int(__stdcall *f_cobinit)();
typedef cobrtncode_t(__stdcall *f_cobcall)(const cobchar_t *callname, int argcnt, cobchar_t **argvec);
int main()
{
HINSTANCE dll1 = LoadLibrary("CobolDlls.dll");
HINSTANCE dll2 = LoadLibrary("CobolDlls2.dll");
HINSTANCE dllCbl = LoadLibrary("cblrtsm.dll");
printf("cobinit : ");
f_cobinit init = (f_cobinit)GetProcAddress(dllCbl, "cobinit");
printf("%d", init());
f_cobcall hi = (f_cobcall)GetProcAddress(dllCbl, "cobcall");
cobrtncode_t c = hi((cobchar_t*)"P2", 0, NULL);
getchar();
}
I successfully load librairies, and my call to cobinit is working (result : 0).
But when I call cobcall method, I have the error code 173 seen previously.
I precise that "P2" is refering to a cobol program named "P2" included in "CobolDlls.dll", which is build as a shared single dynmic library with MicroFocus, and all Dlls are in the same directory than the console application.
P2 program :
identification division.
program-id. P2.
data division.
working-storage section.
local-storage section.
procedure division.
display "Hello I'm P2"
goback.
Thanks for your help,
Yoann
Note: This is a guess as I did not tested it, but for most COBOL implementations there is a a runtime library (which should be the target for dlopen / LoadLibrary) which provides functions to use it.
A quick search in the Micro Focus documentation showed C Functions for Calling COBOL, you likely want to use cobinit(), cobcall() and cobtidy().
As everything concerning the COBOL parts is done by the runtime library (which needs to be either linked or loaded itself) you'll need to set options like where to find the modules and other possibly relevant settings before the call to cobinit().
One setting possibly needed:
COBPATH - If the COBOL modules do not reside in the process' current directory - set the same way you'd set PATH variable (entries separated by ";" on Windows)
I'd try something like the following in this case (in C#, the sample was C++)
System.Environment.SetEnvironmentVariable("COBPATH", "X:\path;Y:\other-path");
According to the documentation about Calling Programs combined programs are loaded by loading the library containing them first with a plain CALL. Your adjusted program would then look like:
#include "stdafx.h"
#include "stdio.h"
#include "windows.h"
#include "cobtypes.h"
typedef int(__stdcall *f_cobinit)();
typedef cobrtncode_t(__stdcall *f_cobcall)(const cobchar_t *callname, int argcnt, cobchar_t **argvec);
int main()
{
HINSTANCE dllCbl = NULL;
f_cobinit cobInit = NULL;
f_cobcall cobCall = NULL;
int retCode;
printf("load COBOL runtime : ");
HINSTANCE dllCbl = LoadLibrary("cblrtsm.dll");
if (!dllCbl) {
retCode = (int)GetLastError();
printf("failure: %d\n", retCode);
return retCode;
}
puts("success");
printf("lookup COBOL functions : ");
cobInit = (f_cobinit)GetProcAddress(dllCbl, "cobinit");
cobCall = (f_cobcall)GetProcAddress(dllCbl, "cobcall");
printf("init: %p, call: %p - ", (void *)cobInit, (void *)cobCall);
if (!cobInit || !cobCall) {
retCode = (int)GetLastError();
printf("failure: %d\n", retCode);
return retCode;
}
puts("success");
printf("cobinit : ");
retCode = cobInit();
if (retCode) {
printf("failure: %d\n", retCode);
return retCode;
}
puts("success");
printf("preload CobolDlls : ");
retCode = cobCall((cobchar_t*)"CobolDlls", 0, NULL);
puts("success");
//printf("preload CobolDlls2 : ");
//retCode = cobCall((cobchar_t*)"CobolDlls2", 0, NULL);
//puts("success");
printf("do actual CALL "P2" : ");
retCode = cobCall((cobchar_t*)"P2", 0, NULL);
printf("returned with: %d\n", retCode);
getchar();
}
Note: Be aware that the COBOL runtime may issue an exit (especially when there are errors in the COBOL parts or a module does STOP RUN). You want to make sure to be able to catch it.
I'm developing an app for C#, and I want to use DirectX (mostly Direct2D) for the graphical component of it. So I'm trying use use C++/CLI as an intermediary layer between the native C++ code and the managed code of C#. So far a have 3 projects in my solution: A C# project (which I won't really discuss since it's not giving me any problems yet), a C++ static library that includes Windows.h, and a dynamic C++/CLI library that's intended to marshal information between the other two projects. Here is my code so far:
In the native C++ project, I have a class named RenderWindowImpl that so for only contains 2 methods:
//RenderWindowImpl.h
#pragma once
#include <Windows.h>
class RenderWindowImpl final
{
public:
RenderWindowImpl() = default;
~RenderWindowImpl() = default;
int test();
private:
static void InitializeWin32Class();
};
// RenderWindowImpl.cpp
#include "RenderWindowImpl.h"
int RenderWindowImpl::test()
{
return 5;
}
void RenderWindowImpl::InitializeWin32Class()
{
WNDCLASSEXW wc = { 0 };
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = nullptr;
wc.hInstance = GetModuleHandleW(0);
wc.hCursor = LoadCursorW(nullptr, IDC_ARROW);
//wc.lpszClassName = L"wz.1RenderWindowImpl";
//// TODO: error check
//RegisterClassExW(&wc);
}
And in my C++/CLI project, I have a class named RenderWindow that acts as a wrapper around RenderWindowImpl:
// wzRenderWindow.h
#pragma once
//#pragma managed(push, off)
#include "RenderWindowImpl.h"
//#pragma managed(pop)
using namespace System;
namespace wzRenderWindow {
public ref class RenderWindow sealed
{
public:
RenderWindow();
~RenderWindow();
int test();
private:
RenderWindowImpl* impl;
};
}
// wzRenderWindow.h.
#include "stdafx.h"
#include "wzRenderWindow.h"
wzRenderWindow::RenderWindow::RenderWindow()
{
// Initialize unmanaged resource
impl = new RenderWindowImpl();
try
{
// Any factory logic can go here
}
catch (...)
{
// Catch any exception and avoid memory leak
delete impl;
throw;
}
}
wzRenderWindow::RenderWindow::~RenderWindow()
{
// Delete unmanaged resource
delete impl;
}
int wzRenderWindow::RenderWindow::test()
{
return impl->test();
}
When I compile my project, I get the following warnings and errors:
Error LNK1120 1 unresolved externals wzRenderWindow d:\documents\visual studio 2015\Projects\WizEngCS\Debug\wzRenderWindow.dll 1
Warning LNK4075 ignoring '/EDITANDCONTINUE' due to '/OPT:LBR' specification wzRenderWindow d:\documents\visual studio 2015\Projects\WizEngCS\wzRenderWindow\wzRenderWindowImpl.lib(RenderWindowImpl.obj) 1
Error LNK2019 unresolved external symbol __imp__LoadCursorW#8 referenced in function "private: static void __cdecl RenderWindowImpl::InitializeWin32Class(void)" (?InitializeWin32Class#RenderWindowImpl##CAXXZ) wzRenderWindow d:\documents\visual studio 2015\Projects\WizEngCS\wzRenderWindow\wzRenderWindowImpl.lib(RenderWindowImpl.obj) 1
It seems to be the call to LoadCursorW that C++/CLI doesn't like, as the code compiles fine if I comment out that line. With the Win32 function calls removed, I was able to successfully call RenderWindow::test() from a C# application, outputting the expected result of 5.
I'm a bit of a loss because my understanding of C++/CLI is that it's very good at wrapping native C++ classes for consumption by managed .NET applications. I would really like to understand why my code is not compiling.
As a related follow-up question, am I barking up the wrong tree here? What's the conventional way to access DirectX methods (or similar COM-based C/C++ libraries) from .NET? I'd like to avoid using 3rd-party wrapper libraries like SharpDX.
I fixed the problem by putting #pragma comment(lib, "User32.lib") at the top of my RenderWindowImpl.cpp. Thanks to #andlabs for the fix. I'm not sure why this fixed the problem (I've never needed to explicitly link to user32.lib in any of my previous projects).
I'm trying to call some Windows basic functions from C#, in particular this one.
Since the moment I want to learn the C++/CLI Language too, I've written down this code:
#pragma once
#include <string>
#include <Windows.h>
using namespace System;
namespace InformazioniSchermo {
public class Native_InformazioniDaSistema
{
public:
int m_nAltezzaPannello;
int m_nLarghezzaPannello;
Native_InformazioniDaSistema(void)
{
DISPLAY_DEVICE dd;
DWORD dev = 0;
dd.cb = sizeof(dd);
EnumDisplayDevices(0, dev, &dd, 0);
m_nAltezzaPannello = 100;
m_nLarghezzaPannello = 100;
}
};
public ref class InformazioniDaSistema
{
public:
InformazioniDaSistema();
~InformazioniDaSistema();
public:
int m_nHeight;
int m_nWidth;
};
InformazioniDaSistema::InformazioniDaSistema()
{
Native_InformazioniDaSistema foo;
m_nHeight = foo.m_nAltezzaPannello;
m_nWidth = foo.m_nLarghezzaPannello;
}
InformazioniDaSistema::~InformazioniDaSistema()
{
}
}
but when I compile, I get this error:
Error 3 error LNK2028: at unresolved token (0A0003B4) "extern "C" int __stdcall EnumDisplayDevicesW(wchar_t const *,unsigned long,struct _DISPLAY_DEVICEW *,unsigned long)" (?EnumDisplayDevicesW##$$J216YGHPB_WKPAU_DISPLAY_DEVICEW##K#Z) referencing in function "public: __thiscall InformazioniSchermo::Native_InformazioniDaSistema::Native_InformazioniDaSistema(void)" (??0Native_InformazioniDaSistema#InformazioniSchermo##$$FQAE#XZ) c:\Users\massimiliano\documents\visual studio 2013\Projects\InformazioniSchermo\InformazioniSchermo\InformazioniSchermo.obj InformazioniSchermo
Where am I doing wrong?
You need to link against user32.lib (the library for the EnumDisplayDevices function, as you'll see in the MSDN page you linked to).
You can do this by going to project properties->Linker->Input and adding user32.lib to the "Additional Dependencies" list.
I notice that the default Visual Studio project settings for C++/CLI don't include the common Windows API libraries by default (regular C++ projects have kernel32.lib, user32.lib, shell32.lib and others added to the project's library dependencies in new projects) so you have to add these libraries yourself if you're using them.
error LNK2028: ... (?EnumDisplayDevicesW##$$J216YGHPB_WKPAU_DISPLAY_DEVICEW##K#Z) ...
That is the name the linker is looking for. That is not it's name, it is a C function and does not have the C++ name mangling. Pretty unclear how you did that, especially since you obfuscated your #includes. But the only reasonable guess is that you declared this function yourself instead of using its declaration in the SDK header.
Never do that. Instead use:
#include <Windows.h>
#pragma comment(lib, "user32.lib")
With the #pragma helpful so you can't forget to link to user32
My goal is to only have a single executable file.
Is it possible to merge a managed .exe into an unmanaged .exe?
There is a lot of information about doing things the other way around (merging unmanaged code into a managed project) but I haven't been able to find anything about going the other direction.
Background:
It's for a system requirements / compatibility checker.
I've written the bulk of it using C# in .NET 2.
However, this part will only run if it's determined that the system at least has .NET 2 installed.
That's where "DotNetVersionIdentifier.exe" comes into play.
It's unmanaged code will run regardless of if the .NET Framework is installed. Instead of showing a dialog box of installed .NET versions, I've modified the code to run my C# program if .NET 2.0 or greater is installed, or else report a custom message.
I'd like to pack the C# program inside the C++ executable so that I only need to distribute a single .exe
I've tried using ILMerge, but since my unmanaged code doesn't compile to Intermediate Language, it crashes...
I can add my C# project to the C++ solution, but it still compiles to two separate executables.
This approach: How to Append Data to the End of an .EXE File sounds like a fascinating, yet outdated hack.
This idea seems like it might work: How to embed an exe inside another exe as a resource and then launch it and I'm looking into it now.
I managed to accomplish my goal by using Resources, as outlined here.
Here's how I made it work. (I'm new to C++, so let me know if you see any stupidity)
Compile the managed code as a single file
Rename the managed code executable as .txt
Create a new Visual C++ Win32 Console Application
Add new item - Resource File (.rc)
Open the resource file, Add Resource, choose Import, enter "TEXT" as the type
Modify "DWORD size" to match the size of the managed .txt file
The rest can be explained with a code dump.
Hopefully this will help someone (C++ newbies like myself...)
#include "stdafx.h"
#include "resource.h"
#include "windows.h"
#include "iostream"
#include <string>
#include <sstream>
using namespace std;
namespace std
{
HRSRC hrsrc = NULL;
HGLOBAL hGlbl = NULL;
BYTE *pExeResource = NULL;
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD size = 8192; //hardcoding the size of the exe resource (in bytes)
HINSTANCE g_Handle = NULL;
HINSTANCE hInstance = NULL; // Passing NULL uses the instance that started the process( CSMerged.exe ).
template <typename T>
string NumberToString(T pNumber)
{
ostringstream oOStrStream;
oOStrStream << pNumber;
return oOStrStream.str();
}
}
int _tmain(int argc, _TCHAR* argv[])
{
hrsrc = FindResource(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_TEXT1), _T("TEXT"));
if (hrsrc == NULL)
{
cout << "hrsc is null! \n";
cin.get(); // leave the console open.
return FALSE;
}
hGlbl = LoadResource(hInstance, hrsrc);
if (hGlbl == NULL)
{
cout << "hGlbl is null! \n";
cin.get(); // leave the console open.
return FALSE;
}
pExeResource = (BYTE*)LockResource(hGlbl);
if (pExeResource == NULL)
{
cout << "pExeResource is null! \n";
cin.get(); // leave the console open.
return FALSE;
}
hFile = CreateFile(L"ManagedCode.exe", GENERIC_WRITE | GENERIC_READ, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
DWORD bytesWritten = 0;
WriteFile(hFile, pExeResource, size, &bytesWritten, NULL);
CloseHandle(hFile);
}
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
int ret = CreateProcess(L"ManagedCode.exe", NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
if (ret == 1) { return 0; } // the process started successfully, so I'm done here.
else
{
cout << "CreatePrecess returns " + NumberToString(ret) + ". \n";
cin.get(); // leave the console open
}
return 0;
}
Resource.h
- This was auto generated, along with the modifications to Recource.rc when I imported the resource using VS2013's GUI.
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Resource.rc
//
#define IDR_TEXT1 101
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
I should a add a couple of other things I had to change to make this run on Windows XP:
Ensure that the Platform target is Win32
Set Configuration Properties>General>Platform Toolset to "Visual Studio 2013 - Windows XP (x120_xp)"
Set Configuration Properties>C/C++>Code Generation>Runtime Library to "Multi-threaded (/MT)