Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Customizing the Application Manifest of a VFP DLL 1

Status
Not open for further replies.

atlopes

Programmer
Mar 15, 2016
420
PT
[Sorry for cross posting]

Dear All

No problem in using a custom Application Manifest in a VFP EXE by placing the manifest file in the project folder, but so far I haven't been able to customize the manifest in VFP DLL COM servers the same way. The included manifest is always the default one.

Am I missing something?

Thanks in advance
 
The build process includes a manifest into the EXE, if and only if it's named correctly.

So your.pjx will result in your.exe and when you have your.exe.manifest before building your.exe, this manifest is embedded into your.exeand you don't need the external manifest file anymore, just for the next build.

So you keep the external manifest as a project source file, but you don't need to redistribute it.

If you now have problems with it working, eg allowing registry free usage of your own VFP made COM servers, this will need a client EXE using your COM Server have that manifest info embedded, it doesn't help if the COM server manifest information is within the COM server EXE/DLL.

Bye, Olaf.
 
Thanks for your reply, Olaf.

For sure, I didn't make myself clear: I don't have any kind of problem in embedding a Manifest in a VFP executable and adding the manifest file to the project workflow. What I need to know is how to embed a Manifest in a VFP DLL using the same process. As far as I can tell, for a DLL the builder keeps using the default VFP Application Manifest.

But I might be missing something.

António
 
This only works for executables.

What detail info would you like to embed in a DLL?

If you want to provide info on reg free usage of your COM servers, that info would need to go into an EXE using your DLL, not into your DLL, so the best thing you can provide to a developer using your DLL is the manifest information he needs for his EXE.

Bye, Olaf.
 
Olaf said:
This only works for executables.

Do you have any source for this? I mean, that the VFP builder only embeds custom application manifests in executables? If it is just common knowledge it's ok, I'll happily take your word for it.

Olaf said:
If you want to provide info on reg free usage of your COM servers

No, among other things, I want to be able to define a reg free usage of other COM servers in an intermediate COM server manifest.
 
There's just one try necessary to see it doesn't work. I think you tried with your.dll.manifest.
So you need to modify the DLL with typical resource editors.

Have you tried adding the necessary info into the EXE using your DLL? I know you want to decouple that, but it would be a solution for the cases you also provide the main executable, anyway. In the end the COM Servers run in the same process, when running as DLL. You may also make your DLLs EXEs, COM servers don't depend on the DLL format, it just makes them light weight and capable to become MTDLLs, but do you really need that aspects?

Bye, Olaf.
 
I need the COM server to be self-contained as much as I can. I'll check the different possibilities and decide on the most comfortable to work with.

Thanks a lot for your input, Olaf, invaluable as always.
 
Hi Antonio, attached you'll find 2 command line tools:

mt.exe: allows you to extract, merge or replace a manifest on any exe or dll ( this is not the microsoft mt.exe )

regsvr42,exe ( )
inspects a dll or group of dlls and creates the manifest for your reg-free exe ( doc attached ).

( note that I've never tested updating a dll manifest before, so please share your experience with that - I'd like to test but short of time right now )

Also check:

and


Marco Plaza
@vfp2nofox
 
Marco, thank you for your directions & tools.

I'll test this probably in my morning, and come back with the results.
 
Following Olaf suggestion, I tried a global declaration of reg free servers for the entire process, that is, at the executable manifest level. Most likely, a declaration on DLLs manifest wouldn't have any effect (in the same way that launching a manifested executable in the VFP IDE won't honor the declarations it may have).

This has to be tested with VFP executed as administrator.

Code:
* tests if the definition of reg free servers in an executable propagates
* throughout the entire process
* 2 servers are created, TestLibHW and TestLibW
* TestLibHW is dependent on TestLibW, and the executable depends on TestLibHW
* the registration of the 2 servers is made only in the executable manifest
* although TestLibHW instantiates a class of TestLibW, it uses the declaration
* of the executable manifest

LOCAL Source AS String
LOCAL Manifest AS String
LOCAL SafetyStatus AS String
LOCAL DefaultStatus AS String
LOCAL TempDir AS String
LOCAL DLLFullPath AS String

m.DefaultStatus = SET("Directory")
m.SafetyStatus = SET("Safety")

SET SAFETY OFF

* the folder can be safely emptied afterwards
m.TempDir = ADDBS(SYS(2023)) + "thread-dll-manifests"
IF !DIRECTORY(m.TempDir)
	MKDIR (m.TempDir)
ENDIF
SET DEFAULT TO (m.TempDir)

* the executable main (and single) program source
TEXT TO m.Source NOSHOW
	LOCAL Test AS TestLibHW.TestClass

	m.Test = CREATEOBJECT("TestLibHW.TestClass")

	MESSAGEBOX(m.Test.HelloWorld())
ENDTEXT

STRTOFILE(m.Source, "testexe.prg", 0)

