Adobe Reader ActiveX + QAxWidget; Getting current page - com

I'm trying to embed Abode Reader into my win app, the target itself is simple viewing of PDF documents from th app, no free rendering libraries (GPL won't do) were found, so that's my solution. QAxWidget solved most of the problem, but I also need to know some minor info, like number of the current page.
Trying sort of:
ui->axWidget-> dynamicCall("LoadFile(const QString &)", "C:/sample.pdf"); // works fine
Seems there's a nice reference: Interapplication Communication API Reference - Adobe
All the methods from AxAcroPDFLib.AxAcroPDF object are called fine and work OK. But those are setters, for getters AcroExch.AVPageView is required, though neither Qt Creator, nor manual search, not even ui->axWidget->querySubObject("something") offer me (other?) suitable ActiveX object for it. GetActiveDoc() can be called from AcroExch.App object, wich I also have nowhere to get from.
The question is: how is it possible to obtain the current page number via AciveX if possible at all?

Damn, I was a bit dumb, found an answer after a while.
All the "getters", like methods obtaining numder of current page, total number of pages, current zoom, etc are supported by the other ActiveX obj, AcroExch.AVPageView (wrapped inside AcroExch.App it is) to be exact.
Detailed info is in the Adobe reference, here: IACReference
But the key point is that Adobe Reader itself provides only AxAcroPDFLib.AxAcroPDF object with "setters" and nothing more, even through DDE messages. Seems that full functionality is only available in Adobe Acrobat, which is not free and is licensed/purchased in a usual commercial way.
Thus, as Adobe has it, you can either use limited Reader OLE methods or purchace Acrobat.

Related

Runtime error 429 in VBA, but class is registered

I'm trying to recreate a program that uses javascript to open a connection to a PLC and then display all sorts of information on a web page. I'd rather have it in a form in MS Access for various reasons, and have spent forever trying to find the right dll to use (Jet32X.dll, if anyone is curious). I finally tracked the CLSID called out in the javascript back to a registered class for the PLC, and I'm trying to create that object in VB code. It won't get any further than the Dim As New line, however, throwing runtime error 429: "Active X Component Cannot Create Object." Really wish I had some more information about why.
I know the class is registered, since that's how I found it in the first place. I know the DLL file isn't corrupted, since the program runs fine from the JS version. I have a sneaky suspicion that there's some sort of incompatibility going on here, since the PLC and supporting software is pretty old, and I'm working in Microsoft Access 2013 (and its associated VBA). I can't really think of a good way to verify this, however. Does anyone have any other ideas? Could anything else be causing this problem?
Figured it out; in case anyone else runs into this sort of issue:
32bit COM dlls will not run in 64bit applications. If you don't want to go back and reinstall 32bit versions of whichever application you're using, one of the easiest workarounds is using dllhost.exe as a surrogate.
You can read a little about it here, but I found this tutorial easier to follow.
I send a new reply just to recap the information and avoid anyone that stumbles in the same problem again after me wasting precious time. All the steps involved assume that you already correctly registered the dll you are trying to use.
How to make a 32bit COM Dll work in a 64bit application
The "easy" solutions involve using the Dll Surrogate method, which runs dllhost.exe and as an intermediary process in 64bit to expose the underlying 32bit dll. When done correctly this works seamlessly without any special measure needing to be taken in neither in the 32bit dll nor in the 64bit application.
There are two main approaches to this:
Using Oleview.exe (i.e. using a GUI)
Oleview can be obtained downloading the Window 10 SDK.
In order to use Oleview it you have to:
Download the Window 10 SDK at the following link:
https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/
Go to C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x86 to find the 32bit version of oleview.exe
ONLY THE FIRST TIME: run it as administrator to avoid see the message related to the impossibility to load iviewer.dll
In the left pane, Object Classes -> All Objects and find your dll name.
WARNING: you may find many entries for your dll. In particular each class has got its own entry such as YourProjectName.YourClassName
In the right pane, go to Implementation -> Inproc Server, and tick Use Surrogate Process. Leave the Path to Custom Surrogate empty to use the system default surrogate, that is dllhost.exe.
You can check the procedure went correctly by returning to the Registry tab, always in the right pane of the Oleviewer and make sure that now under CLSID = {yourAppIdHere} [DllSurrogate] = is listed among the entries.
Edit manually the Windows Registry
The Oleview method is recommended, but the manual method may be ok if you need to do this only once. The tutorial that NickGlowsinDark mentions was moved to https://techtalk.gfi.com/32bit-object-64bit-environment/ .
In order to avoid problems in the future with the page going offline I copy and paste here the most important steps involved. All credit goes to Emmanuel Carabott that is the original author of the tutorial, I added the first two steps in order to facilitate you in the process.
Open the Registry Editor (Windows+R -> regedit), and follow the following steps:
You first need to find your dll GUIDs. You will probably have many GUIDs, one for each of the classes that your dll exports. I find it's easier to find the GUIDs if you go to HKEY_CLASSES_ROOT\YourProjectName.YouClassName. It is the (Default) String Value you find under the Clsid key.
I recommend you find all the GUIDs first and make a note of them in order to have an easier time with the steps after this one.
Then, as Emmanuel Carabott kindly explains in his article, you have to do the following for each of the GUIDs you found:
Locate your COM object GUID under the HKey_Classes_Root\Wow6432Node\CLSID\[GUID]
Once located add a new REG_SZ (string) Value. Name should be AppID and data should be the same COM object GUID you have just searched for.
Add a new key under HKey_Classes_Root\Wow6432Node\AppID\
The new key should be called the same as the com object GUID
Under the new key you just added, add a new REG_SZ (string) Value, and call it DllSurrogate. Leave the value empty.
Create a new Key under HKey_Local_Machine\Software\Classes\AppID\
Again the new key should be called the same as the COM object’s GUID. No values are necessary to be added under this key.
That’s it, your COM Object should now be accessible from a 64bit environment and can be used like a regular COM Object.

