Linking and communication to AutoCAD in C++ - c++

How can i open the AutoCAD application and send commands to that in C++?
In the VB it's possible by CreateObject and GetObject functions.

In C++, you need to use CoCreateInstance instead of CreateObject and CoGetObject instead of GetObject.
Here is some sample code, adapted from this sample of Microsoft:
// Initialize COM for this thread...
CoInitialize(NULL);
// Get CLSID for our server...
CLSID clsid;
HRESULT hr = CLSIDFromProgID(L"AutoCAD.Application", &clsid);
if(FAILED(hr)) {
return -1;
}
// Start server and get IDispatch...
IDispatch *pAcadApp;
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&pAcadApp);
if(FAILED(hr)) {
return -2;
}

You may want to use C# instead of C++, the .Net syntax is much more friendly than the C++ if you start to deal with COM pointers.
Here is an example. You can always create a .Net CLI library and wrap your C++ code so you can use it from .Net.
void LaunchACAD()
{
try
{
//Connect to a running instance
AcadApp = (AcadApplication)System.Runtime.InteropServices.Marshal.GetActiveObject(
"AutoCAD.Application");
}
catch(Exception ex)
{
// starts last run acad version
System.Type acType = System.Type.GetTypeFromProgID("AutoCAD.Application", true);
// ("AutoCAD.Application.17.1"); // starts 2008
// ("AutoCAD.Application.17.2"); // starts 2009
AcadApp = (AcadApplication)System.Activator.CreateInstance(acType);
}
AcadApp.Visible = true; // by the time this is reached AutoCAD is fully functional
}

Related

Calling COM server, C++ CoCreateInstance throws "No such interface supported", C# works without issue

I'm trying to rewrite some code that calls a local COM Server from C# to C++. The C# code works without issue. The key part is:
Guid lr_FactoryGuid = Guid.Parse("AE7CFA4B-985A-4F76-8CC6-2011649FC8A9");
Guid lr_FactoryClass = Guid.Parse("1CA0D073-4ABB-4D06-B318-BFFDE38E4903");
IntPtr lk_FactoryPtr = new IntPtr();
CoGetClassObject(
ref lr_FactoryClass,
4,
new IntPtr(),
ref lr_FactoryGuid,
out lk_FactoryPtr);
if (lk_FactoryPtr == IntPtr.Zero)
{
MessageBox.Show("lk_FactoryPtr == IntPtr.Zero");
return false;
}
I've tried to rewrite this into C++ and I can't get any further than here, the error is give as "No such interface supported":
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CLSID clsid;
HRESULT hr = CLSIDFromString(L"{1CA0D073-4ABB-4D06-B318-BFFDE38E4903}", &clsid);
CLSID iid;
hr = CLSIDFromString(L"{AE7CFA4B-985A-4F76-8CC6-2011649FC8A9}", &iid);
void* pIFace;
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, iid, &pIFace);
if (!SUCCEEDED(hr))
{
_com_error err(hr);
LPCTSTR errMsg = err.ErrorMessage();
MessageBox(NULL, errMsg, L"SiteKiosk demo", MB_ICONEXCLAMATION | MB_OK);
}
There is a .tlb file that I used to generate the interop DLL for C# and to import into the C++, however it's currently commented out of the C++ in an attempt to keep the code smaller and I still get this error from CoCreateInstance.
The COM application I'm calling is a 32 bit app, so both my C# and C++ clients applications are also 32 bit. Both of the clients are Windows Console applications.
Is there anything else I need to set/do to get the C++ working?
The suggestion by Hans solved the problem, I used CoGetClassObject and the rest of the code then clicked into place.

DirectShow CLR having issue with global variables