* the first DLL class source
TEXT TO m.Source NOSHOW
	DEFINE CLASS TestClass AS Session OLEPUBLIC

		FUNCTION HelloWorld

			LOCAL Test AS TestLibW.TestClass
			
			m.Test = CREATEOBJECT("TestLibW.TestClass")
			RETURN "Hello, " + m.Test.World() + "!"
		ENDFUNC

	ENDDEFINE
ENDTEXT

STRTOFILE(m.Source, "testlib-hw.prg", 0)

* build a project for it, and then the DLL itself
BUILD PROJECT "TestLibHW" FROM "testlib-hw.prg"

m.DLLFullPath = ADDBS(m.TempDir) + "testlibhw.dll"

BUILD DLL (m.DLLFullPath) FROM "TestLibHW" RECOMPILE

* unregister, to make sure we don't have it available in the registry
RUN REGSVR32 /u &DLLFullPath

* now, do the same for the second DLL
TEXT TO m.Source NOSHOW
	DEFINE CLASS TestClass AS Session OLEPUBLIC

		FUNCTION World
			RETURN "World"
		ENDFUNC

	ENDDEFINE
ENDTEXT

STRTOFILE(m.Source, "testlib-w.prg", 0)

BUILD PROJECT "TestLibW" FROM "testlib-w.prg"

m.DLLFullPath = ADDBS(m.TempDir) + "testlibw.dll"

BUILD DLL (m.DLLFullPath) FROM "TestLibW" RECOMPILE

RUN REGSVR32 /u &DLLFullPath

* create the manifest
TEXT TO m.Manifest TEXTMERGE NOSHOW
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
	<assemblyIdentity 
		version="1.0.0.0" 
		type="win32" 
		name="Testing with reg free servers" 
		processorArchitecture="x86"
	/>
	<file name="testlibhw.dll">
	  <comClass clsid="{<<STREXTRACT(FILETOSTR("testlibhw.vbr"), "\CLSID = {", "}")>>}"
	           threadingModel="Apartment"
	           progid="testlibhw.testclass"
	           description="testlibhw.testclass" />
	</file>
	<file name="testlibw.dll">
	  <comClass clsid="{<<STREXTRACT(FILETOSTR("testlibw.vbr"), "\CLSID = {", "}")>>}"
	           threadingModel="Apartment"
	           progid="testlibw.testclass"
	           description="testlibw.testclass" />
	</file>
	<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
		<security>
			<requestedPrivileges>
				<requestedExecutionLevel level="asInvoker" /> 
			</requestedPrivileges>
		</security>
	</trustInfo>
	<dependency>
	    <dependentAssembly>
	        <assemblyIdentity
	            type="win32"
	            name="Microsoft.Windows.Common-Controls"
	            version="6.0.0.0"
	            language="*"
	            processorArchitecture="x86"
	            publicKeyToken="6595b64144ccf1df"
	        />
	    </dependentAssembly>
	</dependency>
</assembly>
ENDTEXT

STRTOFILE(m.Manifest, "testexe.exe.manifest", 0)

BUILD PROJECT "TestExe" FROM "testexe.prg"

BUILD EXE TestExe.exe FROM TestExe RECOMPILE

* run as an independent process, the SxS on the manifest will be used
RUN TestExe.exe

LOCAL LaunchError AS Exception

* try to run in the VFP9 IDE process, the manifest won't be honored and the classes won't be found
TRY
	DO TestExe.exe
CATCH TO m.LaunchError
	MESSAGEBOX("Executable could not be launched from the IDE. Reported: " + m.LaunchError.Message)
ENDTRY

SET DEFAULT TO (m.DefaultStatus)
IF m.SafetyStatus == "ON"
	SET SAFETY ON
ENDIF
 
Thanks for sharing that.

atlopes said:
This has to be tested with VFP executed as administrator.

Does that mean you still need to test, whether it works or we have to do so, to see it work?

OK, I read the description now. That answers it. Nice. Another thing to the toolset. The only downside is, now the DLL depends on that EXE.
I would also give it a try to go for EXEcutables instead of DLLs. Their main.prg could simply let them quit when _vfp.startmode isn't 2, otherwise keep it alive with a simple READ EVENTS.
I would just need to think about automatically releasing with a caller.

Bye, Olaf.
 
Olaf said:
Does that mean you still need to test, whether it works or we have to do so, to see it work?
It means that to run the test program, VFP must be executed as administrator because it must register the COM servers after the DLLs are built, and call REGSVR32 for the unregister process.
 
These tests seem to indicate that the declaration for registrationless COM servers enters in effect at the beginning of the process, but I still have to try a declaration in a DLL manifest (just to put the idea aside for good). Hopefully, the Manifest Tool that Marco shared will help with that.

Meanwhile, I'm having some difficulties to use an EXE based COM server on a legacy VFP6 application (and on VFP6 IDE itself, for that matter), and that's why I'm working with DLL (no problem on using the same EXE COM server in VFP9, or on using the DLL version in both VFP9 and VFP6). But will take the EXE version in consideration, especially if its own manifest is respected on the object instantiation.
 