Loading (and executing) a lisp-file in autocad using .NET

I'm currently in the process of rewriting some old AutoCAD plugins from VBA to VB.NET. As it turns out, a (rather large) part of said plugin is implemented in LISP, and I've been told to leave that be. So the problem became running LISP-code in AutoCAD from .NET. Now, there are a few resources online who explain the process necessary to do so (like this one), but all of them takes for granted that the lisp-files/functions are already loaded. The VBA-function I'm currently scratching my head trying to figure out how to convert does a "(LOAD ""<file>"")", and the script is built in such a way that it auto-executes on load (it's a simple script, doesn't register functions, just runs from start to end and does it's thing).
So my question is. How can I load (and thus execute) a lisp-file in autocad from a .NET plugin?
Ok, there are two ways to sendcommand via .NET.
The first thing you need to understand is that ThisDocument doesn't exist in .NET.
ThisDocument is the document where the VBA code is written, but since your addin is document undependant, it stands alone and you must take the documents from the Application object.
You access the application with:
Autodesk.AutoCAD.ApplicationServices.Application
If you want to transform it to the same Application object as in VBA, with same methods and functions
using Autodesk.Autocad.Interop;
using Autodesk.Autocad.Interop.Common;
AcadApplication App = (AcadApplication)Autodesk.AutoCAD.ApplicationServices.Application.AcadApplication;
The first application has MdiActiveDocument, from where you can call the Editor and send written commands, or call the SendStringToExecute as said in other answer.
The AcadApplication has ActiveDocument (an AcadDocument object that behaves exactly as in VBA).
This document will have the same SendCommand your VBA has, use it the same way it's done in VBA.
If you can explain better the autoexecute part, I can help with that too.

How to handle LaTeX/PDF doc reviews?

I am a Ph.D student, and I usually write articles which are later proof-read by my supervisor. I usually do it in LaTeX and reviews are done to the PDF outputs in Adobe Reader itself. There are mostly grammatical ones and mostly I miss prepositions and conjuctions in fast writing. To re-phrase everything I have to manually enter everything in my LaTeX script again.
This seems to be hell lot of work and this goes on multiple times sometimes. Is there any software in current world that makes the task easier? For example, if a text stuck out for grammar errors and suggested alternatives, can I accept the changes to replace old one with new phrase or sentence and also able to blank out the striked text. Please suggest me a tool which really makes my life easier.
You may want to take a look at the following link. It has some good information about version controlling.
http://en.wikibooks.org/wiki/LaTeX/Collaborative_Writing_of_LaTeX_Documents
You could attach the LaTeX sources to the PDF (with the attachfile2 package), so reviewers can directly edit the source and send that back. Or you try to accept comments to the PDF, but currently only Adobe Reader and Foxit allow that - and not on Linux.

Change dwm colorization - Windows 7