I am trying to code up GMFBridge and DirectShow in CLR C++. I am trying to compare its performance against the GMFBridgeLib and the DirectShowLib in the same solution to see which is more efficient.
Right now I am following the GMFBridge source code for setting up C++ capture. One issue I am having is in objects that need to be global so that they can be accessed across the GUI buttons. The GMFBridge code does that as follows:
private:
IGMFBridgeControllerPtr m_pBridge;
that is then used in the setup code as follows:
HRESULT hr = m_pBridge.CreateInstance(__uuidof(GMFBridgeController));
if (FAILED(hr))
{
return hr;
}
// init to video-only, in discard mode (ie when source graph
// is running but not connected, buffers are discarded at the bridge)
hr = m_pBridge->AddStream(true, eMuxInputs, true);
My current problem is that CLR states that any global has to be a pointer of some form, * or ^ depending on managed or unmanaged. It will not just let me add in a global variable such as the GMFBridge source code does. If I create a pointer:
IGMFBridgeControllerPtr* pBridge2;
and try to use that in my GUI code:
(*pBridge2).CreateInstance(__uuidof(GMFBridgeController));
(*pBridge2).AddStream(true, eMuxInputs, true);
It does compile, but when i run it, the code crashes with
An unhandled exception of type 'System.NullReferenceException' occurred in Program.exe.
Addidional information: Object reference not set to an instance of an object.
on the block of code
void _Release() throw()
{
if (m_pInterface != NULL) { <--------------
m_pInterface->Release();
}
}
in comip.h line 823 called from:
HRESULT CreateInstance(const CLSID& rclsid, IUnknown* pOuter = NULL, DWORD dwClsContext = CLSCTX_ALL) throw()
{
HRESULT hr;
_Release();
if (dwClsContext & (CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER)) { <----------
IUnknown* pIUnknown;
hr = CoCreateInstance(rclsid, pOuter, dwClsContext, __uuidof(IUnknown), reinterpret_cast<void**>(&pIUnknown));
if (SUCCEEDED(hr)) {
hr = OleRun(pIUnknown);
if (SUCCEEDED(hr)) {
hr = pIUnknown->QueryInterface(GetIID(), reinterpret_cast<void**>(&m_pInterface));
}
pIUnknown->Release();
}
}
else {
hr = CoCreateInstance(rclsid, pOuter, dwClsContext, GetIID(), reinterpret_cast<void**>(&m_pInterface));
}
if (FAILED(hr)) {
// just in case refcount = 0 and dtor gets called
m_pInterface = NULL;
}
return hr;
}
comip.h line 626 called from this line of code
(*pBridge2).CreateInstance(__uuidof(GMFBridgeController));
the only thing that seems to work is creating a local variable that is not a point object, but then i cant set it to a global, or use it across GUI objects.
if I make it local:
IGMFBridgeControllerPtr pBridge;
pBridge.CreateInstance(__uuidof(GMFBridgeController));
that works.
The problem seems to be that you do not assign anything to the pointer you declare:
IGMFBridgeControllerPtr* pBridge2;
You have to do something like:
pBridge2 = &m_pBridge;
Or just skip the use of pBridge2 completely and use &m_pBridge instead.

Why COM doesn't work in a new thread?

