Now i have another little issue, still with the same DLL.

The idea of this component is to help read and write .ifc files, which contain information about buildings. The .ifc files are actually just ascii files, but it's complicated to extract the needed information from them.

The BSPro.IfcServer works with only one .ifc file at a time. Most often one file contains the data of just one building storey. In my project, i want to deal with the whole building, therefore i need to open multiple .ifc files.

So i thought that i'll just create as many ifcserver objects as there are .ifc files. The code below does that.



invoke CLSIDFromProgID, CStrW(L(BSPro.IfcServer)), addr clsidApp

mov IFCofn.lStructSize,SIZEOF IFCofn
push hwndMain
pop IFCofn.hWndOwner
push hInstance
pop IFCofn.hInstance
mov IFCofn.lpstrFilter, OFFSET IFCFilterString
mov IFCofn.lpstrFile, OFFSET IFCbuffer
mov IFCofn.nMaxFile,(MAXSIZE*MAXIFCCOUNT)
mov IFCofn.lpstrTitle,CStr ("Open IFC file(s)")
mov IFCofn.Flags, OFN_FILEMUSTEXIST or \
OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY or OFN_ALLOWMULTISELECT
invoke GetOpenFileName, ADDR IFCofn

invoke HowManyFileNames,addr IFCbuffer
mov IFCFileCount,eax
mov eax,4
mul IFCFileCount
invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,eax
mov pIfcServers,eax


xor edi,edi
xor esi,esi
NextServer:
.while esi<IFCFileCount
inc edi
invoke MakeSingleFileStr,addr IFCbuffer,edi,addr IFCSinglebuffer
lea ebx,pIfcServers[esi*4]
invoke CoCreateInstance, ADDR clsidApp, NULL, CLSCTX_ALL,\
addr IID_IIfcServer, ebx
invoke MultiByteToWideChar,CP_ACP,MB_PRECOMPOSED, addr IFCSinglebuffer,-1,addr IFCWbuffer,SIZEOF IFCWbuffer
invoke SysAllocString, addr IFCWbuffer
mov bstrString,eax
invoke dm(pIfcServers[esi*4],IIfcServer,ReadIfcFile),bstrString, addr retval
invoke SysFreeString,bstrString
inc esi
.endw


I thought that after this i can access any of the opened files with the appropriate pointer, for example



xor esi,esi
mov ebx,IFCFileCount
.while ebx>0
invoke dm(pIfcServers[esi*4],IIfcServer,get_FileName),addr IFCWbuffer
PrintDec eax
invoke WideCharToMultiByte,CP_ACP,WC_COMPOSITECHECK,addr IFCWbuffer,-1,addr buffer,256,NULL,NULL
dec ebx
inc esi
PrintString buffer
.endw


This is where the problem arises. No matter which object instance i use, it returns information from the LAST file opened.

I can think of a few options here

- put every instance of ifcserver in separate process, maybe that way they wont overwrite each others stuff

- merge all the opened .ifc files together, use only one ifcserver

I have a feeling that both of the above could get complicated, so i'd appreciate any ideas.
Posted on 2003-09-08 13:38:42 by Janne
Pardon the phun...

But the problem your describing is a property that is easily noticed when a class is defined with direct access to its variable instead of throught a method (as Japheth pointed out earlier ).

It sounds to me that due to the developers short-cuts it would have also been intended for single server uses only.

:NaN:
Posted on 2003-09-08 20:42:17 by NaN
My example was'nt too good because it gets a property. But with this problem, it doens'nt matter whether it's a property or method. A new file opened will replace the old. I guess this is only natural when the server objects are in the same address space, if the author of the DLL has'nt specially
handled the multiple file situation.

I was just hoping that maybe there is some simple solution to this problem, that i can't see.
Posted on 2003-09-09 01:17:47 by Janne
Ya.. i wasnt very clear (after rereading the above)..

Its more than likely the author used global values and a global vtable for interfaces. Thus in effect they have made a class for class objects, rather than a class for class instances. To do the latter, the com object should have allocated unique instance memory and copied in the vtable and data onto this memory. Doing so would then give you the behaviour your looking for...

I guess a good check for this is to print out the hex value for the pVtable that is returned every time you create an instance. If they all come back the same, its a safe bet my above assumption is true.... if not, then maybe im overlooking something...

:NaN:
Posted on 2003-09-09 20:56:46 by NaN
I did PrintHex the ppv:s the CoCreateInstance gives, it is different every time.

MSDN says:


Call CoCreateInstance when you want to create only one object on the local system. To create a single object on a remote system, call CoCreateInstanceEx. To create multiple objects based on a single CLSID, refer to the CoGetClassObject function.