I'm currently trying to write a program in VB.NET which fluidly changes the DWM window colorization colors in Windows 7.
I first tried to edit Registry values directly, but I had to restart the UXSMS service. This solution was unsatisfying, because of the toggle of the taskbar.
I'm now searching for a function in a DLL such as user32.dll or themecpl.dll which can reproduce the behaviour of control panel when setting the window color.
I'm now on IDA, searching for the adquate function (CColorCplPage::SetDwmColorizationColor seems good!). If anyone has one, please share it!
(If anyone need screens or code, please ask. Sorry for my poor English.)
Your first attempt failed because manually editing the Registry is never the correct way to change system settings. As you found out, lots of Windows components (and other applications!) read those configuration values once and cache them, preventing your changes from being propagated. Another problem (and you'd be surprised how often I see this) is applications that attempt to muck around in the Registry generally end up corrupting things.
Instead, you should call the documented API to change the settings. There's almost always a documented way of doing this, and if there isn't, well then you shouldn't be doing it.
This appears to be one of those cases. There's a documented DwmGetColorizationColor function, but there's no corresponding DwmSetColorizationColor function, as one might expect.
The reason is that the user is supposed to be the only one who can change their colorization settings, not other applications. You might promise not to abuse this, and to only make such changes at the user's explicit request, but not all applications can be trusted to do this. Lots of people would use it maliciously, so these functions have not been documented and exposed.
But as usual, if you press on, you can usually find an undocumented way of doing things. The problem with using undocumented functions is that there's no guarantee they'll work or continue to work. They've been intentionally left undocumented because they're liable to change on new versions of Windows. You should only use them at your own risk.
In this case, if you use a program like DumpBin to obtain a list of all the exported functions from the DWM DLL (dwmapi.dll), you'll see a number of undocumented exported functions.
The ones you're interested in are DwmGetColorizationParameters and DwmSetColorizationParameters. Both of these functions take a COLORIZATIONPARAMS structure as an argument that contains the values they need.
So, you need to reverse engineer these functions and obtain the appropriate definitions. Then, you can call the DwmGetColorizationParameters function, passing in a COLORIZATIONPARAMS structure to obtain the current configuration settings; modify the member of the structure that contains the current colorization color; and then pass that modified version of the structure to the DwmSetColorizationParameters function.
Did I mention that I don't recommend doing this?

How to access the variable to which OS copies

An interesting feature, I have seen in jDownloader software is any links I copy in the browser window (i.e., Ctrl+c), the copied content links automatically appears ( i.e., with out me actually pasting it) in their UI and starts downloading the content from the links, if they are valid.
I would like to program the same but am puzzled as how to access the variable to which OS copies. Please share your ideas.
Thanks.
Adam Robinson's answer is on the right track, but is not entirely correct. I'm going to try and provide the "long" version (in contrast to his "short" version), and explain along the way where/why I think the solution that he proposes falls short of achieving your ultimate goal.
As the documentation he links to explains, there are three different ways of monitoring changes to the Windows clipboard, each with their own caveats:
Creating a clipboard viewer window that hooks into the clipboard viewer chain and receives notification messages when the contents of the clipboard have been changed by the user. (Available on all versions of Windows, but generally more difficult to code and thus discouraged for newer applications that don't have a specific need for its features.)
Querying the clipboard sequence number, which is a 32-bit value that changes each time the clipboard's contents are changed. Your program calls the Windows API function GetClipboardSequenceNumber once and caches its value, then each time you want to check if the clipboard's contents have changed, you call that same function again and compare its return value to the value you've cached. There are two important caveats here:
This function is only available in Windows 2000 and newer. This is not likely to be a problem if you're writing .NET apps, as versions of the Framework as early as 3.0 dropped W2K support.
This is not a notification method, and you should not call this function repeatedly in a polling loop. That means that you have to manually call the appropriate function and compare the clipboard sequence number. You cannot use this method if you want to "listen in" and be immediately notified whenever the clipboard's contents change, as you describe in your question. The documentation is very explicit here:
This method is more suitable to programs which cache results based on the current clipboard contents and need to know whether the calculations are still valid before using the results from that cache. Note that this is a not a notification method and should not be used in a polling loop. To be notified when clipboard contents change, use a clipboard format listener or a clipboard viewer.
Creating a clipboard format listener, which registers to be notified whenever the clipboard's contents change. This is the ideal solution in your case, because it avoids the complexities of creating a clipboard viewer window (option 1), but also allows you listen in and be notified each time the clipboard's contents are changed (in contrast to option 2).
The problem is that this is only available under Windows Vista and later. If you still have any need to target Windows XP (as most of us do), this is really not an option for you.
Therefore, from the example you provide in the question, it sounds to me like the only option available to you is option 1, creating a clipboard viewer window. The documentation goes into the gory details of how you'd set this up using the SetClipboardViewer function and listening for the WM_DRAWCLIPBOARD and WM_CHANGECBCHAIN messages. Getting this to work right can be a difficult task to do on your own, but fortunately for us .NET developers, others have already done the hard work for us. ("Others", I say, despite having been one of those others myself.)
This article on CodeProject is a good example. It implements three different types of hooks: a mouse hook, a keyboard hook, and a clipboard hook. The only thing you're interested in is the clipboard hook, but you can just add a reference to the DLL in your project to start using its functionality immediately.
If you are interested in the internals of how this works and want to try coding it up yourself, this article appears to be a fantastic description of the specific steps involved.
Use the My.Computer.Clipboard.GetText() function
Also see the msdn page
Check out this MSDN link regarding the clipboard. In particular, this link's anchor should take you to the section of the document about monitoring the clipboard contents.
The short version is that you can monitor either by polling for the sequence number and checking to see if it has changed, or you can register to listen for changes for specific clipboard contents formats. Note that the latter is only available on Vista and above, so you depending on your target platform you may have to stick with polling.
In order to use these functions, you'll have to declare a P/Invoke to the unmanaged function. Here's the PInvoke.net page on the GetClipboardSequenceNumber function, though the declaration here is C#, not VB.NET. The VB.NET syntax should be (I don't have VS in front of me to check):
<DllImport("user32.dll")>
Public Shared Function GetClipboardSequenceNumber() as UInt32
End Function