Hi Antonio, I have a similar dependency tree ( exe -> mtdll -> mtdll ) and SXS works with no problem declaring both dependencies on the main exe manifest. I thought you were looking for a solution to distribute dlls to be used with exes wich you had no control, but if you can make a SXS manifest for the main exe, that's the way to go.










Marco Plaza
@vfp2nofox
 
mplaza said:
I thought you were looking for a solution to distribute dlls to be used with exes which you had no control
That's what I think, too.

That it's bound to VFP6 makes it a less general problem, but if you solve it, and multithreading plays a role, you could do that, so it's still worth trying, of course.

As it's about a VFP6 deficiency: You could use a VFP9 EXE Com Server in VFP6, too, it just requires the VFP9 runtime, of course. No matter if you build an EXE or DLL with VFP9 since any programming language capable to use COM servers can use it, the DLL or EXE doesn't share the same runtime anyway, even when used by a VFP EXE of the same version.

even hints at the runtime being automatically copied:

VFP help said:
Since this behavior (automatic copying/renaming of run time) occurs by default with the main Visual FoxPro run time...

I'm still not sure what exactly that's saying, but of course, a VFP6 application can use COM Servers of a VFP9 EXE, just like any COMServers any other application needing totally different runtimes, like the Office automation servers.

And you may change your multithreaded needs to multiprocessing with the detail of creating SingleUse EXE Servers. That's a setting having a strikingly different effect for EXE vs DLL. For a DLL it really limits instantiation of a class to one instance, whereas specified for EXE it means a new process for each COM server instance aka multiprocessing.


Bye, Olaf.
 
Marco said:
I thought you were looking for a solution to distribute dlls to be used with exes wich you had no control
I still want that (that is, I still want to be able to do that because it simplifies the deployment), but for more controlled environments this seems to be, as you say, the way to go.

Olaf said:
That it's bound to VFP6 makes it a less general problem
Even for VFP6, I'm willing to accept that it's a specific problem concerning only my library, therefore I wouldn't want to generalize on this fail (as I am a bit pressured on time, and since MTDLL + SingleUse is working perfectly, I'll leave further investigation on this to later on).

That said, VFP9 is more competent than VFP6 in consuming COM servers services, which is reasonable, and actually expected.

An example (working in VFP9, not in VFP6), illustrating extended casting capabilities of VFP9.

Code:
LOCAL ErrorMessage

m.ErrorMessage = "No error occurred"

ON ERROR m.ErrorMessage = MESSAGE()

LOCAL XML

m.XML = CREATEOBJECT("MSXML2.ServerXMLHTTP.6.0")
m.XML.Open("Get", "[URL unfurl="true"]https://www.tek-tips.com",[/URL] .F.)
m.XML.Send()

CREATE CURSOR TekTips (Response M)

INSERT INTO TekTips (Response) VALUES (m.XML.ResponseText)
INSERT INTO TekTips (Response) VALUES (m.XML.ResponseBody)

GO TOP

BROWSE NOWAIT

ON ERROR

MESSAGEBOX(m.ErrorMessage)

Thank you both, Olaf and Marco, for your insight and the privilege of your company.

António
 
I don't have VFP6 at hand to see what error it throws, but is that also something you experience when using a VFP9 compiled DLL or EXE in VFP6?

Bye, Olaf.
 
In this case, VFP6 retrieves .ResponseBody as an array, with an ALEN() that can be checked, but without allowing to address its elements or copy the contents. VFP9 just interprets it as a varbinary (I think), and the contents can be easily read and converted/processed.

The VFP9 DLL is working fine in VFP6, but when I try to CREATEOBJECT() the class from an EXE-based COM server, Windows reports a busy application and hangs on asking for task switching, until finally times out. As I said, it may have to do with something specific to the way the server is written (nothing more than DEFINE CLASS xxx OLEPUBLIC). I'll try to look at it, as soon as I get the opportunity.
 
>Windows reports a busy application and hangs on asking for task switching

I can only contribute this sometimes even happens with COM server normally working, eg I had this with Outlook.Application just yesterday. If it happens always, that's another story. It points into the direction of early vs late binding. Does VFP6 already have CreateObjectEx, or was that introduced in VFP7? VFP9 should always expose the IDispatch interface, though, never a problem for VFP6.

Besides, VFP9 sometimes has this itself, then you can work deeper into the COM object hierarchy by simply copying it into a new variable, eg assign oDoc = oIE.Document, before addressing oDoc.Body instead of going for oIE.Document.Body directly can help. This doesn't help in case of getting direct properties of the object you already automate, though.

Bye, Olaf.
 
Thanks once again, Olaf, you gave me more food for thought. Whatever my findings, I'll post them here in a manner that may be reproduced by others.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top