Did'nt have the time to test CoGetClassObject yet, but my guess is it wont make any difference.
Posted on 2003-09-09 22:59:02 by Janne
Hi guys,

I doubt that its a good idea to make any conclusions from values in the vtable. Remember that in many cases you will see a vtable of the proxy only. Thats true for out-of-process servers in any case.

Japheth
Posted on 2003-09-10 02:03:48 by japheth
Another thing what strikes me is this



invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,eax
mov pIfcServers,eax


So pIfcServers is a pointer. But



lea ebx,pIfcServers[esi*4]
invoke CoCreateInstance, ADDR clsidApp, NULL, CLSCTX_ALL,\


Ths code doesnt do what it is expected to do. It would only work ok if pIfcServers is an array. So I wonder why your app doesnt crash.

Corect code would be



mov ebx,pIfcServers
lea ebx, [ebx + esi*4]
invoke CoCreateInstance, ADDR clsidApp, NULL, CLSCTX_ALL,\


the other parts need such a correction as well.

Thats an severe error, but im afraid it wont change your problem.

Japheth
Posted on 2003-09-10 02:29:00 by japheth
Japheth, thanks for pointing out those nasty bugs.

As you guessed, the original problem still remains. However, now i've noticed that some of the functions work as i expected, some have the problem described above. For example, with 3 files opened



xor esi,esi
mov ebx,IFCFileCount
.while ebx>0
mov edx,pIfcServers
invoke dm([edx+esi*4],IIfcServer,GetIfcSchema),addr BString;get the file "format"
mov edx,BString
invoke WideCharToMultiByte,CP_ACP,WC_COMPOSITECHECK,edx,-1,addr buffer,256,NULL,NULL
dec ebx
inc esi
PrintString buffer
.endw


works fine, i get to the output window



buffer = IFC20_LONGFORM
buffer = IFC2X_FINAL
buffer = IFC151


but this



xor esi,esi
mov ebx,IFCFileCount
.while ebx>0
mov edx,pIfcServers
invoke dm([edx+esi*4],IIfcServer,GetSpaces),addr pSpaces
invoke dm(pSpaces,IIfcCollection,GetSpace),0,addr pSpace;get the first space in file
invoke dm(pSpace,IIfcSpace,GetGUID),addr BString;get space's unique identifier
mov edx,BString
invoke WideCharToMultiByte,CP_ACP,WC_COMPOSITECHECK,edx,-1,addr buffer,256,NULL,NULL
dec ebx
inc esi
PrintString buffer
.endw


produces



buffer = 3C1762A6FCF504DB0049
buffer = 3C1762A6FCF504DB0049
buffer = 3C1762A6FCF504DB0049


this string comes only from the file whose server was the last one created.
Posted on 2003-09-10 15:24:58 by Janne
Janne,

most likely you have done it already but I would suggest some error checking:



xor esi,esi
mov ebx,IFCFileCount
.while ebx>0
mov edx,pIfcServers
invoke dm([edx+esi*4],IIfcServer,GetSpaces),addr pSpaces
.if (eax == S_OK)
invoke dm(pSpaces,IIfcCollection,GetSpace),0,addr pSpace;get the first space in file
.if (eax == S_OK)
invoke dm(pSpace,IIfcSpace,GetGUID),addr BString;get space's unique identifier
mov edx,BString
invoke WideCharToMultiByte,CP_ACP,WC_COMPOSITECHECK,edx,-1,addr buffer,256,NULL,NULL
.else
invoke MessageBox, ...
mov buffer, 0
.endif
.else
invoke MessageBox, ...
mov buffer, 0
.endif
dec ebx
inc esi
PrintString buffer
.endw


And remember that the caller must free BSTRs returned by a method.

Japheth
Posted on 2003-09-11 07:51:09 by japheth

And remember that the caller must free BSTRs returned by a method.

Did'nt "remember" that one either.
But still cant make it work with multiple files. Now i have some kind of theory though, why it does'nt work.

The current .ifc files have two parts, the header and the data. The header is usually under 10 lines of ascii text, the data is a lot more, avarage is maybe 100 000 lines or so.

The function above that works, deals with the header, the non-working with the data. So i think that the DLL uses different memory handling routines for the header end the data. So the bottom line is that i think there is no cure for this problem as long as the DLL runs in-process.

I have a pretty strong feeling that if all instances of the ifcserver were in different processes, it would just have to work. But i don't have much of a clue how to make that happen, so next i'll just try to merge all the opened files together and give the one file to the ifcserver.
Posted on 2003-09-14 13:14:49 by Janne
apparently, loading a DLL server in a different process than the client is simple with a DLL surrogate.

