I want to load two assemblies from C++/CLI; assembly A depends on assembly B, and both are VB.Net projects (3.5). I want them to load from a byte array, so I use Assembly::Load(), but when I try to instantiate a class from assembly A, the framework ignores the previously loaded assembly B and attempts to load it again, which fails because it is not in the search path. The "Name" of the assembly is the same, so I don't know why it fails. For testing purposes, my program loads the bytes directly from the compiled image, but the real code will be loaded differently. This is my test code:
#include "stdafx.h"
using namespace System;
using namespace System::Windows::Forms;
using namespace System::IO;
using namespace System::Reflection;
[STAThreadAttribute]
int main(array<System::String ^> ^args)
{
array<unsigned char>^ bytes;
FileStream^ f;
f = gcnew FileStream(L"c:\\...\\AssemblyB.dll", FileMode::Open);
bytes = gcnew array<unsigned char>((int)f->Length);
f->Read( bytes, 0, (int) f->Length );
f->Close();
f = nullptr;
Assembly^ assemblyb = Assembly::Load(bytes);
f = gcnew FileStream(L"c:\\...\\AssemblyA.dll", FileMode::Open);
bytes = gcnew array<unsigned char>((int)f->Length);
f->Read( bytes, 0, (int) f->Length );
f->Close();
f = nullptr;
Assembly^ assemblya = Assembly::Load(bytes);
bytes = nullptr;
// Here I get the file not found exception!
Object^ mf = assemblya->CreateInstance(L"AssemblyA.MainForm");
// This line is not reached unless I copy assemblyb.dll to my app's folder:
mf->GetType()->InvokeMember(L"ShowDialog",BindingFlags::Default | BindingFlags::InvokeMethod,
mf->GetType()->DefaultBinder, mf, nullptr );
return 0;
}
The error is:
Could not load file or assembly 'AssemblyB, Version=1.0.3650.39903, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
When I check assemblyb->FullName, it says exactly 'AssemblyB, Version=1.0.3650.39903, Culture=neutral, PublicKeyToken=null'.
Of course, if I copy AssemblyB.dll to my test program's folder the code works just fine, but that's not what I want.
Any ideas?
(By the way, my second step will be attempt to make AssemblyA use classes that my C++/CLI exe will expose.)
OK, I just embarrased myself. It's all in the docs.
// This class is just for holding a managed static variable for assemblyB
ref class Resolver {
public:
static Assembly^ assemblyB;
};
// This is the delegate for resolving assemblies
Assembly^ ResolveHandler(Object^ Sender, ResolveEventArgs^ args)
{
// Warning: this should check the args for the assembly name!
return Resolver::assemblyB;
}
.
.
.
[STAThreadAttribute]
int main(array<System::String ^> ^args)
{
// Set up the handler for the AssemblyResolve event
AppDomain::CurrentDomain->AssemblyResolve += gcnew ResolveEventHandler( ResolveHandler );
.
.
.
// Load assemblyb into the static variable available to the resolver delegate
Resolver::assemblyb = Assembly::Load(bytes);
.
.
.
I hope someone finds this useful. :)
Related
I'm trying to call a native Windows API from managed C++/CLI. One of the arguments is a void**. The idea is that the function will allocate a memory structure and return a void pointer to the caller, which should be passed back to the API on the next call. So I need to allocate storage for a pointer on the managed side and pass a reference to the C API. I can't figure out how to do this.
I've tried declaring a void * in the caller and passing a reference via various operators: &, internal_ptr<>, pin_ptr<>. I did the same with an IntPtr. I get errors saying the compiler can't convert this to a void**.
Here's one attempt using IntPtr and pin_ptr. I get the following compile error on line 28 (the line that declares the pin_ptr):
E0144 a value of type "interior_ptr<System::IntPtr>" cannot be used to initialize an entity of type "cli::pin_ptr<void *>"
#include <msclr\marshal.h>
using namespace msclr::interop;
using namespace System;
namespace CLRStorage
{
public ref class CompoundFile
{
private:
String ^ pathname;
IntPtr pRootStorage;
public:
CompoundFile CompoundFile::Create(String^ path)
{
STGOPTIONS stgOptions;
stgOptions.usVersion = 1;
stgOptions.reserved = 0;
stgOptions.ulSectorSize = 4096;
stgOptions.pwcsTemplateFile = NULL;
auto cf = gcnew CompoundFile();
cf->pathname = path;
marshal_context^ context = gcnew marshal_context();
pin_ptr<void*> ppRootStorage = &cf->pRootStorage;
StgCreateStorageEx(
context->marshal_as<WCHAR*>(path),
STGM_READWRITE & STGM_CREATE,
STGFMT_DOCFILE,
0,
&stgOptions,
NULL,
IID_IStorage,
ppRootStorage);
}
};
}
IntPtr can be converted to and from void*, but it isn't the same type.
Since the parameter is out-only, the simple solution is just to use a temporary:
void* pRootStorage;
StgCreateStorageEx(
context->marshal_as<WCHAR*>(path),
STGM_READWRITE & STGM_CREATE,
STGFMT_DOCFILE,
0,
&stgOptions,
NULL,
IID_IStorage,
&pRootStorage);
cf->pRootStorage = IntPtr(pRootStorage);
This will actually be a tiny bit faster as well, because no pinning is needed.
You also have a separate problem with bad member function syntax. You want
static CompoundFile^ Create(String^ path)
instead of
CompoundFile CompoundFile::Create(String^ path)
and don't forget to
return cf;
Then, marshal_context is not a ref class, so this line is wrong:
marshal_context^ context = gcnew marshal_context();
Instead use
marshal_context context;
and since it is not a pointer,
context.marshal_as<WCHAR*>(path)
I use windows 7 and VS2010. I write a piece of C++/CLI code to do measurements, during which I want to make screenshot.
I have tried screenshot method in C++ (using GDI library), but failed to compile the file.
So I suppose the GDI library could not be used in C++/CLI.
How to do screenshot and paste to a word in C++/CLI project?
Here is the code:
#include "stdafx.h"
using namespace System;
using namespace System::IO;
int main(array<System::String ^> ^args)
{
String^ fileName = "AP_result.doc";
StreamWriter^ sw = gcnew StreamWriter(fileName);
...
sw->Close();
return 0;
}
I have tried Slawomir Orlowski's method but error occurs:
error
C++/CLI code for screen shot:
using namespace System;
using namespace System::Windows;
using namespace System::Windows::Forms;
using namespace System::Drawing;
using namespace System::Drawing::Imaging;
int main(array<System::String ^> ^args)
{
Screen^ r = System::Windows::Forms::Screen::PrimaryScreen;
int width = r->Bounds.Width;
int height = r->Bounds.Height;
Bitmap^ bmp = gcnew Bitmap(width, height, PixelFormat::Format32bppArgb);
Graphics^ screen = Graphics::FromImage(bmp);
screen->CopyFromScreen(0,0,0,0, Size(width, height), CopyPixelOperation::SourceCopy);
bmp->Save("screenshot.bmp");
return 0;
}
In your project you have to add few references: System.Drawing, System.Windows, System.Windows.Forms.
As explained here
https://stackoverflow.com/a/3291411
you need to get context of screen, then put it in bitmap file, it's better explained in answer above, so check answers and comments
I've been working on DirectX lately and I have a Array<byte>^ function to read the shaders in the program. The function works in a program I made like 3 days ago, but then I ported the whole project to work with XAML and everything works basically the same except that this function now shows errors. The function is:
Array<byte>^ LoadShader(std::string File){
Array<byte>^ FileData = nullptr;
std::ifstream VertexFile(File, std::ios::in | std::ios::binary | std::ios::ate);
if(VertexFile.is_open()){
int Length = (int)VertexFile.tellg();
FileData = ref new Array<byte>(Length);
VertexFile.seekg(0, std::ios::beg);
VertexFile.read(reinterpret_cast<char*>(FileData->Data), Length);
VertexFile.close();
};
return FileData;
};
It's placed in a header file and the 3 errors presented are:
error C2143: syntax error : missing ';' before '<
error C2334: unexpected token(s) preceding '{'; skipping apparent function body
error C4430: missing type specifier - int assumed. Note: C++ does not support default-in
And I just don't know what to do... I've checked correct spelling of the header file, the function is of Array<byte>^ type and I'm certain I didn't jump the only body function in the header file.
If I remove the function, the header file works and I'm just baffled and have no idea how to fix this. For reference I'll post the complete header file underneath (it's not that big).
#pragma once
#include "DirectXHelper.h"
#include <fstream>
ref class DirectXBase abstract{
internal:
DirectXBase();
public:
virtual void Initialize(Windows::UI::Core::CoreWindow^ m_window, Windows::UI::Xaml::Controls::SwapChainBackgroundPanel^ m_panel);
virtual void CreateDeviceResources();
void CreateDepthStencil();
void CreatePipeline();
virtual void Render();
protected private:
Array<byte>^ LoadShader(std::string File){
Array<byte>^ FileData = nullptr;
std::ifstream VertexFile(File, std::ios::in | std::ios::binary | std::ios::ate);
if(VertexFile.is_open()){
int Length = (int)VertexFile.tellg();
FileData = ref new Array<byte>(Length);
VertexFile.seekg(0, std::ios::beg);
VertexFile.read(reinterpret_cast<char*>(FileData->Data), Length);
VertexFile.close();
};
return FileData;
};
protected private:
Platform::Agile<Windows::UI::Core::CoreWindow> window;
Windows::UI::Xaml::Controls::SwapChainBackgroundPanel^ panel;
Microsoft::WRL::ComPtr<ID3D11Device1> DXDevice;
Microsoft::WRL::ComPtr<ID3D11DeviceContext1> DXContext;
Microsoft::WRL::ComPtr<IDXGISwapChain1> SwapChain;
Microsoft::WRL::ComPtr<ID3D11RenderTargetView> RTView; //Render Target View
Microsoft::WRL::ComPtr<ID3D11DepthStencilView> DepthView; //3D Depth Stencil View
Microsoft::WRL::ComPtr<ID3D11Texture2D> DepthBuffer;
Microsoft::WRL::ComPtr<ID3D11InputLayout> InLayout;
Microsoft::WRL::ComPtr<ID3D11VertexShader> VShader; //Vertex Shader
Microsoft::WRL::ComPtr<ID3D11PixelShader> PShader; //Pixel Shader
};
Umm. Is it possible that your previous use had a using namespace Platform; somewhere in the file above the use of Array<byte>?
If so that would explain why it's not working.
i have got a ScreenCameraSDK and it comes with a 11kb dll file, it has a documentation too which lists the functions which can be used. It says
ScreenCamera SDK ActiveX Reference Documentation
ActiveX Reference
The ActiveX ID on the system is: ScreenCameraSDK.RemoteControl
Every method on the interface returns FAIL or SUCCESS. (0 or 1).
Create an instance of the ActiveX on your application, and then call InitializeScreenCameraRemoteControl. If the return value is SUCCESS then ScreenCamera is properly installed and you can then call any other method on the ActiveX's interface. If not ScreenCamera could not be found and you should contact support.**
Now my question is, i have the dll and no other files. How can i use the functions inside it in a VC++ Project with Visual Studio 2008.
Thanks
I TRIED THE FOLLOWING CODE BUT GOT COMPILATION ERROR OF UNDEFINED IDENTIFIER
#include <stdio.h>
// This is the path for your DLL.
// Make sure that you specify the exact path.
#import "e:\ScreenCameraSDK.dll" no_namespace
void main()
{
BSTR bstrDesc;
try
{
CoInitialize(NULL);
short st = 2;
short st1;
// Declare the Interface Pointer for your Visual Basic object. Here,
// _Class1Ptr is the Smart pointer wrapper class representing the
// default interface of the Visual Basic object.
_Class1Ptr ptr;
// Create an instance of your Visual Basic object, here
// __uuidof(Class1) gets the CLSID of your Visual Basic object.
ptr.CreateInstance(__uuidof(Class1));
st1 = ptr->MyVBFunction(&st);
}
catch(_com_error &e)
{
bstrDesc = e.Description();
}
CoUninitialize();
}
it says _Class1Ptr is unknown!
BSTR bstrDesc;
try
{
HRESULT hr= CoInitialize(NULL);
CLSID clsid;
hr = CLSIDFromProgID(OLESTR("<complete class name as see in registry>"),&clsid);
short st = 2;
short st1;
//nameOfClassInOCX is placeholder for explanation. If you OCX com class name is blabla
//use _blabla and so on.
_nameOfClassInOCX * ptr;
hr = CoCreateInstance(clsid,NULL,CLSCTX_INPROC_SERVER,__uuidof(_nameOfClassInOCX ),(LPVOID*)&ptr);
cout << ptr->GetFees("hi") <<endl;
ptr->Release();
}
catch(_com_error &e)
{
bstrDesc = e.Description();
}
CoUninitialize();
First of all you have to do this is #import the dll, and the compiler will automatically generate all required definitions from it. Then create objects from the library by using either smart pointers, or CreateInstance().
#import "C:\files\test.dll" no_namespace rename("EOF", "EOFile")
...
int main() {
if (FAILED(::CoInitialize(NULL)))
return 0;
........
::CoUninitialize();
return 0;
}
I'm trying to use WTL within an in-process COM server DLL (an IE BHO), but am struggling with _Module.
My DLL needs CMyModule derived from CAtlDllModuleT<>:
class CMyModule : public CAtlDllModuleT< CMyModule >
{
public:
DECLARE_LIBID(LIBID_MyLib)
DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MYPROJ, "{...}")
};
CMyModule _Module;
extern "C" BOOL WINAPI DllMain(...)
{
hInstance;
return _Module.DllMain(dwReason, lpReserved);
}
...
STDAPI DllUnregisterServer(void)
{
return _Module.DllUnregisterServer();
}
But this conflicts with most WTL examples, which require something like this within stdafx.h:
extern CAppModule _Module; // WTL version of CComModule
No matter which way I do it, I (unsurprisingly) get compile errors. CMyModule derived from CAppModule borks on _Module.DllUnregisterServer(), etc. CMyModule derived from CAtlDllModuleT<> borks on code like _Module.GetMessageLoop().
Any good references on how WTL is supposed to work within a DLL? Google finds lots of questions, with few answers.
I have a project that uses WTL in a DLL. I looked at how my headers are set up and it looks like I hacked around this same problem...
I have my module set up like your sample code inheriting from CAtlDllModuleT<> except the name of the global module variable is _AtlModule rather than _Module. For example:
class CMyModule : public CAtlDllModuleT< CMyModule >
{
public:
DECLARE_LIBID(LIBID_MyLib)
DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MYPROJ, "{...}")
};
CMyModule _AtlModule;
So, all of the DllMain.cpp entry points use _AtlModule. Then in the stdafx.h file it looks like this:
// WTL includes
#define _Module (*_pModule)
#include <atlapp.h>
#include <atlctrls.h>
#include <atldlgs.h>
#undef _Module
That _pModule thing is defined in atlbase.h like:
__declspec(selectany) CComModule* _pModule = NULL;
There must be a better way, but this does work.
Have you considered the option of multiple inheritance? Try inheriting from both CAtlDllModule and CAppModule since you need both.
I use WTL in an Office add-in; the following works for me. (At the bottom of stdafx.h)
class DECLSPEC_UUID("XXXX-...") MyLib;
using namespace ATL;
/*
* Application module
*/
class CAddInModule : public CAtlDllModuleT< CAddInModule >
{
public:
CAddInModule() : m_hInstance(NULL)
{
}
DECLARE_LIBID(__uuidof(MyLib))
HINSTANCE GetResourceInstance()
{
return m_hInstance;
}
void SetResourceInstance(HINSTANCE hInstance)
{
m_hInstance = hInstance;
}
private:
HINSTANCE m_hInstance;
};
extern CAddInModule _AtlModule;
And then the DLL main use _AtlModule:
// DLL Entry Point
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
_AtlModule.SetResourceInstance(hInstance);
return _AtlModule.DllMain(dwReason, lpReserved);
}
// Used to determine whether the DLL can be unloaded by OLE
STDAPI DllCanUnloadNow(void)
{
return _AtlModule.DllCanUnloadNow();
}
// Returns a class factory to create an object of the requested type
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
return _AtlModule.DllGetClassObject(rclsid, riid, ppv);
}
// DllRegisterServer - Adds entries to the system registry
STDAPI DllRegisterServer(void)
{
// registers object, typelib and all interfaces in typelib
HRESULT hr = _AtlModule.DllRegisterServer();
return hr;
}
// DllUnregisterServer - Removes entries from the system registry
STDAPI DllUnregisterServer(void)
{
HRESULT hr = _AtlModule.DllUnregisterServer();
return hr;
}