My problems started after converting my VS2003 project to VS2008. Solution contains 3 projects. Projects are DLL's. There were A LOT of compilation errors, then some linker errors... Well, I fought them off. Now it just simply doesn't work ;)
So, one of this DLL's is suppoused to communicate with Word by COM.
Word::_ApplicationPtr d_pApp;
Word::_DocumentPtr d_pDoc;
void MSWord2003::init()
{
free();
HRESULT hr;
CLSID clsid;
CLSIDFromProgID(L"Word.Application", &clsid);
// Get an interface to the running instance, if any..
IUnknown *pUnk;
hr = GetActiveObject(clsid, NULL, (IUnknown**)&pUnk);
if(hr!=S_OK)
throw MSWord::MSWordException("Nie znaleziono działającej aplikacji MSWord.");
IDispatch* d_pDispApp;
hr = pUnk->QueryInterface(IID_IDispatch, (void**)&d_pDispApp);
if(hr!=S_OK)
throw MSWord::MSWordException("Nie udało się połączyć z aplikacją MSWord.");
pUnk->Release();
pUnk = 0;
d_pApp = d_pDispApp;
d_pDoc = d_pApp->ActiveDocument;
d_pDispApp->AddRef();
d_currIdx = -1;
paragraphsCount = d_pDoc->GetParagraphs()->Count;
footnotesCount = d_pDoc->GetFootnotes()->Count;
endnotesCount = d_pDoc->GetEndnotes()->Count;
}
void MSWord2003::free()
{
if(d_pApp!=0)
{
d_pApp->Release();
d_pApp=0;
}
}
This code works on VS2003 (and different machine, I don't have VS2003 on my computer) while in VS2008 it works only if it is called by main thread.
When called by a new thread (wich is initialized by CoInitialize) d_pApp is not initialized properly - its ptr shows 0.
While debugging I reached code in comip.h:
template<typename _InterfacePtr> HRESULT _QueryInterface(_InterfacePtr p) throw()
{
HRESULT hr;
// Can't QI NULL
//
if (p != NULL) {
// Query for this interface
//
Interface* pInterface;
hr = p->QueryInterface(GetIID(), reinterpret_cast<void**>(&pInterface));
// Save the interface without AddRef()ing.
//
Attach(SUCCEEDED(hr)? pInterface: NULL);
}
else {
operator=(static_cast<Interface*>(NULL));
hr = E_NOINTERFACE;
}
return hr;
}
In a new thread, QueryInterface returns E_NOINTERFACE, although GetIID() returns the same thing for both threads. And that is where I got stuck - I have no idea, what causes this behaviour...
IMO you should initialize COM not with CoInitialize, but with CoInitializeEx, specifying COINIT_MULTITHREADED. Otherwise you'll have separate single-threaded COM apartment for every thread.

From out-of-process, what is the proper way to get an IWebBrowser2 interface from an instance of IE?

I am working on a browser automation framework, which automates Internet Explorer, as well as other browsers. I am running into an intermittent problem when attempting to launch IE. The framework launches IE using the IELaunchURL API if it is present, and uses CreateProcess if not. The code to obtain the IWebBrowser2 interface is as follows:
// hwndBrowser is obtained by calling ::EnumWindows() with a function that
// compares the process ID of the window handle to the known process ID of
// the IE instance.
CComPtr<IHTMLDocument2> document;
LRESULT result;
::SendMessageTimeout(hwndBrowser,
WM_HTML_GETOBJECT,
0L,
0L,
SMTO_ABORTIFHUNG,
1000,
(PDWORD_PTR)&result);
// oleacc_instance_handle is obtained from ::LoadLibrary("oleacc.dll")
LPFNOBJECTFROMLRESULT object_pointer = reinterpret_cast<LPFNOBJECTFROMLRESULT>(
::GetProcAddress(oleacc_instance_handle, "ObjectFromLresult"));
if (object_pointer != NULL) {
HRESULT hr = (*object_pointer)(result,
IID_IHTMLDocument2,
0,
reinterpret_cast<void **>(&document));
if (SUCCEEDED(hr)) {
CComPtr<IHTMLWindow2> window;
hr = document->get_parentWindow(&window);
if (SUCCEEDED(hr)) {
// http://support.microsoft.com/kb/257717
CComQIPtr<IServiceProvider> provider(window);
if (provider) {
CComPtr<IServiceProvider> child_provider;
hr = provider->QueryService(SID_STopLevelBrowser,
IID_IServiceProvider,
reinterpret_cast<void **>(&child_provider));
if (SUCCEEDED(hr)) {
IWebBrowser2* browser;
hr = child_provider->QueryService(SID_SWebBrowserApp,
IID_IWebBrowser2,
reinterpret_cast<void **>(&browser));
if (SUCCEEDED(hr)) {
// The IWebBrowser2 pointer is passed back to the caller.
// process_window_info->pBrowser = browser;
}
}
}
}
}
}
Now for the problem: It seems that we can always successfully retrieve the IHTMLDocument2 object. However, when we attempt to call get_parentWindow(), we sometimes receive a "class not registered" result (0x80040154 REGDB_E_CLASSNOTREG), most often when launching a new instance of IE after closing a previous one. We can get the IWebBrowser2 interface by omitting the call to get_parentWindow() and simply calling QueryService directly on the document, but we will receive this error further down the line when we try to manipulate parts of the document. What could cause the get_parentWindow() call to fail?
Note that the entirety of the code can be found in context here.
The problem was in the use of threading in the application. We were not properly waiting for a thread to terminate before launching a new instance of IE.

How to globally mute and unmute sound in Vista and 7, and to get a mute state?

I'm using the old good Mixer API right now, but it does not work as expected on Windows Vista & 7 in the normal, not in XP compatibility mode. It mutes the sound for the current app only, but I need a global (hardware) mute. How to rearch the goal? Is there any way to code this w/o COM interfaces and strange calls, in pure C/C++?
The audio stack was significantly rewritten for Vista. Per-application volume and mute control was indeed one of the new features. Strange calls will be required to use the IAudioEndpointVolume interface.
I recently dealt with this same issue. We have a Windows application that uses the sound system for alarms. We cannot abide the user muting the sound system inadvertently. Here is how I was able to use the interface suggested above to address this issue:
During initialization I added a function to initialize a member of type IAudioEndpointVolume. It was a bit tricky and the help wasn't as helpful as it could be. Here's how to do it:
/****************************************************************************
** Initialize the Audio Endpoint (Only for post XP systems)
****************************************************************************/
void CMuteWatchdog::InitAudioEndPoint(void)
{
HRESULT hr;
IMMDeviceEnumerator * pDevEnum;
IMMDevice * pDev;
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pDevEnum);
m_pIaudEndPt = NULL;
if(hr == S_OK)
{
hr = pDevEnum->GetDefaultAudioEndpoint(eRender, eConsole, &pDev);
if(hr == S_OK)
{
DWORD dwClsCtx;
const IID iidAEV = __uuidof(IAudioEndpointVolume);
dwClsCtx = 0;
hr = pDev->Activate(iidAEV, dwClsCtx, NULL, (void**) &m_pIaudEndPt);
if(hr == S_OK)
{
// Everything is groovy.
}
else
{
m_pIaudEndPt = NULL; // Might mean it's running on XP or something. Don't use.
}
pDev->Release();
}
pDevEnum->Release();
}
}
...
About once per second I added a simple call to the following:
////////////////////////////////////////////////////////////////////////
// Watchdog function for mute.
void CMuteWatchdog::GuardMute(void)
{
if(m_pIaudEndPt)
{
BOOL bMute;
HRESULT hr;
bMute = FALSE;
hr = m_pIaudEndPt->GetMute(&bMute);
if(hr == S_OK)
{
if(bMute)
{
m_pIaudEndPt->SetMute(FALSE, NULL);
}
}
}
}
Finally, when the program exits just remember to release the allocated resource.
////////////////////////////////////////////////////////////////////////
// De-initialize the watchdog
void CMuteWatchdog::OnClose(void)
{
if(m_pIaudEndPt)
{
m_pIaudEndPt->Release();
m_pIaudEndPt = NULL;
}
}