But is there a way to force a new instance of the surrogate for every instance of the server object? I guess it is not done by default, because (from MSDN)


DLL servers will share a surrogate if they have matching security identities and share the same AppID value.

Posted on 2003-09-15 23:00:11 by Janne
Hi Janne,

you can write a custom surrogate (implements ISurrogate). The class factory's CreateInstance method then will have to launch another process if count is > 0.

Possibly this job isnt too hard, but I doubt it is worth the efforts.

Japheth
Posted on 2003-09-16 00:15:44 by japheth
I tested the system supplied surrogate, CoCreateInstance fails. Is the flag to be used CLSCTX_LOCAL_SERVER? With that bit only, the call takes about 30 seconds and returns with 80080005 (Server execution failed). The registry should have the required entries:
Posted on 2003-09-16 16:09:57 by Janne
Hi Janne


Is the flag to be used CLSCTX_LOCAL_SERVER?


Yes, AFAIK. And the CLSCTX_INPROC must not be set. This works with my SimpleServer dll at least

Your server is possibly incapable to execute out-of-process. This may be the case because marshalling
isnt available for any interface the server exposes. The "universal", typelib-driven marshaller shoud be able
to handle all dispinterfaces and dual interfaces. custom interfaces must be marked with attribute .

I just tried with comview to create 2 objects with "DllSurrogate", changing the AppId after creating the first. As expected, each server runs it its own process now. Not very elegant, but it works.

Japheth
Posted on 2003-09-17 03:17:00 by japheth
Tested the system surrogate with a totally different DLL, got exactly the same result (80080005). This time i tried to create the instance with Comview.So looks that i have a general, system-wide problem using DLL surrogates. The OS is an old one (win98, not even SE), but it should support using DLL surrogates, i guess.
Posted on 2003-09-17 22:54:53 by Janne
Tested SimpleServer on a Win98 system (should be first edition, files dated may 1998). Has no problem with DllSurrogate. Dll runs in a process with executable DllHost.exe.

MS Office 2000 and IE 6 is installed on that computer, thou. Most likely installation changed some system dlls.
Posted on 2003-09-18 01:00:12 by japheth
In Control Panel / Networks there is by default "Client For Microsoft Networks". I had removed that because it was a security recommendation of my cable modem connection provider.

If i re-install the above, dllhost.exe seems to be doing it's job just fine.
Posted on 2003-09-18 22:32:15 by Janne
Hi Japheth


I just tried with comview to create 2 objects with "DllSurrogate", changing the AppId
after creating the first. As expected, each server runs it its own process now.
Not very elegant, but it works.


I tested the same thing, with the ifcserver and also with your simpleserver.dll
(after adding the DllSurrogate entry to registry).

But there is still only one instance of dllhost.exe, after modifying the AppID.

Also, i added in my program code to change the AppID for every ifcserver instance.
But again, only one instance of dllhost.exe and the multiple .ifc file handling works
no better than when using inproc server.



mov NullByte,0
mov APPID_EXISTED,FALSE;the default is, no AppID under CLSID
mov RegBuffSize,256
mov AppIDSize,40
invoke CLSIDFromProgID, CStrW(L(BSPro.IfcServer)), addr clsidApp
invoke StringFromCLSID,addr clsidApp,addr pCLSIDstrw
invoke WideCharToMultiByte,CP_ACP, 0 ,pCLSIDstrw,\
-1, addr RegBuffer, MAX_PATH, NULL, NULL
invoke lstrcpy,addr MsgBuff,CStr("CLSID\")
invoke lstrcat,addr MsgBuff,addr RegBuffer
invoke RegOpenKeyEx,HKEY_CLASSES_ROOT,addr MsgBuff,0,KEY_ALL_ACCESS,\
addr hkBspro

;get the value of appid (GUID) and save it
;if appid is not found it needs to be created so that surrogate will execute
;in both situations, restore registry as it was, after the ifcservers are created

invoke RegQueryValueEx,hkBspro,CStr("AppID"),NULL,addr AppIDType,addr OrigAppID,
addr AppIDSize
.IF eax==ERROR_SUCCESS
mov APPID_EXISTED,TRUE
.endif
invoke RegCloseKey,hkBspro


;fill the ifc OPENFILENAME struct
mov IFCofn.lStructSize,SIZEOF IFCofn
push hwndMain
pop IFCofn.hWndOwner
push hInstance
pop IFCofn.hInstance
mov IFCofn.lpstrFilter, OFFSET IFCFilterString
mov IFCofn.lpstrFile, OFFSET IFCbuffer
mov IFCofn.nMaxFile,(MAXSIZE*MAXIFCCOUNT);100 ifc files per building must be enough
mov IFCofn.lpstrTitle,CStr ("Open IFC file(s)")
mov IFCofn.Flags, OFN_FILEMUSTEXIST or \
OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY or OFN_ALLOWMULTISELECT
invoke GetOpenFileName, ADDR IFCofn

invoke HowManyFileNames,addr IFCbuffer
mov IFCFileCount,eax
mov eax,4
mul IFCFileCount

;allocate space for array of pIfcservers
invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,eax
mov pIfcServers,eax
mov eax,4
mul IFCFileCount

xor edi,edi
xor esi,esi

.while esi<IFCFileCount
;create new AppID string
invoke CoCreateGuid,addr pTempGUID
invoke StringFromCLSID,addr pTempGUID,addr pCLSIDstrw
invoke WideCharToMultiByte,CP_ACP, 0 ,pCLSIDstrw,\
-1, addr RegBuffer, MAX_PATH, NULL, NULL
invoke lstrcpy,addr AppIDBuff,CStr("AppID\")
invoke lstrcat,addr AppIDBuff,addr RegBuffer

; set the value just created to \CLSID\AppID
invoke RegOpenKeyEx,HKEY_CLASSES_ROOT,addr MsgBuff,0,KEY_ALL_ACCESS,\
addr hkBspro
invoke RegSetValueEx,hkBspro,CStr("AppID"),NULL,AppIDType,addr RegBuffer,AppIDSize
invoke RegFlushKey,hkBspro
invoke RegCloseKey,hkBspro


; create new key in HKEY_CLASSES_ROOT\AppID
invoke RegCreateKeyEx,HKEY_CLASSES_ROOT,addr AppIDBuff,NULL,NULL,\
REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,addr hkAppID,NULL

; create "DllSurrogate" named value under just created AppID
invoke RegSetValueEx,hkAppID,CStr("DllSurrogate"),NULL,REG_SZ,addr NullByte,1
invoke RegFlushKey,hkAppID
invoke RegCloseKey,hkAppID

inc edi
invoke MakeSingleFileStr,addr IFCbuffer,edi,addr IFCSinglebuffer


mov ebx,pIfcServers
lea ebx, [ebx + esi*4]

invoke CoCreateInstance, ADDR clsidApp, NULL, CLSCTX_LOCAL_SERVER,\; CLSCTX_ALL,\
addr IID_IIfcServer, ebx

invoke MultiByteToWideChar,CP_ACP,MB_PRECOMPOSED,addr IFCSinglebuffer,-1,
addr IFCWbuffer,SIZEOF IFCWbuffer

invoke SysAllocString, addr IFCWbuffer
mov bstrString,eax
mov edx,pIfcServers
invoke dm([edx+esi*4],IIfcServer,ReadIfcFile),bstrString, addr retval

; delete the created AppID key
invoke RegDeleteKey,HKEY_CLASSES_ROOT,addr AppIDBuff
invoke SysFreeString,bstrString
inc esi
.endw

.if APPID_EXISTED;if AppId already existed, restore it
invoke RegOpenKeyEx,HKEY_CLASSES_ROOT,addr MsgBuff,0,KEY_ALL_ACCESS,\
addr hkBspro
invoke RegSetValueEx,hkBspro,CStr("AppID"),NULL,AppIDType,addr OrigAppID,AppIDSize
invoke RegCloseKey,hkBspro
.else
; if appid did not exist already, remove the appid value under clsid
invoke RegOpenKeyEx,HKEY_CLASSES_ROOT,addr MsgBuff,0,KEY_ALL_ACCESS,\
addr hkBspro
invoke RegDeleteValue,hkBspro,CStr("AppID")
invoke RegCloseKey,hkBspro
.endif

invoke CoTaskMemFree,pCLSIDstrw

Posted on 2003-09-27 13:56:12 by Janne
Janne,

I tested the multiple instance problem on a XP system. After you mentioned your OS I checked if surrogates work at all with win98, but missed to scrutinize the instance problem on that system. Sorry.

Japheth
Posted on 2003-09-28 01:14:54 by japheth
Seems that under Win2000, the multiple instances of dllhost.exe (and the server dll) work as i wished. Had to fix one bug in the above code though, changed



invoke RegSetValueEx,hkBspro,CStr("AppID"),NULL,AppIDType,addr RegBuffer,AppIDSize


to



invoke RegSetValueEx,hkBspro,CStr("AppID"),NULL,REG_SZ,addr RegBuffer,AppIDSize
Posted on 2003-10-01 05:46:26 